Files
gzth/yuthon_property/wizard/tenant_import_wizard.py
T
2026-02-23 11:51:11 +08:00

212 lines
11 KiB
Python

# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.exceptions import UserError
import base64
import xlrd
from datetime import datetime
class TenantImportWizard(models.TransientModel):
_name = 'tenant.import.wizard'
_description = '承租方信息导入向导'
file = fields.Binary(string='Excel文件', required=True)
filename = fields.Char(string='文件名')
def action_import(self):
"""执行导入"""
if not self.file:
raise UserError('请先上传Excel文件!')
try:
# 解码文件
file_data = base64.b64decode(self.file)
workbook = xlrd.open_workbook(file_contents=file_data)
sheet = workbook.sheet_by_index(0)
# 获取表头行(第一行)
headers = [sheet.cell_value(0, col) for col in range(sheet.ncols)]
# 打印表头用于调试(会显示在Odoo日志中)
import logging
_logger = logging.getLogger(__name__)
_logger.info(f'Excel表头: {headers}')
# 找到各字段对应的列索引
col_map = {}
for idx, header in enumerate(headers):
col_map[header] = idx
_logger.info(f'列 {idx}: {header}')
success_count = 0
error_count = 0
error_messages = []
# 从第二行开始读取数据
for row_idx in range(1, sheet.nrows):
try:
# 读取基本信息
name = sheet.cell_value(row_idx, col_map.get('name', -1)) if 'name' in col_map else ''
email = sheet.cell_value(row_idx, col_map.get('email', -1)) if 'email' in col_map else ''
phone = sheet.cell_value(row_idx, col_map.get('phone', -1)) if 'phone' in col_map else ''
address = sheet.cell_value(row_idx, col_map.get('address', -1)) if 'address' in col_map else ''
# 跳过姓名为空的行
if not name:
continue
# 查找是否已存在该承租方
existing_tenant = self.env['property.tenant.information'].search([
('name', '=', name)
], limit=1)
# 准备开票信息数据 - 同时支持中文列名和Odoo导出格式
# 优先使用中文列名,如果没有则使用Odoo导出格式
scope_business = ''
if '经营范围' in col_map:
scope_business = sheet.cell_value(row_idx, col_map['经营范围'])
elif 'invoicing_ids/scope_business' in col_map:
scope_business = sheet.cell_value(row_idx, col_map['invoicing_ids/scope_business'])
invoicing_type_name = ''
if '开票类型' in col_map:
invoicing_type_name = sheet.cell_value(row_idx, col_map['开票类型'])
elif 'invoicing_ids/invoicing_type_id' in col_map:
invoicing_type_name = sheet.cell_value(row_idx, col_map['invoicing_ids/invoicing_type_id'])
invoicing_begins = ''
if '开票抬头' in col_map:
invoicing_begins = sheet.cell_value(row_idx, col_map['开票抬头'])
elif 'invoicing_ids/invoicing_begins' in col_map:
invoicing_begins = sheet.cell_value(row_idx, col_map['invoicing_ids/invoicing_begins'])
tax_number = ''
if '税号' in col_map:
tax_number = sheet.cell_value(row_idx, col_map['税号'])
elif 'invoicing_ids/tax_number' in col_map:
tax_number = sheet.cell_value(row_idx, col_map['invoicing_ids/tax_number'])
start_date_str = ''
if '生效日期' in col_map:
start_date_str = sheet.cell_value(row_idx, col_map['生效日期'])
elif 'invoicing_ids/start_date' in col_map:
start_date_str = sheet.cell_value(row_idx, col_map['invoicing_ids/start_date'])
# 查找开票类型
invoicing_type_id = False
if invoicing_type_name:
# 尝试多种匹配方式
invoicing_type = self.env['yuthon.resource.billing.type'].search([
('name', '=', invoicing_type_name)
], limit=1)
# 如果找不到,尝试模糊匹配
if not invoicing_type:
invoicing_type = self.env['yuthon.resource.billing.type'].search([
('name', 'ilike', invoicing_type_name)
], limit=1)
# 如果还是找不到,自动创建开票类型
if not invoicing_type:
_logger.info(f'开票类型不存在,自动创建: {invoicing_type_name}')
invoicing_type = self.env['yuthon.resource.billing.type'].create({
'name': invoicing_type_name
})
if invoicing_type:
invoicing_type_id = invoicing_type.id
_logger.info(f'使用开票类型: {invoicing_type_name} -> ID: {invoicing_type_id}')
# 处理日期 - 支持多种格式
start_date = False
if start_date_str:
try:
# 如果是Excel日期数字格式
if isinstance(start_date_str, float):
from xlrd import xldate_as_tuple
date_tuple = xldate_as_tuple(start_date_str, workbook.datemode)
start_date = datetime(*date_tuple[:3]).date()
# 如果是字符串格式
elif isinstance(start_date_str, str) and start_date_str:
# 尝试多种日期格式
for fmt in ['%Y-%m-%d', '%Y/%m/%d', '%Y.%m.%d', '%Y%m%d']:
try:
start_date = datetime.strptime(start_date_str, fmt).date()
break
except:
continue
except Exception as e:
_logger.warning(f'日期解析失败: {start_date_str}, 错误: {str(e)}')
_logger.info(f'第{row_idx + 1}行 - 经营范围: {scope_business}, 开票类型: {invoicing_type_name}, 税号: {tax_number}, 生效日期: {start_date}')
# 准备承租方数据
tenant_vals = {
'name': name,
'email': email if email else False,
'phone': phone if phone else False,
'address': address if address else False,
}
if existing_tenant:
# 更新现有承租方
existing_tenant.write(tenant_vals)
tenant = existing_tenant
else:
# 创建新承租方
tenant = self.env['property.tenant.information'].create(tenant_vals)
# 如果有开票信息,创建开票信息明细
if invoicing_type_id or tax_number or invoicing_begins or scope_business:
invoicing_vals = {
'tenant_id': tenant.id,
'scope_business': scope_business if scope_business else False,
'invoicing_type_id': invoicing_type_id,
'tax_number': tax_number if tax_number else False,
'invoicing_begins': invoicing_begins if invoicing_begins else False,
'start_date': start_date,
}
_logger.info(f'准备创建开票信息: {invoicing_vals}')
# 检查是否已存在相同的开票信息
existing_invoicing = self.env['invoicing.information.line'].search([
('tenant_id', '=', tenant.id),
('invoicing_begins', '=', invoicing_begins),
('start_date', '=', start_date),
], limit=1)
if existing_invoicing:
# 如果已存在,更新记录
existing_invoicing.write(invoicing_vals)
_logger.info(f'更新已存在的开票信息明细,ID: {existing_invoicing.id}, 新开票类型ID: {invoicing_type_id}')
else:
# 如果不存在,创建新记录
created_line = self.env['invoicing.information.line'].create(invoicing_vals)
_logger.info(f'成功创建开票信息明细,ID: {created_line.id}, 开票类型ID: {created_line.invoicing_type_id.id if created_line.invoicing_type_id else None}')
success_count += 1
except Exception as e:
error_count += 1
error_messages.append(f'第{row_idx + 1}行导入失败: {str(e)}')
# 返回结果提示
message = f'导入完成!\n成功: {success_count} 条\n失败: {error_count} 条'
if error_messages:
message += '\n\n错误详情:\n' + '\n'.join(error_messages[:10]) # 只显示前10条错误
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': '导入结果',
'message': message,
'type': 'success' if error_count == 0 else 'warning',
'sticky': True,
}
}
except Exception as e:
raise UserError(f'文件解析失败: {str(e)}')