提交 9e6a63bc authored 作者: 贺阳's avatar 贺阳

提单有一个小包是清关开始状态,有同步日志,提单获取pod并推送,该小包产生了倒叙的数据

上级 d5ccac2a
......@@ -1765,7 +1765,7 @@ class BatchGetPodInfoWizard(models.TransientModel):
def get_detail_info(self, processed_files):
"""
获取提单对应的节点以及时间
:param processed_files: 处理后的文件数组
:param processed_files: 处理后的文件数组(应该已经包含valid_packages字段,只包含满足条件的小包)
:return: 提单对应的节点以及节点操作时间
"""
ship_packages = []
......@@ -1782,6 +1782,30 @@ class BatchGetPodInfoWizard(models.TransientModel):
if not pod_node:
_logger.info(f"未找到尾程POD节点匹配的节点,提单号: {bl.bl_no}")
continue
# 只使用满足条件的小包(经过验证的valid_packages)
valid_packages = file_info.get('valid_packages', [])
if not valid_packages:
_logger.warning(f"提单 {bl.bl_no} 没有满足条件的小包,跳过节点推送")
continue
# 从valid_packages中提取小包ID(记录集对象或列表)
if hasattr(valid_packages, 'ids'):
# 如果是记录集对象,直接获取IDs
valid_package_ids = valid_packages.ids
elif isinstance(valid_packages, list):
# 如果是列表,提取每个对象的ID
valid_package_ids = [p.id for p in valid_packages if hasattr(p, 'id')]
else:
_logger.warning(f"提单 {bl.bl_no} valid_packages格式不正确: {type(valid_packages)}")
valid_package_ids = []
_logger.info(f"提单 {bl.bl_no} 满足条件的小包ID: {valid_package_ids} (共 {len(valid_package_ids)} 个)")
if not valid_package_ids:
_logger.warning(f"提单 {bl.bl_no} 满足条件的小包ID为空,跳过节点推送")
continue
# 从PDF文件提取红色框的时间
file_data = file_info.get('file_data')
if not file_data:
......@@ -1795,10 +1819,10 @@ class BatchGetPodInfoWizard(models.TransientModel):
if extracted_times:
# 取最早的时间作为节点操作时间
earliest_time = min(extracted_times)
_logger.info(f"提取到最早时间: {earliest_time},将作为节点操作时间")
_logger.info(f"提取到最早时间: {earliest_time},将作为节点操作时间,满足条件的小包数量: {len(valid_package_ids)},小包ID: {valid_package_ids}")
ship_packages.append({
'bl_id': bl.id,
'id': bl.ship_package_ids.ids,
'id': valid_package_ids, # 只包含满足条件的小包ID
'tally_time': str(earliest_time)
})
else:
......@@ -2318,6 +2342,18 @@ class BatchGetPodInfoWizard(models.TransientModel):
# OCR文本数据量小,可以直接存储
if 'ocr_texts' in file_info:
data['ocr_texts'] = file_info['ocr_texts']
# 保存valid_packages的ID列表(记录集对象无法直接序列化)
if 'valid_packages' in file_info and file_info['valid_packages']:
valid_packages = file_info['valid_packages']
# 如果是记录集对象,提取ID列表
if hasattr(valid_packages, 'ids'):
data['valid_package_ids'] = valid_packages.ids
elif isinstance(valid_packages, list):
# 如果是列表,提取每个对象的ID
data['valid_package_ids'] = [p.id for p in valid_packages if hasattr(p, 'id')]
else:
data['valid_package_ids'] = []
_logger.info(f"序列化时保存valid_packages: 提单 {bl.bl_no}, 满足条件的小包ID: {data['valid_package_ids']}")
serialized_data.append(data)
return json.dumps(serialized_data, ensure_ascii=False)
......@@ -2377,6 +2413,13 @@ class BatchGetPodInfoWizard(models.TransientModel):
# 如果有OCR文本,也恢复
if 'ocr_texts' in data:
file_info['ocr_texts'] = data['ocr_texts']
# 恢复valid_packages(从ID列表重建记录集对象)
if 'valid_package_ids' in data and data['valid_package_ids']:
valid_package_ids = data['valid_package_ids']
# 重建记录集对象
valid_packages = self.env['cc.ship.package'].browse(valid_package_ids)
file_info['valid_packages'] = valid_packages
_logger.info(f"反序列化时恢复valid_packages: 提单 {bl.bl_no}, 满足条件的小包ID: {valid_package_ids}, 数量: {len(valid_packages)}")
processed_files.append(file_info)
return processed_files
except Exception as e:
......
......@@ -5,7 +5,7 @@ from odoo import models, fields, api, _
import pytz
from datetime import datetime
_logger = logging.getLogger(__name__)
from odoo.exceptions import ValidationError
class BatchGetPodInfoWizard(models.TransientModel):
_inherit = 'batch.get.pod.info.wizard'
......@@ -93,7 +93,6 @@ class BatchGetPodInfoWizard(models.TransientModel):
except Exception as e:
_logger.error(f"验证提单 {bl.bl_no} 节点推送条件失败: {str(e)}")
continue
return valid_files
def _check_packages_same_status(self, ship_packages):
......@@ -106,24 +105,40 @@ class BatchGetPodInfoWizard(models.TransientModel):
if not ship_packages or len(ship_packages) <= 1:
return True
# 使用SQL批量查询所有小包的状态ID
# 使用SQL批量查询所有小包的不同状态(包括NULL)
package_ids = ship_packages.ids
# 直接查询不同的状态(DISTINCT),更高效
sql_query = """
SELECT DISTINCT state
FROM cc_ship_package
WHERE id IN %s AND state IS NOT NULL
WHERE id IN %s
"""
self.env.cr.execute(sql_query, (tuple(package_ids),))
results = self.env.cr.fetchall()
# 直接解包元组中的state值
distinct_states = [row[0] for row in self.env.cr.fetchall()]
unique_status_count = len(distinct_states)
# 检查是否有NULL状态
has_null = None in distinct_states
# 如果只有一个不同的状态ID,说明所有小包都是同一状态
unique_status_count = len(results)
_logger.info(f"提单小包状态检查: {len(package_ids)} 个小包,{unique_status_count} 种不同状态")
# 详细日志
_logger.info(f"提单小包状态检查: {len(package_ids)} 个小包,有 {unique_status_count} 种不同状态" if unique_status_count > 1 else f"提单小包状态检查: {len(package_ids)} 个小包,所有小包都是同一状态")
return unique_status_count <= 1
# 判断是否同一状态
# 如果只有1种状态且不是NULL,说明所有小包都是同一状态
if unique_status_count == 1 and not has_null:
return True
# 如果有多于1种状态,或者有NULL状态,都认为状态不一致
else:
return False
except Exception as e:
_logger.error(f"检查小包状态一致性失败: {str(e)}")
import traceback
_logger.error(traceback.format_exc())
# 即使检查失败,也返回False(状态不一致),继续执行后续的逐个验证逻辑,而不是中断整个流程
_logger.warning(f"状态检查异常,将逐个验证所有小包")
return False
def _get_package_operation_times(self, package_ids):
......@@ -152,7 +167,7 @@ class BatchGetPodInfoWizard(models.TransientModel):
MAX(n.operate_time) as latest_operation_time
FROM cc_ship_package p
LEFT JOIN cc_ship_package_sync_log n ON p.id = n.package_id
AND n.process_code = p.tk_code
AND n.process_code = (SELECT tk_code FROM cc_node WHERE id = p.state ORDER BY operate_time DESC LIMIT 1)
WHERE p.id IN %s
GROUP BY p.id
"""
......@@ -200,44 +215,64 @@ class BatchGetPodInfoWizard(models.TransientModel):
第一步:对比小包当前节点的操作时间是否小于提取时间(同时区对比)
第二步:如果是需要补推的,提取时间 − 小包当前节点的操作时间 < 前序间隔时间的不能推送
:param package: 小包对象
:param extract_time: 提取时间
:param extract_time: 提取时间(UTC时间)
:param bl_no: 提单号
:return: 是否满足推送条件
"""
try:
# 获取小包当前节点的操作时间
# 获取小包当前节点的操作时间(数据库中的时间,应该是UTC时间)
current_node_operation_time = self._get_package_operation_times(package.id)
#没有操作时间,代表没有推送过,可以推送
if not current_node_operation_time:
_logger.info(f"小包 {package.id} 没有操作时间,可以推送")
return True
# 记录详细信息用于调试
_logger.info(f"小包 {package.id} (状态: {package.state.name if package.state else 'None'}) 验证推送条件:")
_logger.info(f" - 提取时间(UTC): {extract_time}")
_logger.info(f" - 操作时间(UTC): {current_node_operation_time}")
# 第一步:对比小包当前节点的操作时间是否小于提取时间(同时区对比)
if current_node_operation_time >= extract_time:
_logger.warning(f"小包 {package.id} 当前节点操作时间 {current_node_operation_time} >= 提取时间 {extract_time},不满足推送条件")
_logger.warning(f"小包 {package.id} 当前节点操作时间 {current_node_operation_time} >= 提取时间 {extract_time},不满足推送条件(倒挂)")
return False
# 计算时间差
time_diff = extract_time - current_node_operation_time
time_diff_minutes = time_diff.total_seconds() / 60
_logger.info(f" - 时间差: {time_diff_minutes:.1f} 分钟")
# 第二步:如果是需要补推的,提取时间 − 小包当前节点的操作时间 < 前序间隔时间的不能推送
flag, match_node = self._need_supplement_push(package)
_logger.info(f" - 是否需要补推: {flag}, 匹配节点: {match_node.name if match_node else 'None'}")
# 如果需要补推,检查前序间隔时间
if flag:
interval_time = self._get_write_node_previous_interval_time(package, match_node)
if interval_time:
# 计算提取时间减去小包当前节点的操作时间
time_diff = extract_time - current_node_operation_time
# 将时间差转换为分钟数
time_diff_minutes = time_diff.total_seconds() / 60
# 将间隔时间转换为分钟数
interval_minutes = interval_time.total_seconds() / 60
_logger.info(f" - 前序间隔时间: {interval_minutes:.1f} 分钟")
_logger.info(f" - 时间差 {time_diff_minutes:.1f} 分钟 vs 前序间隔时间 {interval_minutes:.1f} 分钟")
# 检查:提取时间 − 小包当前节点的操作时间 < 前序间隔时间的不能推送
if time_diff_minutes < interval_minutes:
_logger.warning(f"小包 {package.id} 时间差 {time_diff_minutes:.1f} 分钟 < 前序间隔时间 {interval_minutes:.1f} 分钟,不满足补推条件")
_logger.warning(f"小包 {package.id} 时间差 {time_diff_minutes:.1f} 分钟 < 前序间隔时间 {interval_minutes:.1f} 分钟,不满足补推条件(倒挂)")
return False
else:
_logger.info(f"小包 {package.id} 时间差 {time_diff_minutes:.1f} 分钟 >= 前序间隔时间 {interval_minutes:.1f} 分钟,满足补推条件")
else:
_logger.warning(f"小包 {package.id} 需要补推但未获取到前序间隔时间,跳过补推检查")
else:
_logger.info(f"小包 {package.id} 不需要补推(当前状态: {package.state.name if package.state else 'None'}, 状态ID: {package.state.id if package.state else 'None'}),跳过前序间隔时间检查")
_logger.info(f"小包 {package.id} 满足推送条件")
return True
except Exception as e:
_logger.error(f"验证小包 {package.id} 推送条件失败: {str(e)}")
import traceback
_logger.error(traceback.format_exc())
return False
......@@ -245,22 +280,37 @@ class BatchGetPodInfoWizard(models.TransientModel):
"""
判断是否需要补推节点
:param package: 小包对象
:return: 是否需要补推
:return: (是否需要补推, POD节点对象)
"""
pod_node=False
pod_node = False
try:
# 查找对应的清关节点(勾选了POD节点匹配的节点)
pod_node = self.env['cc.node'].search([
('is_pod_node', '=', True),
('node_type', '=', 'package')
], limit=1)
#小包节点在这个节点之前,则需要补推节点
if pod_node:
return package.state.id < pod_node.id,pod_node
return False,pod_node
if not pod_node:
_logger.info(f"小包 {package.id} 未找到POD节点,不需要补推")
return False, pod_node
# 小包节点在这个节点之前,则需要补推节点
current_state_id = package.state.id if package.state else None
pod_node_id = pod_node.id
_logger.info(f"小包 {package.id} 补推判断: 当前状态ID={current_state_id}, POD节点ID={pod_node_id}, 需要补推={current_state_id < pod_node_id if current_state_id else False}")
if current_state_id:
need_push = current_state_id < pod_node_id
return need_push, pod_node
else:
_logger.warning(f"小包 {package.id} 没有状态,不需要补推")
return False, pod_node
except Exception as e:
_logger.error(f"判断小包 {package.id} 是否需要补推失败: {str(e)}")
return False,pod_node
import traceback
_logger.error(traceback.format_exc())
return False, pod_node
def _get_write_node_previous_interval_time(self, package, match_node):
"""
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论