提交 91994fc7 authored 作者: 贺阳's avatar 贺阳

5.自动改变关务提单状态,并生成同步日志,推送TK

检查逻辑: 提单节点上设置了对应小包状态,当提单所有的小包的同步日志都有该节点的同步日志时,则该提单自动改变状态;操作时间取小包对应操作时间最晚的一条; 如:节点上设置的提单节点为提单已提货,对应的小包状态为已提货;当提单a下所有小包,都有已提货的同步日志时,自动改变提单的关务提单状态,为已提货。操作时间取小包已提货同步日志最晚的一条;(实时) 并要求: 1.若提单节点的前序节点未产生同步日志,则需为其前序节点产生同步日志,操作时间根据设置的前序节点间距时间取值; 2.若小包变化需产生的关务提单节点为当前提单节点的前序节点,则不再产生同步日志,也不推送,且不改变提单节点状态; 3.之前检查提单完成的修改为 也要检查关务提单状态
上级 a742ea14
......@@ -13,6 +13,7 @@
<field optional="show" name="desc" string="Node Description"/>
<field optional="show" name="next_code_ids" widget="many2many_tags" options="{'no_create':True}"/>
<field optional="show" name="tally_state"/>
<field optional="show" name="package_state"/>
<field optional="show" name="is_must" string="Is Must Node"/>
<field optional="show" name="is_done" string="Is Done Node"/>
<field optional="show" name="is_default" string="Is Current Node"/>
......
......@@ -258,23 +258,92 @@ class CcBl(models.Model):
# 增加提单状态操作时间:取最新一条提单节点同步信息的操作时间
process_time = fields.Datetime(string='Process Time', compute='_compute_process_time', store=True)
def change_state_by_ship_package(self):
"""
根据小包的状态修改提单的状态
:return:
"""
# 如果提单有小包变成了清关开始,提单状态变为清关中
if self.state == 'draft' and self.ship_package_ids.filtered(
lambda line: line.state.tk_code == 'cb_imcustoms_start'):
self.ccing_func()
# 如果提单所有小包的清关节点变成"是完成节点",则该提单状态变成已完成
if all(line.state.is_done for line in
self.ship_package_ids) and self.unsync_package_count <= 0 and self.customs_clearance_status.is_done and self.is_bl_sync:
self.done_func()
# 提单节点为提单已提货,对应的小包状态为已提货,当提单a下所有小包,都有已提货的同步日志时,自动改变提单的关务提单状态,为已提货。操作时间取小包已提货同步日志最晚的一条;
def change_state_by_ship_package(self, state_code):
for item in self:
# 获取提单下所有小包
ship_packages = self.env['cc.ship.package'].search([('bl_id', '=', item.id), ('is_sync', '=', True)])
if ship_packages:
# 获取所有小包的已提货同步日志
sync_logs = ship_packages.mapped('sync_log_ids').filtered(lambda r: r.process_code == state_code)
if len(sync_logs) >= len(ship_packages):
item.customs_clearance_status = '已提货'
#同时检查已有关务提单节点的同步日志不再生成,若提单节点的前序节点未产生同步日志,则需为其前序节点产生同步日志,操作时间根据设置的前序节点间距时间取值;
# 检查提单的同步日志该节点是否已生成
if not item.bl_sync_log_ids.filtered(lambda r: r.process_code == state_code):
pass
item.process_time = sorted(sync_logs.mapped('operate_time'), reverse=True)[0] #需要优化
def change_customs_state_by_ship_package(self, package_state_obj):
"""
根据小包的状态修改提单关务状态以及生成同步日志
:param package_state_obj:小包更新后的状态
:return:
"""
# 根据小包的状态找到对应的提单关务状态
state_obj = self.env['cc.node'].search([('package_state', '=', package_state_obj.id)], limit=1)
for bl in self:
# 判断当前提单的关务提单状态是需要更新的前序节点,则进行修改和生成同步日志
if self.customs_clearance_status.is_before_node(state_obj):
# 判断提单下所有小包的同步日志是否都有已提货的同步日志
is_all = self.check_multiple_bl_pickup_status_best(package_state_obj)
if is_all:
# 操作时间取小包对应操作时间最晚的一条
operate_time = sorted(self.ship_package_ids.mapped('process_time'), reverse=True)[0]
# 检查该状态之前的所有节点是否都有同步日志,如果没有,则生成同步日志
before_node_obj = state_obj.get_before_node(node_type='bl')
is_ok = True
for before_node in before_node_obj:
if not bl.bl_sync_log_ids.filtered(lambda r: r.process_code == before_node.tk_code):
# 修改关务提单状态
bl.customs_clearance_status = before_node.id
# 操作时间根据设置的前序节点间距时间取值
before_minutes = before_node.calculate_total_interval(state_obj)
bl.process_time = datetime.strptime(str(operate_time), '%Y-%m-%d %H:%M:%S') - timedelta(
minutes=before_minutes)
# 调用同步关务提单状态
is_ok = bl.callback_track_bl()
if not is_ok:
break
if is_ok:
bl.customs_clearance_status = state_obj.id
bl.process_time = operate_time
# 调用同步提单状态
bl.callback_track_bl()
def check_multiple_bl_pickup_status_best(self, package_state_obj):
"""
最优方案: 使用单次search_count
"""
# 使用search_count直接统计符合条件的小包数量,判断和提单的小包数量是否相等
missing_log_count = self.env['cc.ship.package'].search_count([
('bl_id', '=', self.id),
('sync_log_ids.process_code', '=', package_state_obj.tk_code)
])
return missing_log_count == len(self.ship_package_ids)
def check_multiple_bl_pickup_status(self, package_state_obj):
"""检查单个提单下所有小包是否都有同步日志"""
sql = """
SELECT
CASE WHEN COUNT(DISTINCT sp.id) > 0 -- 确保有小包存在
AND COUNT(DISTINCT sp.id) = COUNT(DISTINCT CASE
WHEN bsl.process_code = '{0}'
THEN sp.id
END)
THEN TRUE
ELSE FALSE
END as all_picked_up
FROM cc_bl bl
JOIN cc_ship_package sp ON sp.bl_id = bl.id
LEFT JOIN cc_ship_package_sync_log bsl ON
bsl.package_id = sp.id AND
bsl.process_code = '{0}'
WHERE bl.id = {1}
""".format(package_state_obj.tk_code, self.id)
self.env.cr.execute(sql)
result = self.env.cr.fetchone()
return result and result[0] or False
# =============同步提单状态==================================
......@@ -310,6 +379,7 @@ class CcBl(models.Model):
responses = asyncio.run(bl_perform_requests())
for response_item in responses:
response_data = response_item[0]
logging.info('bl response_data:%s' % response_data)
data = response_item[1]
bl_id = response_item[2]
bl_order = self.env['cc.bl'].sudo().browse(bl_id)
......@@ -360,6 +430,9 @@ class CcBl(models.Model):
for item in self:
ship_packages = self.env['cc.ship.package'].search([('bl_id', '=', item.id), ('is_sync', '=', False)])
is_ok = item.package_callback_func(ship_packages.ids)
# 根据小包状态更新提单关务状态
if is_ok and self.ship_package_ids:
self.change_customs_state_by_ship_package(self.ship_package_ids[0].state)
return is_ok
def package_callback_func(self, ship_package_ids):
......@@ -385,9 +458,10 @@ class CcBl(models.Model):
# 在 Odoo 中运行异步任务
responses = asyncio.run(perform_requests())
# log_values=[]
for response_item in responses:
response_data = response_item[0]
logging.info('response_data response:%s' % response_data)
logging.info('ship response_data:%s' % response_data)
data = response_item[1]
package_id = response_item[2]
package_order = self.env['cc.ship.package'].sudo().browse(package_id)
......@@ -404,17 +478,75 @@ class CcBl(models.Model):
else:
# 回传成功
package_order.is_sync = True
self._cr.commit() # 提交事务
self.env['cc.ship.package.sync.log'].sudo().create_sync_log(
package_order.id, 'Tiktok', package_order.state.tk_code, package_order.state_explain,
package_order.process_time.strftime('%Y-%m-%d %H:%M:%S'))
self._cr.commit() # 提交事务
request_id = response_data['requestID']
self.env['ao.tt.api.log'].sudo().create_api_log(
package_order.tracking_no or '', '', data, 0, request_id, source='推出')
# log_values.append(
# (package_order.id,
# 'Tiktok',
# package_order.state.tk_code,
# package_order.state_explain or '',
# package_order.process_time.strftime('%Y-%m-%d %H:%M:%S')
# ))
# self.batch_create_package_sync_logs_by_sql(log_values)
# 如果提单有小包变成了清关开始,提单状态变为清关中;如果提单所有小包的清关节点变成"是完成节点",则该提单状态变成已完成
self.change_state_by_ship_package()
return is_ok
def batch_create_package_sync_logs_by_sql(self, log_values):
"""
批量创建小包同步日志 废弃,先不用
Args:
log_values: 日志值列表,每项包含
(package_id, api_customer, process_code, operate_remark, process_time)
"""
if not log_values:
return
sql = """
INSERT INTO cc_ship_package_sync_log (
create_uid,
create_date,
write_uid,
write_date,
package_id,
api_customer,
process_code,
operate_remark,
operate_time
) VALUES %s
"""
uid = self.env.uid
now = fields.Datetime.now()
# 准备批量插入的值
values = []
params = []
for val in log_values:
values.append("(%s, %s, %s, %s, %s, %s, %s, %s, %s)")
params.extend([
uid, now, uid, now,
val[0], # package_id
val[1], # platform
val[2], # process_code
val[3], # state_explain
val[4] # process_time
])
# 构建完整的SQL
sql = sql % ','.join(values)
logging.info('ship_package_sync_log sql:%s' % sql)
# 执行批量插入
self.env.cr.execute(sql, params)
self._cr.commit() # 提交事务
def deal_ship_package_state(self):
for item in self:
ship_packages = self.env['cc.ship.package'].search([('bl_id', '=', item.id), ('is_sync', '=', False)])
......@@ -491,8 +623,11 @@ class CcBl(models.Model):
is_ok = self.callback_track()
else:
is_ok = self.package_callback_func(ship_package_ids)
if is_ok:
return True
if is_ok:
# 根据小包状态更新提单关务状态
if self.ship_package_ids:
self.change_customs_state_by_ship_package(self.ship_package_ids[0].state)
return is_ok
logging.warning(f"Attempt {i + 1}/{max_retries} failed. Retrying...")
return False
......@@ -546,9 +681,7 @@ class CcBl(models.Model):
ship_packages_dict[single_id] = package['tally_time']
# 前序节点 理货或尾程交接之前没有生成的节点
before_node_obj = self.env['cc.node'].sudo().search([
('node_type', '=', 'package'), ('is_must', '=', True), ('seq', '<', node_obj[0].seq)],
order='seq asc')
before_node_obj = node_obj[0].get_before_node()
# 理货或尾程交接之前没有生成的节点
for before_node in before_node_obj:
print('before_node:%s' % before_node.name)
......@@ -591,20 +724,6 @@ class CcBl(models.Model):
self.env.cr.execute(sql)
self._cr.commit() # 提交事务
# # 更新提单的未同步小包数量
# sql = """
# UPDATE cc_bl AS bl SET
# unsync_package_count = (
# SELECT COUNT(*)
# FROM cc_ship_package sp
# WHERE sp.bl_id = bl.id
# AND sp.is_sync = false
# )
# WHERE bl.id = %s
# """
# self.env.cr.execute(sql, (item.id,))
# self._cr.commit() # 提交事务
self.try_callback_track(max_retries=2, ship_package_ids=ship_package_ids)
# 理货或尾程交接的节点
# 预先获取所有状态节点
......@@ -661,19 +780,6 @@ class CcBl(models.Model):
except Exception as err:
logging.error('fetch_mail_dlv--error:%s' % str(err))
def change_state_by_ship_package(self):
"""
根据小包的状态修改提单的状态
:return:
"""
# 如果提单有小包变成了清关开始,提单状态变为清关中
if self.state == 'draft' and self.ship_package_ids.filtered(
lambda line: line.state.tk_code == 'cb_imcustoms_start'):
self.ccing_func()
# 如果提单所有小包的清关节点变成"是完成节点",则该提单状态变成已完成
if all(line.state.is_done for line in self.ship_package_ids) and self.unsync_package_count <= 0:
self.done_func()
# 提单节点同步日志
class CcBlSyncLog(models.Model):
......
......@@ -287,7 +287,7 @@ class CcBl(models.Model):
responses = asyncio.run(perform_requests())
for response_item in responses:
response_data = response_item[0]
logging.info('response_data response:%s' % response_data)
logging.info('response_data:%s' % response_data)
data = response_item[1]
package_id = response_item[2]
package_order = self.env['cc.ship.package'].sudo().browse(package_id)
......
......@@ -287,7 +287,7 @@ class CcBl(models.Model):
responses = asyncio.run(perform_requests())
for response_item in responses:
response_data = response_item[0]
logging.info('response_data response:%s' % response_data)
logging.info('response_data:%s' % response_data)
data = response_item[1]
package_id = response_item[2]
package_order = self.env['cc.ship.package'].sudo().browse(package_id)
......
......@@ -12,6 +12,49 @@ class CCNode(models.Model):
interval_minutes = fields.Integer('Predecessor Node Interval (Minutes)', default=20,
help='Default interval time between predecessor nodes in minutes.') # 前序节点间隔时间,默认20分钟
def get_before_node(self, node_type='package'):
"""
获取当前状态的前序节点
"""
# 前序节点 该节点之前没有生成的节点
before_node_obj = self.env['cc.node'].sudo().search([
('node_type', '=', node_type), ('is_must', '=', True), ('seq', '<', self.seq)],
order='seq asc')
return before_node_obj
def is_before_node(self, target_state):
"""
判断当前状态是否在目标状态之前
Args:
target_state: 目标状态代码
Returns:
bool: 如果当前状态在目标状态之前返回True,否则返回False
"""
if not target_state:
return False
# 获取所有状态的顺序映射
states_seq = dict(
self.search([]).sorted('seq').mapped(lambda r: (r.tk_code, r.seq))
)
# 获取当前状态的序号
current_code = self.tk_code
current_seq = states_seq.get(current_code)
# 获取目标状态的序号
target_code = target_state.tk_code if isinstance(target_state, models.Model) else target_state
target_seq = states_seq.get(target_code)
# 如果任一状态不存在,返回False
if current_seq is None or target_seq is None:
return False
# 比较序号判断先后顺序
return current_seq < target_seq
def is_next_code(self, current_state_obj, next_state_id):
"""
判断更新的节点是否是 小包状态的下级节点
......
......@@ -51,7 +51,7 @@ class TT(models.Model):
timestamp = int(time.time())
sign = self.generate_sign(timestamp, push_data)
response = self.get_response(url, sign, timestamp, push_data)
logging.info('callback_track response:%s' % response)
# logging.info('callback_track response:%s' % response)
return response
def package_invoice_query(self, push_data):
......@@ -126,7 +126,7 @@ class TT(models.Model):
'app_key': app_key
}
request_url = tt_url + url
logging.info('request_url: %s' % request_url)
# logging.info('request_url: %s' % request_url)
# logging.info('request_data: %s' % parameter)
for i in range(3): # 尝试最多3次
try:
......@@ -150,7 +150,7 @@ class TT(models.Model):
timestamp = int(time.time())
sign = self.generate_sign(timestamp, push_data)
response = await self.async_get_response(session, url, sign, timestamp, push_data)
logging.info('callback_track response:%s' % response)
# logging.info('callback_track response:%s' % response)
return response, push_data, package_id
async def async_callback_track(self, session, data, package_id):
......@@ -164,7 +164,7 @@ class TT(models.Model):
timestamp = int(time.time())
sign = self.generate_sign(timestamp, push_data)
response = await self.async_get_response(session, url, sign, timestamp, push_data)
logging.info('bl_callback_track response:%s' % response)
# logging.info('bl_callback_track response:%s' % response)
return response, push_data, package_id
async def async_bl_callback_track(self, session, data, bl_id):
......
......@@ -24,14 +24,16 @@
<field name="model">cc.bl</field>
<field name="inherit_id" ref="ccs_base.form_cc_bl_view"/>
<field name="arch" type="xml">
<!-- # header之间增加一个按钮,调用自定义的方法-->
<header position="inside">
<button name="%(ccs_base.action_batch_input_ship_package_wizard)d" position="after">
<button name="callback_track" string="Sync Package Status" type="object"/>
</button>
<button name="%(ccs_base.action_batch_input_bl_status_wizard)d" position="before">
<button name="batch_action_sync" string="Sync CC Attachment" type="object"/>
</button>
<button name="%(ccs_base.action_batch_input_bl_status_wizard)d" position="after">
<!--增加同步提单状态的按钮-->
<button name="callback_track_bl" string="Sync Bill Of Loading Status" type="object"/>
</header>
</button>
<button name="action_show_ship_package" position="replace">
<button name="action_show_ship_package" type="object"
......
......@@ -163,4 +163,5 @@ class BatchInputShipPackageStatusWizard(models.TransientModel):
self.update_status.name or ''))
# 如果提单有小包变成了清关开始,提单状态变为清关中;如果提单所有小包的清关节点变成“是完成节点”,则该提单状态变成已完成
bl_obj.change_state_by_ship_package()
bl_obj.change_customs_state_by_ship_package(self.update_status)#根据小包状态更新提单关务状态
return obj
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论