提交 57151be6 authored 作者: 贺阳's avatar 贺阳

1、新增提货扫码和修改提货信息,尾程快递理货检查是否倒序,托盘理货检查是否倒序的接口开发

2、交货检查是否倒序的接口优化
上级 f16fec9f
......@@ -6,11 +6,11 @@ msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-22 01:56+0000\n"
"PO-Revision-Date: 2025-09-22 09:57+0800\n"
"POT-Creation-Date: 2025-09-23 09:28+0000\n"
"PO-Revision-Date: 2025-09-23 17:30+0800\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: zh_CN\n"
"Language: zh\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
......@@ -173,6 +173,11 @@ msgstr "<span class=\"o_stat_text\">已交货大包</span>"
msgid "<span class=\"o_stat_text\">Goods</span>"
msgstr "<span class=\"o_stat_text\">货物</span>"
#. module: ccs_base
#: model_terms:ir.ui.view,arch_db:ccs_base.form_cc_bl_view
msgid "<span class=\"o_stat_text\">Picked Up Big Packages</span>"
msgstr "<span class=\"o_stat_text\">已提货大包</span>"
#. module: ccs_base
#: model_terms:ir.ui.view,arch_db:ccs_base.form_cc_big_package_view
#: model_terms:ir.ui.view,arch_db:ccs_base.form_cc_bl_view
......@@ -900,35 +905,6 @@ msgstr "清关重量"
msgid "Checked goods"
msgstr "已理货"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_big_package__pickup_user_id
#: model:ir.model.fields,field_description:ccs_base.field_cc_history_big_package__pickup_user_id
msgid "Pickup User"
msgstr "提货人"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_big_package__pickup_time
#: model:ir.model.fields,field_description:ccs_base.field_cc_history_big_package__pickup_time
msgid "Pickup Time"
msgstr "提货时间"
#. module: ccs_base
#: model:ir.model.fields.selection,name:ccs_base.selection__cc_big_package__tally_state__picked_up
#: model:ir.model.fields.selection,name:ccs_base.selection__cc_history_big_package__tally_state__picked_up
#: model_terms:ir.ui.view,arch_db:ccs_base.search_cc_big_package_view
msgid "Picked Up"
msgstr "已提货"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_bl__picked_up_big_package_qty
msgid "Picked Up Big Package Qty"
msgstr "已提货大包数量"
#. module: ccs_base
#: model:ir.actions.act_window,name:ccs_base.action_show_big_package_picked_up
msgid "Picked Up Big Package"
msgstr "已提货大包"
#. module: ccs_base
#. odoo-python
#: code:addons/ccs_base/wizard/add_exception_info_wizard.py:0
......@@ -2574,6 +2550,39 @@ msgstr "托盘号"
msgid "Pallet Usage Date"
msgstr "托盘使用日期"
#. module: ccs_base
#: model:ir.model.fields.selection,name:ccs_base.selection__cc_big_package__tally_state__picked_up
#: model_terms:ir.ui.view,arch_db:ccs_base.search_cc_big_package_view
msgid "Picked Up"
msgstr "已提货"
#. module: ccs_base
#. odoo-python
#: code:addons/ccs_base/models/cc_bill_loading.py:0
#, python-format
msgid "Picked Up Big Package"
msgstr "已提货大包"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_bl__picked_up_big_package_qty
msgid "Picked Up Big Package Qty"
msgstr "已提货大包数量"
#. module: ccs_base
#: model:ir.model.fields.selection,name:ccs_base.selection__cc_node__tally_state__picked_up
msgid "Picked up"
msgstr "已提货"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_big_package__pickup_time
msgid "Pickup Time"
msgstr "提货时间"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_big_package__pickup_user_id
msgid "Pickup User"
msgstr "提货人"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_last_mile_provider__placement_area
msgid "Placement Area"
......
......@@ -873,24 +873,24 @@ class CcBL(models.Model):
'domain': [('bl_id', '=', self.id), ('is_cancel', '=', False)],
}
def action_show_big_package_tally(self):
# 返回一个action,显示已货的大包
def action_show_big_package_picked_up(self):
# 返回一个action,显示已货的大包
return {
'name': _('Tally Big Package'),
'name': _('Picked Up Big Package'),
'type': 'ir.actions.act_window',
'res_model': 'cc.big.package',
'view_mode': 'tree,form',
'domain': [('bl_id', '=', self.id), ('is_cancel', '=', False), ('tally_state', '=', 'checked_goods')],
'domain': [('bl_id', '=', self.id), ('is_cancel', '=', False), ('tally_state', '=', 'picked_up')],
}
def action_show_big_package_picked_up(self):
# 返回一个action,显示已货的大包
def action_show_big_package_tally(self):
# 返回一个action,显示已货的大包
return {
'name': _('Picked Up Big Package'),
'name': _('Tally Big Package'),
'type': 'ir.actions.act_window',
'res_model': 'cc.big.package',
'view_mode': 'tree,form',
'domain': [('bl_id', '=', self.id), ('is_cancel', '=', False), ('tally_state', '=', 'picked_up')],
'domain': [('bl_id', '=', self.id), ('is_cancel', '=', False), ('tally_state', '=', 'checked_goods')],
}
def action_show_big_package_delivered(self):
......
......@@ -43,6 +43,7 @@ class CcNode(models.Model):
tally_state = fields.Selection([
('unprocessed_goods', 'Unprocessed goods'),
('picked_up', 'Picked up'),
('checked_goods', 'Checked goods'),
('handover_completed', 'Handover Completed')
], default='', string='Corresponding to the status of the big package', index=True) # 对应大包状态 未理货/已理货/尾程交接
......
......@@ -105,10 +105,18 @@ class OrderController(http.Controller):
bl_obj = request.env['cc.bl'].sudo().deal_bl_no_and_transfer_bl_no(
bl_no) # 提单号去掉杠和空格,并转换为小写,优先匹配提单号,匹配不到则匹配转单号
if bl_obj:
# 1.按提单交货时,创建批次,填写的提单号传给后台查找对应的提单号,在匹配查找前,先查找是否pda扫码记录,
# 1.按提单理货时,检查是否存在时间倒序风险
if action_type == 'tally':
# 检查是否存在时间倒序风险
time_check_result = self._check_tally_time_risk(bl_obj, pda_lang)
if time_check_result['has_risk']:
res['state'] = 400
res['message'] = time_check_result['message']
return res
# 2.按提单交货时,创建批次,填写的提单号传给后台查找对应的提单号,在匹配查找前,先查找是否pda扫码记录,
# 若有类型该提单,类型为理货的,且操作时间-当前时间小于等于配置的参数(交货操作晚于提货操作X分钟),则提示:该提单未到交货时间,有风险产生倒叙,请间隔规定时间后再扫码;
#2025-09-23 新增PDA交货时,提示:该提单未到交货时间,有风险产生倒叙,请间隔规定时间后再扫码;此处判断需加上查找提货记录;
if action_type == 'handover':
# 2025-09-23 新增PDA交货时,提示:该提单未到交货时间,有风险产生倒叙,请间隔规定时间后再扫码;此处判断需加上查找提货记录;
elif action_type == 'handover':
# 检查是否存在时间倒序风险
time_check_result = self._check_delivery_time_risk(bl_obj, pda_lang)
if time_check_result['has_risk']:
......@@ -152,8 +160,9 @@ class OrderController(http.Controller):
kwargs = json.loads(request.httprequest.data)
pda_lang = kwargs.get('pda_lang') or 'zh'
res = {'state': 201, 'message': ''}
action_type = kwargs.get('action_type') or 'tally' # tally / handover
operation = 'bill_tally' if action_type == 'tally' else 'bill_handover'
action_type = kwargs.get('action_type') or 'tally' # tally / handover/pickup
operation = 'bill_tally' if action_type == 'tally' else (
'bill_handover' if action_type == 'handover' else 'bill_pickup')
bl_obj = False
tally_user_id = False # 理货人ID
try:
......@@ -204,7 +213,9 @@ class OrderController(http.Controller):
package_obj.update_exception_info(
exception_cause_ids) # 修改异常信息
tally_time = package_item.get('tally_time')
if (action_type == 'tally' and package_item.get('tally_state') == 'checked_goods') or (
if (action_type == 'pickup' and package_item.get('tally_state') == 'picked_up') or (
action_type == 'tally' and package_item.get(
'tally_state') == 'checked_goods') or (
action_type == 'handover' and package_item.get(
'tally_state') == 'handover_completed'):
if package_type == 'ship':
......@@ -215,9 +226,11 @@ class OrderController(http.Controller):
else:
for package in package_obj:
if (
action_type == 'tally' and package.tally_state == 'unprocessed_goods') or (
action_type == 'pickup' and package.tally_state == 'unprocessed_goods') or (
action_type == 'tally' and package.tally_state in (
'unprocessed_goods', 'picked_up')) or (
action_type == 'handover' and package.tally_state in (
'unprocessed_goods', 'checked_goods')):
'unprocessed_goods', 'checked_goods', 'picked_up')):
ship_packages.append({
'id': package.ship_package_ids.ids,
'bl_id': package.bl_id.id,
......@@ -360,8 +373,9 @@ class OrderController(http.Controller):
kwargs = json.loads(request.httprequest.data)
pda_lang = kwargs.get('pda_lang') or 'zh'
res = {'state': 201, 'message': ''}
action_type = kwargs.get('action_type') or 'tally' # tally / handover
operation = 'tail_tally' if action_type == 'tally' else 'tail_handover'
action_type = kwargs.get('action_type') or 'tally' # tally / handover/pickup
operation = 'tail_tally' if action_type == 'tally' else (
'tail_handover' if action_type == 'handover' else 'tail_pickup')
tally_user_id = False # 理货人ID
ship_packages = []
try:
......@@ -400,7 +414,8 @@ class OrderController(http.Controller):
package_obj.update_exception_info(
exception_cause_ids) # 修改异常信息
tally_time = package_item.get('tally_time')
if (action_type == 'tally' and package_item.get('tally_state') == 'checked_goods') or (
if (action_type == 'pickup' and package_item.get('tally_state') == 'picked_up') or (
action_type == 'tally' and package_item.get('tally_state') == 'checked_goods') or (
action_type == 'handover' and package_item.get(
'tally_state') == 'handover_completed'):
if package_type == 'ship':
......@@ -411,9 +426,11 @@ class OrderController(http.Controller):
else:
for package in package_obj:
if (
action_type == 'tally' and package.tally_state == 'unprocessed_goods') or (
action_type == 'pickup' and package.tally_state == 'unprocessed_goods') or (
action_type == 'tally' and package.tally_state in (
'unprocessed_goods', 'checked_goods', 'picked_up')) or (
action_type == 'handover' and package.tally_state in (
'unprocessed_goods', 'checked_goods')):
'unprocessed_goods', 'checked_goods', 'picked_up')):
ship_packages.append({
'id': package.ship_package_ids.ids,
'bl_id': package.bl_id.id,
......@@ -645,6 +662,37 @@ class OrderController(http.Controller):
logging.info('last_mile_delivery_time_check res:%s' % res)
return res
@http.route('/api/last_mile/tally/time_check', type='json', auth='public', csrf=False)
def last_mile_tally_time_check(self):
"""
尾程快递理货检查时间是否正常
"""
kwargs = json.loads(request.httprequest.data)
pda_lang = kwargs.get('pda_lang') or 'zh'
res = {'state': 201, 'message': ''}
try:
logging.info('last_mile_tally_time_check kwargs:%s' % kwargs)
all_bl_ids_in_request = kwargs['bl_ids']
logging.info(f"all_bl_ids_in_request: {all_bl_ids_in_request}")
if all_bl_ids_in_request:
bl_objs = request.env['cc.bl'].sudo().search([('id', 'in', all_bl_ids_in_request)])
for bl_obj in bl_objs:
time_check_result = self._check_tally_time_risk(bl_obj, pda_lang)
if time_check_result['has_risk']:
res['state'] = 400
res['message'] = time_check_result['message']
return res
res['state'] = 200
except Exception as e:
exceptions_msg_dic = {
'en': 'System parsing error, the reason for the error is %s' % e,
'zh': '系统解析错误,错误原因是%s' % e
}
logging.info('last_mile_tally_time_check error:%s' % e)
res['message'] = exceptions_msg_dic[pda_lang]
logging.info('last_mile_tally_time_check res:%s' % res)
return res
def _get_last_mile_grouped(self, tally_state, pda_lang, is_pallet=False):
lang = 'zh_CN' if pda_lang == 'zh' else 'en_US' # 语言
# if is_pallet:
......@@ -696,6 +744,60 @@ class OrderController(http.Controller):
return {'provider_info_arr': provider_info_arr, 'state': 200,
'bl_ids': list(set(list(map(lambda x: x.bl_id.id, big_packages))))}
def _check_tally_time_risk(self, bl_obj, pda_lang='zh', pallet_name=None):
"""
检查提单理货时间倒序风险(需要先有提货记录)
:param bl_obj: 提单对象
:param pda_lang: 语言设置
:param pallet_name: 托盘号
:return: 检查结果字典
"""
# 获取配置的理货操作晚于提货操作的时间参数(分钟)
tally_interval_time = request.env['ir.config_parameter'].sudo().get_param('tally_interval_time', '80')
allowed_minutes = int(tally_interval_time)
# 首先检查是否有提货记录
pickup_records = request.env['pda.scan.record'].sudo().search([
('bl_id', '=', bl_obj.id),
('record_type', '=', 'pickup'), # 提货类型
('state', '=', 'success') # 成功状态
], order='operation_time desc', limit=1)
if pickup_records:
# 有提货记录,检查时间间隔
latest_pickup_record = pickup_records[0]
current_time = request.env['common.common'].sudo().get_utc_time(datetime.now())
operation_time = latest_pickup_record.operation_time
time_diff = (datetime.strptime(current_time, '%Y-%m-%d %H:%M:%S') - operation_time).total_seconds() / 60
logging.info(f"current_time: {current_time}, pickup_time: {operation_time}, time_diff: {time_diff}")
# 如果时间差小于等于配置的参数,则存在风险
if time_diff <= allowed_minutes:
if pallet_name:
if pda_lang == 'en':
message = f"The pallet {pallet_name} has not reached the tally time, and there is a risk of flashback. Please scan the code after {allowed_minutes} minutes."
else:
message = f"该托盘号{pallet_name}装载的提单未到理货上传时间,有风险产生倒叙,请间隔{allowed_minutes}分钟后再扫码;"
else:
if pda_lang == 'en':
message = f"The bill of lading has not reached the tally time, and there is a risk of flashback. Please scan the code after {allowed_minutes} minutes."
else:
message = f"该提单%s未到理货上传时间,有风险产生倒叙,请间隔{allowed_minutes}分钟后再扫码;" % bl_obj.bl_no
return {
'has_risk': True,
'message': message,
'time_diff': time_diff,
'allowed_minutes': allowed_minutes
}
return {
'has_risk': False,
'message': '',
'time_diff': 0,
'allowed_minutes': allowed_minutes
}
def _check_delivery_time_risk(self, bl_obj, pda_lang='zh', pallet_name=None):
"""
检查提单交货时间倒序风险
......@@ -704,23 +806,39 @@ class OrderController(http.Controller):
:param pallet_name: 托盘号
:return: 检查结果字典
"""
# 获取配置的交货操作晚于提货操作的时间参数(分钟)
config_param = request.env['ir.config_parameter'].sudo().get_param('delivery_time', '80')
allowed_minutes = int(config_param)
# 获取配置的时间参数(分钟)
tally_interval_time = int(
request.env['ir.config_parameter'].sudo().get_param('tally_interval_time', '80')) # 理货间隔时间
delivery_time = int(request.env['ir.config_parameter'].sudo().get_param('delivery_time', '80')) # 交货间隔时间
# 查找该提单的PDA扫码记录,类型为理货的
pda_records = request.env['pda.scan.record'].sudo().search([
tally_records = request.env['pda.scan.record'].sudo().search([
('bl_id', '=', bl_obj.id),
('record_type', 'in', ['tally', 'pickup']), # 理货类型/提货类型
('record_type', '=', 'tally'), # 只查找理货类型
('state', '=', 'success') # 成功状态
], order='operation_time desc', limit=1)
if pda_records:
latest_tally_record = pda_records[0]
# 查找该提单的PDA扫码记录,类型为提货的
pickup_records = request.env['pda.scan.record'].sudo().search([
('bl_id', '=', bl_obj.id),
('record_type', '=', 'pickup'), # 只查找提货类型
('state', '=', 'success') # 成功状态
], order='operation_time desc', limit=1)
# 确定使用哪个记录和间隔时间
allowed_minutes = 0
if pickup_records:
# 有提货记录,先提货再理货,使用提货记录,间隔时间 = 交货间隔时间 + 理货间隔时间
latest_record = pickup_records[0]
allowed_minutes = tally_interval_time
if tally_records:
# 没有提货记录,按理货记录,使用理货记录,间隔时间 = 交货间隔时间
latest_record = tally_records[0]
allowed_minutes += delivery_time
if latest_record:
current_time = request.env['common.common'].sudo().get_utc_time(datetime.now())
# Ensure operation_time is also timezone-aware (UTC)
operation_time = latest_tally_record.operation_time
operation_time = latest_record.operation_time
time_diff = (datetime.strptime(current_time, '%Y-%m-%d %H:%M:%S') - operation_time).total_seconds() / 60
logging.info(f"current_time: {current_time}, operation_time: {operation_time},time_diff: {time_diff}")
# 如果时间差小于等于配置的参数,则存在风险
if time_diff <= allowed_minutes:
if pallet_name:
......@@ -1148,3 +1266,187 @@ class OrderController(http.Controller):
res['message'] = exceptions_msg_dic[pda_lang]
logging.info('pallet_delivery_time_check res:%s' % res)
return res
@http.route('/api/pallet/tally/time_check', type='json', auth='public', csrf=False)
def pallet_tally_time_check(self):
"""
托盘理货检查时间是否正常
"""
kwargs = json.loads(request.httprequest.data)
pda_lang = kwargs.get('pda_lang') or 'zh'
res = {'state': 201, 'message': ''}
try:
logging.info('pallet_tally_time_check kwargs:%s' % kwargs)
all_pallet_ids_in_request = kwargs['pallet_ids']
if all_pallet_ids_in_request:
pallet_objs = request.env['cc.pallet'].sudo().search([('id', 'in', all_pallet_ids_in_request)])
for pallet_obj in pallet_objs:
bl_objs = pallet_obj.package_ids.mapped('bl_id')
for bl_obj in bl_objs:
time_check_result = self._check_tally_time_risk(bl_obj, pda_lang, pallet_obj.name)
if time_check_result['has_risk']:
res['state'] = 400
res['message'] = time_check_result['message']
return res
res['state'] = 200
except Exception as e:
exceptions_msg_dic = {
'en': 'System parsing error, the reason for the error is %s' % e,
'zh': '系统解析错误,错误原因是%s' % e
}
logging.info('pallet_tally_time_check error:%s' % e)
res['message'] = exceptions_msg_dic[pda_lang]
logging.info('pallet_tally_time_check res:%s' % res)
return res
@http.route('/api/bl/pickup/info', type='json', auth='public', methods=['GET', 'POST'], csrf=False)
def bl_pickup_info(self):
"""
按提单提货查询接口
返回所有状态为未开始和清关中,且提单的关务节点状态不是已完成节点,
关务节点关联的小包状态,小包状态关联的大包状态不是已提货/已理货/尾程交接的提单信息
"""
kwargs = json.loads(request.httprequest.data)
pda_lang = kwargs.get('pda_lang') or 'zh'
res = {'state': 201, 'message': ''}
try:
logging.info('bl_pickup_info kwargs:%s' % kwargs)
# 查询符合条件的提单
# 1. 状态为未开始和清关中
# 2. 提单的关务节点状态不是已完成节点
bl_domain = [('state', 'in', ['draft', 'ccing']), ('customs_clearance_status.is_done', '=', False), (
'customs_clearance_status.package_state.tally_state', 'not in',
['picked_up', 'checked_goods', 'handover_completed'])]
bl_objs = request.env['cc.bl'].sudo().search(bl_domain)
if bl_objs:
res['bl_arr'] = [bl_obj.search_bl_info(pda_lang=pda_lang, type='pickup', is_all=False) for bl_obj in
bl_objs]
res['state'] = 200
else:
res['message'] = {
'en': 'No bills of lading found that meet the pickup criteria',
'zh': '没有找到符合提货条件的提单'
}[pda_lang]
except Exception as e:
exceptions_msg_dic = {
'en': 'System parsing error, the reason for the error is %s' % e,
'zh': '系统解析错误,错误原因是%s' % e
}
logging.info('bl_pickup_info error:%s' % e)
res['message'] = exceptions_msg_dic[pda_lang]
logging.info('bl_pickup_info res:%s' % res)
return res
@http.route('/api/bl/pickup/update', type='json', auth='public', methods=['GET', 'POST'], csrf=False)
def bl_pickup_update(self):
"""
按提单提货修改提货信息接口
请求参数是字典数组,包含了提单号和提货时间和提货人
回写提单下所有未理货的大包的理货状态和提货人,提货时间
且根据大包的状态回写对应小包的状态
"""
kwargs = json.loads(request.httprequest.data)
pda_lang = kwargs.get('pda_lang') or 'zh'
res = {'state': 201, 'message': ''}
state = 'picked_up' # 提货状态
operation = 'bill_pickup' # 操作
action_type = 'pickup' # 记录类型
tally_user_id = False # 提货人
try:
logging.info('bl_pickup_update kwargs:%s' % kwargs)
bl_arr = kwargs.get('bl_arr', [])
if not bl_arr:
res['message'] = {
'en': 'bl_arr is required',
'zh': '提货数据bl_arr必须提供'
}[pda_lang]
return res
error_messages = []
ship_packages = [] # 需要更新的小包
# 取第一个提货人的id
tally_user_id = bl_arr[0].get('tally_user_id')
for pickup_item in bl_arr:
bl_no = pickup_item.get('bl_no')
pickup_time = pickup_item.get('tally_time') # 提货时间
pickup_user_id = pickup_item.get('tally_user_id') # 提货人
if not all([bl_no, pickup_time, pickup_user_id]):
error_messages.append(f"提单号{bl_no}缺少必要参数")
continue
# 查找提单
bl_obj = request.env['cc.bl'].sudo().deal_bl_no_and_transfer_bl_no(bl_no)
if not bl_obj:
error_messages.append(f"提单号{bl_no}不存在")
continue
# 查找提单下所有未理货的大包
big_packages = bl_obj.big_package_ids.filtered(lambda x: x.tally_state == 'unprocessed_goods')
logging.info(f"big_packages: {big_packages}")
if big_packages:
# 更新大包提货信息
big_packages.update_big_package_info(action_type='pickup', tally_state=state,
tally_user_id=pickup_user_id, tally_time=pickup_time)
ship_packages.append({
'id': big_packages.ship_package_ids.ids,
'bl_id': bl_obj.id,
'tally_time': pickup_time
})
res['state'] = 200
# 创建成功的pda扫码记录
request.env['pda.scan.record'].sudo().create_scan_record(
operation=operation,
record_type=action_type,
bill_number=bl_obj.bl_no,
transfer_number=bl_obj.transfer_bl_no,
state='success',
bl_id=bl_obj.id,
operation_time=pickup_time,
operator_id=tally_user_id)
logging.info(
'update_big_package_tally_detail ship_packages:%s' % len(ship_packages))
# 有小包 就更新小包状态和同步
if ship_packages:
redis_conn = request.env['common.common'].sudo(
).get_redis()
if redis_conn and redis_conn != 'no':
# redis_conn.lpush('push_ship_package_state', json.dumps(
# {'bl_id': bl_obj.id, 'ship_package_ids': ship_package_ids}))
redis_conn.lpush('mail_push_package_list', json.dumps(
{'id': bl_obj.id, 'ship_packages': str(ship_packages), 'action_type': action_type}))
if error_messages:
res['message'] = ';'.join(error_messages)
except Exception as e:
exceptions_msg_dic = {
'en': 'System parsing error, the reason for the error is %s' % e,
'zh': '系统解析错误,错误原因是%s' % e
}
logging.info('bl_pickup_update error:%s' % e)
res['message'] = exceptions_msg_dic[pda_lang]
# 创建失败的pda扫码记录
if ship_packages:
bl_obj = request.env['cc.bl'].sudo().search(
[('id', 'in', [ship_package.get('bl_id') for ship_package in ship_packages])], limit=1)
if bl_obj:
for bl in bl_obj:
request.env['pda.scan.record'].sudo().create_scan_record(
operation=operation,
record_type=action_type,
bill_number=bl.bl_no,
transfer_number=bl.transfer_bl_no,
state='failed',
bl_id=bl.id,
failure_reason=e,
operator_id=tally_user_id)
else:
request.env['pda.scan.record'].sudo().create_scan_record(
operation=operation,
record_type=action_type,
bill_number='',
transfer_number='',
state='failed',
failure_reason=e,
operator_id=tally_user_id
)
logging.info('bl_pickup_update res:%s' % res)
return res
......@@ -6,8 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-09-22 01:59+0000\n"
"PO-Revision-Date: 2025-09-22 10:00+0800\n"
"POT-Creation-Date: 2025-09-23 09:31+0000\n"
"PO-Revision-Date: 2025-09-23 17:35+0800\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: zh_CN\n"
......@@ -95,6 +95,14 @@ msgstr "提单号"
msgid "Bill Of Loading Sync Logs"
msgstr "关务提单状态同步日志"
#. module: ccs_connect_tiktok
#. odoo-python
#: code:addons/ccs_connect_tiktok/models/pda_scan_record.py:0
#: model:ir.model.fields.selection,name:ccs_connect_tiktok.selection__pda_scan_record__operation__bill_pickup
#, python-format
msgid "Bill Pickup"
msgstr "按提单提货"
#. module: ccs_connect_tiktok
#. odoo-python
#: code:addons/ccs_connect_tiktok/models/pda_scan_record.py:0
......@@ -388,6 +396,14 @@ msgstr "托盘"
msgid "Pallet Handover"
msgstr "托盘交货"
#. module: ccs_connect_tiktok
#. odoo-python
#: code:addons/ccs_connect_tiktok/models/pda_scan_record.py:0
#: model:ir.model.fields.selection,name:ccs_connect_tiktok.selection__pda_scan_record__operation__pallet_pickup
#, python-format
msgid "Pallet Pickup"
msgstr "托盘提货"
#. module: ccs_connect_tiktok
#. odoo-python
#: code:addons/ccs_connect_tiktok/models/pda_scan_record.py:0
......@@ -396,6 +412,14 @@ msgstr "托盘交货"
msgid "Pallet Tally"
msgstr "托盘理货"
#. module: ccs_connect_tiktok
#. odoo-python
#: code:addons/ccs_connect_tiktok/models/pda_scan_record.py:0
#: model:ir.model.fields.selection,name:ccs_connect_tiktok.selection__pda_scan_record__record_type__pickup
#, python-format
msgid "Pickup"
msgstr "提货"
#. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_node__interval_minutes
msgid "Predecessor Node Interval (Minutes)"
......@@ -541,6 +565,14 @@ msgstr "进度编码"
msgid "Tail Handover"
msgstr "按尾程交货"
#. module: ccs_connect_tiktok
#. odoo-python
#: code:addons/ccs_connect_tiktok/models/pda_scan_record.py:0
#: model:ir.model.fields.selection,name:ccs_connect_tiktok.selection__pda_scan_record__operation__tail_pickup
#, python-format
msgid "Tail Pickup"
msgstr ""
#. module: ccs_connect_tiktok
#. odoo-python
#: code:addons/ccs_connect_tiktok/models/pda_scan_record.py:0
......@@ -685,7 +717,7 @@ msgstr ""
#. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_res_config_settings__delivery_time
msgid "交货操作晚于提货操作X分钟"
msgid "交货间隔时间"
msgstr ""
#. module: ccs_connect_tiktok
......@@ -901,6 +933,11 @@ msgstr ""
msgid "状态"
msgstr ""
#. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_res_config_settings__tally_interval_time
msgid "理货间隔时间"
msgstr ""
#. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log__source
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_ao_tt_api_log_view
......
......@@ -1169,22 +1169,28 @@ class CcBl(models.Model):
'domain': [('bl_id', '=', self.id), ('is_sync', '=', False)],
}
def search_bl_info(self, pda_lang=False, type='tally'):
def search_bl_info(self, pda_lang=False, type='tally', is_all=True):
"""
查询提单信息
"""
vals = {
'bl_id': self.id, # 提单id
'bl_no': self.bl_no or '', # 提单号
'scan_big_package_qty': self.tally_big_package_qty + self.delivered_big_package_qty if type == 'tally' else self.delivered_big_package_qty,
# 已扫大包数量
'big_package_arr': [big_package_item.search_big_package_info(pda_lang=pda_lang, type=type) for
big_package_item in
self.big_package_ids],
# 大包信息
'ship_package_arr': [ship_package_item.search_ship_package_info(pda_lang=pda_lang) for ship_package_item in
self.ship_package_ids], # 小包信息
'pallet_arr': self.get_unique_pallet_info(), # 托盘信息
}
if is_all:
vals.update({
'scan_big_package_qty': self.tally_big_package_qty + self.delivered_big_package_qty if type == 'tally' else self.delivered_big_package_qty,
# 已扫大包数量
'big_package_arr': [big_package_item.search_big_package_info(pda_lang=pda_lang, type=type) for
big_package_item in
self.big_package_ids],
# 大包信息
'ship_package_arr': [ship_package_item.search_ship_package_info(pda_lang=pda_lang) for ship_package_item
in
self.ship_package_ids], # 小包信息
'pallet_arr': self.get_unique_pallet_info(), # 托盘信息
})
return vals
def get_unique_pallet_info(self):
......@@ -1243,7 +1249,8 @@ class CcBl(models.Model):
user_obj = self.env['res.users'].search([('login', '=', pda_db_user)], limit=1)
ship_package_ids = [ship_package_dict for sublist in [d['id'] for d in ship_packages] for
ship_package_dict in sublist]
tally_state = 'checked_goods' if action_type == 'tally' else 'handover_completed'
tally_state = 'checked_goods' if action_type == 'tally' else (
'picked_up' if action_type == 'pickup' else 'handover_completed')
# 后续节点
node_obj = self.env['cc.node'].sudo().search([
('node_type', '=', 'package'),
......@@ -1444,12 +1451,15 @@ class CcBigPackage(models.Model):
vals = {
'tally_state_label': state_arr[self.tally_state] or '', # 理货状态显示名称
'tally_state': self.tally_state or '', # 理货状态系统KEY
'tally_user_id': (self.tally_user_id.id or 0) if type == 'tally' else ((self.delivery_user_id.id or 0) if type == 'handover' else (self.pickup_user_id.id or 0)),
'tally_user_id': (self.tally_user_id.id or 0) if type == 'tally' else (
(self.delivery_user_id.id or 0) if type == 'handover' else (self.pickup_user_id.id or 0)),
# 理货人id/交货人id/提货人id
'tally_user_name': (self.tally_user_id.name or '') if type == 'tally' else ((
self.delivery_user_id.name or '') if type == 'handover' else (self.pickup_user_id.name or '')),
self.delivery_user_id.name or '') if type == 'handover' else (
self.pickup_user_id.name or '')),
# 理货人名称/交货人名称/提货人名称
'tally_time': (self.tally_time or '') if type == 'tally' else ((self.delivery_time or '') if type == 'handover' else (self.pickup_time or '')),
'tally_time': (self.tally_time or '') if type == 'tally' else (
(self.delivery_time or '') if type == 'handover' else (self.pickup_time or '')),
# 理货时间/交货时间/提货时间
'big_package_no': self.big_package_no or '', # 大包号
'next_service_provider_name': self.next_provider_name or '', # 下一个服务商名称
......@@ -1467,13 +1477,13 @@ class CcBigPackage(models.Model):
action_type = kwargs.get('action_type')
for item in self:
if action_type == 'tally' and item.tally_state == 'unprocessed_goods':
if action_type == 'tally' and item.tally_state in ('unprocessed_goods', 'picked_up'):
# 更新理货信息
self._update_info(item, kwargs, 'tally')
elif action_type == 'handover' and item.tally_state != 'handover_completed':
# 更新交接信息
self._update_info(item, kwargs, 'handover')
elif action_type == 'pickup' and item.tally_state == 'picked_up':
elif action_type == 'pickup' and item.tally_state == 'unprocessed_goods':
# 更新提货信息
self._update_info(item, kwargs, 'pickup')
......
# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
class PDAScanRecord(models.Model):
......@@ -19,31 +18,29 @@ class PDAScanRecord(models.Model):
elif 'pickup' in self.operation:
self.record_type = 'pickup'
elif 'handover' in self.operation:
self.record_type = 'handover'
self.record_type = 'handover'
operator_id = fields.Many2one('res.users', string='操作人', required=True)
operation_time = fields.Datetime(string='操作时间', required=True, default=fields.Datetime.now)
operation = fields.Selection([
('bill_tally', _('Bill Tally')), # 按提单理货
('tail_tally', _('Tail Tally')), # 按尾程理货
('bill_tally', _('Bill Tally')), # 按提单理货
('tail_tally', _('Tail Tally')), # 按尾程理货
('pallet_tally', _('Pallet Tally')), # 按托盘理货
('bill_pickup', _('Bill Pickup')), # 按提单提货
('tail_pickup', _('Tail Pickup')), # 按尾程提货
('pallet_pickup', _('Pallet Pickup')), # 按托盘提货
('bill_handover', _('Bill Handover')), # 按提单交货
('tail_handover', _('Tail Handover')), # 按尾程交货
('bill_pickup', _('Bill Pickup')), # 按提单提货
('bill_handover', _('Bill Handover')), # 按提单交货
('tail_handover', _('Tail Handover')), # 按尾程交货
('pallet_handover', _('Pallet Handover')) # 按托盘交货
], string=_('Operation'), required=True) # 操作
], string=_('Operation'), required=True) # 操作
record_type = fields.Selection([
('tally', _('Tally')), # 理货
('pickup', _('Pickup')), # 提货
('handover', _('Handover')) # 交货
('tally', _('Tally')), # 理货
('pickup', _('Pickup')), # 提货
('handover', _('Handover')) # 交货
], string=_('Type'), required=True) # 类型
bill_number = fields.Char(string=_('Bill Number')) # 提单号
bill_number = fields.Char(string=_('Bill Number')) # 提单号
transfer_number = fields.Char(string=_('Transfer Number')) # 转运单号
# 增加提单关联字段
bl_id = fields.Many2one('cc.bl', string=_('Bill of Lading'), ondelete='cascade') # 提单对象
#增加状态 成功 失败
# 增加状态 成功 失败
state = fields.Selection([
('success', _('Success')),
('failed', _('Failed'))
......@@ -51,7 +48,8 @@ class PDAScanRecord(models.Model):
failure_reason = fields.Char(string=_('Failure Reason')) # 失败原因
@api.model
def create_scan_record(self, operation, record_type, bill_number, transfer_number, state, operator_id=False, bl_id=False, failure_reason=False, operation_time=False):
def create_scan_record(self, operation, record_type, bill_number, transfer_number, state, operator_id=False,
bl_id=False, failure_reason=False, operation_time=False):
"""
创建扫码记录的方法,供接口调用
Create scan record method for API calls
......@@ -59,13 +57,13 @@ class PDAScanRecord(models.Model):
try:
if not operator_id:
operator_id = self.env['res.users'].search([('login', '=', 'pda')], limit=1).id
# 如果没有传入bl_id,根据bill_number查找提单对象
if not bl_id and bill_number:
bl_obj = self.env['cc.bl'].sudo().deal_bl_no_and_transfer_bl_no(bill_number)
if bl_obj:
bl_id = bl_obj.id
# 准备创建记录的数据
record_data = {
'operator_id': operator_id,
......@@ -77,11 +75,11 @@ class PDAScanRecord(models.Model):
'state': state,
'failure_reason': failure_reason
}
# 如果传入了自定义操作时间,使用它
if operation_time:
record_data['operation_time'] = operation_time
record = self.create(record_data)
return {
'success': True,
......@@ -92,4 +90,4 @@ class PDAScanRecord(models.Model):
return {
'success': False,
'message': _('Creation failed: %s') % str(e) # 创建失败
}
\ No newline at end of file
}
......@@ -2,7 +2,7 @@
# Part of SmartGo. See LICENSE file for full copyright and licensing details.
import logging
from odoo import api, fields, models, _
from odoo import api, fields, models
_logger = logging.getLogger(__name__)
......@@ -15,14 +15,19 @@ class ResConfigSettings(models.TransientModel):
tt_app_secret = fields.Char('AppSecret', default='')
tt_version = fields.Char('接口版本', default='3.0')
tt_customer_id = fields.Many2one('res.partner', string='客户')
#交货操作晚于提货操作X分钟【默认80分钟】
delivery_time = fields.Integer('交货操作晚于提货操作X分钟', default=80,config_parameter='delivery_time')
# 交货操作晚于提货操作X分钟【默认80分钟】
delivery_time = fields.Integer('交货间隔时间', default=80, config_parameter='delivery_time')
# 提货操作晚于理货操作X分钟【默认80分钟】
tally_interval_time = fields.Integer('理货间隔时间', default=80, config_parameter='tally_interval_time')
# 巡查配置
patrol_receiver_emails = fields.Char('接收邮件地址', help='多个邮箱地址,用逗号分隔', config_parameter='patrol_receiver_emails')
patrol_receiver_emails = fields.Char('接收邮件地址', help='多个邮箱地址,用逗号分隔',
config_parameter='patrol_receiver_emails')
patrol_sender_email = fields.Char('发送邮箱地址', config_parameter='patrol_sender_email')
patrol_check_days = fields.Integer('巡查天数', default=5, help='检查近几天的提单', config_parameter='patrol_check_days')
patrol_start_hour = fields.Integer('巡查开始时间(小时)', default=8, help='北京时间,24小时制', config_parameter='patrol_start_hour')
patrol_check_days = fields.Integer('巡查天数', default=5, help='检查近几天的提单',
config_parameter='patrol_check_days')
patrol_start_hour = fields.Integer('巡查开始时间(小时)', default=8, help='北京时间,24小时制',
config_parameter='patrol_start_hour')
@api.model
def get_values(self):
......@@ -38,6 +43,7 @@ class ResConfigSettings(models.TransientModel):
tt_version = config.get_param('tt_version', default='')
tt_customer_id = config.get_param('tt_customer_id', default=False)
delivery_time = config.get_param('delivery_time', default=80)
tally_interval_time = config.get_param('tally_interval_time', default=80)
patrol_receiver_emails = config.get_param('patrol_receiver_emails', default='')
patrol_sender_email = config.get_param('patrol_sender_email', default='')
patrol_check_days = config.get_param('patrol_check_days', default=5)
......@@ -50,6 +56,7 @@ class ResConfigSettings(models.TransientModel):
tt_version=tt_version,
tt_customer_id=customer,
delivery_time=delivery_time,
tally_interval_time=tally_interval_time,
patrol_receiver_emails=patrol_receiver_emails,
patrol_sender_email=patrol_sender_email,
patrol_check_days=patrol_check_days,
......@@ -66,7 +73,8 @@ class ResConfigSettings(models.TransientModel):
ir_config.set_param("tt_version", self.tt_version or "")
ir_config.set_param("tt_customer_id", self.tt_customer_id.id or False)
ir_config.set_param("delivery_time", self.delivery_time or 80)
ir_config.set_param("tally_interval_time", self.tally_interval_time or 80)
ir_config.set_param("patrol_receiver_emails", self.patrol_receiver_emails or "")
ir_config.set_param("patrol_sender_email", self.patrol_sender_email or "")
ir_config.set_param("patrol_check_days", self.patrol_check_days or 5)
ir_config.set_param("patrol_start_hour", self.patrol_start_hour or 8)
\ No newline at end of file
ir_config.set_param("patrol_start_hour", self.patrol_start_hour or 8)
......@@ -25,6 +25,10 @@
<label for="tt_customer_id"/>
<field name="tt_customer_id"/>
</div>
<div class="text-muted">
<label for="tally_interval_time"/>
<field name="tally_interval_time"/>
</div>
<div class="text-muted">
<label for="delivery_time"/>
<field name="delivery_time"/>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论