854 lines
34 KiB
Python
854 lines
34 KiB
Python
""" 美国尾端物流模块实现类"""
|
||
import math
|
||
import re
|
||
import pandas
|
||
from logisticsClass.logisticsBaseClass import LogisticsType, TailLogistics
|
||
from data.us_zone import zone_west, zone_east
|
||
from pathlib import Path
|
||
|
||
"""
|
||
port:west(default),east
|
||
currency:str = 'USD'
|
||
logistics_type:LogisticsType (快递,卡派)
|
||
"""
|
||
class WestLogistics_US(TailLogistics):
|
||
_is_loaded = False
|
||
parent_current_directory = Path(__file__).parent.parent
|
||
remote_path = parent_current_directory.joinpath("data")
|
||
_postcode_files = [
|
||
remote_path.joinpath("contiguous_us.txt"),
|
||
remote_path.joinpath("contiguous_us_extended.txt"),
|
||
remote_path.joinpath("contiguous_us_remote.txt")
|
||
]
|
||
|
||
def __new__(cls):
|
||
"""实现单例模式,只加载一次文件"""
|
||
if not cls._is_loaded:
|
||
cls._load_postcodes() # 第一次实例化时加载文件
|
||
cls._is_loaded = True # 标记文件已加载
|
||
return super().__new__(cls)
|
||
|
||
@classmethod
|
||
def _load_postcodes(cls):
|
||
"""加载邮编文件"""
|
||
cls.postcodes_by_file = {
|
||
1: set(), # 用于存储contiguous_us.txt的邮政编码
|
||
2: set(), # 用于存储contiguous_us_extended.txt的邮政编码
|
||
3: set() # 用于存储contiguous_us_remote.txt的邮政编码
|
||
}
|
||
# 加载每个文件,并将邮政编码存入对应的集合中
|
||
for index, file in enumerate(cls._postcode_files, start=1):
|
||
with open(file, "r", encoding="utf-8") as f:
|
||
for line in f:
|
||
cls.postcodes_by_file[index].add(line.strip())
|
||
|
||
def is_remote(self, postcode):
|
||
"""判断是否偏远 0非偏远,1偏远,2超偏远Extend,3超偏远Remote"""
|
||
if not re.match(r'\d{5}-\d{4}|\d{5}', postcode):
|
||
raise ValueError("邮编格式不合法")
|
||
if not hasattr(self, 'postcodes_by_file'):
|
||
raise ValueError("Postcodes are not loaded yet.")
|
||
postcode = postcode[:5]
|
||
postcode = str(postcode)
|
||
for index, postcodes in self.postcodes_by_file.items():
|
||
for line in postcodes:
|
||
postcodes_range = line.strip().split("\t")
|
||
if postcode in postcodes_range:
|
||
return index
|
||
return 0
|
||
|
||
def get_west_zone(self, postcode):
|
||
"""获取美西邮编分区"""
|
||
if not re.match(r'\d{5}-\d{4}|\d{5}', postcode):
|
||
return "邮编格式不合法"
|
||
postcode = postcode[:5]
|
||
postcode = int(postcode)
|
||
for zone, postcodes in zone_west.items():
|
||
start =int(postcodes[0].split("-")[0])
|
||
end = int(postcodes[-1].split("-")[-1])
|
||
if start > postcode or postcode > end:
|
||
continue
|
||
for postcode_range in postcodes:
|
||
if "-" in postcode_range:
|
||
start, end = map(int, postcode_range.split("-"))
|
||
if start <= postcode <= end:
|
||
return zone
|
||
else:
|
||
if int(zone) == postcode:
|
||
return zone
|
||
return "未查询到邮编分区"
|
||
|
||
|
||
class FedexLogistics(WestLogistics_US):
|
||
"""Fedex"""
|
||
country = "United States"
|
||
country_code = "US"
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.volume_weight_ratio:int # lbs抛重系数
|
||
self.base_fee:dict
|
||
self.residential:float
|
||
self.residential_peak:float # 报价表没写,账单有
|
||
self.oversize_2:float
|
||
self.oversize_3:float
|
||
self.oversize_5:float
|
||
self.oversize_7:float
|
||
self.overweight_2:float
|
||
self.overweight_3:float
|
||
self.overweight_5:float
|
||
self.overweight_7:float
|
||
self.overhanding_2:float # 额外操作费 这个可能用不上。
|
||
self.overhanding_3:float
|
||
self.overhanding_5:float
|
||
self.overhanding_7:float
|
||
self.ahs_peak:float # 额外处理费旺季附加费
|
||
self.residential_das:float # 偏远费
|
||
self.residential_extended:float # 超偏远费
|
||
self.residential_remote:float # 超超偏远费
|
||
self.bigpackage_2:float # 大包裹费
|
||
self.bigpackage_3:float
|
||
self.bigpackage_5:float
|
||
self.bigpackage_7:float
|
||
self.bigpackage_peak:float # 大包裹旺季附加费
|
||
self.return_package:float # 超大包裹(不可发)
|
||
self.fuel_rate = 0.16 # 燃油费率
|
||
|
||
def calculate_fee(self,packages,postcode):
|
||
"""计算运费,抛重计费,系数275"""
|
||
detail_amount = {
|
||
"base":0.00,
|
||
"oversize":0.00,
|
||
"remote":0.00,
|
||
"big_package":0.00,
|
||
"big_package_peak":0.00,
|
||
"residential_delivery":0.00,
|
||
"residential_peak":0.00,
|
||
"fuel":0.00,
|
||
"tail_amount":0.00
|
||
}
|
||
# 先看分区
|
||
zone = self.get_west_zone(postcode)
|
||
if zone == 2:
|
||
ahs_oversize = self.oversize_2
|
||
ahs_overweight = self.overweight_2
|
||
ahs_overhanding = self.overhanding_2
|
||
big_oversize = self.bigpackage_2
|
||
elif zone == 3 or zone == 4:
|
||
ahs_oversize = self.oversize_3
|
||
ahs_overweight = self.overweight_3
|
||
ahs_overhanding = self.overhanding_3
|
||
big_oversize = self.bigpackage_3
|
||
elif zone == 5 or zone == 6:
|
||
ahs_oversize = self.oversize_5
|
||
ahs_overweight = self.overweight_5
|
||
ahs_overhanding = self.overhanding_5
|
||
big_oversize = self.bigpackage_5
|
||
elif zone == 7 or zone == 8:
|
||
ahs_oversize = self.oversize_7
|
||
ahs_overweight = self.overweight_7
|
||
ahs_overhanding = self.overhanding_7
|
||
big_oversize = self.bigpackage_7
|
||
else:
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
# 再看是否偏远 0非偏远,1偏远,2超偏远Extend,3超偏远Remote
|
||
is_remote = self.is_remote(postcode)
|
||
if is_remote == 1:
|
||
remote_fee = self.residential_das
|
||
elif is_remote ==2:
|
||
remote_fee = self.residential_extended
|
||
elif is_remote ==3:
|
||
remote_fee = self.residential_remote
|
||
else:
|
||
remote_fee = 0
|
||
|
||
for package in packages:
|
||
if package.lbs_weight>150 or package.fst_inch>108 or package.girth_inch >165:
|
||
detail_amount['tail_amount'] = self.return_package
|
||
return detail_amount
|
||
cal_weight = max(package.lbs_weight,package.volume_inch / self.volume_weight_ratio)
|
||
ahs_fee = 0 # AHS费系列,多种存在只取最高
|
||
if package.fst_inch > 48 or package.sed_inch> 30 or package.girth_inch>105:
|
||
ahs_fee = ahs_oversize +self.ahs_peak
|
||
cal_weight = max(cal_weight,40)
|
||
if package.lbs_weight > 50:
|
||
ahs_fee = max(ahs_fee,ahs_overweight +self.ahs_peak)
|
||
if package.girth_inch >130 or package.fst_inch >96:
|
||
cal_weight = max(cal_weight,90)
|
||
if cal_weight > 150:
|
||
detail_amount['tail_amount'] = self.return_package
|
||
return detail_amount
|
||
detail_amount['oversize'] += 0 if package.girth_inch >130 or package.fst_inch >96 else ahs_fee
|
||
detail_amount['remote'] += remote_fee
|
||
detail_amount['big_package'] += big_oversize if package.girth_inch >130 or package.fst_inch >96 else 0
|
||
detail_amount['big_package_peak'] += self.bigpackage_peak if package.girth_inch >130 or package.fst_inch >96 else 0
|
||
detail_amount['residential_delivery'] += self.residential
|
||
detail_amount['residential_peak'] += self.residential_peak
|
||
|
||
detail_amount['base'] +=self.base_price[self.base_price['lbs.']==math.ceil(cal_weight)][zone].values[0]
|
||
|
||
for key in detail_amount:
|
||
if key!= 'tail_amount' and key!= 'fuel':
|
||
detail_amount['tail_amount'] += detail_amount[key]
|
||
detail_amount['fuel'] += detail_amount[key] * self.fuel_rate
|
||
detail_amount['tail_amount'] += detail_amount['fuel']
|
||
return detail_amount
|
||
|
||
class FedexPPLogistics_US(FedexLogistics):
|
||
company="Fedex-彩虹小马"
|
||
|
||
parent_current_directory = Path(__file__).parent.parent
|
||
price_path = parent_current_directory.joinpath("data")
|
||
_price_files = price_path.joinpath("美国快递.xlsx")
|
||
base_price = None
|
||
def __new__(cls):
|
||
if cls.base_price is None:
|
||
cls.base_price = pandas.read_excel(cls._price_files,sheet_name='邮差小马')
|
||
return super().__new__(cls)
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.volume_weight_ratio=250 # lbs抛重系数
|
||
self.residential = 6.38
|
||
self.residential_peak = 0 # 0.33 0.6 # 报价表没写,账单有
|
||
self.oversize_2 = 4.50
|
||
self.oversize_3 = 4.99
|
||
self.oversize_5 = 5.47
|
||
self.oversize_7 = 6.11
|
||
self.overweight_2 =6.99
|
||
self.overweight_3 = 7.64
|
||
self.overweight_5 =8.12
|
||
self.overweight_7 = 8.84
|
||
self.overhanding_2 = 4.02 # 额外操作费
|
||
self.overhanding_3 = 4.66
|
||
self.overhanding_5 = 4.91
|
||
self.overhanding_7 = 5.07
|
||
self.ahs_peak = 0 # 4.16 5.36 # 操作费旺季附加费
|
||
self.residential_das = 1.58# 偏远费
|
||
self.residential_extended = 1.97 # 超偏远费
|
||
self.residential_remote = 5.82 # 超超偏远费
|
||
self.bigpackage_2 = 32.94 # 大包裹费
|
||
self.bigpackage_3 = 36.16
|
||
self.bigpackage_5 = 38.57
|
||
self.bigpackage_7 = 41.78
|
||
self.bigpackage_peak = 0 # 45.26 53.56# 大包裹旺季附加费
|
||
self.fuel_rate = 0.16 # 燃油费率
|
||
self.return_package = 1419.34 # 超大包裹(不可发)
|
||
|
||
class FedexKHLogistics_US(FedexLogistics):
|
||
"""金宏亚"""
|
||
company = "Fedex-金宏亚"
|
||
|
||
parent_current_directory = Path(__file__).parent.parent
|
||
price_path = parent_current_directory.joinpath("data")
|
||
_price_files = price_path.joinpath("美国快递.xlsx")
|
||
base_price = None
|
||
def __new__(cls):
|
||
if cls.base_price is None:
|
||
cls.base_price = pandas.read_excel(cls._price_files,sheet_name='金宏亚')
|
||
return super().__new__(cls)
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.volume_weight_ratio = 250 # lbs抛重系数
|
||
self.residential = 2.1
|
||
self.residential_peak =0 # 0.57 0.31 # 报价表没写,账单有
|
||
self.oversize_2 = 2.3
|
||
self.oversize_3 = 2.6
|
||
self.oversize_5 = 2.9
|
||
self.oversize_7 = 3.2
|
||
self.overweight_2 =3.6
|
||
self.overweight_3 = 3.9
|
||
self.overweight_5 =4.2
|
||
self.overweight_7 =4.6
|
||
self.overhanding_2 = 3.1 # 额外操作费
|
||
self.overhanding_3 = 3.6
|
||
self.overhanding_5 = 3.8
|
||
self.overhanding_7 = 3.9
|
||
self.ahs_peak = 0 #1.3 # 操作费旺季附加费
|
||
self.residential_das = 2.26 # 偏远费
|
||
self.residential_extended = 3.02 # 超偏远费
|
||
self.residential_remote = 5.64# 超超偏远费
|
||
self.bigpackage_2 = 120 # 大包裹费
|
||
self.bigpackage_3 = 130
|
||
self.bigpackage_5 = 145
|
||
self.bigpackage_7 = 152.5
|
||
self.bigpackage_peak =43.94 # 42.25# 大包裹旺季附加费
|
||
self.return_package = 1325 # 超大包裹(不可发)
|
||
self.fuel_rate = 0.16 # 燃油费率
|
||
|
||
class FedexHOMELogistics_US(FedexLogistics):
|
||
"""FEDEX-HOME (1-35%-30%)"""
|
||
company = "Fedex-HOME"
|
||
|
||
parent_current_directory = Path(__file__).parent.parent
|
||
price_path = parent_current_directory.joinpath("data")
|
||
_price_files = price_path.joinpath("美国快递.xlsx")
|
||
base_price = None
|
||
def __new__(cls):
|
||
if cls.base_price is None:
|
||
cls.base_price = pandas.read_excel(cls._price_files,sheet_name='FEDEX')
|
||
return super().__new__(cls)
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.volume_weight_ratio = 250 # lbs抛重系数
|
||
self.residential = 2.08
|
||
self.residential_peak =0 # 0.57 0.31 # 报价表没写,账单有
|
||
self.oversize_2 = 6.02
|
||
self.oversize_3 = 6.67
|
||
self.oversize_5 = 7.31
|
||
self.oversize_7 = 8.17
|
||
self.overweight_2 =9.35
|
||
self.overweight_3 = 10.21
|
||
self.overweight_5 =10.86
|
||
self.overweight_7 = 11.83
|
||
self.overhanding_2 = 5.375# 额外操作费
|
||
self.overhanding_3 = 6.235
|
||
self.overhanding_5 = 6.5575
|
||
self.overhanding_7 = 6.7725
|
||
self.ahs_peak = 0 #1.3 # 操作费旺季附加费
|
||
self.residential_das = 2.17 # 偏远费
|
||
self.residential_extended = 2.91 # 超偏远费
|
||
self.residential_remote = 5.43# 超超偏远费
|
||
self.bigpackage_2 = 45.58 # 大包裹费
|
||
self.bigpackage_3 = 49.28
|
||
self.bigpackage_5 = 55.04
|
||
self.bigpackage_7 = 57.405
|
||
self.bigpackage_peak =0 # 42.25# 大包裹旺季附加费
|
||
self.return_package = 1325 # 超大包裹(不可发)
|
||
self.fuel_rate = 0.18 # 燃油费率
|
||
class FedexGROUDLogistics_US(FedexLogistics):
|
||
"""FEDEX-GROUD (1-35%-30%)"""
|
||
company = "Fedex-GROUD"
|
||
|
||
parent_current_directory = Path(__file__).parent.parent
|
||
price_path = parent_current_directory.joinpath("data")
|
||
_price_files = price_path.joinpath("美国快递.xlsx")
|
||
base_price = None
|
||
def __new__(cls):
|
||
if cls.base_price is None:
|
||
cls.base_price = pandas.read_excel(cls._price_files,sheet_name='FEDEX')
|
||
return super().__new__(cls)
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.volume_weight_ratio = 250 # lbs抛重系数
|
||
self.residential = 5.95
|
||
self.residential_peak =0 # 0.57 0.31 # 报价表没写,账单有
|
||
self.oversize_2 = 6.02
|
||
self.oversize_3 = 6.67
|
||
self.oversize_5 = 7.31
|
||
self.oversize_7 = 8.17
|
||
self.overweight_2 =9.35
|
||
self.overweight_3 = 10.21
|
||
self.overweight_5 =10.86
|
||
self.overweight_7 = 11.83
|
||
self.overhanding_2 = 5.375# 额外操作费
|
||
self.overhanding_3 = 6.235
|
||
self.overhanding_5 = 6.5575
|
||
self.overhanding_7 = 6.7725
|
||
self.ahs_peak = 0 #1.3 # 操作费旺季附加费
|
||
self.residential_das = 6.2 # 偏远费
|
||
self.residential_extended = 8.3 # 超偏远费
|
||
self.residential_remote = 15.5# 超超偏远费
|
||
self.bigpackage_2 = 38.06 # 大包裹费
|
||
self.bigpackage_3 = 41.76
|
||
self.bigpackage_5 = 44.29
|
||
self.bigpackage_7 = 47.73
|
||
self.bigpackage_peak =0 # 42.25# 大包裹旺季附加费
|
||
self.return_package = 1325 # 超大包裹(不可发)
|
||
self.fuel_rate = 0.18 # 燃油费率
|
||
|
||
class GIGALogistics_US(TailLogistics):
|
||
"""卡派:GIGA,美东美西不变"""
|
||
country = "United States"
|
||
country_code = "US"
|
||
company = "大健-GIGA"
|
||
logistics_type = LogisticsType.LTL
|
||
|
||
parent_current_directory = Path(__file__).parent.parent
|
||
data_path = parent_current_directory.joinpath("data")
|
||
_price_files = data_path.joinpath("GIGA base_fee_20240607223514.xlsx")
|
||
price_df = None
|
||
|
||
def __new__(cls):
|
||
"""实现单例模式,只加载一次文件"""
|
||
if cls.price_df is None:
|
||
cls.price_df = pandas.read_excel(cls._price_files)
|
||
return super().__new__(cls)
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.base_fee:float
|
||
self.warehouse_fee:float
|
||
self.discount = 0.88
|
||
|
||
def calculate_fee(self,packages,postcode):
|
||
"""计算运费,基础费用只跟邮编有关"""
|
||
detail_amount = {
|
||
"base":0.00,
|
||
"warehouse_fee":0.00,
|
||
"discount":0.00,
|
||
"tail_amount":0.00
|
||
}
|
||
if not re.match(r'\d{5}-\d{4}|\d{5}', postcode):
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
postcode = int(postcode[:5])
|
||
base_result = self.price_df[self.price_df['Zip Code']==postcode]['Delivery Fee Rate']
|
||
if base_result.empty:
|
||
detail_amount['tail_amount'] =99999
|
||
return detail_amount
|
||
self.base_fee = base_result.iloc[0]
|
||
self.warehouse_fee = self.price_df[self.price_df['Zip Code']==postcode]['Warehouse Handling Fee'].iloc[0]
|
||
detail_amount['base'] = self.base_fee *self.discount
|
||
detail_amount['warehouse_fee'] = self.warehouse_fee * self.discount
|
||
detail_amount['discount'] = self.discount
|
||
detail_amount['tail_amount'] = detail_amount['base'] + detail_amount['warehouse_fee']
|
||
return detail_amount
|
||
|
||
class CEVALogistics_US(TailLogistics):
|
||
"""卡派:CEVA"""
|
||
country = "United States"
|
||
country_code = "US"
|
||
company = "大健-CEVA"
|
||
logistics_type = LogisticsType.LTL
|
||
|
||
parent_current_directory = Path(__file__).parent.parent
|
||
data_path = parent_current_directory.joinpath("data")
|
||
_price_files = data_path.joinpath("CEVA.xlsx")
|
||
zone_df = None
|
||
price_df = None
|
||
grade_df = None
|
||
|
||
def __new__(cls):
|
||
"""实现单例模式,只加载一次文件"""
|
||
if cls.price_df is None or cls.zone_df is None:
|
||
cls.price_df = pandas.read_excel(cls._price_files,sheet_name="ceva_base_rate")
|
||
cls.zone_df = pandas.read_excel(cls._price_files,sheet_name="remote_zone")
|
||
cls.grade_df = pandas.read_excel(cls._price_files,sheet_name="ceva_zone")
|
||
return super().__new__(cls)
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.surcharge_CA = 18.24 # 出发地和目的地有一个是CA 都会收
|
||
self.metro_NY = 37.99 # 出发邮编和目的邮编前三位在100-104,110-114会收
|
||
self.big_package = 30 # 单件包裹重>=250lbs,长>=75in
|
||
self.oversize = [0,131.04,303,532] # 96,120,240,240以上
|
||
|
||
def calculate_fee(self,packages,postcode):
|
||
"""
|
||
计算运费,in,lbs,250
|
||
from邮编固定,
|
||
to邮编先根据remote_df获取State和remote_type,
|
||
再根据zone_df分组,
|
||
最后从price_df取对应价格
|
||
只有big_package和oversize是以单包裹为单位,其他费用混合计算
|
||
"""
|
||
detail_amount = {
|
||
"base":0.00,
|
||
"remote":0.00,
|
||
"surcharge_CA":0.00,
|
||
"metro_NY":0.00,
|
||
"oversize":0.00,
|
||
"big_package":0.00,
|
||
"tail_amount":0.00
|
||
}
|
||
if not re.match(r'\d{5}-\d{4}|\d{5}', postcode):
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
postcode = int(postcode[:5])
|
||
|
||
# 获取分区和偏远
|
||
result = self.zone_df[self.zone_df['PostalCode'] == postcode]
|
||
if result.empty:
|
||
detail_amount['tail_amount']=99999
|
||
return detail_amount
|
||
zone = result['State'].iloc[0]
|
||
remote = result['remote_type'].iloc[0] # standard 和remote
|
||
|
||
# 假设美西出发,CA是美西,NJ是美东
|
||
zone_west = self.grade_df[self.grade_df['To'] ==zone]['CA'].iloc[0]
|
||
|
||
# 最后查看价格表,磅重-grade
|
||
total_weight = 0
|
||
for package in packages:
|
||
billing_weight = max(package.lbs_weight,package.volume_inch/250)
|
||
total_weight +=billing_weight
|
||
if package.lbs_weight >=250 or package.fst_inch >=75:
|
||
detail_amount["big_package"] +=self.big_package
|
||
if 96<=package.fst_inch <120:
|
||
detail_amount["oversize"] +=self.oversize[1]
|
||
elif 120<=package.fst_inch <240:
|
||
detail_amount["oversize"] +=self.oversize[2]
|
||
elif package.fst_inch >=240:
|
||
detail_amount["oversize"] +=self.oversize[3]
|
||
|
||
billed_weight = round(min(max(101,total_weight),1000),0)
|
||
extrat = 0
|
||
price_result = self.price_df[self.price_df['CEVA']==billed_weight]
|
||
if total_weight >1000:
|
||
per_result = self.price_df[self.price_df['CEVA']=="1000+"][zone_west].iloc[0]
|
||
extrat = per_result * (total_weight -1000)
|
||
detail_amount["base"] = price_result[zone_west].iloc[0] + extrat
|
||
detail_amount['remote'] = price_result['Remote area surcharge'].iloc[0] if remote == 'remote' else 0
|
||
detail_amount["surcharge_CA"] = self.surcharge_CA
|
||
detail_amount["metro_NY"] = self.metro_NY if 100<=postcode//100<=104 or 110<=postcode//100<=114 else 0
|
||
|
||
for key in detail_amount:
|
||
if key!= 'tail_amount':
|
||
detail_amount['tail_amount'] += detail_amount[key]
|
||
return detail_amount
|
||
|
||
class MetroLogistics_US(TailLogistics):
|
||
"""卡派:Metro"""
|
||
country = "United States"
|
||
country_code = "US"
|
||
company = "Metro-SAIR"
|
||
logistics_type = LogisticsType.LTL
|
||
|
||
parent_current_directory = Path(__file__).parent.parent
|
||
data_path = parent_current_directory.joinpath("data")
|
||
_price_files = data_path.joinpath("Metro.xlsx")
|
||
cuft_25 = None
|
||
cuft_35 = None
|
||
over_35_per_cuft = None
|
||
over_35_min = None
|
||
zone_table = None
|
||
remote_table = None
|
||
@staticmethod
|
||
def refresh():
|
||
"""清空已加载的数据,以便下次重新加载"""
|
||
MetroLogistics_US.cuft_25 = None
|
||
MetroLogistics_US.cuft_35 = None
|
||
MetroLogistics_US.over_35_per_cuft = None
|
||
MetroLogistics_US.over_35_min = None
|
||
MetroLogistics_US.zone_table = None
|
||
MetroLogistics_US.remote_table = None
|
||
def __new__(cls):
|
||
"""实现单例模式,只加载一次文件"""
|
||
if cls.cuft_25 is None or cls.cuft_35 is None:
|
||
cls.cuft_25 = pandas.read_excel(cls._price_files,sheet_name="cuft_25")
|
||
cls.cuft_35 = pandas.read_excel(cls._price_files,sheet_name="cuft_35")
|
||
cls.over_35_per_cuft = pandas.read_excel(cls._price_files,sheet_name="over35_per_cuft")
|
||
cls.over_35_min = pandas.read_excel(cls._price_files,sheet_name="over35_min")
|
||
cls.zone_table = pandas.read_excel(cls._price_files,sheet_name="zone_table")
|
||
cls.remote_table = pandas.read_excel(cls._price_files,sheet_name="remote_table")
|
||
return super().__new__(cls)
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.fuel_rate = 0.4 # 基础费用必*1.4,附加费不用
|
||
self.discount = 0.88 # 折扣,所有费用都吃
|
||
|
||
def calculate_fee(self,packages,postcode):
|
||
"""
|
||
计算运费,in,lbs,250
|
||
from邮编固定,
|
||
to邮编先根据remote_df获取State和remote_type,
|
||
再根据zone_df分组,
|
||
最后从price_df取对应价格
|
||
只有big_package和oversize是以单包裹为单位,其他费用混合计算
|
||
"""
|
||
detail_amount = {
|
||
"base":0.00,
|
||
"oversize":0.00,
|
||
"remote":0.00,
|
||
"tail_amount":0.00
|
||
}
|
||
if not re.match(r'\d{5}-\d{4}|\d{5}', postcode):
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
postcode = int(postcode[:5])
|
||
# 出发地分区为 Zone 2 L
|
||
# 取目的地分区
|
||
zone_result = self.zone_table[self.zone_table['Zip Code'] ==postcode]['New Zone Name']
|
||
if zone_result.empty:
|
||
detail_amount["tail_amount"] = 99999
|
||
return detail_amount
|
||
zone = zone_result.iloc[0]
|
||
|
||
|
||
total_cuft = 0
|
||
max_weight = 0
|
||
max_length = 0
|
||
for package in packages:
|
||
total_cuft += round(package.volume_inch/1728,2)
|
||
# cm则为长*宽*高/28316.846592,单位是立方英尺
|
||
max_weight = max(max_weight,package.lbs_weight)
|
||
max_length = max(max_length,package.fst_inch)
|
||
|
||
# base费用 25立方英尺 ,美西是Zone 2 L 美东是Zone 17 L
|
||
if total_cuft < 25:
|
||
detail_amount["base"] = self.cuft_25[self.cuft_25['Origins']==zone]["Zone 2 L"].iloc[0]
|
||
elif 25<=total_cuft <35:
|
||
detail_amount["base"] = self.cuft_35[self.cuft_35['Origins']==zone]["Zone 2 L"].iloc[0]
|
||
else:
|
||
per_cuft = self.over_35_per_cuft[self.over_35_per_cuft['Origins']==zone]["Zone 2 L"].iloc[0]
|
||
base_min = self.over_35_min[self.over_35_min['Origins']==zone]["Zone 2 L"].iloc[0]
|
||
detail_amount["base"] = max(per_cuft*total_cuft,base_min)
|
||
|
||
# 超尺寸,包括超重或超长
|
||
if max_weight >400 or max_length>125: # 不可派
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
if 300<max_weight <=400:
|
||
detail_amount["oversize"] += 110
|
||
if 100<max_length <=125:
|
||
detail_amount["oversize"] += 110
|
||
|
||
# 偏远费
|
||
remote_result = self.remote_table[self.remote_table['ZIPCode'] == postcode]['Area Type']
|
||
if not remote_result.empty:
|
||
value = remote_result.iloc[0]
|
||
if value.startswith("Beyond"):
|
||
detail_amount["remote"] = 110
|
||
|
||
detail_amount["base"] = detail_amount["base"] * (1+self.fuel_rate)
|
||
for key in detail_amount:
|
||
if key!= 'tail_amount':
|
||
detail_amount[key] *= self.discount
|
||
detail_amount['tail_amount'] += detail_amount[key]
|
||
return detail_amount
|
||
|
||
class XmilesLogistics_US(TailLogistics):
|
||
"""一票多件快递:XMILES,
|
||
包含美东和美西,
|
||
美东只在美西基础上+0.5*抛重6000的转运费
|
||
"""
|
||
country = "United States"
|
||
country_code = "US"
|
||
company = "XMILES-SAIR"
|
||
logistics_type = LogisticsType.LTL
|
||
|
||
parent_current_directory = Path(__file__).parent.parent
|
||
data_path = parent_current_directory.joinpath("data")
|
||
_price_files = data_path.joinpath("XMILES.xlsx")
|
||
postcode_table = None
|
||
def __new__(cls):
|
||
"""实现单例模式,只加载一次文件"""
|
||
if cls.postcode_table is None:
|
||
cls.postcode_table = pandas.read_excel(cls._price_files,sheet_name="postcode_table")
|
||
return super().__new__(cls)
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
|
||
def calculate_fee(self,packages,postcode):
|
||
"""
|
||
实重计费
|
||
AH/OS/OM,取最大类型费用做第一件费用,第二件开始全部半价
|
||
重量<=500lbs(226kg)
|
||
最长边<=144inch(365cm)
|
||
围长<=225inch(571cm)
|
||
美东美西费用一样,可派送邮编不同
|
||
"""
|
||
detail_amount = {
|
||
"base":0.00,
|
||
"transfer":0.00, # 美西到美东的中转费
|
||
"tail_amount":0.00
|
||
}
|
||
if not re.match(r'\d{5}-\d{4}|\d{5}', postcode):
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
postcode = int(postcode[:5])
|
||
# 判断邮编是美东还是美西
|
||
zone = self.postcode_table[self.postcode_table['POSTCODE'] == postcode]['地区']
|
||
if zone.empty:
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
|
||
|
||
base_fee_list = [] # 将每个包裹费用都记录在列表中
|
||
volume_weight = 0 # 计算抛重,用于计算美西转运美东价格
|
||
for package in packages:
|
||
volume_weight +=package.get_volume_weight(6000)
|
||
if package.lbs_weight >500 or package.fst_inch >144 or package.girth_inch >225:
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
if package.lbs_weight <=90 and package.fst_inch <=96 and package.girth_inch <=130:
|
||
base_fee_list.append(20)
|
||
elif package.lbs_weight <=150 and package.fst_inch <=96 and package.girth_inch <=130:
|
||
base_fee_list.append(38)
|
||
elif package.lbs_weight <=150 and package.fst_inch <=108 and package.girth_inch <=165:
|
||
base_fee_list.append(50)
|
||
elif package.lbs_weight <=200 and package.fst_inch <=144 and package.girth_inch <=225:
|
||
base_fee_list.append(75)
|
||
else:
|
||
base_fee_list.append(75+0.55*math.ceil(package.lbs_weight))
|
||
# 取所有费用,最大的费用取全部费用,其他取半价然后求和
|
||
base_fee_list = sorted(base_fee_list,reverse=True)
|
||
# 计算base_fee_list最大值+其他费用的一半
|
||
if len(base_fee_list) == 0:
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
elif len(base_fee_list) == 1:
|
||
detail_amount["base"] = base_fee_list[0]
|
||
else:
|
||
detail_amount["base"] = base_fee_list[0] + sum(base_fee_list[1:])/2
|
||
|
||
zone = zone.iloc[0]
|
||
if zone =="NJ":
|
||
detail_amount["transfer"] = 0.5*volume_weight
|
||
|
||
for key in detail_amount:
|
||
if key!= 'tail_amount':
|
||
detail_amount['tail_amount'] += detail_amount[key]
|
||
return detail_amount
|
||
|
||
class AMWestLogistics_US(TailLogistics):
|
||
"""卡派:AM,
|
||
美西派送,
|
||
"""
|
||
country = "United States"
|
||
country_code = "US"
|
||
company = "AM-美西"
|
||
logistics_type = LogisticsType.LTL
|
||
|
||
parent_current_directory = Path(__file__).parent.parent
|
||
data_path = parent_current_directory.joinpath("data")
|
||
_price_files = data_path.joinpath("美国卡派-AM.xlsx")
|
||
postcode_table = None
|
||
price_df = None
|
||
def __new__(cls):
|
||
"""实现单例模式,只加载一次文件"""
|
||
if cls.postcode_table is None or cls.price_df is None:
|
||
cls.price_df = pandas.read_excel(cls._price_files,sheet_name="price")
|
||
cls.postcode_table = pandas.read_excel(cls._price_files,sheet_name="postcode_table")
|
||
return super().__new__(cls)
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.PUZone = "CA" # 派送起点
|
||
|
||
def calculate_fee(self,packages,postcode):
|
||
"""
|
||
实重计费
|
||
lbs
|
||
重量<=1000lbs(453kg)
|
||
美西PUZone:CA, DLZone根据目的地邮编得
|
||
美东PUZone:NY, DLZone根据目的地邮编得
|
||
"""
|
||
detail_amount = {
|
||
"base":0.00,
|
||
"surcharge":0.00, # 附加费用
|
||
"transfer":0.00, # 美西到美东的中转费
|
||
"tail_amount":0.00
|
||
}
|
||
if not re.match(r'\d{5}-\d{4}|\d{5}', postcode):
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
postcode = int(postcode[:5])
|
||
# postcode = str(postcode).zfill(5)
|
||
# 先拿到目的地邮编对应的DLZone
|
||
zone_match = self.postcode_table[self.postcode_table['Zip Code'] == postcode]
|
||
if zone_match.empty:
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
else:
|
||
DLZone = zone_match['Zone'].iloc[0]
|
||
|
||
volume_weight = 0 # 计算抛重,用于计算美西转运美东价格
|
||
total_lbs_weight = 0 # 计算总磅重
|
||
for package in packages:
|
||
volume_weight +=package.get_volume_weight(6000)
|
||
total_lbs_weight += package.lbs_weight
|
||
if package.lbs_weight >1000:
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
|
||
zone = self.PUZone+DLZone
|
||
row = self.price_df[(self.price_df['Zone Combo']==zone)
|
||
&(self.price_df['Minimum']<=total_lbs_weight)
|
||
&(self.price_df['Maximum']>=total_lbs_weight)]
|
||
if row.empty:
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
detail_amount["base"] = row['w/o SC'].iloc[0]
|
||
detail_amount["surcharge"] = row['Surcharge'].iloc[0]
|
||
if detail_amount['base']==0:
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
for key in detail_amount:
|
||
if key!= 'tail_amount':
|
||
detail_amount['tail_amount'] += detail_amount[key]
|
||
return detail_amount
|
||
|
||
class AMEastLogistics_US(TailLogistics):
|
||
"""卡派:AM,
|
||
美东派送,
|
||
美东在费用上额外+0.5*抛重6000的转运费
|
||
"""
|
||
country = "United States"
|
||
country_code = "US"
|
||
company = "AM-美东"
|
||
logistics_type = LogisticsType.LTL
|
||
|
||
parent_current_directory = Path(__file__).parent.parent
|
||
data_path = parent_current_directory.joinpath("data")
|
||
_price_files = data_path.joinpath("美国卡派-AM.xlsx")
|
||
postcode_table = None
|
||
price_df = None
|
||
def __new__(cls):
|
||
"""实现单例模式,只加载一次文件"""
|
||
if cls.postcode_table is None or cls.price_df is None:
|
||
cls.price_df = pandas.read_excel(cls._price_files,sheet_name="price")
|
||
cls.postcode_table = pandas.read_excel(cls._price_files,sheet_name="postcode_table")
|
||
return super().__new__(cls)
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.PUZone = "NY"
|
||
|
||
def calculate_fee(self,packages,postcode):
|
||
"""
|
||
实重计费
|
||
lbs
|
||
重量<=1000lbs(453kg)
|
||
美西PUZone:CA, DLZone根据目的地邮编得
|
||
美东PUZone:NY, DLZone根据目的地邮编得
|
||
"""
|
||
detail_amount = {
|
||
"base":0.00,
|
||
"surcharge":0.00, # 附加费用
|
||
"transfer":0.00, # 美西到美东的中转费
|
||
"tail_amount":0.00
|
||
}
|
||
if not re.match(r'\d{5}-\d{4}|\d{5}', postcode):
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
postcode = int(postcode[:5])
|
||
# postcode = str(postcode).zfill(5)
|
||
# 先拿到目的地邮编对应的DLZone
|
||
zone_match = self.postcode_table[self.postcode_table['Zip Code'] == postcode]
|
||
if zone_match.empty:
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
else:
|
||
DLZone = zone_match['Zone'].iloc[0]
|
||
volume_weight = 0 # 计算抛重,用于计算美西转运美东价格
|
||
total_lbs_weight = 0 # 计算总磅重
|
||
for package in packages:
|
||
volume_weight +=package.get_volume_weight(6000)
|
||
total_lbs_weight += package.lbs_weight
|
||
if package.lbs_weight >1000:
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
|
||
zone = self.PUZone+DLZone
|
||
row = self.price_df[(self.price_df['Zone Combo']==zone)
|
||
&(self.price_df['Minimum']<=total_lbs_weight)
|
||
&(self.price_df['Maximum']>=total_lbs_weight)]
|
||
if row.empty:
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
detail_amount["base"] = row['w/o SC'].iloc[0]
|
||
detail_amount["surcharge"] = row['Surcharge'].iloc[0]
|
||
detail_amount["transfer"] = 0.5*volume_weight
|
||
if detail_amount['base']==0:
|
||
detail_amount['tail_amount'] = 99999
|
||
return detail_amount
|
||
for key in detail_amount:
|
||
if key!= 'tail_amount':
|
||
detail_amount['tail_amount'] += detail_amount[key]
|
||
return detail_amount
|
||
if __name__ == '__main__':
|
||
FedexPPLogistics_US.close_logistics()
|
||
print(1) |