231 lines
10 KiB
Python
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',
|
|
})
|
|
|