212 lines
11 KiB
Python
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)}')
|