2025-06-17 13:40:20 +08:00
|
|
|
|
""" 英国尾端物流模块实现类"""
|
|
|
|
|
|
import math
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
import re
|
|
|
|
|
|
|
|
|
|
|
|
import pandas
|
|
|
|
|
|
from logisticsClass.logisticsBaseClass import LogisticsType, TailLogistics
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-11-17 15:37:15 +08:00
|
|
|
|
class ZGDPDLogistics_UK(TailLogistics):
|
2025-06-17 13:40:20 +08:00
|
|
|
|
# 实重计费
|
|
|
|
|
|
country_code = 'UK'
|
|
|
|
|
|
country = 'United Kingdom'
|
|
|
|
|
|
company = '智谷-DPD'
|
|
|
|
|
|
currency = 'GBP'
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
super().__init__()
|
2025-07-29 09:05:57 +08:00
|
|
|
|
self.base_fee = 3.8
|
2025-06-17 13:40:20 +08:00
|
|
|
|
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:
|
2025-07-29 09:05:57 +08:00
|
|
|
|
if package.weight >= 40000 or package.fst_size >= 175 or package.girth>=339:
|
|
|
|
|
|
detail_amount['tail_amount'] =99999
|
|
|
|
|
|
return detail_amount
|
2025-11-17 15:37:15 +08:00
|
|
|
|
if package.weight > 30000 or package.fst_size >= 100 or package.sed_size >=60:
|
2025-06-17 13:40:20 +08:00
|
|
|
|
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
|
|
|
|
|
|
|
2025-11-17 15:37:15 +08:00
|
|
|
|
class SAIRDPDLogistics_UK(TailLogistics):
|
|
|
|
|
|
# 实重计费
|
|
|
|
|
|
country_code = 'UK'
|
|
|
|
|
|
country = 'United Kingdom'
|
|
|
|
|
|
company = 'SAIR-DPD'
|
|
|
|
|
|
currency = 'GBP'
|
|
|
|
|
|
|
|
|
|
|
|
parent_current_directory = Path(__file__).parent.parent
|
|
|
|
|
|
price_path = parent_current_directory.joinpath("data")
|
|
|
|
|
|
_price_files = price_path.joinpath("智谷物流费.xlsx")
|
|
|
|
|
|
dpd_zone = None
|
|
|
|
|
|
def __new__(cls):
|
|
|
|
|
|
"""实现单例模式,只加载一次文件"""
|
|
|
|
|
|
if cls.dpd_zone is None:
|
|
|
|
|
|
# 取AB两列即可
|
|
|
|
|
|
cls.dpd_zone = pandas.read_excel(cls._price_files,sheet_name="DPD-SAIR分区",usecols='A:B')
|
|
|
|
|
|
return super().__new__(cls)
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
super().__init__()
|
|
|
|
|
|
self.base = {
|
|
|
|
|
|
'A': 3.5,
|
|
|
|
|
|
'B': 10,
|
|
|
|
|
|
'C': 12,
|
|
|
|
|
|
'D': 16
|
|
|
|
|
|
}
|
|
|
|
|
|
self.transportation = 0.11 # 交通费,每单0.11
|
|
|
|
|
|
self.remote_fee = 0.95 # 偏远费
|
|
|
|
|
|
self.fuel_rate = 0.05 # 燃油费率
|
|
|
|
|
|
def is_remote(self,postcode):
|
|
|
|
|
|
"""根据邮编分区,返回分区"""
|
|
|
|
|
|
postcode_prefix = postcode.split()[0].upper()
|
|
|
|
|
|
postcode_prefix = str(postcode_prefix)
|
|
|
|
|
|
# 取前缀的字母
|
|
|
|
|
|
letters = ''.join(re.findall(r'[A-Za-z]', postcode.split()[0])).upper()
|
|
|
|
|
|
letters=str(letters)
|
|
|
|
|
|
# 先匹配字母,字母能找到就直接用,字母对应的找不到,再匹配带上数字的
|
|
|
|
|
|
zone_df = self.dpd_zone[self.dpd_zone['邮编']== letters]
|
|
|
|
|
|
if zone_df.empty:
|
|
|
|
|
|
zone_df = self.dpd_zone[self.dpd_zone['邮编']== postcode_prefix]
|
|
|
|
|
|
if not zone_df.empty:
|
|
|
|
|
|
return zone_df['分区'].values[0]
|
|
|
|
|
|
return "A"
|
|
|
|
|
|
|
|
|
|
|
|
def calculate_fee(self, packages, postcode):
|
|
|
|
|
|
detail_amount = {
|
|
|
|
|
|
"base":0.00,
|
|
|
|
|
|
"transportation":0.00,
|
|
|
|
|
|
"remote":0.00,
|
|
|
|
|
|
"fuel":0.00,
|
|
|
|
|
|
"tail_amount":0.00
|
|
|
|
|
|
}
|
|
|
|
|
|
zone = self.is_remote(postcode)
|
|
|
|
|
|
if zone == "不在配送范围内":
|
|
|
|
|
|
detail_amount['tail_amount'] = 99999
|
|
|
|
|
|
return detail_amount
|
|
|
|
|
|
base_fee = self.base[zone]
|
|
|
|
|
|
postcode_prefix = postcode.split()[0].upper()
|
|
|
|
|
|
postcode_prefix = str(postcode_prefix)
|
|
|
|
|
|
if postcode_prefix in ['E1','EC1','EC2','EC3','EC4','NW1','SE1','SE11','SW1','SW3','SW7','W1','W10','W11','W2','W8','WC1','WC2']:
|
|
|
|
|
|
detail_amount['remote'] = self.remote_fee
|
|
|
|
|
|
for package in packages:
|
|
|
|
|
|
if package.weight > 32000 or package.fst_size > 230:
|
|
|
|
|
|
detail_amount['tail_amount'] =99999
|
|
|
|
|
|
return detail_amount
|
|
|
|
|
|
detail_amount["base"] += base_fee
|
|
|
|
|
|
detail_amount['transportation']=self.transportation
|
|
|
|
|
|
detail_amount['tail_amount'] = (detail_amount['base'] + detail_amount['transportation'] + detail_amount['remote'])*(1+self.fuel_rate)
|
|
|
|
|
|
detail_amount['fuel']=self.fuel_rate*(detail_amount['base'] + detail_amount['transportation'] + detail_amount['remote'])
|
|
|
|
|
|
return detail_amount
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-06-17 13:40:20 +08:00
|
|
|
|
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'
|
2025-07-16 00:05:12 +08:00
|
|
|
|
company = '卡派-ZG'
|
2025-06-17 13:40:20 +08:00
|
|
|
|
currency = 'GBP'
|
2025-06-23 16:08:03 +08:00
|
|
|
|
logistics_type = LogisticsType.LTL
|
2025-06-17 13:40:20 +08:00
|
|
|
|
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
|
2025-11-17 15:37:15 +08:00
|
|
|
|
# 计算托盘数 初始为1个
|
|
|
|
|
|
nember = 0
|
2025-06-17 13:40:20 +08:00
|
|
|
|
for package in packages:
|
2025-11-17 15:37:15 +08:00
|
|
|
|
nember =nember + (int(package.fst_size/180)+int(package.sed_size/120)+int(package.weight/800000))
|
|
|
|
|
|
detail_amount['oversize'] = detail_amount['base'] * nember
|
2025-06-17 13:40:20 +08:00
|
|
|
|
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'
|
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
|
|
|
|
|
|
ltl_zone = None
|
|
|
|
|
|
def __new__(cls):
|
|
|
|
|
|
"""实现单例模式,只加载一次文件"""
|
|
|
|
|
|
if cls.ltl_cost is None or cls.ltl_zone is None:
|
2025-11-17 15:37:15 +08:00
|
|
|
|
cls.ltl_cost = pandas.read_excel(cls._price_files,sheet_name="NV运费")
|
|
|
|
|
|
cls.ltl_zone = pandas.read_excel(cls._price_files,sheet_name="NV分区")
|
2025-06-17 13:40:20 +08:00
|
|
|
|
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
|
|
|
|
|
|
|
2025-11-17 15:37:15 +08:00
|
|
|
|
class ZGbigLogistics_UK(TailLogistics):
|
|
|
|
|
|
country_code = 'UK'
|
|
|
|
|
|
country = 'United Kingdom'
|
|
|
|
|
|
company = '海GB-大件' # 分区的智谷大件
|
|
|
|
|
|
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")
|
|
|
|
|
|
xl_zone = None
|
|
|
|
|
|
def __new__(cls):
|
|
|
|
|
|
"""实现单例模式,只加载一次文件"""
|
|
|
|
|
|
if cls.xl_zone is None:
|
|
|
|
|
|
# 取AB两列即可
|
|
|
|
|
|
cls.xl_zone = pandas.read_excel(cls._price_files,sheet_name="大件分区",usecols='A:B')
|
|
|
|
|
|
return super().__new__(cls)
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
super().__init__()
|
|
|
|
|
|
def is_remote(self,postcode):
|
|
|
|
|
|
"""根据邮编分区,返回分区"""
|
|
|
|
|
|
postcode_prefix = postcode.split()[0].upper()
|
|
|
|
|
|
postcode_prefix = str(postcode_prefix)
|
|
|
|
|
|
# 取前缀的字母
|
|
|
|
|
|
letters = ''.join(re.findall(r'[A-Za-z]', postcode.split()[0])).upper()
|
|
|
|
|
|
letters=str(letters)
|
|
|
|
|
|
# 先匹配字母,字母能找到就直接用,字母对应的找不到,再匹配带上数字的
|
|
|
|
|
|
zone_df = self.xl_zone[self.xl_zone['邮编']== letters]
|
|
|
|
|
|
if zone_df.empty:
|
|
|
|
|
|
zone_df = self.xl_zone[self.xl_zone['邮编']== postcode_prefix]
|
|
|
|
|
|
if not zone_df.empty:
|
|
|
|
|
|
return zone_df['分区'].values[0]
|
|
|
|
|
|
return "不在配送范围内"
|
|
|
|
|
|
def calculate_fee(self, packages, postcode):
|
|
|
|
|
|
"""5000计费重 一票一件
|
|
|
|
|
|
单个包裹实重上限150kg
|
|
|
|
|
|
体积重上限600Kg
|
|
|
|
|
|
尺寸上限: 300*180*180cm
|
|
|
|
|
|
"""
|
|
|
|
|
|
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:
|
|
|
|
|
|
volume_weight = package.get_volume_weight(5000)
|
|
|
|
|
|
if package.weight>=150000 or volume_weight>=600 or package.fst_size>300 or package.sed_size>180:
|
|
|
|
|
|
detail_amount['tail_amount'] = 99999
|
|
|
|
|
|
return detail_amount
|
|
|
|
|
|
bill_weight = max(package.weight/1000, volume_weight)
|
|
|
|
|
|
detail_amount['base'] += self.get_shipping_cost(zone, bill_weight)
|
|
|
|
|
|
detail_amount['tail_amount'] = detail_amount['base']
|
|
|
|
|
|
return detail_amount
|
|
|
|
|
|
|
|
|
|
|
|
def get_shipping_cost(self, zone, weight):
|
|
|
|
|
|
"""获取运费"""
|
|
|
|
|
|
price_table = {
|
|
|
|
|
|
'A': [14, 19, 25, 50, 65, 85, 100, 120],
|
|
|
|
|
|
'B': [19, 24, 30, 55, 70, 90, 105, 125],
|
|
|
|
|
|
'C': [27, 30, 35, 60, 80, 95, 115, 135],
|
|
|
|
|
|
'D': [36, 40, 45, 70, 90, 105, 125, 145]
|
|
|
|
|
|
}
|
|
|
|
|
|
weight_breaks = [30, 40, 50, 70, 90, 110, 130, 150]
|
|
|
|
|
|
overweight_rate = 0.5 # 续重费率 GBP/KG
|
|
|
|
|
|
|
|
|
|
|
|
prices = price_table[zone]
|
|
|
|
|
|
|
|
|
|
|
|
# 150KG以内的标准费用
|
|
|
|
|
|
for i, break_point in enumerate(weight_breaks):
|
|
|
|
|
|
if weight < break_point:
|
|
|
|
|
|
return prices[i]
|
|
|
|
|
|
|
|
|
|
|
|
# 超过150KG的部分:基础费用 + 续重费用
|
|
|
|
|
|
if weight >= 150:
|
|
|
|
|
|
base_cost = prices[-1] # 150KG对应的基础费用(120, 125, 135, 145)
|
|
|
|
|
|
overweight = weight - 150
|
|
|
|
|
|
return base_cost + overweight * overweight_rate
|
|
|
|
|
|
return prices[-1] # 默认返回最后一个价格
|
2025-07-16 00:05:12 +08:00
|
|
|
|
|
2025-11-17 15:37:15 +08:00
|
|
|
|
# BJS-SAIR
|
|
|
|
|
|
# class BJSbigLogistics_UK(TailLogistics):
|
2025-06-17 13:40:20 +08:00
|
|
|
|
# country_code = 'UK'
|
|
|
|
|
|
# country = 'United Kingdom'
|
2025-11-17 15:37:15 +08:00
|
|
|
|
# company = 'BJS-SAIR'
|
2025-06-17 13:40:20 +08:00
|
|
|
|
# currency = 'GBP'
|
2025-11-17 15:37:15 +08:00
|
|
|
|
# logistics_type = LogisticsType.LTL
|
|
|
|
|
|
# parent_current_directory = Path(__file__).parent.parent
|
|
|
|
|
|
# price_path = parent_current_directory.joinpath("data")
|
|
|
|
|
|
# _price_files = price_path.joinpath("")
|
|
|
|
|
|
# xl_zone = None
|
|
|
|
|
|
# def __new__(cls):
|
|
|
|
|
|
# """实现单例模式,只加载一次文件"""
|
|
|
|
|
|
# if cls.xl_zone is None:
|
|
|
|
|
|
# # 取AB两列即可
|
|
|
|
|
|
# cls.xl_zone = pandas.read_excel(cls._price_files,sheet_name="BJS分区",usecols='A:B')
|
|
|
|
|
|
# return super().__new__(cls)
|
|
|
|
|
|
|
2025-06-17 13:40:20 +08:00
|
|
|
|
# def __init__(self):
|
|
|
|
|
|
# super().__init__()
|
2025-11-17 15:37:15 +08:00
|
|
|
|
# def is_remote(self,postcode):
|
|
|
|
|
|
# """根据邮编分区,返回分区"""
|
|
|
|
|
|
# postcode_prefix = postcode.split()[0].upper()
|
|
|
|
|
|
# postcode_prefix = str(postcode_prefix)
|
|
|
|
|
|
# # 取前缀的字母
|
|
|
|
|
|
# letters = ''.join(re.findall(r'[A-Za-z]', postcode.split()[0])).upper()
|
|
|
|
|
|
# letters=str(letters)
|
|
|
|
|
|
# # 先匹配字母,字母能找到就直接用,字母对应的找不到,再匹配带上数字的
|
|
|
|
|
|
# zone_df = self.xl_zone[self.xl_zone['邮编']== letters]
|
|
|
|
|
|
# if zone_df.empty:
|
|
|
|
|
|
# zone_df = self.xl_zone[self.xl_zone['邮编']== postcode_prefix]
|
|
|
|
|
|
# if not zone_df.empty:
|
|
|
|
|
|
# return zone_df['分区'].values[0]
|
|
|
|
|
|
# return "不在配送范围内"
|
2025-06-17 13:40:20 +08:00
|
|
|
|
# def calculate_fee(self, packages, postcode):
|
2025-11-17 15:37:15 +08:00
|
|
|
|
# """5000计费重 一票一件
|
|
|
|
|
|
# 单个包裹实重上限150kg
|
|
|
|
|
|
# 体积重上限600Kg
|
|
|
|
|
|
# 尺寸上限: 300*180*180cm
|
|
|
|
|
|
# """
|
2025-06-17 13:40:20 +08:00
|
|
|
|
# detail_amount = {
|
2025-11-17 15:37:15 +08:00
|
|
|
|
# "base":0.00,
|
|
|
|
|
|
# "fuel":0.00,
|
2025-06-17 13:40:20 +08:00
|
|
|
|
# "tail_amount":0.00
|
2025-11-17 15:37:15 +08:00
|
|
|
|
# }
|
|
|
|
|
|
# zone = self.is_remote(postcode)
|
|
|
|
|
|
# if zone == "不在配送范围内":
|
|
|
|
|
|
# detail_amount['tail_amount'] = 99999
|
|
|
|
|
|
# return detail_amount
|
2025-09-10 13:37:38 +08:00
|
|
|
|
# for package in packages:
|
2025-11-17 15:37:15 +08:00
|
|
|
|
# volume_weight = package.get_volume_weight(5000)
|
|
|
|
|
|
# if package.weight>=150000 or volume_weight>=600 or package.fst_size>300 or package.sed_size>180:
|
2025-09-10 13:37:38 +08:00
|
|
|
|
# detail_amount['tail_amount'] = 99999
|
|
|
|
|
|
# return detail_amount
|
2025-11-17 15:37:15 +08:00
|
|
|
|
# bill_weight = max(package.weight/1000, volume_weight)
|
|
|
|
|
|
# detail_amount['base'] += self.get_shipping_cost(zone, bill_weight)
|
|
|
|
|
|
# detail_amount['tail_amount'] = detail_amount['base']
|
|
|
|
|
|
# return detail_amount
|
2025-06-17 13:40:20 +08:00
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
|
# # 关闭渠道
|
|
|
|
|
|
bigLogistics_UK.active = True
|
|
|
|
|
|
|
|
|
|
|
|
|