|
|
@@ -0,0 +1,269 @@
|
|
|
+<?php
|
|
|
+/**
|
|
|
+ * 隔天售后(冲销)辅助服务
|
|
|
+ * 用途:资格校验、资金方式解析、隔天通过后的金额缓存与返余额;不建独立冲销表。
|
|
|
+ * 对应:xhRefund/xhCgRefund.sameDay=0,订单 nextDayTkPrice 累计且不改 actPrice。
|
|
|
+ */
|
|
|
+namespace bizGhs\order\services;
|
|
|
+
|
|
|
+use bizGhs\custom\classes\CustomClass;
|
|
|
+use bizGhs\custom\services\GhsRechargeSettleService;
|
|
|
+use bizGhs\order\classes\OrderClass;
|
|
|
+use bizGhs\order\classes\RefundOrderClass;
|
|
|
+use bizHd\cg\classes\CgRefundClass;
|
|
|
+use bizHd\purchase\classes\PurchaseClass;
|
|
|
+use common\components\dict;
|
|
|
+use common\components\util;
|
|
|
+use Yii;
|
|
|
+
|
|
|
+class NextDayRefundService
|
|
|
+{
|
|
|
+ const SAME_DAY_YES = 1;
|
|
|
+ const SAME_DAY_NO = 0;
|
|
|
+
|
|
|
+ const FUND_UNUSED = 0;
|
|
|
+ const FUND_ORIGINAL = 1;
|
|
|
+ const FUND_BALANCE = 2;
|
|
|
+ const FUND_NONE = 3;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 原单是否可走隔天冲销(即原「冲销」路径):
|
|
|
+ * - 非当天支付;或
|
|
|
+ * - 挂账已结清;或
|
|
|
+ * - 已结账单(当天已结账也按冲销处理,不走普通售后)
|
|
|
+ * @return array{ok:bool,reason:string,sameDay:int}
|
|
|
+ */
|
|
|
+ public static function checkEligible($order)
|
|
|
+ {
|
|
|
+ if (empty($order)) {
|
|
|
+ return ['ok' => false, 'reason' => '没有原订单', 'sameDay' => self::SAME_DAY_YES];
|
|
|
+ }
|
|
|
+ $payTime = $order->payTime ?? ($order->addTime ?? '');
|
|
|
+ $isToday = false;
|
|
|
+ if (!empty($payTime) && $payTime !== '0000-00-00 00:00:00') {
|
|
|
+ $isToday = 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;
|
|
|
+ // 已结账:当天也强制走冲销(sameDay=0),与隔天同一套资金/金额逻辑
|
|
|
+ $orderCleared = !empty($order->clearId);
|
|
|
+
|
|
|
+ if ($isToday && !$debtCleared && !$orderCleared) {
|
|
|
+ return [
|
|
|
+ 'ok' => false,
|
|
|
+ 'reason' => '当天未结账订单请走普通售后;已结账/挂账结清/隔天可冲销',
|
|
|
+ 'sameDay' => self::SAME_DAY_YES,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ return [
|
|
|
+ 'ok' => true,
|
|
|
+ 'reason' => '',
|
|
|
+ 'sameDay' => self::SAME_DAY_NO,
|
|
|
+ 'suggestFundBalance' => true,
|
|
|
+ 'orderCleared' => $orderCleared ? 1 : 0,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建时决定是否必须按隔天冲销:已结账单不可走当天售后。
|
|
|
+ */
|
|
|
+ public static function mustUseNextDay($order)
|
|
|
+ {
|
|
|
+ return !empty($order) && !empty($order->clearId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析隔天资金方式:挂账/余额强制返余额;在线可选原路/返余额/仅记账。
|
|
|
+ */
|
|
|
+ public static function resolveFundType($fundType, $order, $hasRelateOrder = true)
|
|
|
+ {
|
|
|
+ $fundType = intval($fundType);
|
|
|
+ $payWay = intval($order->payWay ?? 0);
|
|
|
+ $onlinePay = intval($order->onlinePay ?? dict::getDict('onlinePay', 'not'));
|
|
|
+ $debtPay = dict::getDict('payWay', 'debtPay');
|
|
|
+ $balancePay = dict::getDict('payWay', 'balancePay');
|
|
|
+ $wxPay = dict::getDict('payWay', 'wxPay');
|
|
|
+ $aliPay = dict::getDict('payWay', 'alipay');
|
|
|
+ $isOnline = $onlinePay == dict::getDict('onlinePay', 'yes') && in_array($payWay, [$wxPay, $aliPay], true);
|
|
|
+
|
|
|
+ if (!$hasRelateOrder) {
|
|
|
+ if ($fundType === self::FUND_ORIGINAL) {
|
|
|
+ util::fail('自由冲销不能原路退回');
|
|
|
+ }
|
|
|
+ if (!in_array($fundType, [self::FUND_BALANCE, self::FUND_NONE], true)) {
|
|
|
+ return self::FUND_BALANCE;
|
|
|
+ }
|
|
|
+ return $fundType;
|
|
|
+ }
|
|
|
+ if ($payWay === $debtPay || $payWay === $balancePay) {
|
|
|
+ return self::FUND_BALANCE;
|
|
|
+ }
|
|
|
+ if ($isOnline) {
|
|
|
+ if (!in_array($fundType, [self::FUND_ORIGINAL, self::FUND_BALANCE, self::FUND_NONE], true)) {
|
|
|
+ util::fail('请选择资金处理方式');
|
|
|
+ }
|
|
|
+ return $fundType;
|
|
|
+ }
|
|
|
+ if (!in_array($fundType, [self::FUND_BALANCE, self::FUND_NONE], true)) {
|
|
|
+ return self::FUND_BALANCE;
|
|
|
+ }
|
|
|
+ return $fundType;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * GHS 侧隔天通过:累计 nextDayTkPrice,按 fundType 处理资金,不改 actPrice。
|
|
|
+ * @return string 返余额后的客户余额(非返余额则为当前余额)
|
|
|
+ */
|
|
|
+ public static function applyGhsAmountAndFund($refund, $order)
|
|
|
+ {
|
|
|
+ $refundPrice = bcadd((string)($refund->refundPrice ?? '0'), '0', 2);
|
|
|
+ $order->nextDayTkPrice = bcadd((string)($order->nextDayTkPrice ?? '0'), $refundPrice, 2);
|
|
|
+ $order->refund = OrderClass::REFUND_YES;
|
|
|
+ $order->save(false, ['nextDayTkPrice', 'refund']);
|
|
|
+
|
|
|
+ $customId = intval($order->customId ?? 0);
|
|
|
+ $custom = CustomClass::getLockById($customId);
|
|
|
+ if (empty($custom)) {
|
|
|
+ util::fail('没有找到客户');
|
|
|
+ }
|
|
|
+ $buyAmount = bcsub((string)($custom->buyAmount ?? '0'), $refundPrice, 2);
|
|
|
+ if (bccomp($buyAmount, '0', 2) < 0) {
|
|
|
+ $buyAmount = '0.00';
|
|
|
+ }
|
|
|
+ $custom->buyAmount = $buyAmount;
|
|
|
+ $custom->save(false, ['buyAmount']);
|
|
|
+
|
|
|
+ $fundType = intval($refund->fundType ?? self::FUND_UNUSED);
|
|
|
+ $balance = bcadd((string)($custom->balance ?? '0'), '0', 2);
|
|
|
+ if ($fundType === self::FUND_BALANCE) {
|
|
|
+ $pair = GhsRechargeSettleService::lockAccountPair($custom);
|
|
|
+ CustomClass::nextDayRefundReturnBalance($pair['custom'], $pair['ghs'], $refund, $order);
|
|
|
+ $custom = CustomClass::getById($customId, true);
|
|
|
+ $balance = bcadd((string)($custom->balance ?? '0'), '0', 2);
|
|
|
+ }
|
|
|
+ // FUND_ORIGINAL:网关退款在采购侧 CgRefundClass 执行;FUND_NONE:仅记账
|
|
|
+ return $balance;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * HD 采购侧隔天通过:累计 nextDayTkPrice;原路退走拉卡拉,不改 actPrice。
|
|
|
+ */
|
|
|
+ public static function applyCgAmountAndFund($cgRefund, $cg)
|
|
|
+ {
|
|
|
+ $refundPrice = bcadd((string)($cgRefund->refundPrice ?? '0'), '0', 2);
|
|
|
+ $cg->nextDayTkPrice = bcadd((string)($cg->nextDayTkPrice ?? '0'), $refundPrice, 2);
|
|
|
+ $cg->refund = PurchaseClass::REFUND_YES;
|
|
|
+ $cg->save(false, ['nextDayTkPrice', 'refund']);
|
|
|
+
|
|
|
+ $fundType = intval($cgRefund->fundType ?? self::FUND_UNUSED);
|
|
|
+ if ($fundType === self::FUND_ORIGINAL) {
|
|
|
+ CgRefundClass::nextDayOriginalOnlineRefund($cgRefund, $cg);
|
|
|
+ }
|
|
|
+
|
|
|
+ $ghsId = intval($cg->ghsId ?? 0);
|
|
|
+ if ($ghsId > 0) {
|
|
|
+ $ghs = \bizHd\ghs\classes\GhsClass::getLockById($ghsId);
|
|
|
+ if (!empty($ghs) && isset($ghs->expendAmount)) {
|
|
|
+ $expend = bcsub((string)($ghs->expendAmount ?? '0'), $refundPrice, 2);
|
|
|
+ if (bccomp($expend, '0', 2) < 0) {
|
|
|
+ $expend = '0.00';
|
|
|
+ }
|
|
|
+ $ghs->expendAmount = $expend;
|
|
|
+ $ghs->save(false, ['expendAmount']);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按时段汇总成功隔天售后金额(收入/销量统计扣减用)
|
|
|
+ */
|
|
|
+ public static function sumAmountByMainAndTime($mainId, $startTime, $endTime)
|
|
|
+ {
|
|
|
+ $sql = "SELECT COALESCE(SUM(refundPrice),0) AS total FROM xhRefund
|
|
|
+ WHERE mainId=:mainId AND status=:status AND sameDay=:sameDay
|
|
|
+ AND passTime BETWEEN :start AND :end";
|
|
|
+ $row = Yii::$app->db->createCommand($sql, [
|
|
|
+ ':mainId' => $mainId,
|
|
|
+ ':status' => RefundOrderClass::STATUS_COMPLETE,
|
|
|
+ ':sameDay' => self::SAME_DAY_NO,
|
|
|
+ ':start' => $startTime,
|
|
|
+ ':end' => $endTime,
|
|
|
+ ])->queryOne();
|
|
|
+ return bcadd($row['total'] ?? '0', '0', 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 渠道收入对冲:拉取隔天成功售后及原单支付字段
|
|
|
+ */
|
|
|
+ public static function listForChannelIncomeDeduct($mainId, $startTime, $endTime)
|
|
|
+ {
|
|
|
+ $sql = "SELECT r.refundPrice AS amount, r.fundType, o.id AS orderId,
|
|
|
+ o.debtPrice, o.remainDebtPrice, o.onlinePay, o.payWay AS orderPayWay, o.payWay
|
|
|
+ FROM xhRefund r
|
|
|
+ LEFT JOIN xhGhsOrder o ON o.orderSn = r.relateOrderSn
|
|
|
+ WHERE r.mainId=:mainId AND r.status=:status AND r.sameDay=:sameDay
|
|
|
+ AND IFNULL(NULLIF(r.passTime,'0000-00-00 00:00:00'), r.addTime) BETWEEN :start AND :end";
|
|
|
+ $rows = Yii::$app->db->createCommand($sql, [
|
|
|
+ ':mainId' => $mainId,
|
|
|
+ ':status' => RefundOrderClass::STATUS_COMPLETE,
|
|
|
+ ':sameDay' => self::SAME_DAY_NO,
|
|
|
+ ':start' => $startTime,
|
|
|
+ ':end' => $endTime,
|
|
|
+ ])->queryAll();
|
|
|
+ return $rows ?: [];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按客户汇总隔天退款金额
|
|
|
+ */
|
|
|
+ public static function sumAmountGroupByCustom($mainId, $startTime, $endTime)
|
|
|
+ {
|
|
|
+ $sql = "SELECT r.customId, COALESCE(SUM(r.refundPrice),0) AS total FROM xhRefund r
|
|
|
+ WHERE r.mainId=:mainId AND r.status=:status AND r.sameDay=:sameDay
|
|
|
+ AND IFNULL(NULLIF(r.passTime,'0000-00-00 00:00:00'), r.addTime) BETWEEN :start AND :end
|
|
|
+ GROUP BY r.customId";
|
|
|
+ $rows = Yii::$app->db->createCommand($sql, [
|
|
|
+ ':mainId' => $mainId,
|
|
|
+ ':status' => RefundOrderClass::STATUS_COMPLETE,
|
|
|
+ ':sameDay' => self::SAME_DAY_NO,
|
|
|
+ ':start' => $startTime,
|
|
|
+ ':end' => $endTime,
|
|
|
+ ])->queryAll();
|
|
|
+ $map = [];
|
|
|
+ foreach ($rows as $row) {
|
|
|
+ $map[$row['customId']] = bcadd($row['total'] ?? '0', '0', 2);
|
|
|
+ }
|
|
|
+ return $map;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按花材汇总隔天退货数量与金额(退货退款明细)
|
|
|
+ */
|
|
|
+ public static function sumItemByProduct($mainId, $startTime, $endTime)
|
|
|
+ {
|
|
|
+ $sql = "SELECT i.productId, COALESCE(SUM(i.xhNum),0) AS num, COALESCE(SUM(i.xhPrice),0) AS amount
|
|
|
+ FROM xhRefundItem i
|
|
|
+ INNER JOIN xhRefund r ON r.orderSn = i.orderSn
|
|
|
+ WHERE r.mainId=:mainId AND r.status=:status AND r.sameDay=:sameDay
|
|
|
+ AND r.refundType=:rtype
|
|
|
+ AND IFNULL(NULLIF(r.passTime,'0000-00-00 00:00:00'), r.addTime) BETWEEN :start AND :end
|
|
|
+ GROUP BY i.productId";
|
|
|
+ $rows = Yii::$app->db->createCommand($sql, [
|
|
|
+ ':mainId' => $mainId,
|
|
|
+ ':status' => RefundOrderClass::STATUS_COMPLETE,
|
|
|
+ ':sameDay' => self::SAME_DAY_NO,
|
|
|
+ ':rtype' => RefundOrderClass::REFUND_TYPE_MONEY_GOOD,
|
|
|
+ ':start' => $startTime,
|
|
|
+ ':end' => $endTime,
|
|
|
+ ])->queryAll();
|
|
|
+ $map = [];
|
|
|
+ foreach ($rows as $row) {
|
|
|
+ $map[$row['productId']] = [
|
|
|
+ 'num' => bcadd($row['num'] ?? '0', '0', 2),
|
|
|
+ 'amount' => bcadd($row['amount'] ?? '0', '0', 2),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ return $map;
|
|
|
+ }
|
|
|
+}
|