智能体按 reply_code 读后台回复语;陆运整车报价单顶栏色与填表更稳,工单页展示同步收紧。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+16
@@ -44,12 +44,28 @@ public class InquiryAgentController {
|
||||
@Autowired
|
||||
private org.jeecg.modules.inquiry.tms.InquiryTmsCabinService cabinService;
|
||||
|
||||
@Autowired
|
||||
private org.jeecg.modules.inquiry.service.IInquiryBotReplyService botReplyService;
|
||||
|
||||
@Value("${inquiry.agent.callback-token:${AGENT_CALLBACK_TOKEN:}}")
|
||||
private String agentToken;
|
||||
|
||||
@Value("${inquiry.agent.biz-token:${BIZ_API_TOKEN:}}")
|
||||
private String bizToken;
|
||||
|
||||
/**
|
||||
* 智能体只读回复语。停用或不在列表里的编码由智能体回退默认句。
|
||||
* 鉴权同其它 /inquiry/agent/** ,禁止后台菜单权限码。
|
||||
*/
|
||||
@ApiOperation("智能体-已启用回复语")
|
||||
@GetMapping("/botReply/listEnabled")
|
||||
public Result<java.util.List<java.util.Map<String, Object>>> botReplyListEnabled(HttpServletRequest http) {
|
||||
if (!authed(http)) {
|
||||
return Result.error(401, "unauthorized");
|
||||
}
|
||||
return Result.OK(botReplyService.listEnabledForAgent());
|
||||
}
|
||||
|
||||
@ApiOperation("智能体-回写模板 AI 映射结果")
|
||||
@PutMapping("/quoteTemplate/parseResult")
|
||||
public Result<String> quoteTemplateParseResult(@RequestBody Map<String, Object> body,
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ import java.util.List;
|
||||
/**
|
||||
* 机器人回复语 API。
|
||||
* 支持按分类拉取全部条目(后台九分类页)与分页查询。
|
||||
* 调用编码 replyCode 给智能体日后按码取文案;本期只维护、不向智能体下发。
|
||||
* 调用编码 replyCode 供智能体 GET /inquiry/agent/botReply/listEnabled 按码取文案。
|
||||
*/
|
||||
@Slf4j
|
||||
@Api(tags = "询价-机器人回复语")
|
||||
|
||||
+1
-1
@@ -123,7 +123,7 @@ public class InquiryTicket implements Serializable {
|
||||
private List<InquiryPriceLine> saleLines;
|
||||
|
||||
/**
|
||||
* 利润点逐行。不入库。没调过价为空。最后一行是整单利润点。
|
||||
* 利润点逐行。不入库。没调过价每项都是 0%。最后一行是整单利润点。
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
@ApiModelProperty("利润点逐行(展示)")
|
||||
|
||||
+6
@@ -22,4 +22,10 @@ public interface IInquiryBotReplyService extends IService<InquiryBotReply> {
|
||||
* 编辑正文/启停。已有调用编码不可被请求覆盖。
|
||||
*/
|
||||
void updateReply(InquiryBotReply body);
|
||||
|
||||
/**
|
||||
* 智能体只读:已启用且已有调用编码的正文。
|
||||
* 停用项不返回,智能体回退默认句。
|
||||
*/
|
||||
java.util.List<java.util.Map<String, Object>> listEnabledForAgent();
|
||||
}
|
||||
|
||||
+35
@@ -9,7 +9,13 @@ import org.jeecg.modules.inquiry.service.IInquiryBotReplyService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
@@ -90,6 +96,35 @@ public class InquiryBotReplyServiceImpl extends ServiceImpl<InquiryBotReplyMappe
|
||||
body.setReplyCode(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> listEnabledForAgent() {
|
||||
QueryWrapper<InquiryBotReply> qw = new QueryWrapper<>();
|
||||
qw.eq("enabled", 1);
|
||||
qw.isNotNull("reply_code");
|
||||
qw.ne("reply_code", "");
|
||||
qw.orderByAsc("sort_no").orderByAsc("id");
|
||||
Page<InquiryBotReply> page = new Page<>(1, 2000);
|
||||
page.setSearchCount(false);
|
||||
page.setMaxLimit(2000L);
|
||||
List<InquiryBotReply> rows = page(page, qw).getRecords();
|
||||
List<Map<String, Object>> out = new ArrayList<>();
|
||||
if (rows == null) {
|
||||
return out;
|
||||
}
|
||||
for (InquiryBotReply row : rows) {
|
||||
if (row == null || StringUtils.isBlank(row.getReplyCode())) {
|
||||
continue;
|
||||
}
|
||||
Map<String, Object> item = new LinkedHashMap<>();
|
||||
item.put("replyCode", row.getReplyCode());
|
||||
item.put("replyText", row.getReplyText());
|
||||
item.put("triggerScene", row.getTriggerScene());
|
||||
item.put("categoryCode", row.getCategoryCode());
|
||||
out.add(item);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private void assertCodeUnique(String replyCode, String excludeId) {
|
||||
if (StringUtils.isBlank(replyCode)) {
|
||||
return;
|
||||
|
||||
+17
-1
@@ -686,7 +686,7 @@ public class InquiryDashboardServiceImpl implements IInquiryDashboardService {
|
||||
return;
|
||||
}
|
||||
String username = StringUtils.trimToEmpty(login.getUsername());
|
||||
String wecom = StringUtils.trimToEmpty(login.getWorkNo());
|
||||
String wecom = dashboardWecomId(login);
|
||||
qw.and(w -> {
|
||||
boolean any = false;
|
||||
if (StringUtils.isNotBlank(wecom)) {
|
||||
@@ -734,4 +734,20 @@ public class InquiryDashboardServiceImpl implements IInquiryDashboardService {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 看板「只看自己」用的企微工号。
|
||||
* 不用直接调 getWorkNo:测服打包时基座 LoginUser 可能还没有这个方法。
|
||||
*/
|
||||
private static String dashboardWecomId(LoginUser login) {
|
||||
if (login == null) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
Object v = login.getClass().getMethod("getWorkNo").invoke(login);
|
||||
return v == null ? "" : StringUtils.trimToEmpty(String.valueOf(v));
|
||||
} catch (ReflectiveOperationException ignored) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-4
@@ -19,7 +19,9 @@ import java.util.List;
|
||||
* <p>
|
||||
* 职责:从已保存的报价明细铺开展示。不改调价算法,不把各行金额加总冒充费用合计。
|
||||
* 海运/陆运:成本是调价前生效价(手工同名盖 TMS,多出来的补上);销售是最新调价。
|
||||
* 空运:成本整列空;销售是当前生效价;利润点只在调过价之后,对调价前销售价。
|
||||
* 空运:成本整列空;销售是当前生效价。
|
||||
* 未调价:各项利润点与费用合计都是 0%,禁止拿金额去填利润点列。
|
||||
* 已调价:利润点对调价前同名行。
|
||||
* 副作用:改 ticket 的三列逐行;手工盖价算出新的官方总价时,同时改成本价和利润点字符串。不写库。
|
||||
* 调用:{@link InquiryTicketPriceSupport#apply} 末尾。列表 enrich 与保存报价后的 update 都走那里。
|
||||
*/
|
||||
@@ -126,7 +128,7 @@ public final class InquiryQuoteLineSupport {
|
||||
profitTotal = StringUtils.defaultString(InquiryTicketPriceSupport.profitText(saleTotal, baseTotal));
|
||||
ticket.setProfitRate(profitTotal);
|
||||
} else if (!adjusted) {
|
||||
ticket.setProfitRate("");
|
||||
ticket.setProfitRate("0%");
|
||||
}
|
||||
|
||||
if (air) {
|
||||
@@ -134,13 +136,13 @@ public final class InquiryQuoteLineSupport {
|
||||
ticket.setSaleLines(moneyLines(saleFees, saleTotal));
|
||||
ticket.setProfitLines(adjusted && StringUtils.isNotBlank(profitTotal)
|
||||
? profitAgainst(baselineFees, saleFees, profitTotal)
|
||||
: Collections.<InquiryPriceLine>emptyList());
|
||||
: zeroPercentLines(saleFees));
|
||||
return;
|
||||
}
|
||||
if (!adjusted) {
|
||||
ticket.setCostLines(moneyLines(costFees, costTotal));
|
||||
ticket.setSaleLines(Collections.<InquiryPriceLine>emptyList());
|
||||
ticket.setProfitLines(Collections.<InquiryPriceLine>emptyList());
|
||||
ticket.setProfitLines(zeroPercentLines(costFees));
|
||||
return;
|
||||
}
|
||||
List<Aligned> rows = align(costFees, saleFees);
|
||||
@@ -216,6 +218,21 @@ public final class InquiryQuoteLineSupport {
|
||||
return formatMoney(base.add(delta), totalCcy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 未调价:每一项费用和费用合计都写 0%。
|
||||
* 列表/详情都走这里,避免利润点列空白或误把金额填进去。
|
||||
*/
|
||||
private static List<InquiryPriceLine> zeroPercentLines(List<FeeRow> fees) {
|
||||
List<InquiryPriceLine> lines = new ArrayList<InquiryPriceLine>();
|
||||
if (fees != null) {
|
||||
for (FeeRow fee : fees) {
|
||||
lines.add(line(fee.name, "0%"));
|
||||
}
|
||||
}
|
||||
lines.add(line(TOTAL_NAME, "0%"));
|
||||
return lines;
|
||||
}
|
||||
|
||||
private static List<InquiryPriceLine> moneyLines(List<FeeRow> fees, String totalText) {
|
||||
List<InquiryPriceLine> lines = new ArrayList<InquiryPriceLine>();
|
||||
if (fees != null) {
|
||||
|
||||
+4
-3
@@ -14,7 +14,8 @@ import java.util.regex.Pattern;
|
||||
* 后台成本价 / 销售价 / 利润点:按业务线从报价记录重算。
|
||||
* <p>
|
||||
* 海运/陆运:TMS、产品报价是成本;销售调价后才有销售价,利润点=(销售-成本)/成本。
|
||||
* 空运:成本价始终空;TMS/航线/调价都是销售侧;利润点只在销售调过价后算。
|
||||
* 空运:成本价始终空;TMS/航线/调价都是销售侧。
|
||||
* 未调价时利润点固定 0%(含各项),调过价才按差额算。
|
||||
* 副作用:只改传入 ticket 的展示字段(三个总价字符串和三列逐行),不写库。调用方决定是否 updateById。
|
||||
*/
|
||||
public final class InquiryTicketPriceSupport {
|
||||
@@ -63,7 +64,7 @@ public final class InquiryTicketPriceSupport {
|
||||
ticket.setProfitRate(profitText(adjustPrice, baseline));
|
||||
} else {
|
||||
ticket.setSalePrice(StringUtils.isNotBlank(manualPrice) ? manualPrice : tmsPrice);
|
||||
ticket.setProfitRate("");
|
||||
ticket.setProfitRate("0%");
|
||||
}
|
||||
} else {
|
||||
String cost = StringUtils.isNotBlank(manualPrice) ? manualPrice : tmsPrice;
|
||||
@@ -73,7 +74,7 @@ public final class InquiryTicketPriceSupport {
|
||||
ticket.setProfitRate(profitText(adjustPrice, cost));
|
||||
} else {
|
||||
ticket.setSalePrice("");
|
||||
ticket.setProfitRate("");
|
||||
ticket.setProfitRate("0%");
|
||||
}
|
||||
}
|
||||
// 三个总价定完后,按同一批报价拆成逐行。有手工明细盖 TMS 时,这里会改写成本价和利润点。
|
||||
|
||||
+1148
-40
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -689,7 +689,7 @@ public class InquiryTmsQueryService {
|
||||
|
||||
/**
|
||||
* 把本次 TMS 报价写回工单主表,供后台列表/详情展示。
|
||||
* 海运/陆运进成本价,空运进销售价;利润点必须等销售调价后才算。
|
||||
* 海运/陆运进成本价,空运进销售价;未调价利润点为 0%,调过价才按差额算。
|
||||
*/
|
||||
static void applyQuoteToTicket(InquiryTicket ticket, Map<String, String> quote) {
|
||||
if (ticket == null || quote == null) {
|
||||
|
||||
+16
-3
@@ -28,13 +28,16 @@ public class InquiryQuoteLineSupportTest {
|
||||
+ "]}")));
|
||||
assertEquals("CNY 1200.00", ticket.getCostPrice());
|
||||
assertEquals("", ticket.getSalePrice());
|
||||
assertEquals("", ticket.getProfitRate());
|
||||
assertEquals("0%", ticket.getProfitRate());
|
||||
assertLines(ticket.getCostLines(),
|
||||
"运输费", "CNY 1000",
|
||||
"卸货费", "CNY 200",
|
||||
"费用合计", "CNY 1200.00");
|
||||
assertTrue(ticket.getSaleLines() == null || ticket.getSaleLines().isEmpty());
|
||||
assertTrue(ticket.getProfitLines() == null || ticket.getProfitLines().isEmpty());
|
||||
assertLines(ticket.getProfitLines(),
|
||||
"运输费", "0%",
|
||||
"卸货费", "0%",
|
||||
"费用合计", "0%");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -113,7 +116,12 @@ public class InquiryQuoteLineSupportTest {
|
||||
assertTrue(before.getCostLines() == null || before.getCostLines().isEmpty());
|
||||
assertEquals("", before.getCostPrice());
|
||||
assertEquals("CNY 2000.00", before.getSalePrice());
|
||||
assertTrue(before.getProfitLines() == null || before.getProfitLines().isEmpty());
|
||||
assertEquals("0%", before.getProfitRate());
|
||||
assertLines(before.getProfitLines(),
|
||||
"空运费", "0%",
|
||||
"增点费", "0%",
|
||||
"拆板费", "0%",
|
||||
"费用合计", "0%");
|
||||
assertLines(before.getSaleLines(),
|
||||
"空运费", "CNY 1800",
|
||||
"增点费", "CNY 100",
|
||||
@@ -189,6 +197,11 @@ public class InquiryQuoteLineSupportTest {
|
||||
"费用合计", "CNY 1320.00");
|
||||
assertEquals("CNY 1320.00", ticket.getCostPrice());
|
||||
assertTrue(ticket.getSaleLines() == null || ticket.getSaleLines().isEmpty());
|
||||
assertLines(ticket.getProfitLines(),
|
||||
"运输费", "0%",
|
||||
"卸货费", "0%",
|
||||
"配送费", "0%",
|
||||
"费用合计", "0%");
|
||||
}
|
||||
|
||||
private static void assertLines(List<InquiryPriceLine> lines, String... nameText) {
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ public class InquiryTicketPriceSupportTest {
|
||||
InquiryTicketPriceSupport.apply(ticket, Collections.singletonList(quote("TMS", "TMS标准报价", "CNY 121.00")));
|
||||
assertEquals("CNY 121.00", ticket.getCostPrice());
|
||||
assertEquals("", ticket.getSalePrice());
|
||||
assertEquals("", ticket.getProfitRate());
|
||||
assertEquals("0%", ticket.getProfitRate());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -53,7 +53,7 @@ public class InquiryTicketPriceSupportTest {
|
||||
InquiryTicketPriceSupport.apply(ticket, Collections.singletonList(quote("TMS", "TMS标准报价", "USD 1280")));
|
||||
assertEquals("", ticket.getCostPrice());
|
||||
assertEquals("USD 1280", ticket.getSalePrice());
|
||||
assertEquals("", ticket.getProfitRate());
|
||||
assertEquals("0%", ticket.getProfitRate());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+248
@@ -1543,6 +1543,9 @@ public class QuoteTemplateFillTest {
|
||||
matrix.getCell(15).setCellStyle(matrixBlue);
|
||||
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(7, 8, 2, 2));
|
||||
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(7, 8, 9, 9));
|
||||
org.apache.poi.ss.usermodel.Row sub = row(sheet, 8);
|
||||
sub.createCell(3).setCellValue("最低收费");
|
||||
sub.createCell(4).setCellValue("元/KG");
|
||||
org.apache.poi.ss.usermodel.Font red = wb.createFont();
|
||||
red.setColor(org.apache.poi.ss.usermodel.IndexedColors.RED.getIndex());
|
||||
org.apache.poi.ss.usermodel.CellStyle redOnSky = wb.createCellStyle();
|
||||
@@ -1553,6 +1556,14 @@ public class QuoteTemplateFillTest {
|
||||
currencyHead.setCellStyle(redOnSky);
|
||||
matrix.getCell(10).setCellStyle(redOnSky);
|
||||
org.apache.poi.ss.usermodel.Row data = row(sheet, 9);
|
||||
org.apache.poi.ss.usermodel.Font blackData = wb.createFont();
|
||||
blackData.setFontName("宋体");
|
||||
blackData.setFontHeightInPoints((short) 10);
|
||||
blackData.setColor(org.apache.poi.ss.usermodel.IndexedColors.BLACK.getIndex());
|
||||
org.apache.poi.ss.usermodel.CellStyle priceStyle = wb.createCellStyle();
|
||||
priceStyle.setFont(blackData);
|
||||
data.createCell(4).setCellValue(12);
|
||||
data.getCell(4).setCellStyle(priceStyle);
|
||||
org.apache.poi.ss.usermodel.Cell dest = data.createCell(2);
|
||||
dest.setCellStyle(redOnSky);
|
||||
org.apache.poi.ss.usermodel.Row delivery = row(sheet, 18);
|
||||
@@ -1610,6 +1621,9 @@ public class QuoteTemplateFillTest {
|
||||
assertEquals(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER,
|
||||
sheet.getRow(9).getCell(2).getCellStyle().getVerticalAlignment());
|
||||
assertFalse(QuoteTemplateExcelSupport.fontIsRed(sheet.getRow(9).getCell(2)));
|
||||
assertFalse(sheet.getRow(9).getCell(2).getCellStyle().getShrinkToFit());
|
||||
assertEquals(QuoteTemplateExcelSupport.fontHeight(sheet.getRow(9).getCell(4)),
|
||||
QuoteTemplateExcelSupport.fontHeight(sheet.getRow(9).getCell(2)));
|
||||
assertEquals("始发地", read(sheet, 7, 1));
|
||||
assertEquals("币种", read(sheet, 7, 9));
|
||||
byte[] originFill = QuoteTemplateExcelSupport.readFillRgb(sheet.getRow(7).getCell(1));
|
||||
@@ -1678,6 +1692,133 @@ public class QuoteTemplateFillTest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跨境整车:列头是「起始地」,城市必须写在下一行白格,不能盖掉列名。
|
||||
*/
|
||||
@Test
|
||||
public void landCrossBorderFtlKeepsOriginHeader() throws Exception {
|
||||
Path src = Files.createTempFile("quote-land-cb-ftl-src", ".xlsx");
|
||||
Path out = Files.createTempFile("quote-land-cb-ftl-out", ".xlsx");
|
||||
try (Workbook wb = new XSSFWorkbook()) {
|
||||
Sheet sheet = wb.createSheet("跨境整车");
|
||||
org.apache.poi.ss.usermodel.Row top = row(sheet, 6);
|
||||
top.createCell(1).setCellValue("路线");
|
||||
top.createCell(3).setCellValue("车型");
|
||||
org.apache.poi.ss.usermodel.Row head = row(sheet, 7);
|
||||
head.createCell(1).setCellValue("起始地");
|
||||
head.createCell(2).setCellValue("目的地");
|
||||
head.createCell(3).setCellValue("3T");
|
||||
head.createCell(4).setCellValue("5T");
|
||||
head.createCell(5).setCellValue("8T");
|
||||
try (java.io.OutputStream os = Files.newOutputStream(src)) {
|
||||
wb.write(os);
|
||||
}
|
||||
}
|
||||
JSONObject mapping = new JSONObject(true);
|
||||
mapping.put("sheet", "跨境整车");
|
||||
JSONObject fields = new JSONObject(true);
|
||||
fields.put("origin", "跨境整车!B8");
|
||||
fields.put("destination", "跨境整车!C8");
|
||||
mapping.put("fields", fields);
|
||||
JSONObject facts = new JSONObject(true);
|
||||
facts.put("origin", "郑州");
|
||||
facts.put("destination", "河内");
|
||||
QuoteTemplateExcelSupport.fillWorkbook(src, out, mapping, facts);
|
||||
try (Workbook filled = new XSSFWorkbook(Files.newInputStream(out))) {
|
||||
Sheet sheet = filled.getSheetAt(0);
|
||||
assertEquals("起始地", read(sheet, 7, 1));
|
||||
assertEquals("目的地", read(sheet, 7, 2));
|
||||
assertEquals("郑州", read(sheet, 8, 1));
|
||||
assertEquals("河内", read(sheet, 8, 2));
|
||||
} finally {
|
||||
Files.deleteIfExists(src);
|
||||
Files.deleteIfExists(out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跨境整车国内段只写境内起点,境外段只写境外终点,不能两段都写成郑州→吉隆坡。
|
||||
*/
|
||||
@Test
|
||||
public void landCrossBorderFtlSplitsDomesticAndOverseasOd() throws Exception {
|
||||
Path src = Files.createTempFile("quote-land-cb-split-src", ".xlsx");
|
||||
Path out = Files.createTempFile("quote-land-cb-split-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");
|
||||
head1.createCell(15).setCellValue("时效");
|
||||
head1.createCell(16).setCellValue("币种");
|
||||
head1.createCell(17).setCellValue("燃油附加费率");
|
||||
head1.createCell(18).setCellValue("含税合计");
|
||||
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("1.25T");
|
||||
head2.createCell(4).setCellValue("2.5T");
|
||||
head2.createCell(15).setCellValue("时效");
|
||||
head2.createCell(16).setCellValue("币种");
|
||||
head2.createCell(17).setCellValue("燃油附加费率");
|
||||
head2.createCell(18).setCellValue("含税合计");
|
||||
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", 8);
|
||||
JSONObject cols1 = new JSONObject(true);
|
||||
cols1.put("origin", "B");
|
||||
cols1.put("destination", "C");
|
||||
inland.put("columns", cols1);
|
||||
regions.add(inland);
|
||||
JSONObject overseas = new JSONObject(true);
|
||||
overseas.put("title", "境外段报价");
|
||||
overseas.put("headerRow", 27);
|
||||
overseas.put("dataStartRow", 27);
|
||||
JSONObject cols2 = new JSONObject(true);
|
||||
cols2.put("origin", "B");
|
||||
cols2.put("destination", "C");
|
||||
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("transit_days", "N+3");
|
||||
QuoteTemplateExcelSupport.fillWorkbook(src, out, mapping, facts);
|
||||
try (Workbook filled = new XSSFWorkbook(Files.newInputStream(out))) {
|
||||
Sheet sheet = filled.getSheetAt(0);
|
||||
assertEquals("起始地", read(sheet, 7, 1));
|
||||
assertEquals("郑州", read(sheet, 8, 1));
|
||||
assertEquals("", read(sheet, 8, 2));
|
||||
assertEquals("起始地", read(sheet, 26, 1));
|
||||
assertEquals("", read(sheet, 27, 1));
|
||||
assertEquals("吉隆坡", read(sheet, 27, 2));
|
||||
assertEquals("N+3", read(sheet, 8, 15));
|
||||
assertEquals("CNY", read(sheet, 8, 16));
|
||||
assertEquals("", read(sheet, 8, 17));
|
||||
assertEquals("", read(sheet, 8, 18));
|
||||
assertEquals("N+3", read(sheet, 27, 15));
|
||||
assertEquals("CNY", read(sheet, 27, 16));
|
||||
assertEquals("", read(sheet, 27, 17));
|
||||
assertEquals("", read(sheet, 27, 18));
|
||||
} finally {
|
||||
Files.deleteIfExists(src);
|
||||
Files.deleteIfExists(out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 国内整车:4.2米/3T 这类车型费用也是标签右侧,不是海运费目表。
|
||||
*/
|
||||
@@ -1967,6 +2108,113 @@ public class QuoteTemplateFillTest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 国内拼车:元/CBM、元/KG 必须跟「始发站」同一浅蓝,不能留成顶栏深蓝。
|
||||
*/
|
||||
@Test
|
||||
public void landDomesticLtlUnitHeaderRowIsKept() throws Exception {
|
||||
Path src = Files.createTempFile("quote-land-ltl-src", ".xlsx");
|
||||
Path out = Files.createTempFile("quote-land-ltl-out", ".xlsx");
|
||||
try (Workbook wb = new XSSFWorkbook()) {
|
||||
Sheet sheet = wb.createSheet("国内拼车");
|
||||
org.apache.poi.ss.usermodel.CellStyle sky = wb.createCellStyle();
|
||||
sky.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.SKY_BLUE.getIndex());
|
||||
sky.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
|
||||
org.apache.poi.ss.usermodel.CellStyle navy = wb.createCellStyle();
|
||||
navy.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.ROYAL_BLUE.getIndex());
|
||||
navy.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
|
||||
org.apache.poi.ss.usermodel.Row head = row(sheet, 7);
|
||||
head.createCell(1).setCellValue("始发站");
|
||||
head.getCell(1).setCellStyle(sky);
|
||||
head.createCell(2).setCellValue("目的地");
|
||||
head.getCell(2).setCellStyle(sky);
|
||||
head.createCell(5).setCellValue("运输费");
|
||||
head.getCell(5).setCellStyle(sky);
|
||||
org.apache.poi.ss.usermodel.Row units = row(sheet, 8);
|
||||
units.createCell(5).setCellValue("元/CBM");
|
||||
units.getCell(5).setCellStyle(navy);
|
||||
units.createCell(6).setCellValue("元/KG");
|
||||
units.getCell(6).setCellStyle(navy);
|
||||
row(sheet, 9);
|
||||
boolean[] keep = QuoteTemplateExcelSupport.markLandHeaderRows(sheet);
|
||||
assertTrue(keep[7]);
|
||||
try (java.io.OutputStream os = Files.newOutputStream(src)) {
|
||||
wb.write(os);
|
||||
}
|
||||
}
|
||||
JSONObject facts = new JSONObject(true);
|
||||
facts.put("origin", "上海");
|
||||
facts.put("destination", "江苏南京市");
|
||||
facts.put("price_per_cbm", "100");
|
||||
QuoteTemplateExcelSupport.fillWorkbook(src, out, new JSONObject(), facts);
|
||||
try (Workbook filled = new XSSFWorkbook(Files.newInputStream(out))) {
|
||||
Sheet sheet = filled.getSheetAt(0);
|
||||
assertEquals("元/CBM", read(sheet, 8, 5));
|
||||
assertEquals("元/KG", read(sheet, 8, 6));
|
||||
byte[] unitRgb = QuoteTemplateExcelSupport.readFillRgb(sheet.getRow(8).getCell(5));
|
||||
byte[] headRgb = QuoteTemplateExcelSupport.readFillRgb(sheet.getRow(7).getCell(1));
|
||||
assertTrue(unitRgb != null && unitRgb.length >= 3);
|
||||
assertTrue(headRgb != null && headRgb.length >= 3);
|
||||
assertArrayEquals(headRgb, unitRgb);
|
||||
} finally {
|
||||
Files.deleteIfExists(src);
|
||||
Files.deleteIfExists(out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 国内整车:车型格「4.2米/3T」是浅蓝表头,出单后不能变成业务员那种深蓝。
|
||||
*/
|
||||
@Test
|
||||
public void landDomesticFtlVehicleHeaderFillSurvives() throws Exception {
|
||||
Path src = Files.createTempFile("quote-land-ftl-src", ".xlsx");
|
||||
Path out = Files.createTempFile("quote-land-ftl-out", ".xlsx");
|
||||
try (Workbook wb = new XSSFWorkbook()) {
|
||||
Sheet sheet = wb.createSheet("国内整车");
|
||||
org.apache.poi.ss.usermodel.CellStyle sky = wb.createCellStyle();
|
||||
sky.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.SKY_BLUE.getIndex());
|
||||
sky.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
|
||||
org.apache.poi.ss.usermodel.CellStyle navy = wb.createCellStyle();
|
||||
navy.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.ROYAL_BLUE.getIndex());
|
||||
navy.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
|
||||
org.apache.poi.ss.usermodel.Row head = row(sheet, 7);
|
||||
head.createCell(1).setCellValue("始发站");
|
||||
head.getCell(1).setCellStyle(sky);
|
||||
head.createCell(2).setCellValue("目的地");
|
||||
head.getCell(2).setCellStyle(sky);
|
||||
head.createCell(3).setCellValue("车型/费用");
|
||||
head.getCell(3).setCellStyle(sky);
|
||||
head.createCell(8).setCellValue("时效");
|
||||
head.getCell(8).setCellStyle(sky);
|
||||
org.apache.poi.ss.usermodel.Row veh = row(sheet, 8);
|
||||
veh.createCell(3).setCellValue("4.2米/3T");
|
||||
veh.getCell(3).setCellStyle(navy);
|
||||
veh.createCell(4).setCellValue("7.6米/8T");
|
||||
veh.getCell(4).setCellStyle(navy);
|
||||
row(sheet, 9);
|
||||
try (java.io.OutputStream os = Files.newOutputStream(src)) {
|
||||
wb.write(os);
|
||||
}
|
||||
}
|
||||
JSONObject facts = new JSONObject(true);
|
||||
facts.put("origin", "北京");
|
||||
facts.put("destination", "上海");
|
||||
QuoteTemplateExcelSupport.fillWorkbook(src, out, new JSONObject(), facts);
|
||||
try (Workbook filled = new XSSFWorkbook(Files.newInputStream(out))) {
|
||||
Sheet sheet = filled.getSheetAt(0);
|
||||
assertEquals("4.2米/3T", read(sheet, 8, 3));
|
||||
assertEquals("7.6米/8T", read(sheet, 8, 4));
|
||||
byte[] vehRgb = QuoteTemplateExcelSupport.readFillRgb(sheet.getRow(8).getCell(3));
|
||||
byte[] headRgb = QuoteTemplateExcelSupport.readFillRgb(sheet.getRow(7).getCell(1));
|
||||
assertTrue(vehRgb != null && vehRgb.length >= 3);
|
||||
assertTrue(headRgb != null && headRgb.length >= 3);
|
||||
assertArrayEquals(headRgb, vehRgb);
|
||||
} finally {
|
||||
Files.deleteIfExists(src);
|
||||
Files.deleteIfExists(out);
|
||||
}
|
||||
}
|
||||
|
||||
private static org.apache.poi.ss.usermodel.Row row(Sheet sheet, int r) {
|
||||
org.apache.poi.ss.usermodel.Row exist = sheet.getRow(r);
|
||||
return exist == null ? sheet.createRow(r) : exist;
|
||||
|
||||
Reference in New Issue
Block a user