""" 欧洲国家尾端物流模块实现类""" 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" logistics_type = LogisticsType.LTL 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" logistics_type = LogisticsType.LTL 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)