sf-sdk 2.1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sf/__init__.py +7 -0
- sf/api.py +35 -0
- sf/comm/__init__.py +2 -0
- sf/comm/comm.py +80 -0
- sf/comm/exceptions.py +7 -0
- sf/comm/token.py +6 -0
- sf/data/express_type.csv +49 -0
- sf/model/__init__.py +8 -0
- sf/model/address.py +37 -0
- sf/model/cargo.py +99 -0
- sf/model/contact.py +46 -0
- sf/model/service.py +31 -0
- sf/order/__init__.py +2 -0
- sf/order/order.py +314 -0
- sf/order/sheet.py +65 -0
- sf_sdk-2.1.0.0.dist-info/METADATA +173 -0
- sf_sdk-2.1.0.0.dist-info/RECORD +23 -0
- sf_sdk-2.1.0.0.dist-info/WHEEL +5 -0
- sf_sdk-2.1.0.0.dist-info/top_level.txt +2 -0
- sf_sdk-2.1.0.0.dist-info/zip-safe +1 -0
- tests/__init__.py +0 -0
- tests/test_fee_apis.py +103 -0
- tests/test_order.py +137 -0
sf/__init__.py
ADDED
sf/api.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# @Time : 2019-08-21
|
|
3
|
+
# @Author : Kevin Kong (kfx2007@163.com)
|
|
4
|
+
|
|
5
|
+
from sf.comm import Comm
|
|
6
|
+
from sf.order import Order
|
|
7
|
+
from sf.order import Sheet
|
|
8
|
+
import os
|
|
9
|
+
import csv
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SF(object):
|
|
13
|
+
"""顺丰SDK"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, clientcode, checkword, sandbox=False, language='zh-CN'):
|
|
16
|
+
"""
|
|
17
|
+
初始化方法
|
|
18
|
+
"""
|
|
19
|
+
self.clientcode = clientcode
|
|
20
|
+
self.checkword = checkword
|
|
21
|
+
self.sandbox = sandbox
|
|
22
|
+
self.language = language
|
|
23
|
+
|
|
24
|
+
comm = Comm()
|
|
25
|
+
order = Order()
|
|
26
|
+
sheet = Sheet()
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def get_express_types(cls):
|
|
30
|
+
express = []
|
|
31
|
+
csv_path = os.path.dirname(__file__)
|
|
32
|
+
with open(os.path.join(csv_path, "data","express_type.csv")) as f:
|
|
33
|
+
for row in csv.reader(f):
|
|
34
|
+
express.append((row[0], row[1], row[2]))
|
|
35
|
+
return express
|
sf/comm/__init__.py
ADDED
sf/comm/comm.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# @Time : 2019-08-21
|
|
3
|
+
# @Author : Kevin Kong (kfx2007@163.com)
|
|
4
|
+
|
|
5
|
+
import requests
|
|
6
|
+
import uuid
|
|
7
|
+
import time
|
|
8
|
+
from copy import copy
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
URL = "https://bspgw.sf-express.com/std/service"
|
|
12
|
+
HKURL = "https://sfapi-hk.sf-express.com/std/service"
|
|
13
|
+
SANDBOXURL = "https://sfapi-sbox.sf-express.com/std/service"
|
|
14
|
+
TOKEN_URL = "https://sfapi.sf-express.com/oauth2/accessToken"
|
|
15
|
+
TOKEN_SANDBOXURL = "https://sfapi-sbox.sf-express.com/oauth2/accessToken"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Comm(object):
|
|
19
|
+
"""封装公共请求"""
|
|
20
|
+
|
|
21
|
+
def __get__(self, instance, owner):
|
|
22
|
+
self._clientcode = instance.clientcode
|
|
23
|
+
self._checkword = instance.checkword
|
|
24
|
+
self._sandbox = instance.sandbox
|
|
25
|
+
self._language = instance.language
|
|
26
|
+
self.get_access_token()
|
|
27
|
+
return self
|
|
28
|
+
|
|
29
|
+
def get_access_token(self):
|
|
30
|
+
"""获取AccessToken"""
|
|
31
|
+
url = f"{TOKEN_SANDBOXURL if self._sandbox else TOKEN_URL}?partnerID={self._clientcode}&secret={self._checkword}&grantType=password"
|
|
32
|
+
res = requests.post(url, headers={
|
|
33
|
+
"content-type": "application/x-www-form-urlencoded;charset=UTF-8"
|
|
34
|
+
}).json()
|
|
35
|
+
if res['apiResultCode'] == "A1000":
|
|
36
|
+
self._access_token = res['accessToken']
|
|
37
|
+
else:
|
|
38
|
+
raise Exception(
|
|
39
|
+
f"getting access token error:{res['apiResultCode'],res['apiErrorMsg']}")
|
|
40
|
+
return self._access_token
|
|
41
|
+
|
|
42
|
+
def get_public_params(self):
|
|
43
|
+
"""获取公共参数"""
|
|
44
|
+
data = {
|
|
45
|
+
"partnerID": self._clientcode,
|
|
46
|
+
"requestID": str(uuid.uuid4()),
|
|
47
|
+
"timestamp": int(time.time()),
|
|
48
|
+
"accessToken": self._access_token
|
|
49
|
+
}
|
|
50
|
+
return data
|
|
51
|
+
|
|
52
|
+
def post(self, service, data):
|
|
53
|
+
"""
|
|
54
|
+
提交请求
|
|
55
|
+
|
|
56
|
+
param data: 数据结构
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
post_data = self.get_public_params()
|
|
60
|
+
copy_data = copy(data)
|
|
61
|
+
|
|
62
|
+
for key, value in copy_data.items():
|
|
63
|
+
if value is None:
|
|
64
|
+
data.pop(key)
|
|
65
|
+
|
|
66
|
+
post_data.update({
|
|
67
|
+
"serviceCode": service,
|
|
68
|
+
"msgData": json.dumps(data)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
headers = {
|
|
72
|
+
"content-type": "application/x-www-form-urlencoded"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
url = SANDBOXURL if self._sandbox else URL
|
|
76
|
+
|
|
77
|
+
res = requests.post(url, data=post_data, headers=headers).json()
|
|
78
|
+
if res['apiResultCode'] != 'A1000':
|
|
79
|
+
raise Exception(res['apiErrorMsg'])
|
|
80
|
+
return json.loads(res['apiResultData'])
|
sf/comm/exceptions.py
ADDED
sf/comm/token.py
ADDED
sf/data/express_type.csv
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
1,T4,顺丰特快
|
|
2
|
+
2,T6,顺丰标快
|
|
3
|
+
6,T104,顺丰即日
|
|
4
|
+
10,T14,国际小包
|
|
5
|
+
12,T7,国际特惠配送
|
|
6
|
+
23,T9,顺丰国际特惠(文件)
|
|
7
|
+
24,T9,顺丰国际特惠(包裹)
|
|
8
|
+
29,T7,国际电商专递-标准
|
|
9
|
+
30,T4,三号便利箱(特快)
|
|
10
|
+
31,T4,便利封/袋(特快)
|
|
11
|
+
32,T4,二号便利箱(特快)
|
|
12
|
+
33,T4,岛内件(80CM)
|
|
13
|
+
35,T23,物资配送
|
|
14
|
+
39,T4,岛内件(110CM)
|
|
15
|
+
50,T4,千点取60
|
|
16
|
+
53,T4,电商盒子F1
|
|
17
|
+
54,T4,电商盒子F2
|
|
18
|
+
59,SP202,E顺递
|
|
19
|
+
60,T4,顺丰特快(文件)
|
|
20
|
+
61,T4,C1类包裹
|
|
21
|
+
111,T36,顺丰干配
|
|
22
|
+
153,T12,整车直达
|
|
23
|
+
154,T6,重货包裹
|
|
24
|
+
155,T6,标准零担
|
|
25
|
+
199,T4,特快包裹
|
|
26
|
+
201,T15,冷运标快
|
|
27
|
+
202,T4,顺丰微小件
|
|
28
|
+
209,T112,高铁专送
|
|
29
|
+
215,T39,大票直送
|
|
30
|
+
221,T15,香港冷运到家(≤60厘米)
|
|
31
|
+
229,T57,精温专递
|
|
32
|
+
231,T6,陆运包裹
|
|
33
|
+
233,T56,精温专递(样本陆)
|
|
34
|
+
235,T66,极效前置-预售
|
|
35
|
+
238,T29,纯重特配
|
|
36
|
+
242,T77,丰网速运
|
|
37
|
+
247,T68,电商标快
|
|
38
|
+
250,T70,极置店配
|
|
39
|
+
255,T28,顺丰卡航
|
|
40
|
+
256,T28,顺丰卡航(D类)
|
|
41
|
+
257,T4,退换上门
|
|
42
|
+
258,T4,退换自寄
|
|
43
|
+
260,T68,入仓电标
|
|
44
|
+
261,T68,O2O店配
|
|
45
|
+
263,T11,同城半日达
|
|
46
|
+
265,T67,预售电标
|
|
47
|
+
266,T5,顺丰空配(新)
|
|
48
|
+
283,T75,填舱标快
|
|
49
|
+
285,T76,填舱电标
|
sf/model/__init__.py
ADDED
sf/model/address.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# @Time : 2022-12-12
|
|
3
|
+
# @Author : Kevin Kong (kfx2007@163.com)
|
|
4
|
+
|
|
5
|
+
MAPPING = {
|
|
6
|
+
"Hong Kong":"香港",
|
|
7
|
+
"Macau":"澳门"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class Address(object):
|
|
11
|
+
|
|
12
|
+
def __init__(self,province=None,city=None,district=None,address=None,code=None):
|
|
13
|
+
self.province = province
|
|
14
|
+
self.city = city
|
|
15
|
+
self.district = district
|
|
16
|
+
self.address =address
|
|
17
|
+
self.code = code
|
|
18
|
+
|
|
19
|
+
if (self.province and self.province.lower() in ['hong kong','香港']) or (self.city and self.city.lower in ['hong kong','香港']):
|
|
20
|
+
self.code = '852'
|
|
21
|
+
if (self.province and self.province.lower() in ['macau','澳门']) or (self.city and self.city.lower in ['macau','澳门']):
|
|
22
|
+
self.code = '853'
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _convert(self,key):
|
|
26
|
+
if key in MAPPING:
|
|
27
|
+
return MAPPING[key]
|
|
28
|
+
return key
|
|
29
|
+
|
|
30
|
+
def to_dict(self):
|
|
31
|
+
return {
|
|
32
|
+
"province":self._convert(self.province),
|
|
33
|
+
"city": self._convert(self.city),
|
|
34
|
+
"district": self.district,
|
|
35
|
+
"address": self.address,
|
|
36
|
+
"code": self.code
|
|
37
|
+
}
|
sf/model/cargo.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# @Time : 2022-12-03
|
|
3
|
+
# @Author : Kevin Kong (kfx2007@163.com)
|
|
4
|
+
|
|
5
|
+
from copy import copy
|
|
6
|
+
|
|
7
|
+
class CargoDetail(object):
|
|
8
|
+
|
|
9
|
+
def __init__(self, name, count=None, unit=None, weight=None, amount=None, currency=None, sourceArea=None, **kwargs):
|
|
10
|
+
"""
|
|
11
|
+
货物信息
|
|
12
|
+
|
|
13
|
+
param count: Number(5) 条件 货物数量 跨境件报关需要填写
|
|
14
|
+
param unit: String(30) 条件 货物单位,如:个、台、本, 跨境件报关需要填写
|
|
15
|
+
param weight: Number(16,3) 条件 订单货物单位重量,包含子母件, 单位千克,精确到小数点后3位 跨境件报关需要填写
|
|
16
|
+
param amount: Number(17,3) 条件 货物单价,精确到小数点后3位, 跨境件报关需要填写
|
|
17
|
+
param currency: String(5) 条件 货物单价的币别: 参照附录《国际件币别表》
|
|
18
|
+
param sourceArea: String(5) 条件 原产地国别, 跨境件报关需要填写
|
|
19
|
+
param productRecordNo: String(18) 否 货物产品国检备案编号
|
|
20
|
+
param goodPrepardNo: String(100) 否 商品海关备案号
|
|
21
|
+
param taxNo: String(100) 否 商品行邮税号
|
|
22
|
+
param hsCode: String(100) 否 海关编码
|
|
23
|
+
param goodsCode: String(60) 否 商品编号
|
|
24
|
+
param brand: String(60) 否 货物品牌
|
|
25
|
+
param specifications:String(60) 否 货物规格型号
|
|
26
|
+
param manufacturer:String(100) 否 生产厂家
|
|
27
|
+
param shipmentWeight:Double (16,3) 否 托寄物毛重
|
|
28
|
+
param length:Double (16,3) 否 托寄物长
|
|
29
|
+
param width:Double (16,3) 否 托寄物宽
|
|
30
|
+
param height:Double (16,3) 否 托寄物高
|
|
31
|
+
param volume:Double (16,2) 否 托寄物体积
|
|
32
|
+
param cargoDeclaredValue:Double (16,5) 否 托寄物声明价值(杭州口岸必填)
|
|
33
|
+
param declaredValueDeclaredCurrency String(5) 否 托寄物声明价值币别(杭州口岸必填)
|
|
34
|
+
param cargoId:String(60) 否 货物id(逆向物流)
|
|
35
|
+
param intelligentInspection Number(1):否 智能验货标识(1-是,0-否) (逆向物流)
|
|
36
|
+
param snCode:String(4000) 否 货物标识码(逆向物流)
|
|
37
|
+
param stateBarCode: String(50) 否 国条码
|
|
38
|
+
"""
|
|
39
|
+
self.name = name
|
|
40
|
+
self.count = count
|
|
41
|
+
self.unit = unit
|
|
42
|
+
self.weight = weight
|
|
43
|
+
self.amount = amount
|
|
44
|
+
self.currency = currency
|
|
45
|
+
self.sourceArea = sourceArea
|
|
46
|
+
self.productRecordNo = kwargs.get('productRecordNo',None)
|
|
47
|
+
self.goodPrepardNo = kwargs.get('goodPrepardNo',None)
|
|
48
|
+
self.taxNo = kwargs.get('taxNo',None)
|
|
49
|
+
self.hsCode = kwargs.get('hsCode',None)
|
|
50
|
+
self.goodsCode = kwargs.get('goodsCode',None)
|
|
51
|
+
self.brand = kwargs.get('brand',None)
|
|
52
|
+
self.specifications = kwargs.get('specifications',None)
|
|
53
|
+
self.manufacturer = kwargs.get('manufacturer',None)
|
|
54
|
+
self.shipmentWeight = kwargs.get('shipmentWeight',None)
|
|
55
|
+
self.length = kwargs.get('length',None)
|
|
56
|
+
self.width = kwargs.get('width',None)
|
|
57
|
+
self.height = kwargs.get('height',None)
|
|
58
|
+
self.volume = kwargs.get('valume',None)
|
|
59
|
+
self.cargoDeclaredValue = kwargs.get('cargoDeclaredValue',None)
|
|
60
|
+
self.declaredValueDeclaredCurrency = kwargs.get('declaredValueDeclaredCurrency',None)
|
|
61
|
+
self.cargoId = kwargs.get('cargoId',None)
|
|
62
|
+
self.intelligentInspection = kwargs.get('intelligentInspection',None)
|
|
63
|
+
self.snCode = kwargs.get('snCode',None)
|
|
64
|
+
self.stateBarCode = kwargs.get('stateBarCode',None)
|
|
65
|
+
|
|
66
|
+
def to_dict(self):
|
|
67
|
+
res = {
|
|
68
|
+
"name": self.name,
|
|
69
|
+
"count": self.count,
|
|
70
|
+
"unit": self.unit,
|
|
71
|
+
"weight": self.weight,
|
|
72
|
+
"amount": self.amount,
|
|
73
|
+
"currency": self.currency,
|
|
74
|
+
"sourceArea": self.sourceArea,
|
|
75
|
+
"productRecordNo": self.productRecordNo,
|
|
76
|
+
"goodPrepardNo": self.goodPrepardNo,
|
|
77
|
+
"taxNo": self.taxNo,
|
|
78
|
+
"hsCode": self.hsCode,
|
|
79
|
+
"goodsCode": self.goodsCode,
|
|
80
|
+
"brand": self.brand,
|
|
81
|
+
"specifications": self.specifications,
|
|
82
|
+
"manufacturer": self.manufacturer,
|
|
83
|
+
"shipmentWeight": self.shipmentWeight,
|
|
84
|
+
"length": self.length,
|
|
85
|
+
"width": self.width,
|
|
86
|
+
"height": self.height,
|
|
87
|
+
"volume": self.volume,
|
|
88
|
+
"cargoDeclaredValue": self.cargoDeclaredValue,
|
|
89
|
+
"declaredValueDeclaredCurrency": self.declaredValueDeclaredCurrency,
|
|
90
|
+
"cargoId": self.cargoId,
|
|
91
|
+
"intelligentInspection": self.intelligentInspection,
|
|
92
|
+
"snCode": self.snCode,
|
|
93
|
+
"stateBarCode": self.stateBarCode
|
|
94
|
+
}
|
|
95
|
+
copy_data = copy(res)
|
|
96
|
+
for k, v in copy_data.items():
|
|
97
|
+
if v is None:
|
|
98
|
+
res.pop(k)
|
|
99
|
+
return res
|
sf/model/contact.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# @Time : 2022-12-03
|
|
3
|
+
# @Author : Kevin Kong (kfx2007@163.com)
|
|
4
|
+
|
|
5
|
+
from copy import copy
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ContactInfo(object):
|
|
9
|
+
|
|
10
|
+
def __init__(self, address, country='CN', contactType=2, company=None, contact=None, mobile=None, zoneCode=None, province=None, city=None, postCode=None, email=None, taxNo=None, tel=None):
|
|
11
|
+
self.address = address
|
|
12
|
+
self.country = country
|
|
13
|
+
self.contactType = contactType
|
|
14
|
+
self.company = company
|
|
15
|
+
self.contact = contact
|
|
16
|
+
self.mobile = mobile
|
|
17
|
+
self.tel = tel
|
|
18
|
+
self.zoneCode = zoneCode
|
|
19
|
+
self.province = province
|
|
20
|
+
self.city = city
|
|
21
|
+
self.postCode = postCode
|
|
22
|
+
self.email = email
|
|
23
|
+
self.taxNo = taxNo
|
|
24
|
+
|
|
25
|
+
def to_dict(self):
|
|
26
|
+
res = {
|
|
27
|
+
"address":self.address,
|
|
28
|
+
"country":self.country,
|
|
29
|
+
"contactType":self.contactType,
|
|
30
|
+
"company":self.company,
|
|
31
|
+
"contact":self.contact,
|
|
32
|
+
"mobile":self.mobile,
|
|
33
|
+
"tel": self.tel,
|
|
34
|
+
"zoneCode":self.zoneCode,
|
|
35
|
+
"province":self.province,
|
|
36
|
+
"city":self.city,
|
|
37
|
+
"postCode":self.postCode,
|
|
38
|
+
"email":self.email,
|
|
39
|
+
"taxNo":self.taxNo,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
copy_data = copy(res)
|
|
43
|
+
for k, v in copy_data.items():
|
|
44
|
+
if v is None:
|
|
45
|
+
res.pop(k)
|
|
46
|
+
return res
|
sf/model/service.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# @Time : 2022-12-03
|
|
3
|
+
# @Author : Kevin Kong (kfx2007@163.com)
|
|
4
|
+
|
|
5
|
+
from copy import copy
|
|
6
|
+
|
|
7
|
+
class Service(object):
|
|
8
|
+
|
|
9
|
+
def __init__(self, name, value=None, value1=None, value2=None, value3=None, value4=None):
|
|
10
|
+
self.name = name
|
|
11
|
+
self.value = value
|
|
12
|
+
self.value1 = value1
|
|
13
|
+
self.value2 = value2
|
|
14
|
+
self.value3 = value3
|
|
15
|
+
self.value4 = value4
|
|
16
|
+
|
|
17
|
+
def to_dict(self):
|
|
18
|
+
res = {
|
|
19
|
+
"name": name,
|
|
20
|
+
"value": value,
|
|
21
|
+
"value1": value1,
|
|
22
|
+
"value2": value2,
|
|
23
|
+
"value3": value3,
|
|
24
|
+
"value4": value4,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
copy_data = copy(res)
|
|
28
|
+
for k, v in copy_data.items():
|
|
29
|
+
if v is None:
|
|
30
|
+
res.pop(k)
|
|
31
|
+
return res
|
sf/order/__init__.py
ADDED
sf/order/order.py
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# @Time : 2019-08-21
|
|
3
|
+
# @Author : Kevin Kong (kfx2007@163.com)
|
|
4
|
+
|
|
5
|
+
from sf.comm import Comm
|
|
6
|
+
|
|
7
|
+
QUERY_URL = "https://www.sf-express.com/we/ow/chn/sc/waybill/waybill-detail/"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Order(Comm):
|
|
11
|
+
|
|
12
|
+
def pre_order(self, orderId, contactInfoList, cargoDetails, monthlyCard=None, expressTypeId=1, isReturnRoutelabel=1, **kwargs):
|
|
13
|
+
data = {
|
|
14
|
+
"language": kwargs.get("language",None) or self._language,
|
|
15
|
+
"orderId": orderId,
|
|
16
|
+
"contactInfoList": [c.to_dict() for c in contactInfoList],
|
|
17
|
+
"cargoDetails": [c.to_dict() for c in cargoDetails],
|
|
18
|
+
"monthlyCard": monthlyCard,
|
|
19
|
+
"expressTypeId": expressTypeId,
|
|
20
|
+
"isReturnRoutelabel": isReturnRoutelabel,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
data.update(kwargs)
|
|
24
|
+
return self.post("EXP_RECE_PRE_ORDER", data)
|
|
25
|
+
|
|
26
|
+
def create_order(self, orderId, contactInfoList, cargoDetails, monthlyCard=None, expressTypeId=1, isReturnRoutelabel=1, **kwargs):
|
|
27
|
+
"""
|
|
28
|
+
下订单接口
|
|
29
|
+
|
|
30
|
+
param language:String(10) 必填 响应报文的语言, 缺省值为zh-CN,目前支持以下值zh-CN 表示中文简体, zh-TW或zh-HK或 zh-MO表示中文繁体, en表示英文
|
|
31
|
+
param orderId: String(64) 客户订单号,不能重复
|
|
32
|
+
param waybillNoInfoList: List 顺丰运单号
|
|
33
|
+
param customsInfo: CustomsInfo 报关信息
|
|
34
|
+
param cargoDetails: List 托寄物信息
|
|
35
|
+
param cargoDesc: String(20) 拖寄物类型描述,如: 文件,电子产品,衣服等
|
|
36
|
+
param extraInfoList: List 扩展属性
|
|
37
|
+
param serviceList: List 增值服务信息,支持附录: 《增值服务产品表》
|
|
38
|
+
param contactInfoList: List 收寄双方信息
|
|
39
|
+
param monthlyCard: String(20): 条件 顺丰月结卡号 月结支付时传值,现结不需传值;沙箱联调可使用测试月结卡号7551234567(非正式,无须绑定,仅支持联调使用)
|
|
40
|
+
param payMethod: Number(2): 1: 付款方式,支持以下值: 1:寄方付 2:收方付 3:第三方付
|
|
41
|
+
param expressTypeId: Number(5): 1: 快件产品类别, 支持附录 《快件产品类别表》 的产品编码值,仅可使用与顺丰销售约定的快件产品
|
|
42
|
+
param parcelQty: Number(5): 1: 包裹数,一个包裹对应一个运单号;若包裹数大于1,则返回一个母运单号和N-1个子运单号
|
|
43
|
+
param totalLength: Number(16,5) 客户订单货物总长,单位厘米, 精确到小数点后3位, 包含子母件
|
|
44
|
+
param totalWidth: Number(16,5) 客户订单货物总宽,单位厘米, 精确到小数点后3位, 包含子母件
|
|
45
|
+
param totalHeight: Number(16,5) 客户订单货物总高,单位厘米, 精确到小数点后3位, 包含子母件
|
|
46
|
+
param volume: Number(16,5) : 订单货物总体积,单位立方厘米, 精确到小数点后3位,会用于计抛 (是否计抛具体商务沟通中 双方约定)
|
|
47
|
+
param totalWeight: Number(17,5): 条件 订单货物总重量(郑州空港海关必填), 若为子母件必填, 单位千克, 精确到小数点后3位,如果提供此值, 必须>0 (子母件需>6)
|
|
48
|
+
param totalNetWeight: Number(17,5) 商品总净重
|
|
49
|
+
param sendStartTm: Date: 接收 到报 文的 时间: 要求上门取件开始时间, 格式: YYYY-MM-DD HH24:MM:SS, 示例: 2012-7-30 09:30:00 (预约单传预约截止时间,不赋值默认按当前时间下发,1小时内取件)
|
|
50
|
+
param isDocall: Number(1): 0: 是否通过手持终端 通知顺丰收派 员上门收件,支持以下值: 1:要求 0:不要求
|
|
51
|
+
param isSignBack: Number(1): 0: 是否返回签回单 (签单返还)的运单号, 支持以下值: 1:要求 0:不要求
|
|
52
|
+
param custReferenceNo: String(100) 客户参考编码:如客户原始订单号
|
|
53
|
+
param temperatureRange: Number(2): 条件 温度范围类型,当 express_type为12 医药温控件 时必填,支持以下值: 1:冷藏 3:冷冻
|
|
54
|
+
param orderSource: String(50) 订单平台类型 (对于平台类客户, 如果需要在订单中 区分订单来源, 则可使用此字段) 天猫:tmall, 拼多多:pinduoduo, 京东 : jd 等平台类型编码
|
|
55
|
+
param remark: String(100) 备注
|
|
56
|
+
param isOneselfPickup: Number(1): 0: 快件自取,支持以下值: 1:客户同意快件自取 0:客户不同意快件自取
|
|
57
|
+
param filterField: String 筛单特殊字段用来人工筛单
|
|
58
|
+
param isReturnQRCode: Number(1): 0: 是否返回用来退货业务的 二维码URL, 支持以下值: 1:返回二维码 0:不返回二维码
|
|
59
|
+
param specialDeliveryTypeCode: String(3) 特殊派送类型代码 1:身份验证
|
|
60
|
+
param specialDeliveryValue: String(100) 特殊派件具体表述 证件类型: 证件后8位如: 1:09296231(1 表示身份证, 暂不支持其他证件)
|
|
61
|
+
param merchantPayOrderNo: String(100) 商户支付订单号
|
|
62
|
+
param isReturnSignBackRoute label: Number(1): 0: 是否返回签回单路由标签: 默认0, 1:返回路由标签, 0:不返回
|
|
63
|
+
param isReturnRoutelabel: Number(1): 1: 是否返回路由标签: 默认1, 1:返回路由标签, 0:不返回;除部分特殊用户外,其余用户都默认返回
|
|
64
|
+
param isUnifiedWaybillNo: Number(1): 0: 是否使用国家统一面单号 1:是, 0:否(默认)
|
|
65
|
+
param podModelAddress: String(1024) 签单返还范本地址
|
|
66
|
+
param inProcessWaybillNo: String(100) 头程运单号(郑州空港海关必填)
|
|
67
|
+
param isGenWaybillNo: Number(1) 1 是否需求分配运单号1:分配 0:不分配(若带单号下单,请传值0)
|
|
68
|
+
|
|
69
|
+
return: dict 接口返回结果
|
|
70
|
+
success: true/false 接口调用是否成功
|
|
71
|
+
errorCode: 错误码
|
|
72
|
+
errorMsg: 错误信息
|
|
73
|
+
msgData: 成功后返回的数据
|
|
74
|
+
"""
|
|
75
|
+
data = {
|
|
76
|
+
"language": kwargs.get("language",None) or self._language,
|
|
77
|
+
"orderId": orderId,
|
|
78
|
+
"contactInfoList": [c.to_dict() for c in contactInfoList],
|
|
79
|
+
"cargoDetails": [c.to_dict() for c in cargoDetails],
|
|
80
|
+
"monthlyCard": monthlyCard,
|
|
81
|
+
"expressTypeId": expressTypeId,
|
|
82
|
+
"isReturnRoutelabel": isReturnRoutelabel,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
data.update(kwargs)
|
|
86
|
+
return self.post("EXP_RECE_CREATE_ORDER", data)
|
|
87
|
+
|
|
88
|
+
def create_sub_order(self, orderId, parcelQty, waybillNoInfoList=None):
|
|
89
|
+
"""
|
|
90
|
+
子单号申请接口-速运类API
|
|
91
|
+
客户在下单成功后 ,因业务场景需要可以调用此接口获取更多的子单号数。但不能超过配置的最大数/1200个。
|
|
92
|
+
|
|
93
|
+
:param orderId: 客户订单号
|
|
94
|
+
:param parcelQty: 子单号数
|
|
95
|
+
:param waybillNoInfoList: 顺丰运单号子单申请需要指定每个子包裹的长宽高提交重量时需要传入此参数
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
data = {
|
|
99
|
+
"orderId": orderId,
|
|
100
|
+
"parcelQty": parcelQty,
|
|
101
|
+
"waybillNoInfoList": waybillNoInfoList
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return self.post("EXP_RECE_GET_SUB_MAILNO", data)
|
|
105
|
+
|
|
106
|
+
def confirm_order(self, orderId, dealType=1, **kwargs):
|
|
107
|
+
"""
|
|
108
|
+
订单确认/取消接口-速运类API
|
|
109
|
+
|
|
110
|
+
param: orderId String(64) 是 客户订单号
|
|
111
|
+
param: dealType Number(1) 否 1 客户订单操作标识: 1:确认 (丰桥下订单接口默认自动确认,不需客户重复确认,该操作用在其它非自动确认的场景) 2:取消
|
|
112
|
+
param: waybillNoInfoList List 否 顺丰运单号(如dealtype=1, 必填)
|
|
113
|
+
param: customsBatchs String(20) 否 报关批次
|
|
114
|
+
param: collectEmpCode String(30) 否 揽收员工号
|
|
115
|
+
param: inProcessWaybillNo String(100) 否 头程运单号
|
|
116
|
+
param: sourceZoneCode String(10) 否 原寄地网点代码
|
|
117
|
+
param: destZoneCode String(10) 否 目的地网点代码
|
|
118
|
+
param: totalWeight Number(17,5) 否 订单货物总重量,包含子母 件,单位千克,精确到小数点 后3位,如果提供此值,必 须>0
|
|
119
|
+
param: totalVolume Number(16,5) 否 订单货物总体积,单位立方厘 米,精确到小数点后3位,会 用于计抛(是否计抛具体商务 沟通中双方约定)
|
|
120
|
+
param: expressTypeId Number(5) 否 快件产品类别,支持附录《快 件产品类别表》的产品编码 值,仅可使用与顺丰销售约定 的快件产品
|
|
121
|
+
param: extraInfoList List 否 扩展属性
|
|
122
|
+
param: totalLength Number(16, 5) 否 客户订单货物总长,单位厘米, 精确到小数点后3位,包含子 母件
|
|
123
|
+
param: totalWidth Number(16, 5) 否 客户订单货物总宽,单位厘米, 精确到小数点后3位,包含子 母件
|
|
124
|
+
param: totalHeight Number(16, 5) 否 客户订单货物总高,单位厘米, 精确到小数点后3位,包含子 母件
|
|
125
|
+
param: serviceList List 否 增值服务信息
|
|
126
|
+
param: isConfirmNew Number (1) 否 是否走新通用确认1:支持修改联系人 2:支持改其他客户订单默认0
|
|
127
|
+
param: destContactInfo OrderContactInfoDto 否 收件人信息
|
|
128
|
+
param: isDocall Number(1) 否 是否通过手持终端通知顺丰收派员上门收件, 支持以下值:1:要求其它为不要求
|
|
129
|
+
param: specialDeliveryTypeCode String(3) 否 1. 特殊派送类型代码 身份验证 2. 极效前置单
|
|
130
|
+
param: specialDeliveryValue String(100) 否 1> 特殊派件具体表述 证件类型:证件后8位 如:1:09296231(1表示身份证,暂不支持其他证件) 2>.极效前置单时:Y:若不支持则返回普通运单N:若不支持则返回错误码
|
|
131
|
+
param: sendStartTm Date 否 预约时间(上门揽收时间)
|
|
132
|
+
param: pickupAppointEndtime Date 否 上门揽收截止时间
|
|
133
|
+
"""
|
|
134
|
+
data = {
|
|
135
|
+
"orderId": orderId,
|
|
136
|
+
"dealType": dealType
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
data.update(kwargs)
|
|
140
|
+
return self.post("EXP_RECE_UPDATE_ORDER", data)
|
|
141
|
+
|
|
142
|
+
def get_order(self, orderId, searchType='1', language=None):
|
|
143
|
+
"""
|
|
144
|
+
订单结果查询接口
|
|
145
|
+
|
|
146
|
+
params:
|
|
147
|
+
orderId String(64) 是 客户订单号
|
|
148
|
+
searchType String(10) 否 查询类型:1正向单 2退货单
|
|
149
|
+
language String(10) 否 响应报文的语言, 缺省值为zh-CN,目前支持以下值zh-CN 表示中文简体, zh-TW或zh-HK或 zh-MO表示中文繁体, en表示英文
|
|
150
|
+
"""
|
|
151
|
+
data = {
|
|
152
|
+
"language": language or self._language,
|
|
153
|
+
"orderId": orderId,
|
|
154
|
+
"searchType": searchType,
|
|
155
|
+
"language": language
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return self.post("EXP_RECE_SEARCH_ORDER_RESP", data)
|
|
159
|
+
|
|
160
|
+
def get_route_info(self, trackingNumber, trackingType=1, methodType=1, referenceNumber=None, checkPhoneNo=None, language=None):
|
|
161
|
+
"""
|
|
162
|
+
路由查询接口接口-速运类API
|
|
163
|
+
|
|
164
|
+
param trackingNumber: 查询号: trackingType=1,则此值为顺丰运单号 如果trackingType=2,则此值为客户订单号
|
|
165
|
+
param trackingType: 查询号类别: 1:根据顺丰运单号查询,trackingNumber将被当作顺丰运单号处理 2:根据客户订单号查询,trackingNumber将被当作客户订单号处理
|
|
166
|
+
param methodType: 路由查询类别: 1:标准路由查询 2:定制路由查询
|
|
167
|
+
param referenceNumber: 参考编码(目前针对亚马逊客户,由客户传)
|
|
168
|
+
param checkPhoneNo: 校验电话号码后四位值;
|
|
169
|
+
|
|
170
|
+
return: 包含节点信息的路由
|
|
171
|
+
"""
|
|
172
|
+
data = {
|
|
173
|
+
"language": language or self._language,
|
|
174
|
+
"trackingNumber": trackingNumber,
|
|
175
|
+
"trackingType": trackingType,
|
|
176
|
+
"methodType": methodType,
|
|
177
|
+
"referenceNumber": referenceNumber,
|
|
178
|
+
"checkPhoneNo": checkPhoneNo
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return self.post("EXP_RECE_SEARCH_ROUTES", data)
|
|
182
|
+
|
|
183
|
+
def can_delivery(self, orderId, monthlyCard=None, filterType=1, contactInfos=None):
|
|
184
|
+
"""
|
|
185
|
+
订单筛选接口-速运类API
|
|
186
|
+
|
|
187
|
+
客户系统通过此接口向顺丰系统发送主动的筛单请求,用于判断客户的收、派地址是否属于顺丰的收派范围。
|
|
188
|
+
|
|
189
|
+
param orderId: String 到件方详细地址,需要包括省市区
|
|
190
|
+
param orderid: String 客户订单号,filter_type=2则必须提供
|
|
191
|
+
param filterType: Int 筛单类别: 1:自动筛单(系统根据地址库进行判断,并返回结果,系统无法检索到可派送的将返回不可派送) 2:可人工筛单(系统首先根据地址库判断,如果无法自动判断是否收派,系统将生成需要人工判断的任务,后续由人工处理,处理结束后,顺丰可主动推送给客户系统)
|
|
192
|
+
param contactInfos: List 地址信息
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
data = {
|
|
196
|
+
"orderId": orderId,
|
|
197
|
+
"monthlyCard": monthlyCard,
|
|
198
|
+
"filterType": filterType,
|
|
199
|
+
"contactInfos": contactInfos
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return self.post("EXP_RECE_FILTER_ORDER_BSP", data)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def query_delivery(self, businessType,destAddress,srcAddress, weight=None,volume=None,consignedTime=None,searchPrice=0):
|
|
206
|
+
"""
|
|
207
|
+
时效标准及价格查询接口-速运类API
|
|
208
|
+
|
|
209
|
+
客户可通过接口查询从特定原寄地寄特定目的地的时效和价格。
|
|
210
|
+
|
|
211
|
+
param businessType: 快件产品:可以为空,为空时查询默认时效对应的产品列表。不为空时以数字代码业务类型,例如:1:表示“”2:表示“顺丰特惠”5:表示“顺丰次晨”6:表示“即日件
|
|
212
|
+
param destAddress: 目的地信息
|
|
213
|
+
param srcAddress: 原寄地信息
|
|
214
|
+
param weight: 货物总重量,包含子母件,单位千克,精确到小数点后2位,如果提供此值,必须>0
|
|
215
|
+
param volume: 货物的体积(长、宽、高分别以厘米为单位计算体积),精确到小数点后2位。
|
|
216
|
+
param consignedTime: 寄件时间,格式为YYYY-MM-DD HH24:MM:SS,示例2013-12-27 17:54:20。
|
|
217
|
+
param searchPrice: 1:表示查询含价格的接口0:表示查询不含价格的接口 备注:限制只能为0,1或者不传searchPrice,不可以为空,null
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
data = {
|
|
221
|
+
"businessType": businessType,
|
|
222
|
+
"destAddress": destAddress.to_dict(),
|
|
223
|
+
"srcAddress": srcAddress.to_dict(),
|
|
224
|
+
"weight": weight,
|
|
225
|
+
"volume": volume,
|
|
226
|
+
"consignedTime": consignedTime,
|
|
227
|
+
"searchPrice": searchPrice
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return self.post("EXP_RECE_QUERY_DELIVERTM", data)
|
|
231
|
+
|
|
232
|
+
def recommend_products(self, srcProvince, srcCity, destProvince, destCity,
|
|
233
|
+
sendTime, weight, paymentTerms, srcAddress,
|
|
234
|
+
destAddress, **kwargs):
|
|
235
|
+
"""产品推荐接口-速运类API。
|
|
236
|
+
|
|
237
|
+
返回顺丰可用产品、预计送达时间、计费重量、币种、总费用及
|
|
238
|
+
增值服务费明细。燃油附加费在 ``serviceFeeList`` 中的服务代码
|
|
239
|
+
为 ``IN15``。
|
|
240
|
+
|
|
241
|
+
:param srcProvince: 原寄地省/一级行政区
|
|
242
|
+
:param srcCity: 原寄地市/二级行政区
|
|
243
|
+
:param destProvince: 目的地省/一级行政区
|
|
244
|
+
:param destCity: 目的地市/二级行政区
|
|
245
|
+
:param sendTime: 寄件时间,格式 ``YYYY-MM-DD HH:MM:SS``
|
|
246
|
+
:param weight: 重量,单位 kg
|
|
247
|
+
:param paymentTerms: 付款方式:1寄付、2到付、3寄转第三方、4到转第三方
|
|
248
|
+
:param srcAddress: 原寄地详细地址
|
|
249
|
+
:param destAddress: 目的地详细地址
|
|
250
|
+
:param kwargs: 顺丰接口的其他可选字段,如 monthlyCard、srcCounty、
|
|
251
|
+
destCounty、commodityNameList、declaredValue、declaredCurrency 等
|
|
252
|
+
:return: 顺丰接口原始响应字典
|
|
253
|
+
"""
|
|
254
|
+
data = {
|
|
255
|
+
"srcProvince": srcProvince,
|
|
256
|
+
"srcCity": srcCity,
|
|
257
|
+
"destProvince": destProvince,
|
|
258
|
+
"destCity": destCity,
|
|
259
|
+
"sendTime": sendTime,
|
|
260
|
+
"weight": weight,
|
|
261
|
+
"paymentTerms": paymentTerms,
|
|
262
|
+
"srcAddress": srcAddress,
|
|
263
|
+
"destAddress": destAddress,
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
data.update(kwargs)
|
|
267
|
+
return self.post("EXP_RECE_PSDS_PRODUCT_RECOMMEND", data)
|
|
268
|
+
|
|
269
|
+
def estimate_total_fee(self, customerCode, interProductCode, senderInfo,
|
|
270
|
+
receiverInfo, **kwargs):
|
|
271
|
+
"""预估总费用接口-国际件API。
|
|
272
|
+
|
|
273
|
+
该接口返回 ``currency``、``totalFee`` 和 ``feeInfoList``,其中费用
|
|
274
|
+
明细可包含燃油附加费。调用前需在顺丰开放平台开通对应接口权限。
|
|
275
|
+
|
|
276
|
+
:param customerCode: 顺丰国际客户编码
|
|
277
|
+
:param interProductCode: 国际产品映射码
|
|
278
|
+
:param senderInfo: 寄件方信息字典
|
|
279
|
+
:param receiverInfo: 收件方信息字典
|
|
280
|
+
:param kwargs: parcelTotalWeight、paymentInfo、customsInfo、
|
|
281
|
+
addServiceInfoList 等其他可选字段
|
|
282
|
+
:return: 顺丰接口原始响应字典
|
|
283
|
+
"""
|
|
284
|
+
data = {
|
|
285
|
+
"customerCode": customerCode,
|
|
286
|
+
"interProductCode": interProductCode,
|
|
287
|
+
"senderInfo": senderInfo,
|
|
288
|
+
"receiverInfo": receiverInfo,
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
data.update(kwargs)
|
|
292
|
+
return self.post("COM_RECE_IUOP_ESTIMATE_FEE", data)
|
|
293
|
+
|
|
294
|
+
def query_waybill_fee(self, trackingType, trackingNum,
|
|
295
|
+
bizTemplateCode=None, **kwargs):
|
|
296
|
+
"""清单运费查询接口-速运类API。
|
|
297
|
+
|
|
298
|
+
支持按客户订单号或顺丰运单号查询已产生的费用。燃油附加费在
|
|
299
|
+
``waybillFeeList`` 中的费用类型为 ``14``。
|
|
300
|
+
|
|
301
|
+
:param trackingType: 查询类型:1按客户订单号,2按顺丰运单号
|
|
302
|
+
:param trackingNum: 客户订单号或顺丰运单号
|
|
303
|
+
:param bizTemplateCode: 可选业务配置代码
|
|
304
|
+
:param kwargs: 顺丰接口后续扩展字段
|
|
305
|
+
:return: 顺丰接口原始响应字典
|
|
306
|
+
"""
|
|
307
|
+
data = {
|
|
308
|
+
"trackingType": str(trackingType),
|
|
309
|
+
"trackingNum": trackingNum,
|
|
310
|
+
"bizTemplateCode": bizTemplateCode,
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
data.update(kwargs)
|
|
314
|
+
return self.post("EXP_RECE_QUERY_SFWAYBILL", data)
|
sf/order/sheet.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# @Time : 2022-12-04
|
|
3
|
+
# @Author : Kevin Kong (kfx2007@163.com)
|
|
4
|
+
|
|
5
|
+
from sf.comm import Comm
|
|
6
|
+
from sf.comm import SheetException
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Sheet(Comm):
|
|
11
|
+
|
|
12
|
+
def print(self, templateCode, documents, version='2.0', fileType="pdf", sync=True, customTemplateCode=None, extJson=None):
|
|
13
|
+
"""
|
|
14
|
+
云打印面单打印2.0接口-面单类API
|
|
15
|
+
param templateCode: 模板编码 是 String 关联云打印接口后,点击查看,可在接口详情页获取模板编码,类似:fm_76130_standard_{partnerId}
|
|
16
|
+
param documents: 业务数据 是 array 一批不要超过20个运单,字段定义参考 2.3.1 模板固定字段
|
|
17
|
+
param version: 版本号 是 String 版本号,传固定值:2.0
|
|
18
|
+
param fileType: 生成面单文件格式 否 String pdf格式
|
|
19
|
+
param sync: 是否同步 条件 boolean true: 同步,false: 异步,默认同步
|
|
20
|
+
param customTemplateCode: 自定义模板编码,当需要使用模板编辑器编辑自定义区时,将自定义模板编码赋值该字段 否 String 自定义模板必须是已发布的,且规格要和需要打印的模板对应
|
|
21
|
+
param extJson: 扩展字段 否 Json 字段定义参考下方extJson字段
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
data = {
|
|
26
|
+
"templateCode": templateCode,
|
|
27
|
+
"documents": documents,
|
|
28
|
+
"version": version,
|
|
29
|
+
"fileType": fileType,
|
|
30
|
+
"sync": sync,
|
|
31
|
+
"customTemplateCode": customTemplateCode,
|
|
32
|
+
"extJson": extJson
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return self.post("COM_RECE_CLOUD_PRINT_WAYBILLS", data)
|
|
36
|
+
|
|
37
|
+
def sync_print(self, templateCode, documents, **kwargs):
|
|
38
|
+
"""同步获取电子面单"""
|
|
39
|
+
res = self.print(templateCode, documents,**kwargs)
|
|
40
|
+
if not res['success']:
|
|
41
|
+
raise SheetException(f"Getting template error:{res['errorMessage']}")
|
|
42
|
+
files = []
|
|
43
|
+
for file in res['obj']['files']:
|
|
44
|
+
resp = requests.get(file['url'], headers={
|
|
45
|
+
"X-Auth-token": file['token']
|
|
46
|
+
})
|
|
47
|
+
files.append(resp.content)
|
|
48
|
+
return files
|
|
49
|
+
|
|
50
|
+
def get_custom_templates(self, sellerUserId, type=1, standardTemplateCode=None,):
|
|
51
|
+
"""
|
|
52
|
+
获取自定义面单编码
|
|
53
|
+
|
|
54
|
+
param sellerUserId: isv商家在isv平台注册的账号
|
|
55
|
+
param type: 查询类型,(1:商家自定义模板)
|
|
56
|
+
param standardTemplateCode: 标准模板code,取值查看 2.3.2,不传值时,则查询所有规格的
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
data = {
|
|
60
|
+
"sellerUserId": sellerUserId,
|
|
61
|
+
"type": type,
|
|
62
|
+
"standardTemplateCode": standardTemplateCode
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return self.post("COM_RECE_CLOUD_CUSTOMTEMPLATE_LIST", data)
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: sf-sdk
|
|
3
|
+
Version: 2.1.0.0
|
|
4
|
+
Summary: Shunfeng Express Python SDK
|
|
5
|
+
Home-page: https://github.com/block-cat/sf-sdk
|
|
6
|
+
Author: blackcat
|
|
7
|
+
Author-email: kfx2007@163.com
|
|
8
|
+
License: GNU
|
|
9
|
+
Keywords: sf sdk
|
|
10
|
+
Platform: UNKNOWN
|
|
11
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
|
|
12
|
+
Classifier: Programming Language :: Python
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
17
|
+
Classifier: Intended Audience :: Science/Research
|
|
18
|
+
Classifier: Intended Audience :: Developers
|
|
19
|
+
Classifier: Intended Audience :: Information Technology
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: requests
|
|
22
|
+
|
|
23
|
+
[](https://travis-ci.org/block-cat/sf-sdk)
|
|
24
|
+
[](https://coveralls.io/github/block-cat/sf-sdk?branch=master)
|
|
25
|
+
[](https://pypi.org/project/sf-sdk/)
|
|
26
|
+
|
|
27
|
+
# 顺丰 Python SDK
|
|
28
|
+
|
|
29
|
+
基于顺丰官网开放平台2.0 API开发的Python SDK
|
|
30
|
+
|
|
31
|
+
版本:2.1.0.0
|
|
32
|
+
|
|
33
|
+
## 功能概述
|
|
34
|
+
|
|
35
|
+
目前已经开发完成的接口列表:
|
|
36
|
+
|
|
37
|
+
* 下订单接口
|
|
38
|
+
* 订单确认/取消接口-速运类API
|
|
39
|
+
* 订单结果查询接口
|
|
40
|
+
* 路由查询接口接口-速运类API
|
|
41
|
+
* 订单筛选接口-速运类API
|
|
42
|
+
* 时效标准及价格查询接口-速运类API
|
|
43
|
+
* 产品推荐接口-速运类API
|
|
44
|
+
* 预估总费用接口-国际件API
|
|
45
|
+
* 清单运费查询接口-速运类API
|
|
46
|
+
|
|
47
|
+
其他接口正在陆续对接中...
|
|
48
|
+
|
|
49
|
+
## 安装
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
pip install sf-sdk
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 使用示例
|
|
56
|
+
|
|
57
|
+
clientcode和checkword是在顺丰官网注册后得到的用户编码和校验码
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from sf.api import SF
|
|
61
|
+
|
|
62
|
+
sf = SF("clientcode","checkword")
|
|
63
|
+
sf.order.create_order(clientid,..)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 下单
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
contacts = []
|
|
70
|
+
sender = ContactInfo("北京市昌平区回龙观天慧园",company="测试公司",mobile="18512345678",contactType=1)
|
|
71
|
+
receiver = ContactInfo("北京市海淀区新中关大厦A座",company="新东方",mobile="18511223344",contactType=2)
|
|
72
|
+
contacts.append(sender)
|
|
73
|
+
contacts.append(receiver)
|
|
74
|
+
cargo_detail = CargoDetail("测试货物")
|
|
75
|
+
res = self.sf.order.create_order(self.order_no, contacts,[cargo_detail])
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 订单查询
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
res = self.sf.order.get_order(self.order_no)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 确认/取消订单
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
res = self.sf.order.confirm_order(self.order_no, dealType=2)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 路由信息
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
res = self.sf.order.get_route_info(self.order_no)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 判断是否可以派单
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
res = self.sf.order.can_delivery(self.order_no)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 打印电子面单
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
res = self.sf.order.get_order(self.order_no)
|
|
106
|
+
documents = [
|
|
107
|
+
{
|
|
108
|
+
"masterWaybillNo": res['msgData']['waybillNoInfoList'][0]['waybillNo'],
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
res = self.sf.sheet.sync_print(f"fm_150_standard_QXH",documents)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 产品推荐与预估费用
|
|
115
|
+
|
|
116
|
+
产品推荐接口需要先开通 `EXP_RECE_PSDS_PRODUCT_RECOMMEND` 权限。返回值中的
|
|
117
|
+
`msgData.productList` 会包含 `totalFee`、`currency` 和 `serviceFeeList`;其中
|
|
118
|
+
燃油附加费的服务代码为 `IN15`。
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
res = sf.order.recommend_products(
|
|
122
|
+
srcProvince="香港",
|
|
123
|
+
srcCity="香港",
|
|
124
|
+
destProvince="澳门",
|
|
125
|
+
destCity="澳门",
|
|
126
|
+
sendTime="2026-09-04 12:00:00",
|
|
127
|
+
weight=1,
|
|
128
|
+
paymentTerms="1",
|
|
129
|
+
srcAddress="香港葵涌永建路",
|
|
130
|
+
destAddress="澳门半岛南湾大马路",
|
|
131
|
+
monthlyCard="your-monthly-card",
|
|
132
|
+
commodityNameList=["文件"],
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
国际件预估总费用接口会返回 `currency`、`totalFee` 和 `feeInfoList`,需要单独
|
|
137
|
+
申请对应的国际接口权限。
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
res = sf.order.estimate_total_fee(
|
|
141
|
+
customerCode="your-international-customer-code",
|
|
142
|
+
interProductCode="INT0001",
|
|
143
|
+
senderInfo={"country": "HK", "postCode": "999077", "address": "Kwai Chung"},
|
|
144
|
+
receiverInfo={"country": "MO", "postCode": "999078", "address": "Macau"},
|
|
145
|
+
parcelQuantity=1,
|
|
146
|
+
parcelTotalWeight=1,
|
|
147
|
+
)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 清单运费查询
|
|
151
|
+
|
|
152
|
+
`trackingType` 为 `1` 时按客户订单号查询,为 `2` 时按顺丰运单号查询。
|
|
153
|
+
返回值中的 `msgData.waybillFeeList` 是实际费用明细,燃油附加费的费用类型为
|
|
154
|
+
`14`。
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
res = sf.order.query_waybill_fee(
|
|
158
|
+
trackingType=2,
|
|
159
|
+
trackingNum="SF1234567890",
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 沙箱联调
|
|
164
|
+
|
|
165
|
+
实时沙箱测试默认跳过。需要运行时通过环境变量提供凭据,凭据不会写入代码:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
export SF_SANDBOX_CLIENT_CODE="your-client-code"
|
|
169
|
+
export SF_SANDBOX_CHECKWORD="your-checkword"
|
|
170
|
+
pytest
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
sf/__init__.py,sha256=1GX8aoFp-7kCmUb1Osf0s_UrL_1Cak-O5IYzbfdBr0U,137
|
|
2
|
+
sf/api.py,sha256=QdvIWDGoQCojk1NwWXI1SEcgAntjiyRiBNQJ-19Id2k,858
|
|
3
|
+
sf/comm/__init__.py,sha256=6JdNyDcIRDfiiofuryu1Arq1oNRLBr75Vhts48BrcXA,45
|
|
4
|
+
sf/comm/comm.py,sha256=AF9vSApk_Li9uq4s72tO9ZLB56KbmmYlyank8pzgPmM,2460
|
|
5
|
+
sf/comm/exceptions.py,sha256=xs6fgmuEvNxnEziS2OtmTVp6CuIdpo1pmJc-F1swG-k,128
|
|
6
|
+
sf/comm/token.py,sha256=KnNVBhlAiggHaIpSyJVxSGYueS6DfrRIAxaJA9UlAhk,221
|
|
7
|
+
sf/data/express_type.csv,sha256=OGb3Rscm29bEBK7ik4hKJnpVLWCSWobz4vqIhELmiVM,1143
|
|
8
|
+
sf/model/__init__.py,sha256=h76cagNALAFU8nlqng20eehKDZCChVO8MictLYYIfHw,171
|
|
9
|
+
sf/model/address.py,sha256=6pgS5yXLGT7wCqTQRdzU9RVzIie8KCVjIWfeR-OJO3w,1105
|
|
10
|
+
sf/model/cargo.py,sha256=W4aWA7ATdWjcoSNsg8dwsxkTZKywwthg-QVJ2hu_U5E,4794
|
|
11
|
+
sf/model/contact.py,sha256=7aHgiEe3c9hmp-cy5fwLdWvYj8UDZ1U-0pvLfQ0DLfA,1375
|
|
12
|
+
sf/model/service.py,sha256=WQHQxCBVkZOYGbxeQkQoaYBap153gkGeJM-6U9LwATY,760
|
|
13
|
+
sf/order/__init__.py,sha256=0wUPsj5waTgiZCoGN5Ed6ZEhkkdKHhsFnWh_7RxRkPA,42
|
|
14
|
+
sf/order/order.py,sha256=Go0LreSHK6Y5FBAI8nXQninxT2S-Zk4BaD5wsD9Ny3s,18181
|
|
15
|
+
sf/order/sheet.py,sha256=hmJKDjLxXRjovYiMSGpDXiCx8fCvZAxFnNcHyJgY0oE,2722
|
|
16
|
+
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
tests/test_fee_apis.py,sha256=cxz0QKPWmopXhGlMpFMDJvMZMntR7cg3DLMLFshIlco,3146
|
|
18
|
+
tests/test_order.py,sha256=VPBwlsCG5m4YO8s51pEeyApNeKZWk5zRSMsMnJ-9Wr0,6204
|
|
19
|
+
sf_sdk-2.1.0.0.dist-info/METADATA,sha256=GpujS51rKdz1BRNZGMBnNNvh0_1ePhZKHCqA4RivNr4,4645
|
|
20
|
+
sf_sdk-2.1.0.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
21
|
+
sf_sdk-2.1.0.0.dist-info/top_level.txt,sha256=Wzp9OGy9dpK4Rp9m5ctKYX8HGCOSrRJc8eZLxHowLL8,9
|
|
22
|
+
sf_sdk-2.1.0.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
23
|
+
sf_sdk-2.1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
tests/__init__.py
ADDED
|
File without changes
|
tests/test_fee_apis.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
|
|
3
|
+
import unittest
|
|
4
|
+
from unittest.mock import Mock
|
|
5
|
+
|
|
6
|
+
from sf.order.order import Order
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestFeeApiPayloads(unittest.TestCase):
|
|
10
|
+
|
|
11
|
+
def setUp(self):
|
|
12
|
+
self.order = Order()
|
|
13
|
+
self.order.post = Mock(return_value={"success": True})
|
|
14
|
+
|
|
15
|
+
def test_recommend_products_payload(self):
|
|
16
|
+
result = self.order.recommend_products(
|
|
17
|
+
srcProvince="香港",
|
|
18
|
+
srcCity="香港",
|
|
19
|
+
destProvince="澳门",
|
|
20
|
+
destCity="澳门",
|
|
21
|
+
sendTime="2026-09-04 12:00:00",
|
|
22
|
+
weight=1,
|
|
23
|
+
paymentTerms="1",
|
|
24
|
+
srcAddress="香港葵涌永建路",
|
|
25
|
+
destAddress="澳门半岛南湾大马路",
|
|
26
|
+
monthlyCard="test-monthly-card",
|
|
27
|
+
commodityNameList=["文件"],
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
self.assertEqual(result, {"success": True})
|
|
31
|
+
self.order.post.assert_called_once_with(
|
|
32
|
+
"EXP_RECE_PSDS_PRODUCT_RECOMMEND",
|
|
33
|
+
{
|
|
34
|
+
"srcProvince": "香港",
|
|
35
|
+
"srcCity": "香港",
|
|
36
|
+
"destProvince": "澳门",
|
|
37
|
+
"destCity": "澳门",
|
|
38
|
+
"sendTime": "2026-09-04 12:00:00",
|
|
39
|
+
"weight": 1,
|
|
40
|
+
"paymentTerms": "1",
|
|
41
|
+
"srcAddress": "香港葵涌永建路",
|
|
42
|
+
"destAddress": "澳门半岛南湾大马路",
|
|
43
|
+
"monthlyCard": "test-monthly-card",
|
|
44
|
+
"commodityNameList": ["文件"],
|
|
45
|
+
},
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def test_estimate_total_fee_payload(self):
|
|
49
|
+
sender = {
|
|
50
|
+
"country": "HK",
|
|
51
|
+
"postCode": "999077",
|
|
52
|
+
"address": "Kwai Chung",
|
|
53
|
+
}
|
|
54
|
+
receiver = {
|
|
55
|
+
"country": "MO",
|
|
56
|
+
"postCode": "999078",
|
|
57
|
+
"address": "Macau Peninsula",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
result = self.order.estimate_total_fee(
|
|
61
|
+
customerCode="test-customer",
|
|
62
|
+
interProductCode="INT0001",
|
|
63
|
+
senderInfo=sender,
|
|
64
|
+
receiverInfo=receiver,
|
|
65
|
+
parcelQuantity=1,
|
|
66
|
+
parcelTotalWeight=1,
|
|
67
|
+
declaredCurrency="HKD",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
self.assertEqual(result, {"success": True})
|
|
71
|
+
self.order.post.assert_called_once_with(
|
|
72
|
+
"COM_RECE_IUOP_ESTIMATE_FEE",
|
|
73
|
+
{
|
|
74
|
+
"customerCode": "test-customer",
|
|
75
|
+
"interProductCode": "INT0001",
|
|
76
|
+
"senderInfo": sender,
|
|
77
|
+
"receiverInfo": receiver,
|
|
78
|
+
"parcelQuantity": 1,
|
|
79
|
+
"parcelTotalWeight": 1,
|
|
80
|
+
"declaredCurrency": "HKD",
|
|
81
|
+
},
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def test_query_waybill_fee_payload(self):
|
|
85
|
+
result = self.order.query_waybill_fee(
|
|
86
|
+
trackingType=2,
|
|
87
|
+
trackingNum="SF1234567890",
|
|
88
|
+
bizTemplateCode="test-template",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
self.assertEqual(result, {"success": True})
|
|
92
|
+
self.order.post.assert_called_once_with(
|
|
93
|
+
"EXP_RECE_QUERY_SFWAYBILL",
|
|
94
|
+
{
|
|
95
|
+
"trackingType": "2",
|
|
96
|
+
"trackingNum": "SF1234567890",
|
|
97
|
+
"bizTemplateCode": "test-template",
|
|
98
|
+
},
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
if __name__ == "__main__":
|
|
103
|
+
unittest.main()
|
tests/test_order.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# @Time : 2019-08-21
|
|
3
|
+
# @Author : Kevin Kong (kfx2007@163.com)
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import unittest
|
|
7
|
+
import uuid
|
|
8
|
+
from sf.api import SF
|
|
9
|
+
from sf.model.contact import ContactInfo
|
|
10
|
+
from sf.model.cargo import CargoDetail
|
|
11
|
+
from sf.model.address import Address
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestOrder(unittest.TestCase):
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def setUpClass(cls):
|
|
18
|
+
clientcode = os.environ.get("SF_SANDBOX_CLIENT_CODE")
|
|
19
|
+
checkword = os.environ.get("SF_SANDBOX_CHECKWORD")
|
|
20
|
+
if not clientcode or not checkword:
|
|
21
|
+
raise unittest.SkipTest(
|
|
22
|
+
"Set SF_SANDBOX_CLIENT_CODE and SF_SANDBOX_CHECKWORD "
|
|
23
|
+
"to run live sandbox tests."
|
|
24
|
+
)
|
|
25
|
+
cls.sf = SF(clientcode, checkword, True, 'en')
|
|
26
|
+
cls.order_no = uuid.uuid4().hex[:12]
|
|
27
|
+
cls.mail_no = None
|
|
28
|
+
|
|
29
|
+
def _create_order(self):
|
|
30
|
+
contacts = []
|
|
31
|
+
sender = ContactInfo("北京市昌平区回龙观天慧园",company="测试公司",mobile="18512345678")
|
|
32
|
+
receiver = ContactInfo("北京市海淀区新中关大厦A座",company="新东方",mobile="18511223344",contactType=1)
|
|
33
|
+
contacts.append(sender)
|
|
34
|
+
contacts.append(receiver)
|
|
35
|
+
cargo_detail = CargoDetail("测试货物")
|
|
36
|
+
return self.sf.order.create_order(self.order_no, contacts,[cargo_detail])
|
|
37
|
+
|
|
38
|
+
def test_1_create_order(self):
|
|
39
|
+
"""测试下单"""
|
|
40
|
+
res = self._create_order()
|
|
41
|
+
self.assertEqual(res["success"], True, res)
|
|
42
|
+
|
|
43
|
+
def test_2_get_order(self):
|
|
44
|
+
"""测试订单查询接口"""
|
|
45
|
+
res = self.sf.order.get_order(self.order_no)
|
|
46
|
+
self.assertEqual(res["success"], True, res)
|
|
47
|
+
|
|
48
|
+
def test_99_cancel_order(self):
|
|
49
|
+
"""测试取消订单"""
|
|
50
|
+
res = self.sf.order.confirm_order(self.order_no, dealType=2)
|
|
51
|
+
self.assertEqual(res["success"], True, res)
|
|
52
|
+
|
|
53
|
+
def test_4_get_router(self):
|
|
54
|
+
"""测试路由信息"""
|
|
55
|
+
#{'apiErrorMsg': '', 'apiResponseID': '000184DB8E57803FE6AA43A0CAC24D3F',
|
|
56
|
+
# 'apiResultCode': 'A1000', 'apiResultData':
|
|
57
|
+
# '{"success":true,"errorCode":"S0000","errorMsg":null,
|
|
58
|
+
# "msgData":{"routeResps":
|
|
59
|
+
# [{"mailNo":"066106553309","routes":[{"acceptAddress":"深圳市","acceptTime":"2022-12-04 13:14:09","remark":"顺丰速运 已收取快件","opCode":"50"
|
|
60
|
+
# }]}]}}'}
|
|
61
|
+
# 只有先下单 才能拿得到路由信息 否则是空
|
|
62
|
+
contacts = []
|
|
63
|
+
sender = ContactInfo("北京市昌平区回龙观天慧园",company="测试公司",mobile="18512345678")
|
|
64
|
+
receiver = ContactInfo("北京市海淀区新中关大厦A座",company="新东方",mobile="18511223344",contactType=1)
|
|
65
|
+
contacts.append(sender)
|
|
66
|
+
contacts.append(receiver)
|
|
67
|
+
cargo_detail = CargoDetail("测试货物")
|
|
68
|
+
self.sf.order.create_order(uuid.uuid4().hex[:12], contacts,[cargo_detail])
|
|
69
|
+
res = self.sf.order.get_route_info(self.order_no)
|
|
70
|
+
self.assertTrue(res['success'], res)
|
|
71
|
+
|
|
72
|
+
def test_5_can_deliver(self):
|
|
73
|
+
"""是否可以收派"""
|
|
74
|
+
res = self.sf.order.can_delivery(self.order_no)
|
|
75
|
+
self.assertTrue(res['success'], res)
|
|
76
|
+
|
|
77
|
+
def test_0_access_token(self):
|
|
78
|
+
"""测试获取AccessToken"""
|
|
79
|
+
access_token = self.sf.comm.get_access_token()
|
|
80
|
+
self.assertNotEqual(access_token, None)
|
|
81
|
+
|
|
82
|
+
def test_6_print(self):
|
|
83
|
+
""""测试电子面单"""
|
|
84
|
+
# [{'areaNo': 1, 'pageNo': 1, 'seqNo': 1, '
|
|
85
|
+
# token': 'AUTH_tkv12_f146d1855480549d262b5c46ab0ab597ff20a97d9d0db45c16bedeb4fabd112b012deadd477ee524b1d690ce01baa3cdffbb125a6ccf69b73778dba2eb5157eb73744dffd1a1bbe5c4390bfb93ce4c3e9197d359c3ace1b33151306bc9e831ab67720381919951fc04fd23bb349f18cda712e03cdb9961c933bba7d6177fa548e4a43f0933ce2b32089181f98d7c23a786969366c357d7da3f10ac20f099e909e8a27d9f175808a32056e0c85df11786d9c10d75656ea37df97b385c9c79c546',
|
|
86
|
+
# 'url': 'https://eos-scp-core-shenzhen-futian1-oss.sf-express.com:443/v1.2/AUTH_EOS-SCP-CORE/print-file-sbox/QXH_e45c4058-6972-4605-9182-e337814f5dff_SF7444462031543_fm_150_standard_QXH_1_1_1.pdf',
|
|
87
|
+
# 'waybillNo': 'SF7444462031543'}]
|
|
88
|
+
res = self.sf.order.get_order(self.order_no)
|
|
89
|
+
documents = [
|
|
90
|
+
{
|
|
91
|
+
"masterWaybillNo": res['msgData']['waybillNoInfoList'][0]['waybillNo'],
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
res = self.sf.sheet.sync_print(f"fm_150_standard_{self.sf.clientcode}",documents)
|
|
95
|
+
self.assertTrue(len(res[0]), res)
|
|
96
|
+
|
|
97
|
+
def test_7_get_express_types(self):
|
|
98
|
+
res = SF.get_express_types()
|
|
99
|
+
self.assertTrue(len(res), res)
|
|
100
|
+
|
|
101
|
+
def test_8_get_custom_templates(self):
|
|
102
|
+
res = self.sf.sheet.get_custom_templates("123456",0)
|
|
103
|
+
self.assertTrue(res['success'],res)
|
|
104
|
+
|
|
105
|
+
def test_9_query_delivery(self):
|
|
106
|
+
src_address = Address("北京市","北京市","昌平区","回龙观大街")
|
|
107
|
+
dest_address = Address("山东省","青岛市","市北区","万达广场")
|
|
108
|
+
res = self.sf.order.query_delivery(1,src_address, dest_address)
|
|
109
|
+
self.assertTrue(res['success'],res)
|
|
110
|
+
|
|
111
|
+
def test_10_hk_mau(self):
|
|
112
|
+
src_address = Address("北京市","北京市","昌平区","回龙观大街")
|
|
113
|
+
dest_address = Address("台湾","新竹市","市北区","万达广场")
|
|
114
|
+
res = self.sf.order.query_delivery(1,src_address, dest_address)
|
|
115
|
+
self.assertTrue(res['success'],res)
|
|
116
|
+
|
|
117
|
+
def test_11_create_suborders(self):
|
|
118
|
+
res = self.sf.order.create_sub_order(self.order_no, 3)
|
|
119
|
+
self.assertTrue(res['success'], res)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
if __name__ == "__main__":
|
|
123
|
+
suite = unittest.TestSuite()
|
|
124
|
+
suite.addTest(TestOrder("test_0_access_token"))
|
|
125
|
+
suite.addTest(TestOrder("test_1_create_order"))
|
|
126
|
+
suite.addTest(TestOrder("test_2_get_order"))
|
|
127
|
+
suite.addTest(TestOrder("test_4_get_router"))
|
|
128
|
+
suite.addTest(TestOrder("test_5_can_deliver"))
|
|
129
|
+
suite.addTest(TestOrder("test_6_print"))
|
|
130
|
+
suite.addTest(TestOrder("test_99_cancel_order"))
|
|
131
|
+
suite.addTest(TestOrder("test_7_get_express_types"))
|
|
132
|
+
suite.addTest(TestOrder("test_8_get_custom_templates"))
|
|
133
|
+
suite.addTest(TestOrder("test_9_query_delivery"))
|
|
134
|
+
suite.addTest(TestOrder("test_10_hk_mau"))
|
|
135
|
+
suite.addTest(TestOrder("test_11_create_suborders"))
|
|
136
|
+
runner = unittest.TextTestRunner(verbosity=2)
|
|
137
|
+
runner.run(suite)
|