Explorar el Código

feat: EK-76: Member Auto-Maintenance.

kevinElken hace 9 meses
padre
commit
8f46534d28

+ 6 - 1
common/models/ApproachReconsumeOrder.php

@@ -53,6 +53,10 @@ use Yii;
  * @property string $PAY_AMOUNT_STANDARD 支付价格
  * @property string $ORDER_AMOUNT_STANDARD 订单总价格($)
  * @property string $EXCHANGE_RATE 汇率
+ * @property string COUNTRY_ID 国家ID
+ * @property int CURRENCY_ID 币种ID
+ * @property int AUTO_MAINTENANCE AMP订单
+ * @property string $DEC_USER_ID  会员编号
  */
 class ApproachReconsumeOrder extends \common\components\ActiveRecord
 {
@@ -133,7 +137,8 @@ class ApproachReconsumeOrder extends \common\components\ActiveRecord
             'IS_DELETE' => '是否删除',
             'DELETED_AT' => '删除时间',
             'WAREHOUSE' => '发货仓',
-            'EMAIL' => 'Email'
+            'EMAIL' => 'Email',
+            'AUTO_MAINTENANCE' => 'AMP',
         ];
     }
 

+ 752 - 0
common/models/forms/ApproachAutoMaintenanceOrderForm.php

@@ -0,0 +1,752 @@
+<?php
+namespace common\models\forms;
+
+use common\helpers\Cache;
+use common\helpers\Date;
+use common\components\Model;
+use common\helpers\Form;
+use common\helpers\LoggerTool;
+use common\helpers\PayStack;
+use common\helpers\user\Balance;
+use common\helpers\user\Cash;
+use common\helpers\user\Info;
+use common\libs\logging\operate\AdminOperate;
+use common\models\AmpPeriod;
+use common\models\ApproachDecOrder;
+use common\models\ApproachOrder;
+use common\models\ApproachReconsumeOrder;
+use common\models\ApproachOrderGoods;
+use common\models\BaUser;
+use common\models\Countries;
+use common\models\CurrencyConversions;
+use common\models\DealType;
+use common\models\DecLevelLog;
+use common\models\DecOrder;
+use common\models\FreeTemplate;
+use common\models\Order;
+use common\models\OrderGoods;
+use common\models\Period;
+use common\models\ReceiveAddress;
+use common\models\Region;
+use common\models\ShopGoods;
+use common\models\ShopGoodsNature;
+use common\models\User;
+use common\models\UserNetwork;
+use common\models\Instalment;
+use Yii;
+use yii\base\Exception;
+class ApproachAutoMaintenanceOrderForm extends Model
+{
+    public $sn;
+    public $expressCompany;
+    public $orderTrackNo;
+    public $status;
+    public $remark;
+    public $note;
+
+    public $type;
+    public $addressId;
+    public $payType;
+    public $goodsId;
+    public $goodsNum;
+    public $payPassword;
+    public $email;
+
+    public $userName;
+    public $decUserName;
+    public $consignee;
+    public $acceptMobile;
+    public $province;
+    public $city;
+    public $county;
+    public $lgaName;
+    public $cityName;
+    public $autoMaintenance;
+    public $exchangeRate;
+    public $detailaddress;
+
+    private $_address;
+    private $_decAmount;
+    private $_decPv;
+    private $_realPv;
+    private $_freight;
+    private $_payAmount;
+    private $_orderGoods;
+    private $_standardAmount;
+    private $_decAmountStandard;
+
+    /**
+     * @var ApproachOrder
+     */
+    private $_model;
+
+    public function init() {
+        parent::init();
+        $this->adminOperateLogger = new AdminOperate([
+            'fetchClass' => ApproachOrder::class,
+        ]);
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function rules()
+    {
+        return [
+            [['sn', 'expressCompany', 'orderTrackNo', 'status', 'remark','type','addressId','payType','goodsId','goodsNum', 'payPassword','userName','consignee','acceptMobile','province',/*'city','county',*/'lgaName','cityName','detailaddress','email'], 'trim'],
+            [['sn', 'expressCompany', 'orderTrackNo', 'status', 'remark','type','addressId','payType','goodsId','goodsNum', 'payPassword','userName','consignee','acceptMobile','province',/*'city','county',*/'detailaddress'/*,'email'*/], 'required'],
+            [['status'], 'isStatus'],
+            [['addressId'], 'isAddress'],
+            [['payType'], 'isPayType'],
+            [['payPassword'], 'validatePassword'],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'sn' => Yii::t('app', 'orderSn'),
+            'expressCompany' => Yii::t('app', 'expressCompany'),
+            'orderTrackNo' => Yii::t('app', 'orderTrackNo'),
+            'status' => Yii::t('app', 'state'),
+            'remark' => Yii::t('app', 'remark'),
+            'type' => Yii::t('app', 'orderType'),
+            'addressId' => Yii::t('app', 'shippingAddress'),
+            'payType' => Yii::t('app', 'payType'),
+            'goodsId' => Yii::t('app', 'productID'),
+            'goodsNum' => Yii::t('app', 'quantity'),
+            'userName' => Yii::t('app', 'repeatSalesMemberNo'),
+            'consignee' => Yii::t('app', 'consignee'),
+            'acceptMobile' => Yii::t('app', 'acceptMobile'),
+            'province' => Yii::t('app', 'province'),
+            'city' => Yii::t('app', 'city'),
+            'county' => Yii::t('app', 'county'),
+            'lgaName' =>Yii::t('app', 'lgaName'),
+            'cityName' => Yii::t('app', 'cityName'),
+            'detailaddress' => Yii::t('app', 'detailAddress'),
+            'email' => Yii::t('app', 'email'),
+        ];
+    }
+
+    /**
+     * 指定校验场景
+     * @return array
+     */
+    public function scenarios()
+    {
+        $parentScenarios =  parent::scenarios();
+        $customScenarios = [
+            // 管理员修改订单状态
+            'adminStatus' => ['sn', 'status'],
+            // 校验订单支付
+            'verifyPayStack' => ['sn', 'note', 'status'],
+            // 会员下单
+            'userOrder' => ['type','addressId', 'payType','goodsId','goodsNum', 'note', 'payPassword'],
+            // 为会员复消
+            'reconsumeOrder' => ['type','userName', 'payType','goodsId','goodsNum', 'remark', 'payPassword','consignee','acceptMobile','province','cityName','lgaName','detailaddress'],
+        ];
+        return array_merge($parentScenarios, $customScenarios);
+    }
+
+    /**
+     * 校验之前
+     * @return bool
+     */
+    public function beforeValidate()
+    {
+        $parentValidate = parent::beforeValidate();
+        if ($this->sn) {
+            $this->_model = ApproachOrder::findOne(['SN' => $this->sn]);
+            if (!$this->_model){
+                $this->addError('sn', Yii::t('app', 'orderDoesNotExist'));
+                return false;
+            }
+        }
+
+        if ($this->scenario == 'verifyPayStack'){
+            if ($this->_model->STATUS != \Yii::$app->params['orderStatus']['notPaid']['value']) {
+                $this->addError('sn', Yii::t('app', 'payTypeError'));
+                return false;
+            }
+        }
+
+        return $parentValidate;
+    }
+
+    /**
+     * 判断收货地址是否存在
+     * @param $attribute
+     */
+    public function isAddress($attribute){
+        if (!$receiveAddress = ReceiveAddress::find()->where(' ID=:ID', [':ID' => $this->addressId])->asArray()->one()) {
+            $this->addError($attribute, Yii::t('app', 'shippingDoesNotExist'));
+        } else {
+            $this->_address = $receiveAddress;
+        }
+    }
+
+    /**
+     * 校验支付密码
+     * @param $attribute
+     * @param $params
+     */
+    public function validatePassword($attribute, $params) {
+        if (!User::validatePayPassword(\Yii::$app->user->id, $this->payPassword)) {
+            $this->addError($attribute, Yii::t('app', 'paymentPasswordError'));
+        }
+    }
+
+    /**
+     * 判断支付方式
+     * @param $attribute
+     * @throws Exception
+     */
+    public function isPayType($attribute)
+    {
+        if ($this->payType != 'pay_stack'){
+            $this->addError($attribute, Yii::t('app', 'payTypeError'));
+            return;
+        }
+
+        // 一个订单只能包含一类商品
+        $goods = ShopGoods::find()->select('ID,CATEGORY_TYPE')->where(['in', 'ID', $this->goodsId])->andWhere(['STATUS' => 1])->asArray()->all();
+        if (!$goods) {
+            throw new Exception(Yii::t('app', 'orderCanNotContainMultipleProductCategories'));
+            return;
+        }
+        $goodsCategoryType = array_unique(array_column($goods, 'CATEGORY_TYPE'));
+        if (count($goodsCategoryType) > 1) {
+            $this->addError($attribute, Yii::t('app', 'orderCanNotHasMoreClassification'));
+            return;
+        }
+
+        // 购买方式
+        $sellTypeLabelMap = array_column(ShopGoods::SALE_TYPE, NULL, 'label');
+        if (!array_key_exists($this->payType, $sellTypeLabelMap)) {
+            $this->addError($attribute, Yii::t('app', 'payTypeError'));
+            return;
+        }
+
+        // 所选支付方式必须是商品分类支持的类型
+        $categoryType = array_column(ShopGoods::CATEGORY_TYPE, NULL, 'id');
+        // 商品类型
+        $currCategoryType = $goodsCategoryType[0];
+        if (!array_key_exists($currCategoryType, $categoryType)) {
+            $this->addError($attribute, Yii::t('app', 'shopGoodClassificationError'));
+            return;
+        }
+
+        $sellType = $categoryType[$currCategoryType]['sell_type'] ?? [];
+        if (!$sellType || !in_array($this->payType, array_column($sellType, 'label'))) {
+            $this->addError($attribute, Yii::t('app', 'payTypeError'));
+        }
+    }
+
+    /**
+     * 校验类型
+     * @param $attribute
+     */
+    public function isStatus($attribute){
+        if($this->type && !in_array($this->type, \Yii::$app->params['orderStatus'])){
+            $this->addError($attribute, Yii::t('app', 'orderStatusTypeError'));
+            return ;
+        }
+        if ($this->scenario == 'adminStatus'){
+            if ($this->status == $this->_model['STATUS']) {
+                $this->addError($attribute, Yii::t('app', 'orderStatusDoesNotChange'));
+                return ;
+            }
+            if($this->status == \Yii::$app->params['orderStatus']['notPaid'] && $this->_model['STATUS'] >= \Yii::$app->params['orderStatus']['delivery']) {
+                $this->addError($attribute, Yii::t('app', 'orderHasBeenLogisticsStatusDoesNotChangedUnpaid'));
+                return ;
+            }
+            elseif($this->status == \Yii::$app->params['orderStatus']['paid'] && $this->_model['STATUS'] >= \Yii::$app->params['orderStatus']['cancel']) {
+                $this->addError($attribute, Yii::t('app', 'orderHasBeenInvalidCanNotProcess'));
+                return ;
+            }
+            elseif($this->status == \Yii::$app->params['orderStatus']['delivery']) {
+                $this->addError($attribute, Yii::t('app', 'orderCanNotBeenChangedLogistics'));
+                return ;
+            }
+            elseif($this->status == \Yii::$app->params['orderStatus']['complete'] && $this->_model['STATUS'] > \Yii::$app->params['orderStatus']['cancel']) {
+                $this->addError($attribute, Yii::t('app', 'orderHasBeenInvalidCanNotProcess'));
+                return ;
+            }
+            elseif($this->status == \Yii::$app->params['orderStatus']['cancel']) {
+                if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['complete']) {
+                    $this->addError($attribute, Yii::t('app', 'orderHasBeenFinishedCanNotCancel'));
+                    return ;
+                }
+                if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['del']) {
+                    $this->addError($attribute, Yii::t('app', 'orderHasBeenDeletedCanNotCancel'));
+                    return ;
+                }
+            }
+            elseif($this->status == \Yii::$app->params['orderStatus']['del']) {
+                if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['complete']) {
+                    $this->addError($attribute, Yii::t('app', 'orderHasBeenFinishedCanNotDelete'));
+                    return ;
+                }
+            }
+        }
+
+    }
+
+    /**
+     * 校验PayStack支付,更新订单状态.同步到正式订单.
+     * @throws Exception
+     */
+    public function verifyPayStack(): ?ApproachOrder
+    {
+        if (!$this->validate()) {
+            return null;
+        }
+
+        // 调用PayStack支付校验
+        LoggerTool::info([$this->note['reference'], $this->note]);
+        $payload = PayStack::transactionVerify($this->note['reference']);
+        LoggerTool::info($payload);
+        if ($payload['status'] !== true) {
+            throw new Exception(Form::formatErrorsForApi($payload['message']));
+        }
+        if ($payload['data']['amount'] != $this->_model->PAY_AMOUNT * 100) {
+            throw new Exception(Form::formatErrorsForApi(Yii::t('app', 'payAmountNotEqualOrderAmount')));
+        }
+
+        // 订单类型:userOrder(会员订单)、userUpgrade(会员升级)、userDec(会员报单)
+        $orderType = $this->note['metadata']['custom_fields'][1]['value'] ?? false;
+
+        $db = \Yii::$app->db;
+        $transaction = $db->beginTransaction();
+        try {
+            // 更新准订单状态为已支付
+            $this->_model->STATUS = $this->status;
+            $this->_model->NOTE = json_encode($this->note);
+            $this->_model->PAY_AT = Date::utcToTime($this->note['paid_at']);
+            $this->_model->EMAIL = $this->note['email'];
+            if (!$this->_model->save()) {
+                throw new Exception(Form::formatErrorsForApi($this->_model->getErrors()));
+            }
+            // 更新订单商品的支付Email
+            ApproachOrderGoods::updateAll(['EMAIL' => $this->note['email']], 'ORDER_SN = :ORDER_SN', [':ORDER_SN' => $this->sn]);
+
+            // 同步准订单到正式订单
+            Order::insertOne($this->_model->toArray());
+            // 同步准订单商品到正式订单商品
+            $approachOrderGoods = ApproachOrderGoods::findAllAsArray('ORDER_SN = :ORDER_SN', [':ORDER_SN' => $this->sn]);
+            foreach ($approachOrderGoods as &$approachOrderGood) {
+                $approachOrderGood['EMAIL'] = $this->email;
+            }
+            OrderGoods::batchInsert($approachOrderGoods);
+
+            // AMP订单
+            $order = Order::findOne($this->_model->ID);
+            if ($order['AUTO_MAINTENANCE'] == 1) {
+                $this->orderAutoMaintenance($order);
+            }
+
+            // 会员报单、BA升级
+            if (in_array($orderType, ['userDec', 'baUpgrade', 'userUpgrade'])) {
+                // 同步报单
+                $approachDecOrder = ApproachDecOrder::findOneAsArray('ORDER_SN = :ORDER_SN', [':ORDER_SN' => $this->sn]);
+                if ($approachDecOrder) {
+                    unset($approachDecOrder['STATUS']);
+
+                    // 同步报单
+                    DecOrder::insertOne($approachDecOrder);
+
+                    // 修改会员锁定状态
+                    if (in_array($orderType, ['userDec', 'baUpgrade'])) {
+                        if (!User::updateAll(['STATUS' => 1], 'ID=:USER_ID', [':USER_ID' => $approachDecOrder['TO_USER_ID']])) {
+                            throw new Exception(Form::formatErrorsForApi(Yii::t('app', 'changeUserStatusError')));
+                        }
+                    }
+                    // 修改BA会员升级状态
+                    if ($orderType === 'baUpgrade') {
+                        // 查询BA会员名
+                        $userInfo = User::findOneAsArray('ID=:USER_ID', [':USER_ID' => $approachDecOrder['TO_USER_ID']]);
+                        if (!BaUser::updateAll(['WHETHER_UPGRADE' => 1, 'BA_UPGRADE_AT' => time()], 'USER_NAME=:USER_NAME', [':USER_NAME' => $userInfo['USER_NAME']])) {
+                            throw new Exception(Form::formatErrorsForApi(Yii::t('app', 'brandAmbassadorUpgradeError')));
+                        }
+                    }
+
+                    // 正式会员-升级单
+                    if ($orderType === 'userUpgrade') {
+                        // 会员升级 报单类型:2会员升级单
+                        if ($approachDecOrder['DETAIL_TYPE'] == 2) {
+                            // 为被升级人进行升级操作
+                            $decLevelLog = new DecLevelLog();
+                            $decLog = [
+                                'userId' => $approachDecOrder['TO_USER_ID'],//会员ID
+                                'fromId' => $approachDecOrder['ORI_LV'], // 变动前的级别
+                                'levelId' => $approachDecOrder['UPGRADE_LV'],// 变动后的级别
+                                'actionId' => $approachDecOrder['USER_ID'],
+                                'remark' => $approachDecOrder['REMARK'],
+                                'lvPv' => $this->_model->PV,
+                            ];
+                            $modifyDecLv = $decLevelLog->frontendChange($decLog);
+                            if (empty($modifyDecLv)) {
+                                $transaction->rollBack();
+                                throw new Exception(Yii::t('app', 'failedToUpgrade'));
+                            }
+                        }
+                    }
+
+                    // 删除中间表
+                    ApproachDecOrder::deleteAll('ORDER_SN = :ORDER_SN', [':ORDER_SN' => $this->sn]);
+                }
+            }
+
+            // 删除中间表
+            ApproachOrder::deleteAll('SN = :SN', [':SN' => $this->sn]);
+            ApproachOrderGoods::deleteAll('ORDER_SN = :ORDER_SN', [':ORDER_SN' => $this->sn]);
+
+            $transaction->commit();
+        } catch (Exception $e) {
+            $transaction->rollBack();
+            $this->addError('edit', $e->getFile() . '  ' . $e->getMessage());
+            return null;
+        }
+
+        return $this->_model;
+    }
+
+    /**
+     * AMP
+     * @throws Exception
+     * @throws \yii\db\Exception
+     */
+    public function autoMaintenanceSureOrder()
+    {
+        if(!$this->validate()){
+            return null;
+        }
+
+        $ids = $this->goodsId;
+        $totalAmount = 0;
+        $totalPv = 0;
+        $totalRealPv = 0;
+        $totalAmountStandard = 0;
+        $this->autoMaintenance = 1;
+
+        $goodsType = ShopGoods::getGoodType();
+        $hasInstalment = 0;
+        $userId = Info::getUserIdByUserName($this->userName);
+        $user = User::getEnCodeInfo($userId);
+
+        $decUserID = Info::getUserIdByUserName($this->decUserName);
+        $stockist_user = User::getEnCodeInfo($decUserID);
+        if(!$stockist_user || $stockist_user['IS_DEC'] != 1){
+            throw new Exception(Yii::t('app', 'stockistDoesNotExist'));
+        }
+
+        // 报单中心汇率
+        $decCountryId = User::getEnCodeInfo(\Yii::$app->user->id)['COUNTRY_ID'];
+        $decCountry = Countries::getById($decCountryId);
+        $decUserCurrencyRate = CurrencyConversions::getToUSDRate($decCountry['LOCAL_CURRENCY_ID']);
+        // 会员汇率
+        $country = Countries::getById($user['COUNTRY_ID']);
+        $currencyRate = CurrencyConversions::getToUSDRate($country['LOCAL_CURRENCY_ID']);
+        // 判断是否是报单中心
+        $loginUser = User::getEnCodeInfo(\Yii::$app->user->id);
+        if ($loginUser['IS_DEC'] == 1){
+            if($decUserID != \Yii::$app->user->id){
+                throw new Exception(Yii::t('app', 'decUserNameIsWrong'));
+            }
+        }
+
+        foreach ($this->goodsNum as $k => $v) {
+            if ($v) {
+                $goods = ShopGoods::findOneAsArray('ID=:ID AND STATUS=1 AND AUTO_MAINTENANCE=1',[':ID'=> $ids[$k]]);
+                if (!$goods) {
+                    throw new Exception(Yii::t('app', 'productsDoesSoldOut'));
+                }
+
+                $goodsNature = ShopGoodsNature::findOneAsArray('GOODS_ID=:GOODS_ID AND COUNTRY_ID=:COUNTRY_ID',
+                    [':GOODS_ID' => $ids[$k], ':COUNTRY_ID' => $user['COUNTRY_ID']]);
+                if (!$goodsNature) {
+                    throw new Exception(Yii::t('app', 'productsDoesSoldOut'));
+                }
+
+                $this->exchangeRate = CurrencyConversions::getToUSDRate($goodsNature['LOCAL_CURRENCY_ID']);
+
+                if($goods['STORE_NUMS']>0){
+                    if ($goods['TYPE'] == 1 || $goods['TYPE'] == 2) {
+                        $discount = $goodsType[$goods['TYPE']]['discount'];
+                        $realPrice = $goodsNature['SELL_PRICE'] * $discount/100;
+                        $realPv = $goods['PRICE_PV'] * $discount/100;
+                        $realPriceStandard = $goods['SELL_PRICE_STANDARD'] * $discount/100;
+                    } else {
+                        $discount = $goods['SELL_DISCOUNT'];
+                        $realPrice = $goodsNature['SELL_PRICE'] * $discount;
+                        $realPv = $goods['PRICE_PV'] * $discount;
+                        $realPriceStandard = $goods['SELL_PRICE_STANDARD'] * $discount;
+                    }
+
+                    $currentPv = $goods['PRICE_PV'];
+                    $totalPv += $this->payType == 'prp' ? 0 : $realPv * intval($v);
+                    $totalRealPv += $this->payType == 'prp' ? 0 : $realPv * intval($v);
+
+                    $totalAmount += $realPrice * intval($v);
+                    $totalAmountStandard += $realPriceStandard * intval($v);
+
+                    $this->_orderGoods[] = [
+                        'GOODS_ID' => $goods['ID'],
+                        'PRICE' => $goodsNature['SELL_PRICE'],
+                        'PV' => $currentPv,
+                        'REAL_PRICE' => $realPrice,
+                        'REAL_PV' => $realPv,
+                        'REMAIN_PV' => 0,
+                        'POINT' => $goods['POINT'],
+                        'BUY_NUMS' => intval($v),
+                        'SKU_CODE' => $goods['GOODS_NO'],
+                        'GOODS_TITLE' => $goods['GOODS_NAME'],
+                        'CATEGORY_TYPE' => $goods['CATEGORY_TYPE'],
+                        'PAY_TYPE' => $this->payType,
+                        'EMAIL' => $this->email,
+                        'STANDARD_PRICE' => $goods['SELL_PRICE_STANDARD'],
+                        'REAL_STANDARD_PRICE' => $realPriceStandard,
+                        'EXCHANGE_RATE' => $currencyRate,
+                        'TAX_RATE' => $goodsNature['TAX_RATE'],
+                    ];
+                }
+
+                if ($goods['INSTALMENT']>0) {
+                    if ($v > 1) {
+                        throw new Exception(Yii::t('app', 'allowOnlyOne'));
+                    }
+
+                    $userStage = Instalment::getStage($userId);
+                    $userInstalmentInfo = Instalment::getInfo($userId);
+
+                    $instalment = intval(Cache::getSystemConfig()['instalment']['VALUE'] ?? 3);
+                    if (intval($goods['INSTALMENT']) > $instalment) {
+                        throw new Exception(Yii::t('app', 'instalmentGoodsNoError'));
+                    }
+
+                    if ($userStage == $instalment) {
+                        if ($goods['INSTALMENT'] != 1) {
+                            throw new Exception(Yii::t('app', 'canNotBuy'));
+                        }
+                    } else {
+                        if ($userInstalmentInfo) {
+                            if ($userInstalmentInfo['STAGE']>0 && $userInstalmentInfo['ORDER_TYPE'] != 'FX') {
+                                throw new Exception(Yii::t('app', 'canNotBuy'));
+                            }
+                        }
+
+                        if ($userStage + 1 != $goods['INSTALMENT']) {
+                            throw new Exception(Yii::t('app', 'canNotBuy'));
+                        }
+
+                        if ($userStage + 1 > $instalment){
+                            throw new Exception(Yii::t('app', 'canNotBuy'));
+                        }
+                    }
+                    $hasInstalment = $goods['INSTALMENT'];
+                }
+            }
+        }
+
+        // 运费模板
+        $freeTemplate = FreeTemplate::getByCountryId($user['COUNTRY_ID']);
+        // 运费
+        $freight = $freeTemplate['freight'] ?? 0;
+        // 普通商品免运费阈值
+        $freeShipping = $freeTemplate['free_shipping'] ?? 0;
+
+        $this->_decAmount = $totalAmount;
+        $this->_decPv = $totalPv;
+        $this->_realPv = $totalRealPv;
+        $this->_freight = ($totalAmount >= $freeShipping) ? 0 : $freight;
+
+        $this->_payAmount = $this->_decAmount + $this->_freight;
+
+        $this->_decAmountStandard = $totalAmountStandard;
+        $this->_standardAmount = $this->_decAmountStandard + $this->_freight;
+
+        $db = \Yii::$app->db;
+        $transaction = $db->beginTransaction();
+
+        try {
+            //写入订单
+            if (!$orderResult = $this->addOrder()) {
+                throw new Exception(Form::formatErrorsForApi($orderResult->getErrors()));
+            }
+
+            if ($hasInstalment){
+                $instalmentModel = Instalment::findOne(['USER_ID' => $userId]);
+                if(!$instalmentModel) {
+                    $instalmentModel = new Instalment();
+                }
+                $instalmentModel->USER_ID = $userId;
+                $instalmentModel->STAGE = $hasInstalment;
+                $instalmentModel->UPDATE_TIME = time();
+                $instalmentModel->save();
+            }
+
+            $transaction->commit();
+
+            return $orderResult;
+        }catch (\Exception $e){
+            $transaction->rollBack();
+            $this->addError('add', $e->getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 复销订单
+     * @throws Exception
+     */
+    public function addOrder()
+    {
+        $periodObj = Period::instance();
+        $nowPeriodNum = $periodObj->getNowPeriodNum();
+        $nowCalcMonth = $periodObj->getYearMonth($nowPeriodNum);
+        //帮复消会员Id(登陆会员)
+        $loginUserId = \Yii::$app->user->id;
+        $loginUserName = Info::getUserNameByUserId($loginUserId);
+        //订单会员Id
+        $userId = Info::getUserIdByUserName($this->userName);
+        $countryId = Info::getUserCountryByUserId($userId);
+        $email = Info::getEmailByUserId($this->userName);
+        $country = Countries::getById($countryId);
+
+        $warehouse = '01';
+        $_hasPV = $this->_decPv;
+
+        $ordNo = $this->_generateSn();
+        $orderModel = new ApproachReconsumeOrder();
+        $orderModel->SN = 'OS' . $ordNo;
+        $orderModel->DEC_SN = 'DS' . $ordNo;
+        $orderModel->ORDER_TYPE = $this->type;
+        $orderModel->USER_ID = $userId;
+        $orderModel->USER_NAME = $this->userName;
+        $orderModel->ORDER_AMOUNT = $this->_decAmount;
+        $orderModel->PV = $_hasPV;
+        $orderModel->PAY_AMOUNT = $this->_payAmount;
+        $orderModel->PAY_PV = $_hasPV;
+        $orderModel->PAY_AT = 0;
+        $orderModel->PAY_TYPE = $this->payType;
+        $orderModel->PERIOD_NUM = $nowPeriodNum;
+        $orderModel->P_CALC_MONTH = Date::ociToDate($nowCalcMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH);
+        $orderModel->FREIGHT = $this->_freight;
+        $orderModel->PAY_FREIGHT = $this->_freight;
+        $orderModel->CONSIGNEE = $this->consignee;
+        $orderModel->MOBILE = $this->acceptMobile;
+        $orderModel->PROVINCE = $this->province[0];
+        $orderModel->LGA_NAME = $this->lgaName;
+        $orderModel->CITY_NAME = $this->cityName;
+        $orderModel->ADDRESS = $this->detailaddress;
+        $orderModel->FRONT_REMARK = $this->remark;
+        $orderModel->WAREHOUSE = $warehouse;
+        $orderModel->STATUS = \Yii::$app->params['orderStatus']['notPaid']['value'];
+        $orderModel->CREATED_AT = Date::nowTime();
+        $orderModel->CREATE_USER = $loginUserName;
+        $orderModel->EMAIL = $email ?? '';
+        $orderModel->ORDER_AMOUNT_STANDARD = $this->_decAmountStandard;
+        $orderModel->PAY_AMOUNT_STANDARD = $this->_standardAmount;
+        $orderModel->EXCHANGE_RATE = $this->exchangeRate;
+        $orderModel->COUNTRY_ID = $countryId;
+        $orderModel->CURRENCY_ID = $country['LOCAL_CURRENCY_ID'] ?? 0;
+        $orderModel->DEC_USER_ID = $this->decUserName;
+        $orderModel->AUTO_MAINTENANCE = $this->autoMaintenance ?? 0;
+        if(!$orderModel->save()){
+            throw new Exception(Form::formatErrorsForApi($orderModel->getErrors()));
+        }
+
+        // 加入商品到订单商品表
+        foreach($this->_orderGoods as $key=>$value) {
+            $this->_orderGoods[$key]['ORDER_SN'] = $orderModel->SN;
+            $this->_orderGoods[$key]['P_CALC_MONTH'] = Date::ociToDate($nowCalcMonth, Date::OCI_TIME_FORMAT_SHORT_MONTH);
+        }
+        ApproachOrderGoods::batchInsert($this->_orderGoods);
+
+        return $orderModel;
+    }
+
+    /**
+     * 生成流水号
+     * @return string
+     */
+    private function _generateSn() {
+        return Date::today('Ymd') . $this->_random(10, 1);
+    }
+
+    /**
+     * 生成随机数
+     * @param $length
+     * @param int $numeric
+     * @return string
+     */
+    private function _random($length, $numeric = 0) {
+        $seed = base_convert(md5(microtime() . $_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35);
+        $seed = $numeric ? (str_replace('0', '', $seed) . '012340567890') : ($seed . 'zZ' . strtoupper($seed));
+        $hash = '';
+        $max = strlen($seed) - 1;
+        for ($i = 0; $i < $length; $i++) {
+            $hash .= $seed[mt_rand(0, $max)];
+        }
+        return $hash;
+    }
+
+    private function orderAutoMaintenance($order)
+    {
+        $nowPeriodNum = Period::instance()->getNowPeriodNum();
+
+        try {
+            $sysConfig = Cache::getSystemConfig();
+            $divideLine = (int)$sysConfig['monthPcsPvFxCondition']['VALUE'];
+
+            $orderTotalBv = $order->PV;
+
+            if ($orderTotalBv <= $divideLine) {
+                return;
+            }
+
+            $lastAmpPeriod = AmpPeriod::find()
+                ->where(['USER_ID' => $order->USER_ID])
+                ->orderBy(['PERIOD_NUM' => SORT_DESC])
+                ->one();
+
+            $startPeriodNum = $lastAmpPeriod ? $lastAmpPeriod->PERIOD_NUM + 1 : $nowPeriodNum;
+
+            if (!$lastAmpPeriod) {
+                $memberOrderBv = Order::find()
+                    ->select('SUM(PV) AS total_pv')
+                    ->where([
+                        'USER_ID' => $order->USER_ID,
+                        'IS_DELETE' => 0,
+                        'PERIOD_NUM' => $nowPeriodNum
+                    ])
+                    ->scalar();
+
+                if ($memberOrderBv >= $divideLine) {
+                    $startPeriodNum = $nowPeriodNum + 1;
+                }
+            }
+
+            $periodCount = floor($orderTotalBv / $divideLine);
+            $remainderBv = $orderTotalBv % $divideLine;
+
+            $ampPeriods = [];
+            for ($i = 1; $i <= $periodCount; $i++) {
+                $currentBv = $i < $periodCount ? $divideLine : ($divideLine + $remainderBv);
+                $ampPeriods[] = [
+                    'USER_ID' => $order->USER_ID,
+                    'ORDER_SN' => $order->SN,
+                    'PERIOD_NUM' => $startPeriodNum + $i - 1,
+                    'BV' => $currentBv,
+                ];
+            }
+
+            if (!empty($ampPeriods)) {
+                AmpPeriod::batchInsert($ampPeriods);
+            }
+        } catch (\Exception $e) {
+            // 记录日志或进行错误处理
+            \Yii::error('Order auto maintenance failed: ' . $e->getMessage());
+        }
+    }
+}

+ 5 - 8
common/models/forms/OrderForm.php

@@ -1054,7 +1054,8 @@ class OrderForm extends Model
     /**
      * 帮会员复消的订单
      */
-    public function addUserOrder(){
+    public function addUserOrder()
+    {
         $periodObj = Period::instance();
         $nowPeriodNum = $periodObj->getNowPeriodNum();
         $nowCalcMonth = $periodObj->getYearMonth($nowPeriodNum);
@@ -1234,13 +1235,10 @@ class OrderForm extends Model
 
         foreach ($this->goodsNum as $k => $v) {
             if ($v) {
-                $goods = ShopGoods::findOneAsArray('ID=:ID AND STATUS=1',[':ID'=> $ids[$k]]);
+                $goods = ShopGoods::findOneAsArray('ID=:ID AND STATUS=1 AND AUTO_MAINTENANCE=1',[':ID'=> $ids[$k]]);
                 if (!$goods) {
                     throw new Exception(Yii::t('app', 'productsDoesSoldOut'));
                 }
-                if ($goods['AUTO_MAINTENANCE'] != 1) {
-                    throw new Exception(Yii::t('app', 'productsDoesNotAmp'));
-                }
 
                 $goodsNature = ShopGoodsNature::findOneAsArray('GOODS_ID=:GOODS_ID AND COUNTRY_ID=:COUNTRY_ID',
                     [':GOODS_ID' => $ids[$k], ':COUNTRY_ID' => $user['COUNTRY_ID']]);
@@ -1248,7 +1246,6 @@ class OrderForm extends Model
                     throw new Exception(Yii::t('app', 'productsDoesSoldOut'));
                 }
 
-                // 汇率
                 $this->exchangeRate = CurrencyConversions::getToUSDRate($goodsNature['LOCAL_CURRENCY_ID']);
 
                 if($goods['STORE_NUMS']>0){
@@ -1293,8 +1290,8 @@ class OrderForm extends Model
                         'TAX_RATE' => $goodsNature['TAX_RATE'],
                     ];
                 }
-                if($goods['INSTALMENT']>0){ // 如果有分期付款商品,检查用户的分期付款状态
-                    if($v>1){ // 不可以购买多个
+                if($goods['INSTALMENT']>0){
+                    if ($v > 1) {
                         throw new Exception(Yii::t('app', 'allowOnlyOne'));
                     }
 

+ 1 - 0
frontendApi/config/urlManagerRules.php

@@ -98,6 +98,7 @@ return [
             'POST sure-approach-reconsume-order' => 'sure-approach-reconsume-order',
             'GET auto-maintenance' => 'auto-maintenance',
             'POST auto-maintenance-sure-order' => 'auto-maintenance-sure-order',
+            'POST auto-maintenance-approach-sure-order' => 'auto-maintenance-approach-sure-order',
         ],
     ],
     [

+ 29 - 3
frontendApi/modules/v1/controllers/ShopController.php

@@ -29,6 +29,7 @@ use common\models\Currency;
 use common\models\CurrencyConversions;
 use common\models\DecOrder;
 use common\models\BaDecOrder;
+use common\models\forms\ApproachAutoMaintenanceOrderForm;
 use common\models\forms\ApproachOrderForm;
 use common\models\forms\ApproachReconsumeOrderForm;
 use common\models\forms\BaApproachOrderForm;
@@ -47,6 +48,7 @@ use common\models\UserBonus;
 use common\models\UserPerformance;
 use common\models\UserWallet;
 use Yii;
+use yii\base\Exception;
 use yii\data\Pagination;
 use yii\db\Query;
 use yii\web\HttpException;
@@ -1891,7 +1893,7 @@ ORDER;
     }
 
     /**
-     * 确认订单
+     * AMP
      */
     public function actionAutoMaintenanceSureOrder()
     {
@@ -1901,8 +1903,32 @@ ORDER;
             $formModel->remark = Yii::t('app', 'autoMaintenance');
             $post = \Yii::$app->request->post();
             $post['type'] = DeclarationForm::TYPE_FX;
-            if ($formModel->load($post, '') && $formModel->autoMaintenanceSureOrder()) {
-                return static::notice(Yii::t('app', 'successfully'));
+
+            try {
+                if ($formModel->load($post, '') && $formModel->autoMaintenanceSureOrder()) {
+                    return static::notice(Yii::t('app', 'successfully'));
+                } else {
+                    return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
+                }
+            } catch (HttpException|Exception|\Throwable $e) {
+                return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
+            }
+        }
+    }
+
+    /**
+     * AMP
+     */
+    public function actionApproachAutoMaintenanceSureOrder(){
+        if (\Yii::$app->request->isPost) {
+            $formModel = new ApproachAutoMaintenanceOrderForm();
+            $formModel->scenario = 'autoMaintenanceOrder';
+            $formModel->remark = 'Auto Maintenance Order';
+            $post = \Yii::$app->request->post();
+            $post['type'] = DeclarationForm::TYPE_FX;
+
+            if ($formModel->load($post, '') && $order = $formModel->autoMaintenanceSureOrder()) {
+                return static::notice($order);
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()),400);
             }