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)
 | 
						||
    # Path_current_directory = Path(__file__).parent
 | 
						||
    # current_directory = os.path.dirname(__file__)
 | 
						||
    @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])
 | 
						||
 | 
						||
        base_fee_list = set() # 将每个包裹费用都记录在列表中
 | 
						||
        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.add(20)
 | 
						||
            elif package.lbs_weight <=150 and package.fst_inch <=96 and package.girth_inch <=130:
 | 
						||
                base_fee_list.add(38)
 | 
						||
            elif package.lbs_weight <=150 and package.fst_inch <=108 and package.girth_inch <=165:
 | 
						||
                base_fee_list.add(50)
 | 
						||
            elif package.lbs_weight <=200 and package.fst_inch <=144 and package.girth_inch <=225:
 | 
						||
                base_fee_list.add(75)
 | 
						||
            else:
 | 
						||
                base_fee_list.add(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:
 | 
						||
            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 = self.postcode_table[self.postcode_table['POSTCODE'] == postcode]['地区']
 | 
						||
        if zone.empty:
 | 
						||
            detail_amount['tail_amount'] = 99999
 | 
						||
            return detail_amount
 | 
						||
        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) |