完善报价业务问答与连续追问,补齐订单列表和人工转接

This commit is contained in:
2026-11-05 13:06:01 +08:00
parent 880a8fb57c
commit 79fb675af5
15 changed files with 753 additions and 108 deletions
+96
View File
@@ -0,0 +1,96 @@
"""Amounts come from the active book; explicit customer-approved fee policies live here."""
from decimal import Decimal
import re
from .pricing import dims_to_cm
def document(book,required,service=None):
rows=[d for d in book['documents'] if (not service or d['sheet'] in (service,'海派'))
and all(term in d['content'] for term in required)]
return rows[0]['content'] if rows else ''
def amount(text,pattern):
match=re.search(pattern,text,re.I)
return Decimal(match.group(1)) if match else None
def material_fee(book):
text=document(book,('铁铝','附加费'))
return amount(text,r'铁铝制品[^\n]*?[¥¥]\s*(\d+(?:\.\d+)?)\s*/KG')
def specimens(intent):
if intent.get('packages'):
return [{**p,'dims':p.get('dims') or intent.get('dims'),
'dim_unit':p.get('dim_unit') if p.get('dims') else intent.get('dim_unit','cm')} for p in intent['packages']]
if intent.get('boxes')==1 and intent.get('weight'):
return [{'count':1,'weight':intent['weight'],'dims':intent.get('dims'),'dim_unit':intent.get('dim_unit','cm')}]
return []
def limit_fees(intent,book):
"""Maximum per-piece limit fee, not the sum of overlapping weight/size charges.
The malformed 0<length<274 rule does not create a new threshold. Only two
independently explicit large-piece conditions can establish that tier.
"""
normal=document(book,('正常货物',))
cleaned=re.sub(r'\s+','',normal).replace('<','<').upper()
limits=[amount(cleaned,p) for p in (r'0KG<单件重量<(\d+)KG',r'0CM<最长单边长度<(\d+)CM',
r'0CM<第二边<(\d+)CM',r'最长边<(\d+)CM')]
normal_fee=amount(document(book,('超重收',)),r'超重收\s*[++]?\s*(\d+(?:\.\d+)?)')
size_text=document(book,('普通超尺寸',))
size_fee=amount(size_text,r'加收\s*(\d+(?:\.\d+)?)\s*RMB/件')
large_text=document(book,('大尺寸货物','任意2个'))
large_fee=amount(large_text,r'加收\s*(\d+(?:\.\d+)?)\s*RMB/件')
huge=document(book,('超大件货物',))
huge_fee=amount(huge,r'加收\s*USD\s*(\d+(?:\.\d+)?)')
result={'cny':Decimal(0),'items':[],'notes':[]}
if not all(limits) or normal_fee is None:return result
max_weight,max_length,max_second,max_girth=limits
for piece in specimens(intent):
weight=Decimal(piece['weight']);count=piece['count']
dims=sorted([Decimal(str(x)) for x in dims_to_cm(piece['dims'],piece['dim_unit'])],reverse=True) if piece['dims'] else []
length,second,girth=(dims[0],dims[1],dims[0]+2*(dims[1]+dims[2])) if dims else (None,None,None)
if weight>68 or (dims and (length>270 or girth>419)):
result['notes'].append('该件可能超出承运尺寸或重量,需人工确认是否接收。'+(f'原表超大件若接收的附加费为{huge_fee:g}USD/件。' if huge_fee else ''))
continue
candidates=[]
if weight>max_weight:candidates.append(normal_fee)
if dims and (length>max_length or second>max_second or girth>max_girth) and size_fee is not None:candidates.append(size_fee)
if large_fee is not None and max_weight<weight<68 and dims and 330<girth<419:
candidates.append(large_fee)
elif dims and (length>=243 or girth>=330):
result['notes'].append('该件接近大尺寸档,原表有一处长度条件不清,适用档位需人工确认。')
if candidates:
fee=max(candidates)*count
result['cny']+=fee
result['items'].append(f'超重/超尺寸按最高项{max(candidates):g}元/件×{count}件={fee:g}元')
return result
def charge_details(intent,rate,remote,book):
from .quote_engine import quantity
result={'cny':Decimal(0),'items':[],'notes':[]}
charged=quantity(intent,rate)
if charged is None:return result
if rate['kind']=='parcel' and remote:
if remote['fee'] is not None:
fee=charged*Decimal(remote['fee']);result['cny']+=fee
result['items'].append(f'偏远费{remote["fee"]}元/KG×{charged:g}KG={fee:g}元')
else:result['notes'].append('遥远地址的附加费需单独确认,当前金额未包含该项。')
if rate['unit']=='kg' and intent.get('material')=='metal':
per_kg=material_fee(book)
if per_kg is not None:
fee=charged*per_kg;result['cny']+=fee
result['items'].append(f'铁铝附加费{per_kg:g}元/KG×{charged:g}KG={fee:g}元')
if rate['kind']=='parcel':
limits=limit_fees(intent,book)
result['cny']+=limits['cny'];result['items']+=limits['items'];result['notes']+=limits['notes']
if intent.get('address_type')=='non_fba':
result['notes'].append('非亚马逊地址还需核对住宅/商业派送附加费,原表此项需单独询价。')
return result