StoreCashierOrder.php 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297
  1. <?php
  2. namespace addons\Store\common\models;
  3. use addons\Coupon\common\enums\AddonsCouponEnum;
  4. use addons\Coupon\common\models\AddonsCoupon;
  5. use addons\Coupon\common\models\AddonsCouponUserRelate;
  6. use addons\ShoppingLottery\common\helper\LotteryHelper;
  7. use addons\Store\common\enums\StoreEnum;
  8. use addons\Store\common\events\order\CashierOrderPaidEvent;
  9. use addons\Store\common\services\StoreSettingService;
  10. use common\components\export\CsvTool;
  11. use common\enums\AddonsEnum;
  12. use common\enums\DownloadEnum;
  13. use common\enums\OrderStatusEnum;
  14. use common\enums\RedisKeyEnum;
  15. use common\enums\StatusEnum;
  16. use common\enums\UserWalletEnum;
  17. use common\helpers\LockHelper;
  18. use common\helpers\StringHelper;
  19. use common\models\base\BaseActiveRecord;
  20. use common\models\common\Download;
  21. use common\models\common\ScoreLog;
  22. use common\models\user\User;
  23. use common\models\user\UserPartitionRelationship;
  24. use common\models\user\UserWallet;
  25. use services\common\UserWalletService;
  26. use common\models\mall\MallAddons;
  27. use Yii;
  28. use Exception;
  29. use common\models\common\PaymentTrade;
  30. use common\models\common\PaymentTradeOrder;
  31. use common\models\order\Order;
  32. use common\globals\GlobalInvoke;
  33. use common\enums\PayTypeEnum;
  34. use addons\CardCoupons\common\models\CardCouponsScoreGive;
  35. use common\helpers\ArrayHelper;
  36. use common\enums\RabbitMqEnum;
  37. use addons\Store\common\models\StoreCouponUserRelate;
  38. /**
  39. * This is the model class for table "qimall_addons_store_cashier_order".
  40. *
  41. * @property int $id
  42. * @property int $mall_id
  43. * @property int $store_id 门店id
  44. * @property int $user_id 会员id
  45. * @property string $order_no 订单编号
  46. * @property int $pay_time 付款时间
  47. * @property int $pay_status 支付状态
  48. * @property float $money 应付金额
  49. * @property float $pay_money 实付金额
  50. * @property int|null $payment_type 支付类型
  51. * @property float $amount_received 实收金额
  52. * @property float $service_money 服务费
  53. * @property string $remark 备注
  54. * @property string $extra 额外信息:例如优惠信息,渠道分红,经销分红
  55. * @property int $status 状态[0禁用 1启用 -1删除]
  56. * @property int $created_at 创建时间
  57. * @property int $updated_at 修改时间
  58. */
  59. class StoreCashierOrder extends BaseActiveRecord
  60. {
  61. /**
  62. * {@inheritdoc}
  63. */
  64. public static function tableName()
  65. {
  66. return '{{%addons_store_cashier_order}}';
  67. }
  68. /**
  69. * {@inheritdoc}
  70. */
  71. public function rules()
  72. {
  73. return [
  74. [['mall_id', 'store_id', 'user_id', 'pay_status', 'pay_time', 'payment_type', 'status', 'created_at', 'updated_at'], 'integer'],
  75. [['money', 'pay_money', 'amount_received', 'service_money'], 'number'],
  76. [['order_no'], 'string', 'max' => 100],
  77. [['remark', 'extra'], 'string', 'max' => 255],
  78. ];
  79. }
  80. /**
  81. * {@inheritdoc}
  82. */
  83. public function attributeLabels()
  84. {
  85. return [
  86. 'id' => 'ID',
  87. 'mall_id' => 'Mall ID',
  88. 'store_id' => '门店id',
  89. 'user_id' => '会员id',
  90. 'order_no' => '订单编号',
  91. 'pay_status' => '支付状态',
  92. 'pay_time' => '付款时间',
  93. 'money' => '应付金额',
  94. 'pay_money' => '实付金额',
  95. 'payment_type' => '支付类型',
  96. 'amount_received' => '实收金额',
  97. 'service_money' => '服务费',
  98. 'remark' => '备注',
  99. 'extra' => '额外信息:例如优惠信息,渠道分红,经销分红',
  100. 'status' => '状态[0禁用 1启用 -1删除]',
  101. 'created_at' => '创建时间',
  102. 'updated_at' => '修改时间',
  103. ];
  104. }
  105. public static function fieldsList($is_true=false)
  106. {
  107. $data= [
  108. 'user_id' => '会员ID',
  109. 'nickname' => '昵称',
  110. 'pay_time' => '付款时间',
  111. 'money' => '应付金额',
  112. 'pay_money' => '实付金额',
  113. 'extra' => '优惠信息',
  114. 'amount_received' => '实收金额',
  115. 'remark' => '备注',
  116. ];
  117. if($is_true){
  118. $data['store_name'] = '门店名称';
  119. }
  120. return $data;
  121. }
  122. public function getStore()
  123. {
  124. return $this->hasOne(Store::class, ['id' => 'store_id']);
  125. }
  126. /**
  127. * 异步导出至下载中心
  128. * @DateTime 2021-11-12 16:01:25
  129. * @param array $data
  130. * @copyright: Copyright (c) 2020 广东七件事集团
  131. */
  132. public static function exportHandle(array $data)
  133. {
  134. $download_id = $data['download_id'] ?? 0;
  135. $download = Download::findOne(['id' => $download_id]);
  136. if ($download) {
  137. try {
  138. $params = json_decode($download->params, true);
  139. $filename = $download->name;
  140. $type = $download->type;
  141. $model = self::find();
  142. $model->where(['mall_id' => $params['mall_id']]);
  143. $model->andWhere(['store_id' => $params['store_id']]);
  144. if (!empty($params['start'])) $model->andWhere(['>=', 'pay_time', strtotime($params['start'])]);
  145. if (!empty($params['end'])) $model->andWhere(['<=', 'pay_time', strtotime($params['end'])]);
  146. if (!empty($params['min'])) $model->andWhere(['>=', 'pay_money', $params['min']]);
  147. if (!empty($params['max'])) $model->andWhere(['<=', 'pay_money', $params['max']]);
  148. if (!empty($params['keyword'])) {
  149. $user_list = User::lists(['where' => [['mall_id' => $params['mall_id']], [
  150. 'or',
  151. ['like', 'username', $params['keyword']],
  152. ['like', 'nickname', $params['keyword']],
  153. ]], 'select' => 'id']);
  154. $user_ids = array_column($user_list, 'id');
  155. $model->andWhere(['in', 'user_id', $user_ids]);
  156. }
  157. $model->with(['user']);
  158. $fields = $params['fields'];
  159. $fields_arr = self::fieldsList();
  160. $have_select_field_arr = [];
  161. foreach ($fields as $value) {
  162. $have_select_field_arr[] = $fields_arr[$value];
  163. }
  164. $csv = new CsvTool();
  165. $csv->filename = $filename;
  166. $csv->file_dirname = DownloadEnum::getTypeStorageDirMap($type);
  167. $csv->export_mode = CsvTool::EXPORT_MODE_ASYNC;
  168. $csv->header = $have_select_field_arr;
  169. foreach ($model->batch() as $orders) {
  170. // $users 是一个包含100条或小于100条用户表数据的数组
  171. empty($csv->header) && $csv->header = array_keys($orders[0]);
  172. $csv->data = array_map(function ($order) use ($fields) {
  173. $row = [];
  174. foreach ($fields as $field) {
  175. if ($field == 'nickname') {
  176. $row[] = $order['user']['nickname'];
  177. } else if ($field == 'extra') {
  178. $extra = json_decode($order['extra'], true);
  179. $extra_content = '';
  180. if ($extra) {
  181. foreach ($extra as $k => $v) {
  182. if ($v) {
  183. switch ($k) {
  184. case "discount_money":
  185. $extra_content .= ' 优惠:' . $v;
  186. break;
  187. case "agent_commission":
  188. $extra_content .= ' 渠道分红:' . $v;
  189. break;
  190. case "distribution_commission":
  191. $extra_content .= ' 分销分红:' . $v;
  192. break;
  193. }
  194. }
  195. }
  196. }
  197. $row[] = $extra_content;
  198. } else if ($field == 'pay_time') {
  199. $row[] = !empty($order['pay_time']) ? date('Y-m-d H:i:s', $order['pay_time']) : '';
  200. } else {
  201. if ((isset($order[$field]))) $row[] = $order[$field];
  202. }
  203. }
  204. return $row;
  205. }, $orders);
  206. $csv->export();
  207. }
  208. $url = $csv->upload($params['mall_id'], $csv->relative_filename);
  209. $download->status = 2;
  210. $download->url = $url;
  211. $download->num = $csv->line_num;
  212. $download->size = $csv->file_size;
  213. $res = $download->save();
  214. return true;
  215. } catch (\PHPUnit\Util\Exception $e) {
  216. $download->status = 3;
  217. self::setStaticError($e->getMessage());
  218. return false;
  219. }
  220. }
  221. }
  222. public function getUser()
  223. {
  224. return $this->hasOne(User::class, ['id' => 'user_id']);
  225. }
  226. public function getDetail()
  227. {
  228. return $this->hasMany(StoreCashierOrderDetail::class, ['cashier_order_id' => 'id']);
  229. }
  230. public static function setData(array $params)
  231. {
  232. try {
  233. if (!empty($params['id'])) {
  234. $model = self::findOne(['id' => $params['id'], 'status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]]);
  235. if (empty($model)) throw new \Exception('商品不存在或已删除');
  236. } else {
  237. $model = new self();
  238. $model->loadDefaultValues();
  239. }
  240. if (!$model->load($params, '')) throw new \Exception($model->getErrorMessage());
  241. if (!$model->save()) throw new \Exception($model->getErrorMessage());
  242. return $model;
  243. } catch (\Exception $e) {
  244. self::$static_error = $e->getMessage();
  245. return false;
  246. }
  247. }
  248. public static function preview($params)
  249. {
  250. $marketing = [];
  251. $extra = StoreCashierExtra::findOne(['mall_id' => $params['mall_id'], 'store_id' => $params['store_id'], 'status' => StatusEnum::ENABLED]);
  252. $deduct = [];
  253. $dis_pay_money = $params['money'];
  254. $deduct['coupon']['is_can_use'] = 0;
  255. $deduct['score']['is_can_use'] = 0;
  256. $cashier = \Yii::$app->services->setting->addons->get($params['mall_id'], StoreEnum::ADDONS_NAME, 'cashier');
  257. $members = [];
  258. if(!empty($cashier['member_status'])){
  259. $members = json_decode($cashier['members'], true);
  260. }
  261. $store_setting = StoreSettings::findOne(['mall_id' => $params['mall_id'], 'store_id' => $params['store_id'],'status'=>StatusEnum::ENABLED]);
  262. if(!empty($store_setting['cashier_member_status'])){
  263. $members = json_decode($store_setting['cashier_member_settings'], true);
  264. }
  265. if(!empty($members)&&is_array($members)){
  266. $members = array_column($members,null,'level');
  267. }
  268. list($discount_money,$discount) = self::computeMemberPrice($members,$dis_pay_money,$params['user']['level']);
  269. $deduct['member_price'] = ['money'=>-$discount_money, 'name'=>'会员优惠'];
  270. if (!empty($extra['preferential_deduction'])) {
  271. $score_deduct_setting = json_decode($extra['score_deduct_setting'], true);
  272. switch ($score_deduct_setting['type']) {
  273. case 1:
  274. case 2:
  275. $deduct['coupon']['is_can_use'] = 1;
  276. $coupon_list = self::getCanUseCouponList( $params['user_id'], $extra,$dis_pay_money);
  277. $deduct['coupon']['list'] = $coupon_list;
  278. break;
  279. case 3:
  280. break;
  281. }
  282. }
  283. if (MallAddons::isInstall($params['mall_id'], AddonsEnum::ADDONS_NAME_MEMBERS_CARD)) {
  284. $members_card_obj = new \addons\MembersCard\common\logic\order\MemberCardMarketing();
  285. $deduct['member_card']['list'] = $members_card_obj::getMemberCardList($params['user_id'],$params['mall_id']);
  286. }
  287. if (!empty($params['user_coupon_id'])) {
  288. $deduct_money = 0;
  289. self::useCouponDiscount($params['user_id'], $params['user_coupon_id'], $params['money'], $dis_pay_money, $deduct_money);
  290. $deduct['coupon']['discount_money'] = -$deduct_money;
  291. }
  292. $score_name = \Yii::$app->services->setting->mall->get($params['mall_id'], 'score.score_name');
  293. if(empty($score_name)) $score_name = '积分';
  294. if (isset($params['user_card_id']) && !empty($params['user_card_id']) && !empty($params['user_card_money'])) {
  295. $deduct_money = 0;
  296. $members_card_obj = new \addons\MembersCard\common\logic\order\MemberCardMarketing();
  297. $members_card_obj::storeCashierCompute($params['mall_id'],$params['user_id'], $params['user_card_id'], $dis_pay_money, $deduct_money,$params['user_card_money']);
  298. if($deduct_money){
  299. $deduct['member_card']['name'] = '折扣价';
  300. $deduct['member_card']['discount_money'] = -$deduct_money;
  301. }
  302. }
  303. $platform_basic_score_deduction = \Yii::$app->services->setting->addons->get($params['mall_id'], StoreEnum::ADDONS_NAME, 'payment.score_deduction');
  304. $platform_basic_score_deduction = is_array($platform_basic_score_deduction) ? $platform_basic_score_deduction : json_decode($platform_basic_score_deduction, true);
  305. $store_settings_row = StoreSettings::getOne([['mall_id' => $params['mall_id']], ['store_id' => $params['store_id']], ['status' => StatusEnum::ENABLED]], true);
  306. !empty($store_settings_row['score_deduction']) && $store_setting = json_decode($store_settings_row['score_deduction'], true);
  307. $effective = !empty($store_setting['is_alone_score_deduction']) && !empty($store_setting['effective']) ? $store_setting['effective'] : $platform_basic_score_deduction['effective'];
  308. if (!empty($platform_basic_score_deduction['is_score_deduction']) && !empty($platform_basic_score_deduction['score_balance_ratio']) && !empty($store_setting['is_score_deduction']) && $effective != 3) {
  309. $deduct['score']['is_can_use'] = 0;
  310. if (!empty($platform_basic_score_deduction['deduction_type'])) {
  311. // 判断平台是否为门店设置了最高抵扣比例,没有则走全局的
  312. $deduction_ratio =
  313. !empty($platform_basic_score_deduction['store_limit_type']) && isset($platform_basic_score_deduction['store_limit_stores_deduction'][$params['store_id']]) && is_numeric($platform_basic_score_deduction['store_limit_stores_deduction'][$params['store_id']]) ?
  314. $platform_basic_score_deduction['store_limit_stores_deduction'][$params['store_id']] : $platform_basic_score_deduction['max_deduction_percent'];
  315. // 判断门店独立设置最高抵扣比例
  316. isset($store_setting) &&
  317. isset($store_setting['max_deduction_percent']) &&
  318. !empty($store_setting['is_alone_score_deduction']) &&
  319. is_numeric($store_setting['max_deduction_percent']) &&
  320. $deduction_ratio = $store_setting['max_deduction_percent'];
  321. }else{
  322. $store_setting_service_obj = (new StoreSettingService(['mall_id' => $params['mall_id']]));
  323. $store_score_deduction = $store_setting_service_obj->getDeductionProportion(['store_id' => [$params['store_id']], 'order_type' => 'store_cashier_order']);
  324. if (!empty($store_score_deduction[$params['store_id']])){
  325. $deduction_ratio = $store_setting_service_obj->getDeductionRatio($dis_pay_money, $store_score_deduction[$params['store_id']]);//积分抵扣比例
  326. }else{
  327. $deduction_ratio = 0;
  328. }
  329. }
  330. $temp_dis_pay_money = $dis_pay_money;
  331. $deduct_money = 0;
  332. $deduct_num_score = self::scoreDeduction($params['mall_id'], $params['user_id'], $deduction_ratio, $dis_pay_money, $deduct_money,$deduct['score']['is_can_use']);
  333. $deduct['score']['money'] = -$deduct_money;
  334. $deduct['score']['name'] = $score_name;
  335. $deduct['score']['deduct_val'] = $deduct_num_score;
  336. if (empty($params['is_use_score'])) {
  337. $dis_pay_money = $temp_dis_pay_money;
  338. }
  339. }
  340. $marketing['deduct'] = $deduct;
  341. $pay_money = $dis_pay_money;
  342. list($total_credit_money,$credit_rate) = self::getCredit($params['store_id'],$pay_money);
  343. //赊账
  344. $marketing['credit'] = ['name' => '赊账', 'total_credit_money' => $total_credit_money,'total_credit_rate'=>$credit_rate.'%'];
  345. if(!empty($params['is_use_credit'])) $pay_money = bcsub($pay_money,$total_credit_money,2);
  346. $pay_money = bcmul($pay_money,1,2);
  347. return ['pay_money' => $pay_money, 'marketing' => $marketing];
  348. }
  349. //获取赊账信息
  350. protected static function getCredit($store_id,$pay_money){
  351. $store = Store::findOne(['id'=>$store_id]);
  352. $credit_money = $credit_rate = 0;
  353. if(!empty($store['is_open_credit'])){
  354. $extra = StoreCashierExtra::findOne(['store_id'=>$store_id]);
  355. if(!empty($extra['credit_rate'])){
  356. $credit_rate = $extra['credit_rate'];
  357. $credit_money = bcdiv($pay_money*$credit_rate,100,2);
  358. }
  359. }
  360. return [$credit_money,$credit_rate];
  361. }
  362. //获取能使用的优惠券
  363. protected static function getCanUseCouponList($user_id, $extra,$pay_money)
  364. {
  365. $list = [];
  366. if (!empty($extra['preferential_deduction'])) {
  367. $score_deduct_setting = json_decode($extra['score_deduct_setting'], true);
  368. switch ($score_deduct_setting['type']) {
  369. case 1:
  370. case 2:
  371. $coupon_id = $score_deduct_setting['coupon_id'];
  372. $cond = [
  373. 'where' => [
  374. ['coupon_id' => $coupon_id],
  375. ['user_id' => $user_id],
  376. ['status' => StatusEnum::ENABLED],
  377. ['is_giving' => 0],
  378. ['is_force_failure' => 0],
  379. ['is_use' => 0],
  380. ['<=', 'start_time', time()],
  381. ['>=', 'end_time', time()],
  382. ['coupon_status' => 2],
  383. ['giving_status' => [0, 1]],
  384. ],
  385. 'select' => 'id',
  386. ];
  387. $coupon = AddonsCoupon::getOne([['id' => $coupon_id],['<=','min_price',$pay_money]], true);
  388. $user_coupon_list = StoreCouponUserRelate::lists($cond);
  389. if ($user_coupon_list && $coupon) {
  390. foreach ($user_coupon_list as $item) {
  391. $coupon['user_coupon_id'] = $item['id'];
  392. $list[] = $coupon;
  393. }
  394. }
  395. break;
  396. case 3:
  397. break;
  398. }
  399. }
  400. return $list;
  401. }
  402. //计算使用优惠券后的金额
  403. protected static function useCouponDiscount($user_id, $user_coupon_id, $pay_money, &$dis_pay_money, &$deduct_money)
  404. {
  405. if (!empty($user_coupon_id)) {
  406. $cond = [
  407. ['id' => $user_coupon_id],
  408. ['user_id' => $user_id],
  409. ['status' => StatusEnum::ENABLED],
  410. ['is_giving' => 0],
  411. ['is_force_failure' => 0],
  412. ['is_use' => 0],
  413. ['<=', 'start_time', time()],
  414. ['>=', 'end_time', time()],
  415. ['coupon_status' => 2],
  416. ['giving_status' => [0, 1]],
  417. ];
  418. $user_coupon = StoreCouponUserRelate::getOne($cond);
  419. if (!empty($user_coupon)) {
  420. $coupon = AddonsCoupon::getOne([['id' => $user_coupon['coupon_id']]], true);
  421. if ($coupon) {
  422. if (!empty($coupon['is_original_use']) && $pay_money != $dis_pay_money) throw new Exception('原价支付才能使用优惠券:' . $coupon['name']);
  423. //最低消费
  424. if (!empty($coupon['min_price']) && $dis_pay_money < $coupon['min_price']) throw new Exception('没有达到最低支付金额');
  425. //折扣
  426. if ($coupon['type'] == AddonsCouponEnum::DISCOUNT) {
  427. $discount = sprintf('%.2f', $dis_pay_money * $coupon['discount'] / 10); //折扣
  428. if ($coupon['discount_limit'] != '0.00' && $discount > $coupon['discount_limit']) {
  429. $discount = $coupon['discount_limit'];
  430. }
  431. if ($dis_pay_money > $discount) {
  432. $deduct_money = $dis_pay_money - $discount;
  433. $dis_pay_money -= $discount;
  434. } else {
  435. $deduct_money = $dis_pay_money;
  436. $dis_pay_money = 0;
  437. }
  438. }
  439. //满减
  440. if ($coupon['type'] == AddonsCouponEnum::FULL_MINUS) {
  441. $sub_price = $coupon['sub_price'];
  442. if ($dis_pay_money > $sub_price) {
  443. $deduct_money = $sub_price;
  444. $dis_pay_money -= $sub_price;
  445. } else {
  446. $deduct_money = $dis_pay_money;
  447. $dis_pay_money = 0;
  448. }
  449. }
  450. }
  451. }
  452. }
  453. }
  454. protected static function useScoreDiscount($mall_id, $user_id, $extra, &$dis_pay_money, &$deduct_money)
  455. {
  456. $deduct_num_score = 0;
  457. if (!empty($extra['preferential_deduction'])) {
  458. $score_deduct_setting = json_decode($extra['score_deduct_setting'], true);
  459. switch ($score_deduct_setting['type']) {
  460. case 1:
  461. case 2:
  462. break;
  463. case 3:
  464. $deduct_type = $score_deduct_setting['score_deduct']['deduct_type'];
  465. $deduct_num = $score_deduct_setting['score_deduct']['deduct_num'];
  466. if ($deduct_type == 2) $deduct_num = $dis_pay_money * $deduct_num / 100;
  467. $user_score = UserWallet::getOne([['user_id' => $user_id]], false, [], false, 'score');
  468. if ($user_score->score < $deduct_num) {
  469. $deduct_num = $user_score->score;
  470. }
  471. //计算积分==多少元
  472. $deduct_setting = \Yii::$app->services->setting->mall->get($mall_id, 'score');
  473. //金额需要的积分数量
  474. $max_num = bcmul($dis_pay_money, $deduct_setting['score_deduct_ratio'], 2);
  475. if ($deduct_num > $max_num) $deduct_num = $max_num;
  476. $exc_money = 0;
  477. !empty($deduct_setting['score_deduct_ratio']) && $exc_money = bcdiv($deduct_num, $deduct_setting['score_deduct_ratio'], 2);
  478. if ($dis_pay_money > $exc_money) {
  479. $dis_pay_money -= $exc_money;
  480. $deduct_money += $exc_money;
  481. } else {
  482. $deduct_money += $dis_pay_money;
  483. $dis_pay_money = 0;
  484. }
  485. $deduct_num_score = $deduct_num;
  486. break;
  487. }
  488. }
  489. return $deduct_num_score;
  490. }
  491. protected static function scoreDeduction($mall_id, $user_id,$deduction_ratio, &$dis_pay_money, &$deduct_money,&$is_can_use=0)
  492. {
  493. $platform_basic_score_deduction = \Yii::$app->services->setting->addons->get($mall_id, StoreEnum::ADDONS_NAME, 'payment.score_deduction');
  494. $platform_basic_score_deduction = json_decode($platform_basic_score_deduction, true);
  495. $score_balance_ratio = $platform_basic_score_deduction['score_balance_ratio'];//N积分抵1元
  496. if(empty($score_balance_ratio)){
  497. return 0;
  498. }
  499. $exc_money = bcmul($dis_pay_money, $deduction_ratio / 100, 2);
  500. $deduct_num = bcmul($exc_money,$score_balance_ratio,2);
  501. $user_score = UserWallet::getOne([['user_id' => $user_id]], false, [], false, 'score');
  502. if($user_score['score']==0){
  503. $is_can_use = 0;
  504. return 0;
  505. }
  506. if ($user_score['score'] < $deduct_num) {
  507. //积分不足的情况
  508. $deduct_num = $user_score['score'];
  509. $exc_money = bcdiv($deduct_num, $score_balance_ratio , 2);
  510. }
  511. $temp_money = bcdiv($deduct_num, $score_balance_ratio, 2);//会员积分能抵扣多少元
  512. $dis_pay_money = bcsub($dis_pay_money,$exc_money,2);
  513. $deduct_money = $temp_money;
  514. $deduct_money > 0 && $deduct_num > 0 && $is_can_use = 1;
  515. return $deduct_num;
  516. }
  517. //扫码后请求
  518. public static function scanCode($params)
  519. {
  520. try {
  521. $add_user_data = [
  522. 'mall_id' => $params['mall_id'],
  523. 'user_id' => $params['user_id'],
  524. 'store_id' => $params['store_id'],
  525. ];
  526. $res = StoreUser:: setData($add_user_data);
  527. if ($res == false) throw new \Exception('添加店铺会员失败');
  528. $store = Store::getOne([['id' => $params['store_id']]]);
  529. if (empty($params['user']['parent_id']) && !empty($store['scan_code_bind'])) {
  530. $user_id = $params['user_id'];
  531. $parent_id = $store['user_id'];
  532. $param = [
  533. 'user_id' => $user_id,
  534. 'parent_id' => $parent_id,
  535. ];
  536. $parent = User::getOne([['id' => $parent_id]]);
  537. $user = $params['user'];
  538. if($user['id']!=$parent_id){
  539. $parent_id_arr = UserPartitionRelationship::getParentIdArr($store['user_id']);
  540. if(!in_array($user['id'],$parent_id_arr)){
  541. $user->parent_id = $parent_id;
  542. $user->second_parent_id = $parent['parent_id'] ?? 0;
  543. $user->third_parent_id = $parent['second_parent_id'] ?? 0;
  544. $res = $user->save();
  545. if ($res === false) throw new $user->getErrorMessage();
  546. // $res = UserPartitionRelationship::setData($param);
  547. $param['is_admin'] = 0;
  548. $param['ip'] = ArrayHelper::get_client_ip();
  549. $param['operator_user_id'] = $param['user_id'];
  550. $res = \Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK_CHANGE_RELATIONSHIP, RabbitMqEnum::CHANGE_RELATIONSHIP_QUEUE, $param, UserPartitionRelationship::class, 'changeRelationship');
  551. if ($res === false) throw new \PHPUnit\Util\Exception(UserPartitionRelationship::getStaticError());
  552. }
  553. }
  554. }
  555. $marketing = [];
  556. $extra = StoreCashierExtra::findOne(['mall_id' => $params['mall_id'], 'store_id' => $params['store_id']]);
  557. $deduct = [];
  558. if (!empty($extra['preferential_deduction'])) {
  559. $score_deduct_setting = json_decode($extra['score_deduct_setting'], true);
  560. $deduct['score']['is_can_use'] = 0;
  561. $deduct['score']['name'] = '积分抵扣';
  562. $deduct['coupon']['is_can_use'] = 0;
  563. $deduct['coupon']['name'] = '优惠券抵扣';
  564. switch ($score_deduct_setting['type']) {
  565. case 1:
  566. case 2:
  567. $deduct['coupon']['is_can_use'] = 1;
  568. $coupon_list = self::getCanUseCouponList( $params['user_id'], $extra,0);
  569. $deduct['coupon']['list'] = $coupon_list;
  570. break;
  571. case 3:
  572. $deduct['score']['is_can_use'] = 1;
  573. break;
  574. }
  575. }
  576. $setting = StoreSettings::getOne([['mall_id' => $params['mall_id']], ['store_id' => $params['store_id']], ['status' => StatusEnum::ENABLED]], false, [], false, 'cashier_goods_complete_direct,cashier_is_open_discount');
  577. $cashier_goods_complete_direct = $setting['cashier_goods_complete_direct'] ?? 0;
  578. $cashier_is_open_discount = $setting['cashier_is_open_discount'] ?? 0;
  579. $marketing['deduct'] = $deduct;
  580. $advertising = \Yii::$app->services->setting->addons->get($params['mall_id'], StoreEnum::ADDONS_NAME, 'advertising');
  581. if(!isset($advertising)){
  582. $advertising= ['is_open'=>0, 'pic'=>'', 'url'=>''];
  583. }
  584. return ['id' => $store['id'], 'store_name' => $store['store_name'], 'logo_img' => $store['logo_img'],'marketing'=>$marketing, 'cashier_goods_complete_direct' => $cashier_goods_complete_direct,
  585. 'cashier_is_open_discount'=>$cashier_is_open_discount, 'advertising' => $advertising];
  586. } catch (\Exception $e) {
  587. self::$static_error = $e->getMessage();
  588. return false;
  589. }
  590. }
  591. //生成收银台订单
  592. public static function createOrder($params)
  593. {
  594. $t = Yii::$app->db->beginTransaction();
  595. try {
  596. $extra = StoreCashierExtra::getOne([['mall_id' => $params['mall_id']], ['store_id' => $params['store_id']]],true);
  597. $dis_pay_money = $params['money'];
  598. $extra_desc = [];
  599. $user_coupon_id = 0;
  600. $cashier = \Yii::$app->services->setting->addons->get($params['mall_id'], StoreEnum::ADDONS_NAME, 'cashier');
  601. $members = [];
  602. if(!empty($cashier['member_status'])){
  603. $members = json_decode($cashier['members'], true);
  604. }
  605. $deduct_money = 0;
  606. $store_setting = StoreSettings::findOne(['mall_id' => $params['mall_id'], 'store_id' => $params['store_id'],'status'=>StatusEnum::ENABLED]);
  607. if(!empty($store_setting['cashier_member_status'])){
  608. $members = json_decode($store_setting['cashier_member_settings'], true);
  609. }
  610. if(!empty($members)&&is_array($members)){
  611. $members = array_column($members,null,'level');
  612. }
  613. list($member_deduct_money,$discount) = self::computeMemberPrice($members,$dis_pay_money,$params['user']['level']);
  614. if (!empty($params['user_coupon_id']) && !empty($extra['preferential_deduction'])) {
  615. self::useCouponDiscount($params['user_id'], $params['user_coupon_id'], $params['money'], $dis_pay_money, $deduct_money);
  616. if($deduct_money>0){
  617. $user_coupon_id = $params['user_coupon_id'];
  618. $extra_desc['discount_money'] = $deduct_money;
  619. $extra_desc['user_coupon_id'] = $user_coupon_id;
  620. }
  621. }
  622. $deduct_num_score = 0;
  623. if (!empty($params['is_use_score'])) {
  624. $platform_basic_score_deduction = \Yii::$app->services->setting->addons->get($params['mall_id'], StoreEnum::ADDONS_NAME, 'payment.score_deduction');
  625. $platform_basic_score_deduction = is_array($platform_basic_score_deduction) ? $platform_basic_score_deduction : json_decode($platform_basic_score_deduction, true);
  626. $store_settings_row = StoreSettings::getOne([['mall_id' => $params['mall_id']], ['store_id' => $params['store_id']], ['status' => StatusEnum::ENABLED]], true);
  627. !empty($store_settings_row['score_deduction']) && $store_setting = json_decode($store_settings_row['score_deduction'], true);
  628. $effective = !empty($store_setting['is_alone_score_deduction']) && !empty($store_setting['effective']) ? $store_setting['effective'] : $platform_basic_score_deduction['effective'];
  629. if (!empty($platform_basic_score_deduction['is_score_deduction']) && !empty($platform_basic_score_deduction['score_balance_ratio']) && !empty($store_setting['is_score_deduction']) && $effective != 3) {
  630. if (!empty($platform_basic_score_deduction['deduction_type'])) {
  631. // 判断平台是否为门店设置了最高抵扣比例,没有则走全局的
  632. $deduction_ratio =
  633. !empty($platform_basic_score_deduction['store_limit_type']) && isset($platform_basic_score_deduction['store_limit_stores_deduction'][$params['store_id']]) && is_numeric($platform_basic_score_deduction['store_limit_stores_deduction'][$params['store_id']]) ?
  634. $platform_basic_score_deduction['store_limit_stores_deduction'][$params['store_id']] : $platform_basic_score_deduction['max_deduction_percent'];
  635. // 判断门店独立设置最高抵扣比例
  636. isset($store_setting) &&
  637. isset($store_setting['max_deduction_percent']) &&
  638. !empty($store_setting['is_alone_score_deduction']) &&
  639. is_numeric($store_setting['max_deduction_percent']) &&
  640. $deduction_ratio = $store_setting['max_deduction_percent'];
  641. }else{
  642. $store_setting_service_obj = (new StoreSettingService(['mall_id' => $params['mall_id']]));
  643. $store_score_deduction = $store_setting_service_obj->getDeductionProportion(['store_id' => [$params['store_id']], 'order_type' => 'store_cashier_order']);
  644. if (!empty($store_score_deduction[$params['store_id']])){
  645. $deduction_ratio = $store_setting_service_obj->getDeductionRatio($dis_pay_money, $store_score_deduction[$params['store_id']]);//积分抵扣比例
  646. }else{
  647. $deduction_ratio = 0;
  648. }
  649. }
  650. $deduct_num_score = self::scoreDeduction($params['mall_id'], $params['user_id'], $deduction_ratio, $dis_pay_money, $deduct_money);
  651. if($deduct_money>0){
  652. $extra_desc['discount_money'] = $deduct_money;
  653. $extra_desc['deduct_num_score'] = $deduct_num_score;
  654. }
  655. }
  656. }
  657. if (isset($params['user_card_id']) && !empty($params['user_card_id']) && !empty($params['user_card_money'])) {
  658. $deduct_money = 0;
  659. $members_card_obj = new \addons\MembersCard\common\logic\order\MemberCardMarketing();
  660. $members_card_obj::storeCashierCompute($params['mall_id'],$params['user_id'], $params['user_card_id'], $dis_pay_money, $deduct_money,$params['user_card_money']);
  661. if($deduct_money > 0){
  662. $extra_desc['user_card_id'] = [
  663. 'card_give_id' => $params['user_card_id'],
  664. 'deduct_money' => $deduct_money,
  665. 'user_card_money' => $params['user_card_money'],
  666. ];
  667. }
  668. }
  669. $extra_desc['member_deduct_money'] = $member_deduct_money;
  670. $extra_desc['discount'] = $discount;
  671. $extra_desc['user_level'] = $params['user']['level'];
  672. $params['pay_money'] = $dis_pay_money;
  673. $service_money = self:: computeServiceFee($params);
  674. $params['amount_received'] = $params['money'] - $service_money;
  675. $params['service_money'] = $service_money;
  676. $params['order_no'] = StringHelper::generateOrderNo('C');
  677. $store = Store::findOne(['id'=>$params['store_id']]);
  678. $give_score_num = self::computeGiveScore($params['mall_id'],$params['store_id'],$store['user_id'],$dis_pay_money,$deduct_num_score);
  679. $extra_desc['give_score'] = $give_score_num;
  680. if (!empty($extra_desc)) $params['extra'] = json_encode($extra_desc);
  681. $store_cashier_order = self::setData($params);
  682. if ($store_cashier_order == false) throw new \Exception(self::getStaticError());
  683. // 处理收银台商品商品
  684. if(isset($params['attr_ids']) && !empty($params['attr_ids'])){
  685. StoreCashierOrderDetail::batchDataSave($params['attr_ids'], [
  686. 'cashier_order_id' => $store_cashier_order['id'], 'user_id' => $params['user_id'], 'mall_id' => $params['mall_id'], 'store_id' => $params['store_id'], 'pay_money' => $params['pay_money']
  687. ]);
  688. }
  689. $pay_money = $params['pay_money'];
  690. //赊账
  691. if(!empty($params['is_use_credit'])){
  692. $store = Store::findOne(['id'=>$params['store_id']]);
  693. if(!empty($store['is_open_credit'])) {
  694. list($total_credit_money, $credit_rate) = self::getCredit($params['store_id'], $params['pay_money']);
  695. if (!empty($total_credit_money)) {
  696. $params['pay_money'] = bcsub($params['pay_money'], $total_credit_money, 2);
  697. $param = [
  698. 'mall_id' => $store_cashier_order['mall_id'],
  699. 'user_id' => $store_cashier_order['user_id'],
  700. 'order_id' => $store_cashier_order['id'],
  701. 'store_id' => $store_cashier_order['store_id'],
  702. 'type' => 2,
  703. 'is_repayment' => 0,
  704. 'down_payments' => $params['pay_money'],
  705. 'credit_money' => $total_credit_money,
  706. ];
  707. $res = StoreOrderCredit::setData($param);
  708. if ($res === false) throw new \Exception(StoreOrderCredit::getStaticError());
  709. }
  710. }
  711. }
  712. $order_trade_info[] = [
  713. 'order_no' => $params['order_no'],
  714. 'amount' => $params['pay_money'],
  715. 'title' => $params['title'],
  716. 'notify_class' => 'addons\Store\common\logic\notify\CashierNotify',
  717. 'order_type' => 'cashier_order',
  718. ];
  719. //生成支付流水
  720. $res = \Yii::$app->services->pay->createTrade($params['mall_id'], $params['user_id'], $order_trade_info);
  721. if ($res == false) throw new \Exception('生成支付流水失败');
  722. $arr['transaction'] = $res;
  723. $arr['pay_success_page'] = \Yii::$app->services->setting->addons->get($params['mall_id'], StoreEnum::ADDONS_NAME, 'payment.pay_after_link');//支付成功后跳转的地址
  724. // 判断是否有安装抽奖插件
  725. if (MallAddons::isInstall($params['mall_id'], AddonsEnum::ADDONS_NAME_SHOPPING_LOTTERY)) {
  726. $arr['pay_success_page'] = LotteryHelper::getJumpUrl($params['mall_id'], $res['trade_no'], StatusEnum::ENABLED);
  727. }
  728. $payment_expire_time = Yii::$app->services->setting->mall->get($params['mall_id'], 'order.payment_expire_time');
  729. $arr['surplus_second'] = 30 * 60; //剩余秒数
  730. if ($payment_expire_time) {
  731. $arr['surplus_second'] = intval($payment_expire_time) * 60; //剩余秒数
  732. }
  733. if(!empty($user_coupon_id)){
  734. $res = StoreCouponUserRelate::updateAll(['is_use'=>1], ['id' => $user_coupon_id]);
  735. if($res === false) throw new Exception(AddonsCouponUserRelate::getStaticError());
  736. }
  737. if (!empty($params['is_use_score'])&&!empty($deduct_num_score)) {
  738. self::subUserScore($params,$store_cashier_order,$deduct_num_score);
  739. }
  740. if($give_score_num>0){
  741. try {
  742. self::subStoreUserScore($params['mall_id'],$store_cashier_order,$store['user_id'],$give_score_num);
  743. }catch (Exception $e){
  744. $extra_desc['give_score'] = 0;
  745. $store_cashier_order->extra == json_encode($extra_desc);
  746. $store_cashier_order->save();
  747. Yii::error('store_cashier_order_id:'.$store_cashier_order['id'].$e->getMessage());
  748. }
  749. }
  750. $t->commit();
  751. return $arr;
  752. } catch (\Exception $e) {
  753. $t->rollBack();
  754. self::$static_error = $e->getMessage();
  755. return false;
  756. }
  757. }
  758. //计算会员价,优惠金额
  759. public static function computeMemberPrice($members,&$money,$level)
  760. {
  761. $discount = 0;
  762. $discount_money = 0;
  763. if(!empty($members[$level])){
  764. $discount = $members[$level]['discount'];
  765. }
  766. if ($discount) $discount_money = bcdiv($money * $discount,100,2);
  767. $money = bcsub($money,$discount_money,2);
  768. if($money<0) $money = 0;
  769. return [$discount_money,$discount];
  770. }
  771. //计算服务费
  772. public static function computeServiceFee($params)
  773. {
  774. $store_settings = StoreSettings::getOne([['store_id' => $params['store_id'], 'status' => StatusEnum::ENABLED]]);
  775. if (!empty($store_settings['cachier_service_ratio'])) {
  776. $service_fee_ratio = $store_settings['cachier_service_ratio'];
  777. } else {
  778. $service_fee_ratio = \Yii::$app->services->setting->addons->get($params['mall_id'], StoreEnum::ADDONS_NAME, 'cashier.service_fee_ratio');
  779. }
  780. $service_money = 0;
  781. if ($service_fee_ratio) {
  782. $service_money =bcdiv($params['pay_money'] * $service_fee_ratio,100,2);
  783. }
  784. return $service_money;
  785. }
  786. //支付收银台订单
  787. public static function pay($params)
  788. {
  789. try {
  790. $trans = Yii::$app->db->beginTransaction();
  791. $model = self::findOne(['id' => $params['id'], 'status' => [StatusEnum::ENABLED]]);
  792. if (empty($model)) throw new \Exception('订单不存在或已删除');
  793. if ($model->pay_status != OrderStatusEnum::NOT_PAY) throw new Exception('订单已经被处理,无法进行支付');
  794. if ($model->pay_status == StatusEnum::ENABLED) throw new Exception('订单已支付,请不要重复支付');
  795. $model->payment_type = $params['pay_type'];
  796. $model->pay_status = StatusEnum::ENABLED;
  797. $model->pay_time = time();
  798. $res = $model->save();
  799. if ($res === false) throw new Exception('修改订单失败:' . $model->getErrorMessage());
  800. StoreOrderCredit::updateAll(['pay_time'=>time()],['order_id'=>$params['id'],'type'=>2]);
  801. StoreCashierOrderDetail::updateAll(['order_status' => OrderStatusEnum::ACCOMPLISH], ['cashier_order_id' => $model['id'], 'order_status' => OrderStatusEnum::NOT_PAY]);
  802. $trans->commit();
  803. // 触发订单支付事件,事务提交完成后触发
  804. $event = new CashierOrderPaidEvent($model['mall_id']);
  805. $order['id'] = $model['id'];
  806. $order['operator_id'] = $params['operator_id'] ?? 0;
  807. $order['operator_name'] = $params['operator_name'] ?? '';
  808. Yii::$app->services->events->dispatch($event, $order, $event->listeners);
  809. return $model;
  810. } catch (Exception $e) {
  811. if (isset($trans)) $trans->rollback();
  812. self::$static_error = $e->getMessage();
  813. return false;
  814. }
  815. }
  816. /**
  817. * 取消订单
  818. * @Author bing
  819. * @DateTime 2021-09-14 15:43:18
  820. * @copyright: Copyright (c) 2020 广东七件事集团
  821. * @param [type] $params
  822. * @return StoreCashierOrder
  823. */
  824. public static function close($params,$force = false){
  825. $trans = Yii::$app->db->beginTransaction();
  826. try{
  827. $cond = ['mall_id' => $params['mall_id'],'status'=>[StatusEnum::ENABLED]];
  828. if(isset($params['id'])){
  829. $cond['id'] = $params['id'];
  830. }else{
  831. $cond['order_no'] = $params['order_no'];
  832. }
  833. isset($params['user_id']) && $cond['user_id'] = $params['user_id'];
  834. $model = self::findOne($cond);
  835. if(empty($model)) throw new Exception('订单不存在或已删除');
  836. // 判断订单是否已经被关闭
  837. if ($model->status == StatusEnum::DELETE) throw new Exception('订单已经关闭');
  838. // 判断是否是未支付的订单
  839. if ($force == false && $model->pay_status != OrderStatusEnum::NOT_PAY) throw new Exception('订单已经被处理');
  840. $model->status = StatusEnum::DELETE;
  841. if (!$model->save()) throw new Exception($model->getErrorMessage());
  842. $extra = json_decode($model['extra'],true);
  843. //退回优惠券
  844. if(!empty($extra['user_coupon_id'])){
  845. $res = AddonsCouponUserRelate::updateAll(['is_use'=>0], ['id' => $extra['user_coupon_id']]);
  846. if ($res==false) throw new Exception(AddonsCouponUserRelate::getStaticError());
  847. }
  848. $goods_ids_stock = [];
  849. $order_details = StoreCashierOrderDetail::findAll(['cashier_order_id' => $model['id'], 'order_status' => OrderStatusEnum::NOT_PAY]);
  850. /** @var StoreCashierOrderDetail $detail */
  851. foreach($order_details as $detail) {
  852. $detail->order_status = OrderStatusEnum::REPEAL;
  853. if(!$detail->save()){
  854. throw new \Exception($detail->getErrorMessage());
  855. }
  856. $result = StoreGoodsAttr::handleStock($detail['goods_attr_id'], $detail['num']);
  857. if ($result === false) {
  858. throw new \Exception(StoreGoodsAttr::getStaticError());
  859. }
  860. $goods_ids_stock[$detail['goods_id']] = $detail['goods_id'];
  861. }
  862. foreach($goods_ids_stock as $goods_id){
  863. $stock = StoreGoodsAttr::find()->where(['goods_id'=>$goods_id])->sum('stock');
  864. $stock= $stock??0;
  865. $res = StoreGoods::updateAll(['stock'=>$stock],['id'=>$goods_id]);
  866. if(!$res) throw new \Exception(StoreGoods::getStaticError());
  867. }
  868. $trans->commit();
  869. self::backStoreUserScore($model);
  870. if($extra['deduct_num_score']>0){
  871. self::backUserScore($model,$extra['deduct_num_score']);
  872. }
  873. return $model;
  874. }catch(Exception $e){
  875. $trans->rollback();
  876. // 解锁
  877. self::$static_error = $e->getMessage();
  878. return false;
  879. }
  880. }
  881. /**
  882. * 计算赠送的积分
  883. * @param $mall_id
  884. * @param $pay_money
  885. * @param $deduct_num_score
  886. * @return int|string
  887. */
  888. protected static function computeGiveScore($mall_id,$store_id,$store_user_id,$pay_money,$deduct_num_score)
  889. {
  890. $give_score_num = 0;
  891. $score_give_config = \Yii::$app->services->setting->addons->get($mall_id, StoreEnum::ADDONS_NAME, 'scoreGive');
  892. if (empty($score_give_config['score_give_setting'])) {
  893. return $give_score_num;
  894. }
  895. $score_give_setting = json_decode($score_give_config['score_give_setting'], true);//基础配置
  896. if(empty($score_give_setting['is_open'])){
  897. return $give_score_num;
  898. }
  899. $bear = $score_give_setting['bear'] ?? 0;
  900. if ($bear == 1) {
  901. $user_wallet = UserWallet::findOne(['user_id'=>$store_user_id]);
  902. }
  903. // 判断门店是否有“积分赠送”权限限制
  904. if (!empty($score_give_config['store_limit_type']) && isset($score_give_config['store_limit_stores'])) {
  905. $store_limit_stores = json_decode($score_give_config['store_limit_stores'], true);
  906. if (!in_array($store_id, $store_limit_stores)) {
  907. return $give_score_num;
  908. }
  909. }
  910. try {
  911. $condition = $score_give_setting['condition'];
  912. $give_type = $score_give_setting['give_type'];
  913. $give_num = $score_give_setting['give_num'];
  914. if ($bear == 1) {
  915. $store_setting = StoreSettings::getOne( [
  916. ['mall_id' => $mall_id],
  917. ['store_id' => $store_id],
  918. ['status' => StatusEnum::ENABLED]
  919. ],true,[],false,'score_give');
  920. if(!empty($store_setting)){
  921. $store_setting = json_decode($store_setting['score_give'],true);
  922. if(!empty($store_setting)&& !empty($store_setting['is_alone_score_give'])){
  923. $condition = $store_setting['condition'];
  924. $give_type = $store_setting['give_type'];
  925. $give_num = $store_setting['give_num'];
  926. }
  927. }
  928. }
  929. if($condition==1 && $deduct_num_score>0){//未使用积分抵扣的订单
  930. return $give_score_num;
  931. }
  932. if($give_type==2){//百分比
  933. $give_num = $pay_money * $give_num/100 ;
  934. }
  935. if ($bear == 1 ) {
  936. //判断门店主的积分
  937. if(empty($user_wallet)){
  938. $give_num = 0;
  939. }elseif($give_num > $user_wallet['score']){
  940. $give_num = $user_wallet['score'];
  941. }
  942. }
  943. }catch (Exception $e){
  944. $give_num = 0;
  945. }
  946. if($give_num>0){
  947. $give_score_num += $give_num;
  948. }
  949. return bcadd($give_score_num,0,2);
  950. }
  951. /**
  952. * 积分赠送-扣除门店主积分
  953. * @param $mall_id
  954. * @param $score_give_data
  955. * @return void
  956. * @throws Exception
  957. */
  958. protected static function subStoreUserScore($mall_id,$order,$store_user_id,$give_score)
  959. {
  960. $score_give_setting = \Yii::$app->services->setting->addons->get($mall_id, StoreEnum::ADDONS_NAME, 'scoreGive.score_give_setting');
  961. $score_give_setting = json_decode($score_give_setting,true);//基础配置
  962. $bear = $score_give_setting['bear'] ?? 0;
  963. if ($bear != 1) {
  964. return true;
  965. }
  966. $prize_name = '积分赠送';
  967. $desc = '门店承担门店收银台订单(' . $order['order_no'] . ')奖励,奖励类型:' . $prize_name . ',积分:' . $give_score;
  968. $data = [
  969. 'mall_id' =>$mall_id,
  970. 'user_id' => $store_user_id,
  971. 'is_frozen' => false,
  972. 'wallet_type' => 'score',
  973. 'money' => -$give_score,
  974. 'desc' => $desc,
  975. 'source_table' => StoreCashierOrder::tableName(),
  976. 'source_table_id' => $order['id'],
  977. 'from_type' => AddonsEnum::ADDONS_NAME_STORE,
  978. 'capital_type' => UserWalletEnum::CONSUME_ORDER_GIVE,
  979. 'order_no' =>$order['order_no'],
  980. ];
  981. $res = UserWalletService::handle(0, $data);
  982. if ($res == false) throw new \Exception('积分赠送扣除门店主积分失败');
  983. }
  984. /**
  985. * 积分抵扣-扣减用户积分
  986. * @param $params
  987. * @param $order_id
  988. * @param $deduct_num_score
  989. * @return void
  990. * @throws Exception
  991. */
  992. protected static function subUserScore($params,$order,$deduct_num_score)
  993. {
  994. $desc = '用户('.$params['user_id'].'),下单(收银台订单)('.$order['order_no'].')抵扣积分,数量:'.$deduct_num_score;
  995. $data = [
  996. 'message_id' => 0,
  997. 'not_add_total' => true,
  998. 'mall_id' => $params['mall_id'],
  999. 'user_id' => $params['user_id'],
  1000. 'is_frozen' => false,
  1001. 'wallet_type' => 'score',
  1002. 'money' => -$deduct_num_score,
  1003. 'desc' => $desc,
  1004. 'source_table' => StoreCashierOrder::tableName(),
  1005. 'source_table_id' => $order['id'],
  1006. 'from_type' => AddonsEnum::ADDONS_NAME_STORE,
  1007. 'capital_type' => UserWalletEnum::STORE_CASHIER_ORDER_CREATE,
  1008. 'order_no' => $order['order_no'],
  1009. ];
  1010. $res = UserWalletService::handle(0, $data);
  1011. if ($res == false) throw new \Exception('抵扣积分失败');
  1012. }
  1013. /**
  1014. * 订单关闭-退回积分赠送给门店主
  1015. * @param $order
  1016. * @return true|void
  1017. * @throws Exception
  1018. */
  1019. protected static function backStoreUserScore($order)
  1020. {
  1021. $store = Store::findOne(['id'=>$order['store_id']]);
  1022. $store_user_id = $store['user_id']??0;
  1023. if($store_user_id==0){
  1024. return true;
  1025. }
  1026. $score_log_list = ScoreLog::lists([
  1027. 'where'=>[
  1028. ['user_id'=>$store_user_id],
  1029. ['change_type'=>'sub'],
  1030. ['source_table' => StoreCashierOrder::tableName()],
  1031. ['source_table_id'=>$order['id']],
  1032. ['from_type' => AddonsEnum::ADDONS_NAME_STORE],
  1033. ['capital_type'=>UserWalletEnum::CONSUME_ORDER_GIVE],
  1034. ]
  1035. ]);
  1036. if(!empty($score_log_list)){
  1037. $prize_name = '订单关闭-退回积分赠送';
  1038. $insert_wallet = [];
  1039. foreach ($score_log_list as $item){
  1040. $score = $item['change_num'];
  1041. $desc = '订单关闭,退回积分赠送扣除门店主的积分,门店收银台订单(' . $order['order_no'] . '),类型:' . $prize_name . ',积分:' . $score;
  1042. $insert_wallet[] = [
  1043. 'mall_id' => $item['mall_id'],
  1044. 'user_id' => $item['user_id'],
  1045. 'is_frozen' => false,
  1046. 'wallet_type' => 'score',
  1047. 'money' => $score,
  1048. 'desc' => $desc,
  1049. 'source_table' => StoreCashierOrder::tableName(),
  1050. 'source_table_id'=>$order['id'],
  1051. 'from_type' => AddonsEnum::ADDONS_NAME_STORE,
  1052. 'capital_type' => UserWalletEnum::CONSUME_ORDER_GIVE,
  1053. 'order_no' =>$order['order_no'],
  1054. ];
  1055. }
  1056. $res = UserWalletService::batchHandleByQueue($insert_wallet);
  1057. }
  1058. }
  1059. /**
  1060. * 订单关闭-退回积分抵扣积分
  1061. * @param $order
  1062. * @return true|void
  1063. * @throws Exception
  1064. */
  1065. protected static function backUserScore($order,$deduct_num_score)
  1066. {
  1067. $desc = '用户('.$order['user_id'].'),(收银台订单关闭)('.$order['order_no'].')退回积分,数量:'.$deduct_num_score;
  1068. $insert_wallet[] = [
  1069. 'message_id' => 0,
  1070. 'not_add_total' => true,
  1071. 'mall_id' => $order['mall_id'],
  1072. 'user_id' => $order['user_id'],
  1073. 'is_frozen' => false,
  1074. 'wallet_type' => 'score',
  1075. 'money' => $deduct_num_score,
  1076. 'desc' => $desc,
  1077. 'source_table' => StoreCashierOrder::tableName(),
  1078. 'source_table_id' => $order['id'],
  1079. 'from_type' => AddonsEnum::ADDONS_NAME_STORE,
  1080. 'capital_type' => UserWalletEnum::STORE_CASHIER_ORDER_CLOSE,
  1081. 'order_no' =>$order['order_no'],
  1082. ];
  1083. $res = UserWalletService::batchHandleByQueue($insert_wallet);
  1084. }
  1085. /**
  1086. * 收银台订单-退款-贵九汇客户站点需要手动退款,使用
  1087. * @param $order_no
  1088. * @return bool|string
  1089. */
  1090. public function refund($order_no)
  1091. {
  1092. $trans = Yii::$app->db->beginTransaction();
  1093. try {
  1094. $order = StoreCashierOrder::findOne(['order_no' => $order_no,'status'=>StatusEnum::ENABLED]);
  1095. if (empty($order)) throw new Exception("收银台订单不存在或已删除");
  1096. $mallId = $order['mall_id'];
  1097. $refund_price = $order->pay_money;
  1098. if (empty($refund_price)) {
  1099. return true;
  1100. }
  1101. $refund_order_no = Order::generateOrderNo('c_r');
  1102. // 操作退款-退货退款,仅退款
  1103. $insert_wallet = []; //重置写入钱包数据
  1104. $orderUserId = $order->user_id;
  1105. $refundUserId = $order->user_id;
  1106. //是否为组合支付
  1107. $payment_trade_order = PaymentTradeOrder::findOne(['order_no' => $order['order_no'], 'is_pay' => OrderStatusEnum::PAY]);
  1108. if (empty($payment_trade_order)) {
  1109. return '支付流水订单不存在';
  1110. }
  1111. $payment_trade = PaymentTrade::findOne(['id' => $payment_trade_order['trade_id']]);
  1112. if (empty($payment_trade)) {
  1113. return '支付流水不存在';
  1114. }
  1115. if (!empty($payment_trade->is_pay_group)) {
  1116. $payment_group_trade_list = PaymentTrade::find()->where(['is_pay' => 1, 'is_pay_group' => 1, 'parent_trade_id' => $payment_trade->id])->orderBy('id asc')->all();
  1117. } else {
  1118. $payment_group_trade_list[] = $payment_trade;
  1119. }
  1120. $all_refund_price = $order->pay_money;
  1121. // 类型为后台主动退款时,处理佣金、积分数据
  1122. foreach ($payment_group_trade_list as $payment_group_trade) {
  1123. if ($all_refund_price > $payment_group_trade->pay_fee) {
  1124. $refund_price = $payment_group_trade->pay_fee;
  1125. } else {
  1126. $refund_price = $all_refund_price;
  1127. }
  1128. if (in_array($payment_group_trade->pay_type, [PayTypeEnum::BALANCE, PayTypeEnum::SCORE])) {
  1129. // 本地支付
  1130. $wallet_type = $payment_group_trade->pay_type == PayTypeEnum::SCORE ? UserWalletService::WALLET_TYPE_SCORE : UserWalletService::WALLET_TYPE_BALANCE;
  1131. $extra_refund_price = 0;//额外抵扣金额
  1132. if ($wallet_type == UserWalletService::WALLET_TYPE_BALANCE) {
  1133. //判断是否有卡券插件
  1134. if (MallAddons::isInstall($mallId, AddonsEnum::ADDONS_NAME_CARD_COUPONS)) {
  1135. $card_refund_price = $order->pay_money - $payment_group_trade->pay_fee;
  1136. $extra_refund_price = CardCouponsScoreGive::setOrderRefund([
  1137. 'refund_price' => $card_refund_price,
  1138. 'trade_id' => $payment_group_trade->id,
  1139. 'refund_id' => $order->id
  1140. ]);
  1141. }
  1142. }
  1143. $insert_wallet[] = [
  1144. 'mall_id' => $mallId,
  1145. 'user_id' => $refundUserId,
  1146. 'is_frozen' => false,
  1147. 'wallet_type' => $wallet_type,// 暂定退回余额或积分
  1148. 'money' => $refund_price,
  1149. 'desc' => '收银台订单退款-订单(' . $order->id . ')',
  1150. 'source_table' => StoreCashierOrder::tableName(),
  1151. 'source_table_id' => $order->id,
  1152. 'from_type' => UserWalletEnum::REFUND,
  1153. 'capital_type' => UserWalletEnum::REFUND_ORDER,
  1154. 'order_no' => $order->order_no,
  1155. 'not_add_total' => false
  1156. ];
  1157. $refund_price += $extra_refund_price;
  1158. } elseif (in_array($payment_group_trade->pay_type, [PayTypeEnum::JOINPAY, PayTypeEnum::ALI, PayTypeEnum::JOINPAY_FAST_PAY, PayTypeEnum::WECHAT, PayTypeEnum::SANDPAY_FAST_PAY])) {
  1159. //原路返回
  1160. $originUserId = $order->user_id;
  1161. $order->user_id = $orderUserId;
  1162. $res = Yii::$app->services->pay->refund($order->pay_money, $refund_price, $payment_group_trade->out_trade_no, $order, '收银台订单退款');
  1163. $order->user_id = $originUserId;
  1164. if ($res === false) throw new \Exception('退款失败');
  1165. } elseif ($order->payment_type == PayTypeEnum::OFFLINE) {
  1166. // 此支付方式为后台管理员直接支付不做任何处理
  1167. } elseif (
  1168. // 当res返回为false 说明没有相对于的退款方式
  1169. $res = GlobalInvoke::exec(GlobalInvoke::INVOKE_ORDER_REFUND, $mallId, [
  1170. 'id' => $order->id,
  1171. 'mall_id' => $mallId,
  1172. 'refund' => [],
  1173. 'params' => [],
  1174. 'order_no' => $order['order_no'],
  1175. 'refund_order_no' => $refund_order_no,
  1176. 'payment_trade_id' => $payment_group_trade->id,
  1177. 'refund_price' => $refund_price,
  1178. 'pay_type' => $order->payment_type,
  1179. ])
  1180. ) {
  1181. // 当res不为空 进此判断
  1182. list($boolean, $msg) = $res;
  1183. if ($boolean === false) {
  1184. $this->setError($msg);
  1185. return false;
  1186. }
  1187. }
  1188. $all_refund_price = bcsub($all_refund_price, $refund_price, 2);
  1189. if ($all_refund_price <= 0) break;
  1190. }
  1191. $store = Store::findOne(['id'=>$order['store_id']]);
  1192. if($store->balance){
  1193. $store->balance = bcsub($store->balance,$order->pay_money,2);
  1194. if($store->balance<0){
  1195. $store->balance = 0;
  1196. }
  1197. $store->save();
  1198. }
  1199. $order->status = -1;
  1200. $order->save();
  1201. StoreCommission::updateAll(['status'=>StatusEnum::DELETE],['order_no'=>$order['order_no']]);
  1202. $trans->commit();
  1203. $extra= json_decode($order['extra'],true);
  1204. if(!empty($extra['deduct_num_score'])){
  1205. $insert_wallet[] = [
  1206. 'mall_id' => $mallId,
  1207. 'user_id' => $refundUserId,
  1208. 'is_frozen' => false,
  1209. 'wallet_type' => 'score',// 暂定退回余额或积分
  1210. 'money' => $extra['deduct_num_score'],
  1211. 'desc' => '收银台订单退款-订单(' . $order->id . '),退回抵扣的积分',
  1212. 'source_table' => StoreCashierOrder::tableName(),
  1213. 'source_table_id' => $order->id,
  1214. 'from_type' => UserWalletEnum::REFUND,
  1215. 'capital_type' => UserWalletEnum::REFUND_ORDER,
  1216. 'order_no' => $order->order_no,
  1217. ];
  1218. }
  1219. if(!empty($extra['give_score'])){
  1220. $insert_wallet[] = [
  1221. 'mall_id' => $mallId,
  1222. 'user_id' => $refundUserId,
  1223. 'is_frozen' => false,
  1224. 'wallet_type' => 'score',// 暂定退回余额或积分
  1225. 'money' => -$extra['give_score'],
  1226. 'desc' => '收银台订单退款-订单(' . $order->id . '),扣除赠送的积分',
  1227. 'source_table' => StoreCashierOrder::tableName(),
  1228. 'source_table_id' => $order->id,
  1229. 'from_type' => UserWalletEnum::REFUND,
  1230. 'capital_type' => UserWalletEnum::REFUND_ORDER,
  1231. 'order_no' => $order->order_no,
  1232. ];
  1233. }
  1234. if (!empty($insert_wallet)) {
  1235. $res = UserWalletService::batchHandleByQueue($insert_wallet);
  1236. if (empty($res)) throw new \Exception(UserWalletService::getStaticError());
  1237. }
  1238. return true;
  1239. } catch (Exception $e) {
  1240. $trans->rollBack();
  1241. $this->setError($e->getMessage());
  1242. return false;
  1243. }
  1244. }
  1245. }