logistics/logisticsClass/logisticsTail_UK.py

290 lines
13 KiB
Python
Raw Permalink 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.

""" 英国尾端物流模块实现类"""
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以内-->820,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