Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
H
hh_ccs
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
贺阳
hh_ccs
Commits
50774994
提交
50774994
authored
4月 15, 2026
作者:
刘擎阳
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
1.接收起飞落地信息
上级
2b48cba7
显示空白字符变更
内嵌
并排
正在显示
6 个修改的文件
包含
194 行增加
和
3 行删除
+194
-3
__init__.py
ccs_connect_tiktok/controllers/__init__.py
+1
-0
main.py
ccs_connect_tiktok/controllers/main.py
+77
-0
cc_bill_loading.py
ccs_connect_tiktok/models/cc_bill_loading.py
+84
-1
warn_config.py
ccs_connect_tiktok/models/warn_config.py
+4
-2
ir.model.access.csv
ccs_connect_tiktok/security/ir.model.access.csv
+6
-0
cc_bl_view.xml
ccs_connect_tiktok/views/cc_bl_view.xml
+22
-0
没有找到文件。
ccs_connect_tiktok/controllers/__init__.py
浏览文件 @
50774994
...
...
@@ -3,3 +3,4 @@ from . import tt_controllers
from
.
import
order_controller
from
.
import
binary
from
.
import
main
ccs_connect_tiktok/controllers/main.py
0 → 100644
浏览文件 @
50774994
import
json
import
logging
from
odoo
import
http
,
fields
from
odoo.http
import
request
_logger
=
logging
.
getLogger
(
__name__
)
class
CmcIntegrationController
(
http
.
Controller
):
# 1. 核心修复:将 type='json' 改为 type='http'
@http.route
(
'/api/cmc/push'
,
type
=
'http'
,
auth
=
'public'
,
methods
=
[
'POST'
],
csrf
=
False
,
cors
=
'*'
)
def
receive_tk_push
(
self
,
**
post
):
"""
接收创美程推送的接口
"""
# 2. 核心修复:从 httprequest.data 读取原始字节,并用 json.loads 解析
try
:
raw_data
=
request
.
httprequest
.
data
if
not
raw_data
:
return
self
.
_json_response
({
'status'
:
'error'
,
'message'
:
'Empty body'
})
data
=
json
.
loads
(
raw_data
)
_logger
.
info
(
"收到创美程推送数据:
%
s"
,
data
)
except
json
.
JSONDecodeError
:
_logger
.
error
(
"收到的数据不是合法的 JSON 格式"
)
return
self
.
_json_response
({
'status'
:
'error'
,
'message'
:
'Invalid JSON format'
})
# 3. 基础数据校验
tracking_number
=
data
.
get
(
'tracking_no'
)
event_type
=
data
.
get
(
'event'
)
event_time_str
=
data
.
get
(
'event_time'
)
if
not
tracking_number
or
not
event_type
:
return
self
.
_json_response
({
'status'
:
'error'
,
'message'
:
'Missing parameters'
})
public_user
=
request
.
env
.
ref
(
'base.public_user'
)
bill
=
request
.
env
[
'cc.bl'
]
.
sudo
()
.
search
([(
'bl_no'
,
'='
,
tracking_number
)],
limit
=
1
)
if
not
bill
:
return
self
.
_json_response
({
'status'
:
'error'
,
'message'
:
'Bill not found'
})
# 4. 执行业务逻辑更新
try
:
event_time
=
fields
.
Datetime
.
to_datetime
(
event_time_str
)
dynamic_type
=
'departure'
if
event_type
==
'DEPARTURE'
else
'arrival'
bill
.
with_user
(
public_user
)
.
sudo
()
.
write
({
'flight_dynamic_ids'
:
[(
0
,
0
,
{
'type'
:
dynamic_type
,
'event_time_utc'
:
event_time
,
'timezone'
:
'UTC'
,
'status'
:
'valid'
,
})]
})
if
dynamic_type
==
'departure'
:
bill
.
with_user
(
public_user
)
.
sudo
()
.
actual_departure_time
=
event_time
else
:
bill
.
with_user
(
public_user
)
.
sudo
()
.
actual_arrival_time
=
event_time
# 5. 核心修复:使用自建的方法返回标准的 JSON 响应
return
self
.
_json_response
({
'status'
:
'success'
,
'message'
:
'Record updated'
})
except
Exception
as
e
:
_logger
.
error
(
"处理推送数据失败:
%
s"
,
str
(
e
))
return
self
.
_json_response
({
'status'
:
'error'
,
'message'
:
str
(
e
)})
def
_json_response
(
self
,
result_dict
):
"""
辅助方法:将字典转换为 type='http' 接口所需的 HTTP Response 对象
"""
return
request
.
make_response
(
json
.
dumps
(
result_dict
),
headers
=
[(
'Content-Type'
,
'application/json'
)]
)
\ No newline at end of file
ccs_connect_tiktok/models/cc_bill_loading.py
浏览文件 @
50774994
...
...
@@ -10,7 +10,7 @@ import aiohttp
import
certifi
import
pytz
from
odoo
import
models
,
fields
,
api
,
_
from
odoo.exceptions
import
ValidationError
from
odoo.exceptions
import
ValidationError
,
UserError
def
get_rfc339_time
(
utc_time
=
None
):
...
...
@@ -1493,6 +1493,89 @@ class CcBl(models.Model):
except
Exception
as
err
:
logging
.
error
(
'fetch_mail_dlv--error:
%
s'
%
str
(
err
))
# 新增字段:实际起飞/落地时间。tracking=True 用于在 Chatter 中记录由原值改为现值的日志
actual_departure_time
=
fields
.
Datetime
(
string
=
"实际起飞时间"
,
tracking
=
True
)
actual_arrival_time
=
fields
.
Datetime
(
string
=
"实际落地时间"
,
tracking
=
True
)
# 关联起飞落地动态的 One2many 字段
flight_dynamic_ids
=
fields
.
One2many
(
'freight.flight.dynamic'
,
'bill_id'
,
string
=
"起飞落地动态"
)
class
FreightFlightDynamic
(
models
.
Model
):
_name
=
'freight.flight.dynamic'
_description
=
'起飞落地动态'
# 按创建时间降序排列
_order
=
'create_date desc'
bill_id
=
fields
.
Many2one
(
'cc.bl'
,
string
=
"关联提单"
,
ondelete
=
'cascade'
)
type
=
fields
.
Selection
([
(
'departure'
,
'起飞'
),
(
'arrival'
,
'落地'
)
],
string
=
"类型"
,
required
=
True
)
# Odoo 的 Datetime 字段在数据库层面默认就是以 0时区 (UTC) 存储的
event_time_utc
=
fields
.
Datetime
(
string
=
"发生时间(UTC)"
,
default
=
fields
.
Datetime
.
now
,
required
=
True
)
# 时区选择和计算本地时间
@api.model
def
_tz_get
(
self
):
return
[(
x
,
x
)
for
x
in
pytz
.
all_timezones
]
timezone
=
fields
.
Selection
(
_tz_get
,
string
=
'发生地时区'
,
default
=
'UTC'
,
required
=
True
)
event_time_local
=
fields
.
Char
(
string
=
"发生地时间(含时区)"
,
compute
=
'_compute_local_time'
,
store
=
True
)
# 记录人,默认当前用户
user_id
=
fields
.
Many2one
(
'res.users'
,
string
=
"记录人"
,
default
=
lambda
self
:
self
.
env
.
user
,
readonly
=
True
)
# 状态
status
=
fields
.
Selection
([
(
'valid'
,
'有效'
),
(
'invalid'
,
'无效'
)
],
string
=
"状态"
,
default
=
'valid'
,
readonly
=
True
)
@api.depends
(
'event_time_utc'
,
'timezone'
)
def
_compute_local_time
(
self
):
"""根据 UTC 时间和选择的时区,计算并拼接出当地时间字符串"""
for
record
in
self
:
if
record
.
event_time_utc
and
record
.
timezone
:
tz
=
pytz
.
timezone
(
record
.
timezone
)
utc_dt
=
pytz
.
utc
.
localize
(
record
.
event_time_utc
)
local_dt
=
utc_dt
.
astimezone
(
tz
)
# 格式化为:2023-10-25 14:30:00 (Asia/Shanghai)
record
.
event_time_local
=
f
"{local_dt.strftime('
%
Y-
%
m-
%
d
%
H:
%
M:
%
S')} ({record.timezone})"
else
:
record
.
event_time_local
=
False
@api.model
def
create
(
self
,
vals
):
"""
拦截创建方法:
新增时,查找该提单下同类型的【有效】记录,将其置为【无效】。
确保只有最新的一条是【有效】的。
"""
bill_id
=
vals
.
get
(
'bill_id'
)
dyn_type
=
vals
.
get
(
'type'
)
if
bill_id
and
dyn_type
:
# 寻找同类型且状态为有效的老记录
existing_valid_records
=
self
.
search
([
(
'bill_id'
,
'='
,
bill_id
),
(
'type'
,
'='
,
dyn_type
),
(
'status'
,
'='
,
'valid'
)
])
if
existing_valid_records
:
existing_valid_records
.
write
({
'status'
:
'invalid'
})
# 强制将新创建的记录设为有效
vals
[
'status'
]
=
'valid'
return
super
(
FreightFlightDynamic
,
self
)
.
create
(
vals
)
def
unlink
(
self
):
"""拦截删除方法:只能新增,不能删除"""
raise
UserError
(
"动态记录禁止删除!如需修正,请直接新增一条记录覆盖原状态。"
)
# 提单节点同步日志
class
CcBlSyncLog
(
models
.
Model
):
...
...
ccs_connect_tiktok/models/warn_config.py
浏览文件 @
50774994
...
...
@@ -27,7 +27,7 @@ class WarningConfig(models.Model):
# 当类型为"航班落地"时使用的占位展示字段
flight_landing_time
=
fields
.
Char
(
string
=
'预警时间点'
,
default
=
'
预计到达
时间'
,
default
=
'
实际落地
时间'
,
readonly
=
True
)
...
...
@@ -539,7 +539,9 @@ class WarningConfig(models.Model):
for
pkg
in
all_packages
:
packages_by_bl
[
pkg
.
bl_id
.
id
]
.
append
(
pkg
)
bl_eta_utc_dict
=
{
bl
.
id
:
self
.
convert_to_utc
(
bl
.
eta
)
if
bl
.
eta
else
None
for
bl
in
bl_objs
}
# bl_eta_utc_dict = {bl.id: self.convert_to_utc(bl.eta) if bl.eta else None for bl in bl_objs}
bl_eta_utc_dict
=
{
bl
.
id
:
bl
.
actual_arrival_time
if
bl
.
actual_arrival_time
else
None
for
bl
in
bl_objs
}
# 3. 核心优化:彻底解耦时间节点和未同步节点的类型,分别收集 process_code!
bl_codes_needed
=
set
()
...
...
ccs_connect_tiktok/security/ir.model.access.csv
浏览文件 @
50774994
...
...
@@ -26,3 +26,9 @@ access_package_data_wizard_base.group_user,package_data_wizard base.group_user,c
access_warning_config_user,warning.config.user,model_warning_config,base.group_user,1,1,1,1
access_freight_flight_dynamic_user,freight_flight_dynamic,model_freight_flight_dynamic,base.group_user,1,1,1,1
ccs_connect_tiktok/views/cc_bl_view.xml
浏览文件 @
50774994
...
...
@@ -65,6 +65,28 @@
</group>
</page>
</notebook>
<xpath
expr=
"//field[@name='cc_deadline']"
position=
"after"
>
<field
name=
"actual_departure_time"
/>
<field
name=
"actual_arrival_time"
/>
</xpath>
<xpath
expr=
"//notebook"
position=
"inside"
>
<page
string=
"起飞落地动态"
>
<field
name=
"flight_dynamic_ids"
context=
"{'default_bill_id': active_id}"
>
<tree
string=
"动态记录"
editable=
"top"
delete=
"0"
default_order=
"create_date desc"
>
<field
name=
"type"
/>
<field
name=
"event_time_utc"
/>
<field
name=
"timezone"
/>
<field
name=
"event_time_local"
/>
<field
name=
"user_id"
/>
<field
name=
"create_date"
string=
"创建时间"
readonly=
"1"
/>
<field
name=
"status"
widget=
"badge"
decoration-success=
"status == 'valid'"
decoration-muted=
"status == 'invalid'"
/>
</tree>
</field>
</page>
</xpath>
</field>
</record>
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论