logistics/logisticsClass/logisticsTail_AU.py

345 lines
16 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.

""" 澳大利亚尾端物流模块实现类"""
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