修改导入功能
This commit is contained in:
+36
-13
@@ -797,24 +797,40 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
private List<BmsProduct> parseCatalog(Sheet sheet)
|
||||
{
|
||||
List<BmsProduct> list = new ArrayList<>();
|
||||
// Weishi workbook often starts data at column B (A empty); locate by header.
|
||||
int[] header = findHeaderCell(sheet, "\u4ea7\u54c1\u5bf9\u63a5\u4ee3\u7801");
|
||||
// Header is "\u5bf9\u63a5\u4ee3\u7801" or legacy "\u4ea7\u54c1\u5bf9\u63a5\u4ee3\u7801". Column A is often empty.
|
||||
// 20260928 layout inserts "\u8ba1\u6ce1\u89c4\u5219" before country, so map by header name.
|
||||
int[] header = findHeaderCell(sheet, "\u5bf9\u63a5\u4ee3\u7801");
|
||||
if (header == null)
|
||||
{
|
||||
return list;
|
||||
}
|
||||
int headerRow = header[0];
|
||||
int baseCol = header[1];
|
||||
int headerRowIdx = header[0];
|
||||
int colCode = header[1];
|
||||
Row head = sheet.getRow(headerRowIdx);
|
||||
int colName = catalogColumn(head, "\u4ea7\u54c1\u540d\u79f0", colCode + 1);
|
||||
int colLevel = catalogColumn(head, "\u65f6\u6548", colCode + 2);
|
||||
int colCargo = catalogColumn(head, "\u8d70\u8d27\u5c5e\u6027", colCode + 3);
|
||||
int colDest = findColByHeader(head, "\u56fd\u5bb6", colCode);
|
||||
if (colDest < 0)
|
||||
{
|
||||
colDest = findColByHeader(head, "\u76ee\u7684", colCode);
|
||||
}
|
||||
if (colDest < 0)
|
||||
{
|
||||
colDest = colCode + 4;
|
||||
}
|
||||
int colNote = catalogColumn(head, "\u66f4\u65b0\u5185\u5bb9", colCode + 5);
|
||||
int colDate = catalogColumn(head, "\u751f\u6548", colCode + 6);
|
||||
int lastRow = sheet.getLastRowNum();
|
||||
for (int i = headerRow + 1; i <= lastRow; i++)
|
||||
for (int i = headerRowIdx + 1; i <= lastRow; i++)
|
||||
{
|
||||
Row row = sheet.getRow(i);
|
||||
if (row == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String code = cellString(row.getCell(baseCol));
|
||||
if (StringUtils.isEmpty(code) || "\u4ea7\u54c1\u5bf9\u63a5\u4ee3\u7801".equals(code))
|
||||
String code = cellString(row.getCell(colCode));
|
||||
if (StringUtils.isEmpty(code) || code.contains("\u5bf9\u63a5\u4ee3\u7801"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -824,12 +840,12 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
}
|
||||
BmsProduct p = new BmsProduct();
|
||||
p.setProductCode(code.trim());
|
||||
p.setProductName(cellString(row.getCell(baseCol + 1)));
|
||||
p.setServiceLevel(cellString(row.getCell(baseCol + 2)));
|
||||
p.setCargoAttr(cellString(row.getCell(baseCol + 3)));
|
||||
p.setDestRegion(cellString(row.getCell(baseCol + 4)));
|
||||
p.setUpdateNote(cellString(row.getCell(baseCol + 5)));
|
||||
p.setEffectiveDate(cellDate(row.getCell(baseCol + 6)));
|
||||
p.setProductName(cellString(row.getCell(colName)));
|
||||
p.setServiceLevel(cellString(row.getCell(colLevel)));
|
||||
p.setCargoAttr(cellString(row.getCell(colCargo)));
|
||||
p.setDestRegion(cellString(row.getCell(colDest)));
|
||||
p.setUpdateNote(cellString(row.getCell(colNote)));
|
||||
p.setEffectiveDate(cellDate(row.getCell(colDate)));
|
||||
p.setTaxMode(resolveTaxMode(p.getProductName()));
|
||||
if (StringUtils.isEmpty(p.getProductName()))
|
||||
{
|
||||
@@ -840,6 +856,13 @@ public class BmsProductServiceImpl implements IBmsProductService
|
||||
return list;
|
||||
}
|
||||
|
||||
/** Header column, or the legacy fixed offset when the title is absent. */
|
||||
private static int catalogColumn(Row headerRow, String keyword, int fallback)
|
||||
{
|
||||
int col = findColByHeader(headerRow, keyword, 0);
|
||||
return col >= 0 ? col : fallback;
|
||||
}
|
||||
|
||||
/** Keep first row per product_code (catalog often repeats code across country rows). */
|
||||
private List<BmsProduct> dedupeCatalogProducts(List<BmsProduct> products)
|
||||
{
|
||||
|
||||
+10
-2
@@ -204,7 +204,8 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
{
|
||||
countryCode = countryCode.toUpperCase();
|
||||
}
|
||||
BigDecimal weight = request.getWeight();
|
||||
// Charge weight keeps 3 decimals; freight unit price keeps 2 (see buildFromRates).
|
||||
BigDecimal weight = request.getWeight().setScale(3, RoundingMode.HALF_UP);
|
||||
|
||||
String destPostcode = emptyToNull(request.getDestPostcode());
|
||||
String zoneCode = null;
|
||||
@@ -564,7 +565,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
BigDecimal baseRate = base.getFreightPrice();
|
||||
RoundingMode rm = resolveRoundingMode(roundMode);
|
||||
BigDecimal rate = baseRate.multiply(discount)
|
||||
.divide(new BigDecimal("100"), 4, rm);
|
||||
.divide(new BigDecimal("100"), 2, rm);
|
||||
TmsPriceQuoteResult r = buildFromRates(SRC_DISCOUNT, rate, baseRate, discount,
|
||||
base.getRegistrationFee(), base, productCode, productName, weight, roundMode);
|
||||
fillPackage(r, bind, set);
|
||||
@@ -616,6 +617,13 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
}
|
||||
RoundingMode rm = resolveRoundingMode(roundMode);
|
||||
String mode = normalizeRoundMode(roundMode);
|
||||
weight = weight.setScale(3, RoundingMode.HALF_UP);
|
||||
freightRate = freightRate.setScale(2, rm);
|
||||
if (baseFreightRate != null)
|
||||
{
|
||||
baseFreightRate = baseFreightRate.setScale(2, rm);
|
||||
}
|
||||
registrationFee = registrationFee.setScale(2, rm);
|
||||
BigDecimal freightAmount = freightRate.multiply(weight).setScale(2, rm);
|
||||
BigDecimal payable = freightAmount.add(registrationFee).setScale(2, rm);
|
||||
|
||||
|
||||
@@ -52,6 +52,9 @@
|
||||
<template #col-country="{ row }">
|
||||
{{ row.countryName || '-' }} ({{ row.countryCode || '-' }})
|
||||
</template>
|
||||
<template #col-chargeWeight="{ row }">
|
||||
{{ formatWeight(row.chargeWeight) }}
|
||||
</template>
|
||||
<template #col-matchSource="{ row }">
|
||||
<el-tag size="small" :type="sourceTagType(row.matchSource)">{{ sourceLabel(row.matchSource) }}</el-tag>
|
||||
</template>
|
||||
@@ -133,7 +136,7 @@ const columns = [
|
||||
{ label: '产品', prop: 'product', minWidth: 160, slot: 'product' },
|
||||
{ label: '国家', prop: 'country', minWidth: 130, slot: 'country' },
|
||||
{ label: '件数', prop: 'pieces', align: 'center', width: 70 },
|
||||
{ label: '计费重', prop: 'chargeWeight', align: 'center', width: 90 },
|
||||
{ label: '计费重', prop: 'chargeWeight', align: 'center', width: 90, slot: 'chargeWeight' },
|
||||
{ label: '应收总额', prop: 'incomeTotal', align: 'right', width: 120, slot: 'incomeTotal' },
|
||||
{ label: '应付总额', prop: 'payTotal', align: 'right', width: 120, slot: 'payTotal' },
|
||||
{ label: '计价来源', prop: 'matchSource', align: 'center', width: 130, slot: 'matchSource' },
|
||||
@@ -214,7 +217,14 @@ function formatMoney(val) {
|
||||
if (val === null || val === undefined || val === '') return '-'
|
||||
const num = Number(val)
|
||||
if (Number.isNaN(num)) return val
|
||||
return num.toFixed(4)
|
||||
return num.toFixed(2)
|
||||
}
|
||||
|
||||
function formatWeight(val) {
|
||||
if (val === null || val === undefined || val === '') return '-'
|
||||
const num = Number(val)
|
||||
if (Number.isNaN(num)) return val
|
||||
return num.toFixed(3)
|
||||
}
|
||||
|
||||
function sourceLabel(source) {
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
<el-descriptions-item label="目的邮编">{{ detail.destPostcode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="件数">{{ detail.pieces != null ? detail.pieces : '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="尺寸(cm)">{{ dimLabel(detail) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="实重(kg)">{{ formatMoney(detail.actualWeight) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="材积重(kg)">{{ formatMoney(detail.volumeWeight) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="实重(kg)">{{ formatWeight(detail.actualWeight) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="材积重(kg)">{{ formatWeight(detail.volumeWeight) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="体积(m³)">{{ formatMoney(detail.volume) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="下单时间">{{ formatDateTime(detail.orderTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="订单备注" :span="2">{{ detail.orderRemark || '-' }}</el-descriptions-item>
|
||||
@@ -64,7 +64,7 @@
|
||||
</div>
|
||||
<el-descriptions class="detail-desc" :column="2" border size="small">
|
||||
<el-descriptions-item label="产品">{{ productLabel(detail) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="计费重量(kg)">{{ detail.chargeWeight ?? detail.weight ?? '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="计费重量(kg)">{{ formatWeight(detail.chargeWeight ?? detail.weight) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="重量段">{{ detail.weightLabel || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="计费日期">{{ formatDate(detail.bizDate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="计价来源">
|
||||
@@ -77,7 +77,7 @@
|
||||
<dict-tag :options="bms_currency" :value="displayCurrency(detail.currency)" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="取值方式">{{ roundModeLabel(detail.roundMode) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="运费单价" >{{ formatMoney(detail.freightRate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="运费单价" >{{ formatRate(detail.freightRate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="挂号费" >{{ formatMoney(detail.registrationFee) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div v-if="detail.matchMessage" class="match-note">
|
||||
@@ -276,7 +276,21 @@ function formatMoney(val) {
|
||||
if (val === null || val === undefined || val === '') return '-'
|
||||
const num = Number(val)
|
||||
if (Number.isNaN(num)) return val
|
||||
return num.toFixed(4)
|
||||
return num.toFixed(2)
|
||||
}
|
||||
|
||||
function formatWeight(val) {
|
||||
if (val === null || val === undefined || val === '') return '-'
|
||||
const num = Number(val)
|
||||
if (Number.isNaN(num)) return val
|
||||
return num.toFixed(3)
|
||||
}
|
||||
|
||||
function formatRate(val) {
|
||||
if (val === null || val === undefined || val === '') return '-'
|
||||
const num = Number(val)
|
||||
if (Number.isNaN(num)) return val
|
||||
return num.toFixed(2)
|
||||
}
|
||||
|
||||
function formatDate(val) {
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<el-descriptions-item label="国家">{{ result.countryName || '-' }} ({{ result.countryCode }})</el-descriptions-item>
|
||||
<el-descriptions-item label="目的邮编">{{ result.destPostcode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="分区">{{ result.zoneName || result.zoneCode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="计费重量(kg)">{{ result.chargeWeight ?? result.weight }}</el-descriptions-item>
|
||||
<el-descriptions-item label="计费重量(kg)">{{ formatWeight(result.chargeWeight ?? result.weight) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="重量段">{{ result.weightLabel || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="明细生效时间">{{ formatDate(result.effectiveDate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="计价来源">
|
||||
@@ -106,8 +106,8 @@
|
||||
<el-descriptions-item label="币种">
|
||||
<dict-tag :options="bms_currency" :value="displayCurrency(result.currency)" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="运费单价">{{ formatMoney(result.freightRate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="折扣前单价">{{ formatMoney(result.baseFreightRate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="运费单价">{{ formatRate(result.freightRate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="折扣前单价">{{ formatRate(result.baseFreightRate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="折扣%">{{ result.discountPercent != null ? result.discountPercent : '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="取值方式">{{ roundModeLabel(result.roundMode) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="运费金额">{{ formatMoney(result.freightAmount) }}</el-descriptions-item>
|
||||
@@ -180,6 +180,9 @@
|
||||
<template #col-country="{ row }">
|
||||
{{ row.countryName || '-' }} ({{ row.countryCode || '-' }})
|
||||
</template>
|
||||
<template #col-weight="{ row }">
|
||||
{{ formatWeight(row.chargeWeight ?? row.weight) }}
|
||||
</template>
|
||||
<template #col-matchSource="{ row }">
|
||||
<el-tag size="small" :type="sourceTagType(row.matchSource)">{{ sourceLabel(row.matchSource) }}</el-tag>
|
||||
</template>
|
||||
@@ -206,7 +209,7 @@
|
||||
<el-descriptions-item label="国家">{{ viewResult.countryName || '-' }} ({{ viewResult.countryCode }})</el-descriptions-item>
|
||||
<el-descriptions-item label="目的邮编">{{ viewResult.destPostcode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="分区">{{ viewResult.zoneName || viewResult.zoneCode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="计费重量(kg)">{{ viewResult.chargeWeight ?? viewResult.weight }}</el-descriptions-item>
|
||||
<el-descriptions-item label="计费重量(kg)">{{ formatWeight(viewResult.chargeWeight ?? viewResult.weight) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="重量段">{{ viewResult.weightLabel || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="计价来源">
|
||||
<el-tag size="small" :type="sourceTagType(viewResult.matchSource)">{{ sourceLabel(viewResult.matchSource) }}</el-tag>
|
||||
@@ -282,7 +285,7 @@ const historyColumns = [
|
||||
{ label: '客户ID', prop: 'customerId', align: 'center', width: 90 },
|
||||
{ label: '产品', prop: 'product', minWidth: 160, slot: 'product' },
|
||||
{ label: '国家', prop: 'country', minWidth: 130, slot: 'country' },
|
||||
{ label: '计费重量', prop: 'weight', align: 'center', width: 100 },
|
||||
{ label: '计费重量', prop: 'weight', align: 'center', width: 100, slot: 'weight' },
|
||||
{ label: '计费日期', prop: 'bizDate', align: 'center', width: 120 },
|
||||
{ label: '计价来源', prop: 'matchSource', align: 'center', width: 140, slot: 'matchSource' },
|
||||
{ label: '套餐', prop: 'priceSetName', minWidth: 120, showOverflowTooltip: true },
|
||||
@@ -444,7 +447,29 @@ function formatMoney(val) {
|
||||
if (Number.isNaN(num)) {
|
||||
return val
|
||||
}
|
||||
return num.toFixed(4)
|
||||
return num.toFixed(2)
|
||||
}
|
||||
|
||||
function formatWeight(val) {
|
||||
if (val === null || val === undefined || val === '') {
|
||||
return '-'
|
||||
}
|
||||
const num = Number(val)
|
||||
if (Number.isNaN(num)) {
|
||||
return val
|
||||
}
|
||||
return num.toFixed(3)
|
||||
}
|
||||
|
||||
function formatRate(val) {
|
||||
if (val === null || val === undefined || val === '') {
|
||||
return '-'
|
||||
}
|
||||
const num = Number(val)
|
||||
if (Number.isNaN(num)) {
|
||||
return val
|
||||
}
|
||||
return num.toFixed(2)
|
||||
}
|
||||
|
||||
function formatDate(val) {
|
||||
|
||||
Reference in New Issue
Block a user