Files
gzth/yuthon_property/wizard/property_meter_wizard.py
T
2025-10-29 16:16:27 +08:00

231 lines
10 KiB
Python

# -*- coding: utf-8 -*-
from odoo import models, fields, api, _, SUPERUSER_ID
from odoo.exceptions import ValidationError, UserError
from pypinyin import pinyin, Style
import xlrd
import base64
import logging
import datetime
from datetime import datetime
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
_logger = logging.getLogger(__name__)
class PropertyMeterWizard(models.Model):
_name = 'property.meter.wizard'
excel_file = fields.Binary(string="上传Excel表")
excel_name = fields.Char(string="文件名")
def confirm(self):
"""
导入excel文件,按行读取数据并处理,将1-9月数据写入明细行
"""
if not self.excel_file:
raise ValidationError('请上传文件。')
# 解码并打开Excel文件
try:
book = xlrd.open_workbook(file_contents=base64.decodebytes(self.excel_file))
sh = book.sheet_by_index(0)
except Exception as e:
raise ValidationError(f'文件解析错误: {str(e)}')
not_found_unit_numbers = []
# 按行处理数据
for rx in range(1, sh.nrows): # 跳过表头行
row = sh.row(rx)
self.property_meter_data1(row, not_found_unit_numbers)
if not_found_unit_numbers:
print("\n===== 未找到电表库的单元编号汇总 =====")
for unit in not_found_unit_numbers:
print(unit) # 每行打印一个单元编号
print(f"\n共 {len(not_found_unit_numbers)} 个单元编号未匹配到电表库")
# # 处理1-9月份数据
# for month in range(1, 10):
# self.property_meter_data(row, month)
def property_meter_data(self, row, month):
"""
处理单月数据,查找主记录并创建/更新明细行
"""
# 从Excel行获取基础数据
epitope_values = row[0].value if len(row) > 0 else ''
unit_number = row[1].value if len(row) > 1 else ''
installation_location = row[2].value if len(row) > 2 else ''
name = row[3].value if len(row) > 3 else ''
excel_start_date = datetime(1899, 12, 30)
# 计算目标日期
if row[19].value:
start_date = excel_start_date + timedelta(days=row[19].value)
else:
start_date = None
if row[20].value:
end_date = excel_start_date + timedelta(days=row[20].value)
else:
end_date = None
# 处理仪表倍率
gauge_magnification = None
if len(row) > 5 and row[5].value:
try:
gauge_magnification = float(row[5].value)
except ValueError:
gauge_magnification = 1 # 默认为1
# 处理最大位数
maximum_count = None
if len(row) > 6 and row[6].value:
try:
maximum_count = float(row[6].value)
except ValueError:
maximum_count = None
# 处理类型转换
type_dict = {'公共': 'public', '收费': 'fees', '': ''}
meter_type = type_dict.get(row[7].value if len(row) > 7 else '', '')
current_read_index = month + 9
meter_date = datetime(2025, month, 15)
current_read = None
if len(row) > current_read_index and row[current_read_index].value:
try:
current_read = float(row[current_read_index].value)
except ValueError:
current_read = None
property_id = None
if installation_location:
property_search = self.env['yuthon.property'].search(
[('name', '=', installation_location)],
limit=1
)
if property_search:
property_id = property_search
# 处理物业信息(模糊匹配以及小数为.0的)
if type(name) == float:
name = int(name)
archives = self.env['property.meter.archives'].search([
('name', 'ilike', f'%{name}%')
], limit=1)
epitope_values_id = self.env['yuthon.epitope.values'].search([('name', '=', epitope_values)], limit=1)
if not epitope_values_id:
epitope_values_id = self.env['yuthon.epitope.values'].create({'name': epitope_values,
'type': 'ammeter'})
if not archives:
archives = self.env['property.meter.archives'].create({
'name': name,
'epitope_values': epitope_values,
'epitope_values_id': epitope_values_id.id,
'start_time': start_date,
'end_time': end_date,
'unit_number': unit_number,
'installation_location': installation_location,
'type': meter_type,
'gauge_magnification': gauge_magnification,
'maximum_count': maximum_count,
'yuthon_property_ids': [(4, property_id.id)] if property_id else [],
})
else:
# 更新已有记录的部分字段
archives.write({
'epitope_values': epitope_values,
'epitope_values_id': epitope_values_id.id,
'start_time': start_date,
'end_time': end_date,
'unit_number': unit_number,
'installation_location': installation_location,
'type': meter_type,
'gauge_magnification': gauge_magnification,
'maximum_count': maximum_count,
'yuthon_property_ids': [(4, property_id.id)] if property_id else [],
})
# 准备明细行数据
line_vals = {
'initial_value': row[9].value,
'meter_date':meter_date,
'archives_id': archives.id,
'unit_number': unit_number,
'type': meter_type,
'gauge_magnification': gauge_magnification or 1,
'maximum_count': maximum_count,
'current_read': current_read,
'last_reading_time': meter_date,
'yuthon_property_ids': [(4, property_id.id)] if property_id else [],
}
self.env['meter.archives.line'].sudo().create(line_vals)
def property_meter_data1(self, row, not_found_list):
unit_number = int(row[0].value) if isinstance(row[0].value, float) else row[0].value
# 1. 根据单元编号找到电表库记录
archives_id = self.env['property.meter.archives'].sudo().search(
[('unit_number', '=', unit_number)],
limit=1
)
if not archives_id:
not_found_list.append(unit_number)
return
# 2. 从电表任务明细(line_ids)中筛选出2025年1月的记录,并找到last_reading_time最早的那条
target_year = 2025
target_month = 1
# 筛选条件:last_reading_time在2025年1月,且状态正常
line_ids = archives_id.line_ids.filtered(lambda line:
line.last_reading_time and
line.last_reading_time.year == target_year and
line.last_reading_time.month == target_month and
line.archives_state == 'use' # 仅处理正常状态的记录
)
if not line_ids:
return # 无2025年1月的记录,直接返回
# 找到last_reading_time最早的记录(2025年1月的基准记录)
earliest_line = line_ids.sorted(key=lambda l: l.last_reading_time)[0]
# 3. 获取2025年1月的计算参数(从最早记录中取)
unit = earliest_line.unit # 单价
line_loss = earliest_line.line_loss # 线损
gauge_magnification = earliest_line.gauge_magnification # 倍率
last_read_202501 = earliest_line.last_read # 2025年1月的上次读数(作为反推基准)
# 4. 反推2024年12月的本次读数和上次读数
# 已知:2024年12月15日的金额 = row[2].value
dec_amount = float(row[2].value) if row[2].value else 0.0
# 公式:应收金额 = 本次用量 * 单价 * (1 + 线损) * 倍率
# 变形:本次用量 = 应收金额 / (单价 * (1 + 线损) * 倍率)
denominator = unit * (1 + line_loss) * gauge_magnification if (unit and line_loss is not None) else 1
current_meter_dec = int(dec_amount / denominator) if denominator != 0 else 0.0 # 2024年12月的本次用量
# 假设2025年1月的上次读数 = 2024年12月的本次读数(连续读数逻辑)
current_read_dec = last_read_202501 # 2024年12月的本次读数 = 2025年1月的上次读数
# 2024年12月的上次读数 = 本次读数 - 本次用量
last_read_dec = current_read_dec - current_meter_dec
# 5. 创建2024年12月的应收记录(使用2025年1月的参数)
self.env['meter.archives.line'].create({
'archives_id': archives_id.id,
'unit_number': earliest_line.unit_number,
'epitope_values_id': earliest_line.epitope_values_id.id, # 表位置沿用
'lessee_id': earliest_line.lessee_id.id,
'contract_id': earliest_line.contract_id.id, # 承租方沿用
'yuthon_property_ids': [(6, 0, earliest_line.yuthon_property_ids.ids)], # 物业信息沿用
'last_meter': datetime(2024, 11, 15).date(), # 2024年12月上次抄表日期
'initial_value': last_read_dec,
'current_read': current_read_dec, # 反推的2024年12月本次读数
'current_meter': current_meter_dec, # 2024年12月本次用量(计算得出)
'unit': unit, # 取自2025年1月的单价
'line_loss': line_loss, # 取自2025年1月的线损
'gauge_magnification': gauge_magnification, # 取自2025年1月的倍率
'last_reading_time': datetime(2024, 12, 15).date(), # 2024年12月15日(金额对应的日期)
'meter_date': datetime(2024, 12, 15).date(), # 任务日期
'meter_date_month': '2024-12',
'type': earliest_line.type,
'archives_state': 'use',
'state': 'reading',
})