logistics/logisticsClass/logisticsTail_EUR.py

434 lines
17 KiB
Python
Raw Normal View History

2025-06-17 13:40:20 +08:00
""" 欧洲国家尾端物流模块实现类"""
from pathlib import Path
import re
import pandas
from logisticsClass.logisticsBaseClass import LogisticsType, TailLogistics
"""
port:NL(default)
currency:str = 'EUR'
logistics_type:LogisticsType (快递卡派)
"""
# DPD-ASL 欧洲国家逻辑基类
class DPDASLLogistics(TailLogistics):
"""DPD-ASL"""
company = "DPD-ASL" # DPD_ASL荷兰发件欧洲地区默认港口
currency = "EUR"
def __init__(self):
super().__init__()
self.base_fee = None
self.overweight = 2.89
self.bigpackage = 44.5
self.remote_fee = None
self.fuel_rate = 0.13
def is_remote(self,postcode):
"""判断是否偏远,1偏远0非偏远"""
raise NotImplementedError("Subclasses must implement remote calculation.")
def calculate_fee(self,packages,postcode):
"""计算快递费用"""
detail_amount = {
"base":0.00,
"overweight":0.00,
"bigpackage":0.00,
"remote":0.00,
"fuel":0.00,
"tail_amount":0.00
}
isremote = self.is_remote(postcode)
if isremote == "邮编格式不合法":
detail_amount['tail_amount'] = 99999
return detail_amount
for package in packages: # 逐个处理列表中的每个包裹
if package.weight > 31500 or package.girth > 350 or package.fst_size > 200:
detail_amount['tail_amount'] = 99999
return detail_amount
detail_amount['base'] += self.base_fee
detail_amount['remote'] +=self.remote_fee* isremote
detail_amount['overweight'] += self.overweight if package.weight >= 20000 else 0
if package.fst_size >=175 or package.weight >= 30000 or package.girth >= 300:
detail_amount['bigpackage'] += self.bigpackage
for key in detail_amount:
if key != 'tail_amount' and key != 'fuel':
detail_amount['fuel'] += detail_amount[key] * self.fuel_rate
detail_amount['tail_amount'] += detail_amount[key]
detail_amount['tail_amount'] +=detail_amount['fuel']
return detail_amount
# DPDASL 德国实现
class DPDASLLogistics_DE(DPDASLLogistics):
country_code = 'DE'
country = 'Germany'
def __init__(self):
super().__init__()
self.base_fee = 7.1
self.remote_fee = 12
def is_remote(self,postcode):
"""判断是否偏远,1偏远0非偏远"""
# 先判断邮编是否合法
if not re.match(r'^\d{5}$', postcode):
return "邮编格式不合法"
remote_postcodes = ["18565","25845-25847","25849","25859","25863","25869","25929-25933","25938-25942",
"25946-25949","25952-25955","25980","25961-25970","25985-25986","25988-25990","25992-25994",
"25996-25999","26465","26474","26486","26548","26571","26579","26757","27498","83256"
]
postcodes = []
for code in remote_postcodes:
if '-' in code:
start,end = code.split('-')
postcodes.extend(list(range(int(start),int(end)+1)))
else:
postcodes.append(int(code))
return 1 if postcode in postcodes else 0
# DPDASL 法国实现
class DPDASLLogistics_FR(DPDASLLogistics):
country_code = 'FR'
country = 'France'
def __init__(self):
super().__init__()
self.base_fee = 10.2
self.remote_fee = 22.5
def is_remote(self,postcode):
"""判断是否偏远,1偏远0非偏远"""
# 先判断邮编是否合法
if not re.match(r'^\d{5}$', postcode):
return "邮编格式不合法"
if postcode.startswith("20"):
return 1
remove_postcodes = ["17111","17123","17190","17310","17370","17410","17480","17550","17580","17590","17630","17650",
"17670","17740","17840","17880","17940","56360","56590","56780","56840","85330","85350"]
return 1 if postcode in remove_postcodes else 0
# DPDASL 西班牙实现
class DPDASLLogistics_ES(DPDASLLogistics):
country_code = 'ES'
country = 'Spain'
def __init__(self):
super().__init__()
self.base_fee = 11.9
self.remote_fee = 34
def is_remote(self,postcode):
"""判断是否偏远,1偏远0非偏远"""
# 先判断邮编是否合法
if not re.match(r'^\d{5}$', postcode):
return "邮编格式不合法"
if postcode.startswith("07") or postcode.startswith("35") or postcode.startswith("38"):
return 1
else:
return 0
# DPD-ZG 欧洲国家逻辑基类
class DPDZGLogistics(TailLogistics):
"""DPD-智谷"""
company = "DPD-ZG" # DPD-ZG可以海运可以空运海运港口NL,空运港口AMS
currency = "EUR"
def __init__(self):
super().__init__()
self.base_fee:list #[5,10,30]
self.operate_rate = 0.18
self.fuel_rate = 0.125
self.remote_fee:float
def is_remote(self,postcode):
"""判断是否偏远,1偏远0非偏远"""
raise NotImplementedError("Subclasses must implement remote calculation.")
def calculate_fee(self,packages,postcode):
"""计算快递费用"""
detail_amount = {
"base":0.00,
"operate":0.00,
"remote":0.00,
"fuel":0.00,
"tail_amount":0.00
}
isremote = self.is_remote(postcode)
if isremote == "邮编格式不合法":
detail_amount['tail_amount'] = 99999
return detail_amount
for package in packages: # 逐个处理列表中的每个包裹
if package.weight > 31500 or package.girth > 300 or package.fst_size >175:
detail_amount['tail_amount'] = 99999
return detail_amount
if package.weight <5000:
detail_amount['base'] += self.base_fee[0]
elif package.weight < 10000:
detail_amount['base'] += self.base_fee[1]
else:
detail_amount['base'] += self.base_fee[2]
detail_amount['remote'] += float(self.remote_fee) * float(isremote)
detail_amount['operate'] += package.weight * self.operate_rate
for key in detail_amount:
if key != 'tail_amount' and key != 'fuel':
detail_amount['fuel'] += detail_amount[key] * self.fuel_rate
detail_amount['tail_amount'] += detail_amount[key]
detail_amount['tail_amount'] +=detail_amount['fuel']
return detail_amount
# DPD-ZG 德国实现
class DPDZGLogistics_DE(DPDZGLogistics):
country_code = 'DE'
country = 'Germany'
def __init__(self):
super().__init__()
self.base_fee = [7.72,8.83,11.67]
self.operate_rate = 0.18
self.fuel_rate = 0.125
self.remote_fee=11.5
def is_remote(self,postcode):
"""判断是否偏远,1偏远0非偏远"""
# 先判断邮编是否合法
if not re.match(r'^\d{5}$', postcode):
return "邮编格式不合法"
remote_postcodes = ["18565","25845-25847","25849","25859","25863","25869","25929-25933","25938-25942",
"25946-25949","25952-25955","25980","25961-25970","25985-25986","25988-25990","25992-25994",
"25996-25999","26465","26474","26486","26548","26571","26579","26757","27498","83256"
]
postcodes = []
for code in remote_postcodes:
if '-' in code:
start,end = code.split('-')
postcodes.extend(list(range(int(start),int(end)+1)))
else:
postcodes.append(int(code))
return 1 if postcode in postcodes else 0
# DPD-ZG 法国实现
class DPDZGLogistics_FR(DPDZGLogistics):
country_code = 'FR'
country = 'France'
def __init__(self):
super().__init__()
self.base_fee = [10.77,11.92,14.03]
self.operate_rate = 0.18
self.fuel_rate = 0.125
self.remote_fee= 18.5
def is_remote(self,postcode):
"""判断是否偏远,1偏远0非偏远"""
# 先判断邮编是否合法
if not re.match(r'^\d{5}$', postcode):
return "邮编格式不合法"
if postcode.startswith("20"):
return 1
remove_postcodes = ["17111","17123","17190","17310","17370","17410","17480","17550","17580","17590","17630","17650",
"17670","17740","17840","17880","17940","56360","56590","56780","56840","85330","85350"]
return 1 if postcode in remove_postcodes else 0
# DPD-ZG 西班牙实现
class DPDZGLogistics_SP(DPDZGLogistics):
country_code = 'ES'
country = 'Spain'
def __init__(self):
super().__init__()
self.base_fee = [12.1,13.17,15.46]
self.operate_rate = 0.18
self.fuel_rate = 0.125
self.remote_fee= 32
def is_remote(self,postcode):
"""判断是否偏远,1偏远0非偏远"""
# 先判断邮编是否合法
if not re.match(r'^\d{5}$', postcode):
return "邮编格式不合法"
if postcode.startswith("07") or postcode.startswith("35") or postcode.startswith("38"):
return 1
else:
return 0
# 卡派-ASL 欧洲各国基类
class KPASLLogistics(TailLogistics):
"""卡派—ASL"""
company = "卡派-ASL" # 欧洲国家的卡派
currency = "EUR"
2025-06-23 16:08:03 +08:00
logistics_type = LogisticsType.LTL
2025-06-17 13:40:20 +08:00
parent_current_directory = Path(__file__).parent.parent
price_path = parent_current_directory.joinpath("data")
_price_files = price_path.joinpath("欧洲卡派.xlsx")
ltl_cost = None
def __new__(cls):
"""实现单例模式,只加载一次文件"""
if cls.ltl_cost is None:
cls.ltl_cost = pandas.read_excel(cls._price_files,sheet_name="DHL卡派IP报价")
return super().__new__(cls)
def __init__(self):
super().__init__()
self.fuel_rate = 0.08
def calculate_fee(self,packages,postcode):
detail_amount = {
"base":0.00,
"fuel":0.00,
"tail_amount":0.00
}
postcode = str(postcode)
postcode_str = postcode[:2]
bill_weight = 0
for package in packages:
bill_weight +=package.weight/1000
base_df = self.ltl_cost[(self.ltl_cost['Country']==self.country_code)&(self.ltl_cost['Postalcode']==postcode_str)]
if base_df.empty:
detail_amount['tail_amount'] = 99999
return detail_amount
detail_amount['base'] = base_df['1 IP'].iloc[0]
detail_amount['fuel'] = detail_amount['base'] * self.fuel_rate
detail_amount['tail_amount'] = detail_amount['base'] + detail_amount['fuel']
return detail_amount
# ASL卡派德国
class KPASLLogistics_DE(KPASLLogistics):
country_code = 'DE'
country = 'Germany'
def __init__(self):
super().__init__()
def calculate_fee(self,packages,postcode):
return super().calculate_fee(packages,postcode)
# ASL卡派法国
class KPASLLogistics_FR(KPASLLogistics):
country_code = 'FR'
country = 'France'
def __init__(self):
super().__init__()
def calculate_fee(self,packages,postcode):
return super().calculate_fee(packages,postcode)
# ASL卡派西班牙
class KPASLLogistics_SP(KPASLLogistics):
country_code = 'ES'
country = 'Spain'
def __init__(self):
super().__init__()
def calculate_fee(self,packages,postcode):
return super().calculate_fee(packages,postcode)
# 卡派-GEL 欧洲各国基类
class KPGELLogistics(TailLogistics):
"""卡派—GEL
6000抛重,计费重
"""
company = "卡派-GEL" # 欧洲国家的卡派
currency = "EUR"
2025-06-23 16:08:03 +08:00
logistics_type = LogisticsType.LTL
2025-06-17 13:40:20 +08:00
parent_current_directory = Path(__file__).parent.parent
price_path = parent_current_directory.joinpath("data")
_price_files = price_path.joinpath("欧洲卡派-GEL.xlsx")
base_cost = None
def __new__(cls):
"""实现单例模式,只加载一次文件"""
if cls.base_cost is None:
cls.base_cost = pandas.read_excel(cls._price_files,sheet_name="基础费用")
return super().__new__(cls)
def __init__(self):
super().__init__()
self.notify = 5 # 预约通知费,必收
self.remote_fee = 9 # 德国偏远费17/18/19
self.multiple_package = 2.5 # 多包裹附加费超过三个包裹的每个包裹2.5EUR
self.responsibility = 1.85 # 责任保险,必收
self.management = 2 # 订单管理费,必收
self.toll = 0.024 # 过路过桥费0.024*分段最大kg
def is_remote(self,postcode):
"""判断分区,德国只有1,法国有123,"""
raise NotImplementedError("Subclasses must implement remote calculation.")
def calculate_fee(self,packages,postcode):
# 6000计费重
detail_amount = {
"base":0.00,
"notify":0.00,
"multiple_package":0.00,
"responsibility":0.00,
"management":0.00,
"toll" :0.00,
"remote":0.00,
"tail_amount":0.00
}
postcode = str(postcode)
zone = self.is_remote(postcode)
# 德国偏远费
if postcode[:2] in ['17','18','19'] and self.country_code =='DE':
detail_amount['remote'] = self.remote_fee
base_df = self.base_cost[(self.base_cost['国家'] == self.country_code) & (self.base_cost['分区'] == zone)]
if base_df.empty:
detail_amount['tail_amount'] = 99999
return detail_amount
total_weight = 0
for package in packages:
if (package.fst_size > 320 or package.sed_size>170 or package.trd_size>120) and (self.country_code != "DE"):
detail_amount['tail_amount'] = 99999
return detail_amount
if package.fst_size > 320 or package.sed_size>200 or package.trd_size>120:
detail_amount['tail_amount'] = 99999
return detail_amount
billing_weight = max(package.weight/1000,package.get_volume_weight(6000))
total_weight += billing_weight
# 基础费用
weight_limit =[col for col in base_df.columns if isinstance(col, (int, float))]
for weight in weight_limit:
if total_weight <= weight:
detail_amount['base'] = base_df[weight].iloc[0]
detail_amount['toll'] = self.toll * int(weight)
break
if total_weight > 300:
detail_amount['base'] = detail_amount['base'] * total_weight
detail_amount['notify'] = self.notify
detail_amount['multiple_package'] = self.multiple_package * len(packages) if len(packages) > 3 else 0
detail_amount['responsibility'] = self.responsibility
detail_amount['management'] = self.management
for key in detail_amount:
if key != 'tail_amount':
detail_amount['tail_amount'] += detail_amount[key]
return detail_amount
# GEL卡派德国
class KPGELLogistics_DE(KPGELLogistics):
country_code = 'DE'
country = 'Germany'
def __init__(self):
super().__init__()
def is_remote(self,postcode):
return 1
def calculate_fee(self,packages,postcode):
return super().calculate_fee(packages,postcode)
# GEL卡派法国
class KPGELLogistics_FR(KPGELLogistics):
country_code = 'FR'
country = 'France'
def __init__(self):
super().__init__()
def is_remote(self, postcode):
postcode = str(postcode)
postcode_str = postcode[:2]
if postcode_str in ["75","78","91","92","93","94","95",]:
return 1
elif postcode_str in [
"01", "02", "03", "08", "10", "14", "16", "17", "18","21", "22", "23", "25", "27", "28", "29",
"35", "36", "37", "39", "41", "42", "44", "45","49", "50", "51", "52", "53", "54", "55", "56",
"57","58", "59", "60", "61", "62", "63","67", "68", "69", "70", "71", "72", "73", "74",
"76", "77", "79", "80","85", "86", "87", "88", "89", "90"
]:
return 2
elif postcode_str in [
"04", "05", "06", "07", "09", "11", "12", "13", "15", "19", "24", "26",
"30", "31", "32", "33", "34", "38", "40", "43", "46", "47", "48",
"64", "65", "66", "81", "82", "83", "84"
]:
return 3
def calculate_fee(self,packages,postcode):
return super().calculate_fee(packages,postcode)
# GEL卡派西班牙
class KPGELLogistics_SP(KPGELLogistics):
country_code = 'ES'
country = 'Spain'
def __init__(self):
super().__init__()
def is_remote(self, postcode):
postcode = str(postcode)
if postcode.startswith("07"):
return 2
else:
return 1
def calculate_fee(self,packages,postcode):
return super().calculate_fee(packages,postcode)