|
|
@@ -19,17 +19,22 @@ use bizGhs\custom\services\GhsRechargeSettleService;
|
|
|
use bizGhs\clear\classes\OrderCgClearClass;
|
|
|
use bizGhs\order\classes\OrderClass;
|
|
|
use bizGhs\order\classes\OrderExpressClass;
|
|
|
+use bizGhs\order\classes\OrderForwardClass;
|
|
|
use bizGhs\order\classes\OrderItemClass;
|
|
|
use bizGhs\order\classes\OrderSendClass;
|
|
|
use bizGhs\product\classes\ProductClass;
|
|
|
use bizGhs\shop\classes\MainClass;
|
|
|
use bizGhs\shop\classes\ShopMoneyChangeClass;
|
|
|
use bizGhs\stat\classes\StatYjClass;
|
|
|
+use biz\stat\classes\StatOrderCountClass;
|
|
|
+use bizGhs\stock\classes\StockRecordClass;
|
|
|
use bizHd\cg\services\CgRefundService;
|
|
|
+use bizHd\purchase\classes\PurchaseItemClass;
|
|
|
use bizHd\purchase\services\PurchaseService;
|
|
|
use bizHd\stat\classes\StatIncomeClass;
|
|
|
use bizHd\stat\classes\StatOrderClass;
|
|
|
use common\components\dict;
|
|
|
+use common\components\orderSn;
|
|
|
use common\components\util;
|
|
|
use bizHd\purchase\classes\PurchaseClass;
|
|
|
use common\components\stringUtil;
|
|
|
@@ -1010,4 +1015,303 @@ class OrderService extends BaseService
|
|
|
return $saleRefund;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 创建冲销单(金额为负的独立订单)。
|
|
|
+ * 设计说明:不复用极其庞杂的常规开单主链路(createNewOrder->OrderClass::addOrder),
|
|
|
+ * 避免误伤现网所有商户的正常开单流程;只在数据落地时保持与正常订单相同的表结构,方便统一查询/统计。
|
|
|
+ * @param $post array 前端提交参数:customId,product(JSON数组),remark,needPrint,payWay,forwardStock,returnBalance,relateOrderId(可选)
|
|
|
+ * @param $custom array 客户信息(CustomClass::getCustom 返回)
|
|
|
+ * @return array ['order'=>冲销单模型, 'customBalance'=>返充后的余额(未返充时为null)]
|
|
|
+ */
|
|
|
+ public static function createForwardOrder($post, $custom)
|
|
|
+ {
|
|
|
+ $customId = $custom['id'] ?? 0;
|
|
|
+ $mainId = $post['mainId'] ?? 0;
|
|
|
+ $shopId = $post['shopId'] ?? 0;
|
|
|
+ $sjId = $post['sjId'] ?? 0;
|
|
|
+ $shopAdminId = $post['shopAdminId'] ?? 0;
|
|
|
+ $shopAdminName = $post['shopAdminName'] ?? '';
|
|
|
+ $ghsId = $custom['ghsId'] ?? 0;
|
|
|
+
|
|
|
+ $productList = $post['product'] ?? [];
|
|
|
+ if (empty($productList)) {
|
|
|
+ util::fail('请选择花材');
|
|
|
+ }
|
|
|
+
|
|
|
+ $level = $custom['level'] ?? 1;
|
|
|
+ $live = isset($custom['live']) ? $custom['live'] : 1;
|
|
|
+ $respond = ProductClass::formatProductInfo($productList, $level, $live, ['custom' => $custom]);
|
|
|
+ $items = $respond['product'] ?? [];
|
|
|
+ if (empty($items)) {
|
|
|
+ util::fail('花材信息有误');
|
|
|
+ }
|
|
|
+
|
|
|
+ //本次冲销总金额(正数)
|
|
|
+ $totalAmount = 0;
|
|
|
+ foreach ($items as $it) {
|
|
|
+ $totalAmount = bcadd($totalAmount, $it['price'], 2);
|
|
|
+ }
|
|
|
+ if (bccomp($totalAmount, '0', 2) <= 0) {
|
|
|
+ util::fail('冲销金额必须大于0');
|
|
|
+ }
|
|
|
+
|
|
|
+ $forwardStock = isset($post['forwardStock']) && in_array((int)$post['forwardStock'], [0, 1], true) ? (int)$post['forwardStock'] : 1;
|
|
|
+ $returnBalance = isset($post['returnBalance']) && (int)$post['returnBalance'] == 1 ? 1 : 0;
|
|
|
+ $payWay = isset($post['payWay']) ? (int)$post['payWay'] : dict::getDict('payWay', 'wxPay');
|
|
|
+
|
|
|
+ //关联原单场景:累计冲销上限校验,并且强制按原单支付方式,不信任前端传的 payWay
|
|
|
+ $relateOrderId = $post['relateOrderId'] ?? 0;
|
|
|
+ $relateOrder = null;
|
|
|
+ if (!empty($relateOrderId)) {
|
|
|
+ $relateOrder = OrderClass::getLockById($relateOrderId);
|
|
|
+ if (empty($relateOrder)) {
|
|
|
+ util::fail('没有找到原订单');
|
|
|
+ }
|
|
|
+ if ($relateOrder->mainId != $mainId || $relateOrder->customId != $customId) {
|
|
|
+ util::fail('原订单信息不匹配');
|
|
|
+ }
|
|
|
+ $actPrice = $relateOrder->actPrice ?? 0;
|
|
|
+ $hasForwardPrice = $relateOrder->forwardPrice ?? 0;
|
|
|
+ if (bccomp($hasForwardPrice, $actPrice, 2) >= 0) {
|
|
|
+ util::fail('该订单已冲销完,无法再次冲销');
|
|
|
+ }
|
|
|
+ $remain = bcsub($actPrice, $hasForwardPrice, 2);
|
|
|
+ if (bccomp($totalAmount, $remain, 2) == 1) {
|
|
|
+ util::fail("冲销金额超过原订单可冲销余额(剩余可冲销 ¥{$remain})");
|
|
|
+ }
|
|
|
+ //退款方式锁定为原单实际支付方式
|
|
|
+ $payWay = $relateOrder->payWay ?? $payWay;
|
|
|
+ }
|
|
|
+
|
|
|
+ //落库统一取负:金额字段为负,数量字段保持正数(代表冲销的数量)
|
|
|
+ $negItems = [];
|
|
|
+ $totalCost = 0;
|
|
|
+ foreach ($items as $it) {
|
|
|
+ $it['price'] = bcmul($it['price'], -1, 2);
|
|
|
+ $it['userPrice'] = bcmul($it['userPrice'], -1, 2);
|
|
|
+ $it['unitPrice'] = bcmul($it['unitPrice'], -1, 2);
|
|
|
+ $it['smallUnitPrice'] = bcmul($it['smallUnitPrice'], -1, 2);
|
|
|
+ $it['xhPrice'] = bcmul($it['xhPrice'], -1, 2);
|
|
|
+ $it['xhUnitPrice'] = bcmul($it['xhUnitPrice'], -1, 2);
|
|
|
+ $it['xhPrePrice'] = bcmul($it['xhPrePrice'], -1, 2);
|
|
|
+ $it['xhPreUnitPrice'] = bcmul($it['xhPreUnitPrice'], -1, 2);
|
|
|
+ $it['cost'] = bcmul($it['cost'], -1, 2);
|
|
|
+ $it['forward'] = 1;
|
|
|
+ $totalCost = bcadd($totalCost, $it['cost'], 2);
|
|
|
+ $negItems[] = $it;
|
|
|
+ }
|
|
|
+ $actPrice = bcmul($totalAmount, -1, 2);
|
|
|
+
|
|
|
+ $snData = ['shopId' => $shopId, 'mainId' => $mainId, 'ghsId' => $ghsId, 'customId' => $customId];
|
|
|
+ $orderSn = orderSn::getGhsOrderSn($snData);
|
|
|
+ $now = date('Y-m-d H:i:s');
|
|
|
+ $deadline = date('Y-m-d H:i:s', time() + dict::getDict('order_pay_has_time'));
|
|
|
+
|
|
|
+ $orderData = [
|
|
|
+ 'mainId' => $mainId,
|
|
|
+ 'sendNum' => '0' . date('j') . StatOrderCountClass::addOrder(ShopClass::getLockById($shopId), MainClass::getLockById($mainId)),
|
|
|
+ 'orderSn' => $orderSn,
|
|
|
+ 'customId' => $customId,
|
|
|
+ 'customName' => $custom['name'] ?? '',
|
|
|
+ 'customNamePy' => $custom['py'] ?? '',
|
|
|
+ 'customAvatar' => $custom['shortSmallAvatar'] ?? '',
|
|
|
+ 'customMobile' => $custom['mobile'] ?? '',
|
|
|
+ 'ghsId' => $ghsId,
|
|
|
+ 'sjId' => $sjId,
|
|
|
+ 'shopId' => $shopId,
|
|
|
+ 'shopAdminId' => $shopAdminId,
|
|
|
+ 'shopAdminName' => $shopAdminName,
|
|
|
+ 'getStaffId' => $shopAdminId,
|
|
|
+ 'getStaffName' => $shopAdminName,
|
|
|
+ 'itemNum' => $respond['totalItemNum'] ?? 0,
|
|
|
+ 'bigNum' => $respond['bigNum'] ?? 0,
|
|
|
+ 'smallNum' => $respond['smallNum'] ?? 0,
|
|
|
+ 'itemPrice' => $actPrice,
|
|
|
+ 'prePrice' => $actPrice,
|
|
|
+ 'orderPrice' => $actPrice,
|
|
|
+ 'bookPrice' => $actPrice,
|
|
|
+ 'realPrice' => $actPrice,
|
|
|
+ 'actPrice' => $actPrice,
|
|
|
+ 'totalCost' => $totalCost,
|
|
|
+ 'remainCost' => $totalCost,
|
|
|
+ 'payWay' => $payWay,
|
|
|
+ 'fromType' => 1,
|
|
|
+ 'debt' => OrderClass::DEBT_NO,
|
|
|
+ 'status' => OrderClass::ORDER_STATUS_COMPLETE,
|
|
|
+ 'payStatus' => OrderClass::PAY_STATUS_HAS_PAY,
|
|
|
+ 'sendType' => OrderClass::SEND_TYPE_SHOP_GET,
|
|
|
+ 'sendStatus' => 2,
|
|
|
+ 'sendTimeWant' => date('Y-m-d'),
|
|
|
+ 'payTime' => $now,
|
|
|
+ 'deadline' => $deadline,
|
|
|
+ 'autoSetTime' => $deadline,
|
|
|
+ 'needPrint' => $post['needPrint'] ?? 2,
|
|
|
+ 'remark' => $post['remark'] ?? '',
|
|
|
+ 'forward' => 1,
|
|
|
+ 'forwardStock' => $forwardStock,
|
|
|
+ 'salt' => stringUtil::charsShuffleLowerCase(10),
|
|
|
+ ];
|
|
|
+ $order = OrderClass::add($orderData, true);
|
|
|
+
|
|
|
+ foreach ($negItems as $it) {
|
|
|
+ $it['orderSn'] = $orderSn;
|
|
|
+ $it['mainId'] = $mainId;
|
|
|
+ OrderItemClass::add($it, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ //库存处理:forwardStock==0 货退回来则加库存,==1 不退回则不动库存
|
|
|
+ if ($forwardStock == 0) {
|
|
|
+ $customName = $custom['name'] ?? '';
|
|
|
+ foreach ($negItems as $it) {
|
|
|
+ $productId = $it['productId'] ?? 0;
|
|
|
+ $itemNum = $it['num'] ?? 0;
|
|
|
+ if (empty($productId) || bccomp($itemNum, '0', 2) <= 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $stockInfo = ProductClass::addStockByItemNum($productId, $itemNum);
|
|
|
+ $recordData = [
|
|
|
+ 'sjId' => $sjId,
|
|
|
+ 'shopId' => $shopId,
|
|
|
+ 'mainId' => $mainId,
|
|
|
+ 'orderSn' => $orderSn,
|
|
|
+ 'itemId' => $it['itemId'] ?? 0,
|
|
|
+ 'productId' => $productId,
|
|
|
+ 'itemNum' => $itemNum,
|
|
|
+ 'oldStock' => $stockInfo['oldStock'] ?? 0,
|
|
|
+ 'newStock' => $stockInfo['newStock'] ?? 0,
|
|
|
+ 'relateName' => $customName,
|
|
|
+ 'ptStyle' => dict::getDict('ptStyle', 'ghs'),
|
|
|
+ 'io' => 1,
|
|
|
+ ];
|
|
|
+ StockRecordClass::ghsRefundAddRecord($recordData);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //配对生成花店侧采购冲销记录 xhCg/xhCgItem,直接构造模型保存,不复用 PurchaseClass::addPurchase 的完整业务规则
|
|
|
+ $ghs = GhsClass::getById($ghsId);
|
|
|
+ $cgSn = orderSn::getPurchaseSn($snData);
|
|
|
+ $cgData = [
|
|
|
+ 'mainId' => $ghs['ownMainId'] ?? 0,
|
|
|
+ 'saleId' => $order->id ?? 0,
|
|
|
+ 'orderSn' => $cgSn,
|
|
|
+ 'customId' => $customId,
|
|
|
+ 'ghsId' => $ghsId,
|
|
|
+ 'ghsName' => $ghs['name'] ?? '',
|
|
|
+ 'ghsAvatar' => $ghs['avatar'] ?? '',
|
|
|
+ 'ghsMobile' => $ghs['mobile'] ?? '',
|
|
|
+ 'ghsFullAddress' => $ghs['fullAddress'] ?? '',
|
|
|
+ 'sjId' => $custom['sjId'] ?? 0,
|
|
|
+ 'shopId' => $custom['shopId'] ?? 0,
|
|
|
+ 'ghsShopId' => $shopId,
|
|
|
+ 'ghsShopAdminId' => $shopAdminId,
|
|
|
+ 'ghsShopAdminName' => $shopAdminName,
|
|
|
+ 'itemPrice' => $actPrice,
|
|
|
+ 'prePrice' => $actPrice,
|
|
|
+ 'orderPrice' => $actPrice,
|
|
|
+ 'bookPrice' => $actPrice,
|
|
|
+ 'realPrice' => $actPrice,
|
|
|
+ 'actPrice' => $actPrice,
|
|
|
+ 'kindNum' => count($negItems),
|
|
|
+ 'bigNum' => $respond['bigNum'] ?? 0,
|
|
|
+ 'smallNum' => $respond['smallNum'] ?? 0,
|
|
|
+ 'payWay' => $payWay,
|
|
|
+ 'status' => PurchaseClass::STATUS_COMPLETE,
|
|
|
+ 'cgStyle' => dict::getDict('cgStyle', 'ghs'),
|
|
|
+ 'debt' => PurchaseClass::DEBT_NO,
|
|
|
+ 'getType' => PurchaseClass::GET_TYPE_SELF_GET,
|
|
|
+ 'payStatus' => 1,
|
|
|
+ 'sendTimeWant' => date('Y-m-d'),
|
|
|
+ 'payTime' => $now,
|
|
|
+ 'remark' => $post['remark'] ?? '',
|
|
|
+ 'forward' => 1,
|
|
|
+ 'forwardStock' => $forwardStock,
|
|
|
+ 'salt' => stringUtil::charsShuffleLowerCase(10),
|
|
|
+ ];
|
|
|
+ $cg = PurchaseClass::add($cgData, true);
|
|
|
+ $order->purchaseId = $cg->id ?? 0;
|
|
|
+ $order->save(false, ['purchaseId']);
|
|
|
+
|
|
|
+ foreach ($negItems as $it) {
|
|
|
+ $cgItemData = [
|
|
|
+ 'name' => $it['name'] ?? '',
|
|
|
+ 'cover' => $it['cover'] ?? '',
|
|
|
+ 'orderSn' => $cgSn,
|
|
|
+ 'productId' => $it['productId'] ?? 0,
|
|
|
+ 'itemId' => $it['itemId'] ?? 0,
|
|
|
+ 'mainId' => $cgData['mainId'],
|
|
|
+ 'ghsId' => $ghsId,
|
|
|
+ 'itemNum' => $it['num'] ?? 0,
|
|
|
+ 'preItemNum' => $it['num'] ?? 0,
|
|
|
+ 'itemInfo' => json_encode(['itemName' => $it['name'] ?? '', 'bigNum' => $it['bigNum'] ?? 0, 'smallNum' => $it['smallNum'] ?? 0]),
|
|
|
+ 'ghsProductId' => $it['productId'] ?? 0,
|
|
|
+ 'ratio' => $it['ratio'] ?? 0,
|
|
|
+ 'ratioType' => $it['ratioType'] ?? 0,
|
|
|
+ 'price' => $it['userPrice'] ?? 0,
|
|
|
+ 'totalPrice' => $it['price'] ?? 0,
|
|
|
+ 'preTotalPrice' => $it['price'] ?? 0,
|
|
|
+ 'variety' => $it['variety'] ?? 0,
|
|
|
+ 'unitWeight' => $it['unitWeight'] ?? 0,
|
|
|
+ 'belongCost' => $it['belongCost'] ?? 0,
|
|
|
+ 'remark' => $it['remark'] ?? '',
|
|
|
+ 'kind' => $it['kind'] ?? 0,
|
|
|
+ 'forward' => 1,
|
|
|
+ ];
|
|
|
+ PurchaseItemClass::add($cgItemData, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ //余额处理:returnBalance==1 加回客户余额;==0 不改 balance,只把 payWay 记在订单上作为返款渠道记录
|
|
|
+ $customBalance = null;
|
|
|
+ if ($returnBalance == 1) {
|
|
|
+ $lockCustom = CustomClass::getLockById($customId);
|
|
|
+ if (empty($lockCustom)) {
|
|
|
+ util::fail('没有找到客户');
|
|
|
+ }
|
|
|
+ $customBalance = CustomClass::forwardReturnBalance($lockCustom, $order, $totalAmount);
|
|
|
+ }
|
|
|
+
|
|
|
+ //关联原单:写关系表 + 同步原单 hasForward/forwardPrice 与订单项 forwardNum
|
|
|
+ if (!empty($relateOrderId) && !empty($relateOrder)) {
|
|
|
+ OrderForwardClass::addRelate($relateOrder, $order, $totalAmount);
|
|
|
+ $relateOrder->hasForward = 1;
|
|
|
+ $relateOrder->forwardPrice = bcadd($relateOrder->forwardPrice ?? 0, $totalAmount, 2);
|
|
|
+ $relateOrder->save(false, ['hasForward', 'forwardPrice']);
|
|
|
+
|
|
|
+ $relateItems = OrderItemClass::getAllByCondition(['orderSn' => $relateOrder->orderSn], null, '*', 'productId', true);
|
|
|
+ foreach ($negItems as $it) {
|
|
|
+ $productId = $it['productId'] ?? 0;
|
|
|
+ $relateItem = $relateItems[$productId] ?? null;
|
|
|
+ if (!empty($relateItem)) {
|
|
|
+ $relateItem->forward = 1;
|
|
|
+ $relateItem->forwardNum = bcadd($relateItem->forwardNum ?? 0, $it['num'] ?? 0, 2);
|
|
|
+ $relateItem->save(false, ['forward', 'forwardNum']);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //同步花店侧原采购单(xhCg)的 hasForward/forwardPrice 与采购单项(xhCgItem)的 forwardNum,
|
|
|
+ //供 hdApp 的 purDetails.vue/refundList.vue 展示冲销记录,与 GHS 侧原单缓存字段保持一致
|
|
|
+ $relateCgId = $relateOrder->purchaseId ?? 0;
|
|
|
+ if (!empty($relateCgId)) {
|
|
|
+ $relateCg = PurchaseClass::getById($relateCgId, true);
|
|
|
+ if (!empty($relateCg)) {
|
|
|
+ $relateCg->hasForward = 1;
|
|
|
+ $relateCg->forwardPrice = bcadd($relateCg->forwardPrice ?? 0, $totalAmount, 2);
|
|
|
+ $relateCg->save(false, ['hasForward', 'forwardPrice']);
|
|
|
+
|
|
|
+ $relateCgItems = PurchaseItemClass::getAllByCondition(['orderSn' => $relateCg->orderSn], null, '*', 'productId', true);
|
|
|
+ foreach ($negItems as $it) {
|
|
|
+ $productId = $it['productId'] ?? 0;
|
|
|
+ $relateCgItem = $relateCgItems[$productId] ?? null;
|
|
|
+ if (!empty($relateCgItem)) {
|
|
|
+ $relateCgItem->forward = 1;
|
|
|
+ $relateCgItem->forwardNum = bcadd($relateCgItem->forwardNum ?? 0, $it['num'] ?? 0, 2);
|
|
|
+ $relateCgItem->save(false, ['forward', 'forwardNum']);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return ['order' => $order, 'customBalance' => $customBalance];
|
|
|
+ }
|
|
|
+
|
|
|
}
|