@@ -0,0 +1,631 @@
package com.bms.price.pay ;
import static org.junit.jupiter.api.Assertions.assertEquals ;
import static org.junit.jupiter.api.Assertions.assertFalse ;
import static org.junit.jupiter.api.Assertions.assertTrue ;
import java.nio.file.Files ;
import java.nio.file.Path ;
import java.util.List ;
import java.util.stream.Stream ;
import org.junit.jupiter.api.Assumptions ;
import org.junit.jupiter.api.Test ;
import com.bms.price.domain.PayPreview ;
import com.bms.price.domain.PayProduct ;
import com.bms.price.domain.PaySurcharge ;
import com.bms.price.domain.PayWeightBand ;
/**
* 用现有应付价卡核对目录产品和重量段、附加费是否被读出来。
*/
class PayWorkbookParserTest
{
private final PayWorkbookParser parser = new PayWorkbookParser ( ) ;
@Test
void octoberCatalogKeepsTwelveProductSheets ( ) throws Exception
{
PayPreview preview = parse ( " 9、 " ) ;
assertEquals ( 12 , preview . getProducts ( ) . size ( ) ) ;
PayProduct evri = product ( preview , " Evri 48H " ) ;
assertTrue ( evri . getWeightBands ( ) . stream ( ) . anyMatch ( band - > decimal ( band , " 1.51 " ) & & " 英国大陆 " . equals ( zoneTail ( band ) ) ) ) ;
assertTrue ( evri . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 战争附加费 " . equals ( fee . getFeeName ( ) ) & & fee . getChargeText ( ) . contains ( " 2.5 " ) ) ) ;
PayProduct ctt = product ( preview , " CTT 48H " ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > band . getWeightLabel ( ) ! = null & & band . getWeightLabel ( ) . contains ( " 首重 " ) & & decimal ( band , " 2.05 " ) ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " 每增加 1Kg " . equals ( band . getWeightLabel ( ) ) | | ( band . getWeightLabel ( ) ! = null & & band . getWeightLabel ( ) . contains ( " 每增加 " ) ) ) ) ;
}
@Test
void mayCardReadsDhlAndDpdWithoutZoneSheets ( ) throws Exception
{
PayPreview preview = parse ( " 8、 " ) ;
assertEquals ( 8 , preview . getProducts ( ) . size ( ) ) ;
PayProduct dhl = product ( preview , " DHL泛欧 " ) ;
assertEquals ( " 威狮 " , dhl . getSupplierName ( ) ) ;
assertTrue ( dhl . getRelatedSheet ( ) ! = null & & dhl . getRelatedSheet ( ) . contains ( " 高风险 " ) ) ;
assertTrue ( dhl . getWeightBands ( ) . stream ( ) . anyMatch ( band - > decimal ( band , " 9.71 " ) & & band . getCountryName ( ) ! = null & & band . getCountryName ( ) . contains ( " 丹麦 " ) ) ,
dhl . getParseStatus ( ) + " " + dhl . getSheetName ( ) + " " + dhl . getWeightBands ( ) . stream ( ) . limit ( 6 )
. map ( band - > band . getCountryName ( ) + " / " + band . getChargeName ( ) + " / " + band . getPrice ( ) ) . toList ( ) ) ;
assertEquals ( " 末端 " , dhl . getServiceType ( ) ) ;
assertEquals ( " 德国 " , dhl . getCountryScope ( ) ) ;
assertEquals ( " DHL泛欧 " , dhl . getSheetName ( ) ) ;
PaySurcharge afghan = dhl . getSurcharges ( ) . stream ( )
. filter ( fee - > " 高风险地区附加费 " . equals ( fee . getFeeName ( ) ) & & " AF " . equals ( fee . getCalcParam ( ) ) ) . findFirst ( ) . orElseThrow ( ) ;
assertEquals ( " Afghanistan " , afghan . getZoneName ( ) ) ;
assertEquals ( " RMB " , afghan . getCurrency ( ) ) ;
assertTrue ( afghan . getChargeText ( ) . contains ( " 10.4 " ) & & afghan . getChargeText ( ) . contains ( " RMB/kg " ) ) ;
assertTrue ( decimalFee ( afghan , " 10.4 " ) ) ;
assertEquals ( " MANUAL " , afghan . getCalcRule ( ) ) ;
assertTrue ( afghan . getFeeDesc ( ) . contains ( " 非欧洲 " ) ) ;
PayProduct dpd = preview . getProducts ( ) . stream ( ) . filter ( item - > " DPD " . equals ( item . getProductName ( ) ) ) . findFirst ( ) . orElseThrow ( ) ;
assertEquals ( " 末端 " , dpd . getServiceType ( ) ) ;
assertEquals ( " 德国 " , dpd . getCountryScope ( ) ) ;
assertTrue ( dpd . getSheetName ( ) . contains ( " DEDPD " ) ) ;
assertTrue ( dpd . getRelatedSheet ( ) . contains ( " 不可达 " ) ) ;
assertTrue ( dpd . getRelatedSheet ( ) . contains ( " 危机燃油 " ) ) ;
assertTrue ( dpd . getWeightBands ( ) . stream ( ) . anyMatch ( band - > decimal ( band , " 3.29 " ) ) ) ;
assertTrue ( dpd . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 道路通行费 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 0.14 " ) ) ) ;
assertTrue ( dpd . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > fee . getFeeName ( ) . contains ( " 旺季 " ) & & fee . getEffectiveStart ( ) ! = null ) ) ;
PaySurcharge bracket = dpd . getSurcharges ( ) . stream ( )
. filter ( fee - > " 危机燃油附加费 " . equals ( fee . getFeeName ( ) ) & & " 1.95|2 " . equals ( fee . getCalcParam ( ) ) ) . findFirst ( ) . orElseThrow ( ) ;
assertEquals ( " 1.95 ≤ 油价 < 2 " , bracket . getChargeText ( ) ) ;
assertTrue ( decimalFee ( bracket , " 0.0379 " ) ) ;
assertEquals ( " MANUAL " , bracket . getCalcRule ( ) ) ;
PaySurcharge zero = dpd . getSurcharges ( ) . stream ( )
. filter ( fee - > " 危机燃油附加费 " . equals ( fee . getFeeName ( ) ) & & " |1.35 " . equals ( fee . getCalcParam ( ) ) ) . findFirst ( ) . orElseThrow ( ) ;
assertEquals ( " 油价 < 1.35 " , zero . getChargeText ( ) ) ;
assertTrue ( decimalFee ( zero , " 0 " ) ) ;
PayProduct ctt = product ( preview , " CTT " ) ;
assertEquals ( " 末端 " , ctt . getServiceType ( ) ) ;
assertEquals ( " 西班牙 " , ctt . getCountryScope ( ) ) ;
assertEquals ( " 西班牙CTT " , ctt . getSheetName ( ) ) ;
assertTrue ( ctt . getRelatedSheet ( ) . contains ( " 西班牙CTT邮编分区 " ) ) ;
PayProduct international = product ( preview , " DPD国际件 " ) ;
assertEquals ( " 德国 " , international . getCountryScope ( ) ) ;
assertTrue ( international . getRelatedSheet ( ) . contains ( " 岛屿 " ) ) ;
assertTrue ( international . getSurcharges ( ) . stream ( ) . noneMatch ( fee - > " 1.95|2 " . equals ( fee . getCalcParam ( ) ) ) ) ;
}
@Test
void upsZonesStayLeftToRightAndColissimoFuelIsMarked ( ) throws Exception
{
PayPreview preview = parse ( " 32、 " ) ;
PayProduct ups = product ( preview , " 德国UPS " ) ;
assertEquals ( " Zone 1 " , ups . getWeightBands ( ) . get ( 0 ) . getZoneName ( ) ) ;
assertEquals ( " DE " , ups . getWeightBands ( ) . get ( 0 ) . getCountryCode ( ) ) ;
assertTrue ( decimal ( ups . getWeightBands ( ) . get ( 0 ) , " 3.9 " ) ) ;
assertEquals ( " Zone 2 " , ups . getWeightBands ( ) . get ( 1 ) . getZoneName ( ) ) ;
assertTrue ( ups . getWeightBands ( ) . get ( 1 ) . getCountryCode ( ) . startsWith ( " BE,LU " ) ) ;
assertTrue ( decimal ( ups . getWeightBands ( ) . get ( 1 ) , " 7.58 " ) ) ;
assertTrue ( ups . getWeightBands ( ) . get ( 4 ) . getZoneName ( ) . contains ( " Zone 5 " ) ) ;
assertTrue ( decimal ( ups . getWeightBands ( ) . get ( 4 ) , " 9.45 " ) ) ;
assertTrue ( ups . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 报价说明 " . equals ( fee . getFeeName ( ) )
& & fee . getChargeText ( ) . contains ( " 不含管理费 " ) & & fee . getChargeText ( ) . contains ( " 不含燃油 " ) ) ) ;
PaySurcharge handling = ups . getSurcharges ( ) . stream ( )
. filter ( fee - > fee . getFeeName ( ) . startsWith ( " 额外处理费 " ) & & ( fee . getSectionName ( ) = = null | | ! fee . getSectionName ( ) . contains ( " 高峰 " ) ) )
. findFirst ( ) . orElseThrow ( ) ;
assertTrue ( handling . getChargeText ( ) . contains ( " 23.65 " ) & & handling . getChargeText ( ) . contains ( " 26.7 " ) ) ;
assertTrue ( handling . getFeeDesc ( ) . contains ( " 是否加收燃油:是 " ) ) ;
PaySurcharge peak = ups . getSurcharges ( ) . stream ( )
. filter ( fee - > " 高峰附加费 " . equals ( fee . getSectionName ( ) ) & & fee . getFeeName ( ) . startsWith ( " 额外处理费 " ) )
. findFirst ( ) . orElseThrow ( ) ;
assertTrue ( peak . getChargeText ( ) . contains ( " 7.35 " ) ) ;
assertEquals ( java . time . LocalDate . of ( 2026 , 9 , 28 ) ,
peak . getEffectiveStart ( ) . toInstant ( ) . atZone ( java . time . ZoneId . of ( " GMT+8 " ) ) . toLocalDate ( ) ) ;
assertEquals ( java . time . LocalDate . of ( 2027 , 1 , 17 ) ,
peak . getEffectiveEnd ( ) . toInstant ( ) . atZone ( java . time . ZoneId . of ( " GMT+8 " ) ) . toLocalDate ( ) ) ;
PayProduct colissimo = product ( preview , " Colissimo " ) ;
PaySurcharge fuel = colissimo . getSurcharges ( ) . stream ( ) . filter ( fee - > fee . getFeeName ( ) . contains ( " 燃油 " ) ) . findFirst ( ) . orElseThrow ( ) ;
assertTrue ( fuel . getChargeText ( ) . contains ( " 不含燃油 " ) ) ;
assertTrue ( fuel . getChargeText ( ) . contains ( " 不含去碳 " ) ) ;
assertTrue ( colissimo . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > fee . getFeeName ( ) . startsWith ( " 去碳附加费 " ) & & decimalFee ( fee , " 0.05 " ) ) ) ;
assertTrue ( colissimo . getWeightBands ( ) . stream ( ) . anyMatch ( band - > decimal ( band , " 3.03 " ) ) ) ;
assertTrue ( colissimo . getWeightBands ( ) . stream ( ) . noneMatch ( band - > band . getChargeName ( ) ! = null & & band . getChargeName ( ) . contains ( " 燃油 " ) ) ) ;
}
@Test
void imilePostcodesResolveZoneAndGermanCoverage ( ) throws Exception
{
PayPreview preview = parse ( " 32、 " ) ;
PayProduct italy = product ( preview , " IT-iMile " ) ;
assertTrue ( italy . getRelatedSheet ( ) . contains ( " iMile邮编分区 " ) ) ;
assertTrue ( italy . getExtraLines ( ) . size ( ) > 4000 ) ;
assertTrue ( italy . getExtraLines ( ) . stream ( ) . anyMatch ( line - > " 邮编 " . equals ( line . getLineType ( ) ) & & line . getLineText ( ) . contains ( " 00010 " ) & & line . getLineText ( ) . contains ( " Province " ) & & line . getLineText ( ) . contains ( " ROMA " ) ) ) ;
assertTrue ( italy . getExtraLines ( ) . stream ( ) . anyMatch ( line - > line . getLineText ( ) . endsWith ( " | City " ) ) ) ;
assertTrue ( italy . getExtraLines ( ) . stream ( ) . anyMatch ( line - > line . getLineText ( ) . endsWith ( " | Rural " ) ) ) ;
assertTrue ( italy . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " City " . equals ( band . getZoneName ( ) ) & & decimal ( band , " 2.85 " ) ) ) ;
assertTrue ( italy . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " Rural " . equals ( band . getZoneName ( ) ) & & decimal ( band , " 3.68 " ) ) ) ;
PayProduct germany = product ( preview , " DE-iMile " ) ;
assertTrue ( germany . getRelatedSheet ( ) . contains ( " iMile邮编分区 " ) ) ;
assertTrue ( germany . getExtraLines ( ) . size ( ) > 7000 ) ;
assertTrue ( germany . getExtraLines ( ) . stream ( ) . anyMatch ( line - > line . getLineText ( ) . contains ( " 01001 " ) & & line . getLineText ( ) . contains ( " Active " ) ) ) ;
assertTrue ( germany . getExtraLines ( ) . stream ( ) . noneMatch ( line - > line . getLineText ( ) . contains ( " Lazio " ) | | line . getLineText ( ) . contains ( " City " ) ) ) ;
}
@Test
void gweFileOverridesOnlyPanEuroSupplier ( ) throws Exception
{
PayPreview preview = parse ( " 8.1 " ) ;
PayProduct dhl = product ( preview , " DHL泛欧 " ) ;
assertEquals ( " GWE " , dhl . getSupplierName ( ) ) ;
assertTrue ( dhl . getWeightBands ( ) . stream ( ) . anyMatch ( band - > decimal ( band , " 9.79 " ) ) ,
dhl . getParseStatus ( ) + " sur= " + dhl . getSurchargeCount ( ) + " extra= " + dhl . getExtraCount ( ) + " " + dhl . getRemark ( ) + " "
+ dhl . getWeightBands ( ) . stream ( ) . limit ( 8 )
. map ( band - > band . getCountryName ( ) + " / " + band . getWeightLabel ( ) + " / " + band . getChargeName ( ) + " / " + band . getPrice ( ) ) . toList ( ) ) ;
PayProduct ctt = product ( preview , " CTT " ) ;
assertEquals ( " 威狮 " , ctt . getSupplierName ( ) ) ;
}
@Test
void wholeWorkbookPreviewSerializes ( ) throws Exception
{
PayPreview preview = parse ( " 9、 " ) ;
String json = tools . jackson . databind . json . JsonMapper . builder ( ) . build ( ) . writeValueAsString ( preview ) ;
assertTrue ( json . contains ( " \" productName \" : \" Evri 48H \" " ) ) ;
assertTrue ( json . contains ( " 2026-10-01 " ) | | json . contains ( " effectiveDate " ) ) ;
}
@Test
void yideCatalogIncludesPostcodeProducts ( ) throws Exception
{
PayPreview preview = parse ( " 26、 " ) ;
assertEquals ( 10 , preview . getProducts ( ) . size ( ) ) ;
assertTrue ( preview . getProducts ( ) . stream ( ) . anyMatch ( item - > item . getExtraCount ( ) ! = null & & item . getExtraCount ( ) > 0 ) ) ;
PayProduct evri = preview . getProducts ( ) . stream ( ) . filter ( item - > item . getSheetName ( ) . contains ( " 英国evri " ) ) . findFirst ( ) . orElseThrow ( ) ;
assertFalse ( evri . getWeightBands ( ) . isEmpty ( ) ) ;
assertFalse ( evri . getSurcharges ( ) . isEmpty ( ) ) ;
}
@Test
void dictMatchReadsNamedSheetAndSkipsOthers ( ) throws Exception
{
Path file = Path . of ( " D:/payment/Evri 48H.xlsx " ) ;
Assumptions . assumeTrue ( Files . exists ( file ) ) ;
PayPreview preview = parser . parseMatched ( Files . readAllBytes ( file ) , file . getFileName ( ) . toString ( ) , " Evri 48H " ,
List . of ( " Evri 48H " , " DHL Paket " ) ) ;
assertEquals ( 1 , preview . getProducts ( ) . size ( ) ) ;
PayProduct evri = preview . getProducts ( ) . get ( 0 ) ;
assertEquals ( " Evri 48H " , evri . getDictValue ( ) ) ;
assertEquals ( " Evri 48H " , evri . getSheetName ( ) ) ;
assertFalse ( evri . getWeightBands ( ) . isEmpty ( ) ) ;
}
@Test
void panEuroKeepsTwoChargesPerCountryAndEverySurcharge ( ) throws Exception
{
Path file = Path . of ( " D:/payment/DHL泛欧.xlsx " ) ;
Assumptions . assumeTrue ( Files . exists ( file ) ) ;
PayProduct dhl = parser . parseMatched ( Files . readAllBytes ( file ) , file . getFileName ( ) . toString ( ) , " DHL泛欧 " ,
List . of ( " DHL泛欧 " ) ) . getProducts ( ) . get ( 0 ) ;
assertPanEuroPrices ( dhl ) ;
assertEquals ( 15 , dhl . getSurcharges ( ) . size ( ) , dhl . getSurcharges ( ) . stream ( )
. map ( fee - > fee . getFeeName ( ) + " / " + fee . getChargeText ( ) ) . toList ( ) . toString ( ) ) ;
}
@Test
void panEuroSheetInCatalogDoesNotCopyPricesAndKeepsHighRisk ( ) throws Exception
{
Path dir = Path . of ( " D:/应付供应商 " ) ;
Assumptions . assumeTrue ( Files . isDirectory ( dir ) ) ;
Path file ;
try ( Stream < Path > files = Files . list ( dir ) )
{
file = files . filter ( path - > path . getFileName ( ) . toString ( ) . startsWith ( " 8、 " ) & & path . toString ( ) . endsWith ( " .xlsx " ) )
. findFirst ( ) . orElseThrow ( ) ;
}
PayProduct dhl = parser . parseMatched ( Files . readAllBytes ( file ) , file . getFileName ( ) . toString ( ) , " DHL泛欧 " ,
List . of ( " DHL泛欧 " ) ) . getProducts ( ) . get ( 0 ) ;
assertPanEuroPrices ( dhl ) ;
assertTrue ( dhl . getRelatedSheet ( ) ! = null & & dhl . getRelatedSheet ( ) . contains ( " 高风险 " ) ) ;
PaySurcharge afghan = dhl . getSurcharges ( ) . stream ( )
. filter ( fee - > " 高风险地区附加费 " . equals ( fee . getFeeName ( ) ) & & " AF " . equals ( fee . getCalcParam ( ) ) ) . findFirst ( ) . orElseThrow ( ) ;
assertEquals ( " Afghanistan " , afghan . getZoneName ( ) ) ;
assertTrue ( decimalFee ( afghan , " 10.4 " ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . filter ( fee - > " 高风险地区附加费 " . equals ( fee . getFeeName ( ) ) & & " AF " . equals ( fee . getCalcParam ( ) ) ) . count ( ) = = 1 ) ;
}
@Test
void nlDhlVolumeStaysOnTheWeightBand ( ) throws Exception
{
Path file = Path . of ( " D:/payment/NL-DHL.xlsx " ) ;
Assumptions . assumeTrue ( Files . exists ( file ) ) ;
PayPreview preview = parser . parseMatched ( Files . readAllBytes ( file ) , file . getFileName ( ) . toString ( ) , " NL-DHL " ,
List . of ( " NL-DHL " ) ) ;
PayProduct dhl = preview . getProducts ( ) . get ( 0 ) ;
assertEquals ( 10 , dhl . getWeightBands ( ) . size ( ) , dhl . getWeightBands ( ) . stream ( )
. map ( band - > band . getWeightLabel ( ) + " / " + band . getChargeName ( ) + " / " + band . getZoneName ( ) + " / " + band . getPrice ( )
+ " / " + band . getVolumeText ( ) )
. toList ( ) . toString ( ) ) ;
assertTrue ( dhl . getWeightBands ( ) . stream ( ) . noneMatch ( band - > textOf ( band . getChargeName ( ) ) . contains ( " 长 " )
| | textOf ( band . getZoneName ( ) ) . contains ( " 长 " ) ) ) ;
assertTrue ( dhl . getWeightBands ( ) . stream ( ) . allMatch ( band - > band . getVolumeText ( ) ! = null & & band . getVolumeText ( ) . endsWith ( " cm³ " ) ) ) ;
assertTrue ( dhl . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " 0<W≤5 " . equals ( band . getWeightLabel ( ) )
& & decimal ( band , " 3.49 " ) & & " 10000cm³ " . equals ( band . getVolumeText ( ) )
& & " 处理费 " . equals ( band . getChargeName ( ) ) & & " EUR/ITEM " . equals ( band . getPriceUnit ( ) ) ) ) ;
assertTrue ( dhl . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " 20<W≤31.5 " . equals ( band . getWeightLabel ( ) )
& & decimal ( band , " 11.38 " ) & & " 432000cm³ " . equals ( band . getVolumeText ( ) ) ) ) ;
assertTrue ( dhl . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " 0<W≤5 " . equals ( band . getWeightLabel ( ) )
& & decimal ( band , " 4.93 " ) & & " 10000cm³ " . equals ( band . getVolumeText ( ) )
& & band . getCountryName ( ) . contains ( " 比利时 " ) & & " BE " . equals ( band . getCountryCode ( ) ) ) ) ;
assertTrue ( dhl . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " 20<W≤31.5 " . equals ( band . getWeightLabel ( ) )
& & decimal ( band , " 19.88 " ) & & " 432000cm³ " . equals ( band . getVolumeText ( ) ) ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > fee . getFeeName ( ) . startsWith ( " 超尺寸附加费 " ) ) ) ;
}
@Test
void royalMailReadsRemoteAreaTableAndSkipsRemarks ( ) throws Exception
{
Path file = Path . of ( " D:/payment/英国RM.xlsx " ) ;
Assumptions . assumeTrue ( Files . exists ( file ) ) ;
PayPreview preview = parser . parseMatched ( Files . readAllBytes ( file ) , file . getFileName ( ) . toString ( ) , " 英国RM " , List . of ( " 英国RM " ) ) ;
assertEquals ( 1 , preview . getProducts ( ) . size ( ) ) ;
PayProduct rm = preview . getProducts ( ) . get ( 0 ) ;
assertEquals ( 4 , rm . getWeightBands ( ) . size ( ) ) ;
assertTrue ( rm . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " 0-5 " . equals ( band . getWeightLabel ( ) ) & & decimal ( band , " 1.62 " )
& & band . getWeightMin ( ) ! = null & & band . getWeightMin ( ) . compareTo ( java . math . BigDecimal . ZERO ) = = 0
& & band . getWeightMax ( ) ! = null & & band . getWeightMax ( ) . compareTo ( new java . math . BigDecimal ( " 5 " ) ) = = 0
& & " 每票 " . equals ( band . getChargeName ( ) ) & & " GBP/票 " . equals ( band . getPriceUnit ( ) ) ) ) ;
assertTrue ( rm . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " 15.001-20 " . equals ( band . getWeightLabel ( ) ) & & decimal ( band , " 5.18 " )
& & band . getWeightMin ( ) ! = null & & band . getWeightMin ( ) . compareTo ( new java . math . BigDecimal ( " 15.001 " ) ) = = 0
& & band . getWeightMax ( ) ! = null & & band . getWeightMax ( ) . compareTo ( new java . math . BigDecimal ( " 20 " ) ) = = 0 ) ) ;
List < PaySurcharge > remote = rm . getSurcharges ( ) . stream ( ) . filter ( fee - > " 偏远地区附加费 " . equals ( fee . getFeeName ( ) ) ) . toList ( ) ;
assertEquals ( 3 , remote . size ( ) , remote . stream ( ) . map ( fee - > fee . getChargeText ( ) + " / " + fee . getFeeDesc ( ) ) . toList ( ) . toString ( ) ) ;
assertTrue ( remote . stream ( ) . anyMatch ( fee - > fee . getChargeText ( ) . contains ( " 1.80 " ) & & fee . getFeeDesc ( ) . contains ( " Area A " )
& & fee . getFeeDesc ( ) . contains ( " Highlands " ) & & fee . getFeeDesc ( ) . contains ( " IV " ) ) ) ;
assertTrue ( remote . stream ( ) . anyMatch ( fee - > fee . getChargeText ( ) . contains ( " 1.80 " ) & & fee . getFeeDesc ( ) . contains ( " Isle of Man " )
& & fee . getFeeDesc ( ) . contains ( " ZE " ) ) ) ;
assertTrue ( remote . stream ( ) . anyMatch ( fee - > fee . getChargeText ( ) . contains ( " 1.00 " ) & & fee . getFeeDesc ( ) . contains ( " Area B " )
& & fee . getFeeDesc ( ) . contains ( " BT " ) ) ) ;
assertTrue ( remote . stream ( ) . allMatch ( fee - > " POSTCODE " . equals ( fee . getCalcRule ( ) ) ) ) ;
assertTrue ( remote . stream ( ) . anyMatch ( fee - > fee . getCalcParam ( ) ! = null & & fee . getCalcParam ( ) . contains ( " IV " ) ) ) ;
assertTrue ( remote . stream ( ) . anyMatch ( fee - > fee . getCalcParam ( ) ! = null & & fee . getCalcParam ( ) . contains ( " BT " ) ) ) ;
assertTrue ( rm . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 签名费 " . equals ( fee . getFeeName ( ) ) & & fee . getChargeText ( ) . contains ( " 0.7 " )
& & " MANUAL " . equals ( fee . getCalcRule ( ) ) ) ) ;
assertTrue ( rm . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > fee . getFeeName ( ) . contains ( " 超大附加费 " ) & & fee . getFeeDesc ( ) . contains ( " 61 " )
& & " SIZE " . equals ( fee . getCalcRule ( ) ) & & fee . getCalcParam ( ) ! = null & & fee . getCalcParam ( ) . contains ( " box:61 " ) ) ) ;
assertTrue ( rm . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > fee . getFeeName ( ) . contains ( " 超重 " )
& & " WEIGHT " . equals ( fee . getCalcRule ( ) ) & & " 20 " . equals ( fee . getCalcParam ( ) ) ) ) ;
assertTrue ( rm . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > fee . getFeeName ( ) . contains ( " 重复单号 " ) & & fee . getChargeText ( ) . contains ( " 23 " ) ) ) ;
assertTrue ( rm . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > fee . getFeeName ( ) . contains ( " 未预报 " ) & & fee . getChargeText ( ) . contains ( " 1.50 " ) ) ) ;
assertTrue ( rm . getSurcharges ( ) . stream ( ) . noneMatch ( fee - > fee . getFeeDesc ( ) ! = null & & ( fee . getFeeDesc ( ) . contains ( " 计费重量 " )
| | fee . getFeeDesc ( ) . contains ( " 赔偿条款 " ) | | fee . getFeeDesc ( ) . contains ( " royalmail.com " ) ) ) ) ;
assertTrue ( rm . getServiceNote ( ) = = null | | rm . getServiceNote ( ) . isBlank ( ) ) ;
assertTrue ( rm . getExtraLines ( ) . isEmpty ( ) ) ;
}
@Test
void evriOtherFeesKeepOneRowPerRegion ( ) throws Exception
{
Path file = Path . of ( " D:/payment/英国evri.xlsx " ) ;
Assumptions . assumeTrue ( Files . exists ( file ) ) ;
PayPreview preview = parser . parseMatched ( Files . readAllBytes ( file ) , file . getFileName ( ) . toString ( ) , " 英国evri " , List . of ( " 英国evri " ) ) ;
PayProduct evri = preview . getProducts ( ) . get ( 0 ) ;
List < PaySurcharge > oversize = evri . getSurcharges ( ) . stream ( )
. filter ( fee - > " 超尺寸费 " . equals ( fee . getFeeName ( ) ) ) . toList ( ) ;
assertEquals ( 6 , oversize . size ( ) , oversize . stream ( ) . map ( fee - > fee . getZoneName ( ) + " = " + fee . getChargeText ( ) ) . toList ( ) . toString ( ) ) ;
assertTrue ( oversize . stream ( ) . anyMatch ( fee - > fee . getZoneName ( ) . contains ( " 英国大陆 " ) & & fee . getChargeText ( ) . contains ( " 6.99 " ) ) ) ;
assertTrue ( oversize . stream ( ) . anyMatch ( fee - > fee . getZoneName ( ) . contains ( " 高地 " ) & & fee . getChargeText ( ) . contains ( " 9.67 " ) ) ) ;
assertTrue ( oversize . stream ( ) . anyMatch ( fee - > fee . getZoneName ( ) . contains ( " 马恩岛 " ) & & fee . getChargeText ( ) . contains ( " 9.67 " ) ) ) ;
assertTrue ( evri . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 周末服务费 " . equals ( fee . getFeeName ( ) )
& & fee . getZoneName ( ) . contains ( " 北爱尔兰 " ) & & fee . getChargeText ( ) . contains ( " 3.02 " ) ) ) ;
assertTrue ( evri . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > fee . getFeeName ( ) . startsWith ( " 旺季附加费 " )
& & fee . getZoneName ( ) . contains ( " 英国大陆 " ) & & fee . getChargeText ( ) . contains ( " 0.15 " ) ) ) ;
assertTrue ( evri . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > fee . getFeeName ( ) . startsWith ( " 旺季附加费 " )
& & fee . getZoneName ( ) . contains ( " 马恩岛 " ) & & fee . getChargeText ( ) . contains ( " 5.5 " ) ) ) ;
assertTrue ( evri . getSurcharges ( ) . stream ( ) . noneMatch ( fee - > fee . getFeeName ( ) ! = null
& & ( fee . getFeeName ( ) . contains ( " 服务说明 " ) | | fee . getFeeName ( ) . contains ( " 索赔 " ) ) ) ) ;
}
@Test
void spainMainlandKeepsAreasAndTwoSurcharges ( ) throws Exception
{
Path file = Path . of ( " D:/payment/西邮-西班牙(主岛).xlsx " ) ;
Assumptions . assumeTrue ( Files . exists ( file ) ) ;
PayPreview preview = parser . parseMatched ( Files . readAllBytes ( file ) , file . getFileName ( ) . toString ( ) , " 西邮-西班牙(主岛) " ,
List . of ( " 西邮-西班牙(主岛) " ) ) ;
PayProduct spain = preview . getProducts ( ) . get ( 0 ) ;
assertEquals ( 160 , spain . getWeightBands ( ) . size ( ) ) ;
assertTrue ( spain . getWeightBands ( ) . stream ( ) . anyMatch ( band - > band . getWeightLabel ( ) . contains ( " 0.01-0.25 " )
& & " MAD-BCN Dense " . equals ( band . getZoneName ( ) ) & & decimal ( band , " 2.27 " ) & & " 每件 " . equals ( band . getChargeName ( ) )
& & " EUR/件 " . equals ( band . getPriceUnit ( ) ) ) ) ;
assertTrue ( spain . getWeightBands ( ) . stream ( ) . anyMatch ( band - > band . getWeightLabel ( ) . contains ( " 0.01-0.25 " )
& & " Rural Resto " . equals ( band . getZoneName ( ) ) & & decimal ( band , " 3.12 " ) ) ) ;
assertTrue ( spain . getWeightBands ( ) . stream ( ) . anyMatch ( band - > band . getWeightLabel ( ) . contains ( " 29-30 " )
& & " Denso Resto " . equals ( band . getZoneName ( ) ) & & decimal ( band , " 10.08 " ) ) ) ;
assertTrue ( spain . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 重新贴尾程标签附加费 " . equals ( fee . getFeeName ( ) )
& & fee . getChargeText ( ) . contains ( " 0.35 " ) & & fee . getFeeDesc ( ) . contains ( " 重新贴尾程标签 " ) ) ) ;
List < PaySurcharge > oversize = spain . getSurcharges ( ) . stream ( ) . filter ( fee - > " 特大包裹收费 " . equals ( fee . getFeeName ( ) ) ) . toList ( ) ;
assertEquals ( 2 , oversize . size ( ) , oversize . stream ( ) . map ( PaySurcharge : : getChargeText ) . toList ( ) . toString ( ) ) ;
assertTrue ( oversize . stream ( ) . anyMatch ( fee - > fee . getChargeText ( ) . contains ( " 35% " ) & & fee . getFeeDesc ( ) . contains ( " 120 " )
& & " SIZE " . equals ( fee . getCalcRule ( ) ) & & fee . getCalcParam ( ) ! = null & & fee . getCalcParam ( ) . contains ( " join:or " ) ) ) ;
assertTrue ( oversize . stream ( ) . anyMatch ( fee - > fee . getChargeText ( ) . contains ( " 70% " )
& & " SIZE " . equals ( fee . getCalcRule ( ) ) & & fee . getCalcParam ( ) ! = null & & fee . getCalcParam ( ) . contains ( " join:and " ) ) ) ;
assertTrue ( spain . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 重新贴尾程标签附加费 " . equals ( fee . getFeeName ( ) )
& & " MANUAL " . equals ( fee . getCalcRule ( ) ) ) ) ;
assertTrue ( spain . getSurcharges ( ) . stream ( ) . noneMatch ( fee - > fee . getFeeName ( ) ! = null
& & ( fee . getFeeName ( ) . contains ( " 禁运 " ) | | fee . getFeeName ( ) . contains ( " 条款 " ) ) ) ) ;
}
@Test
void spainGlsSplitsCountryCodesAndServiceFees ( ) throws Exception
{
Path file = Path . of ( " D:/payment/西班牙GLS.xlsx " ) ;
Assumptions . assumeTrue ( Files . exists ( file ) ) ;
PayPreview preview = parser . parseMatched ( Files . readAllBytes ( file ) , file . getFileName ( ) . toString ( ) , " 西班牙GLS " ,
List . of ( " 西班牙GLS " ) ) ;
PayProduct gls = preview . getProducts ( ) . get ( 0 ) ;
assertEquals ( " OK " , gls . getParseStatus ( ) ) ;
assertEquals ( 476 , gls . getWeightBands ( ) . size ( ) ) ;
assertTrue ( gls . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " DE " . equals ( band . getCountryCode ( ) ) & & " 德国 " . equals ( band . getCountryName ( ) )
& & " A " . equals ( band . getZoneName ( ) ) & & " 1KG " . equals ( band . getWeightLabel ( ) ) & & decimal ( band , " 5.12 " )
& & band . getWeightMin ( ) . compareTo ( java . math . BigDecimal . ZERO ) = = 0
& & band . getWeightMax ( ) . compareTo ( java . math . BigDecimal . ONE ) = = 0
& & " 每件 " . equals ( band . getChargeName ( ) ) & & " EUR/件 " . equals ( band . getPriceUnit ( ) ) ) ) ;
assertTrue ( gls . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " NL " . equals ( band . getCountryCode ( ) ) & & " 荷兰 " . equals ( band . getCountryName ( ) )
& & " A " . equals ( band . getZoneName ( ) ) & & decimal ( band , " 5.12 " ) ) ) ;
assertTrue ( gls . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " PL " . equals ( band . getCountryCode ( ) ) & & " 波兰 " . equals ( band . getCountryName ( ) ) ) ) ;
assertTrue ( gls . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " FR " . equals ( band . getCountryCode ( ) ) & & " 法国 " . equals ( band . getCountryName ( ) )
& & " B " . equals ( band . getZoneName ( ) ) & & " 1KG " . equals ( band . getWeightLabel ( ) ) & & decimal ( band , " 6.38 " ) ) ) ;
assertTrue ( gls . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " HU " . equals ( band . getCountryCode ( ) ) & & " 匈牙利 " . equals ( band . getCountryName ( ) )
& & " D " . equals ( band . getZoneName ( ) ) & & " 3KG " . equals ( band . getWeightLabel ( ) ) & & decimal ( band , " 8.15 " ) ) ) ;
assertTrue ( gls . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " GB " . equals ( band . getCountryCode ( ) ) & & " 英国 " . equals ( band . getCountryName ( ) )
& & " E " . equals ( band . getZoneName ( ) ) & & " 1KG " . equals ( band . getWeightLabel ( ) ) & & decimal ( band , " 7.11 " ) ) ) ;
assertTrue ( gls . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " FR2 " . equals ( band . getCountryCode ( ) ) & & " 法国-科西嘉岛 " . equals ( band . getCountryName ( ) )
& & " G " . equals ( band . getZoneName ( ) ) & & " 1KG " . equals ( band . getWeightLabel ( ) ) & & decimal ( band , " 16.01 " ) ) ) ;
assertTrue ( gls . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " CY " . equals ( band . getCountryCode ( ) ) & & " 塞浦路斯 " . equals ( band . getCountryName ( ) )
& & " H " . equals ( band . getZoneName ( ) ) & & decimal ( band , " 13.01 " ) ) ) ;
assertTrue ( gls . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " IT " . equals ( band . getCountryCode ( ) ) & & " Ad.kg. " . equals ( band . getWeightLabel ( ) )
& & decimal ( band , " 1.07 " ) & & " 每公斤 " . equals ( band . getChargeName ( ) ) & & " EUR/公斤 " . equals ( band . getPriceUnit ( ) )
& & band . getWeightMin ( ) = = null ) ) ;
assertEquals ( 4 , gls . getSurcharges ( ) . size ( ) ) ;
assertTrue ( gls . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 取还服务 " . equals ( fee . getFeeName ( ) ) & & fee . getChargeText ( ) . contains ( " 7 " )
& & fee . getFeeDesc ( ) . contains ( " 送货上门 " ) & & decimalFee ( fee , " 7 " ) ) ) ;
assertTrue ( gls . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 取货发货服务 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 7 " ) ) ) ;
assertTrue ( gls . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 现金服务 " . equals ( fee . getFeeName ( ) ) & & fee . getFeeDesc ( ) . contains ( " 交货付款 " )
& & decimalFee ( fee , " 6 " ) ) ) ;
assertTrue ( gls . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 灵活配送服务 " . equals ( fee . getFeeName ( ) ) & & " free " . equalsIgnoreCase ( fee . getChargeText ( ) )
& & decimalFee ( fee , " 0 " ) & & fee . getFeeDesc ( ) . contains ( " 灵活性 " ) ) ) ;
assertTrue ( gls . getSurcharges ( ) . stream ( ) . noneMatch ( fee - > fee . getFeeName ( ) ! = null & & ( fee . getFeeName ( ) . contains ( " 格陵兰 " )
| | fee . getFeeName ( ) . contains ( " 转账 " ) | | fee . getFeeName ( ) . contains ( " 备注 " ) | | fee . getFeeName ( ) . contains ( " 能源 " ) ) ) ) ;
assertTrue ( gls . getServiceNote ( ) = = null | | gls . getServiceNote ( ) . isBlank ( ) ) ;
}
@Test
void ctt48hFileKeepsSurchargeTablesOutOfWeightBands ( ) throws Exception
{
Path file = Path . of ( " D:/payment/CTT 48H.xlsx " ) ;
PayPreview preview = parser . parseMatched ( Files . readAllBytes ( file ) , file . getFileName ( ) . toString ( ) , " CTT 48H " ,
List . of ( " CTT 48H " ) ) ;
PayProduct ctt = preview . getProducts ( ) . get ( 0 ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . allMatch ( band - > " 派送费 " . equals ( band . getChargeName ( ) ) ) ,
ctt . getWeightBands ( ) . stream ( ) . map ( band - > band . getChargeName ( ) + " / " + band . getZoneName ( ) + " / " + band . getPrice ( ) ) . toList ( ) . toString ( ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > decimal ( band , " 2.05 " ) & & band . getZoneName ( ) . contains ( " 马德里 " ) ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . noneMatch ( band - > bandText ( band ) . contains ( " 偏远 " ) | | bandText ( band ) . contains ( " 额外 " )
| | bandText ( band ) . toLowerCase ( ) . contains ( " line-haul " ) ) ) ;
PaySurcharge balearic = ctt . getSurcharges ( ) . stream ( )
. filter ( fee - > " 偏远附加费 " . equals ( fee . getFeeName ( ) ) & & fee . getZoneName ( ) . contains ( " Balearic " ) & & decimalFee ( fee , " 7.1 " ) )
. findFirst ( ) . orElseThrow ( ) ;
assertTrue ( balearic . getFeeDesc ( ) . contains ( " 首重 " ) ) ;
assertTrue ( ctt . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 偏远附加费 " . equals ( fee . getFeeName ( ) )
& & fee . getZoneName ( ) . contains ( " Canary " ) & & decimalFee ( fee , " 4.5 " ) & & fee . getChargeText ( ) . contains ( " KG " ) ) ) ;
assertTrue ( ctt . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > fee . getFeeName ( ) . contains ( " 额外线路运输费 " ) & & decimalFee ( fee , " 0.09 " ) ) ) ;
assertTrue ( ctt . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > fee . getFeeName ( ) . contains ( " 额外线路运输费 " )
& & fee . getFeeDesc ( ) . contains ( " 800公里 " ) ) ) ;
assertTrue ( ctt . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 超重附加费 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 5.98 " ) ) ) ;
}
@Test
void spainCttMainlandReadsBillableConcepts ( ) throws Exception
{
Path file = Path . of ( " D:/payment/西班牙CTT带电专线(主要地区).xlsx " ) ;
Assumptions . assumeTrue ( Files . exists ( file ) ) ;
PayPreview preview = parser . parseMatched ( Files . readAllBytes ( file ) , file . getFileName ( ) . toString ( ) ,
" 西班牙CTT带电专线(主要地区) " , List . of ( " 西班牙CTT带电专线(主要地区) " ) ) ;
PayProduct ctt = preview . getProducts ( ) . get ( 0 ) ;
assertEquals ( " OK " , ctt . getParseStatus ( ) ) ;
assertEquals ( " 单票价 " , ctt . getChargeType ( ) ) ;
assertTrue ( ctt . getExtraPerKg ( ) = = null | | ctt . getExtraPerKg ( ) . isBlank ( ) ) ;
assertEquals ( 150 , ctt . getWeightBands ( ) . size ( ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " 1 " . equals ( band . getWeightLabel ( ) )
& & " Mad High Density " . equals ( band . getZoneName ( ) ) & & decimal ( band , " 2.11 " ) ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " 30 " . equals ( band . getWeightLabel ( ) )
& & " Portugal " . equals ( band . getZoneName ( ) ) & & decimal ( band , " 11.96 " ) ) ) ;
assertEquals ( 15 , ctt . getSurcharges ( ) . size ( ) ) ;
assertEquals ( 5 , ctt . getSurcharges ( ) . stream ( ) . filter ( fee - > " Extra Line-Haul (*) " . equals ( fee . getFeeName ( ) ) ) . count ( ) ) ;
assertTrue ( ctt . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " Extra Line-Haul (*) " . equals ( fee . getFeeName ( ) )
& & " Mad High Density " . equals ( fee . getZoneName ( ) ) & & decimalFee ( fee , " 0.12 " ) & & " EUR " . equals ( fee . getCurrency ( ) )
& & " MANUAL " . equals ( fee . getCalcRule ( ) ) ) ) ;
assertTrue ( ctt . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " Extra Line-Haul Additional Kg (**) " . equals ( fee . getFeeName ( ) )
& & " Portugal " . equals ( fee . getZoneName ( ) ) & & decimalFee ( fee , " 0.08 " ) ) ) ;
List < PaySurcharge > pudo = ctt . getSurcharges ( ) . stream ( ) . filter ( fee - > " Pudo " . equals ( fee . getFeeName ( ) ) ) . toList ( ) ;
assertEquals ( 1 , pudo . size ( ) ) ;
assertTrue ( pudo . get ( 0 ) . getChargeText ( ) . contains ( " 0.10 " ) & & pudo . get ( 0 ) . getChargeText ( ) . contains ( " 欧元 " ) ) ;
assertTrue ( decimalFee ( pudo . get ( 0 ) , " 0.10 " ) ) ;
assertEquals ( " " , pudo . get ( 0 ) . getZoneName ( ) = = null ? " " : pudo . get ( 0 ) . getZoneName ( ) ) ;
assertEquals ( 1 , ctt . getSurcharges ( ) . stream ( ) . filter ( fee - > fee . getFeeName ( ) . contains ( " COD " ) ) . count ( ) ) ;
assertEquals ( 1 , ctt . getSurcharges ( ) . stream ( ) . filter ( fee - > " Reverse Logistics " . equals ( fee . getFeeName ( ) ) ) . count ( ) ) ;
assertEquals ( 1 , ctt . getSurcharges ( ) . stream ( ) . filter ( fee - > " Fuel rate " . equals ( fee . getFeeName ( ) ) ) . count ( ) ) ;
assertEquals ( 1 , ctt . getSurcharges ( ) . stream ( ) . filter ( fee - > " Volumetric weight " . equals ( fee . getFeeName ( ) ) ) . count ( ) ) ;
assertTrue ( ctt . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > fee . getFeeName ( ) . contains ( " COD " )
& & fee . getChargeText ( ) . contains ( " 1% " ) & & fee . getAmount ( ) = = null ) ) ;
assertTrue ( ctt . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " Volumetric weight " . equals ( fee . getFeeName ( ) )
& & fee . getChargeText ( ) . contains ( " 90cm " ) & & fee . getAmount ( ) = = null & & " MANUAL " . equals ( fee . getCalcRule ( ) ) ) ) ;
assertTrue ( ctt . getSurcharges ( ) . stream ( ) . noneMatch ( fee - > fee . getFeeName ( ) ! = null & & ( fee . getFeeName ( ) . contains ( " 计费重量 " )
| | fee . getFeeName ( ) . contains ( " 4.95 " ) | | fee . getFeeName ( ) . contains ( " 丢货 " ) | | fee . getFeeName ( ) . contains ( " 退件 " ) ) ) ) ;
}
@Test
void spainCttIslandsReadsFiveRateTables ( ) throws Exception
{
Path file = Path . of ( " D:/payment/西班牙CTT带电专线(岛屿+其他地区).xlsx " ) ;
Assumptions . assumeTrue ( Files . exists ( file ) ) ;
PayPreview preview = parser . parseMatched ( Files . readAllBytes ( file ) , file . getFileName ( ) . toString ( ) ,
" 西班牙CTT带电专线(岛屿+其他地区) " , List . of ( " 西班牙CTT带电专线(岛屿+其他地区) " ) ) ;
PayProduct ctt = preview . getProducts ( ) . get ( 0 ) ;
assertEquals ( " OK " , ctt . getParseStatus ( ) ) ;
assertEquals ( " EUR " , ctt . getCurrency ( ) ) ;
assertEquals ( " 续重计费 " , ctt . getChargeType ( ) ) ;
assertTrue ( covers ( ctt . getExtraPerKg ( ) , " 1.86 " ) ) ;
assertTrue ( covers ( ctt . getExtraPerKg ( ) , " 5.79 " ) ) ;
assertEquals ( 48 , ctt . getWeightBands ( ) . size ( ) ) ;
assertEquals ( 4 , ctt . getSurcharges ( ) . size ( ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " 2. INSULAR RATES / STANDARD " . equals ( band . getSectionName ( ) )
& & " Balearic Islands Economy 48H / Majorca " . equals ( band . getZoneName ( ) ) & & " 西班牙 " . equals ( band . getCountryName ( ) ) & & " ES " . equals ( band . getCountryCode ( ) )
& & " Up to 1 kg " . equals ( band . getWeightLabel ( ) ) & & decimal ( band , " 3.39 " ) & & " 每件 " . equals ( band . getChargeName ( ) )
& & " EUR/件 " . equals ( band . getPriceUnit ( ) ) & & band . getWeightMax ( ) . compareTo ( java . math . BigDecimal . ONE ) = = 0 ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " 2. INSULAR RATES / STANDARD " . equals ( band . getSectionName ( ) )
& & " Canary Islands / Minor Islands " . equals ( band . getZoneName ( ) ) & & " Additional Kg " . equals ( band . getWeightLabel ( ) )
& & decimal ( band , " 1.86 " ) & & " 每公斤 " . equals ( band . getChargeName ( ) ) & & band . getWeightMin ( ) = = null ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " 2. INSULAR RATES / EXPRESS " . equals ( band . getSectionName ( ) )
& & " Canary Islands / Mayor Islands " . equals ( band . getZoneName ( ) ) & & " Up to 1 kg " . equals ( band . getWeightLabel ( ) )
& & decimal ( band , " 7.46 " ) ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " Price WITH CUSTOMS CLEARANCE / STANDARD " . equals ( band . getSectionName ( ) )
& & " Canary Islands (*) / Mayor Islands " . equals ( band . getZoneName ( ) ) & & decimal ( band , " 5.33 " ) ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " Price WITH CUSTOMS CLEARANCE / EXPRESS " . equals ( band . getSectionName ( ) )
& & " Portuguese Island / Island " . equals ( band . getZoneName ( ) ) & & " Additional Kg " . equals ( band . getWeightLabel ( ) )
& & decimal ( band , " 5.79 " ) ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > band . getSectionName ( ) . contains ( " CEUTA " )
& & " Ceuta " . equals ( band . getZoneName ( ) ) & & " Up to 1 kg " . equals ( band . getWeightLabel ( ) ) & & decimal ( band , " 7.61 " ) ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > band . getSectionName ( ) . contains ( " CEUTA " )
& & " Andorra " . equals ( band . getZoneName ( ) ) & & " Additional Kg " . equals ( band . getWeightLabel ( ) ) & & decimal ( band , " 2.85 " ) ) ) ;
assertTrue ( ctt . getSurcharges ( ) . stream ( ) . allMatch ( fee - > " Cubic Capacity " . equals ( fee . getFeeName ( ) ) & & fee . getAmount ( ) = = null
& & " MANUAL " . equals ( fee . getCalcRule ( ) ) ) ) ;
assertTrue ( ctt . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 2. INSULAR RATES / STANDARD " . equals ( fee . getSectionName ( ) )
& & fee . getChargeText ( ) . contains ( " 167 kg/m3 " ) & & fee . getChargeText ( ) . contains ( " 333 kg/m3 " )
& & fee . getFeeDesc ( ) . contains ( " Balearic Islands Economy 48H / Majorca " )
& & fee . getFeeDesc ( ) . contains ( " Canary Islands / Interislands " ) ) ) ;
assertTrue ( ctt . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 2. INSULAR RATES / EXPRESS " . equals ( fee . getSectionName ( ) )
& & fee . getFeeDesc ( ) . contains ( " Portuguese Island / Island " ) ) ) ;
assertTrue ( ctt . getSurcharges ( ) . stream ( ) . noneMatch ( fee - > fee . getSectionName ( ) ! = null & & fee . getSectionName ( ) . contains ( " CEUTA " ) ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . noneMatch ( band - > band . getZoneName ( ) ! = null & & band . getZoneName ( ) . contains ( " 19E " ) ) ) ;
assertTrue ( ctt . getSurcharges ( ) . stream ( ) . noneMatch ( fee - > fee . getFeeName ( ) ! = null & & ( fee . getFeeName ( ) . contains ( " 计费重量 " )
| | fee . getFeeName ( ) . contains ( " 4.95 " ) | | fee . getFeeName ( ) . contains ( " 退货 " ) ) ) ) ;
}
@Test
void spainCttFillsCountryAndContinuedWeight ( ) throws Exception
{
Path file = Path . of ( " D:/payment/西班牙CTT.xlsx " ) ;
Assumptions . assumeTrue ( Files . exists ( file ) ) ;
PayPreview preview = parser . parseMatched ( Files . readAllBytes ( file ) , file . getFileName ( ) . toString ( ) , " 西班牙CTT " ,
List . of ( " 西班牙CTT " ) ) ;
PayProduct ctt = preview . getProducts ( ) . get ( 0 ) ;
assertEquals ( " OK " , ctt . getParseStatus ( ) ) ;
assertEquals ( " 西班牙 " , ctt . getCountryScope ( ) ) ;
assertEquals ( " 续重计费 " , ctt . getChargeType ( ) ) ;
assertTrue ( covers ( ctt . getExtraPerKg ( ) , " 0.82 " ) ) ;
assertEquals ( 72 , ctt . getWeightBands ( ) . size ( ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " 西班牙 " . equals ( band . getCountryName ( ) ) & & " ES " . equals ( band . getCountryCode ( ) )
& & " Mainland / Mad High Density " . equals ( band . getZoneName ( ) ) & & " 1kg " . equals ( band . getWeightLabel ( ) )
& & decimal ( band , " 2.1 " ) & & " 每件 " . equals ( band . getChargeName ( ) )
& & band . getWeightMin ( ) . compareTo ( java . math . BigDecimal . ZERO ) = = 0
& & band . getWeightMax ( ) . compareTo ( java . math . BigDecimal . ONE ) = = 0 ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " Mainland / Portugal " . equals ( band . getZoneName ( ) )
& & " 5kg " . equals ( band . getWeightLabel ( ) ) & & decimal ( band , " 3.95 " )
& & band . getWeightMin ( ) . compareTo ( new java . math . BigDecimal ( " 4 " ) ) = = 0 ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " Balearic Islands Economy 48H / Majorca " . equals ( band . getZoneName ( ) )
& & " Kg.ad. " . equals ( band . getWeightLabel ( ) ) & & decimal ( band , " 0.82 " ) & & " 每公斤 " . equals ( band . getChargeName ( ) )
& & band . getWeightMin ( ) = = null ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " Canary Islands / Minor Islands " . equals ( band . getZoneName ( ) )
& & " 1kg " . equals ( band . getWeightLabel ( ) ) & & decimal ( band , " 13.11 " ) ) ) ;
assertTrue ( ctt . getWeightBands ( ) . stream ( ) . noneMatch ( band - > band . getWeightLabel ( ) ! = null & & band . getWeightLabel ( ) . contains ( " 预估 " ) ) ) ;
}
private PayPreview parse ( String prefix ) throws Exception
{
Path dir = Path . of ( " D:/应付供应商 " ) ;
Path file ;
try ( Stream < Path > files = Files . list ( dir ) )
{
file = files . filter ( path - > path . getFileName ( ) . toString ( ) . startsWith ( prefix ) & & path . toString ( ) . endsWith ( " .xlsx " ) )
. findFirst ( ) . orElseThrow ( ) ;
}
return parser . parse ( Files . readAllBytes ( file ) , file . getFileName ( ) . toString ( ) ) ;
}
private void assertPanEuroPrices ( PayProduct dhl )
{
assertEquals ( 52 , dhl . getWeightBands ( ) . size ( ) , dhl . getWeightBands ( ) . stream ( )
. map ( band - > band . getCountryCode ( ) + " / " + band . getChargeName ( ) + " / " + band . getPrice ( ) ) . toList ( ) . toString ( ) ) ;
assertEquals ( 26 , dhl . getWeightBands ( ) . stream ( ) . map ( PayWeightBand : : getCountryCode ) . distinct ( ) . count ( ) ) ;
assertEquals ( 26 , dhl . getWeightBands ( ) . stream ( ) . filter ( band - > " 处理费 " . equals ( band . getChargeName ( ) ) ) . count ( ) ) ;
assertEquals ( 26 , dhl . getWeightBands ( ) . stream ( ) . filter ( band - > " 速递运费 " . equals ( band . getChargeName ( ) ) ) . count ( ) ) ;
assertTrue ( dhl . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " DK " . equals ( band . getCountryCode ( ) )
& & " 处理费 " . equals ( band . getChargeName ( ) ) & & decimal ( band , " 9.71 " ) & & " EUR/ITEM " . equals ( band . getPriceUnit ( ) ) ) ) ;
assertTrue ( dhl . getWeightBands ( ) . stream ( ) . anyMatch ( band - > " DK " . equals ( band . getCountryCode ( ) )
& & " 速递运费 " . equals ( band . getChargeName ( ) ) & & decimal ( band , " 0.23 " ) & & " EUR/KG " . equals ( band . getPriceUnit ( ) ) ) ) ;
assertTrue ( dhl . getServiceNote ( ) ! = null & & dhl . getServiceNote ( ) . contains ( " 报价已含燃油 " ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 其他费用 " . equals ( fee . getSectionName ( ) )
& & " 道路通行费 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 0.19 " ) ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 旺季附加费 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 0.19 " )
& & fee . getEffectiveStart ( ) ! = null ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 高峰旺季附加费 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 0.5 " ) ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 超尺寸超重费 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 14 " )
& & " 产生时收取 " . equals ( fee . getChargeTiming ( ) ) ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 路由码选择附加费 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 0.49 " ) ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 重量信息修正附加费 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 0.99 " ) ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 未预报附加费 " . equals ( fee . getFeeName ( ) )
& & fee . getChargeText ( ) . contains ( " 31.5kg " ) ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 托收附加费 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 2.2 " )
& & textOf ( fee . getFeeDesc ( ) ) . contains ( " Supplemental " ) ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 提供虚假信息罚款费 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 18 " )
& & textOf ( fee . getFeeDesc ( ) ) . contains ( " 故意 " ) ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 高风险地区附加费 " . equals ( fee . getFeeName ( ) )
& & textOf ( fee . getChargeText ( ) ) . contains ( " 高风险 " ) ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 退件费 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 20 " )
& & textOf ( fee . getFeeDesc ( ) ) . contains ( " 空运 " ) ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 退件费 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 10 " )
& & textOf ( fee . getFeeDesc ( ) ) . contains ( " 陆运 " ) ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 重派费 " . equals ( fee . getFeeName ( ) )
& & textOf ( fee . getChargeText ( ) ) . contains ( " 同正向运费 " ) ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 退件操作费 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 0.5 " ) ) ) ;
assertTrue ( dhl . getSurcharges ( ) . stream ( ) . anyMatch ( fee - > " 重派操作费 " . equals ( fee . getFeeName ( ) ) & & decimalFee ( fee , " 1 " ) ) ) ;
}
private PayProduct product ( PayPreview preview , String name )
{
return preview . getProducts ( ) . stream ( ) . filter ( item - > name . equals ( item . getProductName ( ) ) ) . findFirst ( ) . orElseThrow ( ) ;
}
private boolean covers ( String extraPerKg , String price )
{
if ( extraPerKg = = null | | extraPerKg . isBlank ( ) )
{
return false ;
}
java . math . BigDecimal value = new java . math . BigDecimal ( price ) ;
String [ ] parts = extraPerKg . split ( " ~ " ) ;
java . math . BigDecimal low = new java . math . BigDecimal ( parts [ 0 ] ) ;
java . math . BigDecimal high = new java . math . BigDecimal ( parts [ parts . length - 1 ] ) ;
return low . compareTo ( value ) < = 0 & & high . compareTo ( value ) > = 0 ;
}
private String textOf ( String value )
{
return value = = null ? " " : value ;
}
private boolean decimal ( PayWeightBand band , String price )
{
return band . getPrice ( ) ! = null & & band . getPrice ( ) . compareTo ( new java . math . BigDecimal ( price ) ) = = 0 ;
}
private boolean decimalFee ( PaySurcharge fee , String amount )
{
return fee . getAmount ( ) ! = null & & fee . getAmount ( ) . compareTo ( new java . math . BigDecimal ( amount ) ) = = 0 ;
}
private String bandText ( PayWeightBand band )
{
return ( band . getChargeName ( ) = = null ? " " : band . getChargeName ( ) ) + " "
+ ( band . getSectionName ( ) = = null ? " " : band . getSectionName ( ) ) + " "
+ ( band . getZoneName ( ) = = null ? " " : band . getZoneName ( ) ) ;
}
private String zoneTail ( PayWeightBand band )
{
String zone = band . getZoneName ( ) = = null ? " " : band . getZoneName ( ) ;
return zone . contains ( " 英国大陆 " ) ? " 英国大陆 " : zone ;
}
}