345 lines
16 KiB
Python
345 lines
16 KiB
Python
""" 澳大利亚尾端物流模块实现类"""
|
||
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<billing_weight <=55000:
|
||
detail_amount['Homedelivery'] += self.homedelivery_fee[1]
|
||
elif 55000<package.weight<=90000 or 55000<volume_weight<=135000:
|
||
detail_amount['Homedelivery'] += self.homedelivery_fee[2]
|
||
else:
|
||
detail_amount['Homedelivery'] += self.homedelivery_fee[3]
|
||
[7.8,10.92,12.41,26.42,92.15,123.96,160.07]
|
||
if 110<package.fst_size <=160:
|
||
detail_amount['widthover'] += self.oversize_fee[0]
|
||
elif 160<package.fst_size <=240:
|
||
detail_amount['widthover'] += self.oversize_fee[1]
|
||
elif 240<package.fst_size <=359:
|
||
detail_amount['lengthover'] += self.oversize_fee[2]
|
||
elif 359<package.fst_size <=419:
|
||
detail_amount['lengthover'] += self.oversize_fee[3]
|
||
elif 419<package.fst_size <=479:
|
||
detail_amount['lengthover'] += self.oversize_fee[4]
|
||
elif 479<package.fst_size <=599:
|
||
detail_amount['lengthover'] += self.oversize_fee[5]
|
||
elif package.fst_size > 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<package.fst_size and package.sed_size > 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 |