Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
H
hh_ccs
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
贺阳
hh_ccs
Commits
883f16f6
提交
883f16f6
authored
11月 28, 2024
作者:
伍姿英
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'release/V2.3.0'
上级
dfc222d1
57e046b3
隐藏空白字符变更
内嵌
并排
正在显示
17 个修改的文件
包含
721 行增加
和
8 行删除
+721
-8
__manifest__.py
ccs_base/__manifest__.py
+1
-0
__init__.py
ccs_base/models/__init__.py
+3
-0
cc_bill_loading.py
ccs_base/models/cc_bill_loading.py
+67
-1
common_common.py
ccs_base/models/common_common.py
+9
-2
fetch_mail.py
ccs_base/models/fetch_mail.py
+193
-0
mail_thread.py
ccs_base/models/mail_thread.py
+31
-0
order_state_change_rule.py
ccs_base/models/order_state_change_rule.py
+126
-0
redis_connection.py
ccs_base/models/redis_connection.py
+52
-0
res_config_setting.py
ccs_base/models/res_config_setting.py
+33
-0
ir.model.access.csv
ccs_base/security/ir.model.access.csv
+2
-0
res_config_setting.xml
ccs_base/views/res_config_setting.xml
+29
-0
cc_bill_loading.py
ccs_connect_tiktok/models/cc_bill_loading.py
+9
-2
batch_input_ship_package_statu_wizard.py
...ct_tiktok/wizard/batch_input_ship_package_statu_wizard.py
+11
-3
config.py
consumers/config.py
+48
-0
mail_push.py
consumers/mail_push.py
+57
-0
mail_push.conf
consumers/supervisord_conf/conf.d/mail_push.conf
+16
-0
supervisord.conf
consumers/supervisord_conf/supervisord.conf
+34
-0
没有找到文件。
ccs_base/__manifest__.py
浏览文件 @
883f16f6
...
...
@@ -29,6 +29,7 @@
'views/cc_big_package_view.xml'
,
'views/cc_node_exception_reason_view.xml'
,
'views/cc_bl_view.xml'
,
'views/res_config_setting.xml'
,
# 'views/cc_customers_declaration_order_view.xml',
'templates/login.xml'
,
],
...
...
ccs_base/models/__init__.py
浏览文件 @
883f16f6
...
...
@@ -6,3 +6,6 @@ from . import cc_node_exception_reason
from
.
import
mail_thread
from
.
import
common_common
from
.
import
fetch_mail
from
.
import
order_state_change_rule
from
.
import
res_config_setting
ccs_base/models/cc_bill_loading.py
浏览文件 @
883f16f6
...
...
@@ -2,7 +2,7 @@
import
ast
import
base64
import
re
from
datetime
import
timedelta
from
datetime
import
timedelta
,
datetime
from
pygtrans
import
Translate
import
xlrd
import
pytz
...
...
@@ -898,6 +898,72 @@ class CcBL(models.Model):
# 定义清关国家,关联到国家字段
cc_country_id
=
fields
.
Many2one
(
'res.country'
,
string
=
'CC Country'
)
def
push_clear_customs_start
(
self
,
utc_time
):
# 创建向导
push_node_obj
=
self
.
env
[
'cc.node'
]
.
sudo
()
.
search
([(
'node_type'
,
'='
,
'package'
),
(
'tk_code'
,
'='
,
'cb_imcustoms_start'
)],
limit
=
1
)
node_obj
=
self
.
env
[
'cc.node'
]
.
sudo
()
.
search
([(
'node_type'
,
'='
,
'package'
),
(
'seq'
,
'<'
,
push_node_obj
.
seq
)],
order
=
'seq desc'
,
limit
=
1
)
vals
=
{
'bl_id'
:
self
.
id
,
'bl_count'
:
1
,
'current_status'
:
node_obj
.
id
,
'update_status'
:
push_node_obj
.
id
,
'process_time'
:
utc_time
,
'is_ok'
:
True
}
wizard_obj
=
self
.
env
[
'batch.input.ship.package.status.wizard'
]
.
sudo
()
.
create
(
vals
)
wizard_obj
.
change_ship_package_ids
()
wizard_obj
=
wizard_obj
.
with_context
(
dict
(
self
.
_context
,
active_id
=
self
.
id
))
wizard_obj
.
submit
()
def
push_clear_customs_end
(
self
,
utc_time
):
# 创建向导
push_node_obj
=
self
.
env
[
'cc.node'
]
.
sudo
()
.
search
([(
'node_type'
,
'='
,
'package'
),
(
'tk_code'
,
'='
,
'cb_imcustoms_finished'
)],
limit
=
1
)
node_obj
=
self
.
env
[
'cc.node'
]
.
sudo
()
.
search
([(
'node_type'
,
'='
,
'package'
),
(
'seq'
,
'<'
,
push_node_obj
.
seq
)],
order
=
'seq desc'
,
limit
=
1
)
vals
=
{
'bl_id'
:
self
.
id
,
'bl_count'
:
1
,
'current_status'
:
node_obj
.
id
,
'update_status'
:
push_node_obj
.
id
,
'process_time'
:
utc_time
,
'is_ok'
:
True
}
wizard_obj
=
self
.
env
[
'batch.input.ship.package.status.wizard'
]
.
sudo
()
.
create
(
vals
)
wizard_obj
.
change_ship_package_ids
()
wizard_obj
=
wizard_obj
.
with_context
(
dict
(
self
.
_context
,
active_id
=
self
.
id
))
# print(wizard_obj.get_order())
wizard_obj
.
submit
()
def
try_callback_track
(
self
,
max_retries
=
3
):
""" 封装的重试逻辑 """
for
i
in
range
(
max_retries
):
is_ok
=
self
.
callback_track
()
if
is_ok
:
return
True
logging
.
warning
(
f
"Attempt {i + 1}/{max_retries} failed. Retrying..."
)
return
False
def
mail_auto_push
(
self
,
mail_time
):
self
=
self
.
with_context
(
dict
(
self
.
_context
,
is_mail
=
True
))
for
item
in
self
:
try
:
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."
)
except
Exception
as
err
:
logging
.
error
(
'fetch_mail_dlv--error:
%
s'
%
str
(
err
))
# 增加一个清关进度的业务对象,继承自models.Model, 用于管理业务数据.业务数据包括提单号、清关节点(业务对象)、进度日期、进度描述、更新人
class
CcProgress
(
models
.
Model
):
...
...
ccs_base/models/common_common.py
浏览文件 @
883f16f6
...
...
@@ -3,9 +3,9 @@ import datetime
import
re
from
odoo
import
fields
,
models
,
exceptions
,
api
,
tools
import
logging
from
.redis_connection
import
redis_connection
__author__
=
'zd'
r
=
redis_connection
()
_logger
=
logging
.
getLogger
(
__name__
)
...
...
@@ -22,3 +22,10 @@ class CommonCommon(models.Model):
d
=
dt
+
datetime
.
timedelta
(
hours
=
8
)
nTime
=
d
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
)
return
nTime
def
get_redis
(
self
):
"""
连接redis
:return:
"""
return
r
ccs_base/models/fetch_mail.py
0 → 100644
浏览文件 @
883f16f6
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import
logging
import
poplib
import
time
from
ssl
import
SSLError
from
socket
import
gaierror
,
timeout
from
imaplib
import
IMAP4
,
IMAP4_SSL
from
poplib
import
POP3
,
POP3_SSL
import
email
from
odoo
import
api
,
fields
,
models
,
tools
,
_
from
odoo.exceptions
import
UserError
from
datetime
import
datetime
,
timedelta
from
email.utils
import
parseaddr
import
pytz
_logger
=
logging
.
getLogger
(
__name__
)
MAX_POP_MESSAGES
=
50
MAIL_TIMEOUT
=
60
# Workaround for Python 2.7.8 bug https://bugs.python.org/issue23906
poplib
.
_MAXLINE
=
65536
month_dict
=
{
'Jan'
:
'1'
,
'Feb'
:
'2'
,
'Mar'
:
'3'
,
'Apr'
:
'4'
,
'May'
:
'5'
,
'Jun'
:
'6'
,
'Jul'
:
'7'
,
'Aug'
:
'8'
,
'Sep'
:
'9'
,
'Oct'
:
'10'
,
'Nov'
:
'11'
,
'Dec'
:
'12'
,
'1'
:
'Jan'
,
'2'
:
'Feb'
,
'3'
:
'Mar'
,
'4'
:
'Apr'
,
'5'
:
'May'
,
'6'
:
'Jun'
,
'7'
:
'Jul'
,
'8'
:
'Aug'
,
'9'
:
'Sep'
,
'10'
:
'Oct'
,
'11'
:
'Nov'
,
'12'
:
'Dec'
,
}
class
FetchmailServer
(
models
.
Model
):
"""Incoming POP/IMAP mail server account"""
_inherit
=
'fetchmail.server'
_description
=
'Incoming Mail Server'
_order
=
'priority'
def
get_location_time
(
self
):
"""
获取当前时区时间(带时区)
:return:
"""
now_time
=
datetime
.
utcnow
()
tz
=
self
.
env
.
user
.
tz
or
'Asia/Shanghai'
return
now_time
.
replace
(
tzinfo
=
pytz
.
timezone
(
tz
))
def
fetch_mail
(
self
):
""" WARNING: meant for cron usage only - will commit() after each email! """
additionnal_context
=
{
'fetchmail_cron_running'
:
True
}
MailThread
=
self
.
env
[
'mail.thread'
]
for
server
in
self
:
_logger
.
info
(
'start checking for new emails on
%
s server
%
s'
,
server
.
server_type
,
server
.
name
)
additionnal_context
[
'default_fetchmail_server_id'
]
=
server
.
id
count
,
failed
=
0
,
0
imap_server
=
None
pop_server
=
None
if
server
.
server_type
==
'imap'
:
try
:
imap_server
=
server
.
connect
()
imap_server
.
select
()
# 匹配时间
mail_send_timezone
=
self
.
env
[
'ir.config_parameter'
]
.
sudo
()
.
get_param
(
'mail_send_timezone'
)
or
'+1'
mail_match_minute
=
self
.
env
[
'ir.config_parameter'
]
.
sudo
()
.
get_param
(
'mail_match_minute'
)
or
180
utc_now
=
self
.
get_location_time
()
if
mail_send_timezone
[
0
]
==
'+'
:
now
=
utc_now
+
timedelta
(
hours
=
int
(
mail_send_timezone
[
1
:]))
else
:
now
=
utc_now
-
timedelta
(
hours
=
int
(
mail_send_timezone
[
1
:]))
before_now
=
(
now
-
timedelta
(
minutes
=
int
(
mail_match_minute
)))
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
)
# 今天 mail_before_day查询多少天以前的
today
=
datetime
.
now
()
mail_before_day
=
int
(
self
.
env
[
'ir.config_parameter'
]
.
sudo
()
.
get_param
(
'mail_before_day'
)
or
0
)
offset
=
timedelta
(
days
=-
mail_before_day
)
last_day
=
(
today
+
offset
)
.
strftime
(
'
%
d-
%
b-
%
Y'
)
_logger
.
info
(
'last_day:
%
s,before_now:
%
s'
%
(
last_day
,
before_now
))
# imap_server._mode_utf8()
# result, data = imap_server.search(None, 'SINCE', last_day)
result
,
data
=
imap_server
.
search
(
None
,
'(UNSEEN)'
,
last_day
)
# result, data = imap_server.search(None, '(UNSEEN)')
_logger
.
info
(
'mail_data:
%
s'
%
data
[
0
])
for
num
in
data
[
0
]
.
split
():
result
,
data
=
imap_server
.
fetch
(
num
,
'(RFC822)'
)
msg
=
email
.
message_from_string
(
data
[
0
][
1
]
.
decode
(
'utf-8'
,
'ignore'
))
subject
=
msg
.
get
(
'subject'
)
# 标题
date
=
email
.
header
.
decode_header
(
msg
.
get
(
'date'
))
# 发件时间
time_list
=
date
[
0
][
0
]
.
split
(
','
)[
1
]
.
split
(
' '
)
# date里面的空格可能是一个两个
time_list
=
list
(
filter
(
None
,
time_list
))
year
=
time_list
[
2
]
# 发件时间的年份
# 取8分钟之内接收的邮件
b_time
=
'
%
s
%
s
%
s
%
s'
%
(
time_list
[
0
],
time_list
[
1
],
time_list
[
2
],
time_list
[
3
])
_logger
.
info
(
'--b_time:
%
s--'
%
b_time
)
try
:
a_time
=
time
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
,
time
.
strptime
(
b_time
,
"
%
d
%
b
%
Y
%
H:
%
M:
%
S"
))
except
Exception
as
e
:
b_time
=
'
%
s-
%
s-
%
s
%
s'
%
(
time_list
[
2
],
month_dict
[
time_list
[
1
]],
time_list
[
0
],
time_list
[
3
])
a_time
=
time
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
,
time
.
strptime
(
b_time
,
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
))
# print(a_time, before_now)
if
a_time
>
before_now
:
hdr
,
sender_email
=
parseaddr
(
msg
.
get
(
'From'
))
# 获取发件人邮箱
email_body
=
''
for
part
in
msg
.
walk
():
# 如果ture的话内容是没用的
if
not
part
.
is_multipart
():
# 解码出内容
# email_body = part.get_payload(decode=True).decode('utf-8', 'ignore')
email_body
=
part
.
get_payload
(
decode
=
True
)
.
decode
(
'gbk'
,
'ignore'
)
break
if
email_body
:
if
'CDS REPORT'
in
subject
.
upper
():
rule_obj
=
self
.
env
[
'order.state.change.rule'
]
.
sudo
()
rule_obj
.
fetch_mail_dlv
(
email_body
=
email_body
,
year
=
year
)
imap_server
.
store
(
num
,
'-FLAGS'
,
'
\\
Seen'
)
try
:
pass
# res_id = MailThread.with_context(**additionnal_context).message_process(server.object_id.model, data[0][1], save_original=server.original, strip_attachments=(not server.attach))
except
Exception
:
_logger
.
info
(
'Failed to process mail from
%
s server
%
s.'
,
server
.
server_type
,
server
.
name
,
exc_info
=
True
)
failed
+=
1
imap_server
.
store
(
num
,
'+FLAGS'
,
'
\\
Seen'
)
self
.
_cr
.
commit
()
count
+=
1
_logger
.
info
(
"Fetched
%
d email(s) on
%
s server
%
s;
%
d succeeded,
%
d failed."
,
count
,
server
.
server_type
,
server
.
name
,
(
count
-
failed
),
failed
)
except
Exception
as
e
:
_logger
.
info
(
"General failure when trying to fetch mail from
%
s server
%
s.error:
%
s"
,
server
.
server_type
,
server
.
name
,
e
,
exc_info
=
True
)
finally
:
if
imap_server
:
try
:
imap_server
.
close
()
imap_server
.
logout
()
except
OSError
:
_logger
.
warning
(
'Failed to properly finish imap connection:
%
s.'
,
server
.
name
,
exc_info
=
True
)
elif
server
.
server_type
==
'pop'
:
try
:
while
True
:
pop_server
=
server
.
connect
()
(
num_messages
,
total_size
)
=
pop_server
.
stat
()
pop_server
.
list
()
for
num
in
range
(
1
,
min
(
MAX_POP_MESSAGES
,
num_messages
)
+
1
):
(
header
,
messages
,
octets
)
=
pop_server
.
retr
(
num
)
message
=
(
b
'
\n
'
)
.
join
(
messages
)
res_id
=
None
try
:
res_id
=
MailThread
.
with_context
(
**
additionnal_context
)
.
message_process
(
server
.
object_id
.
model
,
message
,
save_original
=
server
.
original
,
strip_attachments
=
(
not
server
.
attach
))
pop_server
.
dele
(
num
)
except
Exception
:
_logger
.
info
(
'Failed to process mail from
%
s server
%
s.'
,
server
.
server_type
,
server
.
name
,
exc_info
=
True
)
failed
+=
1
self
.
env
.
cr
.
commit
()
if
num_messages
<
MAX_POP_MESSAGES
:
break
pop_server
.
quit
()
_logger
.
info
(
"Fetched
%
d email(s) on
%
s server
%
s;
%
d succeeded,
%
d failed."
,
num_messages
,
server
.
server_type
,
server
.
name
,
(
num_messages
-
failed
),
failed
)
except
Exception
:
_logger
.
info
(
"General failure when trying to fetch mail from
%
s server
%
s."
,
server
.
server_type
,
server
.
name
,
exc_info
=
True
)
finally
:
if
pop_server
:
pop_server
.
quit
()
server
.
write
({
'date'
:
fields
.
Datetime
.
now
()})
return
True
ccs_base/models/mail_thread.py
浏览文件 @
883f16f6
...
...
@@ -101,3 +101,33 @@ class MailThread(models.AbstractModel):
# raise exceptions.UserError(_("Unable to send message, please configure the sender's email address."))
return
author_id
,
email_from
@api.model
def
message_new
(
self
,
msg_dict
,
custom_values
=
None
):
"""Called by ``message_process`` when a new message is received
for a given thread model, if the message did not belong to
an existing thread.
The default behavior is to create a new record of the corresponding
model (based on some very basic info extracted from the message).
Additional behavior may be implemented by overriding this method.
:param dict msg_dict: a map containing the email details and
attachments. See ``message_process`` and
``mail.message.parse`` for details.
:param dict custom_values: optional dictionary of additional
field values to pass to create()
when creating the new thread record.
Be careful, these values may override
any other values coming from the message.
:rtype: int
:return: the id of the newly created thread object
"""
data
=
{}
if
isinstance
(
custom_values
,
dict
):
data
=
custom_values
.
copy
()
fields
=
self
.
fields_get
()
name_field
=
self
.
_rec_name
or
'name'
if
name_field
in
fields
and
not
data
.
get
(
'name'
):
data
[
name_field
]
=
msg_dict
.
get
(
'subject'
,
''
)
# return self.create(data)
return
True
\ No newline at end of file
ccs_base/models/order_state_change_rule.py
0 → 100644
浏览文件 @
883f16f6
# -*- coding: utf-8 -*-
# © <2016> <ToproERP hy>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from
datetime
import
datetime
from
odoo
import
models
,
fields
,
api
,
_
from
odoo.exceptions
import
ValidationError
,
Warning
import
logging
import
json
import
re
import
demjson
from
datetime
import
datetime
,
timedelta
import
re
_logger
=
logging
.
getLogger
(
__name__
)
import
html
class
OrderStateChangeRule
(
models
.
Model
):
_name
=
"order.state.change.rule"
_inherit
=
[
'mail.thread'
]
_description
=
'提单状态变更规则'
# @api.constrains('email_subject', 'change_order_state')
# def check_change_order_state(self):
# if self.email_subject == 'DLV':
# if self.change_order_state != '已提货':
# raise ValidationError('邮件主题包含字段为DLV时,变更后提单状态必须是 已提货')
#
# name = fields.Char('变更规则名称', index=True, tracking=True)
# email_address = fields.Char('发件人邮箱', tracking=True)
# email_subject = fields.Selection([('DLV', 'DLV'), ('FFM', 'FFM'), ('RCF', 'RCF')], string='邮件主题包含字段', tracking=True)
# change_subject = fields.Selection([('提单', '提单'), ('配舱单', '配舱单')], string='变更主体', default='提单', tracking=True)
# order_state = fields.Selection(
# [('待提交', '待提交'), ('待审核', '待审核'), ('已订舱', '已订舱'), ('已发出', '已发出'), ('已收货', '已收货'), ('已安检', '已安检'), ('已入库', '已入库'),
# ('已组板', '已组板'), ('已发货', '已发货'),
# ('已落地', '已落地'), ('已卸机', '已卸机'), ('已提货', '已提货'), ('已完成', '已完成'), ('已取消', '已取消')], string='提单所属状态', index=True, tracking=True)
# change_order_state = fields.Selection(
# [('待提交', '待提交'), ('待审核', '待审核'), ('已订舱', '已订舱'), ('已发出', '已发出'), ('已收货', '已收货'), ('已安检', '已安检'), ('已入库', '已入库'),
# ('已组板', '已组板'), ('已发货', '已发货'),
# ('已落地', '已落地'), ('已提货', '已提货'), ('已完成', '已完成'), ('已取消', '已取消')], string='变更后提单状态', default='已提货', index=True,
# tracking=True)
# method = fields.Char('方法')
# regular_expression = fields.Char('正则表达式')
# active = fields.Boolean('有效性', default=True)
# 函数处理输出
def
print_match
(
self
,
text
):
# 正则表达式
pattern
=
r'([A-Za-z]+\d+)\s+(\d{3}-\d+)\s+(\d{4})?\.?(\d{1,2})\.(\d{1,2})\s+(\d{2}:\d{2})\s+\(([\+\-]?\d+)\)'
match
=
re
.
match
(
pattern
,
text
)
if
match
:
return
match
else
:
return
False
def
find_text
(
self
,
email_body
):
pattern
=
r'([A-Za-z]+\d+)\s+(\d{3}-\d+)\s+(\d{4})?[\.\。]?(\d{1,2})[\.\。](\d{1,2})\s+(\d{1,2}[::]\d{2})?\s*[\(\(]([\+\-]?\d+)[\)\)]'
data_re
=
re
.
compile
(
pattern
)
data_arr
=
data_re
.
findall
(
email_body
)
return
data_arr
def
fetch_mail_dlv
(
self
,
**
kwargs
):
email_body
=
kwargs
[
'email_body'
]
year
=
kwargs
[
'year'
]
# datas = demjson.decode(kwargs['datas'])
current_year
=
datetime
.
now
()
.
year
email_body
=
html
.
unescape
(
email_body
)
text_arr
=
self
.
find_text
(
email_body
)
# text_arr = email_body.split('\r\n') if '\r\n' in email_body else email_body.split('\n')
logging
.
info
(
'data_arr:
%
s'
%
text_arr
)
for
text
in
text_arr
:
try
:
# text = html.unescape(text)
# match = self.print_match(text)
if
text
:
voyage_name
=
text
[
0
]
# SE901
order_no
=
text
[
1
]
# 436-10133970
year
=
text
[
2
]
# 11 或 2024
month
=
text
[
3
]
# 11
day
=
text
[
4
]
# 20
time
=
text
[
5
]
.
replace
(
':'
,
':'
)
# 12:41
timezone_offset
=
int
(
text
[
6
])
# +1 或 -8
# voyage_name = match.group(1) # SE901
# order_no = match.group(2) # 436-10133970
# year = match.group(3) # 11 或 2024
# month = match.group(4) # 11
# day = match.group(5) # 20
# time = match.group(6) # 12:41
# timezone_offset = int(match.group(7)) # +1 或 -8
# 如果没有提供年份,则使用当前年份
if
not
year
:
# 只给了月份和日,默认使用当前年份
year
=
str
(
current_year
)
# 拼接日期时间字符串
date_str
=
f
"{year}-{month}-{day} {time}"
# 转换为 datetime 对象
local_time
=
datetime
.
strptime
(
date_str
,
"
%
Y-
%
m-
%
d
%
H:
%
M"
)
# 调整时区
utc_time
=
local_time
-
timedelta
(
hours
=
timezone_offset
)
sql
=
"select id from cc_bl where UPPER(REPLACE(REPLACE(REPLACE(bl_no, ' ', ''), '-', ''), '/', '')) = '{0}' "
\
"and transport_tool_name='{1}' order by create_date desc limit 1"
.
format
(
order_no
.
replace
(
' '
,
''
)
.
replace
(
'-'
,
''
)
.
replace
(
'/'
,
''
),
voyage_name
)
self
.
_cr
.
execute
(
sql
)
result
=
self
.
_cr
.
fetchall
()
print
(
result
)
# bl_obj = self.env['cc.bl'].sudo().search([('bl_no', '=', order_no), ('transport_tool_name', '=', voyage_name)], order='create_date desc', limit=1)
bl_obj
=
self
.
env
[
'cc.bl'
]
.
sudo
()
.
search
([(
'id'
,
'='
,
result
[
0
][
0
])])
if
result
else
False
if
bl_obj
and
bl_obj
.
state
!=
'done'
:
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_mail_dlv--error:
%
s'
%
str
(
err
))
def
calc_date
(
self
,
pick_date_text
):
"""
获取月份
:param pick_date_text:
:return:
"""
month_abbr_arr
=
[
''
,
'Jan'
,
'Feb'
,
'Mar'
,
'Apr'
,
'May'
,
'Jun'
,
'Jul'
,
'Aug'
,
'Sep'
,
'Oct'
,
'Nov'
,
'Dec'
]
for
mon
in
month_abbr_arr
:
if
mon
!=
''
:
month
=
mon
.
upper
()
if
month
in
pick_date_text
:
return
int
(
month_abbr_arr
.
index
(
mon
))
return
0
ccs_base/models/redis_connection.py
0 → 100644
浏览文件 @
883f16f6
# -*- coding: utf-8 -*-
from
odoo.tools
import
config
import
redis
import
logging
import
psycopg2
_logger
=
logging
.
getLogger
(
__name__
)
def
redis_connection
():
# 连接redis
redis_config
=
config
.
misc
.
get
(
"redis"
,
{})
if
redis_config
:
try
:
redis_options
=
dict
(
host
=
redis_config
.
get
(
'redis_host'
),
port
=
redis_config
.
get
(
'redis_port'
),
password
=
redis_config
.
get
(
'password'
),
decode_responses
=
True
,
db
=
redis_config
.
get
(
'db'
),
)
pool
=
redis
.
ConnectionPool
(
**
redis_options
)
r
=
redis
.
Redis
(
connection_pool
=
pool
)
return
r
except
Exception
as
e
:
_logger
.
error
(
u'连接redis失败,原因:
%
s'
%
str
(
e
))
return
'no'
else
:
_logger
.
error
(
u'conf文件中未配置redis连接信息'
)
return
'no'
# def yhj_psql_connection():
# # 连接yhj数据库
# yhj_config = config.misc.get("yhj", {})
# if yhj_config:
# try:
# db_ip = yhj_config.get('db_ip')
# db_port = yhj_config.get('db_port')
# database = yhj_config.get('db_name')
# password = yhj_config.get('db_password')
# username = yhj_config.get('db_user')
# psql_connection = psycopg2.connect(user=username, password=password, dbname=database, host=db_ip,
# port=db_port)
# # psql_cur = psql_connection.cursor()
# return psql_connection
# except Exception as e:
# _logger.error(u'连接yhj数据库失败,原因:%s' % str(e))
# return 'no'
# else:
# _logger.error(u'conf文件中未配置yhj数据库连接信息')
# return 'no'
ccs_base/models/res_config_setting.py
0 → 100644
浏览文件 @
883f16f6
# -*- coding: utf-8 -*-
# Part of SmartGo. See LICENSE file for full copyright and licensing details.
import
logging
from
odoo
import
api
,
fields
,
models
,
_
_logger
=
logging
.
getLogger
(
__name__
)
class
ResConfigSettings
(
models
.
TransientModel
):
_inherit
=
'res.config.settings'
before_min
=
fields
.
Integer
(
'清关时间取值(早于清关结束)'
)
@api.model
def
get_values
(
self
):
"""
重载获取参数的方法,参数都存在系统参数中
:return:
"""
values
=
super
(
ResConfigSettings
,
self
)
.
get_values
()
config
=
self
.
env
[
'ir.config_parameter'
]
.
sudo
()
before_min
=
config
.
get_param
(
'before_min'
,
default
=
10
)
values
.
update
(
before_min
=
before_min
,
)
return
values
def
set_values
(
self
):
super
(
ResConfigSettings
,
self
)
.
set_values
()
ir_config
=
self
.
env
[
'ir.config_parameter'
]
.
sudo
()
ir_config
.
set_param
(
"before_min"
,
self
.
before_min
or
10
)
ccs_base/security/ir.model.access.csv
浏览文件 @
883f16f6
...
...
@@ -56,3 +56,5 @@ access_cc_clearance_file_base.group_user,cc_clearance_file base.group_user,ccs_b
access_cc_clearance_file_base.group_erp_manager,cc_clearance_file base.group_erp_manager,ccs_base.model_cc_clearance_file,base.group_erp_manager,1,1,1,1
access_cc_clearance_file_ccs_base.group_clearance_of_customs_manager,cc_clearance_file ccs_base.group_clearance_of_customs_manager,ccs_base.model_cc_clearance_file,ccs_base.group_clearance_of_customs_manager,1,1,1,1
access_cc_clearance_file_ccs_base.group_clearance_of_customs_user,cc_clearance_file ccs_base.group_clearance_of_customs_user,ccs_base.model_cc_clearance_file,ccs_base.group_clearance_of_customs_user,1,0,0,0
order_state_change_rule_group_user,order_state_change_rule_group_user,ccs_base.model_order_state_change_rule,base.group_user,1,1,1,1
ccs_base/views/res_config_setting.xml
0 → 100644
浏览文件 @
883f16f6
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record
id=
"res_config_settings_view_form_auto_push"
model=
"ir.ui.view"
>
<field
name=
"name"
>
res.config.settings.view.form.inherit.auto.push
</field>
<field
name=
"model"
>
res.config.settings
</field>
<field
name=
"inherit_id"
ref=
"base_setup.res_config_settings_view_form"
/>
<field
name=
"arch"
type=
"xml"
>
<xpath
expr=
"//div[hasclass('app_settings_block')]/div"
position=
"before"
>
<div>
<h2>
自动推送配置
</h2>
<div
class=
"row mt16 o_settings_container"
id=
"auto_push"
>
<div
class=
"col-12 col-lg-6 o_setting_box"
>
<div
class=
"o_setting_left_pane"
/>
<div
class=
"o_setting_right_pane"
>
<div
class=
"text-muted"
>
<label
for=
"before_min"
/>
<field
name=
"before_min"
/>
</div>
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
</data>
</odoo>
ccs_connect_tiktok/models/cc_bill_loading.py
浏览文件 @
883f16f6
...
...
@@ -115,13 +115,17 @@ class CcShipPackageSyncLog(models.Model):
# 添加一个新增日志的方法,传入小包ID,API客户,操作状态,操作备注,操作时间
@api.model
def
create_sync_log
(
self
,
package_id
,
api_customer
,
process_code
,
operate_remark
,
operate_time
):
return
self
.
create
(
{
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字段
...
...
@@ -247,6 +251,7 @@ class CcBl(models.Model):
# 定义一个方法, 获取提单下的所有未同步的小包,并回传小包状态
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
)])
# for package in ship_packages:
...
...
@@ -279,6 +284,7 @@ class CcBl(models.Model):
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
...
...
@@ -288,6 +294,7 @@ class CcBl(models.Model):
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
=
'推出'
)
return
is_ok
def
deal_ship_package_state
(
self
):
for
item
in
self
:
...
...
ccs_connect_tiktok/wizard/batch_input_ship_package_statu_wizard.py
浏览文件 @
883f16f6
...
...
@@ -150,10 +150,18 @@ class BatchInputShipPackageStatusWizard(models.TransientModel):
# if self.bl_id.state == 'draft' and self.bl_id.ship_package_ids.filtered(
# lambda line: line.state.tk_code == 'cb_imcustoms_start'):
# self.bl_id.ccing_func()
public_user
=
self
.
env
.
ref
(
'base.public_user'
)
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
''
))
# print(self._context)
if
self
.
_context
.
get
(
'is_mail'
):
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
''
),
author_id
=
public_user
.
id
,
email_from
=
public_user
.
display_name
)
else
:
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
''
))
# 如果提单有小包变成了清关开始,提单状态变为清关中
if
bl_obj
.
state
==
'draft'
and
bl_obj
.
ship_package_ids
.
filtered
(
lambda
line
:
line
.
state
.
tk_code
==
'cb_imcustoms_start'
):
...
...
consumers/config.py
0 → 100644
浏览文件 @
883f16f6
# coding=utf-8
# 本地
db_ip
=
"127.0.0.1"
db_port
=
"8888"
db_name
=
"hh_ccs_test"
db_user
=
"admin"
db_password
=
"admin"
redis_options
=
dict
(
host
=
'127.0.0.1'
,
port
=
6379
,
# password='topodoo1314',
decode_responses
=
True
,
db
=
0
)
# 测试
# db_ip = "121.199.167.133"
# db_port = "8369"
# db_name = "airorder0309"
# db_user = "admin"
# db_password = "123123"
#
# redis_options = dict(
# host='172.18.0.6',
# port=6379,
# # password='top123',
# decode_responses=True,
# db=3
# )
# product
# db_ip = "172.18.183.214"
# db_port = "9169"
# db_name = "air_order"
# db_user = "admin"
# db_password = "YHB1408ups"
#
# redis_options = dict(
# host='172.18.183.214',
# port=32768,
# password='top123',
# decode_responses=True,
# db=3
# )
consumers/mail_push.py
0 → 100644
浏览文件 @
883f16f6
# coding=utf-8
import
json
import
logging
import
redis
import
time
import
requests
import
odoorpc
from
requests.adapters
import
HTTPAdapter
from
datetime
import
datetime
import
config
# 默认字符gbk
# logging.basicConfig(filename='./push_data_logger.log', level=logging.INFO)
# 设置文件字符为utf-8
logging
.
basicConfig
(
handlers
=
[
logging
.
FileHandler
(
'logs/mail_push.log'
,
'a'
,
'utf-8'
)],
format
=
'
%(asctime)
s
%(levelname)
s
%(message)
s'
,
level
=
logging
.
INFO
)
class
Order_dispose
(
object
):
def
__init__
(
self
):
# rpc连接
self
.
odoo_db
=
odoorpc
.
ODOO
(
config
.
db_ip
,
port
=
config
.
db_port
)
self
.
odoo_db
.
login
(
config
.
db_name
,
config
.
db_user
,
config
.
db_password
)
def
order_data
(
self
,
data
):
res_data
=
[]
try
:
data
=
json
.
loads
(
data
)
logging
.
info
(
'mail_push_data:
%
s'
,
data
)
bl_obj
=
self
.
odoo_db
.
env
[
'cc.bl'
]
bl_record
=
bl_obj
.
browse
(
data
[
'id'
])
# utc_time = datetime.strptime(data['utc_time'], "%Y-%m-%d %H:%M:%S")
utc_time
=
data
[
'utc_time'
]
bl_record
.
mail_auto_push
(
utc_time
)
except
Exception
as
ex
:
logging
.
error
(
'mail_auto_push error:
%
s'
%
str
(
ex
))
return
res_data
try
:
pool
=
redis
.
ConnectionPool
(
**
config
.
redis_options
)
r
=
redis
.
Redis
(
connection_pool
=
pool
)
logging
.
info
(
u'redis连接成功'
)
Order_dispose
=
Order_dispose
()
while
1
:
try
:
result
=
r
.
brpop
(
'mail_push_package_list'
,
0
)
data1
=
result
[
1
]
response_data
=
Order_dispose
.
order_data
(
data1
)
except
Exception
as
e
:
logging
.
error
(
e
)
continue
except
Exception
as
e
:
logging
.
error
(
"登录失败"
)
logging
.
error
(
e
)
consumers/supervisord_conf/conf.d/mail_push.conf
0 → 100644
浏览文件 @
883f16f6
[
program
:
mail_push_consumer_1
]
process_name
=%(
program_name
)
s_
%(
process_num
)
02
d
; 进程名称
directory
= /
mnt
/
extra
-
addons
; 程序的启动目录
command
= /
usr
/
bin
/
python3
/
mnt
/
extra
-
addons
/
mail_push
.
py
; 启动命令
autostart
=
true
; 在
supervisord
启动的时候也自动启动
startsecs
=
5
; 启动
5
秒后没有异常退出,就当作已经正常启动了
autorestart
=
true
; 程序异常退出后自动重启
startretries
=
3
; 启动失败自动重试次数,默认是
3
user
=
root
; 用哪个用户启动
numprocs
=
1
; 进程数
redirect_stderr
=
true
; 把
stderr
重定向到
stdout
,默认
false
stdout_logfile_maxbytes
=
20
MB
;
stdout
日志文件大小,默认
50
MB
stdout_logfile_backups
=
20
;
stdout
日志文件备份数
;
stdout
日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(
supervisord
会自动创建日志文件)
stdout_logfile
= /
var
/
log
/
supervisor
/
mail_push
.
log
consumers/supervisord_conf/supervisord.conf
0 → 100644
浏览文件 @
883f16f6
;
supervisor
config
file
[
unix_http_server
]
file
=/
var
/
run
/
supervisor
.
sock
; (
the
path
to
the
socket
file
)
chmod
=
0700
;
sockef
file
mode
(
default
0700
)
[
supervisord
]
nodaemon
=
true
logfile
=/
var
/
log
/
supervisor
/
supervisord
.
log
; (
main
log
file
;
default
$
CWD
/
supervisord
.
log
)
pidfile
=/
var
/
run
/
supervisord
.
pid
; (
supervisord
pidfile
;
default
supervisord
.
pid
)
childlogdir
=/
var
/
log
/
supervisor
; (
'AUTO'
child
log
dir
,
default
$
TEMP
)
;
the
below
section
must
remain
in
the
config
file
for
RPC
; (
supervisorctl
/
web
interface
)
to
work
,
additional
interfaces
may
be
;
added
by
defining
them
in
separate
rpcinterface
:
sections
[
rpcinterface
:
supervisor
]
supervisor
.
rpcinterface_factory
=
supervisor
.
rpcinterface
:
make_main_rpcinterface
[
supervisorctl
]
serverurl
=
unix
:///
var
/
run
/
supervisor
.
sock
;
use
a
unix
://
URL
for
a
unix
socket
;
The
[
include
]
section
can
just
contain
the
"files"
setting
.
This
;
setting
can
list
multiple
files
(
separated
by
whitespace
or
;
newlines
).
It
can
also
contain
wildcards
.
The
filenames
are
;
interpreted
as
relative
to
this
file
.
Included
files
*
cannot
*
;
include
files
themselves
.
[
inet_http_server
]
port
=
9001
username
=
admin
password
=
admin
[
include
]
files
= /
mnt
/
extra
-
addons
/
supervisord_conf
/
conf
.
d
/*.
conf
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论