空运成交后可在本群继续锁舱;调价同步舱位费用桶;出单按空运段骨架落费并保留陆运车型尺寸;导出文件名乱码修复。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+12
-1
@@ -12,6 +12,7 @@ import java.util.Set;
|
||||
* 空运群「当前工单」能不能激活。纯内存规则,不写库。
|
||||
* <p>
|
||||
* 可激活:业务线 AIR,且六态询价中 / 已报价 / 协商中。
|
||||
* 已成交只在锁舱补参数(allowDeal)时允许记到本群。
|
||||
* 一张单同时只跟一个群;已经绑了别的群就 occupied。
|
||||
*/
|
||||
public final class AirGroupActivateRule {
|
||||
@@ -27,13 +28,23 @@ public final class AirGroupActivateRule {
|
||||
* @return 空串表示可以激活;否则是给智能体的错误码
|
||||
*/
|
||||
public static String rejectReason(InquiryTicket ticket, String chatId) {
|
||||
return rejectReason(ticket, chatId, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁舱补参数时允许把已成交单记在本群,方便下一句不再带工单号。
|
||||
* 未成交、已关闭、转人工仍然不能激活。普通激活不要传 allowDeal。
|
||||
*/
|
||||
public static String rejectReason(InquiryTicket ticket, String chatId, boolean allowDeal) {
|
||||
if (ticket == null) {
|
||||
return "ticket_not_found";
|
||||
}
|
||||
if (!"AIR".equalsIgnoreCase(StringUtils.trimToEmpty(ticket.getBusinessLine()))) {
|
||||
return "not_air";
|
||||
}
|
||||
if (!OPEN.contains(StringUtils.trimToEmpty(ticket.getStatus()))) {
|
||||
String status = StringUtils.trimToEmpty(ticket.getStatus());
|
||||
boolean dealBind = allowDeal && TicketStatuses.DEAL.equals(status);
|
||||
if (!OPEN.contains(status) && !dealBind) {
|
||||
return "terminal";
|
||||
}
|
||||
String bound = StringUtils.trimToEmpty(ticket.getCollabChatId());
|
||||
|
||||
+31
-4
@@ -1043,6 +1043,22 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
}
|
||||
out.put("customer_name", customer);
|
||||
out.put("客户名称", customer);
|
||||
// 陆运/中港表头「联系人」「电话」映射码是 contact_*;事实里常是中文键,必须投影否则格子只剩标签
|
||||
String contactName = first(src, "联系人", "contact_name", "联系人姓名", "客户联系人", "contactName");
|
||||
if (StringUtils.isNotBlank(contactName)) {
|
||||
out.put("contact_name", contactName);
|
||||
out.put("联系人", contactName);
|
||||
}
|
||||
String contactPhone = first(src, "电话", "联系电话", "contact_phone", "手机", "手机号", "contactPhone");
|
||||
if (StringUtils.isNotBlank(contactPhone)) {
|
||||
out.put("contact_phone", contactPhone);
|
||||
out.put("电话", contactPhone);
|
||||
}
|
||||
String cargoValue = first(src, "货值", "货物价值", "cargo_value", "货值(元)", "cargoValue");
|
||||
if (StringUtils.isNotBlank(cargoValue)) {
|
||||
out.put("cargo_value", cargoValue);
|
||||
out.put("货值", cargoValue);
|
||||
}
|
||||
boolean hideAirEta = looksAirInquiry(src, q, businessLine);
|
||||
String eta = first(q, "eta", "transit_days", "transitDays");
|
||||
if (!hideAirEta && StringUtils.isNotBlank(eta) && !"-".equals(eta)) {
|
||||
@@ -1077,12 +1093,18 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
if (rows.isEmpty()) {
|
||||
rows = QuoteTemplateExcelSupport.parseFeeRows(q.get("fee_rows"));
|
||||
}
|
||||
// 空运:已落库 quote 常只有一条 fee_lines,完整明细在 airOptions.departureCharges。
|
||||
// 行数相同也优先 departureCharges(fee_lines 常把单价误当金额,缺单位)。
|
||||
// 空运:完整明细在 airOptions 分桶 + 顶层 feeItems(空运段)。
|
||||
// 行数相同也优先分桶解析(fee_lines 常把单价误当金额,缺单位/项目列)。
|
||||
Object quoteFeeItems = q.get("feeItems");
|
||||
if (quoteFeeItems == null) {
|
||||
quoteFeeItems = q.get("fee_items");
|
||||
}
|
||||
JSONArray fromAir = QuoteTemplateExcelSupport.parseAirDepartureChargeRows(
|
||||
q.get("airOptions"), q.get("air_options"), chargeable, currency);
|
||||
q.get("airOptions"), q.get("air_options"), quoteFeeItems, chargeable, currency);
|
||||
if (!fromAir.isEmpty()) {
|
||||
rows = fromAir;
|
||||
// 分桶优先,但 flat fee_lines 里多出来的人工调价(如「增值费」)必须并进模板,
|
||||
// 禁止整表替换后把群摘要已展示的费用丢掉(WO202610140006)。
|
||||
rows = QuoteTemplateExcelSupport.mergeAirBucketRowsWithFlatManualFees(fromAir, rows);
|
||||
}
|
||||
if (rows.isEmpty()) {
|
||||
String freight = first(q, "freight");
|
||||
@@ -1104,6 +1126,11 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
}
|
||||
}
|
||||
}
|
||||
// 旧 payload 常丢 unit:从 raw_text 回填后再走默认「票」补全
|
||||
if (!rows.isEmpty()) {
|
||||
QuoteTemplateExcelSupport.backfillMissingFeeUnitsFromRawText(
|
||||
rows, first(q, "raw_text", "rawText", "text"));
|
||||
}
|
||||
// 空运:补单位/数量/单价(手工常只有金额,WO020 空运费与增点费)
|
||||
if (!rows.isEmpty()) {
|
||||
List<Map<String, String>> lineMaps = new ArrayList<>();
|
||||
|
||||
+9
-1
@@ -1307,7 +1307,15 @@ public class InquiryWorkOrderServiceImpl extends ServiceImpl<InquiryTicketMapper
|
||||
String room = text(body, "chatId", "chat_id");
|
||||
InquiryTicket ticket = findByWorkOrderNo(no);
|
||||
Map<String, Object> out = new LinkedHashMap<>();
|
||||
String reject = AirGroupActivateRule.rejectReason(ticket, room);
|
||||
Object rawAllow = body == null ? null : body.get("allowDeal");
|
||||
if (rawAllow == null && body != null) {
|
||||
rawAllow = body.get("allow_deal");
|
||||
}
|
||||
// 只有锁舱补参数才带这个标记。普通激活仍拒绝已成交。
|
||||
boolean allowDeal = Boolean.TRUE.equals(rawAllow)
|
||||
|| "true".equalsIgnoreCase(String.valueOf(rawAllow))
|
||||
|| "1".equals(String.valueOf(rawAllow));
|
||||
String reject = AirGroupActivateRule.rejectReason(ticket, room, allowDeal);
|
||||
if (StringUtils.isNotBlank(reject)) {
|
||||
out.put("ok", false);
|
||||
out.put("error", reject);
|
||||
|
||||
+23
@@ -32,11 +32,18 @@ public final class LandQuoteTemplateFields {
|
||||
add("land_freight", "陆运费", "landFreight");
|
||||
add("vehicle_42_fee", "4.2米/3T费用", "vehicle42Fee");
|
||||
add("vehicle_50_fee", "5米/5T费用", "vehicle50Fee");
|
||||
add("vehicle_68_fee", "6.8/5T费用", "vehicle68Fee");
|
||||
add("vehicle_65_fee", "6.5米/8T费用", "vehicle65Fee");
|
||||
add("vehicle_76_fee", "7.6米/8T费用", "vehicle76Fee");
|
||||
add("vehicle_78_fee", "7.8米/10T费用", "vehicle78Fee");
|
||||
add("vehicle_82_fee", "8.2米/10T费用", "vehicle82Fee");
|
||||
add("vehicle_82_12_fee", "8.2米/12T费用", "vehicle8212Fee");
|
||||
add("vehicle_96_fee", "9.6米/10T费用", "vehicle96Fee");
|
||||
add("vehicle_59_fee", "5.9米/20尺费用", "vehicle59Fee");
|
||||
add("vehicle_12_fee", "12米/40尺费用", "vehicle12Fee");
|
||||
add("vehicle_125_fee", "12.5米/40尺费用", "vehicle125Fee");
|
||||
add("vehicle_135_fee", "13.5米/45尺费用", "vehicle135Fee");
|
||||
add("vehicle_15_reefer_fee", "15米/冷柜费用", "vehicle15ReeferFee");
|
||||
add("vehicle_165_fee", "16.5米/53尺费用", "vehicle165Fee");
|
||||
add("vehicle_3t_fee", "4.1*2.1*2.3M/3T费用", "vehicle3TFee");
|
||||
add("vehicle_5t_fee", "6.5*2.4*2.5M/5T费用", "vehicle5TFee");
|
||||
@@ -51,9 +58,25 @@ public final class LandQuoteTemplateFields {
|
||||
LABEL_TO_CODE.put("时效", "transit_days");
|
||||
LABEL_TO_CODE.put("干线费", "land_freight");
|
||||
LABEL_TO_CODE.put("运费", "land_freight");
|
||||
// 20261012 国内整车 / 中港:短表头(无「费用」后缀)也要能对上填表码
|
||||
LABEL_TO_CODE.put("4.2米/3T", "vehicle_42_fee");
|
||||
LABEL_TO_CODE.put("5米/5T", "vehicle_50_fee");
|
||||
LABEL_TO_CODE.put("6.8/5T", "vehicle_68_fee");
|
||||
LABEL_TO_CODE.put("6.8米/5T", "vehicle_68_fee");
|
||||
LABEL_TO_CODE.put("6.5米/8T", "vehicle_65_fee");
|
||||
LABEL_TO_CODE.put("7.6米/8T", "vehicle_76_fee");
|
||||
LABEL_TO_CODE.put("7.8米/10T", "vehicle_78_fee");
|
||||
LABEL_TO_CODE.put("8.2米/10T", "vehicle_82_fee");
|
||||
LABEL_TO_CODE.put("8.2米/12T", "vehicle_82_12_fee");
|
||||
LABEL_TO_CODE.put("9.6米/10T", "vehicle_96_fee");
|
||||
LABEL_TO_CODE.put("5.9米/20尺", "vehicle_59_fee");
|
||||
LABEL_TO_CODE.put("12米/40尺", "vehicle_12_fee");
|
||||
LABEL_TO_CODE.put("12.5米/40尺", "vehicle_125_fee");
|
||||
LABEL_TO_CODE.put("13.5米/45尺", "vehicle_135_fee");
|
||||
LABEL_TO_CODE.put("15米/冷柜", "vehicle_15_reefer_fee");
|
||||
LABEL_TO_CODE.put("16.5米/53尺", "vehicle_165_fee");
|
||||
LABEL_TO_CODE.put("13.75米平板", "vehicle_1375_fee");
|
||||
LABEL_TO_CODE.put("17.5米平板", "vehicle_175_fee");
|
||||
LABEL_TO_CODE.put("元/KG", "price_per_kg");
|
||||
}
|
||||
|
||||
|
||||
+725
-75
File diff suppressed because it is too large
Load Diff
+39
-8
@@ -32,7 +32,7 @@ import java.util.Map;
|
||||
* 有真实 TMS 编号才带 quoteId。有值才出站,缺航班不拦。
|
||||
* SYSTEM 出站精简(quoteId + 航班等标量,不带 validUntil/feeItems);
|
||||
* MANUAL 费用名按 TMS 查价回包原样带回,有效期必须带时区 ISO8601。
|
||||
* 已成交再释放:本地拦住。测服打开 TMS_V2_LOCK_ENABLED;现网验收期保持关闭。
|
||||
* 只有已成交才能锁舱。成交并锁上之后仍可释放。测服打开 TMS_V2_LOCK_ENABLED;现网验收期保持关闭。
|
||||
* 禁止:按 userid 伪造报价;单测打真实 TMS。
|
||||
*/
|
||||
@Slf4j
|
||||
@@ -55,6 +55,27 @@ public class InquiryTmsCabinService {
|
||||
/** 多段询价锁舱的产品口径。智能体群回复与主账拒绝用同一句。 */
|
||||
public static final String MULTI_LOCK_UNSUPPORTED = "多段询价,暂时不支持锁舱";
|
||||
|
||||
/**
|
||||
* 锁舱成交门禁。已成交返回空串,表示可以继续拼锁舱正文。
|
||||
* 协商中、未成交、已关闭/转人工、以及还没回复成交,各自一个错误码。
|
||||
*/
|
||||
static String lockDealGate(String status) {
|
||||
String st = StringUtils.trimToEmpty(status);
|
||||
if (TicketStatuses.DEAL.equals(st)) {
|
||||
return "";
|
||||
}
|
||||
if (TicketStatuses.NEGOTIATING.equals(st)) {
|
||||
return "lock_negotiating";
|
||||
}
|
||||
if (TicketStatuses.LOST.equals(st)) {
|
||||
return "lock_lost";
|
||||
}
|
||||
if (TicketStatuses.CLOSED.equals(st) || TicketStatuses.HANDOFF.equals(st)) {
|
||||
return "ticket_closed";
|
||||
}
|
||||
return "lock_need_deal";
|
||||
}
|
||||
|
||||
/**
|
||||
* 还没到 TMS 之前的拒绝原因。空运单返回空,表示可以继续锁。
|
||||
* 多段整单不是 AIR,但航线会按空运段来锁,单独说明暂不支持。
|
||||
@@ -86,9 +107,18 @@ public class InquiryTmsCabinService {
|
||||
String code = MULTI_LOCK_UNSUPPORTED.equals(lockReject) ? "multi_no_lock" : "not_air";
|
||||
return fail(code, lockReject);
|
||||
}
|
||||
if (TicketStatuses.DEAL.equals(ticket.getStatus())
|
||||
|| TicketStatuses.CLOSED.equals(ticket.getStatus())) {
|
||||
return fail("ticket_closed", "工单已是「" + ticket.getStatus() + "」,不能锁舱");
|
||||
String dealGate = lockDealGate(ticket.getStatus());
|
||||
if ("lock_negotiating".equals(dealGate)) {
|
||||
return fail(dealGate, "工单还在协商中,成交后才能锁舱");
|
||||
}
|
||||
if ("lock_lost".equals(dealGate)) {
|
||||
return fail(dealGate, "工单已是「未成交」,不能锁舱");
|
||||
}
|
||||
if ("ticket_closed".equals(dealGate)) {
|
||||
return fail(dealGate, "工单已是「" + ticket.getStatus() + "」,不能锁舱");
|
||||
}
|
||||
if ("lock_need_deal".equals(dealGate)) {
|
||||
return fail(dealGate, "请销售先确认工单「已成交」");
|
||||
}
|
||||
List<InquiryQuote> rows = quoteRows(no);
|
||||
if (rows.isEmpty()) {
|
||||
@@ -279,7 +309,7 @@ public class InquiryTmsCabinService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能体释放入口。已成交不调 TMS。
|
||||
* 智能体释放入口。已成交仍可取消已锁舱位。没有成功锁舱记录则不调 TMS。
|
||||
*/
|
||||
public Map<String, Object> releaseCabin(Map<String, Object> body) {
|
||||
String no = text(body, "workOrderNo", "work_order_no");
|
||||
@@ -288,9 +318,6 @@ public class InquiryTmsCabinService {
|
||||
if (ticket == null) {
|
||||
return fail("ticket_not_found", "找不到工单");
|
||||
}
|
||||
if (TicketStatuses.DEAL.equals(ticket.getStatus())) {
|
||||
return fail("deal_blocked", "工单已成交");
|
||||
}
|
||||
if (StringUtils.isBlank(lockId)) {
|
||||
return fail("release_need_lock", "本单还没有成功锁舱记录");
|
||||
}
|
||||
@@ -1442,6 +1469,10 @@ public class InquiryTmsCabinService {
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试会话 3ba460:手工报价锁舱死循环取证。写 NDJSON,不含密钥。
|
||||
*/
|
||||
|
||||
private static String text(Map<String, Object> body, String... keys) {
|
||||
if (body == null || keys == null) {
|
||||
return "";
|
||||
|
||||
+68
-21
@@ -786,8 +786,9 @@ public class InquiryTmsQueryService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 空运舱位选项上的 departureCharges → 扁平费用行。
|
||||
* TMS 字段:chargeName / unitPrice / unit / currency;行金额出单时再按计费重补。
|
||||
* 空运舱位分桶费用 → 扁平费用行。
|
||||
* TMS:feeItems=空运段,departureCharges=起运港,destinationCharges=目的地;
|
||||
* 行金额出单时再按计费重补(仅空运段)。
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<Map<String, String>> extractAirDepartureCharges(Map<String, Object> data, String headerCurrency) {
|
||||
@@ -806,15 +807,21 @@ public class InquiryTmsQueryService {
|
||||
if (!(raw instanceof List)) {
|
||||
return out;
|
||||
}
|
||||
Object topFeeItems = first(data.get("feeItems"), data.get("fee_items"));
|
||||
for (Object optObj : (List<?>) raw) {
|
||||
if (!(optObj instanceof Map)) {
|
||||
continue;
|
||||
}
|
||||
Map<?, ?> opt = (Map<?, ?>) optObj;
|
||||
// 合并 departureCharges + feeItems,避免只取附加费丢掉空运费
|
||||
List<Map<?, ?>> charges = mergeAirOptionChargeMaps(opt);
|
||||
Map<Object, Object> opt = new LinkedHashMap<>((Map<?, ?>) optObj);
|
||||
// 舱位缺 feeItems 时补整单顶层(TMS 常把空运费放在 data.feeItems)
|
||||
if (topFeeItems instanceof List
|
||||
&& !(opt.get("feeItems") instanceof List)
|
||||
&& !(opt.get("fee_items") instanceof List)) {
|
||||
opt.put("feeItems", topFeeItems);
|
||||
}
|
||||
List<Map<String, Object>> charges = mergeAirOptionChargeMaps(opt);
|
||||
String optCcy = firstText(opt.get("currency"), headerCurrency);
|
||||
for (Map<?, ?> ch : charges) {
|
||||
for (Map<String, Object> ch : charges) {
|
||||
if (ch == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -823,18 +830,24 @@ public class InquiryTmsQueryService {
|
||||
if (StringUtils.isBlank(name) || StringUtils.isBlank(unitPrice)) {
|
||||
continue;
|
||||
}
|
||||
String project = firstText(ch.get("project"), "其它");
|
||||
String amountRaw = firstText(ch.get("amount"), unitPrice);
|
||||
String lineCcy = firstText(ch.get("currency"), optCcy, headerCurrency);
|
||||
Map<String, String> extras = new LinkedHashMap<>();
|
||||
putExtra(extras, "unit", firstText(ch.get("unit"), ch.get("unitName")));
|
||||
putExtra(extras, "unit_price", unitPrice);
|
||||
// 固定费无数量时默认 1;运输费 enrich 会改成计费重
|
||||
putExtra(extras, "project", project);
|
||||
String qty = firstText(ch.get("quantity"), ch.get("chargeQuantity"), ch.get("charge_quantity"));
|
||||
putExtra(extras, "quantity", StringUtils.isBlank(qty) ? "1" : qty);
|
||||
putExtra(extras, "project", isAirWeightFeeName(name) ? "空运段" : "起运港");
|
||||
// 先把单价写入 amount;运输费会在 enrich 里改成 单价×计费重
|
||||
addFeeLine(out, name, unitPrice, lineCcy, extras);
|
||||
boolean airSeg = "空运段".equals(project) && isAirWeightFeeName(name);
|
||||
if (StringUtils.isNotBlank(qty)) {
|
||||
putExtra(extras, "quantity", qty);
|
||||
} else if (!airSeg) {
|
||||
putExtra(extras, "quantity", "1");
|
||||
}
|
||||
// 空运段先写金额原文(或单价);enrich 再按计费重重算
|
||||
addFeeLine(out, name, amountRaw, lineCcy, extras);
|
||||
}
|
||||
// departureCharges 只有附加费时,用舱位 salePrice 补空运费(与出单 parse 对齐)
|
||||
// 无空运段运费时,用舱位 salePrice 补空运费(与出单 parse 对齐)
|
||||
injectAirFreightFeeLineIfMissing(out, opt, headerCurrency);
|
||||
// 通常只用首个舱位选项的费用明细
|
||||
if (!out.isEmpty()) {
|
||||
@@ -844,15 +857,31 @@ public class InquiryTmsQueryService {
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 合并舱位 departureCharges / feeItems,同名去重。 */
|
||||
/**
|
||||
* 按 TMS 桶合并:feeItems→空运段,departureCharges→起运港,destinationCharges→目的地。
|
||||
* 同名去重,先出现的桶优先。
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<Map<?, ?>> mergeAirOptionChargeMaps(Map<?, ?> opt) {
|
||||
List<Map<?, ?>> out = new ArrayList<>();
|
||||
static List<Map<String, Object>> mergeAirOptionChargeMaps(Map<?, ?> opt) {
|
||||
List<Map<String, Object>> out = new ArrayList<>();
|
||||
if (opt == null) {
|
||||
return out;
|
||||
}
|
||||
Set<String> seen = new HashSet<>();
|
||||
for (Object key : new Object[]{"departureCharges", "departure_charges", "charges", "feeItems", "fee_items"}) {
|
||||
appendAirBucketMaps(out, seen, opt, "空运段", "feeItems", "fee_items");
|
||||
appendAirBucketMaps(out, seen, opt, "起运港", "departureCharges", "departure_charges", "charges");
|
||||
appendAirBucketMaps(out, seen, opt, "目的地",
|
||||
"destinationCharges", "destination_charges", "arrivalCharges", "destCharges");
|
||||
return out;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void appendAirBucketMaps(List<Map<String, Object>> out, Set<String> seen,
|
||||
Map<?, ?> opt, String project, String... keys) {
|
||||
if (out == null || opt == null || keys == null) {
|
||||
return;
|
||||
}
|
||||
for (String key : keys) {
|
||||
Object raw = opt.get(key);
|
||||
if (!(raw instanceof List)) {
|
||||
continue;
|
||||
@@ -870,22 +899,34 @@ public class InquiryTmsQueryService {
|
||||
if (!seen.add(dedupe)) {
|
||||
continue;
|
||||
}
|
||||
out.add(ch);
|
||||
Map<String, Object> copy = new LinkedHashMap<>();
|
||||
for (Map.Entry<?, ?> e : ch.entrySet()) {
|
||||
if (e.getKey() != null) {
|
||||
copy.put(String.valueOf(e.getKey()), e.getValue());
|
||||
}
|
||||
}
|
||||
copy.put("project", project);
|
||||
out.add(copy);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查价投影 fee_lines:明细无运输/空运费时,用舱位 salePrice/unitPrice 注入。
|
||||
* 查价投影 fee_lines:明细无空运段运费时,用舱位 salePrice/unitPrice 注入。
|
||||
* 与 QuoteTemplateExcelSupport.injectAirFreightLineIfMissing 口径一致。
|
||||
* 起运港「运输费」不算已有空运段。
|
||||
*/
|
||||
static void injectAirFreightFeeLineIfMissing(List<Map<String, String>> lines, Map<?, ?> opt, String headerCurrency) {
|
||||
if (lines == null || opt == null) {
|
||||
return;
|
||||
}
|
||||
for (Map<String, String> line : lines) {
|
||||
if (line != null && isAirWeightFeeName(line.get("name"))) {
|
||||
if (line == null) {
|
||||
continue;
|
||||
}
|
||||
String project = StringUtils.trimToEmpty(line.get("project"));
|
||||
if ((project.isEmpty() || "空运段".equals(project) || "空运".equals(project))
|
||||
&& isAirWeightFeeName(line.get("name"))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -950,16 +991,22 @@ public class InquiryTmsQueryService {
|
||||
if (line == null || !isAirWeightFeeName(line.get("name"))) {
|
||||
continue;
|
||||
}
|
||||
// 已标明非空运段(起运港「运输费」等)禁止乘计费重
|
||||
String project = StringUtils.trimToEmpty(line.get("project"));
|
||||
if (StringUtils.isNotBlank(project)
|
||||
&& !"空运段".equals(project) && !"空运".equals(project)) {
|
||||
continue;
|
||||
}
|
||||
String unitPrice = firstText(line.get("unit_price"), line.get("unitPrice"));
|
||||
String qty = firstText(line.get("quantity"));
|
||||
String amount = firstText(line.get("amount"));
|
||||
String unit = firstText(line.get("unit"));
|
||||
if (StringUtils.isBlank(qty)) {
|
||||
line.put("quantity", chargeable);
|
||||
qty = chargeable;
|
||||
}
|
||||
// TMS 常给单位「板数」;按计费重计价时统一写成 KG
|
||||
line.put("unit", "KG");
|
||||
line.put("project", "空运段");
|
||||
// 有数量、无单价:当前 amount 当作单价(TMS 把费率塞进 salePrice/amount)
|
||||
if (StringUtils.isBlank(unitPrice) && StringUtils.isNotBlank(amount)) {
|
||||
line.put("unit_price", amount);
|
||||
|
||||
+3
@@ -26,6 +26,9 @@ public class AirGroupActivateRuleTest {
|
||||
public void dealRejected() {
|
||||
InquiryTicket ticket = ticket("AIR", TicketStatuses.DEAL, "wr_air_1");
|
||||
assertEquals("terminal", AirGroupActivateRule.rejectReason(ticket, "wr_air_1"));
|
||||
assertEquals("", AirGroupActivateRule.rejectReason(ticket, "wr_air_1", true));
|
||||
InquiryTicket lost = ticket("AIR", TicketStatuses.LOST, "");
|
||||
assertEquals("terminal", AirGroupActivateRule.rejectReason(lost, "wr_air_1", true));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+273
-9
@@ -1,5 +1,6 @@
|
||||
package org.jeecg.modules.inquiry.template;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.apache.poi.ss.usermodel.DataFormatter;
|
||||
@@ -920,6 +921,180 @@ public class QuoteTemplateFillTest {
|
||||
assertEquals("起运港", rows.getJSONObject(2).getString("project"));
|
||||
}
|
||||
|
||||
/**
|
||||
* TMS 分桶:feeItems→空运段,departureCharges→起运港(含名称像「运输费」的),
|
||||
* destinationCharges→目的地;起运「运输费」不得乘计费重把金额算成单价。
|
||||
*/
|
||||
@Test
|
||||
public void parseAirChargesAssignProjectByTmsBucket() {
|
||||
System.setProperty("ytd.agent.debug.log",
|
||||
java.nio.file.Paths.get("").toAbsolutePath().getParent() != null
|
||||
? java.nio.file.Paths.get("debug-4c500b.log").toAbsolutePath().toString()
|
||||
: "debug-4c500b.log");
|
||||
System.setProperty("ytd.agent.debug.runId", "pre-fix-bucket");
|
||||
String airOptions = "["
|
||||
+ "{\"optionNo\":\"AIR-OPT01\",\"salePrice\":183370.0,\"currency\":\"CNY\","
|
||||
+ "\"chargeableWeightKg\":16670.0,"
|
||||
+ "\"feeItems\":[{\"feeCode\":\"AIR_FREIGHT\",\"feeName\":\"空运费\","
|
||||
+ "\"unitPrice\":11.0,\"quantity\":16670.0,\"amount\":183370.0,\"unit\":\"KG\",\"currency\":\"CNY\"}],"
|
||||
+ "\"departureCharges\":["
|
||||
+ "{\"chargeName\":\"运输费\",\"unitPrice\":11.0,\"unit\":\"板数\",\"currency\":\"CNY\"},"
|
||||
+ "{\"chargeName\":\"提送货费\",\"unitPrice\":6.0,\"quantity\":1,\"unit\":\"板数\",\"currency\":\"CNY\"},"
|
||||
+ "{\"chargeName\":\"人工费\",\"unitPrice\":123.0,\"quantity\":1,\"unit\":\"柜\",\"currency\":\"CNY\"}"
|
||||
+ "],"
|
||||
+ "\"destinationCharges\":["
|
||||
+ "{\"chargeName\":\"目的港操作费\",\"unitPrice\":50.0,\"quantity\":1,\"unit\":\"票\",\"currency\":\"CNY\"}"
|
||||
+ "]}]";
|
||||
JSONArray rows = QuoteTemplateExcelSupport.parseAirDepartureChargeRows(
|
||||
airOptions, null, "16670", "CNY");
|
||||
assertEquals(5, rows.size());
|
||||
// WO006:flat 有增值费、分桶没有 → 合并后必须进起运港,不能被整表替换丢掉
|
||||
JSONArray flat = JSON.parseArray(
|
||||
"[{\"name\":\"运输费\",\"amount\":\"11\"},"
|
||||
+ "{\"name\":\"提送货费\",\"amount\":\"6\"},"
|
||||
+ "{\"name\":\"人工费\",\"amount\":\"123\"},"
|
||||
+ "{\"name\":\"增值费\",\"amount\":\"1000\",\"unit\":\"板\",\"quantity\":\"100\","
|
||||
+ "\"unit_price\":\"10\",\"project\":\"起运港\"}]");
|
||||
JSONArray merged = QuoteTemplateExcelSupport.mergeAirBucketRowsWithFlatManualFees(rows, flat);
|
||||
boolean hasZz = false;
|
||||
for (int i = 0; i < merged.size(); i++) {
|
||||
JSONObject one = merged.getJSONObject(i);
|
||||
if (one != null && "增值费".equals(one.getString("name"))) {
|
||||
hasZz = true;
|
||||
assertEquals("起运港", one.getString("project"));
|
||||
assertEquals("1000", one.getString("amount"));
|
||||
assertEquals("板", one.getString("unit"));
|
||||
}
|
||||
}
|
||||
assertTrue("人工增值费必须并进分桶结果", hasZz);
|
||||
assertEquals("空运费", rows.getJSONObject(0).getString("name"));
|
||||
assertEquals("空运段", rows.getJSONObject(0).getString("project"));
|
||||
assertTrue(rows.getJSONObject(0).getString("amount").startsWith("183370"));
|
||||
assertEquals("运输费", rows.getJSONObject(1).getString("name"));
|
||||
assertEquals("起运港", rows.getJSONObject(1).getString("project"));
|
||||
assertEquals("1", rows.getJSONObject(1).getString("quantity"));
|
||||
assertTrue(rows.getJSONObject(1).getString("amount").startsWith("11"));
|
||||
assertEquals("提送货费", rows.getJSONObject(2).getString("name"));
|
||||
assertEquals("起运港", rows.getJSONObject(2).getString("project"));
|
||||
assertEquals("人工费", rows.getJSONObject(3).getString("name"));
|
||||
assertEquals("起运港", rows.getJSONObject(3).getString("project"));
|
||||
assertEquals("目的港操作费", rows.getJSONObject(4).getString("name"));
|
||||
assertEquals("目的地", rows.getJSONObject(4).getString("project"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 顶层 feeItems + 舱位 departureCharges:空运段来自整单 feeItems,起运港来自舱位。
|
||||
*/
|
||||
@Test
|
||||
public void parseAirChargesUsesTopLevelFeeItemsAsAirSegment() {
|
||||
String airOptions = "["
|
||||
+ "{\"optionNo\":\"AIR-OPT01\",\"salePrice\":183370.0,\"currency\":\"CNY\","
|
||||
+ "\"chargeableWeightKg\":16670.0,"
|
||||
+ "\"departureCharges\":["
|
||||
+ "{\"chargeName\":\"运输费\",\"unitPrice\":11.0,\"unit\":\"板数\",\"currency\":\"CNY\"},"
|
||||
+ "{\"chargeName\":\"提送货费\",\"unitPrice\":6.0,\"unit\":\"板数\",\"currency\":\"CNY\"},"
|
||||
+ "{\"chargeName\":\"人工费\",\"unitPrice\":123.0,\"unit\":\"柜\",\"currency\":\"CNY\"}"
|
||||
+ "]}]";
|
||||
String topFees = "[{\"feeCode\":\"AIR_FREIGHT\",\"feeName\":\"空运费\","
|
||||
+ "\"unitPrice\":11.0,\"quantity\":16670.0,\"amount\":183370.0,\"unit\":\"KG\",\"currency\":\"CNY\"}]";
|
||||
JSONArray rows = QuoteTemplateExcelSupport.parseAirDepartureChargeRows(
|
||||
airOptions, null, topFees, "16670", "CNY");
|
||||
assertEquals("空运费", rows.getJSONObject(0).getString("name"));
|
||||
assertEquals("空运段", rows.getJSONObject(0).getString("project"));
|
||||
assertEquals("运输费", rows.getJSONObject(1).getString("name"));
|
||||
assertEquals("起运港", rows.getJSONObject(1).getString("project"));
|
||||
// 起运「运输费」不得再被当成空运段去乘计费重
|
||||
assertFalse("16670".equals(rows.getJSONObject(1).getString("quantity"))
|
||||
|| "16670.0".equals(rows.getJSONObject(1).getString("quantity")));
|
||||
}
|
||||
|
||||
/**
|
||||
* 人工只报起运港费:必须落到模板「起运港」段,禁止占掉「空运段」首行并把骨架改成起运港。
|
||||
*/
|
||||
@Test
|
||||
public void airManualOriginFeeWritesIntoOriginBandNotAirSegment() throws Exception {
|
||||
System.setProperty("ytd.agent.debug.log",
|
||||
new java.io.File("debug-4c500b.log").getAbsolutePath());
|
||||
System.setProperty("ytd.agent.debug.runId", "post-fix-place");
|
||||
Path src = Files.createTempFile("quote-air-band-src", ".xlsx");
|
||||
Path out = Files.createTempFile("quote-air-band-out", ".xlsx");
|
||||
try (Workbook wb = new XSSFWorkbook()) {
|
||||
Sheet sheet = wb.createSheet("Sheet2");
|
||||
org.apache.poi.ss.usermodel.Row head = sheet.createRow(15);
|
||||
head.createCell(0).setCellValue("项目");
|
||||
head.createCell(1).setCellValue("费用名称");
|
||||
head.createCell(2).setCellValue("单位");
|
||||
head.createCell(3).setCellValue("币别");
|
||||
head.createCell(4).setCellValue("单价");
|
||||
head.createCell(5).setCellValue("数量");
|
||||
head.createCell(6).setCellValue("金额");
|
||||
org.apache.poi.ss.usermodel.Row r1 = sheet.createRow(16);
|
||||
r1.createCell(0).setCellValue("空运段");
|
||||
r1.createCell(1).setCellValue("样例空运费");
|
||||
r1.createCell(6).setCellValue(1.0);
|
||||
org.apache.poi.ss.usermodel.Row r2 = sheet.createRow(17);
|
||||
r2.createCell(0).setCellValue("起运港");
|
||||
r2.createCell(1).setCellValue("样例增点");
|
||||
r2.createCell(6).setCellValue(1.0);
|
||||
org.apache.poi.ss.usermodel.Row r3 = sheet.createRow(18);
|
||||
r3.createCell(0).setCellValue("起运港");
|
||||
r3.createCell(1).setCellValue("样例2");
|
||||
org.apache.poi.ss.usermodel.Row r4 = sheet.createRow(19);
|
||||
r4.createCell(0).setCellValue("其他");
|
||||
org.apache.poi.ss.usermodel.Row total = sheet.createRow(22);
|
||||
total.createCell(0).setCellValue("预估费用");
|
||||
total.createCell(6).setCellFormula("SUM(G17:G22)");
|
||||
try (java.io.OutputStream os = Files.newOutputStream(src)) {
|
||||
wb.write(os);
|
||||
}
|
||||
}
|
||||
JSONObject mapping = new JSONObject();
|
||||
mapping.put("fields", new JSONObject());
|
||||
JSONObject region = new JSONObject(true);
|
||||
region.put("id", "air_fee_detail");
|
||||
region.put("anchor", "Sheet2!A17");
|
||||
region.put("stopAtText", java.util.Collections.singletonList("预估费用"));
|
||||
JSONObject cols = new JSONObject(true);
|
||||
cols.put("project", "A");
|
||||
cols.put("name", "B");
|
||||
cols.put("unit", "C");
|
||||
cols.put("currency", "D");
|
||||
cols.put("unit_price", "E");
|
||||
cols.put("quantity", "F");
|
||||
cols.put("amount", "G");
|
||||
region.put("columns", cols);
|
||||
mapping.put("feeRegions", new JSONArray(java.util.Collections.singletonList(region)));
|
||||
JSONObject facts = new JSONObject(true);
|
||||
facts.put("total_amount", "1000");
|
||||
facts.put("currency", "CNY");
|
||||
JSONArray feeRows = new JSONArray();
|
||||
JSONObject one = new JSONObject(true);
|
||||
one.put("project", "起运港");
|
||||
one.put("name", "增值费");
|
||||
one.put("unit", "板");
|
||||
one.put("unit_price", "10");
|
||||
one.put("quantity", "100");
|
||||
one.put("amount", "1000");
|
||||
one.put("currency", "CNY");
|
||||
feeRows.add(one);
|
||||
facts.put("fee_rows", feeRows);
|
||||
QuoteTemplateExcelSupport.fillWorkbook(src, out, mapping, facts);
|
||||
try (Workbook got = new XSSFWorkbook(Files.newInputStream(out))) {
|
||||
Sheet sheet = got.getSheet("Sheet2");
|
||||
DataFormatter fmt = new DataFormatter();
|
||||
// 空运段骨架保留,样例费目清掉
|
||||
assertEquals("空运段", fmt.formatCellValue(sheet.getRow(16).getCell(0)));
|
||||
assertTrue(StringUtils.isBlank(fmt.formatCellValue(sheet.getRow(16).getCell(1))));
|
||||
// 增值费落在起运港段,不在首行
|
||||
assertEquals("起运港", fmt.formatCellValue(sheet.getRow(17).getCell(0)));
|
||||
assertEquals("增值费", fmt.formatCellValue(sheet.getRow(17).getCell(1)));
|
||||
assertEquals("1000", fmt.formatCellValue(sheet.getRow(17).getCell(6)));
|
||||
} finally {
|
||||
Files.deleteIfExists(src);
|
||||
Files.deleteIfExists(out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 映射未标 project 列时,表头「项目」仍要写入,增点费不能留在模板样例「空运段」。
|
||||
*/
|
||||
@@ -2028,11 +2203,12 @@ public class QuoteTemplateFillTest {
|
||||
Sheet sheet = filled.getSheetAt(0);
|
||||
assertEquals("4.1*2.1*2.3M", read(sheet, 8, 3));
|
||||
assertEquals("广东省-深圳市", read(sheet, 9, 1));
|
||||
assertEquals("", read(sheet, 9, 2));
|
||||
// 产品口径 A:单腿门到门两段都写完整起讫
|
||||
assertEquals("谅山-春疆货场", read(sheet, 9, 2));
|
||||
assertEquals("4000", normalizeMoney(read(sheet, 9, 3)));
|
||||
assertEquals("", read(sheet, 9, 4));
|
||||
assertEquals("2.8x1.5x1.6M", read(sheet, 27, 3));
|
||||
assertEquals("", read(sheet, 28, 1));
|
||||
assertEquals("广东省-深圳市", read(sheet, 28, 1));
|
||||
assertEquals("谅山-春疆货场", read(sheet, 28, 2));
|
||||
assertEquals("", read(sheet, 28, 3));
|
||||
assertEquals("", read(sheet, 28, 4));
|
||||
@@ -2079,7 +2255,96 @@ public class QuoteTemplateFillTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 跨境整车国内段只写境内起点,境外段只写境外终点,不能两段都写成郑州→吉隆坡。
|
||||
* WO202610140014 复现:单腿广西→胡志明;模板 3T 有尺寸 vs 无尺寸。
|
||||
* 用于对照 debug-67d875:切片是否把目的地/起始地留空,以及填表是否清掉 3T 尺寸。
|
||||
*/
|
||||
@Test
|
||||
public void landCrossBorderFtlWo014OdAnd3tDimProbe() throws Exception {
|
||||
System.setProperty("ytd.agent.debug.runId", "wo014-junit");
|
||||
Path src = Files.createTempFile("quote-land-wo014-src", ".xlsx");
|
||||
Path out = Files.createTempFile("quote-land-wo014-out", ".xlsx");
|
||||
try (Workbook wb = new XSSFWorkbook()) {
|
||||
Sheet sheet = wb.createSheet("跨境整车");
|
||||
row(sheet, 5).createCell(0).setCellValue("一、国内段报价");
|
||||
org.apache.poi.ss.usermodel.Row head1 = row(sheet, 7);
|
||||
head1.createCell(1).setCellValue("起始地");
|
||||
head1.createCell(2).setCellValue("目的地");
|
||||
head1.createCell(3).setCellValue("3T");
|
||||
head1.createCell(4).setCellValue("5T");
|
||||
org.apache.poi.ss.usermodel.Row dim1 = row(sheet, 8);
|
||||
// 有尺寸:应保留;同时留一列空尺寸模拟「模板缺 3T 尺寸」时对照
|
||||
dim1.createCell(3).setCellValue("4.1*2.1*2.3M");
|
||||
dim1.createCell(4).setCellValue("6.5*2.4*2.5M");
|
||||
row(sheet, 24).createCell(0).setCellValue("二、境外报价");
|
||||
org.apache.poi.ss.usermodel.Row head2 = row(sheet, 26);
|
||||
head2.createCell(1).setCellValue("起始地");
|
||||
head2.createCell(2).setCellValue("目的地");
|
||||
head2.createCell(3).setCellValue("3.5T");
|
||||
org.apache.poi.ss.usermodel.Row dim2 = row(sheet, 27);
|
||||
dim2.createCell(3).setCellValue("4.7x1.8x1.8M");
|
||||
try (java.io.OutputStream os = Files.newOutputStream(src)) {
|
||||
wb.write(os);
|
||||
}
|
||||
}
|
||||
JSONObject mapping = new JSONObject(true);
|
||||
mapping.put("sheet", "跨境整车");
|
||||
JSONArray regions = new JSONArray();
|
||||
JSONObject inland = new JSONObject(true);
|
||||
inland.put("title", "国内段报价");
|
||||
inland.put("headerRow", 8);
|
||||
inland.put("dataStartRow", 9);
|
||||
JSONObject cols1 = new JSONObject(true);
|
||||
cols1.put("origin", "B");
|
||||
cols1.put("destination", "C");
|
||||
JSONObject rates1 = new JSONObject(true);
|
||||
rates1.put("3T", "D");
|
||||
rates1.put("5T", "E");
|
||||
cols1.put("vehicleRates", rates1);
|
||||
inland.put("columns", cols1);
|
||||
regions.add(inland);
|
||||
JSONObject overseas = new JSONObject(true);
|
||||
overseas.put("title", "境外报价");
|
||||
overseas.put("headerRow", 27);
|
||||
overseas.put("dataStartRow", 28);
|
||||
JSONObject cols2 = new JSONObject(true);
|
||||
cols2.put("origin", "B");
|
||||
cols2.put("destination", "C");
|
||||
JSONObject rates2 = new JSONObject(true);
|
||||
rates2.put("3.5T", "D");
|
||||
cols2.put("vehicleRates", rates2);
|
||||
overseas.put("columns", cols2);
|
||||
regions.add(overseas);
|
||||
mapping.put("routeRegions", regions);
|
||||
JSONObject facts = new JSONObject(true);
|
||||
facts.put("origin", "广西壮族自治区");
|
||||
facts.put("destination", "胡志明");
|
||||
facts.put("currency", "CNY");
|
||||
facts.put("route_rows",
|
||||
"[{\"origin\":\"广西壮族自治区\",\"destination\":\"胡志明\","
|
||||
+ "\"vehicleType\":\"3T\",\"amount\":\"12500.00\",\"currency\":\"CNY\","
|
||||
+ "\"segmentSequence\":\"1\",\"routeLegSequence\":\"1\"}]");
|
||||
QuoteTemplateExcelSupport.fillWorkbook(src, out, mapping, facts);
|
||||
try (Workbook filled = new XSSFWorkbook(Files.newInputStream(out))) {
|
||||
Sheet sheet = filled.getSheetAt(0);
|
||||
// 有尺寸时应保留(排除「填表清掉尺寸」)
|
||||
// 3T/5T 尺寸副表头必须保留(后台模板有值,出单不能清掉)
|
||||
assertEquals("4.1*2.1*2.3M", read(sheet, 8, 3));
|
||||
assertEquals("6.5*2.4*2.5M", read(sheet, 8, 4));
|
||||
assertEquals("广西壮族自治区", read(sheet, 9, 1));
|
||||
// 产品口径 A:国内段/境外段都写完整广西→胡志明
|
||||
assertEquals("胡志明", read(sheet, 9, 2));
|
||||
assertEquals("12500", normalizeMoney(read(sheet, 9, 3)));
|
||||
assertEquals("广西壮族自治区", read(sheet, 28, 1));
|
||||
assertEquals("胡志明", read(sheet, 28, 2));
|
||||
assertEquals("4.7x1.8x1.8M", read(sheet, 27, 3));
|
||||
} finally {
|
||||
Files.deleteIfExists(src);
|
||||
Files.deleteIfExists(out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跨境整车单腿门到门:国内段与境外段都写完整起讫(产品口径 A)。
|
||||
*/
|
||||
@Test
|
||||
public void landCrossBorderFtlSplitsDomesticAndOverseasOd() throws Exception {
|
||||
@@ -2143,9 +2408,9 @@ public class QuoteTemplateFillTest {
|
||||
Sheet sheet = filled.getSheetAt(0);
|
||||
assertEquals("起始地", read(sheet, 7, 1));
|
||||
assertEquals("郑州", read(sheet, 8, 1));
|
||||
assertEquals("", read(sheet, 8, 2));
|
||||
assertEquals("吉隆坡", read(sheet, 8, 2));
|
||||
assertEquals("起始地", read(sheet, 26, 1));
|
||||
assertEquals("", read(sheet, 27, 1));
|
||||
assertEquals("郑州", read(sheet, 27, 1));
|
||||
assertEquals("吉隆坡", read(sheet, 27, 2));
|
||||
assertEquals("N+3", read(sheet, 8, 15));
|
||||
assertEquals("CNY", read(sheet, 8, 16));
|
||||
@@ -2369,9 +2634,9 @@ public class QuoteTemplateFillTest {
|
||||
try (Workbook filled = new XSSFWorkbook(Files.newInputStream(out))) {
|
||||
Sheet sheet = filled.getSheetAt(0);
|
||||
assertEquals("起始地", read(sheet, 6, 1));
|
||||
// 国内段只写境内起点;春疆货场是境外终点,不能被「运输费」盖掉起点
|
||||
// 产品口径 A:单腿门到门两段都写完整起讫;运输费不能盖掉起点
|
||||
assertEquals("深圳", read(sheet, 8, 1));
|
||||
assertEquals("", read(sheet, 8, 2));
|
||||
assertEquals("春疆货场", read(sheet, 8, 2));
|
||||
assertEquals("N+1", read(sheet, 8, 13));
|
||||
assertEquals("CNY", read(sheet, 8, 14));
|
||||
assertEquals("8%", read(sheet, 8, 15));
|
||||
@@ -2380,8 +2645,7 @@ public class QuoteTemplateFillTest {
|
||||
assertFalse("运输费".equals(read(sheet, 8, 1)));
|
||||
assertEquals("币种", read(sheet, 6, 14));
|
||||
assertEquals("燃油附加费率", read(sheet, 5, 15));
|
||||
// 境外段只写境外终点
|
||||
assertEquals("", read(sheet, 27, 1));
|
||||
assertEquals("深圳", read(sheet, 27, 1));
|
||||
assertEquals("春疆货场", read(sheet, 27, 2));
|
||||
} finally {
|
||||
Files.deleteIfExists(src);
|
||||
|
||||
+11
@@ -15,6 +15,17 @@ import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
*/
|
||||
public class InquiryTmsCabinServiceTest {
|
||||
|
||||
@Test
|
||||
public void lockOnlyAfterDeal() {
|
||||
assertEquals("", InquiryTmsCabinService.lockDealGate("已成交"));
|
||||
assertEquals("lock_need_deal", InquiryTmsCabinService.lockDealGate("已报价"));
|
||||
assertEquals("lock_need_deal", InquiryTmsCabinService.lockDealGate("询价中"));
|
||||
assertEquals("lock_negotiating", InquiryTmsCabinService.lockDealGate("协商中"));
|
||||
assertEquals("lock_lost", InquiryTmsCabinService.lockDealGate("未成交"));
|
||||
assertEquals("ticket_closed", InquiryTmsCabinService.lockDealGate("已关闭"));
|
||||
assertEquals("ticket_closed", InquiryTmsCabinService.lockDealGate("转人工"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiInquiryCannotLockCabin() {
|
||||
assertEquals(
|
||||
|
||||
+7
-2
@@ -366,14 +366,19 @@ public class TmsQuoteCurrencyTest {
|
||||
tms.put("tmsData", data);
|
||||
Map<String, String> quote = InquiryTmsQueryService.toAgentQuote(tms);
|
||||
String fees = quote.get("fee_lines");
|
||||
// 起运港桶保留运输费/提送货/人工;空运段由 salePrice 注入空运费
|
||||
assertTrue(fees.contains("运输费"));
|
||||
assertTrue(fees.contains("提送货费"));
|
||||
assertTrue(fees.contains("人工费"));
|
||||
assertTrue(fees.contains("\"amount\":\"183370\""));
|
||||
assertTrue(fees.contains("空运费"));
|
||||
assertTrue(fees.contains("\"project\":\"空运段\""));
|
||||
assertTrue(fees.contains("\"project\":\"起运港\""));
|
||||
assertTrue(fees.contains("\"amount\":\"183370\"") || fees.contains("\"amount\":\"183370.0\""));
|
||||
assertTrue(fees.contains("\"amount\":\"6.0\"") || fees.contains("\"amount\":\"6\""));
|
||||
assertTrue(fees.contains("\"amount\":\"123.0\"") || fees.contains("\"amount\":\"123\""));
|
||||
assertTrue(fees.contains("\"unit\":\"KG\""));
|
||||
assertTrue(fees.contains("\"quantity\":\"16670.0000\""));
|
||||
assertTrue(fees.contains("\"quantity\":\"16670\"") || fees.contains("\"quantity\":\"16670.0\"")
|
||||
|| fees.contains("\"quantity\":\"16670.0000\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user