提交 132ed07a authored 作者: 伍姿英's avatar 伍姿英

Merge branch 'release/3.7.0'

from . import wizard
from . import models from . import models
from . import wizard
from . import controllers from . import controllers
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
'views/cc_history_ship_package_view.xml', 'views/cc_history_ship_package_view.xml',
'views/cc_history_package_sync_log_view.xml', 'views/cc_history_package_sync_log_view.xml',
'views/history_tt_api_log.xml', 'views/history_tt_api_log.xml',
'views/res_partner_view.xml',
'views/menu_view.xml', 'views/menu_view.xml',
# 'views/cc_customers_declaration_order_view.xml', # 'views/cc_customers_declaration_order_view.xml',
'templates/login.xml', 'templates/login.xml',
...@@ -62,8 +63,10 @@ ...@@ -62,8 +63,10 @@
'web.assets_backend': [ 'web.assets_backend': [
'ccs_base/static/src/mixins/link_pallet.js', 'ccs_base/static/src/mixins/link_pallet.js',
'ccs_base/static/src/mixins/link_transfer_bl_no.js', 'ccs_base/static/src/mixins/link_transfer_bl_no.js',
# 'ccs_base/static/src/mixins/create_temu_bl.js',
'ccs_base/static/src/views/big_package_list_controller.js', 'ccs_base/static/src/views/big_package_list_controller.js',
'ccs_base/static/src/views/bl_list_controller.js', 'ccs_base/static/src/views/bl_list_controller.js',
# 'ccs_base/static/src/views/temu_bl_list_controller.js',
'ccs_base/static/src/views/list.xml', 'ccs_base/static/src/views/list.xml',
], ],
}, },
......
...@@ -6,11 +6,11 @@ msgid "" ...@@ -6,11 +6,11 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 16.0\n" "Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-10-31 02:13+0000\n" "POT-Creation-Date: 2025-12-04 01:28+0000\n"
"PO-Revision-Date: 2025-10-31 10:14+0800\n" "PO-Revision-Date: 2025-12-04 09:36+0800\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Language: zh_CN\n" "Language: zh\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
...@@ -60,13 +60,6 @@ msgstr "%s %s 操作了异常信息,异常原因:%s,未发送%s邮件" ...@@ -60,13 +60,6 @@ msgstr "%s %s 操作了异常信息,异常原因:%s,未发送%s邮件"
msgid "%s bill of loading cannot find release note file" msgid "%s bill of loading cannot find release note file"
msgstr "%s 提单无法找到release note文件" msgstr "%s 提单无法找到release note文件"
#. module: ccs_base
#. odoo-python
#: code:addons/ccs_base/wizard/batch_get_pod_info_wizard.py:0
#, python-format
msgid "%s bill of loading cannot get node operation time,please manually upload push tk"
msgstr "%s提单号没有获取到节点操作时间,请手动上传推送tk"
#. module: ccs_base #. module: ccs_base
#: model:mail.template,body_html:ccs_base.email_template_exception_notification_en #: model:mail.template,body_html:ccs_base.email_template_exception_notification_en
msgid "" msgid ""
...@@ -199,11 +192,22 @@ msgstr "" ...@@ -199,11 +192,22 @@ msgstr ""
" </span>" " </span>"
#. module: ccs_base #. module: ccs_base
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_create_and_get_pod_info_wizard_form
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_get_pod_info_wizard_form #: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_get_pod_info_wizard_form
msgid "<strong>Description:</strong>" msgid "<strong>Description:</strong>"
msgstr "<strong>说明:</strong>" msgstr "<strong>说明:</strong>"
#. module: ccs_base #. module: ccs_base
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_create_and_get_pod_info_wizard_form
msgid ""
"<strong>Get Last Mile POD:</strong>\n"
" Generate a last mile POD (Proof of Delivery) attachment "
"information, including\n"
" big package quantities and container numbers"
msgstr "<strong>获取尾程POD:</strong>生成一条尾程交接POD(待大包数量和箱号)的附件信息"
#. module: ccs_base
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_create_and_get_pod_info_wizard_form
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_get_pod_info_wizard_form #: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_get_pod_info_wizard_form
msgid "" msgid ""
"<strong>Remove Specified Text:</strong>\n" "<strong>Remove Specified Text:</strong>\n"
...@@ -298,6 +302,7 @@ msgstr "" ...@@ -298,6 +302,7 @@ msgstr ""
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_add_exception_info_wizard__action_type #: model:ir.model.fields,field_description:ccs_base.field_add_exception_info_wizard__action_type
#: model:ir.model.fields,field_description:ccs_base.field_batch_get_pod_info_wizard__action_type
#: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard__action_type #: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard__action_type
msgid "Action Type" msgid "Action Type"
msgstr "动作类型" msgstr "动作类型"
...@@ -567,6 +572,17 @@ msgstr "提单号和转单号重复" ...@@ -567,6 +572,17 @@ msgstr "提单号和转单号重复"
msgid "B/L No is required" msgid "B/L No is required"
msgstr "提单号必填" msgstr "提单号必填"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_bl__bl_type
#: model_terms:ir.ui.view,arch_db:ccs_base.form_cc_bl_view
msgid "B/L Type"
msgstr "提单类型"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_batch_get_pod_info_wizard__bl_numbers
msgid "BL Number"
msgstr "提单号"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_bl__bl_attachment_ids #: model:ir.model.fields,field_description:ccs_base.field_cc_bl__bl_attachment_ids
msgid "B\\L Attachments" msgid "B\\L Attachments"
...@@ -594,6 +610,23 @@ msgstr "批量添加异常信息" ...@@ -594,6 +610,23 @@ msgstr "批量添加异常信息"
msgid "Batch Complete" msgid "Batch Complete"
msgstr "批量完成" msgstr "批量完成"
#. module: ccs_base
#. odoo-javascript
#: code:addons/ccs_base/static/src/views/list.xml:0
#, python-format
msgid "Batch Create B/L"
msgstr "批量创建提单"
#. module: ccs_base
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_create_and_get_pod_info_wizard_form
msgid "Batch Create and Get POD Info"
msgstr "批量创建提单并获取尾程POD信息"
#. module: ccs_base
#: model:ir.actions.server,name:ccs_base.bl_download_pod_server_action
msgid "Batch Download POD"
msgstr "批量下载POD"
#. module: ccs_base #. module: ccs_base
#. odoo-python #. odoo-python
#: code:addons/ccs_base/models/cc_bill_loading.py:0 #: code:addons/ccs_base/models/cc_bill_loading.py:0
...@@ -708,7 +741,7 @@ msgid "Bill Of Loading Last Process Time" ...@@ -708,7 +741,7 @@ msgid "Bill Of Loading Last Process Time"
msgstr "提单最后推送时间" msgstr "提单最后推送时间"
#. module: ccs_base #. module: ccs_base
#: model:ir.actions.act_window,name:ccs_base.action_cc_bl model:ir.model,name:ccs_base.model_cc_bl #: model:ir.model,name:ccs_base.model_cc_bl
#: model:ir.model.fields,field_description:ccs_base.field_batch_input_ship_package_status_wizard__bl_id #: model:ir.model.fields,field_description:ccs_base.field_batch_input_ship_package_status_wizard__bl_id
#: model:ir.model.fields,field_description:ccs_base.field_cc_big_package__bl_id #: model:ir.model.fields,field_description:ccs_base.field_cc_big_package__bl_id
#: model:ir.model.fields,field_description:ccs_base.field_cc_clearance_file__bl_id #: model:ir.model.fields,field_description:ccs_base.field_cc_clearance_file__bl_id
...@@ -720,7 +753,6 @@ msgstr "提单最后推送时间" ...@@ -720,7 +753,6 @@ msgstr "提单最后推送时间"
#: model:ir.model.fields,field_description:ccs_base.field_cc_ship_package__bl_id #: model:ir.model.fields,field_description:ccs_base.field_cc_ship_package__bl_id
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__bl_id #: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__bl_id
#: model:ir.model.fields.selection,name:ccs_base.selection__cc_node__node_type__bl #: model:ir.model.fields.selection,name:ccs_base.selection__cc_node__node_type__bl
#: model:ir.ui.menu,name:ccs_base.menu_cc_bl
#: model_terms:ir.ui.view,arch_db:ccs_base.calendar_cc_bl_view #: model_terms:ir.ui.view,arch_db:ccs_base.calendar_cc_bl_view
#: 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_big_package_view
#: model_terms:ir.ui.view,arch_db:ccs_base.form_cc_bl_view #: model_terms:ir.ui.view,arch_db:ccs_base.form_cc_bl_view
...@@ -739,7 +771,6 @@ msgstr "提单最后推送时间" ...@@ -739,7 +771,6 @@ msgstr "提单最后推送时间"
#: model_terms:ir.ui.view,arch_db:ccs_base.search_cc_progress_view #: model_terms:ir.ui.view,arch_db:ccs_base.search_cc_progress_view
#: model_terms:ir.ui.view,arch_db:ccs_base.search_cc_ship_package_view #: model_terms:ir.ui.view,arch_db:ccs_base.search_cc_ship_package_view
#: model_terms:ir.ui.view,arch_db:ccs_base.tree_cc_big_package_view #: model_terms:ir.ui.view,arch_db:ccs_base.tree_cc_big_package_view
#: model_terms:ir.ui.view,arch_db:ccs_base.tree_cc_bl_view
#: model_terms:ir.ui.view,arch_db:ccs_base.tree_cc_clearance_file_view #: model_terms:ir.ui.view,arch_db:ccs_base.tree_cc_clearance_file_view
#: model_terms:ir.ui.view,arch_db:ccs_base.tree_cc_history_ship_package_view #: model_terms:ir.ui.view,arch_db:ccs_base.tree_cc_history_ship_package_view
#: model_terms:ir.ui.view,arch_db:ccs_base.tree_cc_package_good_view #: model_terms:ir.ui.view,arch_db:ccs_base.tree_cc_package_good_view
...@@ -1011,6 +1042,7 @@ msgstr "清关文件" ...@@ -1011,6 +1042,7 @@ msgstr "清关文件"
#. module: ccs_base #. module: ccs_base
#: model_terms:ir.ui.view,arch_db:ccs_base.view_add_exception_info_wizard #: model_terms:ir.ui.view,arch_db:ccs_base.view_add_exception_info_wizard
#: model_terms:ir.ui.view,arch_db:ccs_base.view_associate_pallet_wizard #: model_terms:ir.ui.view,arch_db:ccs_base.view_associate_pallet_wizard
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_create_and_get_pod_info_wizard_form
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_get_pod_info_wizard_form #: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_get_pod_info_wizard_form
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_input_ship_package_wizard #: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_input_ship_package_wizard
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_update_transfer_bl_no_wizard #: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_update_transfer_bl_no_wizard
...@@ -1050,6 +1082,7 @@ msgstr "配置设置" ...@@ -1050,6 +1082,7 @@ msgstr "配置设置"
#. module: ccs_base #. module: ccs_base
#: model_terms:ir.ui.view,arch_db:ccs_base.view_add_exception_info_wizard #: model_terms:ir.ui.view,arch_db:ccs_base.view_add_exception_info_wizard
#: model_terms:ir.ui.view,arch_db:ccs_base.view_associate_pallet_wizard #: model_terms:ir.ui.view,arch_db:ccs_base.view_associate_pallet_wizard
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_create_and_get_pod_info_wizard_form
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_get_pod_info_wizard_form #: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_get_pod_info_wizard_form
#: model_terms:ir.ui.view,arch_db:ccs_base.view_bl_done_wizard #: model_terms:ir.ui.view,arch_db:ccs_base.view_bl_done_wizard
msgid "Confirm" msgid "Confirm"
...@@ -1095,6 +1128,14 @@ msgstr "快递logo" ...@@ -1095,6 +1128,14 @@ msgstr "快递logo"
msgid "Courier Name" msgid "Courier Name"
msgstr "快递名称" msgstr "快递名称"
#. module: ccs_base
#. odoo-python
#: code:addons/ccs_base/models/cc_bill_loading.py:0
#: code:addons/ccs_base/wizard/batch_get_pod_info_wizard.py:0
#, python-format
msgid "Create TEMU Bill of Loading"
msgstr "创建TEMU提单"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_add_exception_info_wizard__create_uid #: model:ir.model.fields,field_description:ccs_base.field_add_exception_info_wizard__create_uid
#: model:ir.model.fields,field_description:ccs_base.field_associate_pallet_wizard__create_uid #: model:ir.model.fields,field_description:ccs_base.field_associate_pallet_wizard__create_uid
...@@ -1198,6 +1239,7 @@ msgid "Current Status" ...@@ -1198,6 +1239,7 @@ msgid "Current Status"
msgstr "当前状态" msgstr "当前状态"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_batch_get_pod_info_wizard__partner_id
#: model:ir.model.fields,field_description:ccs_base.field_cc_bl__customer_id #: model:ir.model.fields,field_description:ccs_base.field_cc_bl__customer_id
#: model_terms:ir.ui.view,arch_db:ccs_base.form_cc_bl_view #: model_terms:ir.ui.view,arch_db:ccs_base.form_cc_bl_view
#: model_terms:ir.ui.view,arch_db:ccs_base.search_cc_bl_view #: model_terms:ir.ui.view,arch_db:ccs_base.search_cc_bl_view
...@@ -1424,6 +1466,11 @@ msgstr "目的地港口代码" ...@@ -1424,6 +1466,11 @@ msgstr "目的地港口代码"
msgid "English" msgid "English"
msgstr "英文" msgstr "英文"
#. module: ccs_base
#: model:ir.model.fields,help:ccs_base.field_batch_get_pod_info_wizard__bl_numbers
msgid "Enter multiple BL numbers, one per line"
msgstr "输入多个BL号,每行一个"
#. module: ccs_base #. module: ccs_base
#. odoo-python #. odoo-python
#: code:addons/ccs_base/wizard/batch_update_transfer_bl_no_wizard.py:0 #: code:addons/ccs_base/wizard/batch_update_transfer_bl_no_wizard.py:0
...@@ -1618,6 +1665,11 @@ msgstr "毛重" ...@@ -1618,6 +1665,11 @@ msgstr "毛重"
msgid "GROSS WEIGHT (KG)" msgid "GROSS WEIGHT (KG)"
msgstr "毛重(公斤)" msgstr "毛重(公斤)"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_batch_get_pod_info_wizard__get_last_mile_pod
msgid "Get Last Mile POD"
msgstr "获取尾程POD"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields.selection,name:ccs_base.selection__cc_history_package_good__item_type__gift #: model:ir.model.fields.selection,name:ccs_base.selection__cc_history_package_good__item_type__gift
#: model:ir.model.fields.selection,name:ccs_base.selection__cc_package_good__item_type__gift #: model:ir.model.fields.selection,name:ccs_base.selection__cc_package_good__item_type__gift
...@@ -2733,6 +2785,12 @@ msgstr "摆放区域" ...@@ -2733,6 +2785,12 @@ msgstr "摆放区域"
msgid "Placement Area must be unique !" msgid "Placement Area must be unique !"
msgstr "摆放区域必须唯一!" msgstr "摆放区域必须唯一!"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_res_partner__platform_type
#: model:ir.model.fields,field_description:ccs_base.field_res_users__platform_type
msgid "Platform Type"
msgstr "平台类型"
#. module: ccs_base #. module: ccs_base
#. odoo-python #. odoo-python
#: code:addons/ccs_base/wizard/batch_update_transfer_bl_no_wizard.py:0 #: code:addons/ccs_base/wizard/batch_update_transfer_bl_no_wizard.py:0
...@@ -2748,6 +2806,25 @@ msgstr "请检查导入文件和内容是否正确,请根据模板文件导入 ...@@ -2748,6 +2806,25 @@ msgstr "请检查导入文件和内容是否正确,请根据模板文件导入
msgid "Please configure the default customs clearance status of the bill of loading node type first." msgid "Please configure the default customs clearance status of the bill of loading node type first."
msgstr "请先配置默认的提单节点类型的清关节点" msgstr "请先配置默认的提单节点类型的清关节点"
#. module: ccs_base
#. odoo-python
#: code:addons/ccs_base/models/cc_bill_loading.py:0
#, python-format
msgid "Please configure the tail-end handover POD file of the bill of loading first."
msgstr "请先配置提单的尾部交接POD文件。"
#. module: ccs_base
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_create_and_get_pod_info_wizard_form
msgid "Please enter the bill of lading numbers. Multiple entries are allowed, one per line"
msgstr "请输入提单号。允许多次输入,每次一行"
#. module: ccs_base
#. odoo-python
#: code:addons/ccs_base/models/cc_bill_loading.py:0
#, python-format
msgid "Please select at least one bill of loading."
msgstr "请选择至少一个提单。"
#. module: ccs_base #. module: ccs_base
#. odoo-python #. odoo-python
#: code:addons/ccs_base/wizard/batch_input_ship_package_statu_wizard.py:0 #: code:addons/ccs_base/wizard/batch_input_ship_package_statu_wizard.py:0
...@@ -2763,6 +2840,7 @@ msgid "Please upload the B/L data file to be imported first!" ...@@ -2763,6 +2840,7 @@ msgid "Please upload the B/L data file to be imported first!"
msgstr "请先上传需要更新的提单数据文件!" msgstr "请先上传需要更新的提单数据文件!"
#. module: ccs_base #. module: ccs_base
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_create_and_get_pod_info_wizard_form
#: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_get_pod_info_wizard_form #: model_terms:ir.ui.view,arch_db:ccs_base.view_batch_get_pod_info_wizard_form
msgid "Preview" msgid "Preview"
msgstr "预览" msgstr "预览"
...@@ -3439,6 +3517,21 @@ msgstr "同步时间" ...@@ -3439,6 +3517,21 @@ msgstr "同步时间"
msgid "SyncTo.." msgid "SyncTo.."
msgstr "同步" msgstr "同步"
#. module: ccs_base
#. odoo-python
#: code:addons/ccs_base/wizard/batch_get_pod_info_wizard.py:0
#: model:ir.actions.act_window,name:ccs_base.action_temu_bl
#: model:ir.model.fields.selection,name:ccs_base.selection__cc_bl__bl_type__temu
#: model:ir.ui.menu,name:ccs_base.menu_temu_cc_bl
#, python-format
msgid "TEMU Bill of Loading"
msgstr "TEMU提单"
#. module: ccs_base
#: model:ir.model.fields.selection,name:ccs_base.selection__res_partner__platform_type__temu
msgid "TEMU Platform"
msgstr "TEMU 平台"
#. module: ccs_base #. module: ccs_base
#: model:ir.actions.act_window,name:ccs_base.action_history_tt_api_log #: model:ir.actions.act_window,name:ccs_base.action_history_tt_api_log
#: model:ir.ui.menu,name:ccs_base.menu_history_flight_tt_api_log #: model:ir.ui.menu,name:ccs_base.menu_history_flight_tt_api_log
...@@ -3448,11 +3541,23 @@ msgstr "同步" ...@@ -3448,11 +3541,23 @@ msgstr "同步"
msgid "TIKTOK推送日志" msgid "TIKTOK推送日志"
msgstr "" msgstr ""
#. module: ccs_base
#: model:ir.actions.act_window,name:ccs_base.action_cc_bl
#: model:ir.model.fields.selection,name:ccs_base.selection__cc_bl__bl_type__tk
#: model:ir.ui.menu,name:ccs_base.menu_cc_bl model_terms:ir.ui.view,arch_db:ccs_base.tree_cc_bl_view
msgid "TK Bill of Loading"
msgstr "TK提单"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_history_ship_package__tk_code #: model:ir.model.fields,field_description:ccs_base.field_cc_history_ship_package__tk_code
#: model:ir.model.fields,help:ccs_base.field_cc_history_ship_package__tk_code #: model:ir.model.fields,help:ccs_base.field_cc_history_ship_package__tk_code
msgid "TK Code" msgid "TK Code"
msgstr "" msgstr "TK代码"
#. module: ccs_base
#: model:ir.model.fields.selection,name:ccs_base.selection__res_partner__platform_type__tk
msgid "TK Platform"
msgstr "TK 平台"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_history_package_sync_log__process_code #: model:ir.model.fields,field_description:ccs_base.field_cc_history_package_sync_log__process_code
...@@ -3959,6 +4064,11 @@ msgstr "重量单位" ...@@ -3959,6 +4064,11 @@ msgstr "重量单位"
msgid "Whether to export in Big packages" msgid "Whether to export in Big packages"
msgstr "是否分大包导出" msgstr "是否分大包导出"
#. module: ccs_base
#: model:ir.model.fields,help:ccs_base.field_batch_get_pod_info_wizard__get_last_mile_pod
msgid "Whether to get last mile POD information"
msgstr ""
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,help:ccs_base.field_batch_get_pod_info_wizard__remove_specified_text #: model:ir.model.fields,help:ccs_base.field_batch_get_pod_info_wizard__remove_specified_text
msgid "Whether to remove specified text from PDF files" msgid "Whether to remove specified text from PDF files"
...@@ -3967,7 +4077,7 @@ msgstr "是否涂抹PDF中的指定文字" ...@@ -3967,7 +4077,7 @@ msgstr "是否涂抹PDF中的指定文字"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,help:ccs_base.field_batch_get_pod_info_wizard__skip_ocr_direct_ai #: model:ir.model.fields,help:ccs_base.field_batch_get_pod_info_wizard__skip_ocr_direct_ai
msgid "Whether to skip OCR processing and directly use AI processing (for testing AI)" msgid "Whether to skip OCR processing and directly use AI processing (for testing AI)"
msgstr "" msgstr "是否跳过OCR处理直接使用AI处理(用于测试AI)"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,help:ccs_base.field_batch_get_pod_info_wizard__sync_match_node #: model:ir.model.fields,help:ccs_base.field_batch_get_pod_info_wizard__sync_match_node
...@@ -4145,6 +4255,21 @@ msgstr "" ...@@ -4145,6 +4255,21 @@ msgstr ""
msgid "分组" msgid "分组"
msgstr "" msgstr ""
#. module: ccs_base
#: model:ir.model.fields.selection,name:ccs_base.selection__batch_get_pod_info_wizard__action_type__创建temu提单
msgid "创建temu提单"
msgstr ""
#. module: ccs_base
#: model:ir.model.fields,help:ccs_base.field_batch_get_pod_info_wizard__sync_successful_processed
msgid "勾选后显示同步尾程POD和同步匹配节点的字段以及确定按钮"
msgstr ""
#. module: ccs_base
#: model:ir.model.fields,help:ccs_base.field_batch_get_pod_info_wizard__generate_successful_processed
msgid "勾选后显示确定按钮"
msgstr ""
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_bl__is_history #: model:ir.model.fields,field_description:ccs_base.field_cc_bl__is_history
msgid "历史单据" msgid "历史单据"
...@@ -4238,6 +4363,11 @@ msgstr "" ...@@ -4238,6 +4363,11 @@ msgstr ""
msgid "是否同步" msgid "是否同步"
msgstr "" msgstr ""
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_batch_get_pod_info_wizard__sync_successful_processed
msgid "是否同步成功涂抹的提单"
msgstr ""
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_history_tt_api_log__success_bl #: model:ir.model.fields,field_description:ccs_base.field_history_tt_api_log__success_bl
#: model_terms:ir.ui.view,arch_db:ccs_base.search_history_tt_api_log_view #: model_terms:ir.ui.view,arch_db:ccs_base.search_history_tt_api_log_view
...@@ -4265,6 +4395,11 @@ msgstr "" ...@@ -4265,6 +4395,11 @@ msgstr ""
msgid "清理向导临时附件" msgid "清理向导临时附件"
msgstr "" msgstr ""
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_batch_get_pod_info_wizard__generate_successful_processed
msgid "生成成功涂抹的提单附件信息"
msgstr ""
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_history_tt_api_log__source #: model:ir.model.fields,field_description:ccs_base.field_history_tt_api_log__source
#: model_terms:ir.ui.view,arch_db:ccs_base.search_history_tt_api_log_view #: model_terms:ir.ui.view,arch_db:ccs_base.search_history_tt_api_log_view
...@@ -4282,6 +4417,11 @@ msgstr "" ...@@ -4282,6 +4417,11 @@ msgstr ""
msgid "自动获取尾程POD" msgid "自动获取尾程POD"
msgstr "" msgstr ""
#. module: ccs_base
#: model:ir.model.fields.selection,name:ccs_base.selection__batch_get_pod_info_wizard__action_type__获取尾程pod信息
msgid "获取尾程POD信息"
msgstr ""
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_history_tt_api_log__request_id #: model:ir.model.fields,field_description:ccs_base.field_history_tt_api_log__request_id
msgid "请求id" msgid "请求id"
......
# 导入odoo # 导入odoo
# 导入日志 # 导入日志
import base64
import io
import json import json
import logging import logging
import zipfile
from datetime import timedelta from datetime import timedelta
import pytz import pytz
...@@ -636,6 +639,10 @@ class CcBL(models.Model): ...@@ -636,6 +639,10 @@ class CcBL(models.Model):
} }
redis_conn.lpush('history_data_list', json.dumps(vals)) redis_conn.lpush('history_data_list', json.dumps(vals))
# 新增类型 TK提单/TEMU提单
bl_type = fields.Selection([('tk', 'TK Bill of Loading'), ('temu', 'TEMU Bill of Loading')], string='B/L Type',
default='tk')
is_history = fields.Boolean('历史单据', default=False) is_history = fields.Boolean('历史单据', default=False)
# 提单号 # 提单号
bl_no = fields.Char(string='B/L No', index=True) bl_no = fields.Char(string='B/L No', index=True)
...@@ -721,7 +728,8 @@ class CcBL(models.Model): ...@@ -721,7 +728,8 @@ class CcBL(models.Model):
bl_objs = self.env['cc.bl'].search( bl_objs = self.env['cc.bl'].search(
[('state', '=', 'ccing'), ('cc_attachment_ids.file_name', '=', fix_name)]) [('state', '=', 'ccing'), ('cc_attachment_ids.file_name', '=', fix_name)])
if len(bl_objs) > 0: if len(bl_objs) > 0:
line_objs = bl_objs.cc_attachment_ids.filtered(lambda attach: not attach.file and attach.file_name == fix_name) line_objs = bl_objs.cc_attachment_ids.filtered(
lambda attach: not attach.file and attach.file_name == fix_name)
if len(line_objs) > 0: if len(line_objs) > 0:
bl_objs = line_objs.mapped('bl_id') bl_objs = line_objs.mapped('bl_id')
logging.info('cron_get_pod bl_objs:%s,%s' % (len(bl_objs), ','.join([bl.bl_no for bl in bl_objs]))) logging.info('cron_get_pod bl_objs:%s,%s' % (len(bl_objs), ','.join([bl.bl_no for bl in bl_objs])))
...@@ -733,6 +741,52 @@ class CcBL(models.Model): ...@@ -733,6 +741,52 @@ class CcBL(models.Model):
}) })
wizard_obj.confirm() wizard_obj.confirm()
def _get_pod_files(self):
fix_name = '尾程交接POD(待大包数量和箱号)'
res = []
for bl in self:
files = self.env['cc.clearance.file'].sudo().search(
[('bl_id', '=', bl.id), ('file_name', '=', fix_name), ('file', '!=', False)])
files = files.filtered(lambda f: f.file not in (False, b'', ''))
if not files:
continue
total = len(files)
idx = 0
for f in files:
idx += 1
base = bl.bl_no or str(bl.id)
ext = 'pdf'
if f.attachment_name and '.' in f.attachment_name:
ext = f.attachment_name.split('.')[-1]
name = f"{base}.{ext}" if total == 1 else f"{base}-{idx}.{ext}"
res.append((name, f.file))
return res
def action_download_pod(self):
files = self._get_pod_files()
if not files:
return {'type': 'ir.actions.act_window_close'}
if len(self) == 1 and len(files) == 1:
name, data_b64 = files[0]
att = self.env['ir.attachment'].sudo().create(
{'name': name, 'datas': data_b64, 'res_model': 'cc.bl', 'res_id': self.id,
'mimetype': 'application/pdf'})
url = f"/web/content/ir.attachment/{att.id}/datas/{att.name}?download=true"
return {'type': 'ir.actions.act_url', 'url': url, 'target': 'self'}
buf = io.BytesIO()
with zipfile.ZipFile(buf, 'w', compression=zipfile.ZIP_DEFLATED) as zf:
for name, data_b64 in files:
zf.writestr(name, base64.b64decode(data_b64))
datas = base64.b64encode(buf.getvalue())
zip_name = 'POD.zip'
if len(self) == 1:
zip_name = f"{self.bl_no or self.id}.zip"
att = self.env['ir.attachment'].sudo().create(
{'name': zip_name, 'datas': datas, 'res_model': 'cc.bl', 'res_id': (self.id if len(self) == 1 else self[0].id),
'mimetype': 'application/zip'})
url = f"/web/content/ir.attachment/{att.id}/datas/{att.name}?download=true"
return {'type': 'ir.actions.act_url', 'url': url, 'target': 'self'}
# 增加一个can_cancel的方法,用于检查提单当前是否可以取消,返回True表示可以取消, False表示不可以取消,同时返回取消的原因 # 增加一个can_cancel的方法,用于检查提单当前是否可以取消,返回True表示可以取消, False表示不可以取消,同时返回取消的原因
def check_cancel(self): def check_cancel(self):
if self.is_cancel: if self.is_cancel:
...@@ -774,8 +828,25 @@ class CcBL(models.Model): ...@@ -774,8 +828,25 @@ class CcBL(models.Model):
bl.bl_ship_package_qty = len(ship_package_ids) bl.bl_ship_package_qty = len(ship_package_ids)
bl.bl_total_qty = len(bl.good_ids.filtered(lambda good: not good.is_cancel)) bl.bl_total_qty = len(bl.good_ids.filtered(lambda good: not good.is_cancel))
def get_default_partner(self):
"""
获取默认的客户
:return:
"""
print(self._context)
type = self._context.get('default_bl_type', 'tk')
partner_ids = self.env['res.partner'].get_type_partner(type)
if partner_ids:
return partner_ids[0]
return False
def get_customer_domain(self):
type = self._context.get('default_bl_type', 'tk')
return [('platform_type', '=', type)]
# 所属客户 # 所属客户
customer_id = fields.Many2one('res.partner', string='Customer') customer_id = fields.Many2one('res.partner', string='Customer', index=True, default=get_default_partner,
domain=lambda self: self.get_customer_domain())
# 大包明细 # 大包明细
big_package_ids = fields.One2many('cc.big.package', 'bl_id', string='Big Packages') big_package_ids = fields.One2many('cc.big.package', 'bl_id', string='Big Packages')
...@@ -1173,10 +1244,40 @@ class CcBL(models.Model): ...@@ -1173,10 +1244,40 @@ class CcBL(models.Model):
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
'view_mode': 'form', 'view_mode': 'form',
'res_model': 'batch.get.pod.info.wizard', 'res_model': 'batch.get.pod.info.wizard',
'context': {'active_id': self.ids}, 'context': {'active_id': self.ids, 'default_action_type': '获取尾程POD信息'},
'views': [[self.env.ref('ccs_base.view_batch_get_pod_info_wizard_form').id, "form"]],
'target': 'new', 'target': 'new',
} }
def action_create_temu_bl(self):
"""
创建temu提单
"""
return {
'name': _('Create TEMU Bill of Loading'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'batch.get.pod.info.wizard',
'views': [[self.env.ref('ccs_base.view_batch_create_and_get_pod_info_wizard_form').id, "form"]],
'context': {'active_id': self.ids, 'default_action_type': '创建temu提单'},
'target': 'new'
}
def action_batch_download_pod(self):
"""
将选中的提单以提单号命名下载尾程交接POD文件,若文件有多个则以提单-1/-2进行命名下载,若选择多个提单,则将所有文件打包成zip,zip命名则以POD进行命名
"""
# 检查是否有选中的提单
if not self:
raise UserError(_('Please select at least one bill of loading.'))
# 检查是否有尾程交接POD文件
fix_name = '尾程交接POD(待大包数量和箱号)'
has_files = self.env['cc.clearance.file'].search_count(
[('bl_id', 'in', self.ids), ('file_name', '=', fix_name), ('file', '!=', False)])
if not has_files:
raise UserError(_('Please configure the tail-end handover POD file of the bill of loading first.'))
return self.action_download_pod()
# 增加一个清关进度的业务对象,继承自models.Model, 用于管理业务数据.业务数据包括提单号、清关节点(业务对象)、进度日期、进度描述、更新人 # 增加一个清关进度的业务对象,继承自models.Model, 用于管理业务数据.业务数据包括提单号、清关节点(业务对象)、进度日期、进度描述、更新人
class CcProgress(models.Model): class CcProgress(models.Model):
......
from odoo import models, fields, api, _
# 导入日志 # 导入日志
import logging import logging
from odoo.exceptions import UserError from odoo import models, fields
# 获取日志 # 获取日志
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
...@@ -17,3 +15,18 @@ class ResPartner(models.Model): ...@@ -17,3 +15,18 @@ class ResPartner(models.Model):
is_customer = fields.Boolean(string='Is Customer', default=False) is_customer = fields.Boolean(string='Is Customer', default=False)
# 是否是清关公司 # 是否是清关公司
is_clearance_company = fields.Boolean(string='Is Clearance Company', default=False) is_clearance_company = fields.Boolean(string='Is Clearance Company', default=False)
# 新增类型:TK平台/TEMU平台。用英文表示
platform_type = fields.Selection([
('tk', 'TK Platform'),
('temu', 'TEMU Platform'),
], string='Platform Type', default='tk')
def get_type_partner(self, type='tk'):
"""
获取默认的TEMU平台客户
:return:
"""
partner_ids = self.env['res.partner'].search([('platform_type', '=', type)])
if partner_ids:
return partner_ids[0]
return False
...@@ -67,7 +67,7 @@ access_cc_big_package_ccs_base.group_clearance_of_customs_manager,cc_big_package ...@@ -67,7 +67,7 @@ access_cc_big_package_ccs_base.group_clearance_of_customs_manager,cc_big_package
access_cc_clearance_file_base.group_user,cc_clearance_file base.group_user,ccs_base.model_cc_clearance_file,base.group_user,1,0,0,0 access_cc_clearance_file_base.group_user,cc_clearance_file base.group_user,ccs_base.model_cc_clearance_file,base.group_user,1,0,0,0
access_cc_clearance_file_base.group_erp_manager,cc_clearance_file base.group_erp_manager,ccs_base.model_cc_clearance_file,base.group_erp_manager,1,1,1,1 access_cc_clearance_file_base.group_erp_manager,cc_clearance_file base.group_erp_manager,ccs_base.model_cc_clearance_file,base.group_erp_manager,1,1,1,1
access_cc_clearance_file_ccs_base.group_clearance_of_customs_manager,cc_clearance_file ccs_base.group_clearance_of_customs_manager,ccs_base.model_cc_clearance_file,ccs_base.group_clearance_of_customs_manager,1,1,1,1 access_cc_clearance_file_ccs_base.group_clearance_of_customs_manager,cc_clearance_file ccs_base.group_clearance_of_customs_manager,ccs_base.model_cc_clearance_file,ccs_base.group_clearance_of_customs_manager,1,1,1,1
access_cc_clearance_file_ccs_base.group_clearance_of_customs_user,cc_clearance_file ccs_base.group_clearance_of_customs_user,ccs_base.model_cc_clearance_file,ccs_base.group_clearance_of_customs_user,1,0,0,0 access_cc_clearance_file_ccs_base.group_clearance_of_customs_user,cc_clearance_file ccs_base.group_clearance_of_customs_user,ccs_base.model_cc_clearance_file,ccs_base.group_clearance_of_customs_user,1,1,1,1
order_state_change_rule_group_user,order_state_change_rule_group_user,ccs_base.model_order_state_change_rule,base.group_user,1,1,1,1 order_state_change_rule_group_user,order_state_change_rule_group_user,ccs_base.model_order_state_change_rule,base.group_user,1,1,1,1
......
/** @odoo-module */
import {useService} from '@web/core/utils/hooks';
const {useRef, useEffect, useState} = owl;
export const TemuBlCreateMixin = {
setup() {
this._super();
this.actionService = useService('action');
this.notification = useService('notification');
this.orm = useService('orm');
this.http = useService('http');
this.fileInput = useRef('fileInput');
this.root = useRef("root");
},
async onCreateTemuBlClick() {
// 点击按钮弹出创建temu提单的向导
const records = this.model.root.selection;
const recordIds = records.map((a) => a.resId);
const action = await this.orm.call('cc.bl', 'action_create_temu_bl', [recordIds]);
this.actionService.doAction(action);
},
};
...@@ -23,4 +23,12 @@ export const LinkTransferBlNo = { ...@@ -23,4 +23,12 @@ export const LinkTransferBlNo = {
this.actionService.doAction(action); this.actionService.doAction(action);
}, },
async onCreateTemuBlClick() {
// 点击按钮弹出创建temu提单的向导
const records = this.model.root.selection;
const recordIds = records.map((a) => a.resId);
const action = await this.orm.call('cc.bl', 'action_create_temu_bl', [recordIds]);
this.actionService.doAction(action);
},
}; };
...@@ -13,7 +13,7 @@ const {onWillStart} = owl; ...@@ -13,7 +13,7 @@ const {onWillStart} = owl;
export class BigPackageListController extends ListController { export class BigPackageListController extends ListController {
setup() { setup() {
super.setup(); super.setup();
console.log('----------引用成功') console.log('----------big_package_list_controller引用成功')
this.orm = useService('orm'); this.orm = useService('orm');
this.actionService = useService('action'); this.actionService = useService('action');
this.rpc = useService("rpc"); this.rpc = useService("rpc");
...@@ -25,12 +25,17 @@ export class BigPackageListController extends ListController { ...@@ -25,12 +25,17 @@ export class BigPackageListController extends ListController {
console.log('ccs is_link:' + this.is_link) console.log('ccs is_link:' + this.is_link)
}); });
} }
displayLink() { displayLink() {
console.log('ccs flag:' + this.isBigPackage && this.is_link) console.log('ccs flag:' + this.isBigPackage && this.is_link)
// 是大包的对象以及有清关清理的权限才显示按钮 // 是大包的对象以及有清关清理的权限才显示按钮
return this.isBigPackage && this.is_link; return this.isBigPackage && this.is_link;
} }
displayCreateTemuBl (){
return false;
}
displayTransferBlNo() { displayTransferBlNo() {
// 大包页面永远不显示“关联转单号”按钮 // 大包页面永远不显示“关联转单号”按钮
......
...@@ -13,16 +13,49 @@ const {onWillStart} = owl; ...@@ -13,16 +13,49 @@ const {onWillStart} = owl;
export class BlListController extends ListController { export class BlListController extends ListController {
setup() { setup() {
super.setup(); super.setup();
console.log('----------引用成功') console.log('----------bl_list_controller引用成功')
this.orm = useService('orm'); this.orm = useService('orm');
this.actionService = useService('action'); this.actionService = useService('action');
this.rpc = useService("rpc"); this.rpc = useService("rpc");
this.user = useService("user"); this.user = useService("user");
this.isBl = this.model.rootParams.resModel === "cc.bl"; this.isBl = this.model.rootParams.resModel === "cc.bl";
this.isTemuBl=false;
if (this.isBl) {
const d = this.props.domain;
// #输出d类型
console.log('d类型:' + typeof d)
console.log('d:' + d)
const containsTk = (x) => {
if (x === 'temu') {
return true;
}
if (Array.isArray(x)) {
for (const el of x) {
if (containsTk(el)) {
return true;
}
}
return false;
}
if (x && typeof x === 'object') {
for (const k in x) {
if (containsTk(x[k])) {
return true;
}
}
return false;
}
return false;
};
this.isTemuBl = !!containsTk(d);
}
console.log('ccs isBl:' + this.isBl) console.log('ccs isBl:' + this.isBl)
console.log('ccs isTemuBl:' + this.isTemuBl)
onWillStart(async () => { onWillStart(async () => {
this.can_link_transfer_bl_no = await this.user.hasGroup("ccs_base.group_clearance_of_customs_user"); this.can_link_transfer_bl_no = await this.user.hasGroup("ccs_base.group_clearance_of_customs_user");
console.log('ccs can_link_transfer_bl_no:' + this.can_link_transfer_bl_no) console.log('ccs can_link_transfer_bl_no:' + this.can_link_transfer_bl_no)
this.is_user = await this.user.hasGroup("ccs_base.group_clearance_of_customs_user");
console.log('ccs is_user:' + this.is_user)
}); });
} }
...@@ -30,7 +63,13 @@ export class BlListController extends ListController { ...@@ -30,7 +63,13 @@ export class BlListController extends ListController {
// 提单页面永远不显示“关联托盘”按钮 // 提单页面永远不显示“关联托盘”按钮
return false; return false;
} }
displayCreateTemuBl() {
console.log('ccs flag:' + this.isTemuBl && this.is_user)
// 是temu提单的对象以及有清关用户的权限才显示按钮
return this.isTemuBl && this.is_user;
}
displayTransferBlNo() { displayTransferBlNo() {
console.log('ccs flag:' + this.isBl && this.can_link_transfer_bl_no) console.log('ccs flag:' + this.isBl && this.can_link_transfer_bl_no)
// 是提单的对象以及有清关用户的权限才显示按钮 // 是提单的对象以及有清关用户的权限才显示按钮
...@@ -45,4 +84,4 @@ registry.category('views').add('cc_bl_tree', { ...@@ -45,4 +84,4 @@ registry.category('views').add('cc_bl_tree', {
...listView, ...listView,
buttonTemplate: 'ccs_base.ListButtons', buttonTemplate: 'ccs_base.ListButtons',
Controller: BlListController Controller: BlListController
}); });
\ No newline at end of file
...@@ -12,5 +12,10 @@ ...@@ -12,5 +12,10 @@
Link Transfer B/L No Link Transfer B/L No
</button> </button>
</xpath> </xpath>
<xpath expr="//button[hasclass('o_list_button_add')]" position="after">
<button t-if="displayCreateTemuBl()" type="button" class="d-none d-md-inline o_button_create_temu_bl btn btn-primary mx-1" t-on-click.prevent="onCreateTemuBlClick">
Batch Create B/L
</button>
</xpath>
</t> </t>
</templates> </templates>
/** @odoo-module */
import {TemuBlCreateMixin} from '../mixins/create_temu_bl';
import {registry} from '@web/core/registry';
import {patch} from '@web/core/utils/patch';
import {useService} from '@web/core/utils/hooks';
import {listView} from "@web/views/list/list_view";
import {ListController} from "@web/views/list/list_controller";
const {onWillStart} = owl;
export class TemuBlListController extends ListController {
setup() {
super.setup();
console.log('----------temu_bl_list_controller引用成功')
this.orm = useService('orm');
this.actionService = useService('action');
this.rpc = useService("rpc");
this.user = useService("user");
this.isTemuBl = this.model.rootParams.resModel === "cc.bl";
console.log('ccs isTemuBl:' + this.isTemuBl)
onWillStart(async () => {
this.is_user = await this.user.hasGroup("ccs_base.group_clearance_of_customs_user");
console.log('ccs is_user:' + this.is_user)
});
}
displayCreateTemuBl() {
console.log('ccs flag:' + this.isTemuBl && this.is_user)
// 是temu提单的对象以及有清关用户的权限才显示按钮
return this.isTemuBl && this.is_user;
}
displayLink() {
return false;
}
displayTransferBlNo() {
// 大包页面永远不显示“关联转单号”按钮
return false;
}
}
patch(TemuBlListController.prototype, 'temu_bl_list_controller_create', TemuBlCreateMixin);
registry.category('views').add('cc_temu_bl_tree', {
...listView,
buttonTemplate: 'ccs_base.ListButtons',
Controller: TemuBlListController
});
\ No newline at end of file
...@@ -7,13 +7,14 @@ ...@@ -7,13 +7,14 @@
<field name="name">tree.cc.bl</field> <field name="name">tree.cc.bl</field>
<field name="model">cc.bl</field> <field name="model">cc.bl</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="Bill of Loading" decoration-warning="is_cancel==True" js_class="cc_bl_tree"> <tree string="TK Bill of Loading" decoration-warning="is_cancel==True" js_class="cc_bl_tree">
<field optional="show" name="state" string="Status" widget="badge" decoration-info="state=='draft'" <field optional="show" name="state" string="Status" widget="badge" decoration-info="state=='draft'"
decoration-primary="state=='ccing'" decoration-success="state=='done'"/> decoration-primary="state=='ccing'" decoration-success="state=='done'"/>
<field optional="show" name="customs_clearance_status" string="Customs Clearance Status"/> <field optional="show" name="customs_clearance_status" string="Customs Clearance Status"/>
<field optional="show" name="bl_no" string="Bill of Loading No."/> <field optional="show" name="bl_no" string="Bill of Loading No."/>
<field optional="show" name="bl_date" string="B/L Date"/> <field optional="show" name="bl_date" string="B/L Date"/>
<field optional="show" name="last_mile_provider_ids" string="Last Mile Providers" widget="many2many_tags"/> <field optional="show" name="last_mile_provider_ids" string="Last Mile Providers"
widget="many2many_tags"/>
<field optional="hide" name="transfer_bl_no" string="Transfer Bill of Loading No."/> <field optional="hide" name="transfer_bl_no" string="Transfer Bill of Loading No."/>
<field optional="show" name="customer_id" string="Customer"/> <field optional="show" name="customer_id" string="Customer"/>
<field optional="show" name="customs_bl_no" string="Customs Bill of Loading No."/> <field optional="show" name="customs_bl_no" string="Customs Bill of Loading No."/>
...@@ -60,16 +61,17 @@ ...@@ -60,16 +61,17 @@
string="Update Bill Of Loading Status" string="Update Bill Of Loading Status"
context="{'active_id': id,'default_bl_id': active_id, context="{'active_id': id,'default_bl_id': active_id,
'default_last_process_time':process_time,'default_current_status':customs_clearance_status}"/> 'default_last_process_time':process_time,'default_current_status':customs_clearance_status}"/>
<!-- 完成按钮 - 仅在清关中状态显示,仅清关员和清关经理可操作 --> <!-- 完成按钮 - 仅在清关中状态显示,仅清关员和清关经理可操作 -->
<button name="action_done" type="object" class="oe_highlight" <button name="action_done" type="object" class="oe_highlight"
string="Complete" attrs="{'invisible': [('state', '!=', 'ccing')]}" string="Complete" attrs="{'invisible': [('state', '!=', 'ccing')]}"
groups="ccs_base.group_clearance_of_customs_user,ccs_base.group_clearance_of_customs_manager"/> groups="ccs_base.group_clearance_of_customs_user,ccs_base.group_clearance_of_customs_manager"/>
<!-- 追回按钮 - 仅在已完成状态显示,仅清关员和清关经理可操作 --> <!-- 追回按钮 - 仅在已完成状态显示,仅清关员和清关经理可操作 -->
<button name="action_recall" type="object" string="Recall" attrs="{'invisible': [('state', '!=', 'done')]}" <button name="action_recall" type="object" string="Recall"
attrs="{'invisible': [('state', '!=', 'done')]}"
groups="ccs_base.group_clearance_of_customs_user,ccs_base.group_clearance_of_customs_manager"/> groups="ccs_base.group_clearance_of_customs_user,ccs_base.group_clearance_of_customs_manager"/>
<field name="state" widget="statusbar"/> <field name="state" widget="statusbar"/>
<!-- options="{'clickable': '1'}" --> <!-- options="{'clickable': '1'}" -->
...@@ -136,7 +138,7 @@ ...@@ -136,7 +138,7 @@
<field name="is_history" invisible="1"/> <field name="is_history" invisible="1"/>
<label for="bl_no"/> <label for="bl_no"/>
<h1> <h1>
<field name="bl_no" readonly="True"/> <field name="bl_no" attrs="{'readonly': [('bl_type', '=', 'tk')]}"/>
</h1> </h1>
<group> <group>
<group> <group>
...@@ -156,6 +158,7 @@ ...@@ -156,6 +158,7 @@
<field name="end_port_code" string="End Port"/> <field name="end_port_code" string="End Port"/>
<field name="etd" string="ETD"/> <field name="etd" string="ETD"/>
<field name="process_time" string="Customs Clearance Status Process Time"/> <field name="process_time" string="Customs Clearance Status Process Time"/>
<field name="bl_type" string="B/L Type" invisible="1"/>
</group> </group>
<group> <group>
<field name="billing_weight" string="Billing Weight"/> <field name="billing_weight" string="Billing Weight"/>
...@@ -170,23 +173,13 @@ ...@@ -170,23 +173,13 @@
domain="[('is_clearance_company', '=', True)]"/> domain="[('is_clearance_company', '=', True)]"/>
<field name="cc_country_id" string="CC Country" requied="1"/> <field name="cc_country_id" string="CC Country" requied="1"/>
<field name="cc_deadline" string="CC Deadline"/> <field name="cc_deadline" string="CC Deadline"/>
<!-- <field name="cc_progress_date" string="Current CC Progress Date"/>-->
<!-- <field name="cc_progress" string="Current CC Progress"/>-->
</group> </group>
</group> </group>
<notebook> <notebook>
<!-- <page string="CC Progress">-->
<!-- <field name="cc_progress_ids" string="CC Progress"/>-->
<!-- </page>-->
<!-- <page string="Ship Packages">-->
<!-- <field name="ship_package_ids" string="Ship Packages"/>-->
<!-- </page>-->
<!-- <page string="Big Packages">-->
<!-- <field name="big_package_ids" string="Big Packages"/>-->
<!-- </page>-->
<page string="Attachments"> <page string="Attachments">
<group> <group>
<field name="bl_attachment_ids" string="B/L Attachments" widget="many2many_attachment_preview" <field name="bl_attachment_ids" string="B/L Attachments"
widget="many2many_attachment_preview"
readonly="1"/> readonly="1"/>
<field name="cc_attachment_ids" string="CC Attachments"/> <field name="cc_attachment_ids" string="CC Attachments"/>
</group> </group>
...@@ -332,82 +325,32 @@ ...@@ -332,82 +325,32 @@
</record> </record>
<record model="ir.actions.act_window" id="action_cc_bl"> <record model="ir.actions.act_window" id="action_cc_bl">
<field name="name">Bill of Loading</field> <field name="name">TK Bill of Loading</field>
<field name="res_model">cc.bl</field> <field name="res_model">cc.bl</field>
<field name="view_mode">tree,form,pivot,graph,calendar</field> <field name="view_mode">tree,form,pivot,graph,calendar</field>
<field name="domain">[]</field> <field name="domain">[('bl_type','=','tk')]</field>
<field name="context">{'search_default_filter_state_not_finished':1}</field> <field name="context">{'search_default_filter_state_not_finished':1,'default_bl_type':'tk'}</field>
<field name="view_id" ref="tree_cc_bl_view"/>
<field name="help" type="html"> <field name="help" type="html">
<p> <p>
</p> </p>
</field> </field>
</record> </record>
<record model="ir.actions.act_window" id="action_temu_bl">
<field name="name">TEMU Bill of Loading</field>
<field name="res_model">cc.bl</field>
<field name="view_mode">tree,form,pivot,graph,calendar</field>
<field name="domain">[('bl_type','=','temu')]</field>
<field name="view_id" ref="tree_cc_bl_view"/>
<!-- <field name="view_id" ref="tree_temu_cc_bl_view"/> -->
<field name="context">{'search_default_filter_state_not_finished':1,'default_bl_type':'temu'}</field>
<field name="help" type="html">
<p>
</p>
</field>
</record>
<!--&lt;!&ndash; <record model="ir.ui.view" id="search_cc_bl_view">&ndash;&gt;-->
<!--&lt;!&ndash; <field name="name">search.cc.bl</field>&ndash;&gt;-->
<!--&lt;!&ndash; <field name="model">cc.bl</field>&ndash;&gt;-->
<!--&lt;!&ndash; <field name="arch" type="xml">&ndash;&gt;-->
<!--&lt;!&ndash; <search string="Bill of Loading">&ndash;&gt;-->
<!--&lt;!&ndash; <field name="bl_no" string="Fuzzy Search"&ndash;&gt;-->
<!--&lt;!&ndash; filter_domain="['|', ('bl_no', 'ilike', self), '|', ('cc_company_id.name', 'ilike', self),('customer_id.name', 'ilike', self)]"/>&ndash;&gt;-->
<!--&lt;!&ndash; <separator/>&ndash;&gt;-->
<!--&lt;!&ndash; <field name="bl_no" string="B/L No."/>&ndash;&gt;-->
<!--&lt;!&ndash; <field name="customer_id" string="Customer"/>&ndash;&gt;-->
<!--&lt;!&ndash; <field name="cc_country_id" string="CC Country"/>&ndash;&gt;-->
<!--&lt;!&ndash; <field name="cc_company_id" string="CC Company"/>&ndash;&gt;-->
<!--&lt;!&ndash; <field name="bl_date" string="B/L Date"/>&ndash;&gt;-->
<!--&lt;!&ndash; <field name="bl_total_amount" string="B/L Total Amount"/>&ndash;&gt;-->
<!--&lt;!&ndash; <field name="bl_total_qty" string="B/L Total Qty"/>&ndash;&gt;-->
<!--&lt;!&ndash; <field name="cc_progress" string="Current CC Progress"/>&ndash;&gt;-->
<!--&lt;!&ndash; <field name="cc_progress_date" string="Current CC Progress Date"/>&ndash;&gt;-->
<!--&lt;!&ndash; <field name="state" string="Status"/>&ndash;&gt;-->
<!--&lt;!&ndash; <separator/>&ndash;&gt;-->
<!--&lt;!&ndash; <filter name="filter_bl_date" string="B/L Date" date="bl_date"/>&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash; # 增加一个过滤器, 用于显示今天到期的单据&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash; <filter name="filter_today_deadline" string="Today Deadline" domain="[('cc_deadline', '=', time.strftime('%Y-%m-%d'))]"/>&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash; # 增加一个过滤器,用于显示已经到期的单据&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash; <filter name="filter_deadline" string="Deadline" domain="[('cc_deadline', '&lt;', time.strftime('%Y-%m-%d'))]"/>&ndash;&gt;-->
<!--&lt;!&ndash; <separator/>&ndash;&gt;-->
<!--&lt;!&ndash; <group expand="0" string="Group By">&ndash;&gt;-->
<!--&lt;!&ndash; <filter domain="[]" name="groupby_customer_id" string="Customer"&ndash;&gt;-->
<!--&lt;!&ndash; context="{'group_by': 'customer_id'}"/>&ndash;&gt;-->
<!--&lt;!&ndash; <filter domain="[]" name="groupby_cc_country_id" string="CC Country"&ndash;&gt;-->
<!--&lt;!&ndash; context="{'group_by': 'cc_country_id'}"/>&ndash;&gt;-->
<!--&lt;!&ndash; <filter domain="[]" name="groupby_cc_company_id" string="CC Company"&ndash;&gt;-->
<!--&lt;!&ndash; context="{'group_by': 'cc_company_id'}"/>&ndash;&gt;-->
<!--&lt;!&ndash; <filter domain="[]" name="groupby_state" string="Status" context="{'group_by': 'state'}"/>&ndash;&gt;-->
<!--&lt;!&ndash; </group>&ndash;&gt;-->
<!--&lt;!&ndash; <searchpanel>&ndash;&gt;-->
<!--&lt;!&ndash; <field icon="fa-users" name="state"/>&ndash;&gt;-->
<!--&lt;!&ndash; <field icon="fa-users" select="multi" name="cc_country_id" enable_counters="1"/>&ndash;&gt;-->
<!--&lt;!&ndash; </searchpanel>&ndash;&gt;-->
<!--&lt;!&ndash; </search>&ndash;&gt;-->
<!--&lt;!&ndash; </field>&ndash;&gt;-->
<!--&lt;!&ndash; </record>&ndash;&gt;-->
<!-- <record model="ir.actions.act_window" id="action_cc_bl">-->
<!-- <field name="name">Bill of Loading</field>-->
<!-- <field name="res_model">cc.bl</field>-->
<!-- <field name="view_mode">tree,form,pivot,graph</field>-->
<!-- <field name="domain">[]</field>-->
<!-- <field name="context">{-->
<!-- 'search_default_filter_today_deadline': 1,-->
<!-- }</field>-->
<!-- <field name="help" type="html">-->
<!-- <p class="o_view_nocontent_smiling_face">-->
<!-- [Bill of Loading] Not yet! Click the Create button in the top left corner and the sofa is yours!-->
<!-- </p>-->
<!-- <p>-->
<!-- </p>-->
<!-- </field>-->
<!-- </record>-->
<!-- 导出报关数据 <menuitem sequence="10" name="Bill of Loading" id="menu_cc_bl" action="action_cc_bl" web_icon="ccs_base,static/description/icon3.png"/>--> <!-- 导出报关数据 <menuitem sequence="10" name="Bill of Loading" id="menu_cc_bl" action="action_cc_bl" web_icon="ccs_base,static/description/icon3.png"/>-->
<record id="export_order_big_package_xls_server" model="ir.actions.server"> <record id="export_order_big_package_xls_server" model="ir.actions.server">
...@@ -483,4 +426,19 @@ ...@@ -483,4 +426,19 @@
</field> </field>
</record> </record>
<!-- 下载POD -->
<record id="bl_download_pod_server_action" model="ir.actions.server">
<field name="name">Batch Download POD</field>
<field name="model_id" ref="model_cc_bl"/>
<field name="binding_model_id" ref="model_cc_bl"/>
<field name="state">code</field>
<field name="binding_view_types">list,form</field>
<field name="groups_id" eval="[(4, ref('ccs_base.group_clearance_of_customs_user'))]"/>
<field name="code">
if records:
action = records.action_batch_download_pod()
</field>
</record>
</odoo> </odoo>
\ No newline at end of file
...@@ -14,22 +14,6 @@ ...@@ -14,22 +14,6 @@
<field name="web_icon">ccs_base,static/description/icon5.png</field> <field name="web_icon">ccs_base,static/description/icon5.png</field>
</record> </record>
<!-- # 增加一个"客户"action, 仅显示为客户的partner,显示模式为树,表单-->
<record model="ir.actions.act_window" id="action_cc_partner">
<field name="name">Customers</field>
<field name="res_model">res.partner</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('is_customer', '=', True)]</field>
<field name="context">{'default_is_customer': True}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
[Customers] Not yet! Click the Create button in the top left corner and the sofa is yours!
</p>
<p>
</p>
</field>
</record>
<!-- # 增加一个"客户"菜单, 与action_cc_partner关联--> <!-- # 增加一个"客户"菜单, 与action_cc_partner关联-->
<menuitem sequence="10" name="Customers" id="menu_cc_partner" action="action_cc_partner" <menuitem sequence="10" name="Customers" id="menu_cc_partner" action="action_cc_partner"
parent="menu_ccs_base_main"/> parent="menu_ccs_base_main"/>
...@@ -47,23 +31,6 @@ ...@@ -47,23 +31,6 @@
<menuitem parent="menu_ccs_base_main" sequence="99" name="CC Node" id="menu_cc_node" action="action_cc_node"/> <menuitem parent="menu_ccs_base_main" sequence="99" name="CC Node" id="menu_cc_node" action="action_cc_node"/>
<!-- # 增加一个"供应商"action, 仅显示为供应商的partner,显示模式为树,表单-->
<record model="ir.actions.act_window" id="action_cc_is_clearance_company">
<field name="name">Clearance Company</field>
<field name="res_model">res.partner</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('is_clearance_company', '=', True)]</field>
<field name="context">{'default_is_clearance_company': True}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
[Clearance Company] Not yet! Click the Create button in the top left corner and the sofa is yours!
</p>
<p>
</p>
</field>
</record>
<!-- # 增加一个"供应商"菜单, 与action_cc_supplier关联--> <!-- # 增加一个"供应商"菜单, 与action_cc_supplier关联-->
<menuitem sequence="20" name="Clearance Company" id="menu_cc_clearance_company" <menuitem sequence="20" name="Clearance Company" id="menu_cc_clearance_company"
action="action_cc_is_clearance_company" action="action_cc_is_clearance_company"
...@@ -86,7 +53,11 @@ ...@@ -86,7 +53,11 @@
action="action_history_tt_api_log" action="action_history_tt_api_log"
groups="ccs_base.group_clearance_of_customs_user,ccs_base.group_clearance_of_customs_manager"/> groups="ccs_base.group_clearance_of_customs_user,ccs_base.group_clearance_of_customs_manager"/>
<menuitem parent="" sequence="10" name="Bill of Loading" id="menu_cc_bl" action="action_cc_bl" <menuitem parent="" sequence="10" name="TK Bill of Loading" id="menu_cc_bl" action="action_cc_bl"
groups="ccs_base.group_clearance_of_customs_user,ccs_base.group_clearance_of_customs_manager"/>
<!-- TEMU提单 -->
<menuitem parent="" sequence="11" name="TEMU Bill of Loading" id="menu_temu_cc_bl" action="action_temu_bl"
groups="ccs_base.group_clearance_of_customs_user,ccs_base.group_clearance_of_customs_manager"/> groups="ccs_base.group_clearance_of_customs_user,ccs_base.group_clearance_of_customs_manager"/>
<menuitem parent="" sequence="13" name="Big Package" id="menu_cc_big_package" action="action_cc_big_package" <menuitem parent="" sequence="13" name="Big Package" id="menu_cc_big_package" action="action_cc_big_package"
......
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data>
<record id="view_partner_form" model="ir.ui.view">
<field name="name">view_partner_form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='vat']" position="after">
<field name="platform_type" />
</xpath>
</field>
</record>
<record id="view_partner_tree" model="ir.ui.view">
<field name="name">view_partner_tree</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='email']" position="after">
<field name="platform_type" />
</xpath>
</field>
</record>
<record id="view_res_partner_filter" model="ir.ui.view">
<field name="name">view_res_partner_filter</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_res_partner_filter"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='group_country']" position="after">
<filter name="group_platform_type" context="{'group_by': 'platform_type'}"/>
</xpath>
</field>
</record>
<!-- # 增加一个"客户"action, 仅显示为客户的partner,显示模式为树,表单-->
<record model="ir.actions.act_window" id="action_cc_partner">
<field name="name">Customers</field>
<field name="res_model">res.partner</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('is_customer', '=', True)]</field>
<field name="context">{'default_is_customer': True}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
[Customers] Not yet! Click the Create button in the top left corner and the sofa is yours!
</p>
<p>
</p>
</field>
</record>
<!-- # 增加一个"供应商"action, 仅显示为供应商的partner,显示模式为树,表单-->
<record model="ir.actions.act_window" id="action_cc_is_clearance_company">
<field name="name">Clearance Company</field>
<field name="res_model">res.partner</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('is_clearance_company', '=', True)]</field>
<field name="context">{'default_is_clearance_company': True}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
[Clearance Company] Not yet! Click the Create button in the top left corner and the sofa is yours!
</p>
<p>
</p>
</field>
</record>
</data>
</odoo>
\ No newline at end of file
...@@ -31,6 +31,37 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -31,6 +31,37 @@ class BatchGetPodInfoWizard(models.TransientModel):
order_id = [self._context.get('active_id')] order_id = [self._context.get('active_id')]
return self.env['cc.bl'].browse(order_id) return self.env['cc.bl'].browse(order_id)
action_type = fields.Selection([
('获取尾程POD信息', '获取尾程POD信息'),
('创建temu提单', '创建temu提单'),
], string='Action Type', default='获取尾程POD信息')
# ============temu提单 批量创建操作相关字段============
# 客户:默认TEMU平台客户,任一一个,可修改
partner_id = fields.Many2one(
'res.partner',
string='Customer', # 客户
default=lambda self: self.env['res.partner'].get_type_partner('temu'),
)
# 提单号:可输入多个,一行一个
bl_numbers = fields.Text(
string='BL Number', # 提单号
help='Enter multiple BL numbers, one per line' # 输入多个BL号,每行一个
)
# 获取尾程POD信息☑️(默认勾选)
get_last_mile_pod = fields.Boolean(
string='Get Last Mile POD', # 获取尾程POD信息
default=True,
help='Whether to get last mile POD information' # 是否获取尾程POD信息
)
generate_successful_processed = fields.Boolean(
string='生成成功涂抹的提单附件信息', # 生成成功涂抹的提单附件信息
default=False,
help='勾选后显示确定按钮'
)
# ===========提单获取尾程pod操作相关字段============
sync_last_mile_pod = fields.Boolean( sync_last_mile_pod = fields.Boolean(
string='Sync Last Mile POD', # 同步尾程POD string='Sync Last Mile POD', # 同步尾程POD
default=False, default=False,
...@@ -62,11 +93,6 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -62,11 +93,6 @@ class BatchGetPodInfoWizard(models.TransientModel):
help='勾选后显示同步尾程POD和同步匹配节点的字段以及确定按钮' # 勾选后显示同步尾程POD和同步匹配节点的字段以及确定按钮 help='勾选后显示同步尾程POD和同步匹配节点的字段以及确定按钮' # 勾选后显示同步尾程POD和同步匹配节点的字段以及确定按钮
) )
# debug_mode = fields.Boolean(
# string='Debug Mode', # 调试模式
# default=False,
# help='Show red markers for deleted text positions' # 显示删除文字位置的红色标记
# )
show_error_message = fields.Text( show_error_message = fields.Text(
string='Show Error Message', string='Show Error Message',
help='Show error message' help='Show error message'
...@@ -76,31 +102,35 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -76,31 +102,35 @@ class BatchGetPodInfoWizard(models.TransientModel):
pdf_filename = fields.Char(string='PDF文件名称') pdf_filename = fields.Char(string='PDF文件名称')
processed_files_data = fields.Text(string='已处理的文件数据', help='存储已处理的文件信息(JSON格式)') processed_files_data = fields.Text(string='已处理的文件数据', help='存储已处理的文件信息(JSON格式)')
def _get_bill_numbers(self, bl_objs):
_logger.info(f"开始预览操作,提单数量: {len(bl_objs)}")
# 调用接口获取提单pdf文件
pdf_file_arr = self._get_pdf_file_arr(bl_objs)
# 处理PDF文件,匹配提单对象
processed_files = self._match_bl_by_file_name(pdf_file_arr, bl_objs)
# 把没有匹配到文件的进行提示
error_bl = []
matched_bl_ids = [f['bl'].id for f in processed_files if f.get('bl')]
for bl in bl_objs:
if bl.id not in matched_bl_ids:
error_bl.append(bl)
if error_bl:
logging.info('%s个提单无法找到release note文件' % len(error_bl))
if not self._context.get('is_skip_raise_error'):
self.show_error_message = _('%s bill of loading cannot find release note file') % (
', '.join([bl.bl_no for bl in error_bl]))
return processed_files
def action_preview(self): def action_preview(self):
""" """
预览操作:获取PDF、处理涂抹、合并PDF并显示 预览操作:获取PDF、处理涂抹、合并PDF并显示
""" """
action_type = self.action_type
self.show_error_message = False
try: try:
bl_objs = self.get_order() bl_objs = self.get_bl_objs() # 获取提单
_logger.info(f"开始预览操作,提单数量: {len(bl_objs)}")
# 调用接口获取提单pdf文件
pdf_file_arr = self._get_pdf_file_arr()
# 处理PDF文件,匹配提单对象 # 处理PDF文件,匹配提单对象
processed_files = self._match_bl_by_file_name(pdf_file_arr) processed_files = self._get_bill_numbers(bl_objs)
# 把没有匹配到文件的进行提示
error_bl = []
matched_bl_ids = [f['bl'].id for f in processed_files if f.get('bl')]
for bl in bl_objs:
if bl.id not in matched_bl_ids:
error_bl.append(bl)
if error_bl:
logging.info('%s个提单无法找到release note文件' % len(error_bl))
if not self._context.get('is_skip_raise_error'):
self.show_error_message = _('%s bill of loading cannot find release note file') % (
', '.join([bl.bl_no for bl in error_bl]))
# 如果启用了涂抹文字,进行处理 # 如果启用了涂抹文字,进行处理
if self.remove_specified_text and processed_files: if self.remove_specified_text and processed_files:
processed_files = self._remove_specified_text(processed_files, debug_mode=False) processed_files = self._remove_specified_text(processed_files, debug_mode=False)
...@@ -109,15 +139,15 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -109,15 +139,15 @@ class BatchGetPodInfoWizard(models.TransientModel):
successful_files = [] successful_files = []
failed_files = [] failed_files = []
for file_info in processed_files: for file_info in processed_files:
if file_info.get('bl'): if file_info.get('bl_no'):
bl = file_info.get('bl') bl_no = file_info.get('bl_no')
file_data = file_info.get('file_data', '') file_data = file_info.get('file_data', '')
# 检查处理是否失败(通过processing_failed标记或错误消息) # 检查处理是否失败(通过processing_failed标记或错误消息)
processing_failed = file_info.get('processing_failed', False) processing_failed = file_info.get('processing_failed', False)
has_error = False has_error = False
if self.show_error_message: if self.show_error_message:
error_msg = str(self.show_error_message) error_msg = str(self.show_error_message)
if bl and bl.bl_no in error_msg: if bl_no in error_msg:
has_error = True has_error = True
# 如果处理失败或者有错误,则认为失败 # 如果处理失败或者有错误,则认为失败
...@@ -133,17 +163,20 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -133,17 +163,20 @@ class BatchGetPodInfoWizard(models.TransientModel):
# 如果所有文件都成功了(没有失败的文件),自动勾选"是否同步成功涂抹的提单" # 如果所有文件都成功了(没有失败的文件),自动勾选"是否同步成功涂抹的提单"
if len(successful_files) == len(processed_files) and not failed_files and not self.show_error_message: if len(successful_files) == len(processed_files) and not failed_files and not self.show_error_message:
self.sync_successful_processed = True if action_type == '获取尾程POD信息':
_logger.info(f"所有提单都处理成功,自动勾选同步选项") self.sync_successful_processed = True
_logger.info(f"所有提单都处理成功,自动勾选同步选项")
else:
self.generate_successful_processed = True
else: else:
# 有失败的文件,保持未勾选状态,让用户决定 # 有失败的文件,保持未勾选状态,让用户决定
# 将成功处理的提单号追加到show_error_message中 # 将成功处理的提单号追加到show_error_message中
if successful_files: if successful_files:
successful_bl_nos = [] successful_bl_nos = []
for file_info in successful_files: for file_info in successful_files:
if file_info.get('bl'): if file_info.get('bl_no'):
bl = file_info['bl'] bl_no = file_info['bl_no']
successful_bl_nos.append(bl.bl_no) successful_bl_nos.append(bl_no)
# 在错误消息后面追加成功处理的提单号 # 在错误消息后面追加成功处理的提单号
existing_error = self.show_error_message or '' existing_error = self.show_error_message or ''
successful_bl_nos_str = '、'.join(successful_bl_nos) if successful_bl_nos else '' successful_bl_nos_str = '、'.join(successful_bl_nos) if successful_bl_nos else ''
...@@ -151,7 +184,10 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -151,7 +184,10 @@ class BatchGetPodInfoWizard(models.TransientModel):
self.show_error_message = f"{existing_error}{success_msg}" self.show_error_message = f"{existing_error}{success_msg}"
_logger.info( _logger.info(
f"部分提单处理失败(成功:{len(successful_files)},失败:{len(failed_files)}),成功处理的提单号已显示") f"部分提单处理失败(成功:{len(successful_files)},失败:{len(failed_files)}),成功处理的提单号已显示")
self.sync_successful_processed = False if action_type == '获取尾程POD信息':
self.sync_successful_processed = False
else:
self.generate_successful_processed = False
# 序列化并存储处理后的文件数据(包括成功和失败的,但只有成功的才会合并PDF) # 序列化并存储处理后的文件数据(包括成功和失败的,但只有成功的才会合并PDF)
if processed_files: if processed_files:
...@@ -164,35 +200,42 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -164,35 +200,42 @@ class BatchGetPodInfoWizard(models.TransientModel):
self.processed_files_data = self._serialize_processed_files(processed_files) self.processed_files_data = self._serialize_processed_files(processed_files)
successful_bl_data = [] successful_bl_data = []
for file_info in processed_files: for file_info in processed_files:
if file_info.get('bl'): if file_info.get('bl_no'):
bl = file_info['bl'] bl_no = file_info['bl_no']
successful_bl_data.append({ successful_bl_data.append({
'bl_id': bl.id, 'bl_no': bl_no
'bl_no': bl.bl_no
}) })
# 没有涂抹文字,所有文件都成功,自动勾选 # 没有涂抹文字,所有文件都成功,自动勾选
self.sync_successful_processed = True if action_type == '获取尾程POD信息':
_logger.info(f"未启用涂抹文字,所有提单都成功,自动勾选同步选项") self.sync_successful_processed = True
_logger.info(f"未启用涂抹文字,所有提单都成功,自动勾选同步选项")
else:
self.generate_successful_processed = True
else: else:
self.processed_files_data = '' self.processed_files_data = ''
self.sync_successful_processed = False if action_type == '获取尾程POD信息':
self.sync_successful_processed = False
else:
self.generate_successful_processed = False
# 返回表单视图 # 返回表单视图
return { return {
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
'name': _('Batch Get POD Info'), 'name': _('Batch Get POD Info') if action_type == '获取尾程POD信息' else _(
'Create TEMU Bill of Loading'),
'res_model': 'batch.get.pod.info.wizard', 'res_model': 'batch.get.pod.info.wizard',
'view_mode': 'form', 'view_mode': 'form',
'res_id': self.id, 'res_id': self.id,
'target': 'new', 'target': 'new',
'context': {'active_id': bl_objs.ids} 'context': {'active_id': bl_objs.ids if bl_objs else False}
} }
except Exception as e: except Exception as e:
_logger.error(f"预览操作失败: {str(e)}") _logger.error(f"预览操作失败: {str(e)}")
self.show_error_message = _('预览操作失败: %s') % str(e) self.show_error_message = _('预览操作失败: %s') % str(e)
return { return {
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
'name': _('Batch Get POD Info'), 'name': _('Batch Get POD Info') if action_type == '获取尾程POD信息' else _(
'Create TEMU Bill of Loading'),
'res_model': 'batch.get.pod.info.wizard', 'res_model': 'batch.get.pod.info.wizard',
'view_mode': 'form', 'view_mode': 'form',
'res_id': self.id, 'res_id': self.id,
...@@ -207,116 +250,107 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -207,116 +250,107 @@ class BatchGetPodInfoWizard(models.TransientModel):
# 计算整个过程的耗时 # 计算整个过程的耗时
start_time = time.time() start_time = time.time()
self.show_error_message = False self.show_error_message = False
bl_objs = self.get_order() action_type = self.action_type
_logger.info(f"%s提单开始执行批量获取POD信息操作" % len(bl_objs)) bl_objs = self.get_bl_objs()
if action_type == '获取尾程POD信息' or (action_type == '创建temu提单' and self.get_last_mile_pod):
# 优先使用已处理的文件数据(预览时已处理) # 优先使用已处理的文件数据(预览时已处理)
processed_files = None processed_files = None
if self.processed_files_data: if self.processed_files_data:
processed_files = self._deserialize_processed_files(self.processed_files_data) processed_files = self._deserialize_processed_files(self.processed_files_data)
_logger.info(f"使用已处理的文件数据,共 {len(processed_files)} 个文件") _logger.info(f"使用已处理的文件数据,共 {len(processed_files)} 个文件")
# 检查文件数据是否完整 # 检查文件数据是否完整
valid_files = [] valid_files = []
for file_info in processed_files: for file_info in processed_files:
if file_info.get('file_data'): if file_info.get('file_data'):
valid_files.append(file_info) valid_files.append(file_info)
else: else:
_logger.warning(f"提单 {file_info.get('bl', {}).get('bl_no', 'Unknown')} 的文件数据为空") _logger.warning(f"提单 {file_info.get('bl', {}).get('bl_no', 'Unknown')} 的文件数据为空")
processed_files = valid_files processed_files = valid_files
_logger.info(f"有效文件数量: {len(processed_files)}") _logger.info(f"有效文件数量: {len(processed_files)}")
# 如果没有已处理的数据,则执行处理流程 # 如果没有已处理的数据,则执行处理流程
if not processed_files: if not processed_files:
# 调用接口获取提单pdf文件 # 处理PDF文件,匹配提单对象
pdf_file_arr = self._get_pdf_file_arr() bl_objs = self.get_bl_objs() # 获取提单
# 处理PDF文件,匹配提单对象 processed_files = self._get_bill_numbers(bl_objs)
processed_files = self._match_bl_by_file_name(pdf_file_arr) # 如果启用了涂抹文字,进行处理
# 把没有匹配到文件的进行提示 if self.remove_specified_text and processed_files:
error_bl = [] processed_files = self._remove_specified_text(processed_files, debug_mode=False)
matched_bl_ids = [f['bl'].id for f in processed_files if f.get('bl')] # 合并PDF并保存到pdf_file字段
for bl in bl_objs: self._merge_pdf_files(processed_files)
if bl.id not in matched_bl_ids: # 如果有处理后的文件,序列化存储
error_bl.append(bl) if processed_files:
if error_bl: self.processed_files_data = self._serialize_processed_files(processed_files)
logging.info('%s个提单无法找到release note文件' % len(error_bl)) # 跳转到本向导的form视图(显示合并后的PDF)
# 英文提示 return {
if not self._context.get('is_skip_raise_error'): 'type': 'ir.actions.act_window',
self.show_error_message = _('%s bill of loading cannot find release note file') % ( 'res_model': 'batch.get.pod.info.wizard',
', '.join([bl.bl_no for bl in error_bl])) 'name': _('Batch Get POD Info') if action_type == '获取尾程POD信息' else _(
'Create TEMU Bill of Loading'),
'view_mode': 'form',
'res_id': self.id,
'target': 'new',
'context': {'active_id': bl_objs.ids, }
}
# 如果启用了涂抹文字,进行处理 # 检查是否有文字清除失败的错误
if self.remove_specified_text and processed_files: if self.show_error_message and any(
processed_files = self._remove_specified_text(processed_files, debug_mode=False) '仍存在目标文字' in str(self.show_error_message) or '未完全清除文字' in str(
# 合并PDF并保存到pdf_file字段 self.show_error_message)):
self._merge_pdf_files(processed_files) _logger.error(f"检测到文字清除失败,停止处理: {self.show_error_message}")
# 如果有处理后的文件,序列化存储
if processed_files:
self.processed_files_data = self._serialize_processed_files(processed_files)
# 跳转到本向导的form视图(显示合并后的PDF)
return { return {
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
'res_model': 'batch.get.pod.info.wizard', 'res_model': 'batch.get.pod.info.wizard',
'name': _('Batch Get POD Info'), 'name': _('Batch Get POD Info') if action_type == '获取尾程POD信息' else _(
'Create TEMU Bill of Loading'),
'view_mode': 'form', 'view_mode': 'form',
'res_id': self.id, 'res_id': self.id,
'target': 'new', 'target': 'new',
'context': {'active_id': bl_objs.ids, } 'context': {'default_show_error_message': self.show_error_message,
'active_id': bl_objs.ids if bl_objs else False}
} }
# 检查是否有文字清除失败的错误 # 只处理成功涂抹的提单
if self.show_error_message and any( # 直接根据processed_files中的processing_failed标志筛选成功处理的文件,无需从文本解析
'仍存在目标文字' in str(self.show_error_message) or '未完全清除文字' in str(self.show_error_message)): successful_processed_files = []
_logger.error(f"检测到文字清除失败,停止处理: {self.show_error_message}") if processed_files:
return { for file_info in processed_files:
'type': 'ir.actions.act_window', # 检查是否有bl对象和有文件数据
'res_model': 'batch.get.pod.info.wizard', if not file_info.get('bl'):
'name': _('Batch Get POD Info'), continue
'view_mode': 'form', file_data = file_info.get('file_data', '')
'res_id': self.id, # 检查处理是否失败(通过processing_failed标记)
'target': 'new', processing_failed = file_info.get('processing_failed', False)
'context': {'default_show_error_message': self.show_error_message, 'active_id': bl_objs.ids} # 如果有文件数据且未标记为失败,则认为处理成功
} if file_data and not processing_failed:
successful_processed_files.append(file_info)
# 只处理成功涂抹的提单 _logger.info(f"从{len(processed_files)}个文件中筛选出{len(successful_processed_files)}个成功处理的文件")
# 直接根据processed_files中的processing_failed标志筛选成功处理的文件,无需从文本解析
successful_processed_files = []
if processed_files:
for file_info in processed_files:
# 检查是否有bl对象和有文件数据
if not file_info.get('bl'):
continue
bl = file_info['bl']
file_data = file_info.get('file_data', '')
# 检查处理是否失败(通过processing_failed标记)
processing_failed = file_info.get('processing_failed', False)
# 如果有文件数据且未标记为失败,则认为处理成功
if file_data and not processing_failed:
successful_processed_files.append(file_info)
_logger.info(f"从{len(processed_files)}个文件中筛选出{len(successful_processed_files)}个成功处理的文件")
else:
_logger.warning("没有找到已处理的文件数据")
# 回写到附件信息
if successful_processed_files and (self.sync_last_mile_pod or self.sync_match_node):
# 回写PDF文件到清关文件
self._write_pdf_file(successful_processed_files)
# 再同步和回写
if self.sync_last_mile_pod and successful_processed_files:
self._sync_last_mile_pod(successful_processed_files)
# 同步推送匹配节点
if self.sync_match_node and successful_processed_files:
# 且需先对比小包当前节点的操作时间是否小于提取时间(同时区对比)若大于则不能推送,
# 若需补推节点,则需判断提取时间-写入节点(不取写入第一个节点)的前序间隔时间是否大于小包当前节点的操作时间。
# 若不满足以上条件,则不执行生成和自动推送节点,并在小包上新增推送备注(新增该字段)回写备注信息:获取尾程POD,自动推送节点失败,有风险产生倒挂。请手动操作205-10-20 10:20:20(获取时间)
valid_files = self._validate_node_push_conditions(successful_processed_files)
# raise ValidationError('通知')
if valid_files:
self.get_date_sync_match_node(valid_files)
else: else:
_logger.info(f"没有满足条件的文件,不执行生成和自动推送节点") _logger.warning("没有找到已处理的文件数据")
# 回写到附件信息
if successful_processed_files and (
(self.sync_last_mile_pod or self.sync_match_node) or self.get_last_mile_pod):
# 回写PDF文件到清关文件
self._write_pdf_file(successful_processed_files)
# 再同步和回写
if action_type == '获取尾程POD信息':
if self.sync_last_mile_pod and successful_processed_files:
self._sync_last_mile_pod(successful_processed_files)
# 同步推送匹配节点
if self.sync_match_node and successful_processed_files:
# 且需先对比小包当前节点的操作时间是否小于提取时间(同时区对比)若大于则不能推送,
# 若需补推节点,则需判断提取时间-写入节点(不取写入第一个节点)的前序间隔时间是否大于小包当前节点的操作时间。
# 若不满足以上条件,则不执行生成和自动推送节点,并在小包上新增推送备注(新增该字段)回写备注信息:获取尾程POD,自动推送节点失败,有风险产生倒挂。请手动操作205-10-20 10:20:20(获取时间)
valid_files = self._validate_node_push_conditions(successful_processed_files)
# raise ValidationError('通知')
if valid_files:
self.get_date_sync_match_node(valid_files)
else:
_logger.info(f"没有满足条件的文件,不执行生成和自动推送节点")
# 清理所有临时文件(包括数据库记录和物理文件),不能删,不然回写的时候没有文件了 # 清理所有临时文件(包括数据库记录和物理文件),不能删,不然回写的时候没有文件了
self._cleanup_temp_attachments(bl_objs) self._cleanup_temp_attachments(bl_objs)
...@@ -327,13 +361,66 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -327,13 +361,66 @@ class BatchGetPodInfoWizard(models.TransientModel):
return { return {
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
'res_model': 'batch.get.pod.info.wizard', 'res_model': 'batch.get.pod.info.wizard',
'name': _('Batch Get POD Info'), 'name': _('Batch Get POD Info') if action_type == '获取尾程POD信息' else _(
'Create TEMU Bill of Loading'),
'view_mode': 'form', 'view_mode': 'form',
'res_id': self.id, 'res_id': self.id,
'target': 'new', 'target': 'new',
'context': {'default_show_error_message': self.show_error_message, 'active_id': bl_objs.ids} 'context': {'default_show_error_message': self.show_error_message,
'active_id': bl_objs.ids if bl_objs else False}
}
if action_type == '创建temu提单':
return {
'type': 'ir.actions.act_window',
'res_model': 'cc.bl',
'name': _('TEMU Bill of Loading'),
'view_mode': 'list,form',
'domain': [('bl_type', '=', 'temu')],
'context': {'default_bl_type': 'temu'},
'target': 'current',
} }
def get_bl_objs(self):
if self.action_type == '获取尾程POD信息':
bl_objs = self.get_order()
else:
raw_lines = [i.strip() for i in self.bl_numbers.splitlines() if i.strip()]
normalized_list = [self.env['common.common'].sudo().process_match_str(i) for i in raw_lines]
norm_to_raw = {}
for raw, norm in zip(raw_lines, normalized_list):
if norm not in norm_to_raw:
norm_to_raw[norm] = raw
normalized = sorted(set(normalized_list))
exist_set = set()
if normalized:
self._cr.execute(
"select UPPER(REPLACE(REPLACE(REPLACE(bl_no,' ', ''), '-', ''), '/', '')) from cc_bl where UPPER(REPLACE(REPLACE(REPLACE(bl_no,' ', ''), '-', ''), '/', '')) in %s",
(tuple(normalized),))
exist_set.update([r[0] for r in self._cr.fetchall()])
self._cr.execute(
"select UPPER(REPLACE(REPLACE(REPLACE(transfer_bl_no,' ', ''), '-', ''), '/', '')) from cc_bl where transfer_bl_no is not null and UPPER(REPLACE(REPLACE(REPLACE(transfer_bl_no,' ', ''), '-', ''), '/', '')) in %s",
(tuple(normalized),))
exist_set.update([r[0] for r in self._cr.fetchall()])
non_exist_norm = [n for n in normalized if n not in exist_set]
create_vals = [{
'customer_id': self.partner_id.id,
'bl_type': 'temu',
'bl_no': norm_to_raw[n]
} for n in non_exist_norm]
new_bl = self.env['cc.bl'].create(create_vals) if create_vals else self.env['cc.bl']
ids = []
if normalized:
self._cr.execute(
"select id from cc_bl where UPPER(REPLACE(REPLACE(REPLACE(bl_no,' ', ''), '-', ''), '/', '')) in %s",
(tuple(normalized),))
ids += [r[0] for r in self._cr.fetchall()]
self._cr.execute(
"select id from cc_bl where transfer_bl_no is not null and UPPER(REPLACE(REPLACE(REPLACE(transfer_bl_no,' ', ''), '-', ''), '/', '')) in %s",
(tuple(normalized),))
ids += [r[0] for r in self._cr.fetchall()]
bl_objs = self.env['cc.bl'].browse(list(set(ids))) | new_bl
return bl_objs
def _validate_node_push_conditions(self, processed_files): def _validate_node_push_conditions(self, processed_files):
""" """
验证节点推送条件 验证节点推送条件
...@@ -343,14 +430,11 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -343,14 +430,11 @@ class BatchGetPodInfoWizard(models.TransientModel):
return processed_files return processed_files
# 写一个方法调接口获取提单pdf文件 # 写一个方法调接口获取提单pdf文件
def _get_pdf_file_arr(self): def _get_pdf_file_arr(self, bl_objs):
""" """
从API获取PDF文件 从API获取PDF文件
""" """
# 获取当前选中的提单对象
bl_objs = self.get_order()
bill_numbers = [self.env['common.common'].sudo().process_match_str(bl.bl_no) for bl in bl_objs] bill_numbers = [self.env['common.common'].sudo().process_match_str(bl.bl_no) for bl in bl_objs]
# 调用API获取PDF文件 # 调用API获取PDF文件
api_url = self.env['ir.config_parameter'].sudo().get_param('last_mile_pod_api_url', api_url = self.env['ir.config_parameter'].sudo().get_param('last_mile_pod_api_url',
'http://172.104.52.150:7002') 'http://172.104.52.150:7002')
...@@ -472,7 +556,7 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -472,7 +556,7 @@ class BatchGetPodInfoWizard(models.TransientModel):
# 过滤有效的PDF文件 # 过滤有效的PDF文件
valid_files = [] valid_files = []
for file_info in processed_files: for file_info in processed_files:
if file_info.get('bl') and file_info.get('file_data'): if file_info.get('bl_no') and file_info.get('file_data'):
valid_files.append(file_info) valid_files.append(file_info)
if not valid_files: if not valid_files:
...@@ -483,12 +567,13 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -483,12 +567,13 @@ class BatchGetPodInfoWizard(models.TransientModel):
if len(valid_files) == 1: if len(valid_files) == 1:
file_info = valid_files[0] file_info = valid_files[0]
bl = file_info['bl'] bl = file_info['bl']
bl_no = bl.bl_no
file_data = file_info['file_data'] file_data = file_info['file_data']
file_name = file_info.get('file_name', f"{bl.bl_no}.pdf") file_name = file_info.get('file_name', f"{bl_no}.pdf")
# 生成文件名(包含提单号和日期) # 生成文件名(包含提单号和日期)
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
pdf_filename = f"POD文件_{bl.bl_no}_{timestamp}.pdf" pdf_filename = f"POD文件_{bl_no}_{timestamp}.pdf"
# 直接保存到字段 # 直接保存到字段
self.write({ self.write({
...@@ -515,11 +600,10 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -515,11 +600,10 @@ class BatchGetPodInfoWizard(models.TransientModel):
for file_info in batch_files: for file_info in batch_files:
bl = file_info['bl'] bl = file_info['bl']
bl_no = bl.bl_no
file_data = file_info['file_data'] file_data = file_info['file_data']
bl_numbers.append(bl.bl_no) bl_numbers.append(bl_no)
source_pdf = None source_pdf = None
pdf_binary = None
try: try:
# 将base64数据转换为二进制 # 将base64数据转换为二进制
pdf_binary = base64.b64decode(file_data) pdf_binary = base64.b64decode(file_data)
...@@ -530,17 +614,15 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -530,17 +614,15 @@ class BatchGetPodInfoWizard(models.TransientModel):
# 将源PDF的所有页面插入到合并的PDF中 # 将源PDF的所有页面插入到合并的PDF中
merged_pdf.insert_pdf(source_pdf) merged_pdf.insert_pdf(source_pdf)
_logger.info(f"已添加提单 {bl.bl_no} 的PDF到合并文档({len(source_pdf)} 页)") _logger.info(f"已添加提单 {bl_no} 的PDF到合并文档({len(source_pdf)} 页)")
except Exception as e: except Exception as e:
_logger.error(f"合并提单 {bl.bl_no} 的PDF失败: {str(e)}") _logger.error(f"合并提单 {bl_no} 的PDF失败: {str(e)}")
continue continue
finally: finally:
# 立即释放资源 # 立即释放资源
if source_pdf: if source_pdf:
source_pdf.close() source_pdf.close()
source_pdf = None
pdf_binary = None
gc.collect() # 强制垃圾回收 gc.collect() # 强制垃圾回收
# 每批处理完后,保存到临时文件并释放内存 # 每批处理完后,保存到临时文件并释放内存
...@@ -603,13 +685,12 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -603,13 +685,12 @@ class BatchGetPodInfoWizard(models.TransientModel):
except Exception as e: except Exception as e:
_logger.warning(f"删除临时文件失败: {str(e)}") _logger.warning(f"删除临时文件失败: {str(e)}")
def _match_bl_by_file_name(self, pdf_file_arr): def _match_bl_by_file_name(self, pdf_file_arr, bl_obj):
""" """
Match BL by file name and return processed array # 根据文件名匹配提单并返回处理后的数组 Match BL by file name and return processed array # 根据文件名匹配提单并返回处理后的数组
:param pdf_file_arr: PDF文件数组 [{'bill_number':'', 'filename':'', 'file_data':''}] :param pdf_file_arr: PDF文件数组 [{'bill_number':'', 'filename':'', 'file_data':''}]
:return: 处理后的数组 [{'bl': bl_obj, 'file_name': 'xxx.pdf', 'file_data': 'xxx', 'matched': True/False}] :return: 处理后的数组 [{'bl': bl_obj, 'file_name': 'xxx.pdf', 'file_data': 'xxx', 'matched': True/False}]
""" """
bl_obj = self.get_order() # 获取当前选中的提单对象
processed_files = [] processed_files = []
for bl in bl_obj: for bl in bl_obj:
select_bl_no = self.env['common.common'].sudo().process_match_str(bl.bl_no) select_bl_no = self.env['common.common'].sudo().process_match_str(bl.bl_no)
...@@ -796,10 +877,10 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -796,10 +877,10 @@ class BatchGetPodInfoWizard(models.TransientModel):
skip_ocr = self.skip_ocr_direct_ai # 是否跳过OCR直接使用AI skip_ocr = self.skip_ocr_direct_ai # 是否跳过OCR直接使用AI
for file_info in processed_files: for file_info in processed_files:
if not file_info['bl']: if not file_info['bl_no']:
updated_files.append(file_info) updated_files.append(file_info)
continue continue
bl = file_info['bl'] bl_no = file_info['bl_no']
file_data = file_info['file_data'] file_data = file_info['file_data']
processed_file_data = file_data # 默认使用原始数据 processed_file_data = file_data # 默认使用原始数据
processing_failed = False # 标记处理是否失败 processing_failed = False # 标记处理是否失败
...@@ -810,15 +891,15 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -810,15 +891,15 @@ class BatchGetPodInfoWizard(models.TransientModel):
# 先提取文本用于后续同步节点功能(如果需要的话) # 先提取文本用于后续同步节点功能(如果需要的话)
if 'ocr_texts' not in file_info: if 'ocr_texts' not in file_info:
file_info['ocr_texts'] = self._extract_text_from_pdf_with_ocr(pdf_binary, bl.bl_no) file_info['ocr_texts'] = self._extract_text_from_pdf_with_ocr(pdf_binary, bl_no)
# 如果跳过OCR,直接使用AI处理 # 如果跳过OCR,直接使用AI处理
if skip_ocr: if skip_ocr:
_logger.info(f"提单 {bl.bl_no} 跳过OCR,直接使用AI处理") _logger.info(f"提单 {bl_no} 跳过OCR,直接使用AI处理")
try: try:
ai_processed_pdf = self._process_pdf_with_ai_image_edit( ai_processed_pdf = self._process_pdf_with_ai_image_edit(
pdf_data=pdf_binary, pdf_data=pdf_binary,
bl_no=bl.bl_no bl_no=bl_no
) )
if ai_processed_pdf: if ai_processed_pdf:
processed_file_data = base64.b64encode(ai_processed_pdf).decode('utf-8') processed_file_data = base64.b64encode(ai_processed_pdf).decode('utf-8')
...@@ -826,50 +907,50 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -826,50 +907,50 @@ class BatchGetPodInfoWizard(models.TransientModel):
# 检查是否还存在目标文字 # 检查是否还存在目标文字
final_check_pdf = base64.b64decode(processed_file_data) final_check_pdf = base64.b64decode(processed_file_data)
text_still_exists, final_found_texts = self._check_target_texts_exist(final_check_pdf, text_still_exists, final_found_texts = self._check_target_texts_exist(final_check_pdf,
bl.bl_no) bl_no)
if text_still_exists: if text_still_exists:
error_msg = f"提单 {bl.bl_no} 经过AI处理后仍存在目标文字: {', '.join(final_found_texts)},请取消该提单操作,手动处理" error_msg = f"提单 {bl_no} 经过AI处理后仍存在目标文字: {', '.join(final_found_texts)},请取消该提单操作,手动处理"
_logger.error(error_msg) _logger.error(error_msg)
error_messages.append(error_msg) error_messages.append(error_msg)
# 不更新文件数据,保持原始状态 # 不更新文件数据,保持原始状态
processed_file_data = file_data processed_file_data = file_data
processing_failed = True processing_failed = True
else: else:
_logger.info(f"提单 {bl.bl_no} AI处理成功,目标文字已清除") _logger.info(f"提单 {bl_no} AI处理成功,目标文字已清除")
else: else:
error_msg = f"提单 {bl.bl_no} AI处理失败" error_msg = f"提单 {bl_no} AI处理失败"
_logger.error(error_msg) _logger.error(error_msg)
error_messages.append(error_msg) error_messages.append(error_msg)
processing_failed = True processing_failed = True
except Exception as e: except Exception as e:
_logger.error(f"提单 {bl.bl_no} AI处理异常: {str(e)}") _logger.error(f"提单 {bl_no} AI处理异常: {str(e)}")
error_msg = f"提单 {bl.bl_no} AI处理异常: {str(e)}" error_msg = f"提单 {bl_no} AI处理异常: {str(e)}"
error_messages.append(error_msg) error_messages.append(error_msg)
processing_failed = True processing_failed = True
else: else:
# 原有逻辑:先用OCR处理,如果还存在则用AI处理 # 原有逻辑:先用OCR处理,如果还存在则用AI处理
# 第一步:使用OCR方法处理PDF # 第一步:使用OCR方法处理PDF
_logger.info(f"提单 {bl.bl_no} 开始OCR处理") _logger.info(f"提单 {bl_no} 开始OCR处理")
try: try:
processed_pdf = self._process_pdf_with_ocr( processed_pdf = self._process_pdf_with_ocr(
pdf_data=pdf_binary, pdf_data=pdf_binary,
bl_no=bl.bl_no, bl_no=bl_no,
debug_mode=debug_mode debug_mode=debug_mode
) )
if processed_pdf: if processed_pdf:
processed_file_data = base64.b64encode(processed_pdf).decode('utf-8') processed_file_data = base64.b64encode(processed_pdf).decode('utf-8')
# 第二步:检查是否还存在目标文字 # 第二步:检查是否还存在目标文字
pdf_for_check = base64.b64decode(processed_file_data) pdf_for_check = base64.b64decode(processed_file_data)
text_exists, found_texts = self._check_target_texts_exist(pdf_for_check, bl.bl_no) text_exists, found_texts = self._check_target_texts_exist(pdf_for_check, bl_no)
logging.info(f"ocr处理之后的text_exists: {text_exists}") logging.info(f"ocr处理之后的text_exists: {text_exists}")
if text_exists: if text_exists:
# 第三步:如果还存在,使用AI图片编辑处理 # 第三步:如果还存在,使用AI图片编辑处理
_logger.info(f"提单 {bl.bl_no} OCR处理后仍存在目标文字,使用AI图片编辑处理") _logger.info(f"提单 {bl_no} OCR处理后仍存在目标文字,使用AI图片编辑处理")
try: try:
ai_processed_pdf = self._process_pdf_with_ai_image_edit( ai_processed_pdf = self._process_pdf_with_ai_image_edit(
pdf_data=pdf_for_check, pdf_data=pdf_for_check,
bl_no=bl.bl_no bl_no=bl_no
) )
if ai_processed_pdf: if ai_processed_pdf:
processed_file_data = base64.b64encode(ai_processed_pdf).decode('utf-8') processed_file_data = base64.b64encode(ai_processed_pdf).decode('utf-8')
...@@ -877,51 +958,51 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -877,51 +958,51 @@ class BatchGetPodInfoWizard(models.TransientModel):
# 第四步:再次检查是否还存在目标文字 # 第四步:再次检查是否还存在目标文字
final_check_pdf = base64.b64decode(processed_file_data) final_check_pdf = base64.b64decode(processed_file_data)
text_still_exists, final_found_texts = self._check_target_texts_exist( text_still_exists, final_found_texts = self._check_target_texts_exist(
final_check_pdf, bl.bl_no) final_check_pdf, bl_no)
if text_still_exists: if text_still_exists:
# 第五步:如果仍然存在,记录错误信息并停止处理 # 第五步:如果仍然存在,记录错误信息并停止处理
error_msg = f"提单 {bl.bl_no} 经过系统处理后仍存在目标文字: {', '.join(final_found_texts)},请取消该提单操作,手动处理" error_msg = f"提单 {bl_no} 经过系统处理后仍存在目标文字: {', '.join(final_found_texts)},请取消该提单操作,手动处理"
_logger.error(error_msg) _logger.error(error_msg)
error_messages.append(error_msg) error_messages.append(error_msg)
# 不更新文件数据,保持原始状态 # 不更新文件数据,保持原始状态
processed_file_data = file_data processed_file_data = file_data
processing_failed = True processing_failed = True
else: else:
_logger.warning(f"提单 {bl.bl_no} AI处理失败,检查OCR处理结果") _logger.warning(f"提单 {bl_no} AI处理失败,检查OCR处理结果")
# AI处理失败,检查OCR结果是否真的清除了目标文字 # AI处理失败,检查OCR结果是否真的清除了目标文字
ocr_check_pdf = base64.b64decode(processed_file_data) ocr_check_pdf = base64.b64decode(processed_file_data)
text_still_exists, ocr_found_texts = self._check_target_texts_exist( text_still_exists, ocr_found_texts = self._check_target_texts_exist(
ocr_check_pdf, bl.bl_no) ocr_check_pdf, bl_no)
if text_still_exists: if text_still_exists:
error_msg = f"提单 {bl.bl_no} 经过系统处理后仍存在目标文字: {', '.join(ocr_found_texts)},请取消该提单操作,手动处理" error_msg = f"提单 {bl_no} 经过系统处理后仍存在目标文字: {', '.join(ocr_found_texts)},请取消该提单操作,手动处理"
error_messages.append(error_msg) error_messages.append(error_msg)
# 不更新文件数据,保持原始状态 # 不更新文件数据,保持原始状态
processed_file_data = file_data processed_file_data = file_data
processing_failed = True processing_failed = True
else: else:
_logger.info(f"提单 {bl.bl_no} OCR处理成功,目标文字已清除") _logger.info(f"提单 {bl_no} OCR处理成功,目标文字已清除")
except Exception as e: except Exception as e:
_logger.error(f"提单 {bl.bl_no} AI处理异常: {str(e)}") _logger.error(f"提单 {bl_no} AI处理异常: {str(e)}")
# AI处理失败,使用OCR结果,但需要检查 # AI处理失败,使用OCR结果,但需要检查
final_check_pdf = base64.b64decode(processed_file_data) final_check_pdf = base64.b64decode(processed_file_data)
text_still_exists, final_found_texts = self._check_target_texts_exist( text_still_exists, final_found_texts = self._check_target_texts_exist(
final_check_pdf, bl.bl_no) final_check_pdf, bl_no)
if text_still_exists: if text_still_exists:
error_msg = f"提单 {bl.bl_no} 经过系统处理后仍存在目标文字: {', '.join(final_found_texts)},请取消该提单操作,手动处理" error_msg = f"提单 {bl_no} 经过系统处理后仍存在目标文字: {', '.join(final_found_texts)},请取消该提单操作,手动处理"
error_messages.append(error_msg) error_messages.append(error_msg)
# 不更新文件数据,保持原始状态 # 不更新文件数据,保持原始状态
processed_file_data = file_data processed_file_data = file_data
processing_failed = True processing_failed = True
else: else:
_logger.info(f"提单 {bl.bl_no} OCR处理成功,目标文字已清除") _logger.info(f"提单 {bl_no} OCR处理成功,目标文字已清除")
else: else:
_logger.warning(f"提单 {bl.bl_no} OCR处理失败") _logger.warning(f"提单 {bl_no} OCR处理失败")
error_messages.append(f"提单 {bl.bl_no} OCR处理失败") error_messages.append(f"提单 {bl_no} OCR处理失败")
processing_failed = True processing_failed = True
except Exception as e: except Exception as e:
_logger.error(f"提单 {bl.bl_no} OCR处理异常: {str(e)}") _logger.error(f"提单 {bl_no} OCR处理异常: {str(e)}")
error_messages.append(f"提单 {bl.bl_no} OCR处理异常: {str(e)}") error_messages.append(f"提单 {bl_no} OCR处理异常: {str(e)}")
processing_failed = True processing_failed = True
# 更新文件信息,使用处理后的PDF数据 # 更新文件信息,使用处理后的PDF数据
...@@ -2468,8 +2549,6 @@ class BatchGetPodInfoWizard(models.TransientModel): ...@@ -2468,8 +2549,6 @@ class BatchGetPodInfoWizard(models.TransientModel):
temp_attachments = self.env['ir.attachment'].sudo().browse(attachment_ids) temp_attachments = self.env['ir.attachment'].sudo().browse(attachment_ids)
attachment_count = len(temp_attachments) attachment_count = len(temp_attachments)
attachment_names = [att.name for att in temp_attachments]
_logger.info(f"找到 {attachment_count} 个{one_day_ago.strftime('%Y-%m-%d')}之前创建的临时附件,开始清理") _logger.info(f"找到 {attachment_count} 个{one_day_ago.strftime('%Y-%m-%d')}之前创建的临时附件,开始清理")
# 删除物理文件 # 删除物理文件
......
...@@ -13,8 +13,8 @@ ...@@ -13,8 +13,8 @@
<group> <group>
<!-- attrs="{'invisible': [('pdf_file', '!=', False)]}" --> <!-- attrs="{'invisible': [('pdf_file', '!=', False)]}" -->
<field name="remove_specified_text" readonly="1" widget="boolean_toggle"/> <field name="remove_specified_text" readonly="1" widget="boolean_toggle"/>
<field name="skip_ocr_direct_ai" readonly="0" widget="boolean_toggle" <field name="skip_ocr_direct_ai" invisible="1" widget="boolean_toggle"/>
attrs="{'invisible': [('pdf_file', '!=', False)]}"/> <field name="action_type" invisible="1"/>
</group> </group>
<group attrs="{'invisible': ['|', ('pdf_file', '=', False), ('show_error_message', '=', False)]}"> <group attrs="{'invisible': ['|', ('pdf_file', '=', False), ('show_error_message', '=', False)]}">
<field name="sync_successful_processed" widget="boolean_toggle"/> <field name="sync_successful_processed" widget="boolean_toggle"/>
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
Synchronize POD (Proof of Delivery) attachment information with TK system, including Synchronize POD (Proof of Delivery) attachment information with TK system, including
big package quantities and container numbers big package quantities and container numbers
</li> <!-- 同步尾程POD:向TK同步尾程交接POD(待大包数量和箱号)的附件信息 --> </li> <!-- 同步尾程POD:向TK同步尾程交接POD(待大包数量和箱号)的附件信息 -->
<li attrs="{'invisible': [('sync_successful_processed', '=', False)]}"> <li attrs="{'invisible': [('sync_successful_processed', '=', False)]}">
<strong>Sync Push Match Node:</strong> <strong>Sync Push Match Node:</strong>
Synchronize and push matched node information based on POD file, extract time from Synchronize and push matched node information based on POD file, extract time from
...@@ -71,6 +71,77 @@ ...@@ -71,6 +71,77 @@
</field> </field>
</record> </record>
<!-- Batch Create and Get POD Info Wizard Form View 批量创建并获取POD信息向导表单视图 -->
<record id="view_batch_create_and_get_pod_info_wizard_form" model="ir.ui.view">
<field name="name">batch.create.and.get.pod.info.wizard.form</field>
<field name="model">batch.get.pod.info.wizard</field>
<field name="arch" type="xml">
<form string="Batch Create and Get POD Info"> <!-- 批量创建并获取POD信息 -->
<sheet>
<group>
<group>
<field name="partner_id" options="{'no_create':True}"
domain="[('is_customer', '=', True), ('platform_type', '=', 'temu')]"
attrs="{'invisible': [('action_type', '!=', '创建temu提单')]}"/>
<!-- 请输入提单号,可输入多个,一行一个 -->
<field name="bl_numbers"
placeholder="Please enter the bill of lading numbers. Multiple entries are allowed, one per line"
attrs="{'required': [('action_type', '=', '创建temu提单')],'invisible': [('action_type', '!=', '创建temu提单')]}"/>
</group>
<group>
<field name="get_last_mile_pod"
attrs="{'invisible': [('action_type', '!=', '创建temu提单')]}"/>
<field name="remove_specified_text" readonly="1"
attrs="{'invisible': [('get_last_mile_pod', '=', False)]}"
widget="boolean_toggle"/>
<field name="skip_ocr_direct_ai" invisible="1" widget="boolean_toggle"/>
<field name="action_type" invisible="1"/>
</group>
</group>
<group attrs="{'invisible': ['|', ('pdf_file', '=', False), ('show_error_message', '=', False)]}">
<field name="generate_successful_processed" widget="boolean_toggle"/>
</group>
<div class="alert alert-info" role="alert">
<strong>Description:</strong> <!-- 说明: -->
<ul>
<li>
<strong>Get Last Mile POD:</strong>
Generate a last mile POD (Proof of Delivery) attachment information, including
big package quantities and container numbers
</li> <!-- 获取尾程POD:生成一条尾程交接POD(待大包数量和箱号)的附件信息 -->
<li attrs="{'invisible': [('get_last_mile_pod', '=', False)]}">
<strong>Remove Specified Text:</strong>
Remove specified text (AGN, UCLINK LOGISITICS LTD) from PDF files
</li> <!-- 涂抹指定文字:对PDF文件中的指定文字进行涂抹处理 -->
</ul>
</div>
<div class="alert alert-danger" role="alert"
attrs="{'invisible': [('show_error_message', '=', False)]}">
<field name="show_error_message"/>
</div>
<div>
<field name="pdf_file" filename="pdf_filename" widget="pdf_viewer" readonly="1"
attrs="{'invisible': [('pdf_file', '=', False)]}"/>
</div>
<footer>
<!-- 预览按钮:处理PDF并显示合并后的文件 -->
<button string="Preview" type="object" name="action_preview" class="btn-primary"
attrs="{'invisible': ['|',('pdf_file', '!=', False),('get_last_mile_pod','=',False)]}"/>
<!-- 确认按钮:使用已处理的文件数据进行回写 -->
<!-- 如果有失败的文件(show_error_message不为空),需要勾选generate_successful_processed才显示;如果全部成功,直接显示 -->
<button string="Confirm" type="object" name="confirm" class="btn-primary"
attrs="{'invisible': [('get_last_mile_pod','=',True)]}"/>
<button string="Confirm" type="object" name="confirm" class="btn-primary"
attrs="{'invisible': ['|', ('pdf_file', '=', False), '&amp;', ('show_error_message', '!=', False), ('generate_successful_processed', '=', False)]}"/>
<button string="Close" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>
<!-- Batch Get POD Info Wizard Action 批量获取POD信息向导动作 --> <!-- Batch Get POD Info Wizard Action 批量获取POD信息向导动作 -->
<record id="action_batch_get_pod_info_wizard" model="ir.actions.act_window"> <record id="action_batch_get_pod_info_wizard" model="ir.actions.act_window">
<field name="name">Batch Get POD Info</field> <!-- 批量获取POD信息 --> <field name="name">Batch Get POD Info</field> <!-- 批量获取POD信息 -->
...@@ -81,4 +152,4 @@ ...@@ -81,4 +152,4 @@
</record> </record>
</data> </data>
</odoo> </odoo>
\ No newline at end of file
...@@ -30,8 +30,8 @@ ...@@ -30,8 +30,8 @@
'company': 'Cybrosys Techno Solutions', 'company': 'Cybrosys Techno Solutions',
'maintainer': 'Cybrosys Techno Solutions', 'maintainer': 'Cybrosys Techno Solutions',
'website': "http://www.cybrosys.com", 'website': "http://www.cybrosys.com",
'depends': ['base', 'sale_management'], 'depends': ['base'],
'data': ['views/sale_order_views.xml'], 'data': [],
'assets': { 'assets': {
'web.assets_backend': [ 'web.assets_backend': [
'many2many_attachment_preview/static/src/js/attachment_preview.js', 'many2many_attachment_preview/static/src/js/attachment_preview.js',
......
...@@ -19,4 +19,4 @@ ...@@ -19,4 +19,4 @@
# If not, see <http://www.gnu.org/licenses/>. # If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################# #############################################################################
from . import sale_order # from . import sale_order
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论