提交 6bb5c09a authored 作者: 刘擎阳's avatar 刘擎阳

Merge branch 'develop' of https://e.coding.net/yizuo/hh_ccs/hh_ccs into develop

...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
'security/account_security.xml', 'security/account_security.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'wizard/batch_input_ship_package_statu_wizard.xml', 'wizard/batch_input_ship_package_statu_wizard.xml',
'wizard/update_bl_status_wizard.xml',
'wizard/export_bl_big_package_xlsx_wizard.xml', 'wizard/export_bl_big_package_xlsx_wizard.xml',
'wizard/associate_pallet_wizard_views.xml', 'wizard/associate_pallet_wizard_views.xml',
'wizard/add_exception_info_wizard_views.xml', 'wizard/add_exception_info_wizard_views.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-02-12 02:20+0000\n" "POT-Creation-Date: 2025-03-19 08:34+0000\n"
"PO-Revision-Date: 2025-02-12 10:26+0800\n" "PO-Revision-Date: 2025-03-19 16:39+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"
...@@ -506,6 +506,7 @@ msgstr "大包" ...@@ -506,6 +506,7 @@ msgstr "大包"
#: model:ir.model.fields,field_description:ccs_base.field_cc_package_good__bl_id #: model:ir.model.fields,field_description:ccs_base.field_cc_package_good__bl_id
#: model:ir.model.fields,field_description:ccs_base.field_cc_progress__bl_id #: model:ir.model.fields,field_description:ccs_base.field_cc_progress__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_cc_ship_package__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: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
...@@ -542,9 +543,15 @@ msgstr "提单号" ...@@ -542,9 +543,15 @@ msgstr "提单号"
msgid "Billing Weight" msgid "Billing Weight"
msgstr "提单重量" msgstr "提单重量"
#. module: ccs_base
#: model:ir.model.fields,help:ccs_base.field_update_bl_status_wizard__last_process_time
msgid "Bill Of Loading Last Process Time"
msgstr "提单最后推送时间"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_batch_input_ship_package_status_wizard__bl_count #: model:ir.model.fields,field_description:ccs_base.field_batch_input_ship_package_status_wizard__bl_count
msgid "Bl count" #: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__bl_count
msgid "Bill Of Loading count"
msgstr "已选提单" msgstr "已选提单"
#. module: ccs_base #. module: ccs_base
...@@ -755,6 +762,7 @@ msgstr "清关文件" ...@@ -755,6 +762,7 @@ msgstr "清关文件"
#: 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_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_export_bl_big_package_xlsx_wizard #: model_terms:ir.ui.view,arch_db:ccs_base.view_export_bl_big_package_xlsx_wizard
#: model_terms:ir.ui.view,arch_db:ccs_base.view_update_bl_status_wizard
msgid "Close" msgid "Close"
msgstr "关闭" msgstr "关闭"
...@@ -784,6 +792,7 @@ msgstr "确认" ...@@ -784,6 +792,7 @@ msgstr "确认"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_batch_input_ship_package_status_wizard__is_ok #: model:ir.model.fields,field_description:ccs_base.field_batch_input_ship_package_status_wizard__is_ok
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__is_ok
msgid "Confirm Date is ok." msgid "Confirm Date is ok."
msgstr "确认数据是否正确" msgstr "确认数据是否正确"
...@@ -805,6 +814,11 @@ msgstr "集装箱编号" ...@@ -805,6 +814,11 @@ msgstr "集装箱编号"
msgid "Corresponding to the status of the big package" msgid "Corresponding to the status of the big package"
msgstr "对应大包状态" msgstr "对应大包状态"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_node__package_state
msgid "Corresponding to the status of the package"
msgstr "对应小包状态"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_last_mile_provider__name #: model:ir.model.fields,field_description:ccs_base.field_cc_last_mile_provider__name
msgid "Courier Name" msgid "Courier Name"
...@@ -829,6 +843,7 @@ msgstr "快递名称" ...@@ -829,6 +843,7 @@ msgstr "快递名称"
#: model:ir.model.fields,field_description:ccs_base.field_common_common__create_uid #: model:ir.model.fields,field_description:ccs_base.field_common_common__create_uid
#: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard__create_uid #: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard__create_uid
#: model:ir.model.fields,field_description:ccs_base.field_order_state_change_rule__create_uid #: model:ir.model.fields,field_description:ccs_base.field_order_state_change_rule__create_uid
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__create_uid
msgid "Created by" msgid "Created by"
msgstr "创建人" msgstr "创建人"
...@@ -851,6 +866,7 @@ msgstr "创建人" ...@@ -851,6 +866,7 @@ msgstr "创建人"
#: model:ir.model.fields,field_description:ccs_base.field_common_common__create_date #: model:ir.model.fields,field_description:ccs_base.field_common_common__create_date
#: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard__create_date #: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard__create_date
#: model:ir.model.fields,field_description:ccs_base.field_order_state_change_rule__create_date #: model:ir.model.fields,field_description:ccs_base.field_order_state_change_rule__create_date
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__create_date
msgid "Created on" msgid "Created on"
msgstr "创建时间" msgstr "创建时间"
...@@ -881,6 +897,11 @@ msgstr "当前清关进度" ...@@ -881,6 +897,11 @@ msgstr "当前清关进度"
msgid "Current CC Progress Date" msgid "Current CC Progress Date"
msgstr "当前进度日期" msgstr "当前进度日期"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__current_status
msgid "Current Status"
msgstr "当前状态"
#. module: ccs_base #. module: ccs_base
#: 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
...@@ -927,6 +948,18 @@ msgstr "海关提单号" ...@@ -927,6 +948,18 @@ msgstr "海关提单号"
msgid "Customs Bill of Loading No." msgid "Customs Bill of Loading No."
msgstr "海关装货单号" msgstr "海关装货单号"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_bl__customs_clearance_status
#: model_terms:ir.ui.view,arch_db:ccs_base.tree_cc_bl_view
msgid "Customs Clearance Status"
msgstr "关务提单状态"
#. module: ccs_base
#: model_terms:ir.ui.view,arch_db:ccs_base.form_cc_bl_view
#: model_terms:ir.ui.view,arch_db:ccs_base.view_update_bl_status_wizard
msgid "Customs Clearance Status Process Time"
msgstr "关务提单推送时间"
#. module: ccs_base #. module: ccs_base
#: 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
msgid "Deadline" msgid "Deadline"
...@@ -997,6 +1030,7 @@ msgstr "节点描述" ...@@ -997,6 +1030,7 @@ msgstr "节点描述"
#: model:ir.model.fields,field_description:ccs_base.field_common_common__display_name #: model:ir.model.fields,field_description:ccs_base.field_common_common__display_name
#: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard__display_name #: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard__display_name
#: model:ir.model.fields,field_description:ccs_base.field_order_state_change_rule__display_name #: model:ir.model.fields,field_description:ccs_base.field_order_state_change_rule__display_name
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__display_name
msgid "Display Name" msgid "Display Name"
msgstr "显示名称" msgstr "显示名称"
...@@ -1298,6 +1332,7 @@ msgstr "" ...@@ -1298,6 +1332,7 @@ msgstr ""
#: model:ir.model.fields,field_description:ccs_base.field_common_common__id #: model:ir.model.fields,field_description:ccs_base.field_common_common__id
#: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard__id #: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard__id
#: model:ir.model.fields,field_description:ccs_base.field_order_state_change_rule__id #: model:ir.model.fields,field_description:ccs_base.field_order_state_change_rule__id
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__id
msgid "ID" msgid "ID"
msgstr "" msgstr ""
...@@ -1379,6 +1414,16 @@ msgstr "内部帐号" ...@@ -1379,6 +1414,16 @@ msgstr "内部帐号"
msgid "Invoice Attachments" msgid "Invoice Attachments"
msgstr "发票附件" msgstr "发票附件"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_bl__is_bl_sync
msgid "Is Bill Of Loading Synchronized"
msgstr "提单是否同步"
#. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__is_batch
msgid "Is Batch"
msgstr "是否批量"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_big_package__is_cancel #: model:ir.model.fields,field_description:ccs_base.field_cc_big_package__is_cancel
#: model:ir.model.fields,field_description:ccs_base.field_cc_bl__is_cancel #: model:ir.model.fields,field_description:ccs_base.field_cc_bl__is_cancel
...@@ -1563,11 +1608,13 @@ msgstr "尾程服务商" ...@@ -1563,11 +1608,13 @@ msgstr "尾程服务商"
#: model:ir.model.fields,field_description:ccs_base.field_common_common____last_update #: model:ir.model.fields,field_description:ccs_base.field_common_common____last_update
#: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard____last_update #: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard____last_update
#: model:ir.model.fields,field_description:ccs_base.field_order_state_change_rule____last_update #: model:ir.model.fields,field_description:ccs_base.field_order_state_change_rule____last_update
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard____last_update
msgid "Last Modified on" msgid "Last Modified on"
msgstr "最后修改时间" msgstr "最后修改时间"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_batch_input_ship_package_status_wizard__last_process_time #: model:ir.model.fields,field_description:ccs_base.field_batch_input_ship_package_status_wizard__last_process_time
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__last_process_time
msgid "Last Process Time" msgid "Last Process Time"
msgstr "最近操作时间" msgstr "最近操作时间"
...@@ -1590,6 +1637,7 @@ msgstr "最近操作时间" ...@@ -1590,6 +1637,7 @@ msgstr "最近操作时间"
#: model:ir.model.fields,field_description:ccs_base.field_common_common__write_uid #: model:ir.model.fields,field_description:ccs_base.field_common_common__write_uid
#: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard__write_uid #: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard__write_uid
#: model:ir.model.fields,field_description:ccs_base.field_order_state_change_rule__write_uid #: model:ir.model.fields,field_description:ccs_base.field_order_state_change_rule__write_uid
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__write_uid
msgid "Last Updated by" msgid "Last Updated by"
msgstr "最后更新人" msgstr "最后更新人"
...@@ -1612,6 +1660,7 @@ msgstr "最后更新人" ...@@ -1612,6 +1660,7 @@ msgstr "最后更新人"
#: model:ir.model.fields,field_description:ccs_base.field_common_common__write_date #: model:ir.model.fields,field_description:ccs_base.field_common_common__write_date
#: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard__write_date #: model:ir.model.fields,field_description:ccs_base.field_export_bl_big_package_xlsx_wizard__write_date
#: model:ir.model.fields,field_description:ccs_base.field_order_state_change_rule__write_date #: model:ir.model.fields,field_description:ccs_base.field_order_state_change_rule__write_date
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__write_date
msgid "Last Updated on" msgid "Last Updated on"
msgstr "最后更新时间" msgstr "最后更新时间"
...@@ -2007,6 +2056,15 @@ msgstr "托盘号" ...@@ -2007,6 +2056,15 @@ msgstr "托盘号"
msgid "Pallet Usage Date" msgid "Pallet Usage Date"
msgstr "托盘使用日期" msgstr "托盘使用日期"
#. module: ccs_base
#. odoo-python
#: code:addons/ccs_base/models/cc_bill_loading.py:0
#, python-format
msgid ""
"Please configure the default customs clearance status of the bill of "
"loading node type first."
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
...@@ -2016,7 +2074,9 @@ msgstr "请选择异常原因!" ...@@ -2016,7 +2074,9 @@ msgstr "请选择异常原因!"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_batch_input_ship_package_status_wizard__process_time #: model:ir.model.fields,field_description:ccs_base.field_batch_input_ship_package_status_wizard__process_time
#: model:ir.model.fields,field_description:ccs_base.field_cc_bl__process_time
#: model:ir.model.fields,field_description:ccs_base.field_cc_ship_package__process_time #: model:ir.model.fields,field_description:ccs_base.field_cc_ship_package__process_time
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__process_time
#: 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
msgid "Process Time" msgid "Process Time"
msgstr "推送时间" msgstr "推送时间"
...@@ -2136,7 +2196,7 @@ msgstr "提单总大包数" ...@@ -2136,7 +2196,7 @@ msgstr "提单总大包数"
#: 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.tree_cc_bl_view #: model_terms:ir.ui.view,arch_db:ccs_base.tree_cc_bl_view
msgid "Receive Goods Qty" msgid "Receive Goods Qty"
msgstr "提单总大包数" msgstr "提单总货物数"
#. module: ccs_base #. module: ccs_base
#: model_terms:ir.ui.view,arch_db:ccs_base.tree_cc_bl_view #: model_terms:ir.ui.view,arch_db:ccs_base.tree_cc_bl_view
...@@ -2524,9 +2584,13 @@ msgstr "省份" ...@@ -2524,9 +2584,13 @@ msgstr "省份"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_batch_input_ship_package_status_wizard__state_explain #: model:ir.model.fields,field_description:ccs_base.field_batch_input_ship_package_status_wizard__state_explain
#: model:ir.model.fields,field_description:ccs_base.field_cc_bl__state_explain
#: model:ir.model.fields,field_description:ccs_base.field_cc_ship_package__state_explain #: model:ir.model.fields,field_description:ccs_base.field_cc_ship_package__state_explain
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__state_explain
#: model:ir.model.fields,help:ccs_base.field_batch_input_ship_package_status_wizard__state_explain #: model:ir.model.fields,help:ccs_base.field_batch_input_ship_package_status_wizard__state_explain
#: model:ir.model.fields,help:ccs_base.field_cc_bl__state_explain
#: model:ir.model.fields,help:ccs_base.field_cc_ship_package__state_explain #: model:ir.model.fields,help:ccs_base.field_cc_ship_package__state_explain
#: model:ir.model.fields,help:ccs_base.field_update_bl_status_wizard__state_explain
msgid "State Explain" msgid "State Explain"
msgstr "状态说明" msgstr "状态说明"
...@@ -2551,6 +2615,7 @@ msgstr "" ...@@ -2551,6 +2615,7 @@ msgstr ""
#. module: ccs_base #. module: ccs_base
#: 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_export_bl_big_package_xlsx_wizard #: model_terms:ir.ui.view,arch_db:ccs_base.view_export_bl_big_package_xlsx_wizard
#: model_terms:ir.ui.view,arch_db:ccs_base.view_update_bl_status_wizard
msgid "Submit" msgid "Submit"
msgstr "提交" msgstr "提交"
...@@ -2649,6 +2714,15 @@ msgstr "商品号必须唯一。" ...@@ -2649,6 +2714,15 @@ msgstr "商品号必须唯一。"
msgid "The Logistic Order No must be unique." msgid "The Logistic Order No must be unique."
msgstr "物流订单号必须唯一。" msgstr "物流订单号必须唯一。"
#. module: ccs_base
#. odoo-python
#: code:addons/ccs_base/models/cc_bill_loading.py:0
#, python-format
msgid ""
"The customs clearance status of the selected bill of loading must be the "
"same."
msgstr "选择的关务提单状态必须是同一个。"
#. 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
...@@ -2738,7 +2812,7 @@ msgstr "提单总金额" ...@@ -2738,7 +2812,7 @@ msgstr "提单总金额"
#: model:ir.model.fields,field_description:ccs_base.field_cc_bl__bl_total_qty #: model:ir.model.fields,field_description:ccs_base.field_cc_bl__bl_total_qty
#: 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
msgid "Total Goods Qty" msgid "Total Goods Qty"
msgstr "提单总数量" msgstr "提单总货物数量"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_package_good__item_total_price #: model:ir.model.fields,field_description:ccs_base.field_cc_package_good__item_total_price
...@@ -2835,6 +2909,17 @@ msgstr "未理货" ...@@ -2835,6 +2909,17 @@ msgstr "未理货"
msgid "Unsync" msgid "Unsync"
msgstr "未同步" msgstr "未同步"
#. module: ccs_base
#: model_terms:ir.ui.view,arch_db:ccs_base.form_cc_bl_view
msgid "Update Bill Of Loading Status"
msgstr "更新提单状态"
#. module: ccs_base
#: model:ir.actions.act_window,name:ccs_base.action_batch_input_bl_status_wizard
#: model_terms:ir.ui.view,arch_db:ccs_base.view_update_bl_status_wizard
msgid "Update Bill Of Loading Status"
msgstr "更新提单状态"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_cc_progress__update_date #: model:ir.model.fields,field_description:ccs_base.field_cc_progress__update_date
msgid "Update Date" msgid "Update Date"
...@@ -2842,6 +2927,7 @@ msgstr "更新日期" ...@@ -2842,6 +2927,7 @@ msgstr "更新日期"
#. module: ccs_base #. module: ccs_base
#: model:ir.model.fields,field_description:ccs_base.field_batch_input_ship_package_status_wizard__update_status #: model:ir.model.fields,field_description:ccs_base.field_batch_input_ship_package_status_wizard__update_status
#: model:ir.model.fields,field_description:ccs_base.field_update_bl_status_wizard__update_status
msgid "Update Node" msgid "Update Node"
msgstr "更新节点" msgstr "更新节点"
...@@ -2861,6 +2947,19 @@ msgstr "更新者" ...@@ -2861,6 +2947,19 @@ msgstr "更新者"
msgid "Update package progress" msgid "Update package progress"
msgstr "更新包裹进度" msgstr "更新包裹进度"
#. module: ccs_base
#. odoo-python
#: code:addons/ccs_base/models/cc_bill_loading.py:0
#: model:ir.model,name:ccs_base.model_update_bl_status_wizard
#, python-format
msgid "Update the status of the bill of loading"
msgstr "更新提单状态"
#. module: ccs_base
#: model:ir.actions.server,name:ccs_base.batch_update_bl_status_server
msgid "Update the status of the bill of loading"
msgstr "更新提单状态"
#. 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
......
...@@ -5,7 +5,7 @@ from datetime import timedelta ...@@ -5,7 +5,7 @@ from datetime import timedelta
import pytz import pytz
from odoo import models, fields, api, _ from odoo import models, fields, api, _
from odoo.exceptions import UserError from odoo.exceptions import UserError, ValidationError
# 获取日志 # 获取日志
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
...@@ -590,6 +590,30 @@ class CcBL(models.Model): ...@@ -590,6 +590,30 @@ class CcBL(models.Model):
'context': {'active_id': self.ids, 'default_is_batch': True, 'default_bl_id': self.ids} 'context': {'active_id': self.ids, 'default_is_batch': True, 'default_bl_id': self.ids}
} }
def batch_update_bl_status_wizard(self):
"""批量更新提单状态"""
# 检查关务提单状态必须是同一个
customs_clearance_status_list = self.filtered(lambda x: x.customs_clearance_status.id).mapped(
'customs_clearance_status.id')
if len(customs_clearance_status_list) == 0:
# 请先配置默认的提单节点类型的清关节点
raise ValidationError(
_('Please configure the default customs clearance status of the bill of loading node type first.'))
if len(customs_clearance_status_list) > 1:
raise ValidationError(_('The customs clearance status of the selected bill of loading must be the same.'))
# 最近操作时间取最晚的一条提单状态操作时间。
last_process_time = \
self.filtered(lambda x: x.customs_clearance_status.id).mapped('process_time').sorted(reverse=True)[0]
return {
'name': _('Update the status of the bill of loading'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'update.bl.status.wizard',
'target': 'new',
'context': {'active_id': self.ids, 'default_is_batch': True, 'default_last_process_time': last_process_time,
'default_current_status': customs_clearance_status_list[0]}
}
@api.depends('big_package_ids', 'big_package_ids.tally_state', 'big_package_ids.is_cancel') @api.depends('big_package_ids', 'big_package_ids.tally_state', 'big_package_ids.is_cancel')
def cal_tally_big_package_qty(self): def cal_tally_big_package_qty(self):
""" """
...@@ -670,6 +694,16 @@ class CcBL(models.Model): ...@@ -670,6 +694,16 @@ class CcBL(models.Model):
# 通关文件, 关联附件对象(类型限定为image和PDF) # 通关文件, 关联附件对象(类型限定为image和PDF)
cc_attachment_ids = fields.One2many('cc.clearance.file', 'bl_id', string='Clearance Files') cc_attachment_ids = fields.One2many('cc.clearance.file', 'bl_id', string='Clearance Files')
# 提单上新增字段:关务提单状态,用英文:关联节点的配置(cc.node),节点类型过滤提单的节点名称。
customs_clearance_status = fields.Many2one('cc.node', string='Customs Clearance Status', tracking=True,
default=lambda self: self.env['cc.node'].search(
[('node_type', '=', 'bl'), ('is_default', '=', True)], limit=1),
domain=[('node_type', '=', 'bl')])
# 增加关务提单状态操作时间
process_time = fields.Datetime(string='Customs Clearance Status Process Time')
# 添加状态说明字段
state_explain = fields.Text('State Explain', help='State Explain')
# 增加一个can_cancel的方法,用于检查提单当前是否可以取消,返回True表示可以取消, False表示不可以取消,同时返回取消的原因 # 增加一个can_cancel的方法,用于检查提单当前是否可以取消,返回True表示可以取消, False表示不可以取消,同时返回取消的原因
def check_cancel(self): def check_cancel(self):
if self.is_cancel: if self.is_cancel:
...@@ -683,6 +717,8 @@ class CcBL(models.Model): ...@@ -683,6 +717,8 @@ class CcBL(models.Model):
is_cancel = fields.Boolean(string='Is Cancel', default=False) is_cancel = fields.Boolean(string='Is Cancel', default=False)
# 取消原因 # 取消原因
cancel_reason = fields.Char(string='Cancel Reason') cancel_reason = fields.Char(string='Cancel Reason')
# 是否同步
is_bl_sync = fields.Boolean('Is Bill Of Loading Synchronized', default=False)
# 增加提单取消的方法,用于取消提单,取消提单时,需要检查提单是否可以取消,如果可以取消,则将提单的状态设置为取消,并记录取消原因, 同时取消提单下的所有包裹 # 增加提单取消的方法,用于取消提单,取消提单时,需要检查提单是否可以取消,如果可以取消,则将提单的状态设置为取消,并记录取消原因, 同时取消提单下的所有包裹
def action_cancel(self, cancel_reason=''): def action_cancel(self, cancel_reason=''):
......
...@@ -48,3 +48,8 @@ class CcNode(models.Model): ...@@ -48,3 +48,8 @@ class CcNode(models.Model):
('checked_goods', 'Checked goods'), ('checked_goods', 'Checked goods'),
('handover_completed', 'Handover Completed') ('handover_completed', 'Handover Completed')
], default='', string='Corresponding to the status of the big package', index=True) # 对应大包状态 未理货/已理货/尾程交接 ], default='', string='Corresponding to the status of the big package', index=True) # 对应大包状态 未理货/已理货/尾程交接
# 新增字段:对应小包状态。只有类型为提单上才可填写。可选已配置节点类型为小包的节点。单选;
package_state = fields.Many2one('cc.node', string='Corresponding to the status of the package',domain="[('node_type','=','package')]", index=True) # 对应小包状态
...@@ -3,7 +3,7 @@ batch_input_ship_package_status_wizard_group_user,batch_input_ship_package_statu ...@@ -3,7 +3,7 @@ batch_input_ship_package_status_wizard_group_user,batch_input_ship_package_statu
export_bl_big_package_xlsx_wizard_group_user,export_bl_big_package_xlsx_wizard_group_user,ccs_base.model_export_bl_big_package_xlsx_wizard,base.group_user,1,1,1,1 export_bl_big_package_xlsx_wizard_group_user,export_bl_big_package_xlsx_wizard_group_user,ccs_base.model_export_bl_big_package_xlsx_wizard,base.group_user,1,1,1,1
associate_pallet_wizard_group_user,associate_pallet_wizard_group_user,ccs_base.model_associate_pallet_wizard,base.group_user,1,1,1,1 associate_pallet_wizard_group_user,associate_pallet_wizard_group_user,ccs_base.model_associate_pallet_wizard,base.group_user,1,1,1,1
add_exception_info_wizard_group_user,add_exception_info_wizard_group_user,ccs_base.model_add_exception_info_wizard,base.group_user,1,1,1,1 add_exception_info_wizard_group_user,add_exception_info_wizard_group_user,ccs_base.model_add_exception_info_wizard,base.group_user,1,1,1,1
update_bl_status_wizard_group_user,update_bl_status_wizard_group_user,ccs_base.model_update_bl_status_wizard,base.group_user,1,1,1,1
access_group_user_common_common,access_group_user_common_common,model_common_common,base.group_user,1,1,1,1 access_group_user_common_common,access_group_user_common_common,model_common_common,base.group_user,1,1,1,1
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
<tree string="Bill of Loading" decoration-warning="is_cancel==True"> <tree string="Bill of Loading" decoration-warning="is_cancel==True">
<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="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="customer_id" string="Customer"/> <field optional="show" name="customer_id" string="Customer"/>
...@@ -50,10 +51,18 @@ ...@@ -50,10 +51,18 @@
<!-- # 为action_batch_input_ship_package_wizard添加一个按钮, 上下文中添加bl_id--> <!-- # 为action_batch_input_ship_package_wizard添加一个按钮, 上下文中添加bl_id-->
<button name="%(action_batch_input_ship_package_wizard)d" type="action" class="oe_highlight" <button name="%(action_batch_input_ship_package_wizard)d" type="action" class="oe_highlight"
string="Update Ship Package Status" string="Update Ship Package Status"
context="{'default_bl_id': active_id, 'active_id': id}"/> context="{'default_bl_id': active_id, 'active_id': id,}"/>
<button name="%(action_batch_input_bl_status_wizard)d" type="action" class="oe_highlight"
string="Update Bill Of Loading Status"
context="{'active_id': id,'default_bl_id': active_id,
'default_last_process_time':process_time,'default_current_status':customs_clearance_status}"/>
<field name="state" widget="statusbar" options="{'clickable': '1'}"/> <field name="state" widget="statusbar" options="{'clickable': '1'}"/>
</header> </header>
<header>
<field name="customs_clearance_status" widget="statusbar"/>
</header>
<sheet> <sheet>
<div class="oe_button_box" name="button_box"> <div class="oe_button_box" name="button_box">
...@@ -117,6 +126,7 @@ ...@@ -117,6 +126,7 @@
<field name="eta" string="ETA"/> <field name="eta" string="ETA"/>
<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"/>
</group> </group>
<group> <group>
<field name="billing_weight" string="Billing Weight"/> <field name="billing_weight" string="Billing Weight"/>
...@@ -404,4 +414,16 @@ ...@@ -404,4 +414,16 @@
</field> </field>
</record> </record>
<record id="batch_update_bl_status_server" model="ir.actions.server">
<field name="name">Update the status of the bl of loading</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="code">
if records:
action = records.batch_update_bl_status_wizard()
</field>
</record>
</odoo> </odoo>
\ No newline at end of file
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
<field optional="show" name="desc" string="Node Description"/> <field optional="show" name="desc" string="Node Description"/>
<field optional="show" name="next_code_ids" widget="many2many_tags" options="{'no_create':True}"/> <field optional="show" name="next_code_ids" widget="many2many_tags" options="{'no_create':True}"/>
<field optional="show" name="tally_state"/> <field optional="show" name="tally_state"/>
<field optional="show" name="package_state"/>
<field optional="show" name="is_must" string="Is Must Node"/> <field optional="show" name="is_must" string="Is Must Node"/>
<field optional="show" name="is_done" string="Is Done Node"/> <field optional="show" name="is_done" string="Is Done Node"/>
<field optional="show" name="is_default" string="Is Current Node"/> <field optional="show" name="is_default" string="Is Current Node"/>
......
...@@ -4,4 +4,5 @@ from . import batch_input_ship_package_statu_wizard ...@@ -4,4 +4,5 @@ from . import batch_input_ship_package_statu_wizard
from . import export_bl_big_package_xlsx_wizard from . import export_bl_big_package_xlsx_wizard
from . import associate_pallet_wizard from . import associate_pallet_wizard
from . import add_exception_info_wizard from . import add_exception_info_wizard
from . import update_bl_status_wizard
...@@ -50,7 +50,7 @@ class BatchInputShipPackageStatusWizard(models.TransientModel): ...@@ -50,7 +50,7 @@ class BatchInputShipPackageStatusWizard(models.TransientModel):
# print(self.get_order()) # print(self.get_order())
return len(self.get_order()) return len(self.get_order())
bl_count = fields.Integer('Bl count', default=get_bl_count) bl_count = fields.Integer('Bill Of Loading Count', default=get_bl_count)
current_status = fields.Many2one('cc.node', 'Select Node') current_status = fields.Many2one('cc.node', 'Select Node')
next_code_ids = fields.Many2many('cc.node', 'node_next_node_wizard_rel', 'node_id', 'next_node_id', 'Next Node', next_code_ids = fields.Many2many('cc.node', 'node_next_node_wizard_rel', 'node_id', 'next_node_id', 'Next Node',
related='current_status.next_code_ids') related='current_status.next_code_ids')
...@@ -82,12 +82,12 @@ class BatchInputShipPackageStatusWizard(models.TransientModel): ...@@ -82,12 +82,12 @@ class BatchInputShipPackageStatusWizard(models.TransientModel):
state_explain = fields.Text('State Explain', help='State Explain') state_explain = fields.Text('State Explain', help='State Explain')
node_exception_reason_id = fields.Many2one('cc.node.exception.reason', 'Exception Reason', node_exception_reason_id = fields.Many2one('cc.node.exception.reason', 'Exception Reason',
domain="[('code_id', '=', update_status)]") domain="[('code_id', '=', update_status)]")
# 批量更新小包状态 # 批量更新小包状态
def submit(self): def submit(self):
# 确认数据 # 确认数据
if not self.is_ok: if not self.is_ok:
raise ValidationError('Please confirm that the above data is correct.') # 请确认以上数据正确 raise ValidationError('Please confirm that the above data is correct.') # 请确认以上数据正确
parcels = self.get_process_package() parcels = self.get_process_package()
if not parcels: if not parcels:
......
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import models, api, fields
from odoo.exceptions import ValidationError
# 定义一个批量更新提单状态的向导, 用于批量更新提单状态
# 包括以下字段,提单, 当前节点, 更新节点, 排除面单号, 排除状态
class UpdateBlStatusWizard(models.TransientModel):
_name = 'update.bl.status.wizard'
_description = 'Update the status of the bill of loading' # 更新提单状态向导
def get_order(self):
"""
得到单据
:return:
"""
order_id = self._context.get('active_id')
if type(order_id) != list:
order_id = [self._context.get('active_id')]
return self.env['cc.bl'].browse(order_id)
bl_id = fields.Many2one('cc.bl', 'Bill of Loading')
def get_bl_count(self):
# print(self.get_order())
return len(self.get_order())
bl_count = fields.Integer('Bill Of Loading Count', default=get_bl_count)
current_status = fields.Many2one('cc.node', 'Current Status')
update_status = fields.Many2one('cc.node', 'Update Node')
is_ok = fields.Boolean('Confirm Date is ok.', default=False)
process_time = fields.Datetime('Process Time')
last_process_time = fields.Datetime('Last Process Time', readonly=True, help='Bill Of Loading Last Process Time')
# 添加状态说明字段
state_explain = fields.Text('State Explain', help='State Explain')
is_batch = fields.Boolean('Is Batch', default=False)
# 批量更新小包状态
def submit(self):
bl_obj=self.get_order()
# 确认数据
if not self.is_ok:
raise ValidationError('Please confirm that the above data is correct.') # 请确认以上数据正确
# 1.若选择的更新节点为是当前节点【清关节点设置,是当前节点字段名称改为初始节点】,当更新节点为初始节点时,无需填写操作时间;
# if self.update_status and not self.update_status.is_default:
# 2.若选择的更新节点为“选择节点”的后续节点(根据节点设置排序),则按照操作时间不能大于当前时间,且不能早于最近的操作时间。
# 3.若选择的“更新节点”为“选择节点”的前序节点(根据节点设置排序),则查找“选择节点”是否已有同步日志,若有,则操作时间不允许早于前序节点同步日志里的操作时间,且不能大于当前时间。若有多条,以同步时间最晚的一条为准。
# 4.若选择的“更新节点”和“选择节点”一致时,需检查该节点的前序节点是否有同步日志,若有,则操作时间不允许早于前序节点同步日志里的操作时间,且不能大于当前时间。同一节点若有多条同步日志,以同步时间最晚的一条为准。
# 如果更新节点是 默认节点 同步的标志变为True
is_sync = False
if self.update_status.is_default:
is_sync = True
# 更新状态
bl_obj.write(
{'customs_clearance_status': self.update_status.id, 'process_time': self.process_time, 'state_explain': self.state_explain,
'is_bl_sync': is_sync})
<?xml version="1.0" encoding="utf-8"?>
<!-- © <2016> <heyang>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo>
<data>
<record id="view_update_bl_status_wizard" model="ir.ui.view">
<field name="name">view_update_bl_status_wizard</field>
<field name="model">update.bl.status.wizard</field>
<field name="arch" type="xml">
<form string="Update Bill Of Loading Status">
<sheet>
<group>
<field name="bl_count" attrs="{'invisible': [('is_batch', '=', False)]}" readonly="1"/>
<field name="bl_id" invisible="1"/>
<field name="current_status" readonly="1"/>
<field name="last_process_time"/>
<field name="update_status" required="1" domain="[('node_type','=','bl')]"/>
<field name="process_time" required="1" string="Customs Clearance Status Process Time"/>
<field name="state_explain" invisible="1"/>
<field name="is_batch" invisible="1"/>
</group>
<group>
<field name="is_ok"/>
</group>
<footer>
<button name="submit" type="object" string="Submit" class="oe_highlight"
attrs="{'invisible':[('is_ok','=',False)]}"/>
<button string="Close" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>
<!--定义视图动作-->
<record model="ir.actions.act_window" id="action_batch_input_bl_status_wizard">
<field name="name">Update Bill Of Loading Status</field>
<field name="res_model">update.bl.status.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</odoo>
\ No newline at end of file
...@@ -18,12 +18,14 @@ ...@@ -18,12 +18,14 @@
# 'data/data.xml', # 'data/data.xml',
# wizard # wizard
'wizard/batch_input_ship_package_statu_wizard.xml', 'wizard/batch_input_ship_package_statu_wizard.xml',
'wizard/update_bl_status_wizard.xml',
# 'wizard/again_push_wizard.xml', # 'wizard/again_push_wizard.xml',
# 'wizard/batch_push_tiktok.xml', # 'wizard/batch_push_tiktok.xml',
# view # view
'views/config_settings_views.xml', 'views/config_settings_views.xml',
# 'views/flight_order_view.xml', # 'views/flight_order_view.xml',
'views/cc_ship_package_sync_log_view.xml', 'views/cc_ship_package_sync_log_view.xml',
'views/cc_bl_sync_log_view.xml',
'views/ao_tt_api_log_view.xml', 'views/ao_tt_api_log_view.xml',
'views/cc_node_view.xml', 'views/cc_node_view.xml',
'views/cc_ship_package_view.xml', 'views/cc_ship_package_view.xml',
......
...@@ -6,8 +6,8 @@ msgid "" ...@@ -6,8 +6,8 @@ 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-02-18 02:36+0000\n" "POT-Creation-Date: 2025-03-19 08:47+0000\n"
"PO-Revision-Date: 2025-02-18 10:37+0800\n" "PO-Revision-Date: 2025-03-19 16:51+0800\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Language: zh\n" "Language: zh\n"
...@@ -25,12 +25,16 @@ msgstr "<span class=\"o_stat_text\">小包</span>" ...@@ -25,12 +25,16 @@ msgstr "<span class=\"o_stat_text\">小包</span>"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_view_inherit #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_view_inherit
msgid "<span class=\"o_stat_text\">Ship Packages</span>" msgid "<span class=\"o_stat_text\">Ship Packages</span>"
msgstr "Ship Package" msgstr "<span class=\"o_stat_text\">小包</span>"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log__api_customer
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__api_customer #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__api_customer
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_ship_package_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_ship_package_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_ship_package_sync_log_view
msgid "Api Customer" msgid "Api Customer"
msgstr "接口客户" msgstr "接口客户"
...@@ -45,6 +49,11 @@ msgstr "" ...@@ -45,6 +49,11 @@ msgstr ""
msgid "AppSecret" msgid "AppSecret"
msgstr "" msgstr ""
#. module: ccs_connect_tiktok
#: model:ir.actions.server,name:ccs_connect_tiktok.action_batch_sync_bl_status
msgid "Batch Sync Bill Of Loading Status"
msgstr "批量同步关务提单状态"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.actions.server,name:ccs_connect_tiktok.action_batch_sync_package_status #: model:ir.actions.server,name:ccs_connect_tiktok.action_batch_sync_package_status
msgid "Batch Sync Package Status" msgid "Batch Sync Package Status"
...@@ -60,11 +69,29 @@ msgstr "批量更新小包状态向导" ...@@ -60,11 +69,29 @@ msgstr "批量更新小包状态向导"
msgid "Big Package" msgid "Big Package"
msgstr "大包" msgstr "大包"
#. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl__bl_sync_log_ids
msgid "Bill Of Loading Sync Logs"
msgstr "关务提单状态同步日志"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model,name:ccs_connect_tiktok.model_cc_bl #: model:ir.model,name:ccs_connect_tiktok.model_cc_bl
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log__bl_id
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_bl_sync_log_view
msgid "Bill of Loading" msgid "Bill of Loading"
msgstr "提单" msgstr "提单"
#. module: ccs_connect_tiktok
#: model:ir.actions.act_window,name:ccs_connect_tiktok.action_cc_bl_sync_log
#: model:ir.model,name:ccs_connect_tiktok.model_cc_bl_sync_log
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_bl_sync_log_view
msgid "CC Bill Of Loading Sync Log"
msgstr "关务提单状态同步日志"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model,name:ccs_connect_tiktok.model_cc_node #: model:ir.model,name:ccs_connect_tiktok.model_cc_node
msgid "CC Node" msgid "CC Node"
...@@ -79,6 +106,15 @@ msgstr "清关进度节点" ...@@ -79,6 +106,15 @@ msgstr "清关进度节点"
msgid "CC Ship Package Sync Log" msgid "CC Ship Package Sync Log"
msgstr "清关小包同步日志" msgstr "清关小包同步日志"
#. module: ccs_connect_tiktok
#. odoo-python
#: code:addons/ccs_connect_tiktok/wizard/update_bl_status_wizard.py:0
#, python-format
msgid ""
"Check if the bill of loading [%s] has a status that has not been pushed yet "
"or if the bill of loading has already been pushed and will be updated!"
msgstr "检查提单【%s】是否存在还未推送或 提单已经推送过将更变更的状态!"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#. odoo-python #. odoo-python
#: code:addons/ccs_connect_tiktok/wizard/batch_input_ship_package_statu_wizard.py:0 #: code:addons/ccs_connect_tiktok/wizard/batch_input_ship_package_statu_wizard.py:0
...@@ -101,6 +137,7 @@ msgstr "配置设置" ...@@ -101,6 +137,7 @@ msgstr "配置设置"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api__create_uid #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api__create_uid
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log__create_uid #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log__create_uid
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log__create_uid
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__create_uid #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__create_uid
msgid "Created by" msgid "Created by"
msgstr "" msgstr ""
...@@ -108,6 +145,7 @@ msgstr "" ...@@ -108,6 +145,7 @@ msgstr ""
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api__create_date #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api__create_date
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log__create_date #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log__create_date
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log__create_date
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__create_date #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__create_date
msgid "Created on" msgid "Created on"
msgstr "" msgstr ""
...@@ -120,11 +158,13 @@ msgstr "前置节点之间的默认间隔时间(分钟)。" ...@@ -120,11 +158,13 @@ msgstr "前置节点之间的默认间隔时间(分钟)。"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api__display_name #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api__display_name
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log__display_name #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log__display_name
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log__display_name
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__display_name #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__display_name
msgid "Display Name" msgid "Display Name"
msgstr "" msgstr ""
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_ship_package_sync_log_view
msgid "Group By" msgid "Group By"
msgstr "分组" msgstr "分组"
...@@ -137,18 +177,26 @@ msgstr "已更新节点" ...@@ -137,18 +177,26 @@ msgstr "已更新节点"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api__id #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api__id
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log__id #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log__id
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log__id
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__id #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__id
msgid "ID" msgid "ID"
msgstr "" msgstr ""
#. module: ccs_connect_tiktok
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_bl_view_inherit
msgid "Is Bill Of Loading Sync"
msgstr "关务提单状态是否同步"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package__is_sync #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package__is_sync
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_view_inherit
msgid "Is Sync" msgid "Is Sync"
msgstr "是否同步" msgstr "是否同步"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api____last_update #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api____last_update
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log____last_update #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log____last_update
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log____last_update
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log____last_update #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log____last_update
msgid "Last Modified on" msgid "Last Modified on"
msgstr "" msgstr ""
...@@ -156,6 +204,7 @@ msgstr "" ...@@ -156,6 +204,7 @@ msgstr ""
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api__write_uid #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api__write_uid
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log__write_uid #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log__write_uid
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log__write_uid
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__write_uid #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__write_uid
msgid "Last Updated by" msgid "Last Updated by"
msgstr "" msgstr ""
...@@ -163,6 +212,7 @@ msgstr "" ...@@ -163,6 +212,7 @@ msgstr ""
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api__write_date #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api__write_date
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log__write_date #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_ao_tt_api_log__write_date
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log__write_date
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__write_date #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__write_date
msgid "Last Updated on" msgid "Last Updated on"
msgstr "" msgstr ""
...@@ -175,28 +225,40 @@ msgstr "未同步" ...@@ -175,28 +225,40 @@ msgstr "未同步"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#. odoo-python #. odoo-python
#: code:addons/ccs_connect_tiktok/models/cc_bill_loading.py:0 #: code:addons/ccs_connect_tiktok/models/cc_bill_loading.py:0
#: code:addons/ccs_connect_tiktok/models/cc_bill_loading_new.py:0
#: code:addons/ccs_connect_tiktok/models/cc_bill_loading_old.py:0
#, python-format #, python-format
msgid "Not Sync Ship Packages" msgid "Not Sync Ship Packages"
msgstr "待同步小包" msgstr "待同步小包"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log__operate_remark
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__operate_remark #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__operate_remark
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_ship_package_sync_log_view
msgid "Operate Remark" msgid "Operate Remark"
msgstr "操作备注" msgstr "操作备注"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log__operate_time
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__operate_time #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__operate_time
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_ship_package_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_ship_package_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_ship_package_sync_log_view
msgid "Operate Time" msgid "Operate Time"
msgstr "操作时间" msgstr "操作时间"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log__operate_user
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__operate_user #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__operate_user
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_ship_package_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_ship_package_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_ship_package_sync_log_view
msgid "Operate User" msgid "Operate User"
msgstr "操作人" msgstr "操作人"
...@@ -207,10 +269,22 @@ msgid "Predecessor Node Interval (Minutes)" ...@@ -207,10 +269,22 @@ msgid "Predecessor Node Interval (Minutes)"
msgstr "前置节点间隔(分钟)" msgstr "前置节点间隔(分钟)"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_ship_package_view #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl__process_time
msgid "Process Time"
msgstr "推送时间"
#. module: ccs_connect_tiktok
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_ship_package_view_inherit
msgid "Progress" msgid "Progress"
msgstr "进度" msgstr "进度"
#. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log__progress_name
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_bl_sync_log_view
msgid "Progress Name"
msgstr "进度名称"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model,name:ccs_connect_tiktok.model_cc_ship_package #: model:ir.model,name:ccs_connect_tiktok.model_cc_ship_package
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__package_id #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__package_id
...@@ -222,15 +296,27 @@ msgstr "小包" ...@@ -222,15 +296,27 @@ msgstr "小包"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_batch_input_ship_package_status_wizard__is_skip_check #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_batch_input_ship_package_status_wizard__is_skip_check
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_update_bl_status_wizard__is_skip_check
msgid "Skip Check" msgid "Skip Check"
msgstr "跳过检查" msgstr "跳过检查"
#. module: ccs_connect_tiktok
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_view_inherit
msgid "State Explain"
msgstr "状态说明"
#. module: ccs_connect_tiktok
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_view_inherit
msgid "Sync Bill Of Loading Status"
msgstr "同步提单状态"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_view_inherit #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_view_inherit
msgid "Sync CC Attachment" msgid "Sync CC Attachment"
msgstr "同步清关文件" msgstr "同步清关文件"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_view_inherit
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_ship_package_view_inherit #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_ship_package_view_inherit
msgid "Sync Log" msgid "Sync Log"
msgstr "同步日志" msgstr "同步日志"
...@@ -246,9 +332,13 @@ msgid "Sync Package Status" ...@@ -246,9 +332,13 @@ msgid "Sync Package Status"
msgstr "同步包裹状态" msgstr "同步包裹状态"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log__sync_time
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__sync_time #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__sync_time
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_ship_package_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_ship_package_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_ship_package_sync_log_view
msgid "Sync Time" msgid "Sync Time"
msgstr "同步时间" msgstr "同步时间"
...@@ -276,9 +366,13 @@ msgid "TK Code" ...@@ -276,9 +366,13 @@ msgid "TK Code"
msgstr "编码" msgstr "编码"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl_sync_log__process_code
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__process_code #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_ship_package_sync_log__process_code
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.form_cc_ship_package_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.search_cc_ship_package_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_bl_sync_log_view
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_ship_package_sync_log_view #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_ship_package_sync_log_view
msgid "TK Process Code" msgid "TK Process Code"
msgstr "进度编码" msgstr "进度编码"
...@@ -286,6 +380,7 @@ msgstr "进度编码" ...@@ -286,6 +380,7 @@ msgstr "进度编码"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#. odoo-python #. odoo-python
#: code:addons/ccs_connect_tiktok/wizard/batch_input_ship_package_statu_wizard.py:0 #: code:addons/ccs_connect_tiktok/wizard/batch_input_ship_package_statu_wizard.py:0
#: code:addons/ccs_connect_tiktok/wizard/update_bl_status_wizard.py:0
#, python-format #, python-format
msgid "" msgid ""
"The operation time cannot be greater than the current time and cannot be " "The operation time cannot be greater than the current time and cannot be "
...@@ -310,14 +405,28 @@ msgstr "单号【%s】需填写在排除单号中!" ...@@ -310,14 +405,28 @@ msgstr "单号【%s】需填写在排除单号中!"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_bl_view_inherit #: model_terms:ir.ui.view,arch_db:ccs_connect_tiktok.tree_cc_bl_view_inherit
msgid "UnSync" msgid "UnSync Package Count"
msgstr "未同步" msgstr "未同步包裹数"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl__unsync_package_count #: model:ir.model.fields,field_description:ccs_connect_tiktok.field_cc_bl__unsync_package_count
msgid "Unsync Package Count" msgid "Unsync Package Count"
msgstr "未同步包裹数" msgstr "未同步包裹数"
#. module: ccs_connect_tiktok
#: model:ir.model,name:ccs_connect_tiktok.model_update_bl_status_wizard
msgid "Update the status of the bill of loading"
msgstr "更新提单状态"
#. module: ccs_connect_tiktok
#. odoo-python
#: code:addons/ccs_connect_tiktok/wizard/update_bl_status_wizard.py:0
#, python-format
msgid ""
"[%s] The update node is not the next node in the state of the bill of "
"loading!"
msgstr "【%s】更新节点不是提单的状态的下一节点!"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#. odoo-python #. odoo-python
#: code:addons/ccs_connect_tiktok/wizard/batch_input_ship_package_statu_wizard.py:0 #: code:addons/ccs_connect_tiktok/wizard/batch_input_ship_package_statu_wizard.py:0
...@@ -328,10 +437,18 @@ msgstr "[%s]更新节点不是小包状态的下一个节点!" ...@@ -328,10 +437,18 @@ msgstr "[%s]更新节点不是小包状态的下一个节点!"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#. odoo-python #. odoo-python
#: code:addons/ccs_connect_tiktok/wizard/batch_input_ship_package_statu_wizard.py:0 #: code:addons/ccs_connect_tiktok/wizard/batch_input_ship_package_statu_wizard.py:0
#: code:addons/ccs_connect_tiktok/wizard/update_bl_status_wizard.py:0
#, python-format #, python-format
msgid "[%s]%sUpdate to[%s]%s" msgid "[%s]%sUpdate to[%s]%s"
msgstr "[%s]%s更新为[%s]%s" msgstr "[%s]%s更新为[%s]%s"
#. module: ccs_connect_tiktok
#: model_terms:ir.actions.act_window,help:ccs_connect_tiktok.action_cc_bl_sync_log
msgid ""
"[CC Bill Of Loading Sync Log] Not yet! Click the Create button in the top "
"left corner and the sofa is yours!"
msgstr "[提单] 还没有!点击左上角的创建按钮,沙发就是你的了!"
#. module: ccs_connect_tiktok #. module: ccs_connect_tiktok
#: model_terms:ir.actions.act_window,help:ccs_connect_tiktok.action_cc_ship_package_sync_log #: model_terms:ir.actions.act_window,help:ccs_connect_tiktok.action_cc_ship_package_sync_log
msgid "" msgid ""
......
# -*- coding: utf-8 -*-
import asyncio import asyncio
import logging import logging
import ssl import ssl
...@@ -134,17 +135,6 @@ class CcShipPackage(models.Model): ...@@ -134,17 +135,6 @@ class CcShipPackage(models.Model):
# 增加同步日志纪录字段 # 增加同步日志纪录字段
sync_log_ids = fields.One2many('cc.ship.package.sync.log', 'package_id', 'Sync Logs') sync_log_ids = fields.One2many('cc.ship.package.sync.log', 'package_id', 'Sync Logs')
def is_next_code(self, next_state_id):
"""
判断更新的节点是否是 小包状态的下级节点
:param next_state_id:
:return:
"""
if self.state:
if next_state_id in self.state.next_code_ids.ids:
return True
return False
@api.model @api.model
def create(self, vals_list): def create(self, vals_list):
""" """
...@@ -251,15 +241,198 @@ class CcBl(models.Model): ...@@ -251,15 +241,198 @@ class CcBl(models.Model):
else: else:
record.unsync_package_count = 0 record.unsync_package_count = 0
# 计算提单状态操作时间
@api.depends('bl_sync_log_ids')
def _compute_process_time(self):
for bl in self:
if bl.bl_sync_log_ids:
bl.process_time = sorted(bl.bl_sync_log_ids.mapped('operate_time'), reverse=True)[0]
else:
bl.process_time = False
# 增加未同步小包数量字段 # 增加未同步小包数量字段
unsync_package_count = fields.Integer('Unsync Package Count', compute='_compute_unsync_package_count', store=True) unsync_package_count = fields.Integer('Unsync Package Count', compute='_compute_unsync_package_count', store=True)
# 关联提单同步日志
bl_sync_log_ids = fields.One2many('cc.bl.sync.log', 'bl_id', string='Bill Of Loading Sync Logs')
# 增加提单状态操作时间:取最新一条提单节点同步信息的操作时间
process_time = fields.Datetime(string='Process Time', compute='_compute_process_time', store=True)
def change_state_by_ship_package(self):
"""
根据小包的状态修改提单的状态
:return:
"""
# 如果提单有小包变成了清关开始,提单状态变为清关中
if self.state == 'draft' and self.ship_package_ids.filtered(
lambda line: line.state.tk_code == 'cb_imcustoms_start'):
self.ccing_func()
# 如果提单所有小包的清关节点变成"是完成节点",则该提单状态变成已完成
if all(line.state.is_done for line in
self.ship_package_ids) and self.unsync_package_count <= 0 and self.customs_clearance_status.is_done and self.is_bl_sync:
self.done_func()
def change_customs_state_by_ship_package(self, package_state_obj):
"""
根据小包的状态修改提单关务状态以及生成同步日志
:param package_state_obj:小包更新后的状态
:return:
"""
# 根据小包的状态找到对应的提单关务状态
state_obj = self.env['cc.node'].search([('package_state', '=', package_state_obj.id)], limit=1)
for bl in self:
# 判断当前提单的关务提单状态是需要更新的前序节点,则进行修改和生成同步日志
if self.customs_clearance_status.is_before_node(state_obj):
# 判断提单下所有小包的同步日志是否都有已提货的同步日志
is_all = self.check_multiple_bl_pickup_status_best(package_state_obj)
if is_all:
# 操作时间取小包对应操作时间最晚的一条
operate_time = sorted(self.ship_package_ids.mapped('process_time'), reverse=True)[0]
# 检查该状态之前的所有节点是否都有同步日志,如果没有,则生成同步日志
before_node_obj = state_obj.get_before_node(node_type='bl')
is_ok = True
for before_node in before_node_obj:
if not bl.bl_sync_log_ids.filtered(lambda r: r.process_code == before_node.tk_code):
# 修改关务提单状态
bl.customs_clearance_status = before_node.id
# 操作时间根据设置的前序节点间距时间取值
before_minutes = before_node.calculate_total_interval(state_obj)
bl.process_time = datetime.strptime(str(operate_time), '%Y-%m-%d %H:%M:%S') - timedelta(
minutes=before_minutes)
# 调用同步关务提单状态
is_ok = bl.callback_track_bl()
if not is_ok:
break
if is_ok:
bl.customs_clearance_status = state_obj.id
bl.process_time = operate_time
# 调用同步提单状态
bl.callback_track_bl()
def check_multiple_bl_pickup_status_best(self, package_state_obj):
"""
最优方案: 使用单次search_count
"""
# 使用search_count直接统计符合条件的小包数量,判断和提单的小包数量是否相等
missing_log_count = self.env['cc.ship.package'].search_count([
('bl_id', '=', self.id),
('sync_log_ids.process_code', '=', package_state_obj.tk_code)
])
return missing_log_count == len(self.ship_package_ids)
def check_multiple_bl_pickup_status(self, package_state_obj):
"""检查单个提单下所有小包是否都有同步日志"""
sql = """
SELECT
CASE WHEN COUNT(DISTINCT sp.id) > 0 -- 确保有小包存在
AND COUNT(DISTINCT sp.id) = COUNT(DISTINCT CASE
WHEN bsl.process_code = '{0}'
THEN sp.id
END)
THEN TRUE
ELSE FALSE
END as all_picked_up
FROM cc_bl bl
JOIN cc_ship_package sp ON sp.bl_id = bl.id
LEFT JOIN cc_ship_package_sync_log bsl ON
bsl.package_id = sp.id AND
bsl.process_code = '{0}'
WHERE bl.id = {1}
""".format(package_state_obj.tk_code, self.id)
self.env.cr.execute(sql)
result = self.env.cr.fetchone()
return result and result[0] or False
# =============同步提单状态==================================
# 定义一个方法, 获取提单,并回传提单状态
def callback_track_bl(self):
is_ok = True
for item in self:
is_ok = item.bl_callback_func(item.ids)
return is_ok
def bl_callback_func(self, bl_ids):
"""
同步提单状态
"""
bls = self.env['cc.bl'].search([('id', 'in', bl_ids), ('is_bl_sync', '=', False)])
logging.info('bl_callback_func bls:%s' % len(bls))
is_ok = True
tt_api_obj = self.env["ao.tt.api"].sudo()
async def bl_perform_requests():
ssl_context = ssl.create_default_context(cafile=certifi.where())
async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=ssl_context),
timeout=aiohttp.ClientTimeout(total=60)) as session:
tasks = []
for index, bl in enumerate(bls):
if not bl.is_bl_sync and bl.customs_clearance_status and bl.customs_clearance_status.tk_code:
data = bl.get_bl_callback_track_data()
tasks.append(tt_api_obj.async_bl_callback_track(session, data, bl.id))
responses = await asyncio.gather(*tasks)
return responses
# 在 Odoo 中运行异步任务
responses = asyncio.run(bl_perform_requests())
for response_item in responses:
response_data = response_item[0]
logging.info('bl response_data:%s' % response_data)
data = response_item[1]
bl_id = response_item[2]
bl_order = self.env['cc.bl'].sudo().browse(bl_id)
if response_data['code'] != 0:
bl_order.is_bl_sync = False
self._cr.commit() # 提交事务
error_msg = response_data['msg']
request_id = response_data['requestID']
code = response_data['code']
self.env['ao.tt.api.log'].sudo().create_api_log(
bl_order.bl_no or '', '提单状态回传:' + error_msg, data, code, request_id,
source='推出')
is_ok = False
else:
# 回传成功
bl_order.is_bl_sync = True
self._cr.commit() # 提交事务
self.env['cc.bl.sync.log'].sudo().create_sync_log(
bl_order.id, 'Tiktok', bl_order.customs_clearance_status.tk_code,
bl_order.customs_clearance_status.name, bl_order.state_explain or '',
bl_order.process_time.strftime('%Y-%m-%d %H:%M:%S'))
request_id = response_data['requestID']
self.env['ao.tt.api.log'].sudo().create_api_log(
bl_order.bl_no or '', '', data, 0, request_id, source='推出')
return is_ok
def get_bl_callback_track_data(self):
"""
获取提单回传数据
"""
return {
"master_waybill_no": self.bl_no or '', # 主提运单号
"customs_waybill_id": self.customs_bl_no or '', # 关务提单号取海关装货单号
"track_detail":
{
"operate_time": get_rfc339_time(self.process_time), # "2025-03-19T09:28:00+00:00",
# "#get_utc_time(self.process_time), # 事件发⽣时间,RFC3339格式(实操时间)
"waybill_status_code": self.customs_clearance_status.tk_code, # 提单关务状态编码
}
}
# =============同步小包状态==================================
# 定义一个方法, 获取提单下的所有未同步的小包,并回传小包状态 # 定义一个方法, 获取提单下的所有未同步的小包,并回传小包状态
def callback_track(self): def callback_track(self):
is_ok = True is_ok = True
for item in self: for item in self:
ship_packages = self.env['cc.ship.package'].search([('bl_id', '=', item.id), ('is_sync', '=', False)]) ship_packages = self.env['cc.ship.package'].search([('bl_id', '=', item.id), ('is_sync', '=', False)])
is_ok = item.package_callback_func(ship_packages.ids) is_ok = item.package_callback_func(ship_packages.ids)
# 根据小包状态更新提单关务状态
if is_ok and self.ship_package_ids:
self.change_customs_state_by_ship_package(self.ship_package_ids[0].state)
return is_ok return is_ok
def package_callback_func(self, ship_package_ids): def package_callback_func(self, ship_package_ids):
...@@ -285,9 +458,10 @@ class CcBl(models.Model): ...@@ -285,9 +458,10 @@ class CcBl(models.Model):
# 在 Odoo 中运行异步任务 # 在 Odoo 中运行异步任务
responses = asyncio.run(perform_requests()) responses = asyncio.run(perform_requests())
# log_values=[]
for response_item in responses: for response_item in responses:
response_data = response_item[0] response_data = response_item[0]
logging.info('response_data response:%s' % response_data) logging.info('ship response_data:%s' % response_data)
data = response_item[1] data = response_item[1]
package_id = response_item[2] package_id = response_item[2]
package_order = self.env['cc.ship.package'].sudo().browse(package_id) package_order = self.env['cc.ship.package'].sudo().browse(package_id)
...@@ -304,17 +478,75 @@ class CcBl(models.Model): ...@@ -304,17 +478,75 @@ class CcBl(models.Model):
else: else:
# 回传成功 # 回传成功
package_order.is_sync = True package_order.is_sync = True
self._cr.commit() # 提交事务
self.env['cc.ship.package.sync.log'].sudo().create_sync_log( self.env['cc.ship.package.sync.log'].sudo().create_sync_log(
package_order.id, 'Tiktok', package_order.state.tk_code, package_order.state_explain, package_order.id, 'Tiktok', package_order.state.tk_code, package_order.state_explain,
package_order.process_time.strftime('%Y-%m-%d %H:%M:%S')) package_order.process_time.strftime('%Y-%m-%d %H:%M:%S'))
self._cr.commit() # 提交事务
request_id = response_data['requestID'] request_id = response_data['requestID']
self.env['ao.tt.api.log'].sudo().create_api_log( self.env['ao.tt.api.log'].sudo().create_api_log(
package_order.tracking_no or '', '', data, 0, request_id, source='推出') package_order.tracking_no or '', '', data, 0, request_id, source='推出')
# log_values.append(
# (package_order.id,
# 'Tiktok',
# package_order.state.tk_code,
# package_order.state_explain or '',
# package_order.process_time.strftime('%Y-%m-%d %H:%M:%S')
# ))
# self.batch_create_package_sync_logs_by_sql(log_values)
# 如果提单有小包变成了清关开始,提单状态变为清关中;如果提单所有小包的清关节点变成"是完成节点",则该提单状态变成已完成 # 如果提单有小包变成了清关开始,提单状态变为清关中;如果提单所有小包的清关节点变成"是完成节点",则该提单状态变成已完成
self.change_state_by_ship_package() self.change_state_by_ship_package()
return is_ok return is_ok
def batch_create_package_sync_logs_by_sql(self, log_values):
"""
批量创建小包同步日志 废弃,先不用
Args:
log_values: 日志值列表,每项包含
(package_id, api_customer, process_code, operate_remark, process_time)
"""
if not log_values:
return
sql = """
INSERT INTO cc_ship_package_sync_log (
create_uid,
create_date,
write_uid,
write_date,
package_id,
api_customer,
process_code,
operate_remark,
operate_time
) VALUES %s
"""
uid = self.env.uid
now = fields.Datetime.now()
# 准备批量插入的值
values = []
params = []
for val in log_values:
values.append("(%s, %s, %s, %s, %s, %s, %s, %s, %s)")
params.extend([
uid, now, uid, now,
val[0], # package_id
val[1], # platform
val[2], # process_code
val[3], # state_explain
val[4] # process_time
])
# 构建完整的SQL
sql = sql % ','.join(values)
logging.info('ship_package_sync_log sql:%s' % sql)
# 执行批量插入
self.env.cr.execute(sql, params)
self._cr.commit() # 提交事务
def deal_ship_package_state(self): def deal_ship_package_state(self):
for item in self: for item in self:
ship_packages = self.env['cc.ship.package'].search([('bl_id', '=', item.id), ('is_sync', '=', False)]) ship_packages = self.env['cc.ship.package'].search([('bl_id', '=', item.id), ('is_sync', '=', False)])
...@@ -392,14 +624,17 @@ class CcBl(models.Model): ...@@ -392,14 +624,17 @@ class CcBl(models.Model):
else: else:
is_ok = self.package_callback_func(ship_package_ids) is_ok = self.package_callback_func(ship_package_ids)
if is_ok: if is_ok:
return True # 根据小包状态更新提单关务状态
if self.ship_package_ids:
self.change_customs_state_by_ship_package(self.ship_package_ids[0].state)
return is_ok
logging.warning(f"Attempt {i + 1}/{max_retries} failed. Retrying...") logging.warning(f"Attempt {i + 1}/{max_retries} failed. Retrying...")
return False return False
def mail_auto_push(self, mail_time=False, ship_packages=[], action_type='tally'): def mail_auto_push(self, mail_time=False, ship_packages=[], action_type='tally'):
self = self.with_context(dict(self._context, is_mail=True)) self = self.with_context(dict(self._context, is_mail=True))
for item in self: for item in self:
# try: try:
if mail_time: if mail_time:
utc_time = datetime.strptime(mail_time, "%Y-%m-%d %H:%M:%S") utc_time = datetime.strptime(mail_time, "%Y-%m-%d %H:%M:%S")
before_min = self.env['ir.config_parameter'].sudo().get_param('before_min') or 10 before_min = self.env['ir.config_parameter'].sudo().get_param('before_min') or 10
...@@ -446,9 +681,7 @@ class CcBl(models.Model): ...@@ -446,9 +681,7 @@ class CcBl(models.Model):
ship_packages_dict[single_id] = package['tally_time'] ship_packages_dict[single_id] = package['tally_time']
# 前序节点 理货或尾程交接之前没有生成的节点 # 前序节点 理货或尾程交接之前没有生成的节点
before_node_obj = self.env['cc.node'].sudo().search([ before_node_obj = node_obj[0].get_before_node()
('node_type', '=', 'package'), ('is_must', '=', True), ('seq', '<', node_obj[0].seq)],
order='seq asc')
# 理货或尾程交接之前没有生成的节点 # 理货或尾程交接之前没有生成的节点
for before_node in before_node_obj: for before_node in before_node_obj:
print('before_node:%s' % before_node.name) print('before_node:%s' % before_node.name)
...@@ -461,7 +694,8 @@ class CcBl(models.Model): ...@@ -461,7 +694,8 @@ class CcBl(models.Model):
package_id, set()): package_id, set()):
tally_time = ship_packages_dict.get(package_id) tally_time = ship_packages_dict.get(package_id)
if tally_time: if tally_time:
operation_time = (datetime.strptime(tally_time, '%Y-%m-%d %H:%M:%S') - timedelta( operation_time = (
datetime.strptime(tally_time, '%Y-%m-%d %H:%M:%S') - timedelta(
minutes=before_minutes)) if tally_time else fields.Datetime.now() - timedelta( minutes=before_minutes)) if tally_time else fields.Datetime.now() - timedelta(
minutes=before_minutes) minutes=before_minutes)
update_data.append(( update_data.append((
...@@ -490,20 +724,6 @@ class CcBl(models.Model): ...@@ -490,20 +724,6 @@ class CcBl(models.Model):
self.env.cr.execute(sql) self.env.cr.execute(sql)
self._cr.commit() # 提交事务 self._cr.commit() # 提交事务
# # 更新提单的未同步小包数量
# sql = """
# UPDATE cc_bl AS bl SET
# unsync_package_count = (
# SELECT COUNT(*)
# FROM cc_ship_package sp
# WHERE sp.bl_id = bl.id
# AND sp.is_sync = false
# )
# WHERE bl.id = %s
# """
# self.env.cr.execute(sql, (item.id,))
# self._cr.commit() # 提交事务
self.try_callback_track(max_retries=2, ship_package_ids=ship_package_ids) self.try_callback_track(max_retries=2, ship_package_ids=ship_package_ids)
# 理货或尾程交接的节点 # 理货或尾程交接的节点
# 预先获取所有状态节点 # 预先获取所有状态节点
...@@ -532,9 +752,7 @@ class CcBl(models.Model): ...@@ -532,9 +752,7 @@ class CcBl(models.Model):
node.desc, node.desc,
True if node.is_default else False True if node.is_default else False
)) ))
print('update_data:%s' % update_data)
if update_data: if update_data:
print('11111111111')
# 构建批量更新SQL # 构建批量更新SQL
values_str = ','.join( values_str = ','.join(
self.env.cr.mogrify("(%s,%s,%s,%s,%s)", row).decode('utf-8') for row in update_data) self.env.cr.mogrify("(%s,%s,%s,%s,%s)", row).decode('utf-8') for row in update_data)
...@@ -556,26 +774,51 @@ class CcBl(models.Model): ...@@ -556,26 +774,51 @@ class CcBl(models.Model):
# sql = """UPDATE cc_bl AS bl SET unsync_package_count = ( SELECT COUNT(*) FROM cc_ship_package sp WHERE sp.bl_id = bl.id AND sp.is_sync = false) WHERE bl.id = %s """ # sql = """UPDATE cc_bl AS bl SET unsync_package_count = ( SELECT COUNT(*) FROM cc_ship_package sp WHERE sp.bl_id = bl.id AND sp.is_sync = false) WHERE bl.id = %s """
# self.env.cr.execute(sql, (item.id,)) # self.env.cr.execute(sql, (item.id,))
# self._cr.commit() # 提交事务 # self._cr.commit() # 提交事务
print('222222')
self.try_callback_track(max_retries=2, ship_package_ids=ship_package_ids) self.try_callback_track(max_retries=2, ship_package_ids=ship_package_ids)
print('33333')
return True return True
# except Exception as err: except Exception as err:
# logging.error('fetch_mail_dlv--error:%s' % str(err)) logging.error('fetch_mail_dlv--error:%s' % str(err))
def change_state_by_ship_package(self):
""" # 提单节点同步日志
根据小包的状态修改提单的状态 class CcBlSyncLog(models.Model):
:return: _name = 'cc.bl.sync.log'
""" _description = 'CC Bill Of Loading Sync Log'
# 如果提单有小包变成了清关开始,提单状态变为清关中
if self.state == 'draft' and self.ship_package_ids.filtered( # 定义模型字段
lambda line: line.state.tk_code == 'cb_imcustoms_start'): # 提单对象
self.ccing_func() bl_id = fields.Many2one('cc.bl', 'Bill of Loading', required=True)
# 如果提单所有小包的清关节点变成"是完成节点",则该提单状态变成已完成 # 同步时间
if all(line.state.is_done for line in self.ship_package_ids) and self.unsync_package_count <= 0: sync_time = fields.Datetime('Sync Time', default=fields.Datetime.now)
self.done_func() # 增加接口客户
api_customer = fields.Char('Api Customer')
# 操作状态
process_code = fields.Char('TK Process Code')
# 加一个进度名称
progress_name = fields.Char('Progress Name')
# 操作时间
operate_time = fields.Datetime('Operate Time', default=fields.Datetime.now)
# 操作备注
operate_remark = fields.Text('Operate Remark')
# 同步操作人
operate_user = fields.Many2one('res.users', 'Operate User', default=lambda self: self.env.user)
# 添加一个新增日志的方法,传入小包ID,API客户,操作状态,操作备注,操作时间
@api.model
def create_sync_log(self, bl_id, api_customer, process_code, progress_name, operate_remark, operate_time):
vals = {
'bl_id': bl_id,
'api_customer': api_customer,
'process_code': process_code,
'progress_name': progress_name,
'operate_remark': operate_remark,
'operate_time': operate_time
}
if self._context.get('is_mail'):
public_user = self.env.ref('base.public_user')
vals['operate_user'] = public_user.id
return self.create(vals)
class CcBigPackage(models.Model): class CcBigPackage(models.Model):
......
import asyncio
import logging
import ssl
from datetime import timedelta, datetime
import aiohttp
import certifi
import pytz
from odoo import models, fields, api, _
def get_rfc339_time(utc_time=None):
if not utc_time:
# 获取当前时间的UTC时间
utc_time = datetime.utcnow()
# 创建+8时区的对象
target_timezone = pytz.timezone('Asia/Shanghai')
# 将UTC时间转换为目标时区时间
local_time = utc_time.replace(tzinfo=pytz.utc).astimezone(target_timezone)
# 格式化为RFC 3339格式
rfc3339_time = local_time.isoformat(timespec='seconds')
return rfc3339_time
# 定义一个方法,将时间的时区转为UTC时间
def get_utc_time(local_time=None):
if not local_time:
# 获取当前时间的UTC时间
local_time = datetime.now()
# 将本地时间转换为UTC时间
utc_time = local_time.astimezone(pytz.utc)
# 格式化为RFC 3339格式
# rfc3339_time = utc_time.isoformat(timespec='seconds')
return utc_time.strftime('%Y-%m-%d %H:%M:%S')
# 继承cc.clearance.file对象,并重载action_upload方法
class CcClearanceFile(models.Model):
_inherit = "cc.clearance.file"
_description = "Clearance File" # 清关文件
def get_clearance_file_feedback_data(self):
"""通关文件上传数据组织"""
happend_time = get_rfc339_time()
push_data = {
"master_waybill_no": self.bl_id.bl_no or '',
"customs_waybill_id": self.bl_id.customs_bl_no or '',
"operate_time": happend_time,
"file_detail": {
"file_url": "",
"file_code": self.file.decode(), # 将文件内容转换为base64编码,
"file_type": "PDF"
}
}
return push_data
def clearance_file_feedback(self):
if not self.is_upload and self.file:
data = self.get_clearance_file_feedback_data()
tt_api_obj = self.env["ao.tt.api"].sudo()
response = tt_api_obj.clearance_file_feedback(data)
response_data = response.json()
if response_data['code'] != 0:
# 清关文件回传错误
self.is_upload = False
error_msg = response_data['msg']
request_id = response_data['requestID']
code = response_data['code']
self.env['ao.tt.api.log'].sudo().create_api_log(self.file_name or '', '清关文件回传:' + error_msg, data,
code,
request_id, source='推出')
return error_msg
else:
# 清关文件回传成功
self.is_upload = True
self.upload_time = datetime.now()
request_id = response_data['requestID']
self.env['ao.tt.api.log'].sudo().create_api_log(self.file_name or '', '', data, 0, request_id,
source='推出')
return ''
# 重载action_upload方法
def action_sync(self):
self.clearance_file_feedback()
return super(CcClearanceFile, self).action_sync()
# 定义小包同步纪录对象,用于记录小包同步的纪录,包括小包对象,同步时间,操作状态,操作时间,操行备注,同步操作人
class CcShipPackageSyncLog(models.Model):
_name = 'cc.ship.package.sync.log'
_description = 'CC Ship Package Sync Log'
# 定义模型字段
# 小包对象
package_id = fields.Many2one('cc.ship.package', 'Ship Package', required=True)
# 同步时间
sync_time = fields.Datetime('Sync Time', default=fields.Datetime.now)
# 增加接口客户
api_customer = fields.Char('Api Customer')
# 操作状态
process_code = fields.Char('TK Process Code')
# 操作时间
operate_time = fields.Datetime('Operate Time', default=fields.Datetime.now)
# 操作备注
operate_remark = fields.Text('Operate Remark')
# 同步操作人
operate_user = fields.Many2one('res.users', 'Operate User', default=lambda self: self.env.user)
# 添加一个新增日志的方法,传入小包ID,API客户,操作状态,操作备注,操作时间
@api.model
def create_sync_log(self, package_id, api_customer, process_code, operate_remark, operate_time):
vals = {
'package_id': package_id,
'api_customer': api_customer,
'process_code': process_code,
'operate_remark': operate_remark,
'operate_time': operate_time
}
if self._context.get('is_mail'):
public_user = self.env.ref('base.public_user')
vals['operate_user'] = public_user.id
return self.create(vals)
# 继承小包对象,并重载action_sync方法, 增加is_sync字段
class CcShipPackage(models.Model):
_inherit = "cc.ship.package"
is_sync = fields.Boolean('Is Sync', default=False, index=True)
tk_code = fields.Char(related='state.tk_code', store=True, string='TK Code', help='TK Code')
# 增加同步日志纪录字段
sync_log_ids = fields.One2many('cc.ship.package.sync.log', 'package_id', 'Sync Logs')
def is_next_code(self, next_state_id):
"""
判断更新的节点是否是 小包状态的下级节点
:param next_state_id:
:return:
"""
if self.state:
if next_state_id in self.state.next_code_ids.ids:
return True
return False
@api.model
def create(self, vals_list):
"""
第一个节点的时候 默认已同步
"""
obj = super(CcShipPackage, self).create(vals_list)
if obj.state.is_default:
obj.is_sync = True
return obj
def action_sync(self):
for record in self:
record.is_sync = True
self.env['cc.ship.package.sync.log'].sudo().create_sync_log(record.id, 'Tiktok', record.state.tk_code,
record.state_explain,
record.process_time.strftime(
'%Y-%m-%d %H:%M:%S'))
def get_callback_track_data(self):
"""小包上传数据."""
# 获取该提单下的所有同步状态为未同步的小包
push_data = {
"provider_order_id": self.logistic_order_no,
"track_list": [
{
"shipping_method_code": self.state.tk_code,
"operate_time": get_utc_time(self.process_time),
"time_zone": "UTC+0",
"action_code": self.state.tk_code,
"operation_desc": self.state.desc,
"reason_code": self.node_exception_reason_id.name or "" # 异常原因
}
]
}
# logging.info('小包轨迹 push_data:%s' % push_data)
return push_data
def callback_track(self, is_push=True):
if not self.is_sync and self.state and self.state.tk_code:
data = self.get_callback_track_data()
if is_push:
tt_api_obj = self.env["ao.tt.api"].sudo()
response = tt_api_obj.callback_track(data)
response_data = response.json()
if response_data['code'] != 0:
self.is_sync = False
self._cr.commit()
error_msg = response_data['msg']
request_id = response_data['requestID']
code = response_data['code']
self.env['ao.tt.api.log'].sudo().create_api_log(self.tracking_no or '',
'小包状态轨迹回传:' + error_msg,
data,
code,
request_id, source='推出')
return error_msg
else:
# 回传成功
self.is_sync = True
self.env['cc.ship.package.sync.log'].sudo().create_sync_log(self.id, 'Tiktok', self.state.tk_code,
self.state_explain,
self.process_time.strftime(
'%Y-%m-%d %H:%M:%S'))
self._cr.commit()
request_id = response_data['requestID']
self.env['ao.tt.api.log'].sudo().create_api_log(self.tracking_no or '', '', data, 0, request_id,
source='推出')
return ''
else:
self.is_sync = True
self.env['cc.ship.package.sync.log'].sudo().create_sync_log(self.id, 'Tiktok', self.state.tk_code,
self.state_explain,
self.process_time.strftime(
'%Y-%m-%d %H:%M:%S'))
self._cr.commit()
request_id = ''
self.env['ao.tt.api.log'].sudo().create_api_log(self.tracking_no or '', '', data, 0, request_id,
source='推出')
return ''
def search_ship_package_info(self, pda_lang=False):
"""
查询小包信息
:return:
"""
return {
'logistic_order_no': self.logistic_order_no, # 物流订单号
}
# 继承提单对象t
class CcBl(models.Model):
_inherit = 'cc.bl'
# 计算未同步小包数量
@api.depends('ship_package_ids', 'ship_package_ids.is_sync')
def _compute_unsync_package_count(self):
for record in self:
record_counts = record.ship_package_ids.filtered(lambda r: not r.is_sync)
if record_counts:
record.unsync_package_count = len(record_counts)
else:
record.unsync_package_count = 0
# 增加未同步小包数量字段
unsync_package_count = fields.Integer('Unsync Package Count', compute='_compute_unsync_package_count', store=True)
# 定义一个方法, 获取提单下的所有未同步的小包,并回传小包状态
def callback_track(self):
is_ok = True
for item in self:
ship_packages = self.env['cc.ship.package'].search([('bl_id', '=', item.id), ('is_sync', '=', False)])
is_ok = item.package_callback_func(ship_packages.ids)
return is_ok
def package_callback_func(self, ship_package_ids):
"""
同步小包状态
"""
ship_packages = self.env['cc.ship.package'].search([('id', 'in', ship_package_ids), ('is_sync', '=', False)])
logging.info('package_callback_func ship_packages:%s' % len(ship_packages))
is_ok = True
tt_api_obj = self.env["ao.tt.api"].sudo()
async def perform_requests():
ssl_context = ssl.create_default_context(cafile=certifi.where())
async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=ssl_context),
timeout=aiohttp.ClientTimeout(total=60)) as session:
tasks = []
for index, package in enumerate(ship_packages):
if not package.is_sync and package.state and package.state.tk_code:
data = package.get_callback_track_data()
tasks.append(tt_api_obj.async_callback_track(session, data, package.id))
responses = await asyncio.gather(*tasks)
return responses
# 在 Odoo 中运行异步任务
responses = asyncio.run(perform_requests())
for response_item in responses:
response_data = response_item[0]
logging.info('response_data:%s' % response_data)
data = response_item[1]
package_id = response_item[2]
package_order = self.env['cc.ship.package'].sudo().browse(package_id)
if response_data['code'] != 0:
package_order.is_sync = False
self._cr.commit() # 提交事务
error_msg = response_data['msg']
request_id = response_data['requestID']
code = response_data['code']
self.env['ao.tt.api.log'].sudo().create_api_log(
package_order.tracking_no or '', '小包状态轨迹回传:' + error_msg, data, code, request_id,
source='推出')
is_ok = False
else:
# 回传成功
package_order.is_sync = True
self._cr.commit() # 提交事务
self.env['cc.ship.package.sync.log'].sudo().create_sync_log(
package_order.id, 'Tiktok', package_order.state.tk_code, package_order.state_explain,
package_order.process_time.strftime('%Y-%m-%d %H:%M:%S'))
request_id = response_data['requestID']
self.env['ao.tt.api.log'].sudo().create_api_log(
package_order.tracking_no or '', '', data, 0, request_id, source='推出')
# 如果提单有小包变成了清关开始,提单状态变为清关中;如果提单所有小包的清关节点变成"是完成节点",则该提单状态变成已完成
self.change_state_by_ship_package()
return is_ok
def deal_ship_package_state(self):
for item in self:
ship_packages = self.env['cc.ship.package'].search([('bl_id', '=', item.id), ('is_sync', '=', False)])
for package in ship_packages:
package.callback_track(is_push=False)
return True
def batch_action_sync(self):
"""
批量回传通过文件
"""
for item in self:
for line in item.cc_attachment_ids:
line.action_sync()
# 创建显示包裹的action
def action_show_no_sync_ship_package(self):
# 返回一个action,显示包裹
return {
'name': _('Not Sync Ship Packages'),
'type': 'ir.actions.act_window',
'res_model': 'cc.ship.package',
'view_mode': 'tree,form',
'domain': [('bl_id', '=', self.id), ('is_sync', '=', False)],
}
def search_bl_info(self, pda_lang=False, type='tally'):
"""
查询提单信息
"""
vals = {
'bl_no': self.bl_no or '', # 提单号
'scan_big_package_qty': self.tally_big_package_qty + self.delivered_big_package_qty if type == 'tally' else self.delivered_big_package_qty,
# 已扫大包数量
'big_package_arr': [big_package_item.search_big_package_info(pda_lang=pda_lang, type=type) for
big_package_item in
self.big_package_ids],
# 大包信息
'ship_package_arr': [ship_package_item.search_ship_package_info(pda_lang=pda_lang) for ship_package_item in
self.ship_package_ids], # 小包信息
'pallet_arr': self.get_unique_pallet_info(), # 托盘信息
}
return vals
def get_unique_pallet_info(self):
"""获取唯一托盘信息,返回托盘号和最早的使用时间"""
pallet_info = {}
for package in self.big_package_ids:
pallet_number = package.pallet_number
pallet_usage_time = package.pallet_usage_date
if pallet_number and (pallet_number not in pallet_info or pallet_info[pallet_number] > pallet_usage_time):
pallet_info[pallet_number] = pallet_usage_time
return [{'pallet_number': k, 'pallet_usage_time': v} for k, v in pallet_info.items()]
def deal_bl_no(self, bl_no, state_arr=[]):
"""
处理提单号:去掉杠和空格,并转换为小写
:param bl_no:
:return:
"""
processed_bl_no = bl_no.replace('-', '').replace(' ', '').lower()
# 查询所有提单并处理它们的 bl_no
domain = [('state', 'in', state_arr)] if state_arr else []
all_bl_obj = self.env['cc.bl'].sudo().search(domain)
bl_obj = all_bl_obj.filtered(
lambda r: r.bl_no.replace('-', '').replace(' ', '').lower() == processed_bl_no) # 提单
return bl_obj
def try_callback_track(self, max_retries=3, ship_package_ids=[]):
""" 封装的重试逻辑 """
for i in range(max_retries):
if not ship_package_ids:
is_ok = self.callback_track()
else:
is_ok = self.package_callback_func(ship_package_ids)
if is_ok:
return True
logging.warning(f"Attempt {i + 1}/{max_retries} failed. Retrying...")
return False
def mail_auto_push(self, mail_time=False, ship_packages=[], action_type='tally'):
self = self.with_context(dict(self._context, is_mail=True))
for item in self:
# try:
if mail_time:
utc_time = datetime.strptime(mail_time, "%Y-%m-%d %H:%M:%S")
before_min = self.env['ir.config_parameter'].sudo().get_param('before_min') or 10
before_utc_time = utc_time - timedelta(minutes=int(before_min))
item.push_clear_customs_start(before_utc_time)
# 尝试调用 callback_track
if self.try_callback_track():
item.push_clear_customs_end(utc_time)
# 再次尝试调用 callback_track
if not self.try_callback_track():
logging.error(f"Failed to push item after {3} attempts.")
else:
logging.error(f"Failed to start process for item after {3} attempts.")
elif ship_packages:
ship_package_ids = [ship_package_dict for sublist in [d['id'] for d in ship_packages] for
ship_package_dict in sublist]
tally_state = 'checked_goods' if action_type == 'tally' else 'handover_completed'
# 后续节点
node_obj = self.env['cc.node'].sudo().search([
('node_type', '=', 'package'),
('tally_state', '=', tally_state) # 检查理货或尾程交接的节点,根据排序进行升序
], order='seq asc')
if node_obj:
all_ship_package_obj = self.env['cc.ship.package'].search(
[('id', 'in', ship_package_ids)]) # 所有小包
# 预先获取所有同步日志 - 批量查询
all_sync_logs = self.env['cc.ship.package.sync.log'].sudo().search([
('package_id', 'in', ship_package_ids)
])
# 构建同步日志字典以加快查找
sync_log_dict = {}
for log in all_sync_logs:
if log.package_id.id not in sync_log_dict:
sync_log_dict[log.package_id.id] = set()
sync_log_dict[log.package_id.id].add(log.process_code)
# 构建ship_packages字典,用于快速查找
ship_packages_dict = {}
for package in ship_packages:
# 如果一个id在多个package中出现,使用最后一个package的tally_time
if package.get('tally_time'):
for single_id in package['id']:
ship_packages_dict[single_id] = package['tally_time']
# 前序节点 理货或尾程交接之前没有生成的节点
before_node_obj = self.env['cc.node'].sudo().search([
('node_type', '=', 'package'), ('is_must', '=', True), ('seq', '<', node_obj[0].seq)],
order='seq asc')
# 理货或尾程交接之前没有生成的节点
for before_node in before_node_obj:
print('before_node:%s' % before_node.name)
before_minutes = before_node.calculate_total_interval(node_obj[0])
# 准备批量更新数据
update_data = []
for package in all_ship_package_obj:
package_id = package.id
if package_id not in sync_log_dict or before_node.tk_code not in sync_log_dict.get(package_id, set()):
tally_time = ship_packages_dict.get(package_id)
if tally_time:
operation_time = (datetime.strptime(tally_time, '%Y-%m-%d %H:%M:%S') - timedelta(
minutes=before_minutes)) if tally_time else fields.Datetime.now() - timedelta(
minutes=before_minutes)
update_data.append((
package_id,
before_node.id,
operation_time,
before_node.desc,
True if before_node.is_default else False
))
if update_data:
# 构建批量更新SQL
values_str = ','.join(self.env.cr.mogrify("(%s,%s,%s,%s,%s)", row).decode('utf-8') for row in update_data)
sql = """
UPDATE cc_ship_package AS t SET
state = c.state,
process_time = c.process_time,
state_explain = c.state_explain,
is_sync = c.is_sync
FROM (VALUES
{}
) AS c(id, state, process_time, state_explain, is_sync)
WHERE t.id = c.id
""".format(values_str)
self.env.cr.execute(sql)
self._cr.commit() # 提交事务
# # 更新提单的未同步小包数量
# sql = """
# UPDATE cc_bl AS bl SET
# unsync_package_count = (
# SELECT COUNT(*)
# FROM cc_ship_package sp
# WHERE sp.bl_id = bl.id
# AND sp.is_sync = false
# )
# WHERE bl.id = %s
# """
# self.env.cr.execute(sql, (item.id,))
# self._cr.commit() # 提交事务
self.try_callback_track(max_retries=2, ship_package_ids=ship_package_ids)
# 理货或尾程交接的节点
# 预先获取所有状态节点
all_state_nodes = self.env['cc.node'].sudo().search([
('node_type', '=', 'package')
])
state_node_dict = {node.name: node for node in all_state_nodes}
next_minutes = int(self.env['ir.config_parameter'].sudo().get_param('next_minutes', default=20))
for index, node in enumerate(node_obj):
print('node:%s' % node.name)
update_data = []
for package in all_ship_package_obj:
if package.state.name in state_node_dict:
current_state_node = state_node_dict[package.state.name]
if current_state_node.seq < node.seq:
tally_time = ship_packages_dict.get(package.id)
if tally_time:
operation_time = (datetime.strptime(tally_time, '%Y-%m-%d %H:%M:%S') + timedelta(
minutes=next_minutes * index)) if tally_time else fields.Datetime.now() + timedelta(
minutes=next_minutes * index)
update_data.append((
package.id,
node.id,
operation_time,
node.desc,
True if node.is_default else False
))
print('update_data:%s' % update_data)
if update_data:
print('11111111111')
# 构建批量更新SQL
values_str = ','.join(self.env.cr.mogrify("(%s,%s,%s,%s,%s)", row).decode('utf-8') for row in update_data)
sql = """
UPDATE cc_ship_package AS t SET
state = c.state,
process_time = c.process_time,
state_explain = c.state_explain,
is_sync = c.is_sync
FROM (VALUES
{}
) AS c(id, state, process_time, state_explain, is_sync)
WHERE t.id = c.id
""".format(values_str)
self.env.cr.execute(sql)
self._cr.commit() # 提交事务
# # 更新提单的未同步小包数量
# sql = """UPDATE cc_bl AS bl SET unsync_package_count = ( SELECT COUNT(*) FROM cc_ship_package sp WHERE sp.bl_id = bl.id AND sp.is_sync = false) WHERE bl.id = %s """
# self.env.cr.execute(sql, (item.id,))
# self._cr.commit() # 提交事务
print('222222')
self.try_callback_track(max_retries=2, ship_package_ids=ship_package_ids)
print('33333')
return True
# except Exception as err:
# logging.error('fetch_mail_dlv--error:%s' % str(err))
def change_state_by_ship_package(self):
"""
根据小包的状态修改提单的状态
:return:
"""
# 如果提单有小包变成了清关开始,提单状态变为清关中
if self.state == 'draft' and self.ship_package_ids.filtered(
lambda line: line.state.tk_code == 'cb_imcustoms_start'):
self.ccing_func()
# 如果提单所有小包的清关节点变成"是完成节点",则该提单状态变成已完成
if all(line.state.is_done for line in self.ship_package_ids) and self.unsync_package_count <= 0:
self.done_func()
class CcBigPackage(models.Model):
# 模型名称
_inherit = 'cc.big.package'
# 模型描述
_description = 'Big Package'
def search_big_package_info(self, pda_lang=False, type='tally'):
"""
查询大包信息
"""
unprocessed_goods_msg_dic = {
'en': 'Unprocessed goods',
'zh': '未理货'
}
checked_goods_msg_dic = {
'en': 'Checked goods',
'zh': '已理货'
}
handover_completed_msg_dic = {
'en': 'Handover Completed',
'zh': '尾程交接'
}
state_arr = {'unprocessed_goods': unprocessed_goods_msg_dic[pda_lang],
'checked_goods': checked_goods_msg_dic[pda_lang],
'handover_completed': handover_completed_msg_dic[pda_lang]} # 未理货/已理货/尾程交接
# 根据下一阶段服务商名称获取尾程服务商的记录
provider_obj = self.env['cc.last.mile.provider'].match_provider(self.next_provider_name)
vals = {
'tally_state_label': state_arr[self.tally_state] or '', # 理货状态显示名称
'tally_state': self.tally_state or '', # 理货状态系统KEY
'tally_user_id': (self.tally_user_id.id or 0) if type == 'tally' else (self.delivery_user_id.id or 0),
# 理货人id/交货人id
'tally_user_name': (self.tally_user_id.name or '') if type == 'tally' else (
self.delivery_user_id.name or ''),
# 理货人名称/交货人名称
'tally_time': (self.tally_time or '') if type == 'tally' else (self.delivery_time or ''),
# self.env['common.common'].sudo().get_format_time(str(self.tally_time)) if self.tally_time else '',
# 理货时间/交货时间
'big_package_no': self.big_package_no or '', # 大包号
'next_service_provider_name': self.next_provider_name or '', # 下一个服务商名称
'next_service_provider_tape_color': (provider_obj.tape_color_value or '') if provider_obj else '',
# 下一个服务商胶带对应色值
'pallet_number': self.pallet_number or '', # 托盘号
'pallet_usage_time': self.pallet_usage_date or '' # 托盘使用时间
}
return vals
def update_big_package_info(self, **kwargs):
"""
理货 tally/尾程交接 handover
"""
action_type = kwargs.get('action_type')
for item in self:
if action_type == 'tally' and item.tally_state == 'unprocessed_goods':
# 更新理货信息
self._update_info(item, kwargs, 'tally')
elif action_type == 'handover' and item.tally_state != 'handover_completed':
# 更新交接信息
self._update_info(item, kwargs, 'handover')
def _update_info(self, item, kwargs, action_type):
"""
更新信息的通用方法
"""
if action_type == 'tally':
if kwargs.get('tally_state'):
item.tally_state = kwargs['tally_state']
if kwargs.get('tally_user_id'):
item.tally_user_id = kwargs['tally_user_id']
if kwargs.get('tally_time'):
item.tally_time = datetime.strptime(kwargs['tally_time'], '%Y-%m-%d %H:%M:%S')
elif action_type == 'handover':
if kwargs.get('tally_state'):
item.tally_state = kwargs['tally_state']
if kwargs.get('tally_user_id'):
item.delivery_user_id = kwargs['tally_user_id']
if kwargs.get('tally_time'):
item.delivery_time = datetime.strptime(kwargs['tally_time'], '%Y-%m-%d %H:%M:%S')
import asyncio
import logging
import ssl
from datetime import timedelta, datetime
import aiohttp
import certifi
import pytz
from odoo import models, fields, api, _
def get_rfc339_time(utc_time=None):
if not utc_time:
# 获取当前时间的UTC时间
utc_time = datetime.utcnow()
# 创建+8时区的对象
target_timezone = pytz.timezone('Asia/Shanghai')
# 将UTC时间转换为目标时区时间
local_time = utc_time.replace(tzinfo=pytz.utc).astimezone(target_timezone)
# 格式化为RFC 3339格式
rfc3339_time = local_time.isoformat(timespec='seconds')
return rfc3339_time
# 定义一个方法,将时间的时区转为UTC时间
def get_utc_time(local_time=None):
if not local_time:
# 获取当前时间的UTC时间
local_time = datetime.now()
# 将本地时间转换为UTC时间
utc_time = local_time.astimezone(pytz.utc)
# 格式化为RFC 3339格式
# rfc3339_time = utc_time.isoformat(timespec='seconds')
return utc_time.strftime('%Y-%m-%d %H:%M:%S')
# 继承cc.clearance.file对象,并重载action_upload方法
class CcClearanceFile(models.Model):
_inherit = "cc.clearance.file"
_description = "Clearance File" # 清关文件
def get_clearance_file_feedback_data(self):
"""通关文件上传数据组织"""
happend_time = get_rfc339_time()
push_data = {
"master_waybill_no": self.bl_id.bl_no or '',
"customs_waybill_id": self.bl_id.customs_bl_no or '',
"operate_time": happend_time,
"file_detail": {
"file_url": "",
"file_code": self.file.decode(), # 将文件内容转换为base64编码,
"file_type": "PDF"
}
}
return push_data
def clearance_file_feedback(self):
if not self.is_upload and self.file:
data = self.get_clearance_file_feedback_data()
tt_api_obj = self.env["ao.tt.api"].sudo()
response = tt_api_obj.clearance_file_feedback(data)
response_data = response.json()
if response_data['code'] != 0:
# 清关文件回传错误
self.is_upload = False
error_msg = response_data['msg']
request_id = response_data['requestID']
code = response_data['code']
self.env['ao.tt.api.log'].sudo().create_api_log(self.file_name or '', '清关文件回传:' + error_msg, data,
code,
request_id, source='推出')
return error_msg
else:
# 清关文件回传成功
self.is_upload = True
self.upload_time = datetime.now()
request_id = response_data['requestID']
self.env['ao.tt.api.log'].sudo().create_api_log(self.file_name or '', '', data, 0, request_id,
source='推出')
return ''
# 重载action_upload方法
def action_sync(self):
self.clearance_file_feedback()
return super(CcClearanceFile, self).action_sync()
# 定义小包同步纪录对象,用于记录小包同步的纪录,包括小包对象,同步时间,操作状态,操作时间,操行备注,同步操作人
class CcShipPackageSyncLog(models.Model):
_name = 'cc.ship.package.sync.log'
_description = 'CC Ship Package Sync Log'
# 定义模型字段
# 小包对象
package_id = fields.Many2one('cc.ship.package', 'Ship Package', required=True)
# 同步时间
sync_time = fields.Datetime('Sync Time', default=fields.Datetime.now)
# 增加接口客户
api_customer = fields.Char('Api Customer')
# 操作状态
process_code = fields.Char('TK Process Code')
# 操作时间
operate_time = fields.Datetime('Operate Time', default=fields.Datetime.now)
# 操作备注
operate_remark = fields.Text('Operate Remark')
# 同步操作人
operate_user = fields.Many2one('res.users', 'Operate User', default=lambda self: self.env.user)
# 添加一个新增日志的方法,传入小包ID,API客户,操作状态,操作备注,操作时间
@api.model
def create_sync_log(self, package_id, api_customer, process_code, operate_remark, operate_time):
vals = {
'package_id': package_id,
'api_customer': api_customer,
'process_code': process_code,
'operate_remark': operate_remark,
'operate_time': operate_time
}
if self._context.get('is_mail'):
public_user = self.env.ref('base.public_user')
vals['operate_user'] = public_user.id
return self.create(vals)
# 继承小包对象,并重载action_sync方法, 增加is_sync字段
class CcShipPackage(models.Model):
_inherit = "cc.ship.package"
is_sync = fields.Boolean('Is Sync', default=False, index=True)
tk_code = fields.Char(related='state.tk_code', store=True, string='TK Code', help='TK Code')
# 增加同步日志纪录字段
sync_log_ids = fields.One2many('cc.ship.package.sync.log', 'package_id', 'Sync Logs')
def is_next_code(self, next_state_id):
"""
判断更新的节点是否是 小包状态的下级节点
:param next_state_id:
:return:
"""
if self.state:
if next_state_id in self.state.next_code_ids.ids:
return True
return False
@api.model
def create(self, vals_list):
"""
第一个节点的时候 默认已同步
"""
obj = super(CcShipPackage, self).create(vals_list)
if obj.state.is_default:
obj.is_sync = True
return obj
def action_sync(self):
for record in self:
record.is_sync = True
self.env['cc.ship.package.sync.log'].sudo().create_sync_log(record.id, 'Tiktok', record.state.tk_code,
record.state_explain,
record.process_time.strftime(
'%Y-%m-%d %H:%M:%S'))
def get_callback_track_data(self):
"""小包上传数据."""
# 获取该提单下的所有同步状态为未同步的小包
push_data = {
"provider_order_id": self.logistic_order_no,
"track_list": [
{
"shipping_method_code": self.state.tk_code,
"operate_time": get_utc_time(self.process_time),
"time_zone": "UTC+0",
"action_code": self.state.tk_code,
"operation_desc": self.state.desc,
"reason_code": self.node_exception_reason_id.name or "" # 异常原因
}
]
}
# logging.info('小包轨迹 push_data:%s' % push_data)
return push_data
def callback_track(self, is_push=True):
if not self.is_sync and self.state and self.state.tk_code:
data = self.get_callback_track_data()
if is_push:
tt_api_obj = self.env["ao.tt.api"].sudo()
response = tt_api_obj.callback_track(data)
response_data = response.json()
if response_data['code'] != 0:
self.is_sync = False
self._cr.commit()
error_msg = response_data['msg']
request_id = response_data['requestID']
code = response_data['code']
self.env['ao.tt.api.log'].sudo().create_api_log(self.tracking_no or '',
'小包状态轨迹回传:' + error_msg,
data,
code,
request_id, source='推出')
return error_msg
else:
# 回传成功
self.is_sync = True
self.env['cc.ship.package.sync.log'].sudo().create_sync_log(self.id, 'Tiktok', self.state.tk_code,
self.state_explain,
self.process_time.strftime(
'%Y-%m-%d %H:%M:%S'))
self._cr.commit()
request_id = response_data['requestID']
self.env['ao.tt.api.log'].sudo().create_api_log(self.tracking_no or '', '', data, 0, request_id,
source='推出')
return ''
else:
self.is_sync = True
self.env['cc.ship.package.sync.log'].sudo().create_sync_log(self.id, 'Tiktok', self.state.tk_code,
self.state_explain,
self.process_time.strftime(
'%Y-%m-%d %H:%M:%S'))
self._cr.commit()
request_id = ''
self.env['ao.tt.api.log'].sudo().create_api_log(self.tracking_no or '', '', data, 0, request_id,
source='推出')
return ''
def search_ship_package_info(self, pda_lang=False):
"""
查询小包信息
:return:
"""
return {
'logistic_order_no': self.logistic_order_no, # 物流订单号
}
# 继承提单对象t
class CcBl(models.Model):
_inherit = 'cc.bl'
# 计算未同步小包数量
@api.depends('ship_package_ids', 'ship_package_ids.is_sync')
def _compute_unsync_package_count(self):
for record in self:
record_counts = record.ship_package_ids.filtered(lambda r: not r.is_sync)
if record_counts:
record.unsync_package_count = len(record_counts)
else:
record.unsync_package_count = 0
# 增加未同步小包数量字段
unsync_package_count = fields.Integer('Unsync Package Count', compute='_compute_unsync_package_count', store=True)
# 定义一个方法, 获取提单下的所有未同步的小包,并回传小包状态
def callback_track(self):
is_ok = True
for item in self:
ship_packages = self.env['cc.ship.package'].search([('bl_id', '=', item.id), ('is_sync', '=', False)])
is_ok = item.package_callback_func(ship_packages.ids)
return is_ok
def package_callback_func(self, ship_package_ids):
"""
同步小包状态
"""
ship_packages = self.env['cc.ship.package'].search([('id', 'in', ship_package_ids), ('is_sync', '=', False)])
logging.info('package_callback_func ship_packages:%s' % len(ship_packages))
is_ok = True
tt_api_obj = self.env["ao.tt.api"].sudo()
async def perform_requests():
ssl_context = ssl.create_default_context(cafile=certifi.where())
async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=ssl_context),
timeout=aiohttp.ClientTimeout(total=60)) as session:
tasks = []
for index, package in enumerate(ship_packages):
if not package.is_sync and package.state and package.state.tk_code:
data = package.get_callback_track_data()
tasks.append(tt_api_obj.async_callback_track(session, data, package.id))
responses = await asyncio.gather(*tasks)
return responses
# 在 Odoo 中运行异步任务
responses = asyncio.run(perform_requests())
for response_item in responses:
response_data = response_item[0]
logging.info('response_data:%s' % response_data)
data = response_item[1]
package_id = response_item[2]
package_order = self.env['cc.ship.package'].sudo().browse(package_id)
if response_data['code'] != 0:
package_order.is_sync = False
error_msg = response_data['msg']
request_id = response_data['requestID']
code = response_data['code']
self.env['ao.tt.api.log'].sudo().create_api_log(
package_order.tracking_no or '', '小包状态轨迹回传:' + error_msg, data, code, request_id,
source='推出')
is_ok = False
else:
# 回传成功
package_order.is_sync = True
self.env['cc.ship.package.sync.log'].sudo().create_sync_log(
package_order.id, 'Tiktok', package_order.state.tk_code, package_order.state_explain,
package_order.process_time.strftime('%Y-%m-%d %H:%M:%S'))
request_id = response_data['requestID']
self.env['ao.tt.api.log'].sudo().create_api_log(
package_order.tracking_no or '', '', data, 0, request_id, source='推出')
# 如果提单有小包变成了清关开始,提单状态变为清关中;如果提单所有小包的清关节点变成"是完成节点",则该提单状态变成已完成
self.change_state_by_ship_package()
return is_ok
def deal_ship_package_state(self):
for item in self:
ship_packages = self.env['cc.ship.package'].search([('bl_id', '=', item.id), ('is_sync', '=', False)])
for package in ship_packages:
package.callback_track(is_push=False)
return True
def batch_action_sync(self):
"""
批量回传通过文件
"""
for item in self:
for line in item.cc_attachment_ids:
line.action_sync()
# 创建显示包裹的action
def action_show_no_sync_ship_package(self):
# 返回一个action,显示包裹
return {
'name': _('Not Sync Ship Packages'),
'type': 'ir.actions.act_window',
'res_model': 'cc.ship.package',
'view_mode': 'tree,form',
'domain': [('bl_id', '=', self.id), ('is_sync', '=', False)],
}
def search_bl_info(self, pda_lang=False, type='tally'):
"""
查询提单信息
"""
vals = {
'bl_no': self.bl_no or '', # 提单号
'scan_big_package_qty': self.tally_big_package_qty + self.delivered_big_package_qty if type == 'tally' else self.delivered_big_package_qty,
# 已扫大包数量
'big_package_arr': [big_package_item.search_big_package_info(pda_lang=pda_lang, type=type) for
big_package_item in
self.big_package_ids],
# 大包信息
'ship_package_arr': [ship_package_item.search_ship_package_info(pda_lang=pda_lang) for ship_package_item in
self.ship_package_ids], # 小包信息
'pallet_arr': self.get_unique_pallet_info(), # 托盘信息
}
return vals
def get_unique_pallet_info(self):
"""获取唯一托盘信息,返回托盘号和最早的使用时间"""
pallet_info = {}
for package in self.big_package_ids:
pallet_number = package.pallet_number
pallet_usage_time = package.pallet_usage_date
if pallet_number and (pallet_number not in pallet_info or pallet_info[pallet_number] > pallet_usage_time):
pallet_info[pallet_number] = pallet_usage_time
return [{'pallet_number': k, 'pallet_usage_time': v} for k, v in pallet_info.items()]
def deal_bl_no(self, bl_no, state_arr=[]):
"""
处理提单号:去掉杠和空格,并转换为小写
:param bl_no:
:return:
"""
processed_bl_no = bl_no.replace('-', '').replace(' ', '').lower()
# 查询所有提单并处理它们的 bl_no
domain = [('state', 'in', state_arr)] if state_arr else []
all_bl_obj = self.env['cc.bl'].sudo().search(domain)
bl_obj = all_bl_obj.filtered(
lambda r: r.bl_no.replace('-', '').replace(' ', '').lower() == processed_bl_no) # 提单
return bl_obj
def try_callback_track(self, max_retries=3, ship_package_ids=[]):
""" 封装的重试逻辑 """
for i in range(max_retries):
if not ship_package_ids:
is_ok = self.callback_track()
else:
is_ok = self.package_callback_func(ship_package_ids)
if is_ok:
return True
logging.warning(f"Attempt {i + 1}/{max_retries} failed. Retrying...")
return False
def mail_auto_push(self, mail_time=False, ship_packages=[], action_type='tally'):
self = self.with_context(dict(self._context, is_mail=True))
for item in self:
# try:
if mail_time:
utc_time = datetime.strptime(mail_time, "%Y-%m-%d %H:%M:%S")
before_min = self.env['ir.config_parameter'].sudo().get_param('before_min') or 10
before_utc_time = utc_time - timedelta(minutes=int(before_min))
item.push_clear_customs_start(before_utc_time)
# 尝试调用 callback_track
if self.try_callback_track():
item.push_clear_customs_end(utc_time)
# 再次尝试调用 callback_track
if not self.try_callback_track():
logging.error(f"Failed to push item after {3} attempts.")
else:
logging.error(f"Failed to start process for item after {3} attempts.")
elif ship_packages:
ship_package_ids = [ship_package_dict for sublist in [d['id'] for d in ship_packages] for
ship_package_dict in sublist]
tally_state = 'checked_goods' if action_type == 'tally' else 'handover_completed'
# 后续节点
node_obj = self.env['cc.node'].sudo().search([
('node_type', '=', 'package'),
('tally_state', '=', tally_state) # 检查理货或尾程交接的节点,根据排序进行升序
], order='seq asc')
if node_obj:
all_ship_package_obj = self.env['cc.ship.package'].search(
[('id', 'in', ship_package_ids)]) # 所有小包
# 预先获取所有同步日志 - 批量查询
all_sync_logs = self.env['cc.ship.package.sync.log'].sudo().search([
('package_id', 'in', ship_package_ids)
])
# 构建同步日志字典以加快查找
sync_log_dict = {}
for log in all_sync_logs:
if log.package_id.id not in sync_log_dict:
sync_log_dict[log.package_id.id] = set()
sync_log_dict[log.package_id.id].add(log.process_code)
# 构建ship_packages字典,用于快速查找
ship_packages_dict = {}
for package in ship_packages:
# 如果一个id在多个package中出现,使用最后一个package的tally_time
if package.get('tally_time'):
for single_id in package['id']:
ship_packages_dict[single_id] = package['tally_time']
# 前序节点 理货或尾程交接之前没有生成的节点
before_node_obj = self.env['cc.node'].sudo().search([
('node_type', '=', 'package'), ('is_must', '=', True), ('seq', '<', node_obj[0].seq)],
order='seq asc')
# 理货或尾程交接之前没有生成的节点
for before_node in before_node_obj:
before_minutes = before_node.calculate_total_interval(node_obj[0])
for package in all_ship_package_obj:
package_id = package.id
if package_id not in sync_log_dict or before_node.tk_code not in sync_log_dict.get(
package_id, set()):
tally_time = ship_packages_dict.get(package_id)
if tally_time:
operation_time = (
datetime.strptime(tally_time, '%Y-%m-%d %H:%M:%S') - timedelta(
minutes=before_minutes)) if tally_time else fields.Datetime.now() - timedelta(
minutes=before_minutes)
package.write({
'state': before_node.id,
'process_time': operation_time,
'state_explain': before_node.desc,
'is_sync': True if before_node.is_default else False
})
self.try_callback_track(max_retries=2, ship_package_ids=ship_package_ids)
# 理货或尾程交接的节点
# 预先获取所有状态节点
all_state_nodes = self.env['cc.node'].sudo().search([
('node_type', '=', 'package')
])
state_node_dict = {node.name: node for node in all_state_nodes}
next_minutes = int(self.env['ir.config_parameter'].sudo().get_param('next_minutes', default=20))
for index, node in enumerate(node_obj):
for package in all_ship_package_obj:
if package.state.name in state_node_dict:
current_state_node = state_node_dict[package.state.name]
if current_state_node.seq < node.seq:
tally_time = ship_packages_dict.get(package.id)
if tally_time:
operation_time = (
datetime.strptime(tally_time, '%Y-%m-%d %H:%M:%S') + timedelta(
minutes=next_minutes * index)) if tally_time else fields.Datetime.now() + timedelta(
minutes=next_minutes * index)
package.write({
'state': node.id,
'process_time': operation_time,
'state_explain': node.desc,
'is_sync': True if node.is_default else False
})
self.try_callback_track(max_retries=2, ship_package_ids=ship_package_ids)
return True
# except Exception as err:
# logging.error('fetch_mail_dlv--error:%s' % str(err))
def change_state_by_ship_package(self):
"""
根据小包的状态修改提单的状态
:return:
"""
# 如果提单有小包变成了清关开始,提单状态变为清关中
if self.state == 'draft' and self.ship_package_ids.filtered(
lambda line: line.state.tk_code == 'cb_imcustoms_start'):
self.ccing_func()
# 如果提单所有小包的清关节点变成"是完成节点",则该提单状态变成已完成
if all(line.state.is_done for line in self.ship_package_ids) and self.unsync_package_count <= 0:
self.done_func()
class CcBigPackage(models.Model):
# 模型名称
_inherit = 'cc.big.package'
# 模型描述
_description = 'Big Package'
def search_big_package_info(self, pda_lang=False, type='tally'):
"""
查询大包信息
"""
unprocessed_goods_msg_dic = {
'en': 'Unprocessed goods',
'zh': '未理货'
}
checked_goods_msg_dic = {
'en': 'Checked goods',
'zh': '已理货'
}
handover_completed_msg_dic = {
'en': 'Handover Completed',
'zh': '尾程交接'
}
state_arr = {'unprocessed_goods': unprocessed_goods_msg_dic[pda_lang],
'checked_goods': checked_goods_msg_dic[pda_lang],
'handover_completed': handover_completed_msg_dic[pda_lang]} # 未理货/已理货/尾程交接
# 根据下一阶段服务商名称获取尾程服务商的记录
provider_obj = self.env['cc.last.mile.provider'].match_provider(self.next_provider_name)
vals = {
'tally_state_label': state_arr[self.tally_state] or '', # 理货状态显示名称
'tally_state': self.tally_state or '', # 理货状态系统KEY
'tally_user_id': (self.tally_user_id.id or 0) if type == 'tally' else (self.delivery_user_id.id or 0),
# 理货人id/交货人id
'tally_user_name': (self.tally_user_id.name or '') if type == 'tally' else (
self.delivery_user_id.name or ''),
# 理货人名称/交货人名称
'tally_time': (self.tally_time or '') if type == 'tally' else (self.delivery_time or ''),
# self.env['common.common'].sudo().get_format_time(str(self.tally_time)) if self.tally_time else '',
# 理货时间/交货时间
'big_package_no': self.big_package_no or '', # 大包号
'next_service_provider_name': self.next_provider_name or '', # 下一个服务商名称
'next_service_provider_tape_color': (provider_obj.tape_color_value or '') if provider_obj else '',
# 下一个服务商胶带对应色值
'pallet_number': self.pallet_number or '', # 托盘号
'pallet_usage_time': self.pallet_usage_date or '' # 托盘使用时间
}
return vals
def update_big_package_info(self, **kwargs):
"""
理货 tally/尾程交接 handover
"""
action_type = kwargs.get('action_type')
for item in self:
if action_type == 'tally' and item.tally_state == 'unprocessed_goods':
# 更新理货信息
self._update_info(item, kwargs, 'tally')
elif action_type == 'handover' and item.tally_state != 'handover_completed':
# 更新交接信息
self._update_info(item, kwargs, 'handover')
def _update_info(self, item, kwargs, action_type):
"""
更新信息的通用方法
"""
if action_type == 'tally':
if kwargs.get('tally_state'):
item.tally_state = kwargs['tally_state']
if kwargs.get('tally_user_id'):
item.tally_user_id = kwargs['tally_user_id']
if kwargs.get('tally_time'):
item.tally_time = datetime.strptime(kwargs['tally_time'], '%Y-%m-%d %H:%M:%S')
elif action_type == 'handover':
if kwargs.get('tally_state'):
item.tally_state = kwargs['tally_state']
if kwargs.get('tally_user_id'):
item.delivery_user_id = kwargs['tally_user_id']
if kwargs.get('tally_time'):
item.delivery_time = datetime.strptime(kwargs['tally_time'], '%Y-%m-%d %H:%M:%S')
import logging import logging
from datetime import datetime
from odoo import models, fields, api, tools from odoo import models, fields
# 继承节点对象.增加TK编码 # 继承节点对象.增加TK编码
...@@ -13,6 +12,60 @@ class CCNode(models.Model): ...@@ -13,6 +12,60 @@ class CCNode(models.Model):
interval_minutes = fields.Integer('Predecessor Node Interval (Minutes)', default=20, interval_minutes = fields.Integer('Predecessor Node Interval (Minutes)', default=20,
help='Default interval time between predecessor nodes in minutes.') # 前序节点间隔时间,默认20分钟 help='Default interval time between predecessor nodes in minutes.') # 前序节点间隔时间,默认20分钟
def get_before_node(self, node_type='package'):
"""
获取当前状态的前序节点
"""
# 前序节点 该节点之前没有生成的节点
before_node_obj = self.env['cc.node'].sudo().search([
('node_type', '=', node_type), ('is_must', '=', True), ('seq', '<', self.seq)],
order='seq asc')
return before_node_obj
def is_before_node(self, target_state):
"""
判断当前状态是否在目标状态之前
Args:
target_state: 目标状态代码
Returns:
bool: 如果当前状态在目标状态之前返回True,否则返回False
"""
if not target_state:
return False
# 获取所有状态的顺序映射
states_seq = dict(
self.search([]).sorted('seq').mapped(lambda r: (r.tk_code, r.seq))
)
# 获取当前状态的序号
current_code = self.tk_code
current_seq = states_seq.get(current_code)
# 获取目标状态的序号
target_code = target_state.tk_code if isinstance(target_state, models.Model) else target_state
target_seq = states_seq.get(target_code)
# 如果任一状态不存在,返回False
if current_seq is None or target_seq is None:
return False
# 比较序号判断先后顺序
return current_seq < target_seq
def is_next_code(self, current_state_obj, next_state_id):
"""
判断更新的节点是否是 小包状态的下级节点
:param next_state_id:
:return:
"""
if current_state_obj:
if next_state_id in current_state_obj.next_code_ids.ids:
return True
return False
def calculate_total_interval(self, next_node): def calculate_total_interval(self, next_node):
""" """
计算该节点到某个节点直接的间隔时间和 计算该节点到某个节点直接的间隔时间和
......
...@@ -44,14 +44,14 @@ class TT(models.Model): ...@@ -44,14 +44,14 @@ class TT(models.Model):
def callback_track(self, push_data): def callback_track(self, push_data):
"""包裹轨迹回传""" """包裹轨迹/提单状态回传"""
url = '/logistics/provider/cross_border/callback_track?country=GB' url = '/logistics/provider/cross_border/callback_track?country=GB'
# cb_excustoms_finished 出口清关完毕 # cb_excustoms_finished 出口清关完毕
# cb_transport_assigned 干线揽收 # cb_transport_assigned 干线揽收
timestamp = int(time.time()) timestamp = int(time.time())
sign = self.generate_sign(timestamp, push_data) sign = self.generate_sign(timestamp, push_data)
response = self.get_response(url, sign, timestamp, push_data) response = self.get_response(url, sign, timestamp, push_data)
logging.info('callback_track response:%s' % response) # logging.info('callback_track response:%s' % response)
return response return response
def package_invoice_query(self, push_data): def package_invoice_query(self, push_data):
...@@ -66,7 +66,7 @@ class TT(models.Model): ...@@ -66,7 +66,7 @@ class TT(models.Model):
def mwb_status_update(self, push_data): def mwb_status_update(self, push_data):
"""清关提单状态回传""" """清关提单状态回传"""
url = 'logistics/provider/customs/mwb_status_update' url = 'logistics/provider/customs/mwb_status_update?country=GB'
timestamp = int(time.time()) timestamp = int(time.time())
sign = self.generate_sign(timestamp, push_data) sign = self.generate_sign(timestamp, push_data)
response = self.get_response(url, sign, timestamp, push_data) response = self.get_response(url, sign, timestamp, push_data)
...@@ -126,20 +126,20 @@ class TT(models.Model): ...@@ -126,20 +126,20 @@ class TT(models.Model):
'app_key': app_key 'app_key': app_key
} }
request_url = tt_url + url request_url = tt_url + url
logging.info('request_url: %s' % request_url) # logging.info('request_url: %s' % request_url)
logging.info('request_data: %s' % parameter) # logging.info('request_data: %s' % parameter)
for i in range(3): # 尝试最多3次 for i in range(3): # 尝试最多3次
try: try:
async with session.post(request_url, headers=headers, data=parameter) as response: async with session.post(request_url, headers=headers, data=parameter) as response:
response_data = await response.json() response_data = await response.json()
logging.info('response: %s', response_data) # logging.info('response: %s', response_data)
# print(response.json()) # print(response.json())
return response_data return response_data
except Exception as e: except Exception as e:
if i < 2: # 如果不是最后一次尝试,等待后重试 if i < 2: # 如果不是最后一次尝试,等待后重试
await asyncio.sleep(2 ** i) # 指数退避策略 await asyncio.sleep(2 ** i) # 指数退避策略
else: else:
logging.warning('request error:%s' % str(e)) # logging.warning('request error:%s' % str(e))
return {'code': 500, 'requestID': 'request error timeout', 'msg': '超时,请重试'} # 如果重试次数用尽,抛出异常 return {'code': 500, 'requestID': 'request error timeout', 'msg': '超时,请重试'} # 如果重试次数用尽,抛出异常
async def async_callback_track_callback(self, session, push_data, package_id): async def async_callback_track_callback(self, session, push_data, package_id):
...@@ -150,10 +150,24 @@ class TT(models.Model): ...@@ -150,10 +150,24 @@ class TT(models.Model):
timestamp = int(time.time()) timestamp = int(time.time())
sign = self.generate_sign(timestamp, push_data) sign = self.generate_sign(timestamp, push_data)
response = await self.async_get_response(session, url, sign, timestamp, push_data) response = await self.async_get_response(session, url, sign, timestamp, push_data)
logging.info('callback_track response:%s' % response) # logging.info('callback_track response:%s' % response)
return response, push_data, package_id return response, push_data, package_id
async def async_callback_track(self, session, data, package_id): async def async_callback_track(self, session, data, package_id):
"""异步调用推送接口""" """异步调用推送接口"""
# async with semaphore: # async with semaphore:
return await self.async_callback_track_callback(session, data, package_id) return await self.async_callback_track_callback(session, data, package_id)
async def async_bl_callback_track_callback(self, session, push_data, package_id):
"""提单状态回传"""
url = '/logistics/provider/customs/mwb_status_update?country=GB'
timestamp = int(time.time())
sign = self.generate_sign(timestamp, push_data)
response = await self.async_get_response(session, url, sign, timestamp, push_data)
# logging.info('bl_callback_track response:%s' % response)
return response, push_data, package_id
async def async_bl_callback_track(self, session, data, bl_id):
"""异步调用提单推送接口"""
# async with semaphore:
return await self.async_bl_callback_track_callback(session, data, bl_id)
...@@ -7,3 +7,8 @@ access_cc_ship_package_sync_log_base.group_erp_manager,cc_ship_package_sync_log ...@@ -7,3 +7,8 @@ access_cc_ship_package_sync_log_base.group_erp_manager,cc_ship_package_sync_log
access_cc_ship_package_sync_log_ccs_base.group_clearance_of_customs_manager,cc_ship_package_sync_log ccs_base.group_clearance_of_customs_manager,ccs_connect_tiktok.model_cc_ship_package_sync_log,ccs_base.group_clearance_of_customs_manager,1,0,0,0 access_cc_ship_package_sync_log_ccs_base.group_clearance_of_customs_manager,cc_ship_package_sync_log ccs_base.group_clearance_of_customs_manager,ccs_connect_tiktok.model_cc_ship_package_sync_log,ccs_base.group_clearance_of_customs_manager,1,0,0,0
access_cc_ship_package_sync_log_ccs_base.group_clearance_of_customs_user,cc_ship_package_sync_log ccs_base.group_clearance_of_customs_user,ccs_connect_tiktok.model_cc_ship_package_sync_log,ccs_base.group_clearance_of_customs_user,1,0,0,0 access_cc_ship_package_sync_log_ccs_base.group_clearance_of_customs_user,cc_ship_package_sync_log ccs_base.group_clearance_of_customs_user,ccs_connect_tiktok.model_cc_ship_package_sync_log,ccs_base.group_clearance_of_customs_user,1,0,0,0
access_cc_bl_sync_log_base.group_user,cc_bl_sync_log base.group_user,ccs_connect_tiktok.model_cc_bl_sync_log,base.group_user,1,0,0,0
access_cc_bl_sync_log_base.group_erp_manager,cc_bl_sync_log base.group_erp_manager,ccs_connect_tiktok.model_cc_bl_sync_log,base.group_erp_manager,1,1,1,1
access_cc_bl_sync_log_ccs_base.group_clearance_of_customs_manager,cc_bl_sync_log ccs_base.group_clearance_of_customs_manager,ccs_connect_tiktok.model_cc_bl_sync_log,ccs_base.group_clearance_of_customs_manager,1,0,0,0
access_cc_bl_sync_log_ccs_base.group_clearance_of_customs_user,cc_bl_sync_log ccs_base.group_clearance_of_customs_user,ccs_connect_tiktok.model_cc_bl_sync_log,ccs_base.group_clearance_of_customs_user,1,0,0,0
<?xml version="1.0" encoding="utf-8"?>
<odoo>
# ---------- CC Bill Of Loading Sync Log ------------
<record model="ir.ui.view" id="tree_cc_bl_sync_log_view">
<field name="name">tree.cc.bl.sync.log</field>
<field name="model">cc.bl.sync.log</field>
<field name="arch" type="xml">
<tree string="CC Bill Of Loading Sync Log">
<field optional="hide" name="bl_id" string="Bill of Loading"/>
<field optional="show" name="api_customer" string="Api Customer"/>
<field optional="show" name="process_code" string="TK Process Code"/>
<field optional="show" name="progress_name" string="Progress Name"/>
<field optional="show" name="operate_time" string="Operate Time"/>
<field optional="show" name="operate_user" string="Operate User"/>
<field optional="show" name="sync_time" string="Sync Time"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="form_cc_bl_sync_log_view">
<field name="name">form.cc.bl.sync.log</field>
<field name="model">cc.bl.sync.log</field>
<field name="arch" type="xml">
<form string="CC Bill Of Loading Sync Log">
<sheet>
<group>
<group>
<field name="bl_id" string="Bill of Loading"/>
<field name="api_customer" string="Api Customer"/>
<field name="process_code" string="TK Process Code"/>
<field name="progress_name" string="Progress Name"/>
<field name="operate_time" string="Operate Time"/>
<field name="operate_user" string="Operate User"/>
<field name="sync_time" string="Sync Time"/>
</group>
<group>
</group>
</group>
<notebook>
<page string="Operate Remark">
<field name="operate_remark" string="Operate Remark"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="search_cc_bl_sync_log_view">
<field name="name">search.cc.bl.sync.log</field>
<field name="model">cc.bl.sync.log</field>
<field name="arch" type="xml">
<search string="CC Bill Of Loading Sync Log">
<field name="bl_id" string="Bill of Loading"/>
<field name="api_customer" string="Api Customer"/>
<field name="process_code" string="TK Process Code"/>
<field name="operate_time" string="Operate Time"/>
<field name="operate_user" string="Operate User"/>
<field name="sync_time" string="Sync Time"/>
<separator/>
<filter name="filter_operate_time" string="Operate Time" date="operate_time"/>
<filter name="filter_sync_time" string="Sync Time" date="sync_time"/>
<separator/>
<group expand="0" string="Group By">
<filter domain="[]" name="groupby_bl_id" string="Bill of Loading"
context="{'group_by': 'bl_id'}"/>
<filter domain="[]" name="groupby_operate_user" string="Operate User"
context="{'group_by': 'operate_user'}"/>
</group>
</search>
</field>
</record>
<record model="ir.actions.act_window" id="action_cc_bl_sync_log">
<field name="name">CC Bill Of Loading Sync Log</field>
<field name="res_model">cc.bl.sync.log</field>
<field name="view_mode">tree</field>
<field name="domain">[]</field>
<field name="context">{}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
[CC Bill Of Loading Sync Log] Not yet! Click the Create button in the top left corner and the sofa is yours!
</p>
<p>
</p>
</field>
</record>
</odoo>
\ No newline at end of file
...@@ -9,7 +9,11 @@ ...@@ -9,7 +9,11 @@
<field name="inherit_id" ref="ccs_base.tree_cc_bl_view"/> <field name="inherit_id" ref="ccs_base.tree_cc_bl_view"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="state" position="after"> <field name="state" position="after">
<field name="unsync_package_count" string="UnSync" widget="badge" decoration-danger="1 == 1"/> <field name="unsync_package_count" string="UnSync Package Count" widget="badge"
decoration-danger="1 == 1"/>
</field>
<field name="customs_clearance_status" position="after">
<field name="is_bl_sync" string="Is Bill Of Loading Sync"/>
</field> </field>
</field> </field>
</record> </record>
...@@ -20,12 +24,16 @@ ...@@ -20,12 +24,16 @@
<field name="model">cc.bl</field> <field name="model">cc.bl</field>
<field name="inherit_id" ref="ccs_base.form_cc_bl_view"/> <field name="inherit_id" ref="ccs_base.form_cc_bl_view"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<!-- # header之间增加一个按钮,调用自定义的方法--> <button name="%(ccs_base.action_batch_input_ship_package_wizard)d" position="after">
<header position="inside">
<button name="callback_track" string="Sync Package Status" type="object"/> <button name="callback_track" string="Sync Package Status" type="object"/>
</button>
<button name="%(ccs_base.action_batch_input_bl_status_wizard)d" position="before">
<button name="batch_action_sync" string="Sync CC Attachment" type="object"/> <button name="batch_action_sync" string="Sync CC Attachment" type="object"/>
</header> </button>
<button name="%(ccs_base.action_batch_input_bl_status_wizard)d" position="after">
<!--增加同步提单状态的按钮-->
<button name="callback_track_bl" string="Sync Bill Of Loading Status" type="object"/>
</button>
<button name="action_show_ship_package" position="replace"> <button name="action_show_ship_package" position="replace">
<button name="action_show_ship_package" type="object" <button name="action_show_ship_package" type="object"
...@@ -45,48 +53,13 @@ ...@@ -45,48 +53,13 @@
</div> </div>
</button> </button>
</button> </button>
</field>
</record>
<!-- # 继承ccs_base模块的cc_ship_package_view.xml视图,增加is_sync字段在列表中-->
<record model="ir.ui.view" id="tree_cc_ship_package_view_inherit">
<field name="name">tree_cc_ship_package_view_inherit</field>
<field name="model">cc.ship.package</field>
<field name="inherit_id" ref="ccs_base.tree_cc_ship_package_view"/>
<field name="arch" type="xml">
<field name="state" position="after">
<field name="is_sync"/>
</field>
<tree position="attributes">
<attribute name="decoration-danger">is_sync == False</attribute>
</tree>
</field>
</record>
<!-- # 继承ccs_base模块的search_cc_ship_package_view视图,装置加未同步的筛选条件-->
<record model="ir.ui.view" id="search_cc_ship_package_view_inherit">
<field name="name">search_cc_ship_package_view_inherit</field>
<field name="model">cc.ship.package</field>
<field name="inherit_id" ref="ccs_base.search_cc_ship_package_view"/>
<field name="arch" type="xml">
<search position="inside">
<filter string="Not Sync" name="filter_is_sync" domain="[('is_sync','=',False)]"/>
</search>
</field>
</record>
# 继承ccs_base模块的form_cc_ship_package_view视图,增加同步日志列表在notebook中
<record model="ir.ui.view" id="form_cc_ship_package_view_inherit">
<field name="name">form_cc_ship_package_view_inherit</field>
<field name="model">cc.ship.package</field>
<field name="inherit_id" ref="ccs_base.form_cc_ship_package_view"/>
<field name="arch" type="xml">
<notebook position="inside"> <notebook position="inside">
<page string="Sync Log"> <page string="Sync Log">
<field name="sync_log_ids" widget="one2many_list"/> <field name="bl_sync_log_ids" widget="one2many_list"/>
<group invisible="1">
<field name="is_bl_sync" string="Is Sync" readonly="1"/>
<field name="state_explain" string="State Explain"/>
</group>
</page> </page>
</notebook> </notebook>
</field> </field>
...@@ -104,6 +77,18 @@ ...@@ -104,6 +77,18 @@
</field> </field>
</record> </record>
<record id="action_batch_sync_bl_status" model="ir.actions.server">
<field name="name">Batch Sync Bill Of Loading Status</field>
<field name="model_id" ref="model_cc_bl"/>
<field name="binding_model_id" ref="model_cc_bl"/>
<field name="binding_view_types">list</field>
<field name="state">code</field>
<field name="code">
if records:
records.callback_track_bl()
</field>
</record>
<record id="action_batch_update_package_status" model="ir.actions.server"> <record id="action_batch_update_package_status" model="ir.actions.server">
<field name="name">批量手动处理小包异常数据</field> <field name="name">批量手动处理小包异常数据</field>
......
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<record model="ir.ui.view" id="tree_cc_ship_package_view">
<field name="name">tree.cc.ship.package</field> <!-- # 继承ccs_base模块的cc_ship_package_view.xml视图,增加is_sync字段在列表中-->
<record model="ir.ui.view" id="tree_cc_ship_package_view_inherit">
<field name="name">tree_cc_ship_package_view_inherit</field>
<field name="model">cc.ship.package</field> <field name="model">cc.ship.package</field>
<field name="inherit_id" ref="ccs_base.tree_cc_ship_package_view"/> <field name="inherit_id" ref="ccs_base.tree_cc_ship_package_view"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="state" position="after">
<field name="is_sync"/>
</field>
<field name="state" position="replace"> <field name="state" position="replace">
<field name="tk_code" invisible="1"/> <field name="tk_code" invisible="1"/>
<field optional="show" name="state" string="Progress" widget="badge" <field optional="show" name="state" string="Progress" widget="badge"
...@@ -13,7 +19,35 @@ ...@@ -13,7 +19,35 @@
decoration-warning="tk_code in ('cb_imcustoms_inspection','cb_imcustoms_exception')" decoration-warning="tk_code in ('cb_imcustoms_inspection','cb_imcustoms_exception')"
decoration-muted="tk_code=='cb_import_customs_failure'"/> decoration-muted="tk_code=='cb_import_customs_failure'"/>
</field> </field>
<tree position="attributes">
<attribute name="decoration-danger">is_sync == False</attribute>
</tree>
</field>
</record>
<!-- # 继承ccs_base模块的search_cc_ship_package_view视图,装置加未同步的筛选条件-->
<record model="ir.ui.view" id="search_cc_ship_package_view_inherit">
<field name="name">search_cc_ship_package_view_inherit</field>
<field name="model">cc.ship.package</field>
<field name="inherit_id" ref="ccs_base.search_cc_ship_package_view"/>
<field name="arch" type="xml">
<search position="inside">
<filter string="Not Sync" name="filter_is_sync" domain="[('is_sync','=',False)]"/>
</search>
</field>
</record>
# 继承ccs_base模块的form_cc_ship_package_view视图,增加同步日志列表在notebook中
<record model="ir.ui.view" id="form_cc_ship_package_view_inherit">
<field name="name">form_cc_ship_package_view_inherit</field>
<field name="model">cc.ship.package</field>
<field name="inherit_id" ref="ccs_base.form_cc_ship_package_view"/>
<field name="arch" type="xml">
<notebook position="inside">
<page string="Sync Log">
<field name="sync_log_ids" widget="one2many_list"/>
</page>
</notebook>
</field> </field>
</record> </record>
......
...@@ -2,4 +2,5 @@ ...@@ -2,4 +2,5 @@
from . import batch_input_ship_package_statu_wizard from . import batch_input_ship_package_statu_wizard
from . import update_bl_status_wizard
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from odoo import models, api, fields, _ from odoo import models, api, fields, _
from odoo.exceptions import Warning, ValidationError from odoo.exceptions import ValidationError
class BatchInputShipPackageStatusWizard(models.TransientModel): class BatchInputShipPackageStatusWizard(models.TransientModel):
...@@ -129,7 +128,7 @@ class BatchInputShipPackageStatusWizard(models.TransientModel): ...@@ -129,7 +128,7 @@ class BatchInputShipPackageStatusWizard(models.TransientModel):
if parcels: if parcels:
error_package_arr = [] error_package_arr = []
for package_item in parcels: for package_item in parcels:
result = package_item.is_next_code(self.update_status.id) result = self.env['cc.node'].is_next_code(package_item.state, self.update_status.id)
if not result: if not result:
error_package_arr.append(package_item.tracking_no) error_package_arr.append(package_item.tracking_no)
if len(error_package_arr) > 0: if len(error_package_arr) > 0:
...@@ -164,4 +163,5 @@ class BatchInputShipPackageStatusWizard(models.TransientModel): ...@@ -164,4 +163,5 @@ class BatchInputShipPackageStatusWizard(models.TransientModel):
self.update_status.name or '')) self.update_status.name or ''))
# 如果提单有小包变成了清关开始,提单状态变为清关中;如果提单所有小包的清关节点变成“是完成节点”,则该提单状态变成已完成 # 如果提单有小包变成了清关开始,提单状态变为清关中;如果提单所有小包的清关节点变成“是完成节点”,则该提单状态变成已完成
bl_obj.change_state_by_ship_package() bl_obj.change_state_by_ship_package()
bl_obj.change_customs_state_by_ship_package(self.update_status)#根据小包状态更新提单关务状态
return obj return obj
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import models, fields, _
from odoo.exceptions import ValidationError
# 定义一个批量更新提单状态的向导, 用于批量更新提单状态
# 包括以下字段,提单, 当前节点, 更新节点, 排除面单号, 排除状态
class UpdateBlStatusWizard(models.TransientModel):
_inherit = 'update.bl.status.wizard'
_description = 'Update the status of the bill of loading' # 更新提单状态向导
is_skip_check = fields.Boolean('Skip Check', default=False)
def submit(self):
bl_objs = self.get_order()
if not self.is_skip_check:
# 1检查操作时间不能大于当前时间,不能小于 最晚操作时间
current_time = fields.Datetime.now()
if self.process_time and (self.process_time > current_time or (
self.last_process_time and self.process_time < self.last_process_time)):
raise ValidationError(
_('The operation time cannot be greater than the current time and cannot be less than the latest operation time!')) # 操作时间不能大于当前时间且不能小于最晚操作时间
# 2检查更新节点是否是 提单的状态的下一节点
if bl_objs:
error_bl_arr = []
for bl in bl_objs:
result = self.env['cc.node'].is_next_code(bl.customs_clearance_status, self.update_status.id)
if not result:
error_bl_arr.append(bl.bl_no)
if len(error_bl_arr) > 0:
raise ValidationError(
_('[%s] The update node is not the next node in the state of the bill of loading!') % ','.join(
error_bl_arr)) # 更新节点不是提单的状态的下一节点!
# 3检查提单%s是否存在还未推送或 提单已经推送过将更变更的状态
bl_id_arr = self.get_exception_bl() # 异常的提单
if len(bl_id_arr) > 0:
raise ValidationError(
_('Check if the bill of loading [%s] has a status that has not been pushed yet or if the bill of loading has already been pushed and will be updated!') % '\n'.join(
[bl.bl_no for bl in
self.env['cc.bl'].search([('id', 'in', bl_id_arr)])])) # # 提单%s是否存在还未推送或 提单已经推送过将更变更的状态
obj = super(UpdateBlStatusWizard, self).submit()
for bl_obj in bl_objs:
bl_obj.message_post(body=_('[%s]%sUpdate to[%s]%s') % (
self.current_status.tk_code or '', self.current_status.name or '',
self.update_status.tk_code or '',
self.update_status.name or ''))
return obj
def get_exception_bl(self):
"""
比如 已提货 --- 清关开始,选择了清关开始之后 把已提货状态的提单且更新日志明细包含了 清关开始 的编码的 提单显示出来
已提货的判断是否推送过
"""
item = self
bl_ids = []
bl_objs = self.get_order()
if bl_objs and not item.is_skip_check:
where_sql = " and bl_id={0}".format(bl_objs[0].id) if len(
bl_objs) == 1 else " and bl_id in {0}".format(tuple(bl_objs.ids))
current_status = item.current_status
if current_status and not current_status.is_default:
# 更新日志里没有 当前节点(不包括默认节点) 【已提货】的提单
select_sql = "select id from cc_bl_sync_log where process_code='{0}' {1}".format(
current_status.tk_code, where_sql)
self._cr.execute(select_sql)
sync_log_obj = self._cr.fetchall()
if len(sync_log_obj) <= 0:
bl_ids += bl_objs.ids
if item.update_status:
# 更新日志明细包含了 更新节点 【清关开始】 的 提单
select_sql = "select bl_id from cc_bl_sync_log where process_code='{0}' {1}".format(
item.update_status.tk_code, where_sql)
self._cr.execute(select_sql)
bl_ids += [r[0] for r in self._cr.fetchall()]
return bl_ids
<?xml version="1.0" encoding="utf-8"?>
<!-- © <2016> <heyang>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo>
<data>
<record id="view_update_bl_status_wizard" model="ir.ui.view">
<field name="name">view_update_bl_status_wizard</field>
<field name="model">update.bl.status.wizard</field>
<field name="inherit_id" ref="ccs_base.view_update_bl_status_wizard"/>
<field name="arch" type="xml">
<field name="bl_id" position="after">
<field name="is_skip_check"/>
</field>
</field>
</record>
</data>
</odoo>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论