客户余额修改
This commit is contained in:
+7
-1
@@ -42,9 +42,15 @@ public class CustomerBalanceFlow extends BaseEntity
|
||||
/** 客户账户ID */
|
||||
private Long accountId;
|
||||
|
||||
/** 币种 */
|
||||
/** 币种(业务原币) */
|
||||
private String currency;
|
||||
|
||||
/** 对CNY汇率(1原币=?CNY,CNY为1) */
|
||||
private BigDecimal exchangeRate;
|
||||
|
||||
/** 本位币金额(CNY,实际参与余额变动的金额) */
|
||||
private BigDecimal baseAmount;
|
||||
|
||||
/** 业务类型:RECEIPT/BILL_PAY/REFUND/ADJUST/FREEZE/UNFREEZE */
|
||||
private String bizType;
|
||||
|
||||
|
||||
+7
-1
@@ -59,9 +59,15 @@ public class CustomerReceipt extends BaseEntity
|
||||
/** 待核销金额 */
|
||||
private BigDecimal unallocatedAmount;
|
||||
|
||||
/** 币种 */
|
||||
/** 币种(收款原币) */
|
||||
private String currency;
|
||||
|
||||
/** 对CNY汇率(1原币=?CNY,CNY为1;默认取汇率表,可手工修改) */
|
||||
private BigDecimal exchangeRate;
|
||||
|
||||
/** 本位币金额(CNY,原币金额×汇率) */
|
||||
private BigDecimal baseAmount;
|
||||
|
||||
/** 收款方式:BANK_TRANSFER/ALIPAY/WECHAT/CASH/OTHER */
|
||||
private String payMethod;
|
||||
|
||||
|
||||
+62
-12
@@ -1,6 +1,7 @@
|
||||
package com.ruoyi.customer.customermoney.service.impl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
@@ -20,6 +21,8 @@ import com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerReceipt;
|
||||
import com.ruoyi.customer.customermoney.mapper.CustomerMoneyMapper;
|
||||
import com.ruoyi.customer.customermoney.service.ICustomerMoneyService;
|
||||
import com.ruoyi.system.domain.BmsExchangeRate;
|
||||
import com.ruoyi.system.service.IBmsExchangeRateService;
|
||||
|
||||
/**
|
||||
* 客户余额/收款/流水 Service实现
|
||||
@@ -32,12 +35,20 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService
|
||||
{
|
||||
private static final DateTimeFormatter NO_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
|
||||
|
||||
/** 本位币币种 */
|
||||
private static final String BASE_CURRENCY = BmsExchangeRate.BASE_CURRENCY;
|
||||
/** 金额保留位数 */
|
||||
private static final int MONEY_SCALE = 2;
|
||||
|
||||
@Autowired
|
||||
private CustomerMoneyMapper customerMoneyMapper;
|
||||
|
||||
@Autowired
|
||||
private BmsCustomerMapper bmsCustomerMapper;
|
||||
|
||||
@Autowired
|
||||
private IBmsExchangeRateService exchangeRateService;
|
||||
|
||||
@Override
|
||||
public List<CustomerAccount> selectAccountList(Long customerId)
|
||||
{
|
||||
@@ -81,12 +92,41 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService
|
||||
}
|
||||
if (!StringUtils.hasText(receipt.getCurrency()))
|
||||
{
|
||||
receipt.setCurrency("CNY");
|
||||
receipt.setCurrency(BASE_CURRENCY);
|
||||
}
|
||||
else
|
||||
{
|
||||
receipt.setCurrency(receipt.getCurrency().trim().toUpperCase());
|
||||
}
|
||||
// 汇率与本位币金额:CNY 固定 1:1;外币默认取汇率表即期汇率,也允许页面手工修改后传入
|
||||
BigDecimal rate = receipt.getExchangeRate();
|
||||
if (BASE_CURRENCY.equals(receipt.getCurrency()))
|
||||
{
|
||||
rate = BigDecimal.ONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rate == null || rate.compareTo(BigDecimal.ZERO) <= 0)
|
||||
{
|
||||
Date bizDate = receipt.getReceivedAt() != null ? receipt.getReceivedAt() : new Date();
|
||||
BmsExchangeRate rateConfig = exchangeRateService.resolveRate(
|
||||
receipt.getCurrency(), BASE_CURRENCY, BmsExchangeRate.RATE_TYPE_SPOT, bizDate);
|
||||
rate = rateConfig.getExchangeRate();
|
||||
}
|
||||
if (rate.compareTo(BigDecimal.ZERO) <= 0)
|
||||
{
|
||||
throw new ServiceException("汇率必须大于0,请检查录入的汇率");
|
||||
}
|
||||
}
|
||||
receipt.setExchangeRate(rate.setScale(8, RoundingMode.HALF_UP));
|
||||
BigDecimal baseAmount = receipt.getAmount().multiply(rate).setScale(MONEY_SCALE, RoundingMode.HALF_UP);
|
||||
receipt.setBaseAmount(baseAmount);
|
||||
|
||||
receipt.setReceiptNo(generateNo("SK"));
|
||||
receipt.setStatus(CustomerReceipt.STATUS_PENDING);
|
||||
receipt.setAllocatedAmount(BigDecimal.ZERO);
|
||||
receipt.setUnallocatedAmount(receipt.getAmount());
|
||||
// 核销口径统一为本位币 CNY
|
||||
receipt.setUnallocatedAmount(baseAmount);
|
||||
if (!StringUtils.hasText(receipt.getInvoiceStatus()))
|
||||
{
|
||||
receipt.setInvoiceStatus("NONE");
|
||||
@@ -113,17 +153,26 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService
|
||||
throw new ServiceException("仅待确认的收款单可以审核");
|
||||
}
|
||||
|
||||
String currency = StringUtils.hasText(receipt.getCurrency()) ? receipt.getCurrency() : "CNY";
|
||||
String currency = StringUtils.hasText(receipt.getCurrency()) ? receipt.getCurrency().trim().toUpperCase()
|
||||
: BASE_CURRENCY;
|
||||
String username = SecurityUtils.getUsername();
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
|
||||
// 1. 行锁锁定客户币种账户,不存在则自动初始化
|
||||
CustomerAccount account = customerMoneyMapper.selectAccountForUpdate(receipt.getCustomerId(), currency);
|
||||
// 本位币入账金额(兼容历史数据:未折算时按原金额/汇率1处理)
|
||||
BigDecimal rate = receipt.getExchangeRate() != null && receipt.getExchangeRate().compareTo(BigDecimal.ZERO) > 0
|
||||
? receipt.getExchangeRate() : BigDecimal.ONE;
|
||||
BigDecimal originalAmount = nz(receipt.getAmount());
|
||||
BigDecimal baseAmount = receipt.getBaseAmount() != null
|
||||
? receipt.getBaseAmount()
|
||||
: originalAmount.multiply(rate).setScale(MONEY_SCALE, RoundingMode.HALF_UP);
|
||||
|
||||
// 1. 行锁锁定客户本位币(CNY)账户,不存在则自动初始化。无论收什么币种,一律折算 CNY 入账
|
||||
CustomerAccount account = customerMoneyMapper.selectAccountForUpdate(receipt.getCustomerId(), BASE_CURRENCY);
|
||||
if (account == null)
|
||||
{
|
||||
account = new CustomerAccount();
|
||||
account.setCustomerId(receipt.getCustomerId());
|
||||
account.setCurrency(currency);
|
||||
account.setCurrency(BASE_CURRENCY);
|
||||
account.setBalance(BigDecimal.ZERO);
|
||||
account.setFrozenBalance(BigDecimal.ZERO);
|
||||
account.setTotalCredit(BigDecimal.ZERO);
|
||||
@@ -135,16 +184,15 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService
|
||||
customerMoneyMapper.insertAccount(account);
|
||||
}
|
||||
|
||||
// 2. 变动余额(预充值/混合增加余额;还款核销释放已用额度)
|
||||
BigDecimal amount = nz(receipt.getAmount());
|
||||
// 2. 本位币余额变动(预充值/混合增加余额;还款核销释放已用额度)
|
||||
BigDecimal beforeBalance = nz(account.getBalance());
|
||||
BigDecimal afterBalance = beforeBalance.add(amount);
|
||||
BigDecimal afterBalance = beforeBalance.add(baseAmount);
|
||||
BigDecimal frozenBalance = nz(account.getFrozenBalance());
|
||||
|
||||
account.setBalance(afterBalance);
|
||||
if (CustomerReceipt.TYPE_REPAY.equals(receipt.getReceiptType()))
|
||||
{
|
||||
account.setUsedCredit(nz(account.getUsedCredit()).subtract(amount).max(BigDecimal.ZERO));
|
||||
account.setUsedCredit(nz(account.getUsedCredit()).subtract(baseAmount).max(BigDecimal.ZERO));
|
||||
}
|
||||
account.setUpdateBy(username);
|
||||
|
||||
@@ -154,17 +202,19 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService
|
||||
throw new ServiceException("账户余额已被其他操作变动,请刷新后重试");
|
||||
}
|
||||
|
||||
// 3. 写余额流水
|
||||
// 3. 写余额流水:原币金额留痕,本位币金额参与余额变动
|
||||
CustomerBalanceFlow flow = new CustomerBalanceFlow();
|
||||
flow.setFlowNo(generateNo("LS"));
|
||||
flow.setCustomerId(receipt.getCustomerId());
|
||||
flow.setAccountId(account.getId());
|
||||
flow.setCurrency(currency);
|
||||
flow.setExchangeRate(rate);
|
||||
flow.setBaseAmount(baseAmount);
|
||||
flow.setBizType(CustomerBalanceFlow.BIZ_RECEIPT);
|
||||
flow.setBizId(receipt.getId());
|
||||
flow.setBizNo(receipt.getReceiptNo());
|
||||
flow.setDirection(1);
|
||||
flow.setAmount(amount);
|
||||
flow.setAmount(originalAmount);
|
||||
flow.setBeforeBalance(beforeBalance);
|
||||
flow.setAfterBalance(afterBalance);
|
||||
flow.setBeforeFrozenBalance(frozenBalance);
|
||||
|
||||
@@ -101,6 +101,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="allocatedAmount" column="allocated_amount" />
|
||||
<result property="unallocatedAmount" column="unallocated_amount" />
|
||||
<result property="currency" column="currency" />
|
||||
<result property="exchangeRate" column="exchange_rate" />
|
||||
<result property="baseAmount" column="base_amount" />
|
||||
<result property="payMethod" column="pay_method" />
|
||||
<result property="bankTransactionNo" column="bank_transaction_no" />
|
||||
<result property="receivedAt" column="received_at" />
|
||||
@@ -120,7 +122,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
|
||||
<sql id="selectCustomerReceiptVo">
|
||||
select r.id, r.receipt_no, r.customer_id, r.account_id, r.receipt_type, r.amount, r.allocated_amount,
|
||||
r.unallocated_amount, r.currency, r.pay_method, r.bank_transaction_no, r.received_at, r.status,
|
||||
r.unallocated_amount, r.currency, r.exchange_rate, r.base_amount, r.pay_method, r.bank_transaction_no,
|
||||
r.received_at, r.status,
|
||||
r.confirmed_at, r.confirmed_by, r.invoice_status, r.version, r.create_by, r.create_time,
|
||||
r.update_by, r.update_time, r.remark, r.del_flag,
|
||||
(select count(1) from bms_customer_attachment a
|
||||
@@ -170,6 +173,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="allocatedAmount != null">allocated_amount,</if>
|
||||
<if test="unallocatedAmount != null">unallocated_amount,</if>
|
||||
<if test="currency != null">currency,</if>
|
||||
<if test="exchangeRate != null">exchange_rate,</if>
|
||||
<if test="baseAmount != null">base_amount,</if>
|
||||
<if test="payMethod != null">pay_method,</if>
|
||||
<if test="bankTransactionNo != null">bank_transaction_no,</if>
|
||||
<if test="receivedAt != null">received_at,</if>
|
||||
@@ -191,6 +196,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="allocatedAmount != null">#{allocatedAmount},</if>
|
||||
<if test="unallocatedAmount != null">#{unallocatedAmount},</if>
|
||||
<if test="currency != null">#{currency},</if>
|
||||
<if test="exchangeRate != null">#{exchangeRate},</if>
|
||||
<if test="baseAmount != null">#{baseAmount},</if>
|
||||
<if test="payMethod != null">#{payMethod},</if>
|
||||
<if test="bankTransactionNo != null">#{bankTransactionNo},</if>
|
||||
<if test="receivedAt != null">#{receivedAt},</if>
|
||||
@@ -231,6 +238,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="customerId" column="customer_id" />
|
||||
<result property="accountId" column="account_id" />
|
||||
<result property="currency" column="currency" />
|
||||
<result property="exchangeRate" column="exchange_rate" />
|
||||
<result property="baseAmount" column="base_amount" />
|
||||
<result property="bizType" column="biz_type" />
|
||||
<result property="bizId" column="biz_id" />
|
||||
<result property="bizNo" column="biz_no" />
|
||||
@@ -250,7 +259,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectCustomerBalanceFlowVo">
|
||||
select id, flow_no, customer_id, account_id, currency, biz_type, biz_id, biz_no, direction, amount,
|
||||
select id, flow_no, customer_id, account_id, currency, exchange_rate, base_amount, biz_type, biz_id, biz_no,
|
||||
direction, amount,
|
||||
before_balance, after_balance, before_frozen_balance, after_frozen_balance, operator_id,
|
||||
create_by, create_time, update_by, update_time, remark, del_flag
|
||||
from customer_balance_flow
|
||||
@@ -285,6 +295,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="customerId != null">customer_id,</if>
|
||||
<if test="accountId != null">account_id,</if>
|
||||
<if test="currency != null">currency,</if>
|
||||
<if test="exchangeRate != null">exchange_rate,</if>
|
||||
<if test="baseAmount != null">base_amount,</if>
|
||||
<if test="bizType != null">biz_type,</if>
|
||||
<if test="bizId != null">biz_id,</if>
|
||||
<if test="bizNo != null">biz_no,</if>
|
||||
@@ -304,6 +316,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="customerId != null">#{customerId},</if>
|
||||
<if test="accountId != null">#{accountId},</if>
|
||||
<if test="currency != null">#{currency},</if>
|
||||
<if test="exchangeRate != null">#{exchangeRate},</if>
|
||||
<if test="baseAmount != null">#{baseAmount},</if>
|
||||
<if test="bizType != null">#{bizType},</if>
|
||||
<if test="bizId != null">#{bizId},</if>
|
||||
<if test="bizNo != null">#{bizNo},</if>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
-- ----------------------------
|
||||
-- 收款/流水 增加「对CNY汇率」「本位币金额(CNY)」
|
||||
-- 统一本位币核算:原币登记,审核后一律折算为 CNY 入客户本位币账户
|
||||
-- 汇率定义:1 单位原币 = exchange_rate 单位 CNY(CNY 本币汇率固定为 1)
|
||||
-- ----------------------------
|
||||
|
||||
ALTER TABLE `customer_receipt`
|
||||
ADD COLUMN `exchange_rate` decimal(18,8) DEFAULT 1.00000000 COMMENT '对CNY汇率(1原币=?CNY,CNY为1)' AFTER `currency`,
|
||||
ADD COLUMN `base_amount` decimal(18,2) DEFAULT 0.00 COMMENT '本位币金额(CNY,原币金额×汇率)' AFTER `exchange_rate`;
|
||||
|
||||
-- 历史数据(均为CNY收款):汇率1,本位币金额=原金额
|
||||
UPDATE `customer_receipt` SET `exchange_rate` = 1, `base_amount` = `amount`
|
||||
WHERE `exchange_rate` = 1 AND (`base_amount` = 0 OR `base_amount` IS NULL);
|
||||
|
||||
ALTER TABLE `customer_balance_flow`
|
||||
ADD COLUMN `exchange_rate` decimal(18,8) DEFAULT 1.00000000 COMMENT '对CNY汇率(1原币=?CNY,CNY为1)' AFTER `currency`,
|
||||
ADD COLUMN `base_amount` decimal(18,2) DEFAULT 0.00 COMMENT '本位币金额(CNY,变动额折算)' AFTER `exchange_rate`;
|
||||
|
||||
UPDATE `customer_balance_flow` SET `exchange_rate` = 1, `base_amount` = `amount`
|
||||
WHERE `exchange_rate` = 1 AND (`base_amount` = 0 OR `base_amount` IS NULL);
|
||||
+133
-76
@@ -10,10 +10,7 @@
|
||||
</div>
|
||||
<div class="head-code">客户编码:{{ customer.customerCode || route.query.customerCode || '-' }}</div>
|
||||
<div class="head-right">
|
||||
<span style="color:#909399;font-size:13px;">币种</span>
|
||||
<el-select v-model="currentCurrency" style="width: 110px;" @change="loadAccounts">
|
||||
<el-option v-for="acc in accountList" :key="acc.currency" :label="acc.currency" :value="acc.currency" />
|
||||
</el-select>
|
||||
<el-tag type="success" size="small">本位币账户 CNY</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -22,14 +19,8 @@
|
||||
<el-row :gutter="12" class="balance-row">
|
||||
<el-col :xs="12" :sm="8" :md="4">
|
||||
<el-card shadow="hover" class="balance-card">
|
||||
<div class="balance-label">账户余额</div>
|
||||
<div class="balance-label">账户余额(CNY)</div>
|
||||
<div class="balance-value primary">{{ formatMoney(currentAccount.balance) }}</div>
|
||||
<div v-if="currentCurrency !== 'CNY'" class="balance-base">
|
||||
≈ CNY {{ baseAmountText }}
|
||||
<el-tooltip :content="baseRateText" placement="bottom">
|
||||
<el-icon class="balance-base-icon"><InfoFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="8" :md="4">
|
||||
@@ -107,12 +98,20 @@
|
||||
<el-tag :type="receiptTypeTag(scope.row.receiptType)">{{ formatOption(receiptTypeOptions, scope.row.receiptType) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="收款金额" align="center" width="130">
|
||||
<el-table-column label="原币金额" align="center" width="130">
|
||||
<template #default="scope">
|
||||
<span class="money-in">{{ scope.row.currency }} {{ formatMoney(scope.row.amount) }}</span>
|
||||
<span>{{ scope.row.currency }} {{ formatMoney(scope.row.amount) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="待核销" align="center" prop="unallocatedAmount" width="110" />
|
||||
<el-table-column label="汇率" align="center" width="110">
|
||||
<template #default="scope">{{ formatRate(scope.row.exchangeRate) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="本位币金额(CNY)" align="center" width="140">
|
||||
<template #default="scope">
|
||||
<span class="money-in">{{ formatMoney(scope.row.baseAmount) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="待核销(CNY)" align="center" prop="unallocatedAmount" width="110" />
|
||||
<el-table-column label="收款方式" align="center" prop="payMethod" width="110">
|
||||
<template #default="scope">{{ formatOption(payMethodOptions, scope.row.payMethod) }}</template>
|
||||
</el-table-column>
|
||||
@@ -195,16 +194,23 @@
|
||||
<el-tag v-else type="danger">支出</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变动金额" align="center" width="130">
|
||||
<el-table-column label="原币金额" align="center" width="130">
|
||||
<template #default="scope">
|
||||
{{ scope.row.currency }} {{ formatMoney(scope.row.amount) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="汇率" align="center" width="100">
|
||||
<template #default="scope">{{ formatRate(scope.row.exchangeRate) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="本位币变动(CNY)" align="center" width="140">
|
||||
<template #default="scope">
|
||||
<span :class="scope.row.direction === 1 ? 'money-in' : 'money-out'">
|
||||
{{ scope.row.direction === 1 ? '+' : '-' }}{{ formatMoney(scope.row.amount) }}
|
||||
{{ scope.row.direction === 1 ? '+' : '-' }}{{ formatMoney(scope.row.baseAmount != null ? scope.row.baseAmount : scope.row.amount) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变动前余额" align="center" prop="beforeBalance" width="120" />
|
||||
<el-table-column label="变动后余额" align="center" prop="afterBalance" width="120" />
|
||||
<el-table-column label="币种" align="center" prop="currency" width="70" />
|
||||
<el-table-column label="关联单号" align="center" prop="bizNo" width="180" show-overflow-tooltip />
|
||||
<el-table-column label="操作人" align="center" prop="createBy" width="100" />
|
||||
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip min-width="160" />
|
||||
@@ -238,8 +244,8 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="收款金额" prop="amount">
|
||||
<el-input v-model="receiptDialog.form.amount" placeholder="请输入金额" type="number">
|
||||
<template #prepend>¥</template>
|
||||
<el-input v-model="receiptDialog.form.amount" placeholder="请输入原币金额" type="number">
|
||||
<template #prepend>{{ receiptDialog.form.currency || 'CNY' }}</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -252,13 +258,33 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="币种" prop="currency">
|
||||
<el-select v-model="receiptDialog.form.currency" placeholder="请选择" style="width: 100%;">
|
||||
<el-option label="人民币 CNY" value="CNY" />
|
||||
<el-option label="美元 USD" value="USD" />
|
||||
<el-option label="欧元 EUR" value="EUR" />
|
||||
<el-select v-model="receiptDialog.form.currency" placeholder="请选择" style="width: 100%;" @change="handleCurrencyChange">
|
||||
<el-option v-for="c in currencyOptions" :key="c.value" :label="c.label" :value="c.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="汇率" prop="exchangeRate">
|
||||
<el-input-number
|
||||
v-model="receiptDialog.form.exchangeRate"
|
||||
:precision="8"
|
||||
:step="0.0001"
|
||||
:min="0"
|
||||
:disabled="receiptDialog.form.currency === 'CNY'"
|
||||
controls-position="right"
|
||||
style="width: 100%;"
|
||||
@input="handleRateInput"
|
||||
/>
|
||||
<div class="rate-tip">1 {{ receiptDialog.form.currency || 'CNY' }} = {{ formatRate(receiptDialog.form.exchangeRate) }} CNY<span v-if="receiptDialog.form.currency !== 'CNY'">,默认取汇率表,可手工修改</span></div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="本位币金额">
|
||||
<el-input :model-value="'CNY ' + baseAmountPreview" disabled>
|
||||
<template #prepend>≈</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="收款方式" prop="payMethod">
|
||||
<el-select v-model="receiptDialog.form.payMethod" placeholder="请选择" style="width: 100%;" clearable>
|
||||
@@ -327,10 +353,14 @@
|
||||
<el-descriptions-item label="收款类型">
|
||||
<el-tag :type="receiptTypeTag(viewDialog.data.receiptType)">{{ formatOption(receiptTypeOptions, viewDialog.data.receiptType) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="收款金额">
|
||||
<el-descriptions-item label="原币金额">
|
||||
<span class="money-in">{{ viewDialog.data.currency }} {{ formatMoney(viewDialog.data.amount) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="待核销金额">{{ formatMoney(viewDialog.data.unallocatedAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="对CNY汇率">{{ formatRate(viewDialog.data.exchangeRate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="本位币金额">
|
||||
<span class="money-in">CNY {{ formatMoney(viewDialog.data.baseAmount) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="待核销金额">CNY {{ formatMoney(viewDialog.data.unallocatedAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="收款方式">{{ formatOption(payMethodOptions, viewDialog.data.payMethod) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="银行流水号">{{ viewDialog.data.bankTransactionNo || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="确认时间">{{ formatDateTime(viewDialog.data.confirmedAt) }}</el-descriptions-item>
|
||||
@@ -413,6 +443,15 @@ const bizTypeOptions = [
|
||||
{ value: 'FREEZE', label: '冻结' },
|
||||
{ value: 'UNFREEZE', label: '解冻' }
|
||||
]
|
||||
/** 登记收款可选币种(汇率需在 数据管理-汇率管理 维护对CNY汇率) */
|
||||
const currencyOptions = [
|
||||
{ value: 'CNY', label: '人民币 CNY' },
|
||||
{ value: 'USD', label: '美元 USD' },
|
||||
{ value: 'EUR', label: '欧元 EUR' },
|
||||
{ value: 'GBP', label: '英镑 GBP' },
|
||||
{ value: 'HKD', label: '港币 HKD' },
|
||||
{ value: 'JPY', label: '日元 JPY' }
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -434,17 +473,22 @@ function formatMoney(val) {
|
||||
const num = Number(val)
|
||||
return isNaN(num) ? '0.00' : num.toFixed(2)
|
||||
}
|
||||
/** 汇率展示:去尾零,最多8位小数(7.10000000 → 7.1) */
|
||||
function formatRate(val) {
|
||||
if (val === null || val === undefined || val === '') return '1'
|
||||
const num = Number(val)
|
||||
return isNaN(num) ? String(val) : String(num)
|
||||
}
|
||||
function formatDateTime(val) {
|
||||
if (!val) return '-'
|
||||
return String(val).length > 16 ? String(val).substring(0, 16) : val
|
||||
}
|
||||
|
||||
/* ================= 客户信息 & 账户 ================= */
|
||||
/* ================= 客户信息 & 账户(统一本位币 CNY) ================= */
|
||||
const accountList = ref([])
|
||||
const currentCurrency = ref('CNY')
|
||||
|
||||
const currentAccount = computed(() => {
|
||||
return accountList.value.find(a => a.currency === currentCurrency.value) || {}
|
||||
return accountList.value.find(a => a.currency === 'CNY') || {}
|
||||
})
|
||||
|
||||
/** 可用额度 = 总额度 + 额外额度 - 已用额度 */
|
||||
@@ -469,46 +513,9 @@ function loadAccounts() {
|
||||
if (!customerId) return
|
||||
listAccount(customerId).then(res => {
|
||||
accountList.value = res.data || []
|
||||
if (accountList.value.length > 0) {
|
||||
const exists = accountList.value.some(a => a.currency === currentCurrency.value)
|
||||
if (!exists) currentCurrency.value = accountList.value[0].currency
|
||||
}
|
||||
loadBaseAmounts()
|
||||
})
|
||||
}
|
||||
|
||||
/* 外币账户按即期汇率折算本位币(CNY) */
|
||||
const baseAmountMap = ref({})
|
||||
const baseRateMap = ref({})
|
||||
|
||||
function loadBaseAmounts() {
|
||||
baseAmountMap.value = {}
|
||||
baseRateMap.value = {}
|
||||
accountList.value
|
||||
.filter(a => a.currency && a.currency !== 'CNY' && Number(a.balance) !== 0)
|
||||
.forEach(a => {
|
||||
convertToBase({ amount: a.balance, currency: a.currency, rateType: '0' })
|
||||
.then(res => {
|
||||
baseAmountMap.value[a.currency] = res.data.result
|
||||
baseRateMap.value[a.currency] = res.data.rate
|
||||
})
|
||||
.catch(() => {
|
||||
// 未维护汇率时不阻塞页面,折算行不显示
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const baseAmountText = computed(() => {
|
||||
const v = baseAmountMap.value[currentCurrency.value]
|
||||
return v === undefined || v === null ? '-' : formatMoney(v)
|
||||
})
|
||||
const baseRateText = computed(() => {
|
||||
const r = baseRateMap.value[currentCurrency.value]
|
||||
return r === undefined || r === null
|
||||
? '未维护对CNY汇率'
|
||||
: `按即期汇率折算:1 ${currentCurrency.value} = ${r} CNY`
|
||||
})
|
||||
|
||||
function goBack() {
|
||||
router.push({ path: '/customersuppliermanagement/customerinfo' })
|
||||
}
|
||||
@@ -582,12 +589,14 @@ const receiptRules = {
|
||||
}
|
||||
|
||||
function resetReceiptForm() {
|
||||
rateEdited.value = false
|
||||
receiptDialog.form = {
|
||||
customerId: route.query.customerId || null,
|
||||
receivedAt: proxy.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}'),
|
||||
amount: '',
|
||||
receiptType: 'PREPAY',
|
||||
currency: 'CNY',
|
||||
exchangeRate: 1,
|
||||
payMethod: 'BANK_TRANSFER',
|
||||
bankTransactionNo: '',
|
||||
remark: '',
|
||||
@@ -601,9 +610,65 @@ function handleAddReceipt() {
|
||||
receiptDialog.visible = true
|
||||
}
|
||||
|
||||
/** 汇率是否被手工修改过(修改后切换币种/日期不再自动覆盖) */
|
||||
const rateEdited = ref(false)
|
||||
|
||||
/** 按收款日期从汇率表取该币种对CNY即期汇率 */
|
||||
function fetchDefaultRate() {
|
||||
const cur = receiptDialog.form.currency
|
||||
if (!cur || cur === 'CNY') {
|
||||
receiptDialog.form.exchangeRate = 1
|
||||
return
|
||||
}
|
||||
const bizDate = receiptDialog.form.receivedAt
|
||||
? String(receiptDialog.form.receivedAt).substring(0, 10)
|
||||
: proxy.parseTime(new Date(), '{y}-{m}-{d}')
|
||||
convertToBase({ amount: 1, currency: cur, rateType: '0', bizDate })
|
||||
.then(res => {
|
||||
if (!rateEdited.value) receiptDialog.form.exchangeRate = Number(res.data.rate)
|
||||
})
|
||||
.catch(() => {
|
||||
if (!rateEdited.value) {
|
||||
receiptDialog.form.exchangeRate = undefined
|
||||
proxy.$modal.msgWarning(`汇率表中未维护 ${cur} 对 CNY 的汇率,请手工录入汇率`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 切换币种:重置手工标记并重新取默认汇率 */
|
||||
function handleCurrencyChange() {
|
||||
rateEdited.value = false
|
||||
fetchDefaultRate()
|
||||
}
|
||||
|
||||
/** 手工修改汇率 */
|
||||
function handleRateInput() {
|
||||
if (receiptDialog.form.currency !== 'CNY') rateEdited.value = true
|
||||
}
|
||||
|
||||
/** 本位币金额预览 = 原币金额 × 汇率 */
|
||||
const baseAmountPreview = computed(() => {
|
||||
const amount = Number(receiptDialog.form.amount) || 0
|
||||
const rate = Number(receiptDialog.form.exchangeRate) || 0
|
||||
return formatMoney(amount * rate)
|
||||
})
|
||||
|
||||
// 收款日期变化且汇率未手改时,按新日期重新取汇率表
|
||||
watch(() => receiptDialog.form.receivedAt, () => {
|
||||
if (receiptDialog.visible && receiptDialog.form.currency !== 'CNY' && !rateEdited.value) {
|
||||
fetchDefaultRate()
|
||||
}
|
||||
})
|
||||
|
||||
function submitReceipt() {
|
||||
receiptFormRef.value.validate(valid => {
|
||||
if (!valid) return
|
||||
const cur = receiptDialog.form.currency
|
||||
const rate = Number(receiptDialog.form.exchangeRate)
|
||||
if (cur !== 'CNY' && (!rate || rate <= 0)) {
|
||||
proxy.$modal.msgError(`请录入 ${cur} 对 CNY 的汇率(汇率表未配置时可手工填写)`)
|
||||
return
|
||||
}
|
||||
submitLoading.value = true
|
||||
addReceipt(receiptDialog.form).then(() => {
|
||||
proxy.$modal.msgSuccess("登记成功,等待审核")
|
||||
@@ -769,7 +834,6 @@ function initPage() {
|
||||
// 重置页面状态,避免上一个客户的数据残留
|
||||
customer.value = {}
|
||||
accountList.value = []
|
||||
currentCurrency.value = 'CNY'
|
||||
activeTab.value = 'receipt'
|
||||
receiptDialog.visible = false
|
||||
viewDialog.visible = false
|
||||
@@ -855,18 +919,11 @@ watch(() => route.query.customerId, (newId, oldId) => {
|
||||
.balance-value.success { color: var(--el-color-success); }
|
||||
.balance-value.warning { color: var(--el-color-warning); }
|
||||
.balance-value.danger { color: var(--el-color-danger); }
|
||||
.balance-base {
|
||||
margin-top: 6px;
|
||||
.rate-tip {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.balance-base-icon {
|
||||
cursor: help;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.search-form {
|
||||
margin-bottom: -4px;
|
||||
|
||||
@@ -185,12 +185,9 @@
|
||||
<el-table-column label="客服姓名" align="center" prop="serviceUser" />
|
||||
<el-table-column label="结算方式" align="center" prop="settlementType" />
|
||||
<el-table-column label="临时额度" align="center" prop="tempEd" />
|
||||
<el-table-column label="账户余额" align="center" prop="accountBalance" width="110">
|
||||
<el-table-column label="账户余额(CNY)" align="center" prop="accountBalance" width="130">
|
||||
<template #default="scope">{{ formatBalance(scope.row.accountBalance) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="外币余额" align="center" prop="foreignCurrencyBalance" width="110">
|
||||
<template #default="scope">{{ formatBalance(scope.row.foreignCurrencyBalance) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="重量阈值" align="center" prop="weightThreshold" />
|
||||
<el-table-column label="抛比" align="center" prop="throwRatio" />
|
||||
<el-table-column label="信用额度" align="center" prop="creditEd" />
|
||||
@@ -391,13 +388,6 @@
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="外币余额">
|
||||
<el-input :model-value="formatBalance(form.foreignCurrencyBalance)" disabled placeholder="0.00">
|
||||
<template #append>外币</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="重量阈值" prop="weightThreshold">
|
||||
<el-input v-model="form.weightThreshold" placeholder="请输入重量阈值" />
|
||||
|
||||
Reference in New Issue
Block a user