|
@@ -34,11 +34,13 @@ class OutputPresenter:
|
|
|
PAYABLE_FILTER_TOOLS = frozenset(('list_payable_cost_filter_options',))
|
|
PAYABLE_FILTER_TOOLS = frozenset(('list_payable_cost_filter_options',))
|
|
|
PAYABLE_EXPORT_TOOLS = frozenset(('export_payable_cost_list',))
|
|
PAYABLE_EXPORT_TOOLS = frozenset(('export_payable_cost_list',))
|
|
|
DESTINATION_TRAILER_TOOLS = frozenset(('query_destination_trailer_list',))
|
|
DESTINATION_TRAILER_TOOLS = frozenset(('query_destination_trailer_list',))
|
|
|
|
|
+ ARRANGE_TRAILER_TOOLS = frozenset(('arrange_destination_trailer',))
|
|
|
ORDER_ABNORMAL_TOOLS = frozenset(('query_order_abnormal_list',))
|
|
ORDER_ABNORMAL_TOOLS = frozenset(('query_order_abnormal_list',))
|
|
|
RECEIVE_VOLUME_TOOLS = frozenset(('query_receive_volume_list',))
|
|
RECEIVE_VOLUME_TOOLS = frozenset(('query_receive_volume_list',))
|
|
|
GOODS_INFO_TOOLS = frozenset(('query_goods_info',))
|
|
GOODS_INFO_TOOLS = frozenset(('query_goods_info',))
|
|
|
GOODS_TAX_TOOLS = frozenset(('trial_goods_tax',))
|
|
GOODS_TAX_TOOLS = frozenset(('trial_goods_tax',))
|
|
|
CONTAINER_TIMELINESS_TOOLS = frozenset(('query_container_timeliness_list',))
|
|
CONTAINER_TIMELINESS_TOOLS = frozenset(('query_container_timeliness_list',))
|
|
|
|
|
+ INBOUND_RECEIPT_TOOLS = frozenset(('list_inbound_receipt_templates',))
|
|
|
FREIGHTOWER_TRACK_TOOLS = frozenset(('query_freightower_track',))
|
|
FREIGHTOWER_TRACK_TOOLS = frozenset(('query_freightower_track',))
|
|
|
DETAIL_TOOLS = frozenset(('query_outbound_detail',))
|
|
DETAIL_TOOLS = frozenset(('query_outbound_detail',))
|
|
|
ORDER_DETAIL_TOOLS = frozenset(('query_order_detail',))
|
|
ORDER_DETAIL_TOOLS = frozenset(('query_order_detail',))
|
|
@@ -57,6 +59,7 @@ class OutputPresenter:
|
|
|
'export_receivable_cost_list',
|
|
'export_receivable_cost_list',
|
|
|
'export_pallet_data',
|
|
'export_pallet_data',
|
|
|
'export_container_timeliness_report',
|
|
'export_container_timeliness_report',
|
|
|
|
|
+ 'export_inbound_receipt',
|
|
|
))
|
|
))
|
|
|
TASK_TOOLS = frozenset(('query_export_task',))
|
|
TASK_TOOLS = frozenset(('query_export_task',))
|
|
|
SAFE_TOOLS = (
|
|
SAFE_TOOLS = (
|
|
@@ -65,11 +68,13 @@ class OutputPresenter:
|
|
|
| RECEIVABLE_LIST_TOOLS | RECEIVABLE_FILTER_TOOLS
|
|
| RECEIVABLE_LIST_TOOLS | RECEIVABLE_FILTER_TOOLS
|
|
|
| PAYABLE_COST_TOOLS | PAYABLE_FILTER_TOOLS | PAYABLE_EXPORT_TOOLS
|
|
| PAYABLE_COST_TOOLS | PAYABLE_FILTER_TOOLS | PAYABLE_EXPORT_TOOLS
|
|
|
| DESTINATION_TRAILER_TOOLS
|
|
| DESTINATION_TRAILER_TOOLS
|
|
|
|
|
+ | ARRANGE_TRAILER_TOOLS
|
|
|
| ORDER_ABNORMAL_TOOLS
|
|
| ORDER_ABNORMAL_TOOLS
|
|
|
| RECEIVE_VOLUME_TOOLS
|
|
| RECEIVE_VOLUME_TOOLS
|
|
|
| GOODS_INFO_TOOLS
|
|
| GOODS_INFO_TOOLS
|
|
|
| GOODS_TAX_TOOLS
|
|
| GOODS_TAX_TOOLS
|
|
|
| CONTAINER_TIMELINESS_TOOLS
|
|
| CONTAINER_TIMELINESS_TOOLS
|
|
|
|
|
+ | INBOUND_RECEIPT_TOOLS
|
|
|
| FREIGHTOWER_TRACK_TOOLS
|
|
| FREIGHTOWER_TRACK_TOOLS
|
|
|
| OPTION_TOOLS | EXPORT_TOOLS | TASK_TOOLS
|
|
| OPTION_TOOLS | EXPORT_TOOLS | TASK_TOOLS
|
|
|
)
|
|
)
|
|
@@ -548,6 +553,15 @@ class OutputPresenter:
|
|
|
('wharf_wait_time', '码头等待时间'),
|
|
('wharf_wait_time', '码头等待时间'),
|
|
|
('amazon_wait_time', '亚马逊等待时间'),
|
|
('amazon_wait_time', '亚马逊等待时间'),
|
|
|
('bill_status', '应付费用状态'),
|
|
('bill_status', '应付费用状态'),
|
|
|
|
|
+ ('issue_way', '出单方式'),
|
|
|
|
|
+ ('release_status', '放货状态'),
|
|
|
|
|
+ ('freight_collect', '海运费到付'),
|
|
|
|
|
+ ('is_dg', 'DG'),
|
|
|
|
|
+ ('is_soc', 'SOC'),
|
|
|
|
|
+ ('is_oneway', 'ONEWAY'),
|
|
|
|
|
+ ('appointment_method', '预约方式'),
|
|
|
|
|
+ ('need_appointment_code', '是否需要预约码'),
|
|
|
|
|
+ ('warehouse_special_remark', '仓库特殊备注'),
|
|
|
),
|
|
),
|
|
|
'do': (
|
|
'do': (
|
|
|
('trailer_number', '拖车单号'), ('outbound_number', '出库单号'),
|
|
('trailer_number', '拖车单号'), ('outbound_number', '出库单号'),
|
|
@@ -926,6 +940,8 @@ class OutputPresenter:
|
|
|
return self._present_destination_trailer_list(
|
|
return self._present_destination_trailer_list(
|
|
|
data, tool_result.get('meta'), meta
|
|
data, tool_result.get('meta'), meta
|
|
|
)
|
|
)
|
|
|
|
|
+ if tool_name in self.ARRANGE_TRAILER_TOOLS:
|
|
|
|
|
+ return self._present_arrange_destination_trailer(data, meta)
|
|
|
if tool_name in self.ORDER_ABNORMAL_TOOLS:
|
|
if tool_name in self.ORDER_ABNORMAL_TOOLS:
|
|
|
return self._present_order_abnormal_list(
|
|
return self._present_order_abnormal_list(
|
|
|
data, tool_result.get('meta'), meta
|
|
data, tool_result.get('meta'), meta
|
|
@@ -944,6 +960,8 @@ class OutputPresenter:
|
|
|
return self._present_container_timeliness_list(
|
|
return self._present_container_timeliness_list(
|
|
|
data, tool_result.get('meta'), meta
|
|
data, tool_result.get('meta'), meta
|
|
|
)
|
|
)
|
|
|
|
|
+ if tool_name in self.INBOUND_RECEIPT_TOOLS:
|
|
|
|
|
+ return self._present_inbound_receipt_templates(data, meta)
|
|
|
if tool_name in self.FREIGHTOWER_TRACK_TOOLS:
|
|
if tool_name in self.FREIGHTOWER_TRACK_TOOLS:
|
|
|
return self._present_freightower_track(data, meta)
|
|
return self._present_freightower_track(data, meta)
|
|
|
if tool_name in self.TABLE_TOOLS:
|
|
if tool_name in self.TABLE_TOOLS:
|
|
@@ -1721,6 +1739,23 @@ class OutputPresenter:
|
|
|
}
|
|
}
|
|
|
return self._success_result(content, self._render_table(content), meta)
|
|
return self._success_result(content, self._render_table(content), meta)
|
|
|
|
|
|
|
|
|
|
+ def _present_arrange_destination_trailer(self, data, meta):
|
|
|
|
|
+ expected = ('trailer_number', 'bl_number', 'container_code')
|
|
|
|
|
+ if set(data) != set(expected):
|
|
|
|
|
+ return self._format_error(meta)
|
|
|
|
|
+ for key in expected:
|
|
|
|
|
+ if not isinstance(data.get(key), str):
|
|
|
|
|
+ return self._format_error(meta)
|
|
|
|
|
+ content = {
|
|
|
|
|
+ '拖车单号': data['trailer_number'],
|
|
|
|
|
+ '提单号': data['bl_number'],
|
|
|
|
|
+ '柜号': data['container_code'],
|
|
|
|
|
+ }
|
|
|
|
|
+ text = '拖车单号:{0};提单号:{1};柜号:{2}'.format(
|
|
|
|
|
+ data['trailer_number'], data['bl_number'], data['container_code']
|
|
|
|
|
+ )
|
|
|
|
|
+ return self._success_result(content, text, meta)
|
|
|
|
|
+
|
|
|
def _present_destination_trailer_list(self, data, raw_meta, meta):
|
|
def _present_destination_trailer_list(self, data, raw_meta, meta):
|
|
|
if set(data) != {'trailer_stage', 'columns', 'records'}:
|
|
if set(data) != {'trailer_stage', 'columns', 'records'}:
|
|
|
return self._format_error(meta)
|
|
return self._format_error(meta)
|
|
@@ -2215,6 +2250,38 @@ class OutputPresenter:
|
|
|
content['pagination'] = pagination
|
|
content['pagination'] = pagination
|
|
|
return self._success_result(content, self._render_table(content), meta)
|
|
return self._success_result(content, self._render_table(content), meta)
|
|
|
|
|
|
|
|
|
|
+ def _present_inbound_receipt_templates(self, data, meta):
|
|
|
|
|
+ if set(data) != {'receipt_kind', 'templates'}:
|
|
|
|
|
+ return self._format_error(meta)
|
|
|
|
|
+ kind = data.get('receipt_kind')
|
|
|
|
|
+ templates = data.get('templates')
|
|
|
|
|
+ if kind not in ('直送入仓单', '非直送入仓单') or not isinstance(templates, list):
|
|
|
|
|
+ return self._format_error(meta)
|
|
|
|
|
+ if not templates:
|
|
|
|
|
+ return self._format_error(meta)
|
|
|
|
|
+ rows = []
|
|
|
|
|
+ for item in templates:
|
|
|
|
|
+ if not isinstance(item, dict) or set(item) != {'name', 'template_code'}:
|
|
|
|
|
+ return self._format_error(meta)
|
|
|
|
|
+ name = item.get('name')
|
|
|
|
|
+ code = item.get('template_code')
|
|
|
|
|
+ if (
|
|
|
|
|
+ not isinstance(name, str) or not name.strip()
|
|
|
|
|
+ or not isinstance(code, str) or not code.strip()
|
|
|
|
|
+ ):
|
|
|
|
|
+ return self._format_error(meta)
|
|
|
|
|
+ rows.append([name.strip(), code.strip()])
|
|
|
|
|
+ content = {
|
|
|
|
|
+ 'receipt_kind': kind,
|
|
|
|
|
+ 'summary': kind,
|
|
|
|
|
+ 'headers': [
|
|
|
|
|
+ {'label': '模板名称'},
|
|
|
|
|
+ {'label': '模板编码'},
|
|
|
|
|
+ ],
|
|
|
|
|
+ 'rows': rows,
|
|
|
|
|
+ }
|
|
|
|
|
+ return self._success_result(content, self._render_table(content), meta)
|
|
|
|
|
+
|
|
|
def _present_export_submission(self, data, meta):
|
|
def _present_export_submission(self, data, meta):
|
|
|
expected = {'task_ref', 'status', 'retry_after_seconds'}
|
|
expected = {'task_ref', 'status', 'retry_after_seconds'}
|
|
|
if set(data) != expected:
|
|
if set(data) != expected:
|
|
@@ -2347,9 +2414,23 @@ class OutputPresenter:
|
|
|
},
|
|
},
|
|
|
)
|
|
)
|
|
|
code = 'MCP_9001'
|
|
code = 'MCP_9001'
|
|
|
- if code == 'MCP_1401':
|
|
|
|
|
|
|
+ if code == 'MCP_1401' and tool_name in self.ARRANGE_TRAILER_TOOLS:
|
|
|
|
|
+ if not self._arrange_trailer_message(message):
|
|
|
|
|
+ message = self._parameter_message('')
|
|
|
|
|
+ return self._error_result(code, message, False, meta)
|
|
|
|
|
+ elif code == 'MCP_1401':
|
|
|
label = self._find_field_label(tool_name, raw_message)
|
|
label = self._find_field_label(tool_name, raw_message)
|
|
|
message = self._parameter_message(label)
|
|
message = self._parameter_message(label)
|
|
|
|
|
+ elif tool_name in self.INBOUND_RECEIPT_TOOLS | self.EXPORT_TOOLS and (
|
|
|
|
|
+ tool_name in (
|
|
|
|
|
+ 'list_inbound_receipt_templates',
|
|
|
|
|
+ 'export_inbound_receipt',
|
|
|
|
|
+ )
|
|
|
|
|
+ ):
|
|
|
|
|
+ allowed = self._inbound_receipt_message(message)
|
|
|
|
|
+ message = allowed or self.ERROR_MESSAGES.get(
|
|
|
|
|
+ code, self.ERROR_MESSAGES['MCP_9001']
|
|
|
|
|
+ )
|
|
|
elif tool_name in self.FREIGHTOWER_TRACK_TOOLS:
|
|
elif tool_name in self.FREIGHTOWER_TRACK_TOOLS:
|
|
|
allowed = str(raw_message or '').strip()
|
|
allowed = str(raw_message or '').strip()
|
|
|
if allowed not in (
|
|
if allowed not in (
|
|
@@ -2368,6 +2449,49 @@ class OutputPresenter:
|
|
|
retryable = code not in self.NON_RETRYABLE_CODES
|
|
retryable = code not in self.NON_RETRYABLE_CODES
|
|
|
return self._error_result(code, message, retryable, meta)
|
|
return self._error_result(code, message, retryable, meta)
|
|
|
|
|
|
|
|
|
|
+ @staticmethod
|
|
|
|
|
+ def _arrange_trailer_message(message):
|
|
|
|
|
+ if message in (
|
|
|
|
|
+ '请提供提单号或柜号',
|
|
|
|
|
+ '存在不能填写的内容',
|
|
|
|
|
+ '号码不正确',
|
|
|
|
|
+ '请填写拖车行',
|
|
|
|
|
+ '找不到可安排的柜子',
|
|
|
|
|
+ '匹配到不止一柜,请改说另一个号码',
|
|
|
|
|
+ '当前柜子不能安排拖车行',
|
|
|
|
|
+ '当前不需要填写海外仓',
|
|
|
|
|
+ '当前不需要填写直送改拆',
|
|
|
|
|
+ '当前不需要填写改拆原因',
|
|
|
|
|
+ '直送柜只能选择是或否',
|
|
|
|
|
+ '拖车行请说完整的名称-代码',
|
|
|
|
|
+ '拖车行不能填',
|
|
|
|
|
+ '港后跟进人不能填',
|
|
|
|
|
+ '预计提柜时间不正确',
|
|
|
|
|
+ '请选择直送改拆',
|
|
|
|
|
+ '直送改拆只能选择是或否',
|
|
|
|
|
+ '改拆原因不正确',
|
|
|
|
|
+ '请填写海外仓',
|
|
|
|
|
+ '海外仓不能填',
|
|
|
|
|
+ '请填写仓库联系人',
|
|
|
|
|
+ '请填写仓库联系电话',
|
|
|
|
|
+ '当前不需要填写重量体积',
|
|
|
|
|
+ '该柜子需要在页面上传BOL后再安排',
|
|
|
|
|
+ '重量体积不正确',
|
|
|
|
|
+ '订单不在这一柜',
|
|
|
|
|
+ '请填写重量体积',
|
|
|
|
|
+ '干线未出发不可安排拖车',
|
|
|
|
|
+ '当前柜子已安排拖车行',
|
|
|
|
|
+ '仓库不存在',
|
|
|
|
|
+ '仓库联系人不能为空',
|
|
|
|
|
+ '仓库联系电话不能为空',
|
|
|
|
|
+ '拖车行不存在',
|
|
|
|
|
+ '拖车行类型不是海外拖车',
|
|
|
|
|
+ '排舱单不存在',
|
|
|
|
|
+ '安排拖车行失败',
|
|
|
|
|
+ ):
|
|
|
|
|
+ return True
|
|
|
|
|
+ return message.startswith('请填写订单') and message.endswith('的重量和体积')
|
|
|
|
|
+
|
|
|
def _find_field_label(self, tool_name, raw_message):
|
|
def _find_field_label(self, tool_name, raw_message):
|
|
|
message = str(raw_message or '')
|
|
message = str(raw_message or '')
|
|
|
fields = self.FIELD_LABELS.get(tool_name, {})
|
|
fields = self.FIELD_LABELS.get(tool_name, {})
|
|
@@ -2380,6 +2504,45 @@ class OutputPresenter:
|
|
|
def _parameter_message(label):
|
|
def _parameter_message(label):
|
|
|
return '{0}参数不正确'.format(label) if label else '工具参数不正确,请检查后重试'
|
|
return '{0}参数不正确'.format(label) if label else '工具参数不正确,请检查后重试'
|
|
|
|
|
|
|
|
|
|
+ @classmethod
|
|
|
|
|
+ def _inbound_receipt_message(cls, message):
|
|
|
|
|
+ text = str(message or '').strip()
|
|
|
|
|
+ if text in (
|
|
|
|
|
+ '当前没有可用的入仓单模板',
|
|
|
|
|
+ '所选模板不属于这次柜号对应的入仓单',
|
|
|
|
|
+ ):
|
|
|
|
|
+ return text
|
|
|
|
|
+ missing_prefix = '这些柜号没有对应的排舱单:'
|
|
|
|
|
+ if text.startswith(missing_prefix) and cls._container_code_list(
|
|
|
|
|
+ text[len(missing_prefix):]
|
|
|
|
|
+ ):
|
|
|
|
|
+ return text
|
|
|
|
|
+ direct_prefix = '这些柜号是直送柜:'
|
|
|
|
|
+ indirect_mark = '。这些柜号是非直送柜:'
|
|
|
|
|
+ if text.startswith(direct_prefix) and text.endswith('。') and indirect_mark in text:
|
|
|
|
|
+ body = text[len(direct_prefix):-1]
|
|
|
|
|
+ direct, indirect = body.split(indirect_mark, 1)
|
|
|
|
|
+ if cls._container_code_list(direct) and cls._container_code_list(indirect):
|
|
|
|
|
+ return text
|
|
|
|
|
+ return ''
|
|
|
|
|
+
|
|
|
|
|
+ @staticmethod
|
|
|
|
|
+ def _container_code_list(text):
|
|
|
|
|
+ if not text or len(text) > 200 * 101:
|
|
|
|
|
+ return False
|
|
|
|
|
+ parts = text.split('、')
|
|
|
|
|
+ if not parts:
|
|
|
|
|
+ return False
|
|
|
|
|
+ for part in parts:
|
|
|
|
|
+ if (
|
|
|
|
|
+ not part
|
|
|
|
|
+ or part != part.strip()
|
|
|
|
|
+ or len(part) > 100
|
|
|
|
|
+ or any(ord(char) < 32 for char in part)
|
|
|
|
|
+ ):
|
|
|
|
|
+ return False
|
|
|
|
|
+ return True
|
|
|
|
|
+
|
|
|
@staticmethod
|
|
@staticmethod
|
|
|
def _build_meta(raw_meta):
|
|
def _build_meta(raw_meta):
|
|
|
if not isinstance(raw_meta, dict):
|
|
if not isinstance(raw_meta, dict):
|