添加单价和金额计算

This commit is contained in:
zxj
2025-07-10 11:23:11 +08:00
parent 6cd5069aac
commit b657821b23
8 changed files with 254 additions and 27 deletions
@@ -36,7 +36,7 @@ object NetworkModule {
Timber.d("okhttp logger ==>${it}") Timber.d("okhttp logger ==>${it}")
}).apply { }).apply {
level = if (MyApp.DEBUG) { level = if (MyApp.DEBUG) {
HttpLoggingInterceptor.Level.BODY HttpLoggingInterceptor.Level.BASIC
} else { } else {
HttpLoggingInterceptor.Level.NONE HttpLoggingInterceptor.Level.NONE
} }
@@ -33,6 +33,17 @@ private fun Double.roundToDouble(decimalPlaces: Int): Double {
return kotlin.math.round(this * factor) / factor return kotlin.math.round(this * factor) / factor
} }
private fun Float.roundToDouble(decimalPlaces: Int): Double {
val factor = 10.0.pow(decimalPlaces)
return kotlin.math.round(this * factor) / factor
}
fun Float?.toSafeString(unitName: String?): String {
if (this == null || this == 0f) return ""
val decimalPlaces = getDecimalPlaces(unitName)
return "%.${decimalPlaces}f".format(this)
}
fun Double?.toSafeString(unitName: String?): String { fun Double?.toSafeString(unitName: String?): String {
if (this == null || this == 0.0) return "" if (this == null || this == 0.0) return ""
val decimalPlaces = getDecimalPlaces(unitName) val decimalPlaces = getDecimalPlaces(unitName)
@@ -46,6 +57,12 @@ fun Double?.toSafeDouble(unitName: String?): Double {
return roundToDouble(decimalPlaces) return roundToDouble(decimalPlaces)
} }
fun Double?.toSafeDouble(decimalPlaces: Int = 2): Double {
if (this == null || this == 0.0) return 0.0
return roundToDouble(decimalPlaces)
}
fun Double?.toSafeFloat(unitName: String?): Float { fun Double?.toSafeFloat(unitName: String?): Float {
if (this == null || this == 0.0) return 0f if (this == null || this == 0.0) return 0f
@@ -53,6 +70,18 @@ fun Double?.toSafeFloat(unitName: String?): Float {
return roundToDouble(decimalPlaces).toFloat() return roundToDouble(decimalPlaces).toFloat()
} }
fun Double?.toSafeFloat(decimalPlaces: Int = 2): Float {
if (this == null || this == 0.0) return 0f
return roundToDouble(decimalPlaces).toFloat()
}
fun Float?.toSafeFloat(decimalPlaces: Int = 2): Float {
if (this == null || this == 0f) return 0f
return roundToDouble(decimalPlaces).toFloat()
}
/** /**
* 通过单位判断小数 * 通过单位判断小数
*/ */
@@ -33,10 +33,15 @@ data class PurchaseWarehouseParam(
* 入库单价 * 入库单价
*/ */
var goodsUnitPrice: Double = 0.0, var goodsUnitPrice: Double = 0.0,
// 单价 输入框显示
var goodsUnitPriceTemp: String = "",
/** /**
* 入库金额 * 入库金额
*/ */
var goodsPrice: Double = 0.0, var goodsPrice: Double = 0.0,
// 用于界面显示的金额
var goodsPriceTemp: String = "",
/** /**
* (物品列表里 businessUnitId字段的值) * (物品列表里 businessUnitId字段的值)
*/ */
@@ -29,6 +29,9 @@ data class GoodsInfo(
*/ */
var recUnitPriceTaxIn: Float? = null, var recUnitPriceTaxIn: Float? = null,
// 单价 界面显示
var recUnitPriceTaxInTemp: String = "",
/** /**
* 单位名称 * 单位名称
*/ */
@@ -47,6 +50,8 @@ data class GoodsInfo(
* 金额 * 金额
*/ */
var recPriceExItem: Float? = null, var recPriceExItem: Float? = null,
// 金额 界面显示
var recPriceExItemTemp: String = "",
/** /**
* 仓库id * 仓库id
*/ */
@@ -555,21 +555,35 @@ private fun ReceiptProductEditView(
RowInputLayout( RowInputLayout(
label = "收货单价", label = "收货单价",
value = selectedItem!!.recUnitPriceTaxInStr, value = selectedItem!!.recUnitPriceTaxInTemp,
trailingLabel = "", trailingLabel = "",
isInitUpdate = true,
onValueChange = { value -> onValueChange = { value ->
viewModel.updatePriceInputState(value.isNotEmpty())
selectedItem?.let { selectedItem?.let {
viewModel.updateSelectedItem(selectedItem!!.copy(recUnitPriceTaxIn = value.toSafeFloat())) viewModel.updateSelectedItem(
selectedItem!!.copy(
recUnitPriceTaxInTemp = value,
recUnitPriceTaxIn = value.toSafeFloat()
)
)
} }
}, inputType = InputType.Decimal }, inputType = InputType.Decimal
) )
RowInputLayout( RowInputLayout(
label = "收货金额", label = "收货金额",
value = selectedItem!!.recPriceExItemStr, value = selectedItem!!.recPriceExItemTemp,
trailingLabel = "", trailingLabel = "",
isInitUpdate = true,
onValueChange = { value -> onValueChange = { value ->
viewModel.updateAmountInputState(value.isNotEmpty())
selectedItem?.let { selectedItem?.let {
viewModel.updateSelectedItem(selectedItem!!.copy(recPriceExItem = value.toSafeFloat())) viewModel.updateSelectedItem(
selectedItem!!.copy(
recPriceExItemTemp = value,
recPriceExItem = value.toSafeFloat()
)
)
} }
}, inputType = InputType.Decimal }, inputType = InputType.Decimal
) )
@@ -622,6 +636,13 @@ private fun ReceiptProductEditView(
ToastUtils.showToast(errorInfo) ToastUtils.showToast(errorInfo)
return@clickable return@clickable
} }
val expectAmount =
selectedItem!!.receivedNum!!.times(selectedItem!!.recUnitPriceTaxIn!!)
Timber.d("金额判断:期望金额 = $expectAmount, 实际金额 = ${selectedItem!!.recPriceExItem}")
if (expectAmount != selectedItem!!.recPriceExItem) {
ToastUtils.showToast("数量和金额不符,请检查后再试")
return@clickable
}
viewModel.updatePurchaseItem(purchaseOrder = selectedItem!!.copy(isAdjusted = true)) viewModel.updatePurchaseItem(purchaseOrder = selectedItem!!.copy(isAdjusted = true))
viewModel.updateSelectedItem(null) viewModel.updateSelectedItem(null)
}, painter = painterResource(R.mipmap.ic_btn_confirm), contentDescription = "确定") }, painter = painterResource(R.mipmap.ic_btn_confirm), contentDescription = "确定")
@@ -581,7 +581,7 @@ private fun SelfProductEditView(
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(selectedItem) { LaunchedEffect(selectedItem) {
Timber.d("界面显示") Timber.d("selectedItem变化 selectedItem is ${selectedItem == null}")
if (selectedItem == null) { if (selectedItem == null) {
viewModel.updateSelectedItem(purchaseOrder = PurchaseWarehouseParam()) viewModel.updateSelectedItem(purchaseOrder = PurchaseWarehouseParam())
} }
@@ -589,6 +589,7 @@ private fun SelfProductEditView(
DisposableEffect(lifecycleOwner) { DisposableEffect(lifecycleOwner) {
Timber.d("开始传感器采集") Timber.d("开始传感器采集")
viewModel.startSensorScale() viewModel.startSensorScale()
onDispose { onDispose {
Timber.d("停止传感器采集") Timber.d("停止传感器采集")
@@ -670,22 +671,37 @@ private fun SelfProductEditView(
RowInputLayout( RowInputLayout(
label = "采购单价", label = "采购单价",
value = selectedItem!!.goodsUnitPriceStr, value = selectedItem!!.goodsUnitPriceTemp,
inputType = InputType.Decimal, inputType = InputType.Decimal,
trailingLabel = "", trailingLabel = "",
isInitUpdate = true,
onValueChange = { value -> onValueChange = { value ->
viewModel.updatePriceInputState(value.isNotEmpty())
selectedItem.let { selectedItem.let {
viewModel.updateSelectedItem(selectedItem!!.copy(goodsUnitPrice = value.toSafeDouble())) Timber.d("goodsUnitPrice 更新 = $value")
viewModel.updateSelectedItem(
selectedItem!!.copy(
goodsUnitPriceTemp = value,
goodsUnitPrice = value.toSafeDouble()
)
)
} }
}) })
RowInputLayout( RowInputLayout(
label = "采购金额", label = "采购金额",
value = selectedItem!!.goodsPriceStr, value = selectedItem!!.goodsPriceTemp,
inputType = InputType.Decimal, inputType = InputType.Decimal,
trailingLabel = "", trailingLabel = "",
isInitUpdate = true,
onValueChange = { value -> onValueChange = { value ->
viewModel.updateAmountInputState(value.isNotEmpty())
selectedItem.let { selectedItem.let {
viewModel.updateSelectedItem(selectedItem!!.copy(goodsPrice = value.toSafeDouble())) viewModel.updateSelectedItem(
selectedItem!!.copy(
goodsPriceTemp = value,
goodsPrice = value.toSafeDouble()
)
)
} }
}) })
@@ -760,7 +776,12 @@ private fun SelfProductEditView(
ToastUtils.showToast(errInfo) ToastUtils.showToast(errInfo)
return@CustomButton return@CustomButton
} }
val expectAmount = selectedItem!!.goodsCount * selectedItem!!.goodsUnitPrice
Timber.d("金额判断:期望金额 = $expectAmount, 实际金额 = ${selectedItem!!.goodsPrice}")
if (expectAmount != selectedItem!!.goodsPrice) {
ToastUtils.showToast("数量和金额不符,请检查后再试")
return@CustomButton
}
viewModel.addPurchaseItem(selectedItem!!) viewModel.addPurchaseItem(selectedItem!!)
}) })
} }
@@ -1,9 +1,9 @@
package com.sw.inbound.viewmodel package com.sw.inbound.viewmodel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sw.inbound.ext.toFormattedString
import com.sw.inbound.ext.toSafeBigDecimal import com.sw.inbound.ext.toSafeBigDecimal
import com.sw.inbound.ext.toSafeFloat import com.sw.inbound.ext.toSafeFloat
import com.sw.inbound.ext.toSafeString
import com.sw.inbound.model.request.UploadInfo import com.sw.inbound.model.request.UploadInfo
import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.DictType
import com.sw.inbound.model.response.GoodsInfo import com.sw.inbound.model.response.GoodsInfo
@@ -72,6 +72,8 @@ class ReceiptViewModel @Inject constructor(
// 物品数量 是否由用户输入 // 物品数量 是否由用户输入
private val _countUserInput = MutableStateFlow<Boolean>(false) private val _countUserInput = MutableStateFlow<Boolean>(false)
private val _priceUserInput = MutableStateFlow<Boolean>(false)
private val _amountUserInput = MutableStateFlow<Boolean>(false)
/** /**
* 更新调整状态 * 更新调整状态
@@ -84,6 +86,7 @@ class ReceiptViewModel @Inject constructor(
fun StateFlow<List<GoodsInfo>>.getValidOrders(): List<GoodsInfo> { fun StateFlow<List<GoodsInfo>>.getValidOrders(): List<GoodsInfo> {
return this.value.filter { it.goodId != null } return this.value.filter { it.goodId != null }
} }
/** /**
* 更新收货提示弹窗 * 更新收货提示弹窗
*/ */
@@ -103,6 +106,28 @@ class ReceiptViewModel @Inject constructor(
* 更新选中的item * 更新选中的item
*/ */
fun updateSelectedItem(purchaseOrder: GoodsInfo?) { fun updateSelectedItem(purchaseOrder: GoodsInfo?) {
if (purchaseOrder != null) {
purchaseOrder.receivedNum.takeIf { it != 0f }?.let { receivedNum ->
// 计算金额 单价不为空,并且金额没有手动输入
if (purchaseOrder.recUnitPriceTaxIn != null && purchaseOrder.recUnitPriceTaxIn == 0f && !_amountUserInput.value) {
val result =
receivedNum.times(purchaseOrder.recUnitPriceTaxIn!!)
purchaseOrder.recPriceExItem = result.toSafeFloat()
purchaseOrder.recPriceExItemTemp = result.toFormattedString()
}
// 计算单价 金额不为空,并且单价没有手动输入
if (purchaseOrder.recPriceExItem != null && purchaseOrder.recPriceExItem == 0f && !_priceUserInput.value) {
val result =
purchaseOrder.recPriceExItem!!.div(receivedNum)
purchaseOrder.recUnitPriceTaxIn = result.toSafeFloat()
purchaseOrder.recUnitPriceTaxInTemp = result.toFormattedString()
}
}
} else {
_countUserInput.value = false
_priceUserInput.value = false
_amountUserInput.value = false
}
_selectedItem.value = purchaseOrder _selectedItem.value = purchaseOrder
} }
@@ -179,31 +204,70 @@ class ReceiptViewModel @Inject constructor(
_orders.value = emptyList() _orders.value = emptyList()
} }
/**
* 更新数量输入状态
*/
fun updateCountInputState(boolean: Boolean) { fun updateCountInputState(boolean: Boolean) {
_countUserInput.value = boolean _countUserInput.value = boolean
} }
/**
* 更新单价输入状态
*/
fun updatePriceInputState(boolean: Boolean) {
_priceUserInput.value = boolean
}
/**
* 更新金额输入状态
*/
fun updateAmountInputState(boolean: Boolean) {
_amountUserInput.value = boolean
}
fun startSensorScale() { fun startSensorScale() {
Timber.d("开始称重") Timber.d("开始称重")
SensorScaleUtils.startScale(callback = { weight -> SensorScaleUtils.startScale(callback = { weight ->
val currentItem = _selectedItem.value val currentItem = _selectedItem.value
currentItem?.let { info -> currentItem?.let { info ->
// 用户输入数量后不再通过重量反算
if (_countUserInput.value) {
_selectedItem.value = info.copy(
goodsWeight = weight.toSafeBigDecimal(),
)
return@startScale
}
val consumeValue = currentItem.consumeValue?.toDouble() ?: 1.0 val consumeValue = currentItem.consumeValue?.toDouble() ?: 1.0
val purchaseValue = currentItem.purchaseValue?.toDouble() ?: 1.0 val purchaseValue = currentItem.purchaseValue?.toDouble() ?: 1.0
val count = val count =
weight * 1000 / consumeValue / purchaseValue weight * 1000 / consumeValue / purchaseValue
val finalCount = count.toSafeFloat(currentItem.unitName) val finalCount = count.toSafeFloat(currentItem.unitName)
Timber.d("startSensorScale weight = $weight, count = $count, finalCount = $finalCount, consumeValue = $consumeValue, purchaseValue = $purchaseValue") Timber.d("startSensorScale weight = $weight, count = $count, finalCount = $finalCount, consumeValue = $consumeValue, purchaseValue = $purchaseValue")
if (info.receivedNumTemp.isEmpty()) { // 计算单价
_countUserInput.value = false val finalPrice = if (count == 0.0) {
} 0f
} else if (info.recPriceExItem != null && info.recPriceExItem != 0f && !_countUserInput.value && !_priceUserInput.value) {
info.recPriceExItem!!.div(count).toSafeFloat()
} else info.recUnitPriceTaxIn ?: 0f
// 计算金额
val finalAmount = if (count == 0.0) {
0f
} else if (info.recUnitPriceTaxIn != null && info.recUnitPriceTaxIn != 0f && !_countUserInput.value && !_amountUserInput.value) {
count.times(info.recUnitPriceTaxIn!!).toSafeFloat()
} else info.recPriceExItem ?: 0f
_selectedItem.value = info.copy( _selectedItem.value = info.copy(
goodsWeight = weight.toSafeBigDecimal(), goodsWeight = weight.toSafeBigDecimal(),
receivedNumTemp = if (_countUserInput.value) info.receivedNumTemp else count.toSafeString( // 收货数量
currentItem.unitName receivedNumTemp = if (_countUserInput.value) info.receivedNumTemp else count.toFormattedString(),
), receivedNum = if (_countUserInput.value) info.receivedNum else finalCount,
receivedNum = if (_countUserInput.value) info.receivedNum else finalCount // 单价
recUnitPriceTaxIn = if (_priceUserInput.value) info.recUnitPriceTaxIn else finalPrice,
recUnitPriceTaxInTemp = if (_priceUserInput.value) info.recUnitPriceTaxInTemp else finalPrice.toFormattedString(),
// 金额
recPriceExItem = if (_amountUserInput.value) info.recPriceExItem else finalAmount,
recPriceExItemTemp = if (_amountUserInput.value) info.recPriceExItemTemp else finalAmount.toFormattedString(),
) )
} }
}) })
@@ -2,9 +2,9 @@ package com.sw.inbound.viewmodel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sw.inbound.GlobalData import com.sw.inbound.GlobalData
import com.sw.inbound.ext.toFormattedString
import com.sw.inbound.ext.toSafeBigDecimal import com.sw.inbound.ext.toSafeBigDecimal
import com.sw.inbound.ext.toSafeDouble import com.sw.inbound.ext.toSafeDouble
import com.sw.inbound.ext.toSafeString
import com.sw.inbound.model.request.GoodsAddParam import com.sw.inbound.model.request.GoodsAddParam
import com.sw.inbound.model.request.PurchaseWarehouseParam import com.sw.inbound.model.request.PurchaseWarehouseParam
import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.DictType
@@ -67,6 +67,16 @@ class SelfProcurementViewModel @Inject constructor(
*/ */
private val _countUserInput = MutableStateFlow<Boolean>(false) private val _countUserInput = MutableStateFlow<Boolean>(false)
/**
* 单价是否是用户输入的值
*/
private val _priceUserInput = MutableStateFlow<Boolean>(false)
/**
* 金额输入状态
*/
private val _amountUserInput = MutableStateFlow<Boolean>(false)
/** /**
* 更新全局仓库 * 更新全局仓库
*/ */
@@ -80,6 +90,14 @@ class SelfProcurementViewModel @Inject constructor(
val currentItem = _selectedItem.value val currentItem = _selectedItem.value
currentItem?.let { info -> currentItem?.let { info ->
// 用户输入数量后不再通过重量反算
if (_countUserInput.value) {
_selectedItem.value = info.copy(
goodsWeight = weight.toSafeBigDecimal(),
)
return@startScale
}
val selectUnitType = currentItem.selectUnitType val selectUnitType = currentItem.selectUnitType
if (selectUnitType == null) return@startScale if (selectUnitType == null) return@startScale
@@ -89,15 +107,35 @@ class SelfProcurementViewModel @Inject constructor(
weight * 1000 / consumeValue / purchaseValue weight * 1000 / consumeValue / purchaseValue
val finalCount = count.toSafeDouble(currentItem.unitName) val finalCount = count.toSafeDouble(currentItem.unitName)
Timber.d("startSensorScale weight = $weight, count = $count, finalCount = $finalCount, consumeValue = $consumeValue, purchaseValue = $purchaseValue") Timber.d("startSensorScale weight = $weight, count = $count, finalCount = $finalCount, consumeValue = $consumeValue, purchaseValue = $purchaseValue")
if (info.goodsCountTemp.isEmpty()) {
_countUserInput.value = false Timber.d("startSensorScale goodsUnitPrice = ${info.goodsUnitPrice}, goodsPrice = ${info.goodsPrice} ")
} // 计算单价 金额不为空,数量也不为空, 不是数量输入 也不是单价输入
val finalPrice =
if (count == 0.0) {
0.0
} else if (info.goodsPrice != 0.0 && !_countUserInput.value && !_priceUserInput.value) {
info.goodsPrice.div(count).toSafeDouble()
} else info.goodsUnitPrice
// 计算金额 单价不为空,不是数量输入 也不是金额输入
val finalAmount =
if (count == 0.0) {
0.0
} else if (info.goodsUnitPrice != 0.0 && !_countUserInput.value && !_amountUserInput.value) {
count.times(info.goodsUnitPrice).toSafeDouble()
} else info.goodsPrice
Timber.d("startSensorScale finalPrice = $finalPrice, finalAmount = $finalAmount ")
_selectedItem.value = info.copy( _selectedItem.value = info.copy(
goodsWeight = weight.toSafeBigDecimal(), goodsWeight = weight.toSafeBigDecimal(),
goodsCountTemp = if (_countUserInput.value) info.goodsCountTemp else count.toSafeString( // 数量
currentItem.unitName goodsCountTemp = if (_countUserInput.value) info.goodsCountTemp else finalCount.toFormattedString(),
), goodsCount = if (_countUserInput.value) info.goodsCount else finalCount,
goodsCount = if (_countUserInput.value) info.goodsCount else finalCount // 单价
goodsUnitPrice = if (_priceUserInput.value) info.goodsUnitPrice else finalPrice,
goodsUnitPriceTemp = if (_priceUserInput.value) info.goodsUnitPriceTemp else finalPrice.toFormattedString(),
// 金额
goodsPrice = if (_amountUserInput.value) info.goodsPrice else finalAmount,
goodsPriceTemp = if (_amountUserInput.value) info.goodsPriceTemp else finalAmount.toFormattedString()
) )
} }
}) })
@@ -113,7 +151,34 @@ class SelfProcurementViewModel @Inject constructor(
} }
fun updateSelectedItem(purchaseOrder: PurchaseWarehouseParam?) { fun updateSelectedItem(purchaseOrder: PurchaseWarehouseParam?) {
if (purchaseOrder != null) {
purchaseOrder.goodsCount.takeIf { it != 0.0 }?.let { receivedNum ->
// 计算金额 单价不为空,并且金额没有手动输入
if (purchaseOrder.goodsUnitPrice != 0.0 && !_amountUserInput.value) {
Timber.d("updateSelectedItem 计算金额")
val result =
receivedNum.times(purchaseOrder.goodsUnitPrice)
purchaseOrder.goodsPrice = result.toSafeDouble()
purchaseOrder.goodsPriceTemp = result.toFormattedString()
}
// 计算单价 金额不为空,并且单价没有手动输入
if (purchaseOrder.goodsPrice != 0.0 && !_priceUserInput.value) {
Timber.d("updateSelectedItem 计算单价")
val result =
purchaseOrder.goodsPrice.div(receivedNum)
purchaseOrder.goodsUnitPrice = result.toSafeDouble()
purchaseOrder.goodsUnitPriceTemp = result.toFormattedString()
}
}
} else {
// 重置输入状态
Timber.d("重置输入状态")
_countUserInput.value = false
_priceUserInput.value = false
_amountUserInput.value = false
}
_selectedItem.value = purchaseOrder _selectedItem.value = purchaseOrder
Timber.d("updateSelectedItem 更新,goodsUnitPrice = ${_selectedItem.value?.goodsUnitPrice}, ${_selectedItem.value?.goodsUnitPriceTemp}")
} }
fun updateGoodsAddParam(newState: GoodsAddParam) { fun updateGoodsAddParam(newState: GoodsAddParam) {
@@ -195,10 +260,27 @@ class SelfProcurementViewModel @Inject constructor(
} }
} }
/**
* 数量输入状态
*/
fun updateCountInputState(boolean: Boolean) { fun updateCountInputState(boolean: Boolean) {
_countUserInput.value = boolean _countUserInput.value = boolean
} }
/**
* 单价输入状态
*/
fun updatePriceInputState(boolean: Boolean) {
_priceUserInput.value = boolean
}
/**
* 金额输入状态
*/
fun updateAmountInputState(boolean: Boolean) {
_amountUserInput.value = boolean
}
fun addGoodsInfo() { fun addGoodsInfo() {
val imageUri = GlobalData.imageUri val imageUri = GlobalData.imageUri
if (imageUri == null) { if (imageUri == null) {