提交 b49fb174 authored 作者: 刘擎阳's avatar 刘擎阳

1.优化bug

上级 8279ed7a
......@@ -6,6 +6,7 @@ import pytz
from datetime import datetime, date
from odoo import models, fields, api, _
from odoo.exceptions import UserError
from dateutil import parser
import openpyxl
_logger = logging.getLogger(__name__)
......@@ -55,8 +56,13 @@ class PackageDataWizard(models.TransientModel):
return False
elif isinstance(val, str):
try:
# 优先尝试框架原生的标准解析,效率高
dt = fields.Datetime.from_string(val)
except ValueError:
except (ValueError, AttributeError):
try:
# 原生解析失败时,使用 dateutil 兜底解析 "2026/6/25" 等非标格式
dt = parser.parse(val)
except Exception:
return False
elif isinstance(val, datetime):
dt = val
......@@ -79,8 +85,8 @@ class PackageDataWizard(models.TransientModel):
def action_import_data_with_validation(self):
"""
表格数据校验与导入核心逻辑:
1. 校验未来时
2. 根据当前 node_id 校验与系统存量数据的时序
1. 校验未来时 (含托盘使用日期)
2. 根据当前 node_id 校验与系统存量数据的时序及必填项
3. 错误回写表格,保存到当前向导的 report_file 字段
4. 成功返回 JSON 格式数据
"""
......@@ -103,7 +109,7 @@ class PackageDataWizard(models.TransientModel):
header_map = {col: idx for idx, col in enumerate(headers)}
# 预设错误回写列 (如果已经有错误原因列则覆盖,否则新增一列)
# 预设错误回写列
error_col_idx = header_map.get('错误原因', sheet.max_column) + 1
if '错误原因' not in headers:
sheet.cell(row=1, column=error_col_idx, value="错误原因")
......@@ -120,11 +126,28 @@ class PackageDataWizard(models.TransientModel):
all_pkg_nums.append(str(pkg_num).strip())
system_packages = self.env['cc.big.package'].search([('big_package_no', 'in', all_pkg_nums)])
# 【优化点】:因为上面搜索的是 big_package_no,字典映射改用 big_package_no 更安全
sys_pkg_map = {pkg.big_package_no: pkg for pkg in system_packages if pkg.big_package_no}
current_node_name = self.node_id.name if self.node_id else ''
# 【新增优化 4】:预先批量查询“已提货”节点ID及所有相关小包数据
picked_up_node_id = False
sp_by_bp_map = {}
if current_node_name == '待尾程提货':
picked_up_node = self.env['cc.node'].search([('node_type', '=', 'package'), ('name', '=', '已提货')], limit=1)
if picked_up_node:
picked_up_node_id = picked_up_node.id
# 一次性查出表格里所有大包关联的小包
small_packages = self.env['cc.ship.package'].search(
[('big_package_id.big_package_no', 'in', all_pkg_nums)])
# 按大包号对小包进行分组字典映射
for sp in small_packages:
bp_no = sp.big_package_id.big_package_no
if bp_no:
if bp_no not in sp_by_bp_map:
sp_by_bp_map[bp_no] = []
sp_by_bp_map[bp_no].append(sp)
# --- 3. 逐行遍历校验 ---
for row_idx, row in enumerate(sheet.iter_rows(min_row=2, max_col=error_col_idx), start=2):
pkg_num_cell = row[header_map.get('大包号')] if header_map.get('大包号') is not None else None
......@@ -142,20 +165,82 @@ class PackageDataWizard(models.TransientModel):
continue
row_data_dict[col_name] = row[col_idx].value
# 获取并转换需要校验的两个时间
# 获取并转换需要校验的时间字段
raw_tally = row_data_dict.get('理货时间')
raw_delivery = row_data_dict.get('尾程交货时间')
raw_pallet_date = row_data_dict.get('托盘使用日期')
tally_utc = self._parse_and_convert_tz(raw_tally)
delivery_utc = self._parse_and_convert_tz(raw_delivery)
pallet_date_utc = self._parse_and_convert_tz(raw_pallet_date)
if tally_utc:
row_data_dict['理货时间'] = tally_utc.strftime('%Y-%m-%d %H:%M:%S')
if delivery_utc:
row_data_dict['尾程交货时间'] = delivery_utc.strftime('%Y-%m-%d %H:%M:%S')
if pallet_date_utc:
row_data_dict['托盘使用日期'] = pallet_date_utc.strftime('%Y-%m-%d %H:%M:%S')
# 提取人员与托盘号文本(防呆:去空格判断)
tally_person = row_data_dict.get('理货人')
delivery_person = row_data_dict.get('尾程交货人')
pallet_no = row_data_dict.get('托盘号')
tally_person_str = str(tally_person).strip() if tally_person else ''
delivery_person_str = str(delivery_person).strip() if delivery_person else ''
pallet_no_str = str(pallet_no).strip() if pallet_no else ''
# -----------------------------------------------------
# 【优化 1, 2, 4】:根据变更状态校验时序、必填项及小包状态
# -----------------------------------------------------
if current_node_name == '待尾程提货':
if not raw_tally:
row_errors.append("变更状态为待尾程提货时,理货时间不能为空")
if not tally_person_str:
row_errors.append("变更状态为待尾程提货时,理货人不能为空")
if tally_person_str:
user = self.env['res.users'].sudo().search([('login', '=', tally_person_str)], limit=1)
if not user:
row_errors.append("系统不存在该理货人")
# 【新增优化 4】:小包“已提货”状态交叉校验
if picked_up_node_id:
sps = sp_by_bp_map.get(pkg_num, [])
if not sps:
row_errors.append("该大包下未找到任何系统小包记录")
else:
# 找出所有状态不是“已提货”的小包 (注: 这里假定小包模型代表单号的字段为 name)
not_ready_sps = [sp.logistic_order_no for sp in sps if sp.state.id != picked_up_node_id]
if len(not_ready_sps) == len(sps):
row_errors.append("该大包下的小包全部不是已提货状态")
elif len(not_ready_sps) > 0:
# 部分未提货,截取前10个单号展示
show_sps = not_ready_sps[:10]
row_errors.append(f"部分小包不是已提货状态: {', '.join(show_sps)}")
elif current_node_name == '尾程交接':
if not raw_delivery:
row_errors.append("变更状态为尾程交接时,尾程交货时间不能为空")
if not delivery_person_str:
row_errors.append("变更状态为尾程交接时,尾程交货人不能为空")
if delivery_person_str:
user = self.env['res.users'].sudo().search([('login', '=', delivery_person_str)], limit=1)
if not user:
row_errors.append("系统不存在该尾程交货人")
# -----------------------------------------------------
# 【需求 1】:理货时间、尾程交货时间不能是未来时间
# 【优化 3】:托盘号、托盘使用日期必填及未来日期校验
# -----------------------------------------------------
if not pallet_no_str:
row_errors.append("托盘号不能为空")
# print(raw_pallet_date, pallet_date_utc, now_utc)
if not raw_pallet_date:
row_errors.append("托盘使用日期不能为空")
elif pallet_date_utc and pallet_date_utc > now_utc:
row_errors.append("托盘使用日期不能是未来日期")
# -----------------------------------------------------
# 原逻辑:理货时间、尾程交货时间不能是未来时间
# -----------------------------------------------------
if tally_utc and tally_utc > now_utc:
row_errors.append("理货时间不能是未来时间")
......@@ -163,46 +248,40 @@ class PackageDataWizard(models.TransientModel):
row_errors.append("尾程交货时间不能是未来时间")
# -----------------------------------------------------
# 结合系统数据的状态时序校验 (需求 2, 3)
# 结合系统数据的状态时序校验
# -----------------------------------------------------
sys_pkg = sys_pkg_map.get(pkg_num)
# 【需求 2】:系统选择“待尾程提货”,理货时间不能小于系统的提货时间
if current_node_name == '待尾程提货' and tally_utc and sys_pkg and sys_pkg.pickup_time:
if tally_utc < sys_pkg.pickup_time:
row_errors.append("理货时间不能小于系统内该大包的提货时间")
if tally_utc <= sys_pkg.pickup_time:
row_errors.append("理货时间必须大于系统内该大包的提货时间")
# 【需求 3】:系统选择“尾程交接”,尾程交货时间不能小于系统的理货时间
if current_node_name == '尾程交接' and delivery_utc and sys_pkg and sys_pkg.tally_time:
if delivery_utc < sys_pkg.tally_time:
row_errors.append("尾程交货时间不能小于系统内该大包的理货时间")
if delivery_utc <= sys_pkg.tally_time:
row_errors.append("尾程交货时间必须大于系统内该大包的理货时间")
# --- 4. 组装结果与回写 ---
if row_errors:
has_error = True
error_msg = " | ".join(row_errors)
# 将错误写在这一行的最后一列
row[error_col_idx - 1].value = error_msg
else:
final_data_to_insert.append(row_data_dict)
# --- 5. 【修改部分】:异常数据保存到模型字段,刷新页面 ---
# --- 5. 异常数据保存到模型字段,刷新页面 ---
if has_error:
out_stream = io.BytesIO()
wb.save(out_stream)
out_stream.seek(0)
# 生成文件名和 Base64 数据
error_file_name = f'异常数据.xlsx'
encoded_file = base64.b64encode(out_stream.read())
# 写入当前向导记录
self.write({
'report_file': encoded_file,
'file_name': error_file_name
})
# 返回前端动作:刷新当前向导页面 (用户将能看到并点击下载你的 report_file)
return {
'type': 'ir.actions.act_window',
'res_model': 'package.data.wizard',
......@@ -214,28 +293,8 @@ class PackageDataWizard(models.TransientModel):
# --- 6. 校验全部通过,返回 JSON 格式数据 ---
json_result = json.dumps(final_data_to_insert, ensure_ascii=False, indent=4)
print(json_result)
return json_result
# _logger.info("\n" + "=" * 50)
# _logger.info(f"所有数据校验完美通过!共计 {len(final_data_to_insert)} 行。")
# _logger.info("返回的 JSON 数据如下:\n%s", json_result)
# _logger.info("=" * 50)
#
# # 如果需要彻底清空错误文件字段,避免下次正常上传时依然显示,可以在这里加上清理代码
# self.write({
# 'report_file': False,
# 'file_name': False
# })
#
# return {
# 'type': 'ir.actions.client',
# 'tag': 'display_notification',
# 'params': {
# 'title': '校验与转换成功',
# 'message': f'全部 {len(final_data_to_insert)} 条数据校验通过!已转换为 JSON 数据格式。',
# 'type': 'success',
# 'sticky': False,
# }
# }
# def submit(self):
# for attachment_obj in self.attachment_ids:
......@@ -415,7 +474,10 @@ class PackageDataWizard(models.TransientModel):
wj_time,
big_package_obj.id
))
if self.node_id.name == '待尾程提货':
big_package_obj.tally_state = 'checked_goods'
else:
big_package_obj.tally_state = 'handover_completed'
# 7. 更新托盘状态
if pallet_no:
pallet_obj = self.env['cc.pallet'].sudo().search([('name', '=', pallet_no)], limit=1)
......
......@@ -54,10 +54,10 @@
<field name="target">new</field>
</record>
<menuitem id="menu_package_data_wizard"
<menuitem id="menu_import_package_data_wizard"
name="导入包裹数据"
action="action_package_data_wizard"
groups="base.group_system"
groups="ccs_base.group_import_package"
sequence="50"/>
</data>
</odoo>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论