""" 澳大利亚尾端物流模块实现类""" from pathlib import Path import re import math import pandas as pd from logisticsClass.logisticsBaseClass import TailLogistics from utils.Package import Package, Package_group """ counrty:Australia company:POST,ALL,TOLL port:海SY,空SYD(default) currency:str = 'AUD'(default) logistics_type:LogisticsType (快递(default),卡派) """ # POST class PostLogistics_AU(TailLogistics): country_code = 'AU' country = 'Australia' company = 'POST' currency = 'AUD' _is_loaded = False parent_current_directory = Path(__file__).parent.parent remote_path = parent_current_directory.joinpath("data") _postcode_files = remote_path.joinpath("澳洲三大渠道.xlsx") 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): """加载文件""" # 获取eparcel邮编所属的分区 cls.eparcel_zone = pd.read_excel(str(cls._postcode_files),sheet_name="eparcel_postcode",usecols="A:B") # 获取eparcel的价格 cls.eparcel_price = pd.read_excel(str(cls._postcode_files),sheet_name="eparcel",usecols="A:M") def __init__(self): super().__init__() self.eparcel_zone = self.__class__.eparcel_zone self.eparcel_price = self.__class__.eparcel_price self.fuel_rate =0.051# 0.074 0.064 def calculate_fee(self, packages, postcode): """ 计费重: 常规计费, 4000 限制条件: 32kg,最长边100cm 是否有燃油费:是 """ detail_amount = { "base":0.00, "fuel":0.00, "tail_amount":0.00 } if isinstance(postcode, str): postcode = ''.join(filter(str.isdigit, postcode)) # 只保留数字部分 if postcode: # 确保postcode不是空字符串 postcode = str(int(postcode)).zfill(4) # 转换为整数再补齐4位 else: detail_amount['tail_amount'] = 88888 return detail_amount # 先看邮编呐 self.eparcel_zone['邮编'] = self.eparcel_zone['邮编'].astype(str).str.zfill(4) filtered_df = self.eparcel_zone[self.eparcel_zone['邮编'] == postcode] if filtered_df.empty: detail_amount['tail_amount'] = 99999 return detail_amount post = filtered_df['地区代码'].iloc[0] # 获取第一行的 '地区代码' # 根据post筛选出对应行 row = self.eparcel_price[self.eparcel_price['post'] == post] # 检查 row 是否为空 if row.empty: detail_amount['tail_amount'] = 99999 return detail_amount base_data = list(zip(self.eparcel_price.columns[1:], row.values[0][1:])) for package in packages: billing_weight = max(package.weight, package.volume/4) if package.fst_size > 100 or billing_weight >= 32000: detail_amount['tail_amount'] = 99999 return detail_amount if billing_weight > 22000: detail_amount['base'] += base_data[-2][1] + base_data[-1][1] * math.ceil(billing_weight/1000) else: for weight,price in base_data[:-2]: if billing_weight <=weight*1000: detail_amount['base'] += price break detail_amount['fuel'] = detail_amount['base'] * self.fuel_rate detail_amount['tail_amount'] = detail_amount['base'] + detail_amount['fuel'] return detail_amount # TOLL class TollLogistics_AU(TailLogistics): country_code = 'AU' country = 'Australia' company = 'TOLL' currency = 'AUD' _is_loaded = False parent_current_directory = Path(__file__).parent.parent remote_path = parent_current_directory.joinpath("data") _postcode_files = remote_path.joinpath("澳洲三大渠道.xlsx") 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.toll_zone = pd.read_excel(str(cls._postcode_files),sheet_name="toll_postcode",usecols="A:B") cls.toll_price = pd.read_excel(str(cls._postcode_files),sheet_name="toll",usecols="A:D") cls.toll_remote = pd.read_excel(str(cls._postcode_files),sheet_name="toll_remote",usecols="A:D") def __init__(self): super().__init__() self.toll_zone = self.__class__.toll_zone self.toll_price = self.__class__.toll_price self.toll_remote = self.__class__.toll_remote self.fuel_rate = 0.0725 self.oversize_fee = [15.5,62] def calculate_fee(self, packages, postcode): # 抛重4000 detail_amount = { "base":0.00, "oversize":0.00, "remote":0.00, "fuel":0.00, "tail_amount":0.00 } if isinstance(postcode, str): postcode = ''.join(filter(str.isdigit, postcode)) # 只保留数字部分 if postcode: # 确保postcode不是空字符串 postcode = str(int(postcode)).zfill(4) # 转换为整数再补齐4位 else: detail_amount['tail_amount'] = 88888 return detail_amount # 先看邮编呐 self.toll_zone['postcode'] = self.toll_zone['postcode'].astype(str).str.zfill(4) filtered_df = self.toll_zone[self.toll_zone['postcode'] == postcode] if filtered_df.empty: detail_amount['tail_amount'] = 99999 return detail_amount post = filtered_df['post'].iloc[0] # 获取第一行的 '地区代码' # 根据post筛选出对应行 row = self.toll_price[self.toll_price['post'] == post] # 检查 row 是否为空 if row.empty: detail_amount['tail_amount'] = 99999 return detail_amount base = row['Base'].iloc[0] per = self.toll_price[self.toll_price['post']==post]['Per'].iloc[0] minimun = self.toll_price[self.toll_price['post']==post]['Minimun'].iloc[0] for package in packages: volume_weight = package.get_volume_weight(4) billing_weight = max(package.weight,volume_weight) detail_amount['base'] += max(base + per * math.ceil(billing_weight/1000), minimun) # if package.weight >= 35000 or package.fst_size >= 180 or package.volume >=700000: # detail_amount['oversize'] += self.oversize_fee[1] if package.weight >30000 or package.fst_size > 120 or package.sed_size > 80: detail_amount['oversize'] += self.oversize_fee[0] # 计算偏远附加费,只跟地区有关,不需要放入循环内部计算 postcode_counts = self.toll_zone[self.toll_zone['postcode'] == postcode].shape[0] # 该邮编总共多少个地区 remote_count = self.toll_remote[self.toll_remote['postcode'] == postcode].shape[0] # 该邮编有多少个偏远地区 postcode_counts = int(postcode_counts) remote_count=int(remote_count) # 将 price 列中的 NaN 替换为 0 self.toll_remote['price'] = self.toll_remote['price'].fillna(0) # 该邮编偏远地区的偏远费 remote_fee = self.toll_remote[self.toll_remote['postcode'] == postcode]['price'].mean() # 检查 remote_fee 是否为 NaN,若是则设为 0 if pd.isna(remote_fee): remote_fee = 0 # 计算最终的偏远附加费 if postcode_counts > 0 and remote_count > 0: remote_fee = remote_fee * remote_count / postcode_counts else: remote_fee = 0 # 处理为其他合适的值 detail_amount['remote'] = remote_fee * len(packages) 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 # ALL class AllLogistics_AU(TailLogistics): country_code = 'AU' country = 'Australia' company = 'ALL' currency = 'AUD' _is_loaded = False parent_current_directory = Path(__file__).parent.parent remote_path = parent_current_directory.joinpath("data") _postcode_files = remote_path.joinpath("澳洲三大渠道.xlsx") 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.all_zone = pd.read_excel(str(cls._postcode_files),sheet_name="allied_postcode",usecols="A:C") cls.all_price = pd.read_excel(str(cls._postcode_files),sheet_name="allied",usecols="A:D") cls.all_remote = pd.read_excel(str(cls._postcode_files),sheet_name="allied_remote",usecols="A:E") def __init__(self): super().__init__() self.all_zone = self.__class__.all_zone self.all_price = self.__class__.all_price self.all_remote = self.__class__.all_remote self.fuel_rate = 0.269 self.homedelivery_fee = [5.51,11.02,38.56,82.61] self.oversize_fee = [7.8,10.92,12.41,26.42,92.15,123.96,160.07] self.handing_fee = 15.34 # 不收燃油费 self.twomancrew_fee = [49.92,78,124.8] # 多包裹的情况下,只收一次最大的 def calculate_fee(self, packages, postcode): # 抛重4000 detail_amount = { "base":0.00, "Homedelivery":0.00, "twomancrew":0.00, # 多包裹的情况下,只收一次最大的 "lengthover":0.00, "fuel":0.00, "widthover":0.00, # 240cm内不收燃油费 "handling":0.00, "tail_amount":0.00 } if isinstance(postcode, str): postcode = ''.join(filter(str.isdigit, postcode)) # 只保留数字部分 if postcode: # 确保postcode不是空字符串 postcode = str(int(postcode)).zfill(4) # 转换为整数再补齐4位 else: detail_amount['tail_amount'] = 88888 return detail_amount # 先看邮编呐 self.all_zone['postcode'] = self.all_zone['postcode'].astype(str).str.zfill(4) filtered_df = self.all_zone[self.all_zone['postcode'] == postcode] if filtered_df.empty: detail_amount['tail_amount'] = 99999 return detail_amount post = filtered_df['post'].iloc[0] # 获取第一行的 '地区代码' try: base = self.all_price[self.all_price['post']==post]['Base'].iloc[0] per = self.all_price[self.all_price['post']==post]['Per'].iloc[0] minimun = self.all_price[self.all_price['post']==post]['Minimun'].iloc[0] except: detail_amount['tail_amount'] = 99999 return detail_amount total_weight = 0 # 总计费重,用于计算偏远附加费 for package in packages: volume_weight = package.get_volume_weight(4) billing_weight = max(package.weight,volume_weight) total_weight += billing_weight # 总计费重,用于计算偏远附加费 detail_amount['base'] += max(base + per * math.ceil(billing_weight/1000), minimun) if billing_weight <= 22000: detail_amount['Homedelivery'] += self.homedelivery_fee[0] elif 22000 599: detail_amount['lengthover'] += self.oversize_fee[6] if 130<=package.fst_size <=190 and package.sed_size > 90 and (package.weight >46000 or billing_weight>91000): detail_amount['twomancrew'] = self.twomancrew_fee[0] if 190<=package.fst_size <=240 and package.sed_size > 130 and (package.weight >55000 or billing_weight>110000): detail_amount['twomancrew'] = self.twomancrew_fee[1] if 240 130 and (package.weight >75000 or billing_weight>150000): detail_amount['twomancrew'] = self.twomancrew_fee[2] if billing_weight >30000 or package.fst_size >110: detail_amount['handling'] += self.handing_fee # 计算偏远附加费,只跟地区有关,不需要放入循环内部计算 postcode_counts = self.all_zone[self.all_zone['postcode'] == postcode].shape[0] # 该邮编总共多少个地区 remote_count = self.all_remote[self.all_remote['Postcode'] == postcode].shape[0] # 该邮编有多少个偏远地区 postcode_counts = int(postcode_counts) remote_count=int(remote_count) # 该邮编偏远地区的偏远费 remote_base_fee = self.all_remote[self.all_remote['Postcode']==postcode]['Base'].mean() # 该邮编偏远地区的偏远基础费 remote_per_fee = self.all_remote[self.all_remote['Postcode']==postcode]['Per'].mean() # 检查 remote_base_fee 是否为 NaN,若是则设为 0 if pd.isna(remote_base_fee) or pd.isna(remote_per_fee): remote_base_fee = 0 remote_per_fee = 0 # 计算最终的偏远附加费 if postcode_counts > 0 and remote_count > 0: detail_amount['remote'] = remote_base_fee+remote_per_fee*math.ceil(total_weight/1000)* remote_count/postcode_counts for key in detail_amount: if key!= 'tail_amount' and key!= 'fuel': detail_amount['tail_amount'] += detail_amount[key] if key!='widthover' and key!='handling': detail_amount['fuel'] += detail_amount[key] * self.fuel_rate detail_amount['tail_amount'] += detail_amount['fuel'] return detail_amount if __name__ == '__main__': # 测试 aau = PostLogistics_AU() package = Package("wxx",40,25,25,1780) packages = Package_group([package]) aau.calculate_fee(packages,'3101') TollLogistics_AU.active = True PostLogistics_AU.active = True