提交 5fb6d1ea authored 作者: 伍姿英's avatar 伍姿英

Merge branch 'release/V2.5.2'

...@@ -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',
......
差异被折叠。
...@@ -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 = \
sorted(self.filtered(lambda x: x.customs_clearance_status.id).mapped('process_time'), 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=''):
......
# 导入odoo # 导入odoo
import base64
# 导入日志 # 导入日志
import logging import logging
import xlrd
from odoo import models, fields, api, _ from odoo import models, fields
from odoo.exceptions import UserError
# 获取日志 # 获取日志
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
...@@ -48,3 +46,14 @@ class CcNode(models.Model): ...@@ -48,3 +46,14 @@ 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) # 对应小包状态
def node_is_sync(self):
# 如果更新节点是 默认节点 同步的标志变为True
is_sync = False
if self.is_default:
is_sync = True
return is_sync
...@@ -151,6 +151,10 @@ class FetchmailServer(models.Model): ...@@ -151,6 +151,10 @@ class FetchmailServer(models.Model):
elif email_body: elif email_body:
rule_obj = self.env['order.state.change.rule'].sudo() rule_obj = self.env['order.state.change.rule'].sudo()
rule_obj.fetch_mail_dlv(email_body=email_body, year=year) rule_obj.fetch_mail_dlv(email_body=email_body, year=year)
if 'POD' in subject.upper():
if attachment_arr:
rule_obj = self.env['order.state.change.rule'].sudo()
rule_obj.fetch_final_mail_dlv(attachment_arr=attachment_arr, email_body=email_body)
imap_server.store(num, '-FLAGS', '\\Seen') imap_server.store(num, '-FLAGS', '\\Seen')
try: try:
pass pass
......
...@@ -113,6 +113,43 @@ class OrderStateChangeRule(models.Model): ...@@ -113,6 +113,43 @@ class OrderStateChangeRule(models.Model):
file_obj.is_upload = False file_obj.is_upload = False
file_obj.action_sync() file_obj.action_sync()
def upload_pod_attachment(self, bl_obj, name, data):
"""尾程交接POD(待大包数量和箱号) 文件上传与同步"""
file_objs = self.env['cc.clearance.file'].sudo().search([('file_name', '=', '尾程交接POD(待大包数量和箱号)'),
('bl_id', 'in', bl_obj.ids)])
# 最大重试次数
max_retries = 2
for file_obj in file_objs:
retries = 0
while retries <= max_retries:
try:
# 设置文件内容
file_obj.file = base64.encodebytes(data)
file_obj.attachment_name = name
file_obj.is_upload = False # 确保文件状态是未上传
# 尝试上传操作
file_obj.action_sync()
# 检查是否上传成功,假设 is_upload 为 False 表示上传失败
if file_obj.is_upload is False:
retries += 1 # 上传失败,增加重试次数
if retries > max_retries:
_logger.info(f"上传文件 {file_obj.attachment_name} 失败,已尝试 {max_retries} 次,仍然失败。")
break # 超过最大重试次数后跳出循环
else:
_logger.info(f"上传文件 {file_obj.attachment_name} 失败,正在重新尝试第 {retries} 次...")
else:
# 如果上传成功,退出重试循环
_logger.info(f"文件 {file_obj.attachment_name} 上传成功")
break
except Exception as e:
# 捕获任何异常并重试
retries += 1
_logger.info(f"发生异常:{e},正在重新尝试第 {retries} 次上传文件 {file_obj.attachment_name}...")
if retries > max_retries:
_logger.info(f"上传文件 {file_obj.attachment_name} 失败,已尝试 {max_retries} 次,仍然失败。")
break # 超过最大重试次数后跳出循环
def fetch_mail_dlv_attachment(self, **kwargs): def fetch_mail_dlv_attachment(self, **kwargs):
attachment_arr = kwargs['attachment_arr'] attachment_arr = kwargs['attachment_arr']
for attachment_tuple in attachment_arr: for attachment_tuple in attachment_arr:
...@@ -144,6 +181,41 @@ class OrderStateChangeRule(models.Model): ...@@ -144,6 +181,41 @@ class OrderStateChangeRule(models.Model):
except Exception as err: except Exception as err:
logging.error('fetch_mail_dlv_attachment--error:%s' % str(err)) logging.error('fetch_mail_dlv_attachment--error:%s' % str(err))
def find_final_email_text(self, email_body):
"""匹配尾程pod邮件内容"""
pattern = re.compile("\\d{3}-\\d{8}\s*")
data_re = re.compile(pattern)
data_arr = data_re.findall(email_body)
data_arr = [i.replace('\r\n', '') for i in data_arr]
return data_arr
def fetch_final_mail_dlv(self, **kwargs):
"""尾程交接邮件提取"""
email_body = kwargs['email_body']
email_body = html.unescape(email_body)
text_arr = self.find_final_email_text(email_body)
logging.info('data_arr: %s' % text_arr)
attachment_arr = kwargs['attachment_arr']
attachment_tuple = attachment_arr[0] if attachment_arr else []
# order_obj_arr = []
try:
text_arr = [i.replace('-', '').replace(' ', '') for i in text_arr]
sql = "select id from cc_bl where UPPER(REPLACE(REPLACE(REPLACE(bl_no, ' ', ''), '-', ''), '/', '')) in %s"
self._cr.execute(sql, (tuple(text_arr),))
result = self._cr.fetchall()
ids = [i[0] for i in result]
bl_objs = self.env['cc.bl'].sudo().search([('id', 'in', ids)]) if result else False
if bl_objs and attachment_tuple:
attachment_name, attachment_data = attachment_tuple
self.upload_pod_attachment(bl_objs, attachment_name, attachment_data)
# redis_conn = self.env['common.common'].sudo().get_redis()
# if redis_conn == 'no':
# raise ValidationError('未连接redis')
# else:
# redis_conn.lpush('mail_push_package_list', json.dumps({'id': bl_obj.id, 'utc_time': utc_time.strftime("%Y-%m-%d %H:%M:%S")}))
except Exception as err:
logging.error('fetch_final_mail_dlv--error:%s' % str(err))
def fetch_mail_dlv(self, **kwargs): def fetch_mail_dlv(self, **kwargs):
email_body = kwargs['email_body'] email_body = kwargs['email_body']
year = kwargs['year'] year = kwargs['year']
......
...@@ -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" attrs="{'invisible': [('node_type', '=', 'package')]}" />
<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:
...@@ -102,13 +102,11 @@ class BatchInputShipPackageStatusWizard(models.TransientModel): ...@@ -102,13 +102,11 @@ class BatchInputShipPackageStatusWizard(models.TransientModel):
if reason_obj and not self.node_exception_reason_id: if reason_obj and not self.node_exception_reason_id:
raise ValidationError(_('Please select the reason for the exception!')) # 请选择异常原因 raise ValidationError(_('Please select the reason for the exception!')) # 请选择异常原因
# 如果更新节点是 默认节点 同步的标志变为True # 如果更新节点是 默认节点 同步的标志变为True
is_sync = False is_sync = self.update_status.node_is_sync()
if self.update_status.is_default:
is_sync = True
# 更新状态 # 更新状态
parcels.write( parcels.write(
{'state': self.update_status.id, 'node_exception_reason_id': self.node_exception_reason_id.id, {'state': self.update_status.id, 'node_exception_reason_id': self.node_exception_reason_id.id,
'process_time': self.process_time, 'state_explain': self.state_explain, 'is_sync': is_sync}) 'process_time': self.process_time, 'state_explain': self.state_explain, 'is_sync': is_sync})
# if parcels: # if parcels:
# where_sql = " where id={0}".format(parcels[0].id) if len( # where_sql = " where id={0}".format(parcels[0].id) if len(
# parcels) == 1 else " where id in {0}".format(tuple(parcels.ids)) # parcels) == 1 else " where id in {0}".format(tuple(parcels.ids))
......
# -*- 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):
_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')
next_code_ids = fields.Many2many('cc.node', 'bl_node_next_node_wizard_rel', 'node_id', 'next_node_id', 'Next Node',
related='current_status.next_code_ids')
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 = self.update_status.node_is_sync()
# 更新状态
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="[('id','in',next_code_ids)]"/>
<field name="process_time" required="1" string="Customs Clearance Status Process Time"/>
<field name="state_explain" invisible="1"/>
<field name="is_batch" invisible="1"/>
<field name="next_code_ids" invisible="1" widget="many2many_tags"/>
</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',
......
import logging import logging
from datetime import datetime
from odoo import models, fields, api, tools from odoo import models, fields
# 继承节点对象.增加TK编码 # 继承节点对象.增加TK编码
...@@ -13,13 +12,67 @@ class CCNode(models.Model): ...@@ -13,13 +12,67 @@ 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([('node_type', '=', target_state.node_type)]).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):
""" """
计算该节点到某个节点直接的间隔时间和 计算该节点到某个节点直接的间隔时间和
""" """
total_interval = 0 total_interval = 0
predecessor_nodes = self.env['cc.node'].search( predecessor_nodes = self.env['cc.node'].search(
[('node_type', '=', 'package'), ('is_must', '=', True), ('seq', '<=', next_node.seq), [('node_type', '=', next_node.node_type), ('is_must', '=', True), ('seq', '<=', next_node.seq),
('seq', '>', self.seq)]) ('seq', '>', self.seq)])
for node in predecessor_nodes: for node in predecessor_nodes:
total_interval += node.interval_minutes total_interval += node.interval_minutes
......
...@@ -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>
...@@ -100,7 +73,19 @@ ...@@ -100,7 +73,19 @@
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
records.callback_track() records.callback_track()
</field>
</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> </field>
</record> </record>
...@@ -110,11 +95,11 @@ ...@@ -110,11 +95,11 @@
<field name="model_id" ref="model_cc_bl"/> <field name="model_id" ref="model_cc_bl"/>
<field name="binding_model_id" ref="model_cc_bl"/> <field name="binding_model_id" ref="model_cc_bl"/>
<field name="binding_view_types">list</field> <field name="binding_view_types">list</field>
<field name="groups_id" eval="[(4, ref('base.group_system'))]"/> <field name="groups_id" eval="[(4, ref('base.group_system'))]"/>
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
if records: if records:
records.deal_ship_package_state() records.deal_ship_package_state()
</field> </field>
</record> </record>
</data> </data>
......
<?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:
...@@ -163,5 +162,6 @@ class BatchInputShipPackageStatusWizard(models.TransientModel): ...@@ -163,5 +162,6 @@ class BatchInputShipPackageStatusWizard(models.TransientModel):
self.current_status.tk_code or '', self.current_status.name or '', self.update_status.tk_code or '', self.current_status.tk_code or '', self.current_status.name or '', self.update_status.tk_code or '',
self.update_status.name or '')) self.update_status.name or ''))
# 如果提单有小包变成了清关开始,提单状态变为清关中;如果提单所有小包的清关节点变成“是完成节点”,则该提单状态变成已完成 # 如果提单有小包变成了清关开始,提单状态变为清关中;如果提单所有小包的清关节点变成“是完成节点”,则该提单状态变成已完成
bl_obj.change_state_by_ship_package() bl_obj.change_customs_state_by_ship_package(self.update_status)#根据小包状态更新提单关务状态
# bl_obj.change_state_by_ship_package()
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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论