|
|
@@ -0,0 +1,701 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace bizGhs\forward\services;
|
|
|
+
|
|
|
+use biz\ghs\classes\GhsClass;
|
|
|
+use bizGhs\base\services\BaseService;
|
|
|
+use bizGhs\custom\classes\CustomClass;
|
|
|
+use bizGhs\forward\classes\ForwardClass;
|
|
|
+use bizGhs\forward\classes\ForwardItemClass;
|
|
|
+use bizGhs\order\classes\OrderClass;
|
|
|
+use bizGhs\order\classes\OrderItemClass;
|
|
|
+use bizGhs\order\classes\RefundOrderClass;
|
|
|
+use bizGhs\order\classes\RefundOrderItemClass;
|
|
|
+use bizGhs\product\classes\ProductClass;
|
|
|
+use bizGhs\stock\classes\StockRecordClass;
|
|
|
+use bizHd\cg\classes\CgForwardClass;
|
|
|
+use bizHd\cg\classes\CgForwardItemClass;
|
|
|
+use bizHd\cg\classes\CgRefundClass;
|
|
|
+use bizHd\purchase\classes\PurchaseClass;
|
|
|
+use bizHd\purchase\classes\PurchaseItemClass;
|
|
|
+use bizHd\shop\classes\ShopClass as HdShopClass;
|
|
|
+use common\components\dict;
|
|
|
+use common\components\orderSn;
|
|
|
+use common\components\util;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 冲销凭证服务(方案A)
|
|
|
+ * 职责:同事务创建双侧凭证/明细,处理资金三分支、库存回退、原单汇总缓存、售后转凭证取消;
|
|
|
+ * 不插入负销售单/负采购单。ssh 冲销单功能
|
|
|
+ */
|
|
|
+class ForwardService extends BaseService
|
|
|
+{
|
|
|
+ public static $baseFile = '\bizGhs\forward\classes\ForwardClass';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建冲销凭证(唯一写入口)
|
|
|
+ * @param array $post 含 orderId/refundId/customId、fundType、forwardStock、items、门店操作人等
|
|
|
+ * @return array {forwardId, forwardSn, cgForwardId, fundType, amount, customId}
|
|
|
+ */
|
|
|
+ public static function create($post)
|
|
|
+ {
|
|
|
+ $orderId = intval($post['orderId'] ?? 0);
|
|
|
+ $refundId = intval($post['refundId'] ?? 0);
|
|
|
+ $customId = intval($post['customId'] ?? 0);
|
|
|
+ $fundType = intval($post['fundType'] ?? 0);
|
|
|
+ $forwardStock = intval($post['forwardStock'] ?? ForwardClass::STOCK_KEEP);
|
|
|
+ $remark = trim((string)($post['remark'] ?? ''));
|
|
|
+ $now = date('Y-m-d H:i:s');
|
|
|
+
|
|
|
+ $order = null;
|
|
|
+ $cg = null;
|
|
|
+ $refund = null;
|
|
|
+ $cgRefund = null;
|
|
|
+ $items = $post['items'] ?? [];
|
|
|
+
|
|
|
+ // 售后转冲销:以售后明细为准,并锁原单
|
|
|
+ if ($refundId > 0) {
|
|
|
+ $refund = RefundOrderClass::getLockById($refundId);
|
|
|
+ if (empty($refund)) {
|
|
|
+ util::fail('没有找到售后单');
|
|
|
+ }
|
|
|
+ if (intval($refund->status) !== RefundOrderClass::STATUS_UN_COMPLETE) {
|
|
|
+ util::fail('仅待审核售后可转冲销');
|
|
|
+ }
|
|
|
+ if (!empty($refund->forwardId)) {
|
|
|
+ util::fail('该售后已转冲销');
|
|
|
+ }
|
|
|
+ $relateOrderSn = $refund->relateOrderSn ?? '';
|
|
|
+ $orderRow = OrderClass::getByCondition(['orderSn' => $relateOrderSn], true);
|
|
|
+ if (empty($orderRow)) {
|
|
|
+ util::fail('没有找到原订单');
|
|
|
+ }
|
|
|
+ $order = OrderClass::getLockById($orderRow->id);
|
|
|
+ if (empty($order)) {
|
|
|
+ util::fail('没有找到原订单');
|
|
|
+ }
|
|
|
+ $orderId = intval($order->id);
|
|
|
+ $customId = intval($order->customId ?? 0);
|
|
|
+ $items = self::buildItemsFromRefund($refund);
|
|
|
+ $post['amount'] = $refund->refundPrice ?? ($post['amount'] ?? 0);
|
|
|
+ $cgRefundId = intval($refund->cgRefundId ?? 0);
|
|
|
+ if ($cgRefundId > 0) {
|
|
|
+ $cgRefund = CgRefundClass::getLockById($cgRefundId);
|
|
|
+ }
|
|
|
+ } elseif ($orderId > 0) {
|
|
|
+ $order = OrderClass::getLockById($orderId);
|
|
|
+ if (empty($order)) {
|
|
|
+ util::fail('没有找到原订单');
|
|
|
+ }
|
|
|
+ $customId = intval($order->customId ?? 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($customId <= 0) {
|
|
|
+ util::fail('请选择客户');
|
|
|
+ }
|
|
|
+ $custom = CustomClass::getLockById($customId);
|
|
|
+ if (empty($custom)) {
|
|
|
+ util::fail('没有找到客户');
|
|
|
+ }
|
|
|
+ $mainId = intval($post['mainId'] ?? ($order->mainId ?? ($custom->ownMainId ?? 0)));
|
|
|
+ if (intval($custom->ownMainId ?? 0) !== $mainId) {
|
|
|
+ util::fail('不是你的客户');
|
|
|
+ }
|
|
|
+
|
|
|
+ $hasRelate = !empty($order) || $refundId > 0;
|
|
|
+ if ($hasRelate) {
|
|
|
+ self::assertEligible($order);
|
|
|
+ self::assertOrderCanForward($order, $mainId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析明细与金额(仅退款无花材明细时允许空明细,仍记金额)
|
|
|
+ $normalizedItems = self::normalizeItems($items, $order);
|
|
|
+ $amount = bcadd((string)($post['amount'] ?? '0'), '0', 2);
|
|
|
+ if (bccomp($amount, '0', 2) <= 0) {
|
|
|
+ $amount = '0.00';
|
|
|
+ foreach ($normalizedItems as $it) {
|
|
|
+ $amount = bcadd($amount, $it['amount'], 2);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (bccomp($amount, '0', 2) <= 0) {
|
|
|
+ util::fail('冲销金额必须大于0');
|
|
|
+ }
|
|
|
+ if (empty($normalizedItems) && $refundId <= 0) {
|
|
|
+ util::fail('请选择冲销花材');
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!empty($order)) {
|
|
|
+ $forwarded = bcadd((string)($order->forwardPrice ?? '0'), '0', 2);
|
|
|
+ $cap = bcadd($forwarded, $amount, 2);
|
|
|
+ $actPrice = bcadd((string)($order->actPrice ?? '0'), '0', 2);
|
|
|
+ if (bccomp($cap, $actPrice, 2) === 1) {
|
|
|
+ util::fail('累计冲销金额不能超过订单实付金额');
|
|
|
+ }
|
|
|
+ $purchaseId = intval($order->purchaseId ?? 0);
|
|
|
+ if ($purchaseId > 0) {
|
|
|
+ $cg = PurchaseClass::getLockById($purchaseId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $payWay = !empty($order) ? intval($order->payWay ?? 0) : intval($post['payWay'] ?? dict::getDict('payWay', 'balancePay'));
|
|
|
+ $fundType = self::resolveFundType($fundType, $payWay, $order, $hasRelate);
|
|
|
+
|
|
|
+ $forwardSn = orderSn::getGhsForwardSn();
|
|
|
+ $cgForwardSn = orderSn::getCgForwardSn();
|
|
|
+ $ghsId = intval($custom->ghsId ?? 0);
|
|
|
+ $shopId = intval($post['shopId'] ?? ($order->shopId ?? ($custom->ownShopId ?? 0)));
|
|
|
+ $sjId = intval($post['sjId'] ?? ($order->sjId ?? ($custom->sjId ?? 0)));
|
|
|
+ $shopAdminId = intval($post['shopAdminId'] ?? 0);
|
|
|
+ $shopAdminName = (string)($post['shopAdminName'] ?? '');
|
|
|
+
|
|
|
+ // 花店侧主体/门店
|
|
|
+ $hdShopId = intval($custom->shopId ?? 0);
|
|
|
+ $hdShop = HdShopClass::getById($hdShopId, true);
|
|
|
+ $hdMainId = intval($hdShop->mainId ?? 0);
|
|
|
+ $hdSjId = intval($hdShop->sjId ?? ($custom->sjId ?? 0));
|
|
|
+
|
|
|
+ // 处理中写 GHS 凭证
|
|
|
+ $ghsForward = ForwardClass::add([
|
|
|
+ 'forwardSn' => $forwardSn,
|
|
|
+ 'mainId' => $mainId,
|
|
|
+ 'shopId' => $shopId,
|
|
|
+ 'sjId' => $sjId,
|
|
|
+ 'customId' => $customId,
|
|
|
+ 'ghsId' => $ghsId,
|
|
|
+ 'orderId' => $orderId,
|
|
|
+ 'orderSn' => $order->orderSn ?? '',
|
|
|
+ 'amount' => $amount,
|
|
|
+ 'payWay' => $payWay,
|
|
|
+ 'fundType' => $fundType,
|
|
|
+ 'forwardStock' => $forwardStock,
|
|
|
+ 'cgForwardId' => 0,
|
|
|
+ 'refundId' => $refundId,
|
|
|
+ 'thirdRefundNo' => '',
|
|
|
+ 'status' => ForwardClass::STATUS_PROCESSING,
|
|
|
+ 'remark' => $remark,
|
|
|
+ 'shopAdminId' => $shopAdminId,
|
|
|
+ 'shopAdminName' => $shopAdminName,
|
|
|
+ 'addTime' => $now,
|
|
|
+ ], true);
|
|
|
+
|
|
|
+ foreach ($normalizedItems as $it) {
|
|
|
+ ForwardItemClass::add([
|
|
|
+ 'forwardId' => $ghsForward->id,
|
|
|
+ 'forwardSn' => $forwardSn,
|
|
|
+ 'mainId' => $mainId,
|
|
|
+ 'customId' => $customId,
|
|
|
+ 'productId' => $it['productId'],
|
|
|
+ 'orderItemId' => $it['orderItemId'],
|
|
|
+ 'name' => $it['name'],
|
|
|
+ 'num' => $it['num'],
|
|
|
+ 'bigNum' => $it['bigNum'],
|
|
|
+ 'smallNum' => $it['smallNum'],
|
|
|
+ 'unitPrice' => $it['unitPrice'],
|
|
|
+ 'price' => $it['price'],
|
|
|
+ 'amount' => $it['amount'],
|
|
|
+ 'addTime' => $now,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // HD 镜像凭证
|
|
|
+ $cgForward = CgForwardClass::add([
|
|
|
+ 'forwardSn' => $cgForwardSn,
|
|
|
+ 'mainId' => $hdMainId,
|
|
|
+ 'shopId' => $hdShopId,
|
|
|
+ 'sjId' => $hdSjId,
|
|
|
+ 'customId' => $customId,
|
|
|
+ 'ghsId' => $ghsId,
|
|
|
+ 'cgId' => intval($cg->id ?? 0),
|
|
|
+ 'cgSn' => $cg->orderSn ?? '',
|
|
|
+ 'ghsForwardId' => $ghsForward->id,
|
|
|
+ 'amount' => $amount,
|
|
|
+ 'payWay' => $payWay,
|
|
|
+ 'fundType' => $fundType,
|
|
|
+ 'forwardStock' => $forwardStock,
|
|
|
+ 'cgRefundId' => intval($cgRefund->id ?? 0),
|
|
|
+ 'status' => CgForwardClass::STATUS_PROCESSING,
|
|
|
+ 'remark' => $remark,
|
|
|
+ 'addTime' => $now,
|
|
|
+ ], true);
|
|
|
+
|
|
|
+ // 互挂 id
|
|
|
+ $ghsForward->cgForwardId = $cgForward->id;
|
|
|
+ $ghsForward->save(false, ['cgForwardId']);
|
|
|
+
|
|
|
+ $cgItemMap = [];
|
|
|
+ if (!empty($cg)) {
|
|
|
+ $cgItems = PurchaseItemClass::getAllByCondition(['orderSn' => $cg->orderSn], null, '*', null, true);
|
|
|
+ foreach ($cgItems ?: [] as $ci) {
|
|
|
+ $cgItemMap[intval($ci->itemId ?? 0)] = $ci;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ foreach ($normalizedItems as $it) {
|
|
|
+ $cgItemId = 0;
|
|
|
+ $ptItemId = intval($it['itemId'] ?? 0);
|
|
|
+ if ($ptItemId > 0 && isset($cgItemMap[$ptItemId])) {
|
|
|
+ $cgItemId = intval($cgItemMap[$ptItemId]->id ?? 0);
|
|
|
+ }
|
|
|
+ CgForwardItemClass::add([
|
|
|
+ 'forwardId' => $cgForward->id,
|
|
|
+ 'forwardSn' => $cgForwardSn,
|
|
|
+ 'ghsId' => $ghsId,
|
|
|
+ 'customId' => $customId,
|
|
|
+ 'productId' => $it['productId'],
|
|
|
+ 'cgItemId' => $cgItemId,
|
|
|
+ 'name' => $it['name'],
|
|
|
+ 'num' => $it['num'],
|
|
|
+ 'bigNum' => $it['bigNum'],
|
|
|
+ 'smallNum' => $it['smallNum'],
|
|
|
+ 'unitPrice' => $it['unitPrice'],
|
|
|
+ 'price' => $it['price'],
|
|
|
+ 'amount' => $it['amount'],
|
|
|
+ 'addTime' => $now,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 资金分支(原单 actPrice/tkPrice 不动)
|
|
|
+ $thirdRefundNo = '';
|
|
|
+ if ($fundType === ForwardClass::FUND_ORIGINAL) {
|
|
|
+ if (empty($cg)) {
|
|
|
+ util::fail('无原采购单,无法原路退回');
|
|
|
+ }
|
|
|
+ $thirdRefundNo = PurchaseClass::forwardOriginalOnlineRefund($cg, $forwardSn, $amount, $remark ?: '冲销原路退回');
|
|
|
+ $ghsForward->thirdRefundNo = $thirdRefundNo;
|
|
|
+ $ghsForward->save(false, ['thirdRefundNo']);
|
|
|
+ } elseif ($fundType === ForwardClass::FUND_BALANCE) {
|
|
|
+ CustomClass::forwardReturnBalance($custom, $ghsForward, $amount);
|
|
|
+ }
|
|
|
+ // FUND_NONE:仅记账,不动作
|
|
|
+
|
|
|
+ // 库存:仅 GHS 仓,与资金解耦
|
|
|
+ if ($forwardStock === ForwardClass::STOCK_RETURN) {
|
|
|
+ self::returnStock($normalizedItems, $forwardSn, $custom, $shopId, $sjId, $mainId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 原单/采购汇总缓存
|
|
|
+ if (!empty($order)) {
|
|
|
+ $order->hasForward = 1;
|
|
|
+ $order->forwardPrice = bcadd((string)($order->forwardPrice ?? '0'), $amount, 2);
|
|
|
+ $order->save(false, ['hasForward', 'forwardPrice']);
|
|
|
+ self::increaseOrderItemForwardNum($normalizedItems);
|
|
|
+ }
|
|
|
+ if (!empty($cg)) {
|
|
|
+ $cg->hasForward = 1;
|
|
|
+ $cg->forwardPrice = bcadd((string)($cg->forwardPrice ?? '0'), $amount, 2);
|
|
|
+ $cg->save(false, ['hasForward', 'forwardPrice']);
|
|
|
+ self::increaseCgItemForwardNum($normalizedItems, $cgItemMap);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 消费累计:减金额,不加笔数
|
|
|
+ $buyAmount = bcsub((string)($custom->buyAmount ?? '0'), $amount, 2);
|
|
|
+ if (bccomp($buyAmount, '0', 2) < 0) {
|
|
|
+ $buyAmount = '0.00';
|
|
|
+ }
|
|
|
+ $custom->buyAmount = $buyAmount;
|
|
|
+ $custom->save(false, ['buyAmount']);
|
|
|
+ if ($ghsId > 0) {
|
|
|
+ $ghs = GhsClass::getLockById($ghsId);
|
|
|
+ if (!empty($ghs) && isset($ghs->expendAmount)) {
|
|
|
+ $expend = bcsub((string)($ghs->expendAmount ?? '0'), $amount, 2);
|
|
|
+ if (bccomp($expend, '0', 2) < 0) {
|
|
|
+ $expend = '0.00';
|
|
|
+ }
|
|
|
+ $ghs->expendAmount = $expend;
|
|
|
+ $ghs->save(false, ['expendAmount']);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 售后转冲销:取消两侧售后并回写 forwardId(不走 passRefund)
|
|
|
+ if (!empty($refund)) {
|
|
|
+ $refund->status = RefundOrderClass::STATUS_CANCEL;
|
|
|
+ $refund->forwardId = $ghsForward->id;
|
|
|
+ $refund->remark = trim(($refund->remark ?? '') . ';已转冲销凭证' . $forwardSn, ';');
|
|
|
+ $refund->save(false, ['status', 'forwardId', 'remark']);
|
|
|
+ if (!empty($cgRefund)) {
|
|
|
+ $cgRefund->status = CgRefundClass::STATUS_CANCEL;
|
|
|
+ $cgRefund->forwardId = $cgForward->id;
|
|
|
+ $cgRefund->remark = trim(($cgRefund->remark ?? '') . ';已转冲销凭证' . $cgForwardSn, ';');
|
|
|
+ $cgRefund->save(false, ['status', 'forwardId', 'remark']);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $ghsForward->status = ForwardClass::STATUS_SUCCESS;
|
|
|
+ $ghsForward->save(false, ['status']);
|
|
|
+ $cgForward->status = CgForwardClass::STATUS_SUCCESS;
|
|
|
+ $cgForward->save(false, ['status']);
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'forwardId' => $ghsForward->id,
|
|
|
+ 'forwardSn' => $forwardSn,
|
|
|
+ 'cgForwardId' => $cgForward->id,
|
|
|
+ 'fundType' => $fundType,
|
|
|
+ 'amount' => $amount,
|
|
|
+ 'customId' => $customId,
|
|
|
+ 'thirdRefundNo' => $thirdRefundNo,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 有原单时的冲销资格:非当天支付,或挂账已结清(debtPrice>0 且 remainDebtPrice=0)
|
|
|
+ */
|
|
|
+ public static function assertEligible($order)
|
|
|
+ {
|
|
|
+ if (empty($order)) {
|
|
|
+ util::fail('没有原订单');
|
|
|
+ }
|
|
|
+ $payTime = $order->payTime ?? ($order->addTime ?? '');
|
|
|
+ $notToday = true;
|
|
|
+ if (!empty($payTime)) {
|
|
|
+ $notToday = date('Y-m-d', strtotime($payTime)) !== date('Y-m-d');
|
|
|
+ }
|
|
|
+ $debtPrice = bcadd((string)($order->debtPrice ?? '0'), '0', 2);
|
|
|
+ $remainDebt = bcadd((string)($order->remainDebtPrice ?? '0'), '0', 2);
|
|
|
+ $debtCleared = bccomp($debtPrice, '0', 2) > 0 && bccomp($remainDebt, '0', 2) === 0;
|
|
|
+ if (!$notToday && !$debtCleared) {
|
|
|
+ util::fail('当天订单请走常规售后,不可冲销');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 前端资格预检(不调用 util::fail,供详情页展示按钮)
|
|
|
+ */
|
|
|
+ public static function checkEligible($order)
|
|
|
+ {
|
|
|
+ if (empty($order)) {
|
|
|
+ return ['ok' => false, 'reason' => '没有原订单'];
|
|
|
+ }
|
|
|
+ $payTime = $order->payTime ?? ($order->addTime ?? '');
|
|
|
+ $notToday = true;
|
|
|
+ if (!empty($payTime)) {
|
|
|
+ $notToday = date('Y-m-d', strtotime($payTime)) !== date('Y-m-d');
|
|
|
+ }
|
|
|
+ $debtPrice = bcadd((string)($order->debtPrice ?? '0'), '0', 2);
|
|
|
+ $remainDebt = bcadd((string)($order->remainDebtPrice ?? '0'), '0', 2);
|
|
|
+ $debtCleared = bccomp($debtPrice, '0', 2) > 0 && bccomp($remainDebt, '0', 2) === 0;
|
|
|
+ if (!$notToday && !$debtCleared) {
|
|
|
+ return ['ok' => false, 'reason' => '当天订单请走常规售后,不可冲销'];
|
|
|
+ }
|
|
|
+ return ['ok' => true, 'reason' => ''];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 原单基础校验:归属、状态、结账
|
|
|
+ */
|
|
|
+ protected static function assertOrderCanForward($order, $mainId)
|
|
|
+ {
|
|
|
+ if (intval($order->mainId ?? 0) !== intval($mainId)) {
|
|
|
+ util::fail('不是你的订单');
|
|
|
+ }
|
|
|
+ if (intval($order->status) === OrderClass::ORDER_STATUS_CANCEL) {
|
|
|
+ util::fail('已取消订单不能冲销');
|
|
|
+ }
|
|
|
+ if (intval($order->status) !== OrderClass::ORDER_STATUS_COMPLETE) {
|
|
|
+ util::fail('仅已完成订单可冲销');
|
|
|
+ }
|
|
|
+ if (!empty($order->clearId)) {
|
|
|
+ util::fail('订单已结账,请线下处理');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按支付方式强制/校正 fundType
|
|
|
+ * 线上:允许原路退或返余额(互斥,由入参决定);挂账/余额强制返余额;无原单禁止原路退
|
|
|
+ */
|
|
|
+ protected static function resolveFundType($fundType, $payWay, $order, $hasRelate)
|
|
|
+ {
|
|
|
+ $wxPay = dict::getDict('payWay', 'wxPay');
|
|
|
+ $aliPay = dict::getDict('payWay', 'alipay');
|
|
|
+ $debtPay = dict::getDict('payWay', 'debtPay');
|
|
|
+ $balancePay = dict::getDict('payWay', 'balancePay');
|
|
|
+ $onlinePay = !empty($order) ? intval($order->onlinePay ?? 0) : 0;
|
|
|
+ $isOnline = $onlinePay == dict::getDict('onlinePay', 'yes') && in_array($payWay, [$wxPay, $aliPay], true);
|
|
|
+
|
|
|
+ if (!$hasRelate) {
|
|
|
+ // 自由冲销:仅返余额或仅记账
|
|
|
+ if ($fundType === ForwardClass::FUND_ORIGINAL) {
|
|
|
+ util::fail('自由冲销不能原路退回');
|
|
|
+ }
|
|
|
+ if (!in_array($fundType, [ForwardClass::FUND_BALANCE, ForwardClass::FUND_NONE], true)) {
|
|
|
+ $fundType = ForwardClass::FUND_BALANCE;
|
|
|
+ }
|
|
|
+ return $fundType;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($payWay === $debtPay || $payWay === $balancePay) {
|
|
|
+ return ForwardClass::FUND_BALANCE;
|
|
|
+ }
|
|
|
+ if ($isOnline) {
|
|
|
+ if (!in_array($fundType, [ForwardClass::FUND_ORIGINAL, ForwardClass::FUND_BALANCE, ForwardClass::FUND_NONE], true)) {
|
|
|
+ util::fail('请选择资金处理方式');
|
|
|
+ }
|
|
|
+ return $fundType;
|
|
|
+ }
|
|
|
+ // 现金等其它:默认返余额,也允许仅记账
|
|
|
+ if (!in_array($fundType, [ForwardClass::FUND_BALANCE, ForwardClass::FUND_NONE], true)) {
|
|
|
+ return ForwardClass::FUND_BALANCE;
|
|
|
+ }
|
|
|
+ return $fundType;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从售后明细生成冲销明细
|
|
|
+ */
|
|
|
+ protected static function buildItemsFromRefund($refund)
|
|
|
+ {
|
|
|
+ $refundSn = $refund->orderSn ?? '';
|
|
|
+ $list = RefundOrderItemClass::getAllByCondition(['orderSn' => $refundSn], null, '*', null, true);
|
|
|
+ if (empty($list)) {
|
|
|
+ // 仅退款无明细时,用金额做一条空花材记账(仍要求前端传 items 更稳妥)
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ $items = [];
|
|
|
+ $unitSmall = dict::getDict('unitType', 'small');
|
|
|
+ foreach ($list as $row) {
|
|
|
+ $unitType = intval($row->xhUnitType ?? 0);
|
|
|
+ $xhNum = bcadd((string)($row->xhNum ?? '0'), '0', 2);
|
|
|
+ $itemNum = bcadd((string)($row->itemNum ?? '0'), '0', 2);
|
|
|
+ $bigNum = $unitType === $unitSmall ? '0' : $xhNum;
|
|
|
+ $smallNum = $unitType === $unitSmall ? $xhNum : '0';
|
|
|
+ $items[] = [
|
|
|
+ 'productId' => intval($row->productId ?? 0),
|
|
|
+ 'orderItemId' => intval($row->orderItemId ?? 0),
|
|
|
+ 'itemId' => intval($row->itemId ?? 0),
|
|
|
+ 'name' => (string)($row->name ?? ''),
|
|
|
+ 'num' => $itemNum,
|
|
|
+ 'bigNum' => $bigNum,
|
|
|
+ 'smallNum' => $smallNum,
|
|
|
+ 'unitPrice' => bcadd((string)($row->xhUnitPrice ?? '0'), '0', 2),
|
|
|
+ 'price' => bcadd((string)($row->xhUnitPrice ?? '0'), '0', 2),
|
|
|
+ 'amount' => bcadd((string)($row->xhPrice ?? '0'), '0', 2),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ return $items;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 规范化明细:补全名称/数量/金额,并校验原单明细上限
|
|
|
+ */
|
|
|
+ protected static function normalizeItems($items, $order)
|
|
|
+ {
|
|
|
+ if (empty($items) || !is_array($items)) {
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ $orderItemMap = [];
|
|
|
+ if (!empty($order)) {
|
|
|
+ $orderItems = OrderItemClass::getAllByCondition(['orderSn' => $order->orderSn], null, '*', null, true);
|
|
|
+ foreach ($orderItems ?: [] as $oi) {
|
|
|
+ $orderItemMap[intval($oi->id)] = $oi;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $result = [];
|
|
|
+ foreach ($items as $raw) {
|
|
|
+ $productId = intval($raw['productId'] ?? 0);
|
|
|
+ $orderItemId = intval($raw['orderItemId'] ?? 0);
|
|
|
+ $orderItem = $orderItemId > 0 && isset($orderItemMap[$orderItemId]) ? $orderItemMap[$orderItemId] : null;
|
|
|
+ if (!empty($orderItem)) {
|
|
|
+ $productId = intval($orderItem->productId ?? $productId);
|
|
|
+ }
|
|
|
+ if ($productId <= 0) {
|
|
|
+ util::fail('花材信息缺失');
|
|
|
+ }
|
|
|
+ $name = (string)($raw['name'] ?? ($orderItem->name ?? ''));
|
|
|
+ if ($name === '') {
|
|
|
+ $product = ProductClass::getById($productId, true);
|
|
|
+ $name = $product->name ?? '';
|
|
|
+ }
|
|
|
+ $bigNum = bcadd((string)($raw['bigNum'] ?? '0'), '0', 2);
|
|
|
+ $smallNum = bcadd((string)($raw['smallNum'] ?? '0'), '0', 2);
|
|
|
+ $ratio = bcadd((string)($orderItem->ratio ?? ($raw['ratio'] ?? '1')), '0', 2);
|
|
|
+ if (bccomp($ratio, '0', 2) <= 0) {
|
|
|
+ $ratio = '1';
|
|
|
+ }
|
|
|
+ // 有大小单位时按售后口径换算 itemNum;否则用入参 num
|
|
|
+ if (bccomp($bigNum, '0', 2) > 0 || bccomp($smallNum, '0', 2) > 0) {
|
|
|
+ $num = ProductClass::mergeItemNum($bigNum, $smallNum, $ratio);
|
|
|
+ } else {
|
|
|
+ $num = bcadd((string)($raw['num'] ?? '0'), '0', 2);
|
|
|
+ }
|
|
|
+ if (bccomp($num, '0', 2) <= 0) {
|
|
|
+ util::fail('冲销数量必须大于0');
|
|
|
+ }
|
|
|
+ $unitPrice = bcadd((string)($raw['unitPrice'] ?? ($raw['price'] ?? '0')), '0', 2);
|
|
|
+ $price = bcadd((string)($raw['price'] ?? $unitPrice), '0', 2);
|
|
|
+ $amount = bcadd((string)($raw['amount'] ?? '0'), '0', 2);
|
|
|
+ if (bccomp($amount, '0', 2) <= 0) {
|
|
|
+ $baseNum = bccomp($bigNum, '0', 2) > 0 ? $bigNum : $num;
|
|
|
+ $amount = bcmul($baseNum, $price, 2);
|
|
|
+ }
|
|
|
+ if (!empty($orderItem)) {
|
|
|
+ $remain = bcsub((string)($orderItem->itemNum ?? '0'), (string)($orderItem->forwardNum ?? '0'), 2);
|
|
|
+ if (bccomp($num, $remain, 2) === 1) {
|
|
|
+ util::fail(($name ?: '花材') . '冲销数量超过可冲销量');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $result[] = [
|
|
|
+ 'productId' => $productId,
|
|
|
+ 'orderItemId' => $orderItemId,
|
|
|
+ 'itemId' => intval($raw['itemId'] ?? ($orderItem->itemId ?? 0)),
|
|
|
+ 'name' => $name,
|
|
|
+ 'num' => $num,
|
|
|
+ 'bigNum' => $bigNum,
|
|
|
+ 'smallNum' => $smallNum,
|
|
|
+ 'unitPrice' => $unitPrice,
|
|
|
+ 'price' => $price,
|
|
|
+ 'amount' => $amount,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ return $result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 回库存并写流水(关联 forwardSn)
|
|
|
+ */
|
|
|
+ protected static function returnStock($items, $forwardSn, $custom, $shopId, $sjId, $mainId)
|
|
|
+ {
|
|
|
+ $productIds = [];
|
|
|
+ foreach ($items as $it) {
|
|
|
+ $productIds[] = intval($it['productId']);
|
|
|
+ }
|
|
|
+ $productIds = array_values(array_unique(array_filter($productIds)));
|
|
|
+ sort($productIds);
|
|
|
+ foreach ($productIds as $pid) {
|
|
|
+ ProductClass::getLockById($pid);
|
|
|
+ }
|
|
|
+ $customName = $custom->name ?? '';
|
|
|
+ foreach ($items as $it) {
|
|
|
+ $itemNum = $it['num'];
|
|
|
+ if (bccomp($itemNum, '0', 2) <= 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $stockInfo = ProductClass::addStockByItemNum($it['productId'], $itemNum);
|
|
|
+ $product = ProductClass::getById($it['productId'], true);
|
|
|
+ $recordData = [
|
|
|
+ 'relateName' => $customName,
|
|
|
+ 'itemNum' => $itemNum,
|
|
|
+ 'sjId' => $sjId,
|
|
|
+ 'shopId' => $shopId,
|
|
|
+ 'mainId' => $mainId,
|
|
|
+ 'orderSn' => $forwardSn,
|
|
|
+ 'itemId' => $product->itemId ?? ($it['itemId'] ?? 0),
|
|
|
+ 'oldStock' => $stockInfo['oldStock'] ?? 0,
|
|
|
+ 'productId' => $it['productId'],
|
|
|
+ 'newStock' => $stockInfo['newStock'] ?? 0,
|
|
|
+ 'io' => 1,
|
|
|
+ ];
|
|
|
+ StockRecordClass::ghsRefundAddRecord($recordData);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 累加原销售明细 forwardNum
|
|
|
+ */
|
|
|
+ protected static function increaseOrderItemForwardNum($items)
|
|
|
+ {
|
|
|
+ foreach ($items as $it) {
|
|
|
+ $orderItemId = intval($it['orderItemId'] ?? 0);
|
|
|
+ if ($orderItemId <= 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $oi = OrderItemClass::getLockById($orderItemId);
|
|
|
+ if (empty($oi)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $oi->forwardNum = bcadd((string)($oi->forwardNum ?? '0'), $it['num'], 2);
|
|
|
+ $oi->save(false, ['forwardNum']);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 累加采购明细 forwardNum(按平台 itemId 对齐)
|
|
|
+ */
|
|
|
+ protected static function increaseCgItemForwardNum($items, $cgItemMap)
|
|
|
+ {
|
|
|
+ foreach ($items as $it) {
|
|
|
+ $ptItemId = intval($it['itemId'] ?? 0);
|
|
|
+ if ($ptItemId <= 0 || !isset($cgItemMap[$ptItemId])) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $ci = $cgItemMap[$ptItemId];
|
|
|
+ $ci = PurchaseItemClass::getLockById($ci->id);
|
|
|
+ if (empty($ci)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $ci->forwardNum = bcadd((string)($ci->forwardNum ?? '0'), $it['num'], 2);
|
|
|
+ $ci->save(false, ['forwardNum']);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按原单查冲销凭证列表(GHS 侧)
|
|
|
+ */
|
|
|
+ public static function listByOrder($orderId, $mainId)
|
|
|
+ {
|
|
|
+ $orderId = intval($orderId);
|
|
|
+ $order = OrderClass::getById($orderId, true);
|
|
|
+ if (empty($order) || intval($order->mainId) !== intval($mainId)) {
|
|
|
+ util::fail('没有找到订单');
|
|
|
+ }
|
|
|
+ $list = ForwardClass::getAllByCondition(
|
|
|
+ ['orderId' => $orderId, 'status' => ForwardClass::STATUS_SUCCESS],
|
|
|
+ 'id DESC',
|
|
|
+ '*',
|
|
|
+ null,
|
|
|
+ true
|
|
|
+ );
|
|
|
+ $rows = [];
|
|
|
+ foreach ($list ?: [] as $row) {
|
|
|
+ $rows[] = self::formatForwardRow($row);
|
|
|
+ }
|
|
|
+ return ['list' => $rows, 'orderId' => $orderId, 'forwardPrice' => $order->forwardPrice ?? '0.00'];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 凭证详情(含明细)
|
|
|
+ */
|
|
|
+ public static function detail($forwardId, $mainId)
|
|
|
+ {
|
|
|
+ $forward = ForwardClass::getById($forwardId, true);
|
|
|
+ if (empty($forward) || intval($forward->mainId) !== intval($mainId)) {
|
|
|
+ util::fail('没有找到冲销凭证');
|
|
|
+ }
|
|
|
+ $items = ForwardItemClass::getAllByCondition(['forwardId' => $forwardId], null, '*', null, true);
|
|
|
+ $itemRows = [];
|
|
|
+ foreach ($items ?: [] as $it) {
|
|
|
+ $itemRows[] = [
|
|
|
+ 'id' => $it->id,
|
|
|
+ 'productId' => $it->productId,
|
|
|
+ 'name' => $it->name,
|
|
|
+ 'num' => $it->num,
|
|
|
+ 'bigNum' => $it->bigNum,
|
|
|
+ 'smallNum' => $it->smallNum,
|
|
|
+ 'unitPrice' => $it->unitPrice,
|
|
|
+ 'price' => $it->price,
|
|
|
+ 'amount' => $it->amount,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ $data = self::formatForwardRow($forward);
|
|
|
+ $data['items'] = $itemRows;
|
|
|
+ return $data;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected static function formatForwardRow($row)
|
|
|
+ {
|
|
|
+ return [
|
|
|
+ 'id' => $row->id,
|
|
|
+ 'forwardSn' => $row->forwardSn,
|
|
|
+ 'orderId' => $row->orderId,
|
|
|
+ 'orderSn' => $row->orderSn,
|
|
|
+ 'customId' => $row->customId,
|
|
|
+ 'amount' => $row->amount,
|
|
|
+ 'payWay' => $row->payWay,
|
|
|
+ 'fundType' => $row->fundType,
|
|
|
+ 'forwardStock' => $row->forwardStock,
|
|
|
+ 'status' => $row->status,
|
|
|
+ 'remark' => $row->remark,
|
|
|
+ 'shopAdminName' => $row->shopAdminName,
|
|
|
+ 'thirdRefundNo' => $row->thirdRefundNo,
|
|
|
+ 'addTime' => $row->addTime,
|
|
|
+ 'cgForwardId' => $row->cgForwardId,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+}
|