陆运选项不全时改为复制填空清单,方便销售按编号回填。
停发填写样例图,同群只跟当前工单,重量体积统一为 KG/CBM。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+21
-3
@@ -814,8 +814,8 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
out.put("container_type_qty", first(src, "箱型箱量", "container_type_qty"));
|
||||
out.put("pieces", first(src, "件数", "pieces"));
|
||||
// 销售常写「重量」,合同键是毛重;两边都认
|
||||
out.put("weight_kg", first(src, "毛重", "重量", "weight_kg"));
|
||||
out.put("volume_cbm", first(src, "体积", "volume_cbm", "volume"));
|
||||
out.put("weight_kg", first(src, "毛重", "重量", "重量(KG)", "weight_kg"));
|
||||
out.put("volume_cbm", first(src, "体积", "体积(CBM)", "volume_cbm", "volume"));
|
||||
// 去掉单位后缀,避免模板「KG/CBM」列旁再写一遍单位
|
||||
String w = out.getString("weight_kg");
|
||||
if (StringUtils.isNotBlank(w)) {
|
||||
@@ -825,7 +825,9 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
if (StringUtils.isNotBlank(vol)) {
|
||||
out.put("volume_cbm", vol.replaceAll("(?i)\\s*(CBM|立方米|方)\\s*$", "").trim());
|
||||
}
|
||||
out.put("quote_date", first(src, "报价日期", "quote_date", "询价日期"));
|
||||
// 询价/走货/发货/出货/提货的日期或时间,都是报价日期
|
||||
out.put("quote_date", first(src, "报价日期", "quote_date", "询价日期", "走货日期", "询价时间",
|
||||
"走货时间", "发货时间", "发货日期", "出货日期", "出货时间", "提货时间", "提货日期"));
|
||||
out.put("trade_terms", first(src, "贸易条款", "trade_terms"));
|
||||
out.put("运输分类", first(src, "运输分类"));
|
||||
out.put("cargo_ready_date", first(src, "货好时间", "cargo_ready_date"));
|
||||
@@ -833,6 +835,16 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
String oil = first(src, "是否含油", "has_oil");
|
||||
String elec = first(src, "是否含电", "has_battery");
|
||||
String mag = first(src, "是否含磁", "has_magnet");
|
||||
// 空运品名正好是普货:报价单含电、含磁空着或只写横杠时默认否。海运陆运不套这条。
|
||||
String cargoName = StringUtils.trimToEmpty(first(src, "品名", "货物品名", "commodity"));
|
||||
if (looksAirInquiry(src, q, businessLine) && "普货".equals(cargoName)) {
|
||||
if (collabBlank(elec)) {
|
||||
elec = "否";
|
||||
}
|
||||
if (collabBlank(mag)) {
|
||||
mag = "否";
|
||||
}
|
||||
}
|
||||
String oem = first(src, "oil_elec_mag", "是否含油,电,磁");
|
||||
if (StringUtils.isBlank(oem) && (StringUtils.isNotBlank(oil) || StringUtils.isNotBlank(elec) || StringUtils.isNotBlank(mag))) {
|
||||
oem = "油:" + (StringUtils.isNotBlank(oil) ? oil : "-")
|
||||
@@ -1059,6 +1071,12 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
return "";
|
||||
}
|
||||
|
||||
/** 协同项没写,或只写了横杠。横杠是空占位,不能当成已经填过。 */
|
||||
private static boolean collabBlank(String raw) {
|
||||
String val = StringUtils.trimToEmpty(raw);
|
||||
return val.isEmpty() || "-".equals(val) || "—".equals(val) || "-".equals(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 空运销售面没有时效:出单不把 eta / transit_* 填进模板。
|
||||
* 业务线 AIR / 运输方式含空运 / 报价带 airOptions 都算空运。
|
||||
|
||||
+88
-19
@@ -822,11 +822,11 @@ public class InquiryWorkOrderServiceImpl extends ServiceImpl<InquiryTicketMapper
|
||||
out.put("ok", true);
|
||||
out.put("status", status);
|
||||
out.put("business_line", StringUtils.defaultString(ticket.getBusinessLine()));
|
||||
String salesWecomId = StringUtils.defaultString(ticket.getSalesWecomId());
|
||||
out.put("salesWecomId", salesWecomId);
|
||||
out.put("sales_wecom_id", salesWecomId);
|
||||
String ticketSalesWecomId = StringUtils.defaultString(ticket.getSalesWecomId());
|
||||
out.put("salesWecomId", ticketSalesWecomId);
|
||||
out.put("sales_wecom_id", ticketSalesWecomId);
|
||||
String salesName = InquiryTicketDisplaySupport.resolveSalesDisplayName(
|
||||
ticket.getSalesName(), salesWecomId, resolveStaffDisplayName(salesWecomId));
|
||||
ticket.getSalesName(), ticketSalesWecomId, resolveStaffDisplayName(ticketSalesWecomId));
|
||||
out.put("sales_name", salesName);
|
||||
out.put("salesName", salesName);
|
||||
out.put("facts", facts);
|
||||
@@ -983,7 +983,9 @@ public class InquiryWorkOrderServiceImpl extends ServiceImpl<InquiryTicketMapper
|
||||
}
|
||||
|
||||
/**
|
||||
* 记下海运协同群 chat_id 与成员。不改六态、不写报价版本。
|
||||
* 记下协同群 chat_id 与成员。不改六态、不写报价版本。
|
||||
* <p>
|
||||
* 同一群只留这一张单,陆运/海运/空运旧绑定一并摘掉。
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -1000,6 +1002,7 @@ public class InquiryWorkOrderServiceImpl extends ServiceImpl<InquiryTicketMapper
|
||||
ticket.setCollabChatId(chatId);
|
||||
ticket.setCollabMemberIds(memberIdsJson(body));
|
||||
ticket.setUpdateTime(new Date());
|
||||
unbindOtherTicketsInRoom(chatId, no);
|
||||
updateById(ticket);
|
||||
appendLog(ticket, ticket.getStatus(), ticket.getStatus(), "bind_collab_group", "agent", chatId);
|
||||
out.put("ok", true);
|
||||
@@ -1064,6 +1067,8 @@ public class InquiryWorkOrderServiceImpl extends ServiceImpl<InquiryTicketMapper
|
||||
|
||||
/**
|
||||
* 按协同群找回工单。群里补字段用。只读。
|
||||
* <p>
|
||||
* 同群若仍有多张单,取最近更新的一张,避免 LIMIT 1 无排序拿到最早的陆运旧单。
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> findByCollabChat(String chatId) {
|
||||
@@ -1075,6 +1080,7 @@ public class InquiryWorkOrderServiceImpl extends ServiceImpl<InquiryTicketMapper
|
||||
}
|
||||
InquiryTicket ticket = getOne(new LambdaQueryWrapper<InquiryTicket>()
|
||||
.eq(InquiryTicket::getCollabChatId, id)
|
||||
.orderByDesc(InquiryTicket::getUpdateTime)
|
||||
.last("LIMIT 1"), false);
|
||||
if (ticket == null) {
|
||||
return out;
|
||||
@@ -1241,7 +1247,10 @@ public class InquiryWorkOrderServiceImpl extends ServiceImpl<InquiryTicketMapper
|
||||
}
|
||||
|
||||
/**
|
||||
* 空运群当前工单:清掉同群其它空运单绑定,一单一群。不改六态。
|
||||
* 空运群当前工单:清掉同群其它工单绑定,一单一群。不改六态。
|
||||
* <p>
|
||||
* 陆运/海运旧单也要摘掉。测服 WO202609230023 激活后补协同,
|
||||
* 按群找回仍拿到同群陆运 WO202609230007,摘要字段全空。
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -1260,20 +1269,10 @@ public class InquiryWorkOrderServiceImpl extends ServiceImpl<InquiryTicketMapper
|
||||
}
|
||||
return out;
|
||||
}
|
||||
List<InquiryTicket> others = list(new LambdaQueryWrapper<InquiryTicket>()
|
||||
.eq(InquiryTicket::getCollabChatId, room)
|
||||
.ne(InquiryTicket::getWorkOrderNo, no));
|
||||
if (others != null) {
|
||||
for (InquiryTicket other : others) {
|
||||
if (!"AIR".equalsIgnoreCase(StringUtils.trimToEmpty(other.getBusinessLine()))) {
|
||||
continue;
|
||||
}
|
||||
other.setCollabChatId("");
|
||||
other.setUpdateTime(new Date());
|
||||
updateById(other);
|
||||
}
|
||||
}
|
||||
unbindOtherTicketsInRoom(room, no);
|
||||
ticket.setCollabChatId(room);
|
||||
// 品名是普货:激活时就把含电、含磁写成否,群摘要不再显示横杠。
|
||||
seedAirGeneralCargoCollab(ticket);
|
||||
ticket.setUpdateTime(new Date());
|
||||
updateById(ticket);
|
||||
appendLog(ticket, ticket.getStatus(), ticket.getStatus(), "activate_air_group", "agent", room);
|
||||
@@ -1288,9 +1287,79 @@ public class InquiryWorkOrderServiceImpl extends ServiceImpl<InquiryTicketMapper
|
||||
out.put("facts", parseFactsJson(ticket.getFactsJson()));
|
||||
out.put("quote", latestQuotePayload(no));
|
||||
out.put("collab_chat_id", room);
|
||||
out.put("collab_facts", parseFactsJson(ticket.getCollabFactsJson()));
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同一协同群只留 keep 这一张单。陆运、海运、空运旧绑定都清。
|
||||
*/
|
||||
private void unbindOtherTicketsInRoom(String room, String keepWorkOrderNo) {
|
||||
String chat = StringUtils.trimToEmpty(room);
|
||||
String keep = StringUtils.trimToEmpty(keepWorkOrderNo);
|
||||
if (StringUtils.isBlank(chat)) {
|
||||
return;
|
||||
}
|
||||
List<InquiryTicket> others = list(new LambdaQueryWrapper<InquiryTicket>()
|
||||
.eq(InquiryTicket::getCollabChatId, chat)
|
||||
.ne(InquiryTicket::getWorkOrderNo, keep));
|
||||
if (others == null) {
|
||||
return;
|
||||
}
|
||||
Date now = new Date();
|
||||
for (InquiryTicket other : others) {
|
||||
other.setCollabChatId("");
|
||||
other.setUpdateTime(now);
|
||||
updateById(other);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 空运品名正好是「普货」时,含电、含磁还空着就写成否。
|
||||
* <p>
|
||||
* 已经写了「是」或「否」的不改。摘要里的横杠当成没写。
|
||||
* 只改传入工单上的协同 JSON,由调用方落库。海运、陆运不进这里。
|
||||
*/
|
||||
private void seedAirGeneralCargoCollab(InquiryTicket ticket) {
|
||||
if (ticket == null || !"AIR".equalsIgnoreCase(StringUtils.trimToEmpty(ticket.getBusinessLine()))) {
|
||||
return;
|
||||
}
|
||||
String name = StringUtils.trimToEmpty(ticket.getGoodsName());
|
||||
if (!"普货".equals(name)) {
|
||||
Map<String, String> facts = parseFactsJson(ticket.getFactsJson());
|
||||
name = StringUtils.trimToEmpty(facts.get("品名"));
|
||||
if (!"普货".equals(name)) {
|
||||
name = StringUtils.trimToEmpty(facts.get("货物品名"));
|
||||
}
|
||||
}
|
||||
if (!"普货".equals(name)) {
|
||||
return;
|
||||
}
|
||||
JSONObject collab = new JSONObject(true);
|
||||
if (StringUtils.isNotBlank(ticket.getCollabFactsJson())) {
|
||||
JSONObject parsed = JSON.parseObject(ticket.getCollabFactsJson());
|
||||
if (parsed != null) {
|
||||
collab.putAll(parsed);
|
||||
}
|
||||
}
|
||||
boolean changed = false;
|
||||
for (String key : new String[]{"是否含电", "是否含磁"}) {
|
||||
if (collabBlank(collab.getString(key))) {
|
||||
collab.put(key, "否");
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
ticket.setCollabFactsJson(JSON.toJSONString(collab));
|
||||
}
|
||||
}
|
||||
|
||||
/** 协同项没写,或只写了横杠。 */
|
||||
private static boolean collabBlank(String raw) {
|
||||
String val = StringUtils.trimToEmpty(raw);
|
||||
return val.isEmpty() || "-".equals(val) || "—".equals(val) || "-".equals(val);
|
||||
}
|
||||
|
||||
private static Map<String, String> parseFactsJson(String raw) {
|
||||
Map<String, String> facts = new LinkedHashMap<>();
|
||||
if (StringUtils.isBlank(raw)) {
|
||||
|
||||
+8
-8
@@ -45,20 +45,20 @@ public final class InquiryTicketDisplaySupport {
|
||||
List<String> quantities = new ArrayList<>();
|
||||
if ("AIR".equals(businessLine) || containsChinese(lineLabel, "空运")) {
|
||||
addLabel(quantities, "件数", ticket.getPieces());
|
||||
addUnit(quantities, "毛重", ticket.getWeightKg(), "kg");
|
||||
addUnit(quantities, "体积", ticket.getVolumeCbm(), "CBM");
|
||||
addUnit(quantities, "重量(KG)", ticket.getWeightKg(), "kg");
|
||||
addUnit(quantities, "体积(CBM)", ticket.getVolumeCbm(), "CBM");
|
||||
} else if ("SEA".equals(businessLine) || containsChinese(lineLabel, "海运")) {
|
||||
addLabel(quantities, "件数", ticket.getPieces());
|
||||
addUnit(quantities, "重量", ticket.getWeightKg(), "kg");
|
||||
addUnit(quantities, "体积", ticket.getVolumeCbm(), "CBM");
|
||||
addUnit(quantities, "重量(KG)", ticket.getWeightKg(), "kg");
|
||||
addUnit(quantities, "体积(CBM)", ticket.getVolumeCbm(), "CBM");
|
||||
} else if ("LAND".equals(businessLine) || containsChinese(lineLabel, "陆运")) {
|
||||
addUnit(quantities, "体积", ticket.getVolumeCbm(), "CBM");
|
||||
addUnit(quantities, "重量", ticket.getWeightKg(), "kg");
|
||||
addUnit(quantities, "体积(CBM)", ticket.getVolumeCbm(), "CBM");
|
||||
addUnit(quantities, "重量(KG)", ticket.getWeightKg(), "kg");
|
||||
addLabel(quantities, "件数", ticket.getPieces());
|
||||
} else {
|
||||
addLabel(quantities, "件数", ticket.getPieces());
|
||||
addUnit(quantities, "重量", ticket.getWeightKg(), "kg");
|
||||
addUnit(quantities, "体积", ticket.getVolumeCbm(), "CBM");
|
||||
addUnit(quantities, "重量(KG)", ticket.getWeightKg(), "kg");
|
||||
addUnit(quantities, "体积(CBM)", ticket.getVolumeCbm(), "CBM");
|
||||
}
|
||||
addLine(lines, join(quantities, " · "));
|
||||
|
||||
|
||||
+14
-2
@@ -41,6 +41,16 @@ public final class QuoteTemplateExcelSupport {
|
||||
static {
|
||||
FILL_BY_LABEL.put("询价日期", "quote_date");
|
||||
FILL_BY_LABEL.put("报价日期", "quote_date");
|
||||
// 销售口中的报价日期。货好时间、提货地址不在这里。
|
||||
FILL_BY_LABEL.put("走货日期", "quote_date");
|
||||
FILL_BY_LABEL.put("询价时间", "quote_date");
|
||||
FILL_BY_LABEL.put("走货时间", "quote_date");
|
||||
FILL_BY_LABEL.put("发货时间", "quote_date");
|
||||
FILL_BY_LABEL.put("发货日期", "quote_date");
|
||||
FILL_BY_LABEL.put("出货日期", "quote_date");
|
||||
FILL_BY_LABEL.put("出货时间", "quote_date");
|
||||
FILL_BY_LABEL.put("提货时间", "quote_date");
|
||||
FILL_BY_LABEL.put("提货日期", "quote_date");
|
||||
FILL_BY_LABEL.put("品名", "commodity");
|
||||
FILL_BY_LABEL.put("货物数量", "quantity");
|
||||
FILL_BY_LABEL.put("整柜或拼柜", "fcl_or_lcl");
|
||||
@@ -70,7 +80,9 @@ public final class QuoteTemplateExcelSupport {
|
||||
FILL_BY_LABEL.put("目的地", "destination");
|
||||
FILL_BY_LABEL.put("件数", "pieces");
|
||||
FILL_BY_LABEL.put("重量", "weight_kg");
|
||||
FILL_BY_LABEL.put("重量(KG)", "weight_kg");
|
||||
FILL_BY_LABEL.put("体积", "volume_cbm");
|
||||
FILL_BY_LABEL.put("体积(CBM)", "volume_cbm");
|
||||
FILL_BY_LABEL.put("始发地", "origin");
|
||||
FILL_BY_LABEL.put("始发站", "origin");
|
||||
FILL_BY_LABEL.put("目的地", "destination");
|
||||
@@ -530,8 +542,8 @@ public final class QuoteTemplateExcelSupport {
|
||||
m.put("trade_terms", "贸易条款");
|
||||
m.put("cargo_ready_date", "货好时间");
|
||||
m.put("pieces", "件数");
|
||||
m.put("weight_kg", "重量");
|
||||
m.put("volume_cbm", "体积");
|
||||
m.put("weight_kg", "重量(KG)");
|
||||
m.put("volume_cbm", "体积(CBM)");
|
||||
m.put("total_amount", "报价合计");
|
||||
return m;
|
||||
}
|
||||
|
||||
+13
-2
@@ -96,8 +96,8 @@
|
||||
{ "code": "trade_terms", "label": "贸易条款", "requiredHint": false },
|
||||
{ "code": "cargo_ready_date", "label": "货好时间", "requiredHint": false },
|
||||
{ "code": "pieces", "label": "件数", "requiredHint": false },
|
||||
{ "code": "weight_kg", "label": "重量", "requiredHint": false },
|
||||
{ "code": "volume_cbm", "label": "体积", "requiredHint": false },
|
||||
{ "code": "weight_kg", "label": "重量(KG)", "requiredHint": false },
|
||||
{ "code": "volume_cbm", "label": "体积(CBM)", "requiredHint": false },
|
||||
{ "code": "chargeable_weight_kg", "label": "计费重", "requiredHint": false },
|
||||
{ "code": "delivery_address", "label": "派送地址", "requiredHint": false },
|
||||
{ "code": "pickup_address", "label": "提货地址", "requiredHint": false },
|
||||
@@ -131,6 +131,15 @@
|
||||
"报价编号": "quote_no",
|
||||
"报价日期": "quote_date",
|
||||
"询价日期": "quote_date",
|
||||
"走货日期": "quote_date",
|
||||
"询价时间": "quote_date",
|
||||
"走货时间": "quote_date",
|
||||
"发货时间": "quote_date",
|
||||
"发货日期": "quote_date",
|
||||
"出货日期": "quote_date",
|
||||
"出货时间": "quote_date",
|
||||
"提货时间": "quote_date",
|
||||
"提货日期": "quote_date",
|
||||
"有效期至": "valid_until",
|
||||
"报价有效期": "valid_until",
|
||||
"币别": "currency",
|
||||
@@ -152,8 +161,10 @@
|
||||
"件数": "pieces",
|
||||
"件 数": "pieces",
|
||||
"重量": "weight_kg",
|
||||
"重量(KG)": "weight_kg",
|
||||
"重 量": "weight_kg",
|
||||
"体积": "volume_cbm",
|
||||
"体积(CBM)": "volume_cbm",
|
||||
"体 积": "volume_cbm",
|
||||
"费用总计": "total_amount",
|
||||
"报价合计": "total_amount",
|
||||
|
||||
+2
-2
@@ -30,12 +30,12 @@ public class InquiryTicketDisplaySupportTest {
|
||||
|
||||
List<String> lines = InquiryTicketDisplaySupport.requirementSummaryLines(ticket);
|
||||
assertEquals("空运 · ZUH → CRK", lines.get(0));
|
||||
assertEquals("件数 10 · 毛重 1kg · 体积 8CBM", lines.get(1));
|
||||
assertEquals("件数 10 · 重量(KG) 1kg · 体积(CBM) 8CBM", lines.get(1));
|
||||
assertEquals("品名 茶叶", lines.get(2));
|
||||
|
||||
InquiryTicketDisplaySupport.applyRequirementSummary(ticket);
|
||||
assertEquals("空运 · ZUH → CRK", ticket.getDemandTitle());
|
||||
assertEquals("件数 10 · 毛重 1kg · 体积 8CBM\n品名 茶叶", ticket.getDemandDetail());
|
||||
assertEquals("件数 10 · 重量(KG) 1kg · 体积(CBM) 8CBM\n品名 茶叶", ticket.getDemandDetail());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+16
@@ -112,6 +112,22 @@ public class QuoteTemplateFillTest {
|
||||
assertTrue(StringUtils.isBlank(out.getString("时效")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildFillFactsAirGeneralCargoDefaultsBatteryAndMagnet() {
|
||||
Map<String, Object> facts = new HashMap<>();
|
||||
facts.put("品名", "普货");
|
||||
facts.put("运输方式", "空运");
|
||||
JSONObject out = org.jeecg.modules.inquiry.service.impl.InquiryQuoteTemplateServiceImpl
|
||||
.buildFillFacts("WO202609230017", facts, new HashMap<>(), "WuJiLin", "AIR");
|
||||
assertEquals("否", out.getString("是否含电"));
|
||||
assertEquals("否", out.getString("是否含磁"));
|
||||
facts.put("是否含电", "是");
|
||||
JSONObject kept = org.jeecg.modules.inquiry.service.impl.InquiryQuoteTemplateServiceImpl
|
||||
.buildFillFacts("WO202609230017", facts, new HashMap<>(), "WuJiLin", "AIR");
|
||||
assertEquals("是", kept.getString("是否含电"));
|
||||
assertEquals("否", kept.getString("是否含磁"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeFeeDetailColumnsOntoNamedFeeRow() throws Exception {
|
||||
Path src = Files.createTempFile("quote-fee-src", ".xlsx");
|
||||
|
||||
Reference in New Issue
Block a user