This commit is contained in:
parent
c2cbfaf59c
commit
38cc3c8b51
BIN
data/Metro.xlsx (Stored with Git LFS)
BIN
data/Metro.xlsx (Stored with Git LFS)
Binary file not shown.
BIN
data/XMILES.xlsx (Stored with Git LFS)
BIN
data/XMILES.xlsx (Stored with Git LFS)
Binary file not shown.
|
|
@ -46,6 +46,17 @@ class PostLogistics_AU(TailLogistics):
|
|||
self.eparcel_price = self.__class__.eparcel_price
|
||||
self.fuel_rate =0.051# 0.074 0.064
|
||||
|
||||
def is_remote(self, postcode):
|
||||
"""判断分区"""
|
||||
# 先看邮编呐
|
||||
self.eparcel_zone['邮编'] = self.eparcel_zone['邮编'].astype(str).str.zfill(4)
|
||||
|
||||
filtered_df = self.eparcel_zone[self.eparcel_zone['邮编'] == postcode]
|
||||
if filtered_df.empty:
|
||||
return "不支持配送"
|
||||
|
||||
post = filtered_df['地区代码'].iloc[0] # 获取第一行的 '地区代码'
|
||||
return post
|
||||
def calculate_fee(self, packages, postcode):
|
||||
"""
|
||||
计费重: 常规计费, 4000
|
||||
|
|
@ -65,15 +76,10 @@ class PostLogistics_AU(TailLogistics):
|
|||
detail_amount['tail_amount'] = 88888
|
||||
return detail_amount
|
||||
|
||||
# 先看邮编呐
|
||||
self.eparcel_zone['邮编'] = self.eparcel_zone['邮编'].astype(str).str.zfill(4)
|
||||
|
||||
filtered_df = self.eparcel_zone[self.eparcel_zone['邮编'] == postcode]
|
||||
if filtered_df.empty:
|
||||
post = self.is_remote(postcode)
|
||||
if post == "不支持配送":
|
||||
detail_amount['tail_amount'] = 99999
|
||||
return detail_amount
|
||||
|
||||
post = filtered_df['地区代码'].iloc[0] # 获取第一行的 '地区代码'
|
||||
# 根据post筛选出对应行
|
||||
row = self.eparcel_price[self.eparcel_price['post'] == post]
|
||||
# 检查 row 是否为空
|
||||
|
|
@ -138,7 +144,16 @@ class TollLogistics_AU(TailLogistics):
|
|||
self.toll_remote = self.__class__.toll_remote
|
||||
self.fuel_rate = 0.0725
|
||||
self.oversize_fee = [15.5,62]
|
||||
|
||||
|
||||
def is_remote(self, postcode):
|
||||
"""判断分区"""
|
||||
# 先看邮编呐
|
||||
self.toll_zone['postcode'] = self.toll_zone['postcode'].astype(str).str.zfill(4)
|
||||
filtered_df = self.toll_zone[self.toll_zone['postcode'] == postcode]
|
||||
if filtered_df.empty:
|
||||
return "不支持配送"
|
||||
post = filtered_df['post'].iloc[0] # 获取第一行的 '地区代码'
|
||||
return post
|
||||
def calculate_fee(self, packages, postcode):
|
||||
# 抛重4000
|
||||
detail_amount = {
|
||||
|
|
@ -155,14 +170,10 @@ class TollLogistics_AU(TailLogistics):
|
|||
else:
|
||||
detail_amount['tail_amount'] = 88888
|
||||
return detail_amount
|
||||
# 先看邮编呐
|
||||
self.toll_zone['postcode'] = self.toll_zone['postcode'].astype(str).str.zfill(4)
|
||||
filtered_df = self.toll_zone[self.toll_zone['postcode'] == postcode]
|
||||
if filtered_df.empty:
|
||||
post = self.is_remote(postcode)
|
||||
if post == "不支持配送":
|
||||
detail_amount['tail_amount'] = 99999
|
||||
return detail_amount
|
||||
|
||||
post = filtered_df['post'].iloc[0] # 获取第一行的 '地区代码'
|
||||
# 根据post筛选出对应行
|
||||
row = self.toll_price[self.toll_price['post'] == post]
|
||||
# 检查 row 是否为空
|
||||
|
|
@ -246,6 +257,17 @@ class AllLogistics_AU(TailLogistics):
|
|||
self.oversize_fee = [7.8,10.92,12.41,26.42,92.15,123.96,160.07]
|
||||
self.handing_fee = 15.34 # 不收燃油费
|
||||
self.twomancrew_fee = [49.92,78,124.8] # 多包裹的情况下,只收一次最大的
|
||||
|
||||
def is_remote(self, postcode):
|
||||
"""判断分区"""
|
||||
# 先看邮编呐
|
||||
# 先看邮编呐
|
||||
self.all_zone['postcode'] = self.all_zone['postcode'].astype(str).str.zfill(4)
|
||||
filtered_df = self.all_zone[self.all_zone['postcode'] == postcode]
|
||||
if filtered_df.empty:
|
||||
return "不支持配送"
|
||||
post = filtered_df['post'].iloc[0] # 获取第一行的 '地区代码'
|
||||
return post
|
||||
def calculate_fee(self, packages, postcode):
|
||||
# 抛重4000
|
||||
detail_amount = {
|
||||
|
|
@ -265,15 +287,10 @@ class AllLogistics_AU(TailLogistics):
|
|||
else:
|
||||
detail_amount['tail_amount'] = 88888
|
||||
return detail_amount
|
||||
|
||||
# 先看邮编呐
|
||||
self.all_zone['postcode'] = self.all_zone['postcode'].astype(str).str.zfill(4)
|
||||
filtered_df = self.all_zone[self.all_zone['postcode'] == postcode]
|
||||
if filtered_df.empty:
|
||||
post = self.is_remote(postcode)
|
||||
if post == "不支持配送":
|
||||
detail_amount['tail_amount'] = 99999
|
||||
return detail_amount
|
||||
|
||||
post = filtered_df['post'].iloc[0] # 获取第一行的 '地区代码'
|
||||
try:
|
||||
base = self.all_price[self.all_price['post']==post]['Base'].iloc[0]
|
||||
per = self.all_price[self.all_price['post']==post]['Per'].iloc[0]
|
||||
|
|
@ -343,7 +360,82 @@ class AllLogistics_AU(TailLogistics):
|
|||
detail_amount['fuel'] += detail_amount[key] * self.fuel_rate
|
||||
detail_amount['tail_amount'] += detail_amount['fuel']
|
||||
return detail_amount
|
||||
|
||||
# TMS
|
||||
# class TMSLogistics_AU(TailLogistics):
|
||||
# country_code = 'AU'
|
||||
# country = 'Australia'
|
||||
# company = 'TMS'
|
||||
# currency = 'AUD'
|
||||
|
||||
# _is_loaded = False
|
||||
# parent_current_directory = Path(__file__).parent.parent
|
||||
# remote_path = parent_current_directory.joinpath("data")
|
||||
# _postcode_files = remote_path.joinpath("澳洲三大渠道.xlsx")
|
||||
|
||||
# def __new__(cls):
|
||||
# """实现单例模式,只加载一次文件"""
|
||||
# if not cls._is_loaded:
|
||||
# cls._load_postcodes() # 第一次实例化时加载文件
|
||||
# cls._is_loaded = True # 标记文件已加载
|
||||
# return super().__new__(cls)
|
||||
# @classmethod
|
||||
# def _load_postcodes(cls):
|
||||
# """加载文件"""
|
||||
# cls.toll_zone = pd.read_excel(str(cls._postcode_files),sheet_name="toll_postcode",usecols="A:B")
|
||||
# cls.toll_price = pd.read_excel(str(cls._postcode_files),sheet_name="toll",usecols="A:D")
|
||||
# cls.toll_remote = pd.read_excel(str(cls._postcode_files),sheet_name="toll_remote",usecols="A:D")
|
||||
|
||||
# def __init__(self):
|
||||
# super().__init__()
|
||||
# self.toll_zone = self.__class__.toll_zone
|
||||
# self.toll_price = self.__class__.toll_price
|
||||
# self.toll_remote = self.__class__.toll_remote
|
||||
# self.fuel_rate = 0.0725
|
||||
# self.oversize_fee = [15.5,62]
|
||||
|
||||
# def is_remote(self, postcode):
|
||||
# """判断分区"""
|
||||
# # 先看邮编呐
|
||||
# self.toll_zone['postcode'] = self.toll_zone['postcode'].astype(str).str.zfill(4)
|
||||
# filtered_df = self.toll_zone[self.toll_zone['postcode'] == postcode]
|
||||
# if filtered_df.empty:
|
||||
# return "不支持配送"
|
||||
# post = filtered_df['post'].iloc[0] # 获取第一行的 '地区代码'
|
||||
# return post
|
||||
# def calculate_fee(self, packages, postcode):
|
||||
# # 抛重4000
|
||||
# detail_amount = {
|
||||
# "base":0.00,
|
||||
# "oversize":0.00,
|
||||
# "remote":0.00,
|
||||
# "fuel":0.00,
|
||||
# "tail_amount":0.00
|
||||
# }
|
||||
# if isinstance(postcode, str):
|
||||
# postcode = ''.join(filter(str.isdigit, postcode)) # 只保留数字部分
|
||||
# if postcode: # 确保postcode不是空字符串
|
||||
# postcode = str(int(postcode)).zfill(4) # 转换为整数再补齐4位
|
||||
# else:
|
||||
# detail_amount['tail_amount'] = 88888
|
||||
# return detail_amount
|
||||
# post = self.is_remote(postcode)
|
||||
# if post == "不支持配送":
|
||||
# detail_amount['tail_amount'] = 99999
|
||||
# return detail_amount
|
||||
# # 根据post筛选出对应行
|
||||
# row = self.toll_price[self.toll_price['post'] == post]
|
||||
# # 检查 row 是否为空
|
||||
# if row.empty:
|
||||
# detail_amount['tail_amount'] = 99999
|
||||
# return detail_amount
|
||||
|
||||
# base = row['Base'].iloc[0]
|
||||
# per = self.toll_price[self.toll_price['post']==post]['Per'].iloc[0]
|
||||
# minimun = self.toll_price[self.toll_price['post']==post]['Minimun'].iloc[0]
|
||||
# for package in packages:
|
||||
|
||||
# return detail_amount
|
||||
if __name__ == '__main__':
|
||||
# 测试
|
||||
aau = PostLogistics_AU()
|
||||
|
|
|
|||
|
|
@ -860,5 +860,111 @@ class EURZGLogistics_IR(EURZGLogistics):
|
|||
self.limit_kg = 30
|
||||
self.remote = 999999
|
||||
self.fuel_rate = 0.01
|
||||
def is_remote(self,postcode):
|
||||
return 0
|
||||
|
||||
# DPD-NV,德国始发
|
||||
class DPDNVLogistics(TailLogistics):
|
||||
"""DPD-NV"""
|
||||
currency = "EUR"
|
||||
company = "DPD-NV"
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_20 = None
|
||||
self.base_31= None
|
||||
self.oversize = 4.62
|
||||
self.big_package = 40.15
|
||||
self.remote = 0
|
||||
def is_remote(self,postcode):
|
||||
"""判断是否偏远,1偏远0非偏远"""
|
||||
raise NotImplementedError("Subclasses must implement remote calculation.")
|
||||
def calculate_fee(self,packages,postcode):
|
||||
"""计算快递费用,40kg,250cm长,330cm围长"""
|
||||
detail_amount = {
|
||||
"base":0.00,
|
||||
"oversize":0.00,
|
||||
"big_package":0.00,
|
||||
"remote":0.00,
|
||||
"tail_amount":0.00
|
||||
}
|
||||
isremote = self.is_remote(postcode)
|
||||
if isremote == "邮编格式不合法":
|
||||
detail_amount['tail_amount'] = 99999
|
||||
return detail_amount
|
||||
if isremote == 1:
|
||||
detail_amount['remote'] = self.remote
|
||||
|
||||
for package in packages: # 逐个处理列表中的每个包裹
|
||||
if package.fst_size >= 250 or package.weight >= 40000 or package.girth>=330:
|
||||
detail_amount['tail_amount'] = 99999
|
||||
return detail_amount
|
||||
detail_amount['base'] += self.base_31 if package.weight >= 20000 else self.base_20
|
||||
if package.fst_size >= 175 or package.weight >= 31500 or package.girth>=300:
|
||||
detail_amount['big_package'] = self.big_package
|
||||
elif package.fst_size >= 120 or package.sed_size>=60 or package.volume>=150000:
|
||||
detail_amount['oversize'] = self.oversize
|
||||
|
||||
|
||||
for key in detail_amount:
|
||||
if key != 'tail_amount':
|
||||
detail_amount['tail_amount'] += detail_amount[key]
|
||||
return detail_amount
|
||||
class DPDNVLogistics_DE(DPDNVLogistics):
|
||||
country_code = 'DE'
|
||||
country = 'Germany'
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_20 = 4.3625
|
||||
self.base_31= 4.6125
|
||||
def is_remote(self,postcode):
|
||||
return 0
|
||||
|
||||
class DPDNVLogistics_FR(DPDNVLogistics):
|
||||
country_code = 'FR'
|
||||
country = 'France'
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_20 = 12.2375
|
||||
self.base_31= 12.4875
|
||||
def is_remote(self,postcode):
|
||||
return 0
|
||||
|
||||
class DPDNVLogistics_IT(DPDNVLogistics):
|
||||
country_code = 'IT'
|
||||
country = 'Italy'
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_20 = 14.0375
|
||||
self.base_31= 14.7375
|
||||
def is_remote(self,postcode):
|
||||
return 0
|
||||
|
||||
class DPDNVLogistics_PT(DPDNVLogistics):
|
||||
country_code = 'PT'
|
||||
country = 'Portugal'
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_20 = 18.45
|
||||
self.base_31= 22.375
|
||||
def is_remote(self,postcode):
|
||||
return 0
|
||||
|
||||
class DPDNVLogistics_ES(DPDNVLogistics):
|
||||
country_code = 'ES'
|
||||
country = 'Spain'
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_20 = 14.0375
|
||||
self.base_31=14.7375
|
||||
def is_remote(self,postcode):
|
||||
return 0
|
||||
|
||||
class DPDNVLogistics_IR(DPDNVLogistics):
|
||||
country_code = 'IR'
|
||||
country = 'Ireland'
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_20 = 18.45
|
||||
self.base_31= 22.375
|
||||
def is_remote(self,postcode):
|
||||
return 0
|
||||
|
|
@ -271,9 +271,9 @@ class KPNVlogistics_UK(TailLogistics):
|
|||
# class KPDXLogistics_UK(TailLogistics):
|
||||
# country_code = 'UK'
|
||||
# country = 'United Kingdom'
|
||||
# company = 'DX-EL'
|
||||
# company = 'AIT-FUXIN'
|
||||
# currency = 'GBP'
|
||||
# logistics_type = LogisticsType.LTL
|
||||
# # logistics_type = LogisticsType.LTL
|
||||
# def __init__(self):
|
||||
# super().__init__()
|
||||
# self.base_fee = 0
|
||||
|
|
@ -283,6 +283,11 @@ class KPNVlogistics_UK(TailLogistics):
|
|||
# "oversize":0.00,
|
||||
# "tail_amount":0.00
|
||||
# }
|
||||
# for package in packages:
|
||||
# if package.weight>=250000 or package.volume>=1500000:
|
||||
# detail_amount['tail_amount'] = 99999
|
||||
# return detail_amount
|
||||
# if
|
||||
|
||||
if __name__ == '__main__':
|
||||
# # 关闭渠道
|
||||
|
|
|
|||
|
|
@ -662,7 +662,6 @@ class MetroNYLogistics_US(TailLogistics):
|
|||
return detail_amount
|
||||
zone = zone_result.iloc[0]
|
||||
|
||||
|
||||
total_cuft = 0
|
||||
max_weight = 0
|
||||
max_length = 0
|
||||
|
|
|
|||
|
|
@ -137,30 +137,30 @@ def air_order_price(packages):
|
|||
return express_fee, express_type
|
||||
|
||||
# 美国售价2025
|
||||
def call_sell_price_2025(price, package_dict):
|
||||
def call_sell_price_2025(price, packages):
|
||||
"""
|
||||
price:采购价
|
||||
package_dict:包裹数据
|
||||
head_type:头程类型 海运/空运
|
||||
"""
|
||||
packages = Package_group()
|
||||
def extract_number(value):
|
||||
# 提取字符串中的第一个数字
|
||||
match = re.search(r"[-+]?\d*\.\d+|\d+", str(value))
|
||||
return float(match.group()) if match else 0.0
|
||||
# packages = Package_group()
|
||||
# def extract_number(value):
|
||||
# # 提取字符串中的第一个数字
|
||||
# match = re.search(r"[-+]?\d*\.\d+|\d+", str(value))
|
||||
# return float(match.group()) if match else 0.0
|
||||
|
||||
for key, package in package_dict.items():
|
||||
package['长'] = extract_number(package['长'])
|
||||
package['宽'] = extract_number(package['宽'])
|
||||
package['高'] = extract_number(package['高'])
|
||||
package['重量'] = extract_number(package['重量'])
|
||||
# for key, package in package_dict.items():
|
||||
# package['长'] = extract_number(package['长'])
|
||||
# package['宽'] = extract_number(package['宽'])
|
||||
# package['高'] = extract_number(package['高'])
|
||||
# package['重量'] = extract_number(package['重量'])
|
||||
|
||||
if package['长'] == 0 or package['宽'] == 0 or package['高'] == 0 or package['重量'] == 0:
|
||||
return 0,0,0
|
||||
packages.add_package(Package(key,package['长'], package['宽'], package['高'], package['重量']))
|
||||
# if package['长'] == 0 or package['宽'] == 0 or package['高'] == 0 or package['重量'] == 0:
|
||||
# return 0,0,0
|
||||
# packages.add_package(Package(key,package['长'], package['宽'], package['高'], package['重量']))
|
||||
|
||||
if packages is None:
|
||||
return 0,0,0
|
||||
# if packages is None:
|
||||
# return 0,0,0
|
||||
litfad = SellPriceBase.litfad_2025(packages, price,1)
|
||||
# 修改版本,网站售价
|
||||
sell_price = litfad.cal_sell_price_2025()
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@ ACTIVE_LOGISTICS = {
|
|||
|
||||
# EUR
|
||||
"DPDASLLogistics":False, # 需关闭
|
||||
"DPDNVLogistics":True,
|
||||
|
||||
# GB
|
||||
"bigLogistics_UK":False, # 需关闭
|
||||
"KPZGLogistics_UK":False, # 需关闭
|
||||
"KPZGLogistics_UK":True, # 需关闭
|
||||
"bigLogistics_UK":False, # 需关闭,智谷旧版大件,8,新版是分区的
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ logistics_name = {
|
|||
"空AMS-EUR":"EUR-ZG",
|
||||
"海NL-卡派2":"卡派-GEL",
|
||||
"海NL-DPD-ASL":"DPD-ASL",
|
||||
"海NL-DPD-NV":"--"
|
||||
"海NL-DPD-NV":"DPD-NV"
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
240
物流t投递审核.py
240
物流t投递审核.py
|
|
@ -10,13 +10,38 @@ from utils.logisticsBill import BillFactory, Billing
|
|||
from utils.countryOperator import OperateCountry
|
||||
from utils.Package import Package, Package_group
|
||||
from utils.logistics_name_config import logistics_name
|
||||
from datetime import date
|
||||
# 货币转换,其他转RMB
|
||||
def convert_currency(amount, current_currency):
|
||||
"""
|
||||
货币转换
|
||||
"""
|
||||
if amount is None or amount ==0:
|
||||
return "金额为空"
|
||||
if amount >=9999:
|
||||
return "无可用渠道"
|
||||
if current_currency == "USD":
|
||||
amount=amount*7
|
||||
elif current_currency == "GBP":
|
||||
amount =amount*9
|
||||
elif current_currency == "EUR":
|
||||
amount = amount*8
|
||||
elif current_currency == "AUD":
|
||||
amount = amount*5
|
||||
elif current_currency == "CAD":
|
||||
amount = amount*5
|
||||
elif current_currency == "JPY":
|
||||
amount =amount*0.05
|
||||
return amount
|
||||
|
||||
# 获取数据
|
||||
def fetch_order_data():
|
||||
"""从数据库获取原始订单数据"""
|
||||
with MySQLconnect('ods') as db:
|
||||
sql = """
|
||||
SELECT
|
||||
ol.order_date,
|
||||
DATE_FORMAT(ol.order_date, '%%Y-%%m-%%d') AS order_date,
|
||||
DATE_FORMAT(oe.投递时间, '%%Y-%%m-%%d') AS 投递时间,
|
||||
ol.fund_status,
|
||||
oe.`包裹状态`,
|
||||
oe.包裹号 AS package,
|
||||
|
|
@ -33,9 +58,8 @@ def fetch_order_data():
|
|||
pvi.weight AS 重量,
|
||||
pfi.express_fee AS 基础估算,
|
||||
pfi.express_additional_fee AS 偶发估算,
|
||||
pfi.express_fee + pfi.express_additional_fee AS 总估算,
|
||||
|
||||
oe.快递公司
|
||||
pfi.express_fee + pfi.express_additional_fee AS 包裹总估算,
|
||||
oe.快递公司 AS 投递渠道
|
||||
FROM
|
||||
ods.order_express oe
|
||||
LEFT JOIN ods.express_company ecm ON oe.快递公司 = ecm.快递公司
|
||||
|
|
@ -43,14 +67,16 @@ def fetch_order_data():
|
|||
LEFT JOIN ods.package_fee_info pfi ON oe.包裹号 = pfi.package
|
||||
LEFT JOIN ods.order_list ol ON oe.单号 = ol.order_id
|
||||
WHERE
|
||||
oe.包裹状态 REGEXP '已经投递|发货仓出库'
|
||||
AND oe.`快递公司` NOT REGEXP "--"
|
||||
oe.包裹状态 not REGEXP '已作废|--|客户签收'
|
||||
# AND oe.`快递公司` NOT REGEXP "--"
|
||||
AND `卡板发货时间` REGEXP "--"
|
||||
AND ol.fund_status NOT REGEXP '等待|全额退款'
|
||||
AND ol.site_name REGEXP 'litfad|kwoking|lakiq'
|
||||
AND oe.投递时间 >= DATE_SUB(NOW(), INTERVAL 3 DAY)
|
||||
AND pvi.length>0 AND pvi.width >0 AND pvi.hight>0 AND pvi.weight>0
|
||||
and oe.目的国 regexp 'United States'
|
||||
AND pvi.length>0 AND pvi.width >0 AND pvi.hight>0 AND pvi.weight>0
|
||||
and oe.目的国 regexp 'United States|Australia|United Kingdom|Germany|France|Spain|Italy|Netherlands|Belgium'
|
||||
order by ol.order_id,ol.order_date
|
||||
|
||||
"""
|
||||
|
||||
return pd.read_sql(sql, db.engine())
|
||||
|
|
@ -67,6 +93,7 @@ def cal_min_fee(raw_data: pd.DataFrame):
|
|||
package_group = Package_group()
|
||||
opCountry = OperateCountry(group['目的国'].iloc[0])
|
||||
express_fee = 0
|
||||
express_type=''
|
||||
for index, row in group.iterrows():
|
||||
# 计算一票一件
|
||||
packages=Package_group()
|
||||
|
|
@ -74,29 +101,37 @@ def cal_min_fee(raw_data: pd.DataFrame):
|
|||
packages.add_package(package)
|
||||
bill_express = Billing("1",opCountry,packages,row['postcode'],company_name=None,head_type=1,beizhu="")
|
||||
if bill_express.tail_amount[0] == 0 or bill_express.tail_amount[0] >=9999:
|
||||
df.loc[index,"快递尾端费用"] = "不可派"
|
||||
df.loc[index,"单票最小费用"] = ""
|
||||
df.loc[index,"单票渠道"] = ""
|
||||
express_fee = 999999
|
||||
express_type = '不可派'
|
||||
else:
|
||||
df.loc[index,"快递尾端费用"] = bill_express.tail_amount[0]
|
||||
df.loc[index,"快递尾端渠道"] = bill_express.company_name
|
||||
df.loc[index,"单票最小费用"] = bill_express.tail_amount[0]
|
||||
df.loc[index,"单票渠道"] = bill_express.company_name
|
||||
express_fee += bill_express.tail_amount[0]
|
||||
express_type = bill_express.logistic_type
|
||||
if bill_express.logistic_type == '卡派':
|
||||
express_type = '卡派单包裹'
|
||||
|
||||
# 计算一票多件
|
||||
package_group.add_package(package)
|
||||
# 计算一票多件
|
||||
if len(package_group) > 1:
|
||||
bill_ltl = Billing("1",opCountry,package_group,row['postcode'],company_name=None,head_type=1,beizhu="")
|
||||
df.loc[df['order_id']==order_id,'卡派尾端费用'] = bill_ltl.tail_amount[0]/len(package_group)
|
||||
df.loc[df['order_id']==order_id,'卡派尾端渠道'] = bill_ltl.company_name
|
||||
if bill_ltl.tail_amount[0] == 0 or bill_ltl.tail_amount[0] >=9999:
|
||||
df.loc[df['order_id']==order_id,'多票最小费用'] = ""
|
||||
df.loc[df['order_id']==order_id,'多票渠道'] = "不可派"
|
||||
df.loc[df['order_id']==order_id,'多票最小费用'] = bill_ltl.tail_amount[0]/len(package_group)
|
||||
df.loc[df['order_id']==order_id,'多票渠道'] = bill_ltl.company_name
|
||||
min_fee = min(bill_ltl.tail_amount[0],express_fee)
|
||||
df.loc[df['order_id']==order_id,'最优总费用'] = min_fee
|
||||
df.loc[df['order_id']==order_id,'最优渠道类型'] = bill_ltl.logistic_type if min_fee == bill_ltl.tail_amount[0] else express_type
|
||||
else:
|
||||
min_fee = express_fee
|
||||
if min_fee == express_fee:
|
||||
df.loc[df['order_id']==order_id,'最优总物流费用'] = min_fee
|
||||
df.loc[df['order_id']==order_id,'最优渠道类型'] = "快递"
|
||||
else:
|
||||
df.loc[df['order_id']==order_id,'最优总物流费用'] = min_fee
|
||||
df.loc[df['order_id']==order_id,'最优渠道类型'] = "卡派"
|
||||
df.loc[df['order_id']==order_id,'尾端货币'] = bill_ltl.tail_amount[1]
|
||||
df.loc[df['order_id']==order_id,'最优总费用'] = min_fee
|
||||
df.loc[df['order_id']==order_id,'最优渠道类型'] = express_type
|
||||
|
||||
df.loc[df['order_id']==order_id,'尾端货币'] = bill_express.tail_amount[1]
|
||||
return df
|
||||
|
||||
# 订单层面审核,防止出现混合渠道投递,卡派订单包含多个不同快递单号,多渠道订单总重量小于1000KG
|
||||
|
|
@ -113,7 +148,7 @@ def analyze_orders(raw_data: pd.DataFrame):
|
|||
'渠道类型': '未知类型',
|
||||
'基础估算': 0,
|
||||
'偶发估算': 0,
|
||||
'总估算': 0,
|
||||
'包裹总估算': 0,
|
||||
'重量': 0,
|
||||
'长': 0,
|
||||
'宽': 0,
|
||||
|
|
@ -137,16 +172,19 @@ def analyze_orders(raw_data: pd.DataFrame):
|
|||
grouped = data.groupby('order_id')
|
||||
|
||||
aggregated = pd.DataFrame({
|
||||
'订单时间': grouped['order_date'].first(),
|
||||
'最晚投递时间': grouped['投递时间'].max(),
|
||||
'包裹数量': grouped.size(),
|
||||
'总重量': grouped['重量'].sum(),
|
||||
'总基础估算': grouped['基础估算'].sum(),
|
||||
'总附加估算': grouped['偶发估算'].sum(),
|
||||
'总物流估算': grouped['总估算'].sum(),
|
||||
'订单总估算': grouped['包裹总估算'].sum(),
|
||||
'包裹数据': grouped.apply(create_package_details), # 使用新函数
|
||||
'快递公司列表': grouped['快递公司'].unique(),
|
||||
'投递渠道列表': grouped['投递渠道'].unique(),
|
||||
'渠道类型列表': grouped['渠道类型'].unique(),
|
||||
'邮编列表': grouped['postcode'].first(),
|
||||
'快递跟踪号': grouped['快递跟踪号'].unique()
|
||||
'快递跟踪号': grouped['快递跟踪号'].unique(),
|
||||
'最优渠道推荐':grouped['最优渠道'].first(),
|
||||
'最优渠道类型':grouped['最优渠道类型'].first(),
|
||||
'最优总费用':grouped['最优总费用'].first(),
|
||||
'费用差(RMB)':grouped['费用差(RMB)'].first(),
|
||||
}).reset_index()
|
||||
|
||||
# 3. 实现业务逻辑判断(保持不变)
|
||||
|
|
@ -159,7 +197,7 @@ def analyze_orders(raw_data: pd.DataFrame):
|
|||
return '未知类型'
|
||||
|
||||
def determine_channel_type(row):
|
||||
if len(row['快递公司列表']) > 1:
|
||||
if len(row['投递渠道列表']) > 1:
|
||||
return '多渠道'
|
||||
else:
|
||||
return '单渠道'
|
||||
|
|
@ -195,14 +233,14 @@ def analyze_orders(raw_data: pd.DataFrame):
|
|||
|
||||
rule_results = aggregated.apply(apply_business_rules, axis=1)
|
||||
aggregated = pd.concat([aggregated, rule_results], axis=1)
|
||||
|
||||
aggregated['测算日期'] = date.today().strftime("%Y-%m-%d")
|
||||
# 5. 整理最终输出列
|
||||
final_columns = [
|
||||
'order_id', '订单类型', '渠道种类',
|
||||
'order_id','订单时间','最晚投递时间', '订单类型', '渠道种类','快递跟踪号',
|
||||
'包裹数量', '总重量',
|
||||
'总基础估算', '总附加估算', '总物流估算',
|
||||
'快递公司列表', '邮编列表',
|
||||
'包裹数据' ,'状态', '备注','快递跟踪号'# 使用新列名
|
||||
'订单总估算',
|
||||
'投递渠道列表',
|
||||
'包裹数据' ,'状态', '备注','最优渠道推荐','最优总费用','费用差(RMB)','测算日期'# 使用新列名
|
||||
]
|
||||
|
||||
return aggregated[final_columns]
|
||||
|
|
@ -216,48 +254,130 @@ def analyze_logistics(df: pd.DataFrame):
|
|||
# 1. 计算最优渠道和费用
|
||||
df= cal_min_fee(df)
|
||||
# 判断渠道是否一致
|
||||
df['最优渠道'] = df.apply(lambda row: row['快递尾端渠道'] if row['最优渠道类型'] == "快递" else row['卡派尾端渠道'], axis=1)
|
||||
df['渠道一致'] = df.apply(lambda row: row['最优渠道'] == logistics_name.get(row['快递公司']), axis=1)
|
||||
df['测算日期'] = date.today().strftime("%Y-%m-%d")
|
||||
df['最优渠道'] = df.apply(lambda row: row['单票渠道'] if row['最优渠道类型'] == "快递" or row['最优渠道类型'] == "卡派单包裹" else row['多票渠道'], axis=1)
|
||||
df['渠道一致'] = df.apply(lambda row: row['最优渠道'] == logistics_name.get(row['投递渠道']), axis=1)
|
||||
# 2. 计算费用是否一致
|
||||
|
||||
def all_estimate(row):
|
||||
if row['总估算'] is None or row['总估算'] ==0:
|
||||
return "暂无系统估算值"
|
||||
if row['最优总物流费用'] is None or row['最优总物流费用'] ==0:
|
||||
return "暂无最优费用"
|
||||
if row['尾端货币'] == "USD":
|
||||
all_estimate= row['总估算']/7
|
||||
elif row['尾端货币'] == "GBP":
|
||||
all_estimate = row['总估算']/9
|
||||
elif row['尾端货币'] == "EUR":
|
||||
all_estimate = row['总估算']/8
|
||||
elif row['尾端货币'] == "AUD":
|
||||
all_estimate = row['总估算']/5
|
||||
elif row['尾端货币'] == "CAD":
|
||||
all_estimate = row['总估算']/5
|
||||
elif row['尾端货币'] == "JPY":
|
||||
all_estimate = row['总估算']/0.05
|
||||
if row['最优总费用'] >=9999:
|
||||
return "费用有误"
|
||||
all_estimate = convert_currency(row['最优总费用'], row['尾端货币'])
|
||||
return all_estimate
|
||||
|
||||
|
||||
df['费用一致'] = df.apply(lambda row: False if isinstance(all_estimate(row), str) else abs(all_estimate(row) - row['最优总物流费用']) < 1,axis=1)
|
||||
df['订单总估算']= df.groupby('order_id')['包裹总估算'].transform('sum')
|
||||
df['费用一致'] = df.apply(lambda row: False if isinstance(all_estimate(row), str) else abs(all_estimate(row) - row['订单总估算']) < 1,axis=1)
|
||||
|
||||
df['费用差(当地货币)'] = df.apply(lambda row: "费用有误" if isinstance(all_estimate(row), str) else row['最优总物流费用'] - all_estimate(row),axis=1)
|
||||
df['费用差(RMB)'] = df.apply(lambda row: "费用有误" if isinstance(all_estimate(row), str) else round( all_estimate(row)-row['订单总估算'],2),axis=1)
|
||||
df['是否改投'] = df.apply(lambda row: "不改投" if row['渠道一致'] == True else 0,axis=1) # 渠道一致只检查费用问题,无需改投,0不确定,需要人工确认
|
||||
df['异常情况'] = None
|
||||
# 调整输出列
|
||||
final_columns = ['order_date','投递时间','fund_status','包裹状态','运输方式','快递跟踪号','目的国','postcode','快递分区','order_id','package','长','宽','高','重量',
|
||||
'基础估算','偶发估算','包裹总估算','订单总估算','本地估算RMB','渠道类型','投递渠道','单票最小费用','单票渠道','多票最小费用','多票渠道','最优总费用',
|
||||
'最优渠道','最优渠道类型','尾端货币','渠道一致','费用一致','费用差(RMB)','测算日期','是否改投','异常情况']
|
||||
return df[final_columns]
|
||||
|
||||
# 系统渠道下的本地计算费用
|
||||
def local_fee_cal(df: pd.DataFrame):
|
||||
df_grouped= df.groupby('快递跟踪号')
|
||||
for order_num, group in df_grouped:
|
||||
postcode = group['postcode'].iloc[0]
|
||||
if pd.isna(postcode) or str(postcode).lower() == "nan":
|
||||
continue
|
||||
|
||||
packages= Package_group() # Metro-SAIR
|
||||
company_name = logistics_name.get(group['投递渠道'].iloc[0])
|
||||
opCountry = OperateCountry(group['目的国'].iloc[0])
|
||||
|
||||
total_weight=0 # 按体积重分费用
|
||||
for index,row in group.iterrows():
|
||||
if row['长'] == 0 or row['宽'] == 0 or row['高'] == 0 or row['重量'] == 0:
|
||||
continue
|
||||
total_weight = row['长']*row['宽']*row['高']/6000
|
||||
package = Package(row['package'],row['长'],row['宽'],row['高'],row['重量'])
|
||||
packages.add_package(package)
|
||||
try:
|
||||
bill = Billing(str(index),opCountry,packages,postcode,company_name=company_name,head_type=1,beizhu='1')
|
||||
for index,row in group.iterrows():
|
||||
propertion = bill.bill_dict()["体积重"]/total_weight
|
||||
tail_fee = bill.tail_amount[0]*propertion
|
||||
# 转rmb
|
||||
tail_fee = convert_currency(tail_fee, bill.tail_amount[1])
|
||||
df.loc[df['package']==row['package'],'本地估算RMB'] =round(tail_fee,2) if tail_fee <9999 else "暂无配置"
|
||||
except:
|
||||
df.loc[df['快递跟踪号'] == order_num, '本地估算RMB']= "暂无配置"
|
||||
continue
|
||||
print(bill)
|
||||
return df
|
||||
|
||||
|
||||
# 合并新旧df并写入
|
||||
def append_result(new_data, excel_path, only_columns):
|
||||
try:
|
||||
df_existing = pd.read_excel(excel_path,dtype={'order_id': str})
|
||||
except FileNotFoundError:
|
||||
# 文件不存在就直接存
|
||||
new_data.to_excel(excel_path, index=False)
|
||||
return
|
||||
# 识别老表里的特殊列
|
||||
special_cols = [col for col in ['是否改投', '异常情况','是否处理'] if col in df_existing.columns]
|
||||
# 新老合并(先全部concat起来以便后面筛选)
|
||||
df_all = pd.concat([df_existing, new_data], ignore_index=True)
|
||||
# 找出:重复的(即同时在新旧里都有的 only_columns 值)
|
||||
duplicated_keys = set(df_existing[only_columns]) & set(new_data[only_columns])
|
||||
# 1️⃣ 对有重复的 key → 保留 旧表的特殊列 + 新表的其他列
|
||||
if duplicated_keys:
|
||||
duplicated_keys = list(duplicated_keys)
|
||||
# 老表保留特殊列
|
||||
old_part = df_existing[df_existing[only_columns].isin(duplicated_keys)][[only_columns] + special_cols]
|
||||
# 新表保留除特殊列外的所有列
|
||||
new_part = new_data[new_data[only_columns].isin(duplicated_keys)]
|
||||
new_part_no_special = new_part.drop(columns=special_cols, errors='ignore')
|
||||
# 合并
|
||||
merged_part = new_part_no_special.merge(old_part, on=only_columns, how='left')
|
||||
else:
|
||||
merged_part = pd.DataFrame(columns=df_all.columns) # 空
|
||||
# 2️⃣ 对没有重复的 → 直接保留新表的完整行
|
||||
unique_new_part = new_data[~new_data[only_columns].isin(duplicated_keys)]
|
||||
# 3️⃣ 把 老数据的全部 + 处理好的新数据拼起来
|
||||
final_result = pd.concat([df_existing, merged_part, unique_new_part], ignore_index=True)
|
||||
# 去重(以 only_columns 为唯一键,保留最后一次出现的)
|
||||
final_result = final_result.drop_duplicates(subset=[only_columns], keep='last')
|
||||
# 写回
|
||||
final_result.to_excel(excel_path, index=False)
|
||||
|
||||
|
||||
def main():
|
||||
# 将前一天改投的数据保存到excel
|
||||
# 1.先读取logistics_analysis,并筛选是否改投列为1的数据
|
||||
# 2.将筛选结果追加到另一个excel
|
||||
df_new = pd.read_excel(r'D:\test\logistics\拦截数据\logistics_analysis.xlsx')
|
||||
df_new = df_new [df_new ['是否改投'] == "是"]
|
||||
df_new = df_new[['目的国','运输方式','order_id','package','基础估算','偶发估算','包裹总估算',
|
||||
'渠道类型','最优渠道类型','投递渠道','最优渠道','尾端货币','订单总估算','最优总费用','费用差(RMB)','测算日期','是否改投','异常情况']]
|
||||
target_file1 = r'D:\test\logistics\拦截数据\改投记录表.xlsx'
|
||||
append_result(df_new,target_file1,'package')
|
||||
print("前一天的数据已保存")
|
||||
|
||||
# 获取数据
|
||||
raw_data = fetch_order_data()
|
||||
print('已获取数据')
|
||||
# 订单层面审核
|
||||
order_result = analyze_orders(raw_data)
|
||||
print('已完成订单层面审核')
|
||||
order_result.to_excel(r'D:\test\logistics\拦截数据\order_analysis.xlsx', index=False)
|
||||
# 本地计算投递渠道的费用
|
||||
order_result =local_fee_cal(raw_data)
|
||||
# 计算最优渠道和费用
|
||||
raw_data = analyze_logistics(raw_data)
|
||||
target_file2 = r'D:\test\logistics\拦截数据\logistics_analysis.xlsx'
|
||||
append_result(raw_data,target_file2,'package')
|
||||
print('已完成物流费用层面审核')
|
||||
raw_data.to_excel(r'D:\test\logistics\拦截数据\logistics_analysis.xlsx', index=False)
|
||||
# 订单层面审核
|
||||
order_result = analyze_orders(raw_data)
|
||||
target_file3 = r'D:\test\logistics\拦截数据\order_analysis.xlsx'
|
||||
append_result(order_result,target_file3,'order_id')
|
||||
print('已完成订单层面审核')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
main()
|
||||
# 取数
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue