logistics/logisticsClass/logisticsTail_US.py

957 lines
39 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

""" 美国尾端物流模块实现类"""
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
from utils.gtools import DBconnect
"""
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):
"""获取美西邮编分区"""
postcode = str(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.18 # 燃油费率
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 >157: # 400cm
detail_amount['tail_amount'] = 999999
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'] = 999999
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
self.base_price.columns = self.base_price.columns.map(str)
detail_amount['base'] +=self.base_price[self.base_price['lbs.']==math.ceil(cal_weight)][str(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"
base_price = None
_instance = None
def __new__(cls):
if cls.base_price is None:
cls._instance = super().__new__(cls)
with DBconnect() as db:
cls.base_price = pandas.read_sql("SELECT * FROM us_fedex_home", db.engine())
return cls._instance
return cls._instance
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
_instance = None
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._instance = super().__new__(cls)
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-104110-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 MetroLALogistics_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
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 MetroNYLogistics_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
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,
"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 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
volume_weight = 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)
volume_weight += package.weight/1000
# base费用 25立方英尺 美西是Zone 2 L 美东是Zone 17 L
if total_cuft < 25:
detail_amount["base"] = self.cuft_25[self.cuft_25['Origins']==zone]["Zone 17 L"].iloc[0]
elif 25<=total_cuft <35:
detail_amount["base"] = self.cuft_35[self.cuft_35['Origins']==zone]["Zone 17 L"].iloc[0]
else:
per_cuft = self.over_35_per_cuft[self.over_35_per_cuft['Origins']==zone]["Zone 17 L"].iloc[0]
base_min = self.over_35_min[self.over_35_min['Origins']==zone]["Zone 17 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]
detail_amount['transfer'] = 0.5*volume_weight # 美西到美东的中转费
detail_amount['tail_amount'] += detail_amount['transfer']
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
for key in detail_amount:
if key!= 'tail_amount':
detail_amount['tail_amount'] += detail_amount[key]
zone = zone.iloc[0]
if zone =="NJ":
detail_amount["transfer"] = 0.5*volume_weight # 美西到美东的中转费
detail_amount['tail_amount'] += detail_amount['transfer']
return detail_amount
class AMTWestLogistics_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 AMTEastLogistics_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'] = 999999
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)