""" 英国尾端物流模块实现类""" import math from pathlib import Path import re import pandas from logisticsClass.logisticsBaseClass import LogisticsType, TailLogistics class DPDLogistics_UK(TailLogistics): # 实重计费 country_code = 'UK' country = 'United Kingdom' company = '智谷-DPD' currency = 'GBP' def __init__(self): super().__init__() self.base_fee = 3.7 self.oversize = 26 self.fuel_rate = 0.2 # BT开头的有20%燃油费 self.remote_fee = 35 def calculate_fee(self, packages, postcode): detail_amount = { "base":0.00, "oversize":0.00, "remote":0.00, "fuel":0.00, "tail_amount":0.00 } # zone = self.is_remote(postcode) # if zone == 2: # return detail_amount if self.is_remote(postcode): detail_amount['remote'] = self.remote_fee for package in packages: if package.weight >= 40000 or package.fst_size >= 175 or package.girth>=339: detail_amount['tail_amount'] =99999 return detail_amount if package.weight >= 30000 or package.fst_size >= 100 or package.sed_size >=60: detail_amount['oversize'] += self.oversize detail_amount['base'] += self.base_fee detail_amount['tail_amount'] = detail_amount['base'] + detail_amount['oversize'] + detail_amount['remote'] # if postcode.startswith('BT'): # detail_amount['fuel'] = detail_amount['tail_amount'] * self.fuel_rate # detail_amount['tail_amount'] += detail_amount['fuel'] return detail_amount def is_remote(self,postcode): """判断是否偏远,1偏远0非偏远""" # 先判断邮编是否合法 # if not re.match(r'^[A-Z]{1,2}[0-9]{1,2}[A-Z]?\s?[0-9][ABD-HJLNP-UW-Z]{2}$', postcode): # print("邮编不合法") # return 2 postcode = postcode.split()[0].upper() if postcode[0:2] not in ['BT','IM','JE','ZE','GY','HS','PO','IV','KA','KW','PH','PA']: return 0 remote_postcodes = ["GY1-9","HS1-9","PO30-41","IM1-9","JE1-4","ZE1-3","BT1-71", "BT86-88","BT74-82","BT92-97","IV1-28","IV36","IV40-56","PA20-38", "PA41-48","IV63","KA27-28","KW1-17","PH19-26","PH31-44"] postcodelist = [] # 解析并直接展开 for postcodes in remote_postcodes: if "-" in postcodes: # 如果包含范围 prefix = ''.join(filter(str.isalpha, postcodes)) # 提取字母前缀 start, end = map(int, (''.join(filter(str.isdigit, part)) for part in postcodes.split("-"))) postcodelist.extend([f"{prefix}{i}" for i in range(start, end + 1)]) else: postcodelist.append(postcodes) # 没有范围,直接添加 return 1 if postcode in postcodelist else 0 class bigLogistics_UK(TailLogistics): # 计费重5000取大 country_code = 'UK' country = 'United Kingdom' company = '智谷-大件' currency = 'GBP' def __init__(self): super().__init__() self.base_fee:float = 8 self.oversize:float = 0 self.congestion = 3 def calculate_fee(self, packages, postcode): detail_amount = { "base":0.00, "oversize":0.00, "congestion":0.00, # 伦敦中心地区有拥堵费 "tail_amount":0.00} # 0正常,1收拥堵费,2不派送 is_remote = self.is_remote(postcode) if is_remote==2: detail_amount['tail_amount'] =99999 return detail_amount elif is_remote==1: detail_amount['congestion'] = self.congestion for package in packages: # 计费重 volume_weight = package.get_volume_weight(5000) bill_weight = math.ceil(max(package.weight/1000,volume_weight)) if package.weight/1000 > 150 or volume_weight > 600 or package.fst_size > 300: detail_amount['tail_amount'] =99999 return detail_amount # 大包 计费重 20以内-->8,(20,50)-->0.5*(计费重-20)+8 , 50以上-->50+0.5*(计费重-50) if bill_weight < 50 and package.fst_size < 300: detail_amount['base'] += 8 if bill_weight > 20: detail_amount['oversize'] += 0.5 * (bill_weight -20) else: detail_amount['base'] += 50 if bill_weight > 50: detail_amount['oversize'] += 0.5 * (bill_weight -50) detail_amount['tail_amount'] = detail_amount['base'] + detail_amount['oversize'] + detail_amount['congestion'] return detail_amount def is_remote(self,postcode): """判断邮编情况,0正常1收拥堵费,2不派送""" # 先判断邮编是否合法 # if not re.match(r'^[A-Z]{1,2}[0-9]{1,2}[A-Z]?\s?[0-9][ABD-HJLNP-UW-Z]{2}$', postcode): # print("邮编不合法") # return 2 postcode = postcode.strip().upper() # 先判断london中心区 congestion = ['E','EC','N','NW','SE','SW','W','WC'] for c in congestion: pattern = rf"^{c}\d.*" if re.match(pattern, postcode): return 1 # prefixes = ["BS","AL","B","BA","BB","BD","BH","BL","BN","BR","CB","CH","CM","CR","CT","CV","CW","DA","DE","DN","DT","DY","EN","FY","GL","GU","HA","HD","HG","HP","HU","HX","IG","L","LA","LE","LN","LS","LU","M","ME","MK","NG","NN","OL","OX","PE","PO1-24","PR","RG","RH","RM","S","SG","SK","SL","SM","SO","SP","SS","ST","TF","TN","TW","UB","WA","WD","WF","WN","WR","WS","WV","YO","KT","OX","PR","RG","RM","S","SG","SK","SL","SM","SN","ST","TF","TW","UB","WA","WD","WF","WN","WR","WS","WV",] # for prefix in prefixes: # if "-" in prefix: # # 处理范围格式前缀 (如 "PO1-24") # po_post = [] # prefix_code = ''.join(filter(str.isalpha, prefix)) # 提取字母前缀 # start, end = map(int, (''.join(filter(str.isdigit, part)) for part in prefix.split("-"))) # po_post.extend([f"{prefix_code}{i}" for i in range(start, end + 1)]) # if postcode.split()[0] in po_post: # return 0 # else: # # 精确匹配单一前缀(后面必须是数字或空格) # pattern = rf"^{prefix}\d.*" # if re.match(pattern, postcode): # return 0 return 0 class KPZGLogistics_UK(TailLogistics): # 实重 country_code = 'UK' country = 'United Kingdom' company = '海GB-卡派' currency = 'GBP' logistics_type = LogisticsType.LTL def __init__(self): super().__init__() self.base_dice = { 55: ['RG7-8', 'RG14', 'RG17-29'], 65: ['B','CV','DY','NN','WS','WV'], 75: ['AL', 'BA', 'BB', 'BD', 'BL', 'BS', 'CB', 'CF', 'CH', 'CM', 'CW', 'DE', 'DN', 'FY', 'GL', 'HD', 'HG', 'HP', 'H', 'HU', 'L', 'LA1-9', 'LE', 'NG', 'NN', 'NP', 'OL', 'OX', 'PE1-19', 'PE26-29', 'PE34', 'PE38-99', 'PR', 'S', 'SG', 'SK', 'SN', 'ST', 'TF', 'WA', 'WD', 'WF', 'WN', 'WR', 'M', 'BR', 'HA','CO', 'DH', 'DL', 'IP', 'NE', 'NR', 'PE20-25', 'PE30-32', 'PE33', 'PE35-37', 'PO1-29', 'RH', 'SO', 'SP', 'SR', 'SS', 'TA', 'TS', 'YO', 'UB', 'KT', 'HR','DA','EN', 'IG','RG1-7','RG9-13','RG15-16','RG30-99','RM','SL'], 78: ['BH','DT','GU1-24','GU26-99','ME','TW','BN','EX','LD','TN','TQ'], 80: ['CR','SM'], 95: ['E','EC','N','NW','SE','SW','W','WC','CA','CT','DG','EH','FK','G','KA1-26','KA29-99','KY','LA10-99','LL','ML','PA1-19','PL','SA','SY','TD','TR'], 125:['DD','PH1-7','PH14'], 140:['IV1-3','IV30','IV36','AB10-16','AB21-25'], 150:['HS1-2','IV20-29','IV37-99','PH15-26','KW1-14','PH8-13','PO30-41'], 160:['HS3-99','KA27-28','PA20-33','PA35-59','PA62-75','PA79-99','PH27-41','PH45-50'], 200:['KW15-17','PA34','PA60-61','PA76-78','PH42-44', 'ZE' ], } def calculate_fee(self, packages, postcode): detail_amount = { "base":0.00, "oversize":0.00, "tail_amount":0.00} postcode_prefix = postcode.split()[0].upper() letters = ''.join([char for char in postcode_prefix if char.isalpha()]) numbers = ''.join([char for char in postcode_prefix if char.isdigit()]) if numbers=='': detail_amount['tail_amount'] = 99999 return detail_amount for price,codes in self.base_dice.items(): for code in codes: if letters == code: detail_amount['base'] += price break elif code.startswith(letters) and '-' in code: start, end = map(int, (''.join(filter(str.isdigit, part)) for part in code.split("-"))) if int(numbers) in range(start, end + 1): detail_amount['base'] += price break else: code_numbers = ''.join([char for char in code if char.isdigit()]) if code_numbers=='': continue if int(numbers) == int(code_numbers): detail_amount['base'] += price break if detail_amount['base']>0: break # 处理超尺寸问题 if detail_amount['base']==0: detail_amount['tail_amount'] = 99999 return detail_amount for package in packages: if package.fst_size > 180 or package.sed_size >120 or package.trd_size > 100: detail_amount['oversize'] = detail_amount['base'] detail_amount['tail_amount'] = detail_amount['base'] + detail_amount['oversize'] return detail_amount class KPNVlogistics_UK(TailLogistics): country_code = 'UK' country = 'United Kingdom' company = '卡派-NV' currency = 'GBP' 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 ltl_zone = None def __new__(cls): """实现单例模式,只加载一次文件""" if cls.ltl_cost is None or cls.ltl_zone is None: cls.ltl_cost = pandas.read_excel(cls._price_files,sheet_name="运费") cls.ltl_zone = pandas.read_excel(cls._price_files,sheet_name="分区") return super().__new__(cls) def __init__(self): super().__init__() self.base_fee = 0 self.fuel_rate = 0.1 def is_remote(self,postcode): """根据邮编分区,返回分区""" postcode_prefix = postcode.split()[0].upper() postcode_prefix = str(postcode_prefix) zone_df = self.ltl_zone[self.ltl_zone['邮编']== postcode_prefix] if not zone_df.empty: return zone_df['区域'].values[0] return "不在配送范围内" def calculate_fee(self, packages, postcode): detail_amount = { "base":0.00, "fuel":0.00, "tail_amount":0.00 } zone = self.is_remote(postcode) if zone == "不在配送范围内": detail_amount['tail_amount'] = 99999 return detail_amount for package in packages: tuopan = math.ceil(package.fst_size/120) tuopan = min(tuopan, 7) base_df = self.ltl_cost[(self.ltl_cost['分区']==zone)&(self.ltl_cost['托盘']==tuopan)] if base_df.empty: detail_amount['tail_amount'] = 99999 return detail_amount self.base_fee = base_df['运费'].values[0] price = self.base_fee * tuopan/len(packages) detail_amount['base'] += price detail_amount['fuel'] = detail_amount['base'] * self.fuel_rate detail_amount['tail_amount'] = detail_amount['base']+detail_amount['fuel'] return detail_amount # class KPDXLogistics_UK(TailLogistics): # country_code = 'UK' # country = 'United Kingdom' # company = 'DX-EL' # currency = 'GBP' # logistics_type = LogisticsType.LTL # def __init__(self): # super().__init__() # self.base_fee = 0 # def calculate_fee(self, packages, postcode): # detail_amount = { # "base":0.00, # "oversize":0.00, # "tail_amount":0.00 # } if __name__ == '__main__': # # 关闭渠道 bigLogistics_UK.active = True