|
|
@@ -929,35 +929,53 @@ class ProductClass extends BaseClass
|
|
|
}
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
+ * 库存变动统一入口:用数据库行锁串行化同一花材的读-算-写,避免并发覆盖库存。
|
|
|
+ */
|
|
|
+ private static function runStockMutation($productId, callable $callback)
|
|
|
+ {
|
|
|
+ $db = Yii::$app->db;
|
|
|
+ $currentTransaction = $db->getTransaction();
|
|
|
+ $ownTransaction = empty($currentTransaction) || !$currentTransaction->isActive;
|
|
|
+ $transaction = $ownTransaction ? $db->beginTransaction() : $currentTransaction;
|
|
|
+ try {
|
|
|
+ $productData = self::getLockById($productId);
|
|
|
+ if (!$productData) {
|
|
|
+ util::fail("花材不存在");
|
|
|
+ }
|
|
|
+ $respond = call_user_func($callback, $productData);
|
|
|
+ if ($ownTransaction) {
|
|
|
+ $transaction->commit();
|
|
|
+ }
|
|
|
+ return $respond;
|
|
|
+ } catch (\Exception $exception) {
|
|
|
+ if ($ownTransaction && !empty($transaction) && $transaction->isActive) {
|
|
|
+ $transaction->rollBack();
|
|
|
+ }
|
|
|
+ throw $exception;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
//加库存,允许负库存,负数数量(盘点时)
|
|
|
public static function addStock($productId, $itemNumBundle, $itemNumPiece)
|
|
|
{
|
|
|
//小单位库存不再考虑 ssh 20230303
|
|
|
//$itemNumPiece = 0;
|
|
|
+ return self::runStockMutation($productId, function ($productData) use ($itemNumBundle, $itemNumPiece) {
|
|
|
+ $oldStock = $productData->stock;
|
|
|
+ $ratio = $productData->ratio;
|
|
|
+ //转成小数,加库存
|
|
|
+ $itemNum = self::mergeItemNum($itemNumBundle, $itemNumPiece, $ratio);
|
|
|
+ $newStock = bcadd($oldStock, $itemNum, 2);
|
|
|
+ $productData->stock = $newStock;
|
|
|
+ //上架
|
|
|
+ if ($newStock > 0 && $productData->status == 2) {
|
|
|
+ $productData->status = 1;
|
|
|
+ }
|
|
|
+ $productData->save();
|
|
|
|
|
|
- $key = self::LOCK_STOCK . '_' . $productId;
|
|
|
- $lock = util::lock($key);
|
|
|
- if (!$lock) {
|
|
|
- util::fail("系统繁忙中,请稍后再试!");
|
|
|
- }
|
|
|
- $productData = self::getById($productId);
|
|
|
- if (!$productData) {
|
|
|
- util::unlock($key);
|
|
|
- util::fail("花材不存在");
|
|
|
- }
|
|
|
- // $itemId = $productData['itemId'];
|
|
|
- $ratio = $productData['ratio'];
|
|
|
- //转成小数,加库存
|
|
|
- $itemNum = self::mergeItemNum($itemNumBundle, $itemNumPiece, $ratio);
|
|
|
- $newStock = bcadd($productData['stock'], $itemNum, 2);
|
|
|
- self::updateStockById($productId, $newStock);
|
|
|
- //上架
|
|
|
- if ($newStock > 0 && $productData['status'] == 2) {
|
|
|
- self::changeStatus($productId, 1);
|
|
|
- }
|
|
|
-
|
|
|
- util::unlock($key);
|
|
|
- return ['oldStock' => $productData['stock'], 'newStock' => $newStock, 'itemNum' => $itemNum];
|
|
|
+ return ['oldStock' => $oldStock, 'newStock' => $newStock, 'itemNum' => $itemNum];
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
//加路上库存
|
|
|
@@ -984,64 +1002,55 @@ class ProductClass extends BaseClass
|
|
|
//加库存
|
|
|
public static function addStockByItemNum($productId, $itemNum, $params = [])
|
|
|
{
|
|
|
- $key = self::LOCK_STOCK . '_' . $productId;
|
|
|
- $lock = util::lock($key);
|
|
|
- if (!$lock) {
|
|
|
- util::fail("系统繁忙中,请稍后再试!");
|
|
|
- }
|
|
|
- $productData = self::getById($productId, true);
|
|
|
- if (!$productData) {
|
|
|
- util::unlock($key);
|
|
|
- util::fail("花材不存在");
|
|
|
- }
|
|
|
-
|
|
|
- $newItemCost = 0;
|
|
|
- $newItemStock = 0;
|
|
|
- $avCost = 0;
|
|
|
- $changeCost = 0;
|
|
|
- $unitCost = 0;
|
|
|
- //涉及平均成本计算的,多处要修改,搜索关键词 calc_av_cost
|
|
|
- if (!empty($params['cost']) && $params['cost'] > 0) {
|
|
|
-
|
|
|
- //原库存和原成本,也叫剩余库存和剩余成本
|
|
|
- $oldItemStock = $productData->stock ?? 0;
|
|
|
- $oldAvCost = $productData->avCost ?? 0;
|
|
|
- $oldItemCost = bcmul($oldItemStock, $oldAvCost, 2);
|
|
|
-
|
|
|
- //变动库存和成本
|
|
|
- $unitCost = $params['cost'];
|
|
|
- $changeCost = bcmul($params['cost'], $itemNum, 2);
|
|
|
-
|
|
|
- //新库存和新成本
|
|
|
- $newItemCost = bcadd($changeCost, $oldItemCost, 2);
|
|
|
- $newItemStock = bcadd($itemNum, $oldItemStock, 2);
|
|
|
+ return self::runStockMutation($productId, function ($productData) use ($itemNum, $params) {
|
|
|
+ $oldStock = $productData->stock;
|
|
|
+ $newItemCost = 0;
|
|
|
+ $newItemStock = 0;
|
|
|
+ $avCost = 0;
|
|
|
+ $changeCost = 0;
|
|
|
+ $unitCost = 0;
|
|
|
+ //涉及平均成本计算的,多处要修改,搜索关键词 calc_av_cost
|
|
|
+ if (!empty($params['cost']) && $params['cost'] > 0) {
|
|
|
+
|
|
|
+ //原库存和原成本,也叫剩余库存和剩余成本
|
|
|
+ $oldItemStock = $productData->stock ?? 0;
|
|
|
+ $oldAvCost = $productData->avCost ?? 0;
|
|
|
+ $oldItemCost = bcmul($oldItemStock, $oldAvCost, 2);
|
|
|
+
|
|
|
+ //变动库存和成本
|
|
|
+ $unitCost = $params['cost'];
|
|
|
+ $changeCost = bcmul($params['cost'], $itemNum, 2);
|
|
|
+
|
|
|
+ //新库存和新成本
|
|
|
+ $newItemCost = bcadd($changeCost, $oldItemCost, 2);
|
|
|
+ $newItemStock = bcadd($itemNum, $oldItemStock, 2);
|
|
|
+
|
|
|
+ if ($newItemStock > 0) {
|
|
|
+ $avCost = bcdiv($newItemCost, $newItemStock, 2);
|
|
|
+ } else {
|
|
|
+ $newItemStock = 0;
|
|
|
+ $newItemCost = 0;
|
|
|
+ }
|
|
|
+ $productData->avCost = $avCost;
|
|
|
+ $productData->totalCost = $newItemCost;
|
|
|
+ $productData->totalStock = $newItemStock;
|
|
|
+ }
|
|
|
|
|
|
- if ($newItemStock > 0) {
|
|
|
- $avCost = bcdiv($newItemCost, $newItemStock, 2);
|
|
|
- } else {
|
|
|
- $newItemStock = 0;
|
|
|
- $newItemCost = 0;
|
|
|
+ $newStock = bcadd($oldStock, $itemNum, 2);
|
|
|
+ $productData->stock = $newStock;
|
|
|
+ //上架
|
|
|
+ if ($newStock > 0 && $productData->status == 2) {
|
|
|
+ $productData->status = 1;
|
|
|
+ }
|
|
|
+ //如果花材删除了则要恢复,用于调拨出库时,入库店的花材被删除
|
|
|
+ if ($newStock > 0 && $productData->delStatus == 1) {
|
|
|
+ $productData->delStatus = 0;
|
|
|
}
|
|
|
- $productData->avCost = $avCost;
|
|
|
- $productData->totalCost = $newItemCost;
|
|
|
- $productData->totalStock = $newItemStock;
|
|
|
$productData->save();
|
|
|
- }
|
|
|
|
|
|
- $newStock = bcadd($productData->stock, $itemNum, 2);
|
|
|
- self::updateStockById($productId, $newStock);
|
|
|
- //上架
|
|
|
- if ($newStock > 0 && $productData->status == 2) {
|
|
|
- self::changeStatus($productId, 1);
|
|
|
- }
|
|
|
- //如果花材删除了则要恢复,用于调拨出库时,入库店的花材被删除
|
|
|
- if ($newStock > 0 && $productData->delStatus == 1) {
|
|
|
- $productData->delStatus = 0;
|
|
|
- $productData->save();
|
|
|
- }
|
|
|
- util::unlock($key);
|
|
|
- return ['oldStock' => $productData['stock'], 'newStock' => $newStock, 'itemNum' => $itemNum,
|
|
|
- 'avCost' => $avCost, 'totalCost' => $newItemCost, 'totalStock' => $newItemStock, 'changeCost' => $changeCost, 'unitCost' => $unitCost];
|
|
|
+ return ['oldStock' => $oldStock, 'newStock' => $newStock, 'itemNum' => $itemNum,
|
|
|
+ 'avCost' => $avCost, 'totalCost' => $newItemCost, 'totalStock' => $newItemStock, 'changeCost' => $changeCost, 'unitCost' => $unitCost];
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
//减库存,允许负库存,负数数量(盘点时)
|
|
|
@@ -1051,121 +1060,99 @@ class ProductClass extends BaseClass
|
|
|
{
|
|
|
//小单位库存不再考虑 ssh 20230303
|
|
|
//$itemNumPiece = 0;
|
|
|
-
|
|
|
- $key = self::LOCK_STOCK . '_' . $productId;
|
|
|
- $lock = util::lock($key);
|
|
|
- if (!$lock) {
|
|
|
- util::fail("系统繁忙中,请稍后再试!");
|
|
|
- }
|
|
|
- $productData = self::getById($productId, true);
|
|
|
- if (!$productData) {
|
|
|
- util::unlock($key);
|
|
|
- util::fail("花材不存在");
|
|
|
- }
|
|
|
-
|
|
|
- $ratio = $productData->ratio;
|
|
|
- //转成小数,更新库存
|
|
|
- $itemNum = self::mergeItemNum($itemNumBundle, $itemNumPiece, $ratio);
|
|
|
- $newStock = bcsub($productData->stock, $itemNum, 2);
|
|
|
- $avCost = 0;
|
|
|
- $newItemStock = 0;
|
|
|
- $newItemCost = 0;
|
|
|
- $changeCost = 0;
|
|
|
- $unitCost = 0;
|
|
|
- //涉及平均成本计算的,多处要修改,搜索关键词 calc_av_cost
|
|
|
- if (!empty($params['cost']) && $params['cost'] > 0) {
|
|
|
- //变成库存和变成成本
|
|
|
- $unitCost = $params['cost'];
|
|
|
- $changeCost = bcmul($params['cost'], $itemNum, 2);
|
|
|
-
|
|
|
- //原库存和原成本,也叫剩余库存和剩余成本
|
|
|
- $oldItemStock = $productData->stock;
|
|
|
- $oldAvCost = $productData->avCost ?? 0;
|
|
|
- $oldItemCost = bcmul($oldItemStock, $oldAvCost, 2);
|
|
|
-
|
|
|
- //新库存和新成本
|
|
|
- $newItemCost = bcsub($oldItemCost, $changeCost, 2);
|
|
|
- $newItemStock = bcsub($oldItemStock, $itemNum, 2);
|
|
|
-
|
|
|
- if ($newItemStock > 0) {
|
|
|
- $avCost = bcdiv($newItemCost, $newItemStock, 2);
|
|
|
- } else {
|
|
|
- $newItemStock = 0;
|
|
|
- $newItemCost = 0;
|
|
|
- }
|
|
|
- //如果减库存之后,还有剩余库存,则变更新的平均成本。如果没有库存不再变更平均成本。如没有库存了还变更,会造成全部调拨出库时,保存的成本是0,重要!!!!!!!!!!
|
|
|
- //确实是,如果没有库存了,平均成本也不能再变化了。如果再变化,平均成本也是变成0。变成0后,如果再盘点几个库存出来,会变成没有成本的库存。 ssh 20251130
|
|
|
- if ($newStock > 0) {
|
|
|
- $productData->avCost = $avCost;
|
|
|
+ return self::runStockMutation($productId, function ($productData) use ($itemNumBundle, $itemNumPiece, $checkStock, $addSold, $params) {
|
|
|
+ $oldStock = $productData->stock;
|
|
|
+ $ratio = $productData->ratio;
|
|
|
+ //转成小数,更新库存
|
|
|
+ $itemNum = self::mergeItemNum($itemNumBundle, $itemNumPiece, $ratio);
|
|
|
+ $newStock = bcsub($oldStock, $itemNum, 2);
|
|
|
+ $avCost = 0;
|
|
|
+ $newItemStock = 0;
|
|
|
+ $newItemCost = 0;
|
|
|
+ $changeCost = 0;
|
|
|
+ $unitCost = 0;
|
|
|
+ //涉及平均成本计算的,多处要修改,搜索关键词 calc_av_cost
|
|
|
+ if (!empty($params['cost']) && $params['cost'] > 0) {
|
|
|
+ //变成库存和变成成本
|
|
|
+ $unitCost = $params['cost'];
|
|
|
+ $changeCost = bcmul($params['cost'], $itemNum, 2);
|
|
|
+
|
|
|
+ //原库存和原成本,也叫剩余库存和剩余成本
|
|
|
+ $oldItemStock = $productData->stock;
|
|
|
+ $oldAvCost = $productData->avCost ?? 0;
|
|
|
+ $oldItemCost = bcmul($oldItemStock, $oldAvCost, 2);
|
|
|
+
|
|
|
+ //新库存和新成本
|
|
|
+ $newItemCost = bcsub($oldItemCost, $changeCost, 2);
|
|
|
+ $newItemStock = bcsub($oldItemStock, $itemNum, 2);
|
|
|
+
|
|
|
+ if ($newItemStock > 0) {
|
|
|
+ $avCost = bcdiv($newItemCost, $newItemStock, 2);
|
|
|
+ } else {
|
|
|
+ $newItemStock = 0;
|
|
|
+ $newItemCost = 0;
|
|
|
+ }
|
|
|
+ //如果减库存之后,还有剩余库存,则变更新的平均成本。如果没有库存不再变更平均成本。如没有库存了还变更,会造成全部调拨出库时,保存的成本是0,重要!!!!!!!!!!
|
|
|
+ //确实是,如果没有库存了,平均成本也不能再变化了。如果再变化,平均成本也是变成0。变成0后,如果再盘点几个库存出来,会变成没有成本的库存。 ssh 20251130
|
|
|
+ if ($newStock > 0) {
|
|
|
+ $productData->avCost = $avCost;
|
|
|
+ }
|
|
|
+ $productData->totalCost = $newItemCost;
|
|
|
+ $productData->totalStock = $newItemStock;
|
|
|
}
|
|
|
- $productData->totalCost = $newItemCost;
|
|
|
- $productData->totalStock = $newItemStock;
|
|
|
- $productData->save();
|
|
|
- }
|
|
|
|
|
|
- if ($checkStock && $newStock < 0) {
|
|
|
- //需要判断库存是否充足
|
|
|
- util::unlock($key);
|
|
|
- $currentName = $productData->name ?? '';
|
|
|
- $currentRemainStock = floatval($productData->stock);
|
|
|
- util::fail("{$currentName} 只剩{$currentRemainStock}{$productData->bigUnit}");
|
|
|
- }
|
|
|
- self::updateStockById($productId, $newStock);
|
|
|
-
|
|
|
- if ($addSold) {
|
|
|
- //减库存,加销量 2021.6.21 lqh
|
|
|
- $actualSold = $productData->actualSold;
|
|
|
- $newActualSold = bcadd($actualSold, $itemNum, 2);
|
|
|
- self::updateActualSoldById($productId, $newActualSold);
|
|
|
- }
|
|
|
+ if ($checkStock && $newStock < 0) {
|
|
|
+ //需要判断库存是否充足
|
|
|
+ $currentName = $productData->name ?? '';
|
|
|
+ $currentRemainStock = floatval($oldStock);
|
|
|
+ util::fail("{$currentName} 只剩{$currentRemainStock}{$productData->bigUnit}");
|
|
|
+ }
|
|
|
+ $productData->stock = $newStock;
|
|
|
|
|
|
- //如果花材是自动补充库存类型的花材,则库存低于900时,自动补充到98520,多处要同步修改,关键词 auto_add_stock
|
|
|
- if ($productData->virtualStock == 1 && $newStock < 9900) {
|
|
|
- self::updateStockById($productId, 98520);
|
|
|
- }
|
|
|
+ if ($addSold) {
|
|
|
+ //减库存,加销量 2021.6.21 lqh
|
|
|
+ $actualSold = $productData->actualSold;
|
|
|
+ $productData->actualSold = bcadd($actualSold, $itemNum, 2);
|
|
|
+ }
|
|
|
|
|
|
- //库存=0 下架商品,特价去掉,限购去掉 lqh 2021.7.9 ssh 20251106 花材满减去掉 20260318
|
|
|
- if ($newStock <= 0) {
|
|
|
- self::changeStatus($productId, 2);
|
|
|
+ //如果花材是自动补充库存类型的花材,则库存低于900时,自动补充到98520,多处要同步修改,关键词 auto_add_stock
|
|
|
+ if ($productData->virtualStock == 1 && $newStock < 9900) {
|
|
|
+ $productData->stock = 98520;
|
|
|
+ }
|
|
|
|
|
|
- //限购数量重置为 0,但会把限购不为 0 的数据记录到缓存
|
|
|
- self::createRecoverLimitBuyCache($productId, $productData->limitBuy);
|
|
|
- }
|
|
|
+ //库存=0 下架商品,特价去掉,限购去掉 lqh 2021.7.9 ssh 20251106 花材满减去掉 20260318
|
|
|
+ if ($newStock <= 0) {
|
|
|
+ $productData->status = 2;
|
|
|
+ //限购数量重置为 0,但会把限购不为 0 的数据记录到缓存
|
|
|
+ self::createRecoverLimitBuyCache($productId, $productData->limitBuy);
|
|
|
+ }
|
|
|
+ $productData->save();
|
|
|
|
|
|
- util::unlock($key);
|
|
|
- return ['oldStock' => $productData['stock'], 'newStock' => $newStock, 'itemNum' => $itemNum,
|
|
|
- 'avCost' => $avCost, 'totalCost' => $newItemCost, 'totalStock' => $newItemStock, 'changeCost' => $changeCost, 'unitCost' => $unitCost];
|
|
|
+ return ['oldStock' => $oldStock, 'newStock' => $newStock, 'itemNum' => $itemNum,
|
|
|
+ 'avCost' => $avCost, 'totalCost' => $newItemCost, 'totalStock' => $newItemStock, 'changeCost' => $changeCost, 'unitCost' => $unitCost];
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
|
|
|
//根据数量减库存
|
|
|
public static function decreaseStockByItemNum($productId, $itemNum, $checkStock = false)
|
|
|
{
|
|
|
- $key = self::LOCK_STOCK . '_' . $productId;
|
|
|
- $lock = util::lock($key);
|
|
|
- if (!$lock) {
|
|
|
- util::fail("系统繁忙中,请稍后再试!");
|
|
|
- }
|
|
|
- $productData = self::getById($productId);
|
|
|
- if (!$productData) {
|
|
|
- util::unlock($key);
|
|
|
- util::fail("花材不存在");
|
|
|
- }
|
|
|
- $newStock = bcsub($productData['stock'], $itemNum, 2);
|
|
|
- if ($checkStock && $newStock < 0) {
|
|
|
- //需要判断库存是否充足
|
|
|
- util::unlock($key);
|
|
|
- util::fail("库存不足");
|
|
|
- }
|
|
|
- self::updateStockById($productId, $newStock);
|
|
|
+ return self::runStockMutation($productId, function ($productData) use ($itemNum, $checkStock) {
|
|
|
+ $oldStock = $productData->stock;
|
|
|
+ $newStock = bcsub($oldStock, $itemNum, 2);
|
|
|
+ if ($checkStock && $newStock < 0) {
|
|
|
+ //需要判断库存是否充足
|
|
|
+ util::fail("库存不足");
|
|
|
+ }
|
|
|
+ $productData->stock = $newStock;
|
|
|
|
|
|
- //减库存,加销量 2021.6.21 lqh
|
|
|
- $actualSold = $productData['actualSold'];
|
|
|
- $newActualSold = bcadd($actualSold, $itemNum, 2);
|
|
|
- self::updateActualSoldById($productId, $newActualSold);
|
|
|
+ //减库存,加销量 2021.6.21 lqh
|
|
|
+ $actualSold = $productData->actualSold;
|
|
|
+ $productData->actualSold = bcadd($actualSold, $itemNum, 2);
|
|
|
+ $productData->save();
|
|
|
|
|
|
- util::unlock($key);
|
|
|
- return ['oldStock' => $productData['stock'], 'newStock' => $newStock, 'itemNum' => $itemNum];
|
|
|
+ return ['oldStock' => $oldStock, 'newStock' => $newStock, 'itemNum' => $itemNum];
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
//减少在路上的库存
|
|
|
@@ -1347,16 +1334,17 @@ class ProductClass extends BaseClass
|
|
|
//修改花材商品的库存: 统一方法
|
|
|
public static function updateStockById($id, $stock, $adminId = 0)
|
|
|
{
|
|
|
- $data['stock'] = $stock;
|
|
|
- //如果有库存需要保持上架状态
|
|
|
- if ($stock > 0) {
|
|
|
- $data['status'] = 1;
|
|
|
- }
|
|
|
- if ($adminId) {
|
|
|
- $data['adminId'] = $adminId;
|
|
|
- }
|
|
|
- $res = self::updateById($id, $data);
|
|
|
- return $res;
|
|
|
+ return self::runStockMutation($id, function ($productData) use ($stock, $adminId) {
|
|
|
+ $productData->stock = $stock;
|
|
|
+ //如果有库存需要保持上架状态
|
|
|
+ if ($stock > 0) {
|
|
|
+ $productData->status = 1;
|
|
|
+ }
|
|
|
+ if ($adminId) {
|
|
|
+ $productData->adminId = $adminId;
|
|
|
+ }
|
|
|
+ return $productData->save();
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
//订单退回库存
|