From 9ee09ea201abe8fa55272dea49a31413795a0bdc Mon Sep 17 00:00:00 2001 From: Vladimir Fomichev Date: Fri, 6 Mar 2026 15:57:46 +0300 Subject: [PATCH] =?utf8?q?=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- erp24/api2/controllers/BonusController.php | 3830 ++++++++++---------- plan_email.md | 293 -- 2 files changed, 1915 insertions(+), 2208 deletions(-) delete mode 100644 plan_email.md diff --git a/erp24/api2/controllers/BonusController.php b/erp24/api2/controllers/BonusController.php index c54ec8a7..6dc2a9f8 100644 --- a/erp24/api2/controllers/BonusController.php +++ b/erp24/api2/controllers/BonusController.php @@ -1,1915 +1,1915 @@ -response->format = \yii\web\Response::FORMAT_JSON; - $data = file_get_contents('php://input'); - $result = json_decode($data, true); - - $fl = date('_Y_m_d__H_i_s_'); - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . $result['phone']); - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - $__API_PARAMS = ['store_id', 'seller_id', 'phone']; // check_amount, items - - foreach ($__API_PARAMS as $paramName) { - if (empty($result[$paramName])) { - - if ($paramName != 'phone') { - LogService::apiErrorLog(json_encode(["error_id" => 0, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); - } - - return $this->asJson(["error_id" => 0, "error" => "$paramName is required"]); - } - } - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - $phone = ClientHelper::phoneClear($result['phone']); - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if (!ClientHelper::phoneVerify($phone)) { - return $this->asJson(["error_id" => 0.2, "error" => "phone is required"]); - } - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $result['phone'] = $phone; - - $check_amount = intval($result['check_amount'] ?? 0); - - $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->one(); - $bonusLevels = BonusLevels::find()->where(['active' => 1])->indexBy('alias')->asArray()->all(); - $bonusLevel = $user->bonus_level ?? "silver"; - - $bonus_rate = isset($bonusLevels[$bonusLevel]['bonus_rate']) - ? $bonusLevels[$bonusLevel]['bonus_rate'] / 100 - : self::$FIRST_SALE_PROCENT; - - $cashback_rate = isset($bonusLevels[$bonusLevel]['cashback_rate']) - ? $bonusLevels[$bonusLevel]['cashback_rate'] / 100 - : self::$FIRST_SALE_PROCENT; - - $mess = []; - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - // массив с id товарыми не участвующих в бонусной - $items_arr_no = array_values(ArrayHelper::map( - UniversalCatalogItem::find()->where(['catalog_alias' => 'unused_nomenclature'])->all(), 'guid', 'guid')); - $items_arr_no_bonus_writeoffs = array_values(ArrayHelper::map( - UniversalCatalogItem::find()->where(['catalog_alias' => 'non_bonusable_goods'])->all(), 'guid', 'guid')); - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - $all_amount = 0; - $has_actions = false; - $summa_no = 0; - $summa_no_writeoffs = 0; - if (!empty($result["items"])) { - foreach ($result["items"] as $item) { - if (in_array($item["product_id"], $items_arr_no)) { - $summa_no = $summa_no + $item["price"] * $item["quantity"]; - $has_actions = true; - } elseif (in_array($item["product_id"], $items_arr_no_bonus_writeoffs)) { - $summa_no_writeoffs = $summa_no_writeoffs + $item["price"] * $item["quantity"]; - } - $all_amount += $item["price"] * $item["quantity"]; - } - } - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $baza_nachislenie = $all_amount - $summa_no; - - $check_amount = $check_amount - $summa_no; - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - //$cnt = intval(Sales::find()->where(['phone' => $result['phone'], 'operation' => Sales::OPERATION_SALE])->count()); - //$max_procent = $cnt == 0 ? self::$FIRST_SALE_PROCENT : ($cnt == 1 ? self::$SECOND_SALE_PROCENT : self::$MAX_PROCENT); - $max_procent = $bonus_rate; - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $percent = ($result['phone'] == "79049031399") ? 0.9 : $max_procent; - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $userFound = Users::find()->where(['phone' => $result['phone']])->one(); - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - /** @var $userFound Users */ - $salesCount = -1; /* Из-за нулевого значения по умолчанию куча клиентов получило бонус 20% за покупку */ - if ($userFound && $userFound->telegram_created_at) { - $salesCount = intval(Sales::find()->where(['phone' => $result['phone'], 'operation' => Sales::OPERATION_SALE]) - ->andWhere(['>=', 'date', $userFound->telegram_created_at])->count()); - } - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $credit_procent = $userFound && $userFound->source > 0 && $salesCount == 0 ? self::$CREDIT_HIGH_PROCENT : $cashback_rate; - $will_be_credited_bonuses = $credit_procent * $baza_nachislenie; - - $will_be_credited_bonuses = round($will_be_credited_bonuses); - $mess["will_be_credited_bonuses"] = $will_be_credited_bonuses; - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $store_id = ClientHelper::getExportId($result['store_id'], "city_store", 1); - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - // Логи введённых номеров телефонов кассирами - $userPhone = UsersPhones::find()->where(['phone' => $result['phone']])->andWhere(['store_id' => $store_id]) - ->andWhere(['seller_id' => $result['seller_id']])->one(); - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if (!$userPhone) { - $userPhone = new UsersPhones(); - $userPhone->phone = $result['phone']; - $userPhone->store_id = $store_id; - $userPhone->store_guid = $result['store_id']; - $userPhone->seller_id = $result['seller_id']; - } - if (!$userPhone->store_guid) { - $userPhone->store_guid = $result['store_id']; - } - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $userPhone->date = date('Y-m-d H:i:s'); - $userPhone->save(); - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($userPhone->getErrors()) { - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => $userPhone->getErrors()], JSON_UNESCAPED_UNICODE)); - - return $this->asJson(["error_id" => 1, "error" => $userPhone->getErrors()]); - } - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->one(); - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if (!$user) { - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '-нет в бонусной программе-' . __LINE__, FILE_APPEND); - $mess["new_client"] = true; - $mess["message_cashier"] = "Заполните данные клиента"; - $mess["error"] = "Покупателя " . $result['phone'] . " нет в бонусной программе!"; - - return $this->asJson($mess); - } - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $keycode = $user->keycode; - $black_list = $user->black_list; - - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if (!$black_list) { - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $stop = UsersStopList::find()->select(['phone'])->where(['phone' => $result['phone']])->one(); - if ($stop) { - $black_list = 1; - $user->black_list = 1; - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $user->save(); - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($user->getErrors()) { - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - LogService::apiErrorLog(json_encode(["error_id" => 3, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE)); - - return $this->asJson(["error_id" => 3, "error" => $user->getErrors()]); - } - } - } - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $name = $user->name; - $user_balans = ClientHelper::getBonusBalance($result['phone']); - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $baza_spisanie = $baza_nachislenie - $summa_no_writeoffs; - if ($baza_spisanie < 0) { - $baza_spisanie = 0; - } - $max = $baza_spisanie * $percent; // максимально можем разрешить списывать до 30 процентов от суммы заказа - $max = round($max); - $available_bonus = $user_balans; - if ($available_bonus > $max) { // если баллов бонусов больше чем 30 процентов списываем по максимуму 30 - $available_bonus = $max; - } -// $baza = $check_amount - $bonus; - - $mess["message_cashier"] = "Клиент $name найден"; // Код: $keycode - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($black_list) { - $mess['error'] = 'Этот номер в черном списке'; - - return $this->asJson($mess); - } - - $txt = $has_actions ? 'В чеке есть акционные товары, на них бонусы не начислятся.' : ''; - - $mess["result"] = true; - $mess["auth_code"] = $keycode; - $mess["name"] = $name; - $mess["total_bonuses"] = $user_balans; - $mess["bonus_level"] = $bonusLevel; - $mess["burn_balans"] = $user->burn_balans; - $mess["available_bonuses"] = $available_bonus; - $mess["message_cashier"] = $txt . " Спросите последние 4 цифры телефона который позвонит клиенту $user_balans"; - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); - - file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__ . ' OK ', FILE_APPEND); - return $this->asJson($mess); - } - -// public function actionSendMessage() -// { -// Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; -// $data = file_get_contents('php://input'); -// $result = json_decode($data, true); -// -// $__API_PARAMS = ['store_id', 'seller_id', 'phone']; -// -// foreach ($__API_PARAMS as $paramName) { -// if (empty($result[$paramName])) { -// -// if ($paramName != 'phone') { -// LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); -// } -// -// return $this->asJson(["error" => "$paramName is required"]); -// } -// } -// -// $phone = ClientHelper::phoneClear($result['phone']); -// if (!ClientHelper::phoneVerify($phone)) { -// return $this->asJson(["error_id" => 0.2, "error" => "phone is required"]); -// } -// $result['phone'] = $phone; -// -// $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->andWhere(['black_list' => '0'])->one(); -// if (!$user) { -// $mess["error"] = "Покупателя " . $result['phone'] . " нет в бонусной программе!"; -// -// return $this->asJson($mess); -// } -// $keycode = $user->keycode; -// -// $mess = []; -// $mess["result"] = true; -// -// $mess['auth_code'] = $user->keycode; -// $mess['message_cashier'] = 'Отсканируйте QR код из телеграм бота или введите его руками'; -// -// LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); -// -// return $this->asJson($mess); -// } - - public function actionSendMessage() { - Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; - $data = file_get_contents('php://input'); - $result = json_decode($data, true); - - $__API_PARAMS = ['store_id', 'seller_id', 'phone']; - - foreach ($__API_PARAMS as $paramName) { - if (empty($result[$paramName])) { - - if ($paramName != 'phone') { - LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); - } - - return $this->asJson(["error" => "$paramName is required"]); - } - } - - $phone = ClientHelper::phoneClear($result['phone']); - if (!ClientHelper::phoneVerify($phone)) { - return $this->asJson(["error_id" => 0.2, "error" => "phone is required"]); - } - $result['phone'] = $phone; - - $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->andWhere(['black_list' => '0'])->one(); - if (!$user) { - $mess["error"] = "Покупателя " . $result['phone'] . " нет в бонусной программе!"; - - return $this->asJson($mess); - } - - $mess["message_cashier"] = "Звонок-последние 4 цифры телефона"; - - $userAuthCallLog = UsersAuthCallLog::find()->select(['COUNT(*) as cnt'])->where(['phone' => $result['phone']]) - ->andWhere(['store_id' => $result['store_id']])->andWhere(['>=', 'date', date('Y-m-d H:i:s', strtotime('-10 minutes'))])->one(); - - $cnt = $userAuthCallLog ? $userAuthCallLog->cnt : 1; - - $keycode = ''; - - if ($cnt < 2) { - $body = @file_get_contents("https://sms.ru/code/call?phone=" . $result['phone'] . "&api_id=4DFE45F9-1897-79C0-6872-08F05D6B7FA4&ip=" . $_SERVER["REMOTE_ADDR"]); - $json_res = json_decode($body, true, 512, JSON_UNESCAPED_UNICODE); - if ($json_res["status"] == "OK") { - $keycode = $json_res["code"]; - $user->keycode = '' . $keycode; - $user->password = ClientHelper::generatePassword(8);; - $user->save(); - if ($user->getErrors()) { - LogService::apiErrorLog(json_encode(["error_id" => 3.1415, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE)); - return $this->asJson(["error_id" => 3.1415, "error" => $user->getErrors()]); - } - $mess["auth_code"] = $keycode; - $mess["message_cashier"] = "Попытка:$cnt Звонок клиенту! последние 4 цифры номера"; - } - } elseif ($cnt > 2) { - $mess["message_cashier"] = "Попытка $cnt -извиняемся перед клиентом"; - } - $name = "$keycode Попытка $cnt " . $_SERVER["REMOTE_ADDR"]; - $userAuthCallLog = new UsersAuthCallLog; - $userAuthCallLog->date = date('Y-m-d H:i:s'); - $userAuthCallLog->store_id = $result['store_id']; - $userAuthCallLog->seller_id = $result['seller_id']; - $userAuthCallLog->phone = $result['phone']; - $userAuthCallLog->name = $name; - $userAuthCallLog->save(); - if ($userAuthCallLog->getErrors()) { - LogService::apiErrorLog(json_encode(["error_id" => 4.15, "error" => $userAuthCallLog->getErrors()], JSON_UNESCAPED_UNICODE)); - return $this->asJson(["error_id" => 4.15, "error" => $userAuthCallLog->getErrors()]); - } - - Yii::info("keykod={$user->keycode} store_id={$result['store_id']} seller_id={$result['seller_id']} phone={$result['phone']} $name", self::LOG_CATEGORY_BONUS); - - $mess["timeout"] = 15; - - LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); - - return $this->asJson($mess); - } - -// $mess["message_cashier"] = "Звонок-последние 4 цифры телефона"; -// -// $userAuthCallLog = UsersAuthCallLog::find()->select(['COUNT(*) as cnt'])->where(['phone' => $result['phone']]) -// ->andWhere(['store_id' => $result['store_id']])->andWhere(['>=', 'date', date('Y-m-d H:i:s', strtotime('-10 minutes'))])->one(); -// -// $cnt = $userAuthCallLog ? $userAuthCallLog->cnt : 1; -// -// if ($cnt < 2) { -// $body = @file_get_contents("https://sms.ru/code/call?phone=" . $result['phone'] . "&api_id=4DFE45F9-1897-79C0-6872-08F05D6B7FA4&ip=" . $_SERVER["REMOTE_ADDR"]); -// $json_res = json_decode($body, true, 512, JSON_UNESCAPED_UNICODE); -// -// if ($json_res["status"] == "OK") { -// $keycode = $json_res["code"]; -// $user->keycode = '' . $keycode; -// $user->password = ClientHelper::generatePassword(8);; -// $user->save(); -// -// if ($user->getErrors()) { -// -// LogService::apiErrorLog(json_encode(["error_id" => 3, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE)); -// -// return $this->asJson(["error_id" => 3, "error" => $user->getErrors()]); -// } -// -// $mess["auth_code"] = $keycode; -// $mess["message_cashier"] = "Попытка:$cnt Звонок клиенту! последние 4 цифры номера"; -// } -// } else if ($cnt > 2) { -// $mess["message_cashier"] = "Попытка $cnt -извиняемся перед клиентом"; -// } -// -// $name = "$keycode Попытка $cnt " . $_SERVER["REMOTE_ADDR"]; -// $userAuthCallLog = new UsersAuthCallLog; -// $userAuthCallLog->date = date('Y-m-d H:i:s'); -// $userAuthCallLog->store_id = $result['store_id']; -// $userAuthCallLog->seller_id = $result['seller_id']; -// $userAuthCallLog->phone = $result['phone']; -// $userAuthCallLog->name = $name; -// $userAuthCallLog->save(); -// if ($userAuthCallLog->getErrors()) { -// -// LogService::apiErrorLog(json_encode(["error_id" => 4, "error" => $userAuthCallLog->getErrors()], JSON_UNESCAPED_UNICODE)); -// -// return $this->asJson(["error_id" => 4, "error" => $userAuthCallLog->getErrors()]); -// } -// - - public function actionSaveClientInfo() - { - Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; - $data = file_get_contents('php://input'); - $result = json_decode($data, true); - - $fl = date('_Y_m_d__H_i_s_'); - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . $result['phone']); - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - - $__API_PARAMS = ['store_id', 'seller_id', 'phone']; // first_name, second_name, sex, birth_day, referral_id, comment, events - - foreach ($__API_PARAMS as $paramName) { - if (empty($result[$paramName])) { - - if ($paramName != 'phone') { - LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); - } - - return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); - } - } - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $phone = ClientHelper::phoneClear($result['phone']); - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if (!ClientHelper::phoneVerify($phone)) { - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - return $this->asJson(["error_id" => 1.2, "error" => "phone is required"]); - } - $result['phone'] = $phone; - - $source = $result["source"] ?? 0; - $store_id = $result["store_id"]; - $store_id_guid = $store_id; - $seller_id = $result["seller_id"]; - $phone = $result["phone"]; - $first_name = $result["first_name"] ?? ""; - $second_name = $result["second_name"] ?? ""; - $sex2 = $result["sex"] ?? ""; - $birth_day = $result["birth_day"] ?? ""; - $referral_id = $result["referral_id"] ?? null; - $comment = $result["comment"] ?? ""; - $events = $result["events"] ?? []; - $sex = "man"; - if ($sex2 == "male") { - $sex = "man"; - } - if ($sex2 == "female") { - $sex = "women"; - } -// if ($referral_phone == $phone) { -// $referral_phone = ""; -// } - - $mess = []; - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - /* @var $user Users */ - $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->andWhere(['black_list' => '0'])->one(); - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($user) { - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $user->referral_id = $referral_id != $user->id ? $referral_id : null; - $user->pol = $sex; - $user->bdate = $birth_day; - $user->name = "$first_name $second_name"; - $user->comment = $comment; - $user->password = ClientHelper::generatePassword(8); - $user->keycode = '' . rand(1000, 9999); - $user->source = $source == 2 ? 1 : 0; - $user->save(); // иначе не пройдём валидацию, т.к. множество полей в бд не заполнены. - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($user->getErrors()) { - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - LogService::apiErrorLog(json_encode(["error_id" => 2, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE)); - - return $this->asJson(["error_id" => 2, "error" => $user->getErrors()]); - } - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $userEventOld = UsersEvents::find()->where(['phone' => $phone])->orderBy(['date_add' => SORT_ASC])->one(); - if ($userEventOld && $userEventOld->date_add < date('Y-m-d H:i:s', time() - 2 * 86400)) { // Дата добавление последнего события не старше двух дней - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $mess["result"] = true; - $mess["message_cashier"] = "Возможность внесения памятных дат ограничена"; - - LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); - - return $this->asJson($mess); - } - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - $nomer_event = 1; - $dates = []; - - foreach ($events as $k => $mass) { - $date = $mass["date"] ?? ''; - $event_id = intval($mass['event_id'] ?? 0); - - $datea = explode("-", $date); - $date_end = date("Y", time() + self::$YEAR_PERIOD * 86400) . "-" . $datea[1] . "-" . $datea[2]; - $userEvent2 = UsersEvents::find()->where(['phone' => $phone])->andWhere(['date_day' => $datea[2]])->andWhere(['date_month' => $datea[1]])->one(); - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($userEvent2) { - $userEvent2->delete(); - } - $userEvent3 = new UsersEvents; - $userEvent3->number = $nomer_event; - $userEvent3->date = $date; - $userEvent3->tip_id = $event_id; - $userEvent3->phone = $phone; - $userEvent3->date_day = $datea[2]; - $userEvent3->date_month = $datea[1]; - $userEvent3->date_add = date('Y-m-d H:i:s'); - $userEvent3->tip = strval('???'); - $userEvent3->name = 'М'; - $userEvent3->sex = 'm'; - $userEvent3->date_edit = date("Y-m-d H:i:s"); - $userEvent3->date_edit_info = date("Y-m-d H:i:s"); - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $userEvent3->save(); // иначе не пройдём валидацию, т.к. множество полей в бд не заполнены. - if ($userEvent3->getErrors()) { - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - LogService::apiErrorLog(json_encode(["error_id" => 3, "error" => $userEvent3->getErrors()], JSON_UNESCAPED_UNICODE)); - - return $this->asJson(["error_id" => 3, "error" => $userEvent3->getErrors()]); - } - $dates [] = $date; - $nomer_event++; - } - } else { - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $created_name = $seller_id; - $rand = rand(1000, 9999); - $name = "$first_name $second_name"; - $pass = ClientHelper::generatePassword(8); - $product1 = Products1c::find()->select(['name'])->where(['tip' => 'admin'])->andWhere(['id' => $seller_id])->one(); - $product2 = Products1c::find()->select(['name'])->where(['tip' => 'city_store'])->andWhere(['id' => $store_id])->one(); - - $created_name = $product1 ? $product1->name : ''; - $created_store = $product2 ? $product2->name : ''; - - $store_id_new = ExportImportTable::find()->select(['entity_id'])->where(['entity' => 'city_store'])->andWhere(['export_id' => '1']) - ->andWhere(['export_val' => $store_id])->one(); - $seller_id_new = ExportImportTable::find()->select(['entity_id'])->where(['entity' => 'admin'])->andWhere(['export_id' => '1']) - ->andWhere(['export_val' => $seller_id])->one(); - if ($store_id_new) { - $store_id_int = $store_id_new->entity_id; - } - if ($seller_id_new) { - $seller_id_int = $seller_id_new->entity_id; - } - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - Users::deleteAll(['phone' => $phone, 'phone_true' => '0']); - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $user2 = new Users; - $user2->source = $source; - $user2->pol = $sex; - $user2->keycode = strval($rand); - $user2->phone = $phone; - $user2->name = $name; - $user2->name_name = $first_name; - $user2->name_last = $second_name; - $user2->password = $pass; - $user2->phone_true = strval(1); - $user2->bdate = $birth_day; - $user2->referral_id = $referral_id; - $user2->comment = $comment; - $user2->created_id = $seller_id_int ?? 0; - $user2->created_name = $created_name; - $user2->seller_id = strval($seller_id); - $user2->store_id = $store_id_guid; - $user2->created_store_id = $store_id_int ?? 0; - $user2->created_store = $created_store; - $user2->date = date('Y-m-d H:i:s'); - $user2->sale_store_id = $store_id_int ?? 0; - $user2->sale_store = ''; - $user2->sms_info = 1; - $user2->reklama_info = 1; - $user2->info = ''; - $setka_id = 1; - $user2->setka_id = $setka_id; - $user2->card = "" . ($phone * 2 + 1608 + $setka_id); // генерируем номер карты который зависит от номера сетки + ДР Тимура - $user2->save(); // иначе не пройдём валидацию, т.к. множество полей в бд не заполнены. - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($user2->getErrors()) { - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - LogService::apiErrorLog(json_encode(["error_id" => 4, "error" => $user2->getErrors()], JSON_UNESCAPED_UNICODE)); - - return $this->asJson(["error_id" => 4, "error" => $user2->getErrors()]); - } - - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($store_id == '56524cb1-4763-11ea-8cce-b42e991aff6c') { - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $admin_id = ClientHelper::getExportId($seller_id, "admin", 1); - - $usersBonus = new UsersBonus; - $usersBonus->date = date('Y-m-d H:i:s'); - $usersBonus->tip = 'plus'; - $usersBonus->tip_sale = 'podarok'; - $usersBonus->phone = $phone; - $usersBonus->name = "Приветственные бонусы посетителю сайта"; - $usersBonus->store_id = $store_id_int ?? 0; - $usersBonus->site_id = 0; - $usersBonus->referal_id = 0; - $usersBonus->admin_id = $admin_id; - $usersBonus->price = 0; - $usersBonus->price_skidka = 0; - $usersBonus->bonus = 50; - $usersBonus->store_id_1c = $store_id; - $usersBonus->seller_id_1c = $seller_id; - $usersBonus->date_start = date('Y-m-d 08:00:00', strtotime('+1 day', strtotime($usersBonus->date))); - $usersBonus->date_end = date('Y-m-d H:i:s', strtotime('+1 week', strtotime($usersBonus->date_start))); - $usersBonus->save(); - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($usersBonus->getErrors()) { - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - LogService::apiErrorLog(json_encode(["error_id" => 5, "error" => $usersBonus->getErrors()], JSON_UNESCAPED_UNICODE)); - - return $this->asJson(["error_id" => 5, "error" => $usersBonus->getErrors()]); - } - } - } - - $mess["result"] = true; - $mess["message_cashier"] = "Данные клиента сохранены"; - - LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); - - file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__ . ' OK ', FILE_APPEND); - return $this->asJson($mess); - } - - public function actionSale() - { - Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; - $data = file_get_contents('php://input'); - $result = json_decode($data, true); - $resultTest = $result; - $fl = date('_Y_m_d__H_i_s_'); - $json=json_encode($resultTest,JSON_UNESCAPED_UNICODE); - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '_info.json', PHP_EOL . '--' . $result['phone']); - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '_info.json', ' '.date("d.m.Y H:i:s",time()).' JSON: '.$json.' ', FILE_APPEND); - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . $result['phone']); - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - $__API_PARAMS = ['store_id', 'seller_id', 'phone', 'check_amount', 'check_id', 'check_name']; // items, auth_code, write_off_bonuses - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - foreach ($__API_PARAMS as $paramName) { - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if (empty($result[$paramName])) { - - if ($paramName != 'phone') { - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '-error-' . __LINE__, FILE_APPEND); - LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); - } - - return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); - } - } - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $phone = ClientHelper::phoneClear($result['phone']); - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if (!ClientHelper::phoneVerify($phone)) { - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '-error-' . __LINE__, FILE_APPEND); - return $this->asJson(["error_id" => 1.2, "error" => "phone is required"]); - } - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $result['phone'] = $phone; - $result['items'] = $result['items'] ?? []; - - $store_id = $result["store_id"]; - $seller_id = $result["seller_id"]; - $check_amount = $result["check_amount"]; - $check_id = $result["check_id"]; - $check_name = $result["check_name"]; - $lid_id = $result["lid_id"] ?? 0; - $auth_code = $result['auth_code'] ?? 0; - $write_off_bonuses = intval($result["write_off_bonuses"] ?? 0); // только при продаже - - $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->one(); - $bonusLevels = BonusLevels::find()->where(['active' => 1])->indexBy('alias')->asArray()->all(); - $bonusLevel = $user->bonus_level ?? "silver"; - $cashback_rate = isset($bonusLevels[$bonusLevel]['cashback_rate']) - ? $bonusLevels[$bonusLevel]['cashback_rate'] / 100 - : self::$FIRST_SALE_PROCENT; - -// $referal_rate = isset($bonusLevels[$bonusLevel]['referal_rate']) -// ? $bonusLevels[$bonusLevel]['referal_rate'] / 100 -// : self::$CREDIT_HIGH_PROCENT; - - $bonus_rate = isset($bonusLevels[$bonusLevel]['bonus_rate']) - ? $bonusLevels[$bonusLevel]['bonus_rate'] / 100 - : self::$FIRST_SALE_PROCENT; - - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $amount_real = 0; - $items_arr_no = array_values(ArrayHelper::map( - UniversalCatalogItem::find()->where(['catalog_alias' => 'unused_nomenclature'])->all(), 'guid', 'guid')); - $items_arr_no_bonus_writeoffs = array_values(ArrayHelper::map( - UniversalCatalogItem::find()->where(['catalog_alias' => 'non_bonusable_goods'])->all(), 'guid', 'guid')); - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $summa_no = 0; - $summa_no_writeoffs = 0; - $amount_all = 0; - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - foreach ($result['items'] as $item) { - if (in_array($item["product_id"], $items_arr_no)) { - $summa_no = $summa_no + $item["price"] * $item["quantity"]; - } else if (in_array($item["product_id"], $items_arr_no_bonus_writeoffs)) { - $summa_no_writeoffs = $summa_no_writeoffs + $item["price"] * $item["quantity"]; - } else { - $amount_real = $amount_real + $item["price"] * $item["quantity"]; - } - $amount_all = $amount_all + $item["price"] * $item["quantity"]; - } - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - $cnt = intval(Sales::find()->where(['phone' => $result['phone'], 'operation' => Sales::OPERATION_SALE])->count()); - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - // $max_procent = $cnt == 0 ? self::$FIRST_SALE_PROCENT : ($cnt == 1 ? self::$SECOND_SALE_PROCENT : self::$MAX_PROCENT); - $max_procent = $bonus_rate; - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - // если списывается в попытке больше бонусов чем может списаться - - $percent = $phone == "79049031399" ? 0.9 : $max_procent; - $write_off_bonuses_theory = round($amount_real * $percent); - if ($write_off_bonuses > $write_off_bonuses_theory) { - $write_off_bonuses = $write_off_bonuses_theory; - } - $user_balans = ClientHelper::getBonusBalance($phone); - if ($user_balans < $write_off_bonuses) { - $write_off_bonuses = $user_balans; - } - - // TO8-22: Промо-списание БЛАГО - // Проверяем: если у клиента есть промо-баланс >= 350, покупка >= 1700 и 350 > стандартного максимума — списываем промо - $usePromoWriteOff = false; - $promoWriteOffAmount = 350; - $promoMinCheckAmount = 1700; - - $now = date('Y-m-d H:i:s'); - $promoPlusSum = (float) UsersBonus::find() - ->where(['phone' => $phone, 'tip' => 'plus', 'tip_sale' => Promocode::TIP_SALE_PROMOBONUS]) - ->andWhere(['<=', 'date_start', $now]) - ->andWhere(['>=', 'date_end', $now]) - ->sum('bonus'); - $promoMinusSum = (float) UsersBonus::find() - ->where(['phone' => $phone, 'tip' => 'minus', 'tip_sale' => Promocode::TIP_SALE_PROMOBONUS]) - ->sum('bonus'); - $promoBalance = max(0, $promoPlusSum - $promoMinusSum); - - if ($promoBalance >= $promoWriteOffAmount - && $amount_all >= $promoMinCheckAmount - && $promoWriteOffAmount > $write_off_bonuses_theory - ) { - $usePromoWriteOff = true; - $write_off_bonuses = $promoWriteOffAmount; - } - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - // сумма со скидкой - $summa_chek = $amount_all - $write_off_bonuses; - $baza_back = $amount_real + $summa_no_writeoffs - $write_off_bonuses; - - $mess = []; - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - /** @var $user Users */ - if (!$user) { - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $mess["error"] = "Покупателя " . $result['phone'] . " нет в бонусной программе!"; - - return $this->asJson($mess); - } - // TO8-22: При промо-списании burn_balans не трогаем — списываются промо-бонусы, а не обычные - if (!$usePromoWriteOff) { - $user->burn_balans = max(0, $user->burn_balans - $write_off_bonuses); - } - // [balans - burn_balance, burn_balans] - показать клиенту что мы сожгли сжигаемый баланс - -// старая точка проверки кода -// file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); -// if ($user->keycode != strval($auth_code)) { -// -// file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '-auth_code not valid-' . __LINE__ . ' keycode ' .$user->keycode . '| auth_code ' . strval($auth_code), FILE_APPEND); -// return $this->asJson(['error' => 'auth_code not valid']); -// } - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $user_id = $user->id; -// $keycode = $user->keycode; -// $name = $user->name; -// $referral_id = $user->referral_id; -// $sale_avg_price = $user->sale_avg_price; - $sale_price = $user->sale_price; - $sale_cnt = $user->sale_cnt; -// if ($referral_id == $user_id) { -// $referral_id = 0; -// } - $ip = $_SERVER['REMOTE_ADDR']; - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - - $store_id_1c = $store_id; - $site_id = 0; - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - // получаем внутренний ID продаца - сотрудника из таблицы admin - $admin_id = ClientHelper::getExportId($seller_id, "admin", 1); - // получаем внутренний ID продаца - сотрудника из таблицы admin - $store_id = ClientHelper::getExportId($store_id_1c, "city_store", 1); - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $writeOffAlready = false; - if (!empty($lid_id)) { - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $tipSaleForCheck = $usePromoWriteOff ? Promocode::TIP_SALE_PROMOBONUS : 'sale'; - $writeOffAlready = UsersBonus::find()->where(['lid_id' => $lid_id, 'phone' => $phone, 'tip_sale' => $tipSaleForCheck, 'tip' => 'minus'])->one() != null; - } - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - $user_balans_new = $user_balans; - if ($write_off_bonuses && !$writeOffAlready) { - // TO8-22: При промо-списании auth_code не требуется (списание автоматическое) - if (!$usePromoWriteOff) { - // Проверка кода только при стандартном списании - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($user->keycode != strval($auth_code)) { - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '-auth_code not valid-' . __LINE__ . ' keycode ' .$user->keycode . '| auth_code ' . strval($auth_code), FILE_APPEND); - return $this->asJson(['error' => 'auth_code not valid']); - } - } - - // TO8-22: При промо-списании обычный баланс не уменьшается — списываются промо-бонусы - $user_balans_new = $usePromoWriteOff ? $user_balans : ($user_balans - $write_off_bonuses); - $tipSaleForWriteOff = $usePromoWriteOff ? Promocode::TIP_SALE_PROMOBONUS : 'sale'; - $name_b = $usePromoWriteOff - ? "Списание промо-бонусов БЛАГО по чеку $check_name" - : "Спиcание бонусов по чеку $check_name"; - $usersBonus = new UsersBonus; - $usersBonus->date = date('Y-m-d H:i:s'); - $usersBonus->tip = 'minus'; - $usersBonus->tip_sale = $tipSaleForWriteOff; - $usersBonus->phone = $phone; - $usersBonus->name = $name_b; - $usersBonus->check_id = $check_id; - $usersBonus->store_id = $store_id; - $usersBonus->ip = $ip; - $usersBonus->site_id = $site_id; // ??? - $usersBonus->referal_id = 0;// $referal_id; - $usersBonus->admin_id = $admin_id; - $usersBonus->price = $summa_chek; - $usersBonus->price_skidka = $write_off_bonuses; - $usersBonus->bonus = $write_off_bonuses; - $usersBonus->store_id_1c = $store_id_1c; - $usersBonus->seller_id_1c = $seller_id; - $usersBonus->user_id = $user_id; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении - $usersBonus->lid_id = $lid_id; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении - $usersBonus->date_start = $usersBonus->date; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении - $usersBonus->date_end = $usersBonus->date; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении - $usersBonus->date_dell = $usersBonus->date; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - $usersBonus->save(); // иначе не пройдём валидацию, т.к. множество полей в бд не заполнены. - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - if ($user->first_minus_balance === null) { - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $user->first_minus_balance = $usersBonus->date; - $user->save(); - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - } - - if ($usersBonus->getErrors()) { - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - LogService::apiErrorLog(json_encode(["error_id" => 4, "error" => $usersBonus->getErrors()], JSON_UNESCAPED_UNICODE)); - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - return $this->asJson(["error_id" => 4, "error" => $usersBonus->getErrors()]); - } - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - Yii::info("MINUS write_off_bonuses={$write_off_bonuses}", self::LOG_CATEGORY_BONUS); - } - // TO8-22: При промо-списании кэшбек НЕ начисляется - if ($usePromoWriteOff) { - Yii::info("PROMO write_off={$write_off_bonuses}, no cashback for {$phone}", self::LOG_CATEGORY_BONUS); - - // Обновляем поля пользователя (аналогично стандартному пути) - $sale_price += $check_amount; - $sale_avg_price = round($sale_price / ($sale_cnt + 1)); - - $user->keycode = "" . rand(1000, 9999); - $user->password = ClientHelper::generatePassword(8); - $user->date_last_sale = date('Y-m-d H:i:s'); - $user->sale_cnt = $sale_cnt + 1; - if ($user->sale_cnt == 1) { - $user->date_first_sale = $user->date_last_sale; - } - $user->sale_store_id = $store_id; - $user->sale_price = $sale_price; - $user->sale_avg_price = $sale_avg_price; - $user->check_id_last_sale = $check_id; - if (!$user->date) { - $user->date = (new \DateTime('now', new \DateTimeZone('Europe/Moscow')))->format('Y-m-d H:i:sP'); - } - $user->balans = ClientHelper::getBonusBalance($phone); - $user->save(); - - if ($user->getErrors()) { - LogService::apiErrorLog(json_encode(["error_id" => 6, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE)); - return $this->asJson(["error_id" => 6, "error" => $user->getErrors()]); - } - - $this->updateUserBonusLevel($user, $sale_price, $check_id, $check_name); - - $mess["write_off_bonuses"] = $write_off_bonuses; - $mess["summa_chek"] = $summa_chek; - $mess["bonus_back"] = 0; - $mess["user_balans"] = $user_balans_new; - $mess["user_balans_actual"] = $user->balans; - $mess["promo_writeoff"] = true; - - return $this->asJson($mess); - } - - //начисляем кэшбек клиенту 10% от покупки - с базы за вычитом бонусов которые он списывает - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $userFound = Users::find()->where(['phone' => $result['phone']])->one(); - /** @var $userFound Users */ - $salesCount = -1; /* Из-за нулевого значения по умолчанию куча клиентов получило бонус 20% за покупку */ - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($userFound && $userFound->telegram_created_at) { - $salesCount = intval(Sales::find()->where(['phone' => $result['phone'], 'operation' => Sales::OPERATION_SALE]) - ->andWhere(['>=', 'date', $userFound->telegram_created_at])->count()); - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - } - $credit_procent_index = $userFound && $userFound->source > 0 && $salesCount == 0 ? 1 : 0; - - - $back10 = $back20 = 0; - $back1 = $back = round($baza_back * $cashback_rate); - $nm = "Возврат с покупки " . (100 * $cashback_rate) . "% $check_name сумма чека $check_amount"; - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $userBonus2 = UsersBonus::find()->where(['phone' => $phone])->andWhere(['check_id' => $check_id])->andWhere(['site_id' => $site_id]) - ->andWhere(['store_id' => $store_id])->andWhere(['tip' => 'plus'])->andWhere(['bonus' => $back])->andWhere(['name' => $nm])->one(); - if (!$userBonus2) { - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $user_balans_new += $back; - - $userBonus2 = new UsersBonus; - $userBonus2->tip = 'plus'; - $userBonus2->tip_sale = 'sale'; - $userBonus2->date = date('Y-m-d H:i:s'); - $userBonus2->date_start = date('Y-m-d H:i:s', strtotime('+1 day', time())); - $userBonus2->date_end = date('Y-m-d H:i:s', strtotime('+' . self::$YEAR_PERIOD . ' day', time())); - $userBonus2->phone = $phone; - $userBonus2->name = $nm; - $userBonus2->check_id = $check_id; - $userBonus2->store_id = $store_id; - $userBonus2->bonus = $back; - $userBonus2->ip = $ip; - $userBonus2->site_id = $site_id; - $userBonus2->referal_id = 0; // $referal_id; - $userBonus2->admin_id = $admin_id; - $userBonus2->price = $summa_chek; - $userBonus2->store_id_1c = $store_id_1c; - $userBonus2->seller_id_1c = $seller_id; - $userBonus2->user_id = $user_id; - $userBonus2->lid_id = $lid_id; - $userBonus2->price_skidka = 0; - $userBonus2->date_dell = $userBonus2->date_end; - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $userBonus2->save(); - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($userBonus2->getErrors()) { - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - LogService::apiErrorLog(json_encode(["error_id" => 5, "error" => $userBonus2->getErrors()], JSON_UNESCAPED_UNICODE)); - - return $this->asJson(["error_id" => 5, "error" => $userBonus2->getErrors()]); - } else { - $back10 = $back; - } - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - Yii::info("PLUS bonus={$back}", self::LOG_CATEGORY_BONUS); - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($credit_procent_index) { - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $back = round($baza_back * self::$CREDIT_HIGH_PROCENT_PART20); - $nm = "Возврат с покупки " . (100 * self::$CREDIT_HIGH_PROCENT_PART20) . "% $check_name сумма чека $check_amount"; - - $user_balans_new += $back; - - $userBonus2 = new UsersBonus(); - $userBonus2->tip = 'plus'; - $userBonus2->tip_sale = 'sale'; - $userBonus2->date = date('Y-m-d H:i:s'); - $userBonus2->date_start = date('Y-m-d H:i:s', strtotime('+1 day', time())); - $userBonus2->date_end = date('Y-m-d H:i:s', strtotime('+3 month', time())); - $userBonus2->phone = $phone; - $userBonus2->name = $nm; - $userBonus2->check_id = $check_id; - $userBonus2->store_id = $store_id; - $userBonus2->bonus = $back; - $userBonus2->ip = $ip; - $userBonus2->site_id = $site_id; - $userBonus2->referal_id = 0; - $userBonus2->admin_id = $admin_id; - $userBonus2->price = $summa_chek; - $userBonus2->store_id_1c = $store_id_1c; - $userBonus2->seller_id_1c = $seller_id; - $userBonus2->user_id = $user_id; - $userBonus2->lid_id = $lid_id; - $userBonus2->price_skidka = 0; - $userBonus2->date_dell = $userBonus2->date_end; - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $userBonus2->save(); - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($userBonus2->getErrors()) { - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - LogService::apiErrorLog(json_encode(["error_id" => 5.2, "error" => $userBonus2->getErrors()], JSON_UNESCAPED_UNICODE)); - - return $this->asJson(["error_id" => 5.2, "error" => $userBonus2->getErrors()]); - } else { - $back20 = $back; - } - if ($userFound->telegram_created_at == null) { - $userFound->telegram_created_at = date("Y-m-d H:i:s"); - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $userFound->save(); - if ($userFound->getErrors()) { - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - - LogService::apiErrorLog(json_encode(["error_id" => 5.3, "error" => $userFound->getErrors()], JSON_UNESCAPED_UNICODE)); - - return $this->asJson(["error_id" => 5.3, "error" => $userFound->getErrors()]); - } - } - - $notifiableUser = new NotifiableUser; - $notifiableUser->phone = $phone; - $notifiableUser->type = "first_given_bonus"; - $notifiableUser->data = "" . ($back1 + $back); - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $notifiableUser->save(); - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($notifiableUser->getErrors()) { - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - return $this->asJson(["error_id" => 5.4, "error" => $notifiableUser->getErrors()]); - } - } - } - -// /////// Добавляем бонусов рефералу -// if ($referal_id && $back) { -// $name = "Вознаграждение за приведенного друга"; -// $referalBonus = UsersBonus::find()->where(['phone' => $phone])->andWhere(['referal_id' => $referal_id])->andWhere(['tip' => 'plus'])->one(); // phone = referal_id ??? -// if (!$referalBonus) { -// $referalBonus = new UsersBonus; -// $referalBonus->tip = 'plus'; -// $referalBonus->tip_sale = 'referal'; -// $referalBonus->date = date('Y-m-d H:i:s'); -// $referalBonus->date_start = date('Y-m-d H:i:s', strtotime('+1 day', time())); -// $referalBonus->date_end = date('Y-m-d H:i:s', strtotime('+' . self::$YEAR_PERIOD . ' day', time())); -// $referalBonus->phone = $phone; -// $referalBonus->name = $name; -// $referalBonus->check_id = $check_id; -// $referalBonus->store_id = $store_id; -// $referalBonus->bonus = $back; -// $referalBonus->ip = $ip; -// $referalBonus->site_id = $site_id; // ??? $user_id_referal -// $referalBonus->referal_id = $referal_id; // ??? -// $referalBonus->admin_id = $admin_id; -// $referalBonus->price = $summa_chek; -// $referalBonus->store_id_1c = $store_id_1c; -// $referalBonus->seller_id_1c = $seller_id; -// $referalBonus->save(); -// if ($referalBonus->getErrors()) { -// return $this->asJson(["error_id" => 3, "error" => $referalBonus->getErrors()]); -// } -// } -// } - - /////// -// $itogo = 0; -// foreach ($result["items"] as $k => $mass) { -// $seller_id_item = $mass["seller_id"]; -// $product_id = $mass["product_id"]; -// $price = $mass["price"]; -// $quantity = $mass["quantity"]; -// $sm = $price * $quantity; -// //$info .=" id=$product_id ($quantity шт. x $price руб.) = $sm руб.,"; -// $itogo += $sm; -// -// //получаем внутренний ID товара -// $item_id = ClientHelper::get_export_id($product_id, "products",1); -// -// //товары к продаже -// $salesItem = new SalesItems; -// $salesItem->date = date('Y-m-d H:i:s'); -// $salesItem->phone = $phone; -// $salesItem->check_id = $check_id; -// $salesItem->store_id = $store_id; -// $salesItem->store_id_1c = $store_id_1c; -// $salesItem->seller_id = $seller_id_item; -// $salesItem->admin_id = $admin_id; -// $salesItem->id_1c = $product_id; -// $salesItem->item_id = $item_id; -// $salesItem->kol = $quantity; -// $salesItem->summa = $sm; -// $salesItem->referal_id = 0; // $referal_id; -// $salesItem->color_id = 0; // $color_id ??? -// $salesItem->lid_id = $lid_id; -// $salesItem->complect_id = 0; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $salesItem->name = '???'; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $salesItem->skidka = $mass['discount'] ?? 0; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $salesItem->vozvrat = 0; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $salesItem->save(); -// if ($salesItem->getErrors()) { -// return $this->asJson(["error_id" => 4, "error" => $salesItem->getErrors()]); -// } -// } - // sale_avg_price sale_price - $sale_price += $check_amount; - $sale_avg_price = round($sale_price / ($sale_cnt + 1)); - - $user->keycode = "" . rand(1000, 9999); - $user->password = ClientHelper::generatePassword(8); - $user->date_last_sale = date('Y-m-d H:i:s'); - $user->sale_cnt = $sale_cnt + 1; - if ($user->sale_cnt == 1) { - $user->date_first_sale = $user->date_last_sale; - } - $user->sale_store_id = $store_id; - $user->sale_price = $sale_price; - $user->sale_avg_price = $sale_avg_price; - $user->check_id_last_sale = $check_id; - if (!$user->date) { - $user->date = (new DateTime('now', new DateTimeZone('Europe/Moscow')))->format('Y-m-d H:i:sP'); - } - $user->balans = ClientHelper::getBonusBalance($phone); - -// $user->email_old = "example@example.ru"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $user->phone_old = "71111111111"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $user->check_id_forgot = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $user->sid_forgot = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $user->alerts_balans = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $user->alerts_date = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $user->alerts_reklama = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $user->seller_id = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $user->save(); - if ($user->getErrors()) { - - - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - LogService::apiErrorLog(json_encode(["error_id" => 6, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE)); - - Yii::info("BEFORE END errors=" . json_encode($user->getErrors(), JSON_UNESCAPED_UNICODE), self::LOG_CATEGORY_BONUS); - - return $this->asJson(["error_id" => 6, "error" => $user->getErrors()]); - } else { - $this->updateUserBonusLevel($user, $sale_price, $check_id, $check_name); - } - Yii::info("BEFORE END", self::LOG_CATEGORY_BONUS); -// $itogo -= $write_off_bonuses; - -// // продажа заносим в таблицу -// $sale = new Sales; -// $sale->date = date("Y-m-d H:i:s"); -// $sale->phone = $phone; -// $sale->operation = 'Продажа'; -// $sale->store_id = $store_id; -// $sale->admin_id = $admin_id; -// $sale->seller_id = $seller_id; -// $sale->store_id_1c = $store_id_1c; -// $sale->id = $check_id; -// $sale->number = $check_name; -// $sale->summ = $amount_all; -// $sale->skidka = $write_off_bonuses; -// $sale->status = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $sale->payments = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $sale->pay_arr = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $sale->sales_check = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $sale->order_id = ""; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $sale->terminal_id = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $sale->terminal = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $sale->kkm_id = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $sale->held = 0; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $sale->date_up = $sale->date; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении -// $sale->save(); -// if ($sale->getErrors()) { -// return $this->asJson(["error_id" => 6, "error" => $sale->getErrors()]); -// } - - $mess["result"] = true; - $mess["message_cashier"] = "Бонусы списаны"; - $mess["user_balans_old"] = $user_balans; - $mess["user_balans_new"] = $user_balans_new; - $mess["user_balans_actual"] = $user->balans; - LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); - file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__ . ' OK ', FILE_APPEND); - - $totalBonus = $back10 + $back20; - - $input = [ - 'phone' => $phone, - 'bonusCount' => $totalBonus, - 'purchaseDate' => date("Y-m-d H:i:s"), - 'orderId' => $check_id, - ]; - - $userBonusSendToTgLogs = new UserBonusSendToTgLogs; - $userBonusSendToTgLogs->input_hash = md5(Json::encode($input)); - $userBonusSendToTgLogs->input = Json::encode($input); - $userBonusSendToTgLogs->check_id = $check_id; - $userBonusSendToTgLogs->phone = $phone; - $userBonusSendToTgLogs->bonusCount = $totalBonus; - $userBonusSendToTgLogs->status = 1; - $userBonusSendToTgLogs->date = date('Y-m-d H:I:s'); - $userBonusSendToTgLogs->save(); - if ($userBonusSendToTgLogs->getErrors()) { - LogService::apiErrorLog(json_encode(["error_id" => 100.001, "error" => $userBonusSendToTgLogs->getErrors()], JSON_UNESCAPED_UNICODE)); - } - Yii::$app->queue->push(new SendBonusInfoToSiteJob($input)); - -// SiteService::notifySiteAboutBonuses($phone, $totalBonus, date("Y-m-d H:i:s"), $check_id); - - return $this->asJson($mess); - } - - /** - * Создаёт новую запись в таблице UsersBonusLevels. - * - * @param Users $user - * @param string $bonusLevel - * @param string $check_id - * @param string $check_name - * @param string $createdAt - * @return bool - */ - protected function createBonusHistoryRecord($user, $bonusLevel, $check_id, $check_name, $createdAt) - { - $bonusRecord = new UsersBonusLevels(); - $bonusRecord->phone = $user->phone; - $bonusRecord->user_id = $user->id; - $bonusRecord->bonus_level = $bonusLevel; - $bonusRecord->date_from = $createdAt; - $bonusRecord->check_id = $check_id; - $bonusRecord->check_name = $check_name; - $bonusRecord->active = 1; - - if (!$bonusRecord->save()) { - LogService::apiErrorLog( - json_encode(["error_id" => 100, "error" => $bonusRecord->getErrors()], JSON_UNESCAPED_UNICODE) - ); - return false; - } - return true; - } - - /** - * Обновляет бонусный уровень пользователя. - * - * @param Users $user Модель пользователя. - * @param float $sale_price Текущая сумма покупок. - * @param string $check_id Идентификатор чека. - * @param string $check_name Имя (номер) чека. - */ - protected function updateUserBonusLevel($user, $sale_price, $check_id, $check_name) - { - $bonusLevels = BonusLevels::find() - ->where(['active' => 1]) - ->orderBy(['threshold' => SORT_ASC]) - ->all(); - - $computedBonusLevel = null; - foreach ($bonusLevels as $level) { - if ($sale_price > $level->threshold) { - $computedBonusLevel = $level->alias; - } - } - $newBonusLevel = $computedBonusLevel ?? 'silver'; - - $existingHistoryLevel = UsersBonusLevels::find() - ->where(['or', ['phone' => $user->phone], ['user_id' => $user->id]]) - ->andWhere(['active' => 1]) - ->one(); - - $now = date('Y-m-d H:i:s'); - - if (empty($user->bonus_level) || $user->bonus_level !== $newBonusLevel) { - $user->bonus_level = $newBonusLevel; - if (!$user->save()) { - LogService::apiErrorLog( - json_encode(["error_id" => 6.1, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE) - ); - } - - if ($existingHistoryLevel) { - $existingHistoryLevel->active = 0; - $existingHistoryLevel->date_to = $now; - if (!$existingHistoryLevel->save()) { - LogService::apiErrorLog( - json_encode( - ["error_id" => 6.2, "error" => $existingHistoryLevel->getErrors()], - JSON_UNESCAPED_UNICODE - ) - ); - } - } - - $this->createBonusHistoryRecord($user, $newBonusLevel, $check_id, $check_name, $now); - } - } - - public function actionGetClientInfo() - { - Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; - $data = file_get_contents('php://input'); - $result = json_decode($data, true); - - $__API_PARAMS = ['phone']; - - foreach ($__API_PARAMS as $paramName) { - if (empty($result[$paramName])) { - - if ($paramName != 'phone') { - LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); - } - - return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); - } - } - - $phone = ClientHelper::phoneClear($result['phone']); - if (!ClientHelper::phoneVerify($phone)) { - return $this->asJson(["error_id" => 1.2, "error" => "phone is required"]); - } - - $mess = []; - $user = Users::find()->select(['id', 'keycode', 'bonus_level', 'burn_balans', 'name', 'referral_id', 'bdate', 'comment', 'pol', 'extract(epoch FROM date) as date']) - ->where(['phone' => $phone])->one(); - if (!$user) { - $mess["error"] = "Покупателя " . $phone . " нет в бонусной программе!"; - - return $this->asJson($mess); - } - $name = explode(" ", $user->name); - $birth_day = $user->bdate; - $first_name = $name[0] ?? ''; - $second_name = $name[1] ?? ''; - $comment = $user->comment; - $pol = "male"; - if ($user->pol == "women") { - $pol = "female"; - } - // если с момента добавления клиента прошло не более 5 часов позволяем редактировать даты иначе запрещаем редактирование - if ($user->date > time() - 3600 * 5) { - $mess["birth_day_readonly"] = true; - $mess["events_readonly"] = false; - } - if ($birth_day) { - $mess["birth_day_readonly"] = true; - } - - $data = UsersEvents::find()->where(['phone' => $phone])->orderBy(['date' => SORT_DESC])->all(); - foreach ($data as $row) { - if (strlen($row->date_day) == 1) { - $row->date_day = "0" . $row->date_day; - } - if (strlen($row->date_month) == 1) { - $row->date_month = "0" . $row->date_month; - } - if (!isset($mess["events"])) { - $mess["events"] = []; - } - $mess["events"][] = ["date" => $row->date, "event_id" => $row->tip_id]; - } - - $user_balance = ClientHelper::getBonusBalance($phone); - - $mess["result"] = true; - $mess["sex"] = $pol; - $mess["first_name"] = $first_name; - $mess["second_name"] = $second_name; - $mess["birth_day"] = $birth_day; - $mess["comment"] = $comment; - $mess["balance"] = $user_balance; - $mess["bonus_level"] = $user->bonus_level; - $mess["burn_balans"] = $user->burn_balans; - - LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); - - return $this->asJson($mess); - } - - public function actionReturn() - { - Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; - $data = file_get_contents('php://input'); - $result = json_decode($data, true); - - $__API_PARAMS = ['store_id', 'check_id']; // check_name, seller_id - - foreach ($__API_PARAMS as $paramName) { - if (!isset($result[$paramName])) { - - LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); - - return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); - } - } - - $store_id = $result["store_id"]; -// $seller_id = $result["seller_id"] ?? ''; - $check_id = $result["check_id"]; -// $check_name = $result["check_name"] ?? ''; - - UsersBonus::deleteAll(['and', ['check_id' => $check_id], - ['>', 'date', date('Y-m-d H:i:s', strtotime('-3 day', time()))]]); - - // api_logs... event: return, seller_id, when, check - - LogService::apiLogs(1, json_encode(['Удачный возврат'], JSON_UNESCAPED_UNICODE)); - - return $this->asJson(['ok']); - } - - public function actionAuthCodeFail() - { - Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; - $data = file_get_contents('php://input'); - $result = json_decode($data, true); - - $__API_PARAMS = [/*'store_id', 'seller_id',*/ - 'phone']; - - foreach ($__API_PARAMS as $paramName) { - if (empty($result[$paramName])) { - - if ($paramName != 'phone') { - LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); - } - - return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); - } - } - - $phone = ClientHelper::phoneClear($result['phone']); - if (!ClientHelper::phoneVerify($phone)) { - return $this->asJson(["error_id" => 1.2, "error" => "phone is required"]); - } - $result['phone'] = $phone; - -// $seller_id = $result['seller_id']; -// $store_id = $result['store_id']; - - $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->one(); - if (!$user) { - $mess["error"] = "Покупателя $phone нет в бонусной программе!"; - - return $this->asJson($mess); - } - - $user->keycode = "" . rand(1000, 9999); - $user->save(); - - if ($user->getErrors()) { - - LogService::apiErrorLog(json_encode(["error_id" => 3, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE)); - - return $this->asJson(["error_id" => 3, "error" => $user->getErrors()]); - } - - $mess = []; - $mess["result"] = true; - // api_logs seller, store - - LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); - - return $this->asJson($mess); - } - - public function actionCurrentItems() - { - Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; - $data = file_get_contents('php://input'); - $result = json_decode($data, true); - - $__API_PARAMS = ['store_id', 'seller_id', 'phone', 'amount_no_discount', 'amount_to_pay']; - - foreach ($__API_PARAMS as $paramName) { - if (empty($result[$paramName])) { - - if ($paramName != 'phone') { - LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); - } - - return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); - } - } - - $phone = ClientHelper::phoneClear($result['phone']); - if (!ClientHelper::phoneVerify($phone)) { - return $this->asJson(["error_id" => 1.2, "error" => "phone is required"]); - } - $result['phone'] = $phone; - -// $store_id = $result["store_id"]; -// $seller_id = $result["seller_id"]; - $phone = $result["phone"]; -// $check_amount = intval($result["check_amount"] ?? 0); -// $check_id = $result["check_id"] ?? ''; -// $check_name = $result["check_name"] ?? ''; -// $items = $result['items'] ?? []; - - $mess = []; - - $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->one(); - if (!$user) { - $mess["error"] = "Покупателя $phone нет в бонусной программе!"; - - return $this->asJson($mess); - } - -// $user_balans = ClientHelper::getBonusBalance($phone); -// $max = $itogo * self::$MAX_PROCENT; // максимально можем разрешить списывать до 30 процентов от суммы заказа -// $max = ceil($max); -// $available_bonus = $user_balans; -// if ($available_bonus > $max) { // если баллов бонусов больше чем 30 процентов списываем по максимуму 30 -// $available_bonus = $max; -// } -// $baza = $check_amount - $available_bonus; -// $back = ceil(self::$CREDIT_PROCENT * $baza); - - -// foreach ($items as $k => $mass) { -// $seller_id_item = $mass["seller_id"]; -// $product_id = $mass["product_id"]; -// $price = $mass["price"]; -// $quantity = $mass["quantity"]; -// $sm = $price * $quantity; -// $info .=" id=$product_id ($quantity шт. x $price руб.) = $sm руб.,"; -// $itogo += $sm; -// -// //получаем внутренний ID товара -// $item_id = ClientHelper::getExportId($product_id, "products",1); -// } - - LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); - - return $this->asJson(["NOT IMPLEMENTED"]); - } - - public function actionGetSettings() - { - Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; - $data = file_get_contents('php://input'); - $result = json_decode($data, true); - - $__API_PARAMS = ['store_id', 'seller_id']; - - foreach ($__API_PARAMS as $paramName) { - if (!isset($result[$paramName])) { - - LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); - - return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); - } - } - - $store_id = $result['store_id']; - $seller_id = $result['seller_id']; - - $mess = []; - $mess["result"] = true; - $mess["attempts_auth_code"] = 5; // Количество возможных попыток ввода кода подтверждения - $mess["list_events"] = [ - ["id" => 1, "name" => "День рождения"], - ["id" => 2, "name" => "8 марта"], - ["id" => 3, "name" => "День матери"], - ["id" => 4, "name" => "День влюбленных"], - ["id" => 5, "name" => "День свадьбы"], - ["id" => 6, "name" => "Другое"] - ]; - $mess["send_current_items"] = false; // Отправлять или нет текущие позиции чека - - LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); - - return $this->asJson($mess); - } - - public function actionGetUnusedNumenclatur() { - $items_arr_no = array_values(ArrayHelper::map( - UniversalCatalogItem::find()->where(['catalog_alias' => 'unused_nomenclature'])->all(), 'guid', 'guid')); - return $this->asJson(['unused_nomenclature' => $items_arr_no]); - } - - public function actionGetContest001Participant() { - Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; - - $request = Yii::$app->request->getRawBody(); - - try { - $result = Json::decode($request); - } catch (\Exception $ex) { - return $this->asJson(['error' => ['code' => 400, 'message' => 'Json body invalid']]); - } - - if (!isset($result['phone'])) { - return $this->asJson(["error_id" => 1, "error" => "phone is required"]); - } - - $phone = ClientHelper::phoneClear($result['phone']); - if (!ClientHelper::phoneVerify($phone)) { - return $this->asJson(["error_id" => 1.2, "error" => "phone is required"]); - } - $result['phone'] = $phone; - - $mess = []; - - $contestants = Contest001::find()->where(['phone' => $phone])->all(); - if (count($contestants) > 0) { - $mess['is_participant'] = true; - $raffle_numbers = []; - foreach ($contestants as $contestant) { - $raffle_numbers[] = $contestant->number; - } - $mess['raffle_numbers'] = implode(', ', $raffle_numbers); - } else { - $mess['is_participant'] = false; - } - - LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); - - return $this->asJson(['response' => $mess]); - } - - public function actionAdd() { - Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; - - $request = Yii::$app->request->getRawBody(); - - $requestTest = json_decode(\Yii::$app->getRequest()->getRawBody(), true); - - $fl = date('_Y_m_d__H_i_s_'); - - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', implode(',', $requestTest)); - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - try { - $result = Json::decode($request); - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - } catch (\Exception $ex) { - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - return $this->asJson(['error' => ['code' => 400, 'message' => 'Json body invalid']]); - } - - $__API_PARAMS = ['phone', 'description', 'tip_sale', 'bonus', 'date_end']; - - foreach ($__API_PARAMS as $paramName) { - if (!isset($result[$paramName])) { - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($paramName != 'phone') { - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); - } - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); - } - } - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - // - if (!in_array($result['tip_sale'], ['podarok', 'senat', 'nino802', 'sale', '14feb', '23feb', '8mar', 'quest001'])) { - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - return $this->asJson(["error_id" => 1.1, "error" => "tip_sale не разрешён (podarok, senat, nino802)"]); - } - - $phone = ClientHelper::phoneClear($result['phone']); - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if (!ClientHelper::phoneVerify($phone)) { - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - return $this->asJson(["error_id" => 1.2, "error" => "phone is required"]); - } - $result['phone'] = $phone; - - $stop = UsersStopList::find()->select(['phone'])->where(['phone' => $result['phone']])->one(); - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($stop) { - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - return $this->asJson(["error_id" => 4, "error" => 'Номер телефона числится в стоп листе']); - } - - $bonus = min((int)$result['bonus'], 1000); - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $found = UsersBonus::find()->where(['phone' => $phone])->andWhere(['>=', 'date_start', date('Y-m-d H:i:s', time() - self::$YEAR_PERIOD * 86400)]) - ->andWhere(['tip_sale' => $result['tip_sale']])->andWhere(['tip' => 'plus'])->andWhere(['bonus' => $bonus])->one(); - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($found) { - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - return $this->asJson(["error_id" => 3, "error" => 'Бонусы уже начисляли']); - } - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - $userBonus = new UsersBonus; - $userBonus->phone = $phone; - $userBonus->name = $result['description']; - $userBonus->date = date('Y-m-d H:i:s'); - $userBonus->site_id = 1; - $userBonus->setka_id = 1; - $userBonus->tip = 'plus'; - $userBonus->tip_sale = $result['tip_sale']; - $userBonus->bonus = $bonus; - - $userBonusDateStart = $result['date_start'] ?? $userBonus->date; - $userBonus->date_start = date('Y-m-d H:i:s', strtotime($userBonusDateStart)); - - $userBonusDateEnd = $result['date_end']; - $userBonus->date_end = date('Y-m-d H:i:s', strtotime($userBonusDateEnd)); - - $userBonus->save(); - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - if ($userBonus->getErrors()) { - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); - LogService::apiErrorLog(json_encode(["error_id" => 2, "error" => $userBonus->getErrors()], JSON_UNESCAPED_UNICODE)); - return $this->asJson(["error_id" => 2, "error" => $userBonus->getErrors()]); - } - file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__ . ' OK', FILE_APPEND); - return $this->asJson(['response' => true]); - } - - /** - * TO8-22: Активация промокода БЛАГО. - * POST /bonus/activate-promocode - * Параметры: phone, code - * - * Логика: - * 1. Найти промокод по коду - * 2. Проверить isActivatable() - * 3. Найти клиента по телефону - * 4. В транзакции: начислить 350 промо-бонусов (tip_sale='promobonus'), пометить промокод used=1 - */ - public function actionActivatePromocode() - { - Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; - $data = file_get_contents('php://input'); - $result = json_decode($data, true); - - if (empty($result['code'])) { - return $this->asJson(["error_id" => 1, "error" => "code is required"]); - } - if (empty($result['phone'])) { - return $this->asJson(["error_id" => 4, "error" => "phone is required"]); - } - - $phone = ClientHelper::phoneClear($result['phone']); - if (!ClientHelper::phoneVerify($phone)) { - return $this->asJson(["error_id" => 4, "error" => "phone is not valid"]); - } - - $user = Users::find() - ->where(['phone' => $phone]) - ->andWhere(['phone_true' => '1']) - ->one(); - - if (!$user) { - return $this->asJson(["error_id" => 4, "error" => "Пользователь с телефоном $phone не найден"]); - } - - $transaction = Yii::$app->db->beginTransaction(); - try { - // SELECT FOR UPDATE — блокируем строку промокода от параллельной активации - $promocode = Promocode::find() - ->where(['code' => $result['code']]) - ->forUpdate() - ->one(); - - if (!$promocode) { - $transaction->rollBack(); - return $this->asJson(["error_id" => 1, "error" => "Промокод не найден"]); - } - - $activatable = $promocode->isActivatable(); - if ($activatable !== true) { - $transaction->rollBack(); - $errorMessages = [ - 1 => "Промокод неактивен", - 2 => "Промокод уже использован", - 3 => "Срок действия промокода истёк", - ]; - return $this->asJson([ - "error_id" => $activatable, - "error" => $errorMessages[$activatable] ?? "Промокод недоступен", - ]); - } - - $bonusAmount = $promocode->bonus ?: 350; - $duration = $promocode->duration ?: self::$YEAR_PERIOD; - - $usersBonus = new UsersBonus(); - $usersBonus->phone = $phone; - $usersBonus->name = "Активация промокода {$promocode->code}"; - $usersBonus->date = date('Y-m-d H:i:s'); - $usersBonus->tip = 'plus'; - $usersBonus->tip_sale = Promocode::TIP_SALE_PROMOBONUS; - $usersBonus->bonus = $bonusAmount; - $usersBonus->price = 0; - $usersBonus->price_skidka = 0; - $usersBonus->user_id = $user->id; - $usersBonus->store_id = 0; - $usersBonus->site_id = 0; - $usersBonus->setka_id = 0; - $usersBonus->referal_id = 0; - $usersBonus->admin_id = 0; - $usersBonus->lid_id = 0; - $usersBonus->date_start = date('Y-m-d H:i:s'); - $usersBonus->date_end = date('Y-m-d H:i:s', strtotime('+' . $duration . ' day')); - $usersBonus->date_dell = $usersBonus->date_end; - $usersBonus->ip = $_SERVER['REMOTE_ADDR'] ?? ''; - - if (!$usersBonus->save()) { - throw new \Exception('Ошибка сохранения бонуса: ' . json_encode($usersBonus->getErrors())); - } - - $promocode->used = Promocode::USED_YES; - $promocode->activated_by = $user->id; - $promocode->activated_at = date('Y-m-d H:i:s'); - if (!$promocode->save(false)) { - throw new \Exception('Ошибка обновления промокода: ' . json_encode($promocode->getErrors())); - } - - $transaction->commit(); - - Yii::info("Промокод {$promocode->code} активирован для {$phone}, бонус={$bonusAmount}", self::LOG_CATEGORY_BONUS); - - return $this->asJson([ - "success" => true, - "bonus" => $bonusAmount, - "message" => "Промокод активирован, начислено {$bonusAmount} бонусов", - ]); - } catch (\Exception $e) { - $transaction->rollBack(); - LogService::apiErrorLog(json_encode([ - "error_id" => 10, - "error" => $e->getMessage(), - "phone" => $phone, - "code" => $result['code'], - ], JSON_UNESCAPED_UNICODE)); - return $this->asJson(["error_id" => 10, "error" => "Ошибка активации промокода"]); - } - } -} +response->format = \yii\web\Response::FORMAT_JSON; + $data = file_get_contents('php://input'); + $result = json_decode($data, true); + + $fl = date('_Y_m_d__H_i_s_'); + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . $result['phone']); + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + $__API_PARAMS = ['store_id', 'seller_id', 'phone']; // check_amount, items + + foreach ($__API_PARAMS as $paramName) { + if (empty($result[$paramName])) { + + if ($paramName != 'phone') { + LogService::apiErrorLog(json_encode(["error_id" => 0, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); + } + + return $this->asJson(["error_id" => 0, "error" => "$paramName is required"]); + } + } + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + $phone = ClientHelper::phoneClear($result['phone']); + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if (!ClientHelper::phoneVerify($phone)) { + return $this->asJson(["error_id" => 0.2, "error" => "phone is required"]); + } + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $result['phone'] = $phone; + + $check_amount = intval($result['check_amount'] ?? 0); + + $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->one(); + $bonusLevels = BonusLevels::find()->where(['active' => 1])->indexBy('alias')->asArray()->all(); + $bonusLevel = $user->bonus_level ?? "silver"; + + $bonus_rate = isset($bonusLevels[$bonusLevel]['bonus_rate']) + ? $bonusLevels[$bonusLevel]['bonus_rate'] / 100 + : self::$FIRST_SALE_PROCENT; + + $cashback_rate = isset($bonusLevels[$bonusLevel]['cashback_rate']) + ? $bonusLevels[$bonusLevel]['cashback_rate'] / 100 + : self::$FIRST_SALE_PROCENT; + + $mess = []; + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + // массив с id товарыми не участвующих в бонусной + $items_arr_no = array_values(ArrayHelper::map( + UniversalCatalogItem::find()->where(['catalog_alias' => 'unused_nomenclature'])->all(), 'guid', 'guid')); + $items_arr_no_bonus_writeoffs = array_values(ArrayHelper::map( + UniversalCatalogItem::find()->where(['catalog_alias' => 'non_bonusable_goods'])->all(), 'guid', 'guid')); + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + $all_amount = 0; + $has_actions = false; + $summa_no = 0; + $summa_no_writeoffs = 0; + if (!empty($result["items"])) { + foreach ($result["items"] as $item) { + if (in_array($item["product_id"], $items_arr_no)) { + $summa_no = $summa_no + $item["price"] * $item["quantity"]; + $has_actions = true; + } elseif (in_array($item["product_id"], $items_arr_no_bonus_writeoffs)) { + $summa_no_writeoffs = $summa_no_writeoffs + $item["price"] * $item["quantity"]; + } + $all_amount += $item["price"] * $item["quantity"]; + } + } + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $baza_nachislenie = $all_amount - $summa_no; + + $check_amount = $check_amount - $summa_no; + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + //$cnt = intval(Sales::find()->where(['phone' => $result['phone'], 'operation' => Sales::OPERATION_SALE])->count()); + //$max_procent = $cnt == 0 ? self::$FIRST_SALE_PROCENT : ($cnt == 1 ? self::$SECOND_SALE_PROCENT : self::$MAX_PROCENT); + $max_procent = $bonus_rate; + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $percent = ($result['phone'] == "79049031399") ? 0.9 : $max_procent; + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $userFound = Users::find()->where(['phone' => $result['phone']])->one(); + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + /** @var $userFound Users */ + $salesCount = -1; /* Из-за нулевого значения по умолчанию куча клиентов получило бонус 20% за покупку */ + if ($userFound && $userFound->telegram_created_at) { + $salesCount = intval(Sales::find()->where(['phone' => $result['phone'], 'operation' => Sales::OPERATION_SALE]) + ->andWhere(['>=', 'date', $userFound->telegram_created_at])->count()); + } + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $credit_procent = $userFound && $userFound->source > 0 && $salesCount == 0 ? self::$CREDIT_HIGH_PROCENT : $cashback_rate; + $will_be_credited_bonuses = $credit_procent * $baza_nachislenie; + + $will_be_credited_bonuses = round($will_be_credited_bonuses); + $mess["will_be_credited_bonuses"] = $will_be_credited_bonuses; + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $store_id = ClientHelper::getExportId($result['store_id'], "city_store", 1); + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + // Логи введённых номеров телефонов кассирами + $userPhone = UsersPhones::find()->where(['phone' => $result['phone']])->andWhere(['store_id' => $store_id]) + ->andWhere(['seller_id' => $result['seller_id']])->one(); + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if (!$userPhone) { + $userPhone = new UsersPhones(); + $userPhone->phone = $result['phone']; + $userPhone->store_id = $store_id; + $userPhone->store_guid = $result['store_id']; + $userPhone->seller_id = $result['seller_id']; + } + if (!$userPhone->store_guid) { + $userPhone->store_guid = $result['store_id']; + } + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $userPhone->date = date('Y-m-d H:i:s'); + $userPhone->save(); + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($userPhone->getErrors()) { + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => $userPhone->getErrors()], JSON_UNESCAPED_UNICODE)); + + return $this->asJson(["error_id" => 1, "error" => $userPhone->getErrors()]); + } + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->one(); + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if (!$user) { + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '-нет в бонусной программе-' . __LINE__, FILE_APPEND); + $mess["new_client"] = true; + $mess["message_cashier"] = "Заполните данные клиента"; + $mess["error"] = "Покупателя " . $result['phone'] . " нет в бонусной программе!"; + + return $this->asJson($mess); + } + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $keycode = $user->keycode; + $black_list = $user->black_list; + + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if (!$black_list) { + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $stop = UsersStopList::find()->select(['phone'])->where(['phone' => $result['phone']])->one(); + if ($stop) { + $black_list = 1; + $user->black_list = 1; + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $user->save(); + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($user->getErrors()) { + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + LogService::apiErrorLog(json_encode(["error_id" => 3, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE)); + + return $this->asJson(["error_id" => 3, "error" => $user->getErrors()]); + } + } + } + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $name = $user->name; + $user_balans = ClientHelper::getBonusBalance($result['phone']); + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $baza_spisanie = $baza_nachislenie - $summa_no_writeoffs; + if ($baza_spisanie < 0) { + $baza_spisanie = 0; + } + $max = $baza_spisanie * $percent; // максимально можем разрешить списывать до 30 процентов от суммы заказа + $max = round($max); + $available_bonus = $user_balans; + if ($available_bonus > $max) { // если баллов бонусов больше чем 30 процентов списываем по максимуму 30 + $available_bonus = $max; + } +// $baza = $check_amount - $bonus; + + $mess["message_cashier"] = "Клиент $name найден"; // Код: $keycode + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($black_list) { + $mess['error'] = 'Этот номер в черном списке'; + + return $this->asJson($mess); + } + + $txt = $has_actions ? 'В чеке есть акционные товары, на них бонусы не начислятся.' : ''; + + $mess["result"] = true; + $mess["auth_code"] = $keycode; + $mess["name"] = $name; + $mess["total_bonuses"] = $user_balans; + $mess["bonus_level"] = $bonusLevel; + $mess["burn_balans"] = $user->burn_balans; + $mess["available_bonuses"] = $available_bonus; + $mess["message_cashier"] = $txt . " Спросите последние 4 цифры телефона который позвонит клиенту $user_balans"; + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); + + file_put_contents(self::OUT_DIR . '/get_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__ . ' OK ', FILE_APPEND); + return $this->asJson($mess); + } + +// public function actionSendMessage() +// { +// Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; +// $data = file_get_contents('php://input'); +// $result = json_decode($data, true); +// +// $__API_PARAMS = ['store_id', 'seller_id', 'phone']; +// +// foreach ($__API_PARAMS as $paramName) { +// if (empty($result[$paramName])) { +// +// if ($paramName != 'phone') { +// LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); +// } +// +// return $this->asJson(["error" => "$paramName is required"]); +// } +// } +// +// $phone = ClientHelper::phoneClear($result['phone']); +// if (!ClientHelper::phoneVerify($phone)) { +// return $this->asJson(["error_id" => 0.2, "error" => "phone is required"]); +// } +// $result['phone'] = $phone; +// +// $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->andWhere(['black_list' => '0'])->one(); +// if (!$user) { +// $mess["error"] = "Покупателя " . $result['phone'] . " нет в бонусной программе!"; +// +// return $this->asJson($mess); +// } +// $keycode = $user->keycode; +// +// $mess = []; +// $mess["result"] = true; +// +// $mess['auth_code'] = $user->keycode; +// $mess['message_cashier'] = 'Отсканируйте QR код из телеграм бота или введите его руками'; +// +// LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); +// +// return $this->asJson($mess); +// } + + public function actionSendMessage() { + Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; + $data = file_get_contents('php://input'); + $result = json_decode($data, true); + + $__API_PARAMS = ['store_id', 'seller_id', 'phone']; + + foreach ($__API_PARAMS as $paramName) { + if (empty($result[$paramName])) { + + if ($paramName != 'phone') { + LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); + } + + return $this->asJson(["error" => "$paramName is required"]); + } + } + + $phone = ClientHelper::phoneClear($result['phone']); + if (!ClientHelper::phoneVerify($phone)) { + return $this->asJson(["error_id" => 0.2, "error" => "phone is required"]); + } + $result['phone'] = $phone; + + $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->andWhere(['black_list' => '0'])->one(); + if (!$user) { + $mess["error"] = "Покупателя " . $result['phone'] . " нет в бонусной программе!"; + + return $this->asJson($mess); + } + + $mess["message_cashier"] = "Звонок-последние 4 цифры телефона"; + + $userAuthCallLog = UsersAuthCallLog::find()->select(['COUNT(*) as cnt'])->where(['phone' => $result['phone']]) + ->andWhere(['store_id' => $result['store_id']])->andWhere(['>=', 'date', date('Y-m-d H:i:s', strtotime('-10 minutes'))])->one(); + + $cnt = $userAuthCallLog ? $userAuthCallLog->cnt : 1; + + $keycode = ''; + + if ($cnt < 2) { + $body = @file_get_contents("https://sms.ru/code/call?phone=" . $result['phone'] . "&api_id=4DFE45F9-1897-79C0-6872-08F05D6B7FA4&ip=" . $_SERVER["REMOTE_ADDR"]); + $json_res = json_decode($body, true, 512, JSON_UNESCAPED_UNICODE); + if ($json_res["status"] == "OK") { + $keycode = $json_res["code"]; + $user->keycode = '' . $keycode; + $user->password = ClientHelper::generatePassword(8);; + $user->save(); + if ($user->getErrors()) { + LogService::apiErrorLog(json_encode(["error_id" => 3.1415, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE)); + return $this->asJson(["error_id" => 3.1415, "error" => $user->getErrors()]); + } + $mess["auth_code"] = $keycode; + $mess["message_cashier"] = "Попытка:$cnt Звонок клиенту! последние 4 цифры номера"; + } + } elseif ($cnt > 2) { + $mess["message_cashier"] = "Попытка $cnt -извиняемся перед клиентом"; + } + $name = "$keycode Попытка $cnt " . $_SERVER["REMOTE_ADDR"]; + $userAuthCallLog = new UsersAuthCallLog; + $userAuthCallLog->date = date('Y-m-d H:i:s'); + $userAuthCallLog->store_id = $result['store_id']; + $userAuthCallLog->seller_id = $result['seller_id']; + $userAuthCallLog->phone = $result['phone']; + $userAuthCallLog->name = $name; + $userAuthCallLog->save(); + if ($userAuthCallLog->getErrors()) { + LogService::apiErrorLog(json_encode(["error_id" => 4.15, "error" => $userAuthCallLog->getErrors()], JSON_UNESCAPED_UNICODE)); + return $this->asJson(["error_id" => 4.15, "error" => $userAuthCallLog->getErrors()]); + } + + Yii::info("keykod={$user->keycode} store_id={$result['store_id']} seller_id={$result['seller_id']} phone={$result['phone']} $name", self::LOG_CATEGORY_BONUS); + + $mess["timeout"] = 15; + + LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); + + return $this->asJson($mess); + } + +// $mess["message_cashier"] = "Звонок-последние 4 цифры телефона"; +// +// $userAuthCallLog = UsersAuthCallLog::find()->select(['COUNT(*) as cnt'])->where(['phone' => $result['phone']]) +// ->andWhere(['store_id' => $result['store_id']])->andWhere(['>=', 'date', date('Y-m-d H:i:s', strtotime('-10 minutes'))])->one(); +// +// $cnt = $userAuthCallLog ? $userAuthCallLog->cnt : 1; +// +// if ($cnt < 2) { +// $body = @file_get_contents("https://sms.ru/code/call?phone=" . $result['phone'] . "&api_id=4DFE45F9-1897-79C0-6872-08F05D6B7FA4&ip=" . $_SERVER["REMOTE_ADDR"]); +// $json_res = json_decode($body, true, 512, JSON_UNESCAPED_UNICODE); +// +// if ($json_res["status"] == "OK") { +// $keycode = $json_res["code"]; +// $user->keycode = '' . $keycode; +// $user->password = ClientHelper::generatePassword(8);; +// $user->save(); +// +// if ($user->getErrors()) { +// +// LogService::apiErrorLog(json_encode(["error_id" => 3, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE)); +// +// return $this->asJson(["error_id" => 3, "error" => $user->getErrors()]); +// } +// +// $mess["auth_code"] = $keycode; +// $mess["message_cashier"] = "Попытка:$cnt Звонок клиенту! последние 4 цифры номера"; +// } +// } else if ($cnt > 2) { +// $mess["message_cashier"] = "Попытка $cnt -извиняемся перед клиентом"; +// } +// +// $name = "$keycode Попытка $cnt " . $_SERVER["REMOTE_ADDR"]; +// $userAuthCallLog = new UsersAuthCallLog; +// $userAuthCallLog->date = date('Y-m-d H:i:s'); +// $userAuthCallLog->store_id = $result['store_id']; +// $userAuthCallLog->seller_id = $result['seller_id']; +// $userAuthCallLog->phone = $result['phone']; +// $userAuthCallLog->name = $name; +// $userAuthCallLog->save(); +// if ($userAuthCallLog->getErrors()) { +// +// LogService::apiErrorLog(json_encode(["error_id" => 4, "error" => $userAuthCallLog->getErrors()], JSON_UNESCAPED_UNICODE)); +// +// return $this->asJson(["error_id" => 4, "error" => $userAuthCallLog->getErrors()]); +// } +// + + public function actionSaveClientInfo() + { + Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; + $data = file_get_contents('php://input'); + $result = json_decode($data, true); + + $fl = date('_Y_m_d__H_i_s_'); + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . $result['phone']); + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + + $__API_PARAMS = ['store_id', 'seller_id', 'phone']; // first_name, second_name, sex, birth_day, referral_id, comment, events + + foreach ($__API_PARAMS as $paramName) { + if (empty($result[$paramName])) { + + if ($paramName != 'phone') { + LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); + } + + return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); + } + } + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $phone = ClientHelper::phoneClear($result['phone']); + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if (!ClientHelper::phoneVerify($phone)) { + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + return $this->asJson(["error_id" => 1.2, "error" => "phone is required"]); + } + $result['phone'] = $phone; + + $source = $result["source"] ?? 0; + $store_id = $result["store_id"]; + $store_id_guid = $store_id; + $seller_id = $result["seller_id"]; + $phone = $result["phone"]; + $first_name = $result["first_name"] ?? ""; + $second_name = $result["second_name"] ?? ""; + $sex2 = $result["sex"] ?? ""; + $birth_day = $result["birth_day"] ?? ""; + $referral_id = $result["referral_id"] ?? null; + $comment = $result["comment"] ?? ""; + $events = $result["events"] ?? []; + $sex = "man"; + if ($sex2 == "male") { + $sex = "man"; + } + if ($sex2 == "female") { + $sex = "women"; + } +// if ($referral_phone == $phone) { +// $referral_phone = ""; +// } + + $mess = []; + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + /* @var $user Users */ + $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->andWhere(['black_list' => '0'])->one(); + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($user) { + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $user->referral_id = $referral_id != $user->id ? $referral_id : null; + $user->pol = $sex; + $user->bdate = $birth_day; + $user->name = "$first_name $second_name"; + $user->comment = $comment; + $user->password = ClientHelper::generatePassword(8); + $user->keycode = '' . rand(1000, 9999); + $user->source = $source == 2 ? 1 : 0; + $user->save(); // иначе не пройдём валидацию, т.к. множество полей в бд не заполнены. + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($user->getErrors()) { + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + LogService::apiErrorLog(json_encode(["error_id" => 2, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE)); + + return $this->asJson(["error_id" => 2, "error" => $user->getErrors()]); + } + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $userEventOld = UsersEvents::find()->where(['phone' => $phone])->orderBy(['date_add' => SORT_ASC])->one(); + if ($userEventOld && $userEventOld->date_add < date('Y-m-d H:i:s', time() - 2 * 86400)) { // Дата добавление последнего события не старше двух дней + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $mess["result"] = true; + $mess["message_cashier"] = "Возможность внесения памятных дат ограничена"; + + LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); + + return $this->asJson($mess); + } + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + $nomer_event = 1; + $dates = []; + + foreach ($events as $k => $mass) { + $date = $mass["date"] ?? ''; + $event_id = intval($mass['event_id'] ?? 0); + + $datea = explode("-", $date); + $date_end = date("Y", time() + self::$YEAR_PERIOD * 86400) . "-" . $datea[1] . "-" . $datea[2]; + $userEvent2 = UsersEvents::find()->where(['phone' => $phone])->andWhere(['date_day' => $datea[2]])->andWhere(['date_month' => $datea[1]])->one(); + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($userEvent2) { + $userEvent2->delete(); + } + $userEvent3 = new UsersEvents; + $userEvent3->number = $nomer_event; + $userEvent3->date = $date; + $userEvent3->tip_id = $event_id; + $userEvent3->phone = $phone; + $userEvent3->date_day = $datea[2]; + $userEvent3->date_month = $datea[1]; + $userEvent3->date_add = date('Y-m-d H:i:s'); + $userEvent3->tip = strval('???'); + $userEvent3->name = 'М'; + $userEvent3->sex = 'm'; + $userEvent3->date_edit = date("Y-m-d H:i:s"); + $userEvent3->date_edit_info = date("Y-m-d H:i:s"); + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $userEvent3->save(); // иначе не пройдём валидацию, т.к. множество полей в бд не заполнены. + if ($userEvent3->getErrors()) { + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + LogService::apiErrorLog(json_encode(["error_id" => 3, "error" => $userEvent3->getErrors()], JSON_UNESCAPED_UNICODE)); + + return $this->asJson(["error_id" => 3, "error" => $userEvent3->getErrors()]); + } + $dates [] = $date; + $nomer_event++; + } + } else { + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $created_name = $seller_id; + $rand = rand(1000, 9999); + $name = "$first_name $second_name"; + $pass = ClientHelper::generatePassword(8); + $product1 = Products1c::find()->select(['name'])->where(['tip' => 'admin'])->andWhere(['id' => $seller_id])->one(); + $product2 = Products1c::find()->select(['name'])->where(['tip' => 'city_store'])->andWhere(['id' => $store_id])->one(); + + $created_name = $product1 ? $product1->name : ''; + $created_store = $product2 ? $product2->name : ''; + + $store_id_new = ExportImportTable::find()->select(['entity_id'])->where(['entity' => 'city_store'])->andWhere(['export_id' => '1']) + ->andWhere(['export_val' => $store_id])->one(); + $seller_id_new = ExportImportTable::find()->select(['entity_id'])->where(['entity' => 'admin'])->andWhere(['export_id' => '1']) + ->andWhere(['export_val' => $seller_id])->one(); + if ($store_id_new) { + $store_id_int = $store_id_new->entity_id; + } + if ($seller_id_new) { + $seller_id_int = $seller_id_new->entity_id; + } + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + Users::deleteAll(['phone' => $phone, 'phone_true' => '0']); + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $user2 = new Users; + $user2->source = $source; + $user2->pol = $sex; + $user2->keycode = strval($rand); + $user2->phone = $phone; + $user2->name = $name; + $user2->name_name = $first_name; + $user2->name_last = $second_name; + $user2->password = $pass; + $user2->phone_true = strval(1); + $user2->bdate = $birth_day; + $user2->referral_id = $referral_id; + $user2->comment = $comment; + $user2->created_id = $seller_id_int ?? 0; + $user2->created_name = $created_name; + $user2->seller_id = strval($seller_id); + $user2->store_id = $store_id_guid; + $user2->created_store_id = $store_id_int ?? 0; + $user2->created_store = $created_store; + $user2->date = date('Y-m-d H:i:s'); + $user2->sale_store_id = $store_id_int ?? 0; + $user2->sale_store = ''; + $user2->sms_info = 1; + $user2->reklama_info = 1; + $user2->info = ''; + $setka_id = 1; + $user2->setka_id = $setka_id; + $user2->card = "" . ($phone * 2 + 1608 + $setka_id); // генерируем номер карты который зависит от номера сетки + ДР Тимура + $user2->save(); // иначе не пройдём валидацию, т.к. множество полей в бд не заполнены. + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($user2->getErrors()) { + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + LogService::apiErrorLog(json_encode(["error_id" => 4, "error" => $user2->getErrors()], JSON_UNESCAPED_UNICODE)); + + return $this->asJson(["error_id" => 4, "error" => $user2->getErrors()]); + } + + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($store_id == '56524cb1-4763-11ea-8cce-b42e991aff6c') { + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $admin_id = ClientHelper::getExportId($seller_id, "admin", 1); + + $usersBonus = new UsersBonus; + $usersBonus->date = date('Y-m-d H:i:s'); + $usersBonus->tip = 'plus'; + $usersBonus->tip_sale = 'podarok'; + $usersBonus->phone = $phone; + $usersBonus->name = "Приветственные бонусы посетителю сайта"; + $usersBonus->store_id = $store_id_int ?? 0; + $usersBonus->site_id = 0; + $usersBonus->referal_id = 0; + $usersBonus->admin_id = $admin_id; + $usersBonus->price = 0; + $usersBonus->price_skidka = 0; + $usersBonus->bonus = 50; + $usersBonus->store_id_1c = $store_id; + $usersBonus->seller_id_1c = $seller_id; + $usersBonus->date_start = date('Y-m-d 08:00:00', strtotime('+1 day', strtotime($usersBonus->date))); + $usersBonus->date_end = date('Y-m-d H:i:s', strtotime('+1 week', strtotime($usersBonus->date_start))); + $usersBonus->save(); + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($usersBonus->getErrors()) { + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + LogService::apiErrorLog(json_encode(["error_id" => 5, "error" => $usersBonus->getErrors()], JSON_UNESCAPED_UNICODE)); + + return $this->asJson(["error_id" => 5, "error" => $usersBonus->getErrors()]); + } + } + } + + $mess["result"] = true; + $mess["message_cashier"] = "Данные клиента сохранены"; + + LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); + + file_put_contents(self::OUT_DIR . '/save_client_info_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__ . ' OK ', FILE_APPEND); + return $this->asJson($mess); + } + + public function actionSale() + { + Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; + $data = file_get_contents('php://input'); + $result = json_decode($data, true); + $resultTest = $result; + $fl = date('_Y_m_d__H_i_s_'); + $json=json_encode($resultTest,JSON_UNESCAPED_UNICODE); + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '_info.json', PHP_EOL . '--' . $result['phone']); + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '_info.json', ' '.date("d.m.Y H:i:s",time()).' JSON: '.$json.' ', FILE_APPEND); + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . $result['phone']); + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + $__API_PARAMS = ['store_id', 'seller_id', 'phone', 'check_amount', 'check_id', 'check_name']; // items, auth_code, write_off_bonuses + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + foreach ($__API_PARAMS as $paramName) { + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if (empty($result[$paramName])) { + + if ($paramName != 'phone') { + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '-error-' . __LINE__, FILE_APPEND); + LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); + } + + return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); + } + } + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $phone = ClientHelper::phoneClear($result['phone']); + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if (!ClientHelper::phoneVerify($phone)) { + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '-error-' . __LINE__, FILE_APPEND); + return $this->asJson(["error_id" => 1.2, "error" => "phone is required"]); + } + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $result['phone'] = $phone; + $result['items'] = $result['items'] ?? []; + + $store_id = $result["store_id"]; + $seller_id = $result["seller_id"]; + $check_amount = $result["check_amount"]; + $check_id = $result["check_id"]; + $check_name = $result["check_name"]; + $lid_id = $result["lid_id"] ?? 0; + $auth_code = $result['auth_code'] ?? 0; + $write_off_bonuses = intval($result["write_off_bonuses"] ?? 0); // только при продаже + + $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->one(); + $bonusLevels = BonusLevels::find()->where(['active' => 1])->indexBy('alias')->asArray()->all(); + $bonusLevel = $user->bonus_level ?? "silver"; + $cashback_rate = isset($bonusLevels[$bonusLevel]['cashback_rate']) + ? $bonusLevels[$bonusLevel]['cashback_rate'] / 100 + : self::$FIRST_SALE_PROCENT; + +// $referal_rate = isset($bonusLevels[$bonusLevel]['referal_rate']) +// ? $bonusLevels[$bonusLevel]['referal_rate'] / 100 +// : self::$CREDIT_HIGH_PROCENT; + + $bonus_rate = isset($bonusLevels[$bonusLevel]['bonus_rate']) + ? $bonusLevels[$bonusLevel]['bonus_rate'] / 100 + : self::$FIRST_SALE_PROCENT; + + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $amount_real = 0; + $items_arr_no = array_values(ArrayHelper::map( + UniversalCatalogItem::find()->where(['catalog_alias' => 'unused_nomenclature'])->all(), 'guid', 'guid')); + $items_arr_no_bonus_writeoffs = array_values(ArrayHelper::map( + UniversalCatalogItem::find()->where(['catalog_alias' => 'non_bonusable_goods'])->all(), 'guid', 'guid')); + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $summa_no = 0; + $summa_no_writeoffs = 0; + $amount_all = 0; + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + foreach ($result['items'] as $item) { + if (in_array($item["product_id"], $items_arr_no)) { + $summa_no = $summa_no + $item["price"] * $item["quantity"]; + } else if (in_array($item["product_id"], $items_arr_no_bonus_writeoffs)) { + $summa_no_writeoffs = $summa_no_writeoffs + $item["price"] * $item["quantity"]; + } else { + $amount_real = $amount_real + $item["price"] * $item["quantity"]; + } + $amount_all = $amount_all + $item["price"] * $item["quantity"]; + } + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + $cnt = intval(Sales::find()->where(['phone' => $result['phone'], 'operation' => Sales::OPERATION_SALE])->count()); + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + // $max_procent = $cnt == 0 ? self::$FIRST_SALE_PROCENT : ($cnt == 1 ? self::$SECOND_SALE_PROCENT : self::$MAX_PROCENT); + $max_procent = $bonus_rate; + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + // если списывается в попытке больше бонусов чем может списаться - + $percent = $phone == "79049031399" ? 0.9 : $max_procent; + $write_off_bonuses_theory = round($amount_real * $percent); + if ($write_off_bonuses > $write_off_bonuses_theory) { + $write_off_bonuses = $write_off_bonuses_theory; + } + $user_balans = ClientHelper::getBonusBalance($phone); + if ($user_balans < $write_off_bonuses) { + $write_off_bonuses = $user_balans; + } + + // TO8-22: Промо-списание БЛАГО + // Проверяем: если у клиента есть промо-баланс >= 350, покупка >= 1700 и 350 > стандартного максимума — списываем промо + $usePromoWriteOff = false; + $promoWriteOffAmount = 350; + $promoMinCheckAmount = 1700; + + $now = date('Y-m-d H:i:s'); + $promoPlusSum = (float) UsersBonus::find() + ->where(['phone' => $phone, 'tip' => 'plus', 'tip_sale' => Promocode::TIP_SALE_PROMOBONUS]) + ->andWhere(['<=', 'date_start', $now]) + ->andWhere(['>=', 'date_end', $now]) + ->sum('bonus'); + $promoMinusSum = (float) UsersBonus::find() + ->where(['phone' => $phone, 'tip' => 'minus', 'tip_sale' => Promocode::TIP_SALE_PROMOBONUS]) + ->sum('bonus'); + $promoBalance = max(0, $promoPlusSum - $promoMinusSum); + + if ($promoBalance >= $promoWriteOffAmount + && $amount_all >= $promoMinCheckAmount + && $promoWriteOffAmount > $write_off_bonuses_theory + ) { + $usePromoWriteOff = true; + $write_off_bonuses = $promoWriteOffAmount; + } + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + // сумма со скидкой + $summa_chek = $amount_all - $write_off_bonuses; + $baza_back = $amount_real + $summa_no_writeoffs - $write_off_bonuses; + + $mess = []; + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + /** @var $user Users */ + if (!$user) { + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $mess["error"] = "Покупателя " . $result['phone'] . " нет в бонусной программе!"; + + return $this->asJson($mess); + } + // TO8-22: При промо-списании burn_balans не трогаем — списываются промо-бонусы, а не обычные + if (!$usePromoWriteOff) { + $user->burn_balans = max(0, $user->burn_balans - $write_off_bonuses); + } + // [balans - burn_balance, burn_balans] - показать клиенту что мы сожгли сжигаемый баланс + +// старая точка проверки кода +// file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); +// if ($user->keycode != strval($auth_code)) { +// +// file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '-auth_code not valid-' . __LINE__ . ' keycode ' .$user->keycode . '| auth_code ' . strval($auth_code), FILE_APPEND); +// return $this->asJson(['error' => 'auth_code not valid']); +// } + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $user_id = $user->id; +// $keycode = $user->keycode; +// $name = $user->name; +// $referral_id = $user->referral_id; +// $sale_avg_price = $user->sale_avg_price; + $sale_price = $user->sale_price; + $sale_cnt = $user->sale_cnt; +// if ($referral_id == $user_id) { +// $referral_id = 0; +// } + $ip = $_SERVER['REMOTE_ADDR']; + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + + $store_id_1c = $store_id; + $site_id = 0; + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + // получаем внутренний ID продаца - сотрудника из таблицы admin + $admin_id = ClientHelper::getExportId($seller_id, "admin", 1); + // получаем внутренний ID продаца - сотрудника из таблицы admin + $store_id = ClientHelper::getExportId($store_id_1c, "city_store", 1); + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $writeOffAlready = false; + if (!empty($lid_id)) { + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $tipSaleForCheck = $usePromoWriteOff ? Promocode::TIP_SALE_PROMOBONUS : 'sale'; + $writeOffAlready = UsersBonus::find()->where(['lid_id' => $lid_id, 'phone' => $phone, 'tip_sale' => $tipSaleForCheck, 'tip' => 'minus'])->one() != null; + } + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + $user_balans_new = $user_balans; + if ($write_off_bonuses && !$writeOffAlready) { + // TO8-22: При промо-списании auth_code не требуется (списание автоматическое) + if (!$usePromoWriteOff) { + // Проверка кода только при стандартном списании + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($user->keycode != strval($auth_code)) { + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '-auth_code not valid-' . __LINE__ . ' keycode ' .$user->keycode . '| auth_code ' . strval($auth_code), FILE_APPEND); + return $this->asJson(['error' => 'auth_code not valid']); + } + } + + // TO8-22: При промо-списании обычный баланс не уменьшается — списываются промо-бонусы + $user_balans_new = $usePromoWriteOff ? $user_balans : ($user_balans - $write_off_bonuses); + $tipSaleForWriteOff = $usePromoWriteOff ? Promocode::TIP_SALE_PROMOBONUS : 'sale'; + $name_b = $usePromoWriteOff + ? "Списание промо-бонусов БЛАГО по чеку $check_name" + : "Спиcание бонусов по чеку $check_name"; + $usersBonus = new UsersBonus; + $usersBonus->date = date('Y-m-d H:i:s'); + $usersBonus->tip = 'minus'; + $usersBonus->tip_sale = $tipSaleForWriteOff; + $usersBonus->phone = $phone; + $usersBonus->name = $name_b; + $usersBonus->check_id = $check_id; + $usersBonus->store_id = $store_id; + $usersBonus->ip = $ip; + $usersBonus->site_id = $site_id; // ??? + $usersBonus->referal_id = 0;// $referal_id; + $usersBonus->admin_id = $admin_id; + $usersBonus->price = $summa_chek; + $usersBonus->price_skidka = $write_off_bonuses; + $usersBonus->bonus = $write_off_bonuses; + $usersBonus->store_id_1c = $store_id_1c; + $usersBonus->seller_id_1c = $seller_id; + $usersBonus->user_id = $user_id; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении + $usersBonus->lid_id = $lid_id; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении + $usersBonus->date_start = $usersBonus->date; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении + $usersBonus->date_end = $usersBonus->date; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении + $usersBonus->date_dell = $usersBonus->date; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + $usersBonus->save(); // иначе не пройдём валидацию, т.к. множество полей в бд не заполнены. + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + if ($user->first_minus_balance === null) { + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $user->first_minus_balance = $usersBonus->date; + $user->save(); + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + } + + if ($usersBonus->getErrors()) { + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + LogService::apiErrorLog(json_encode(["error_id" => 4, "error" => $usersBonus->getErrors()], JSON_UNESCAPED_UNICODE)); + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + return $this->asJson(["error_id" => 4, "error" => $usersBonus->getErrors()]); + } + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + Yii::info("MINUS write_off_bonuses={$write_off_bonuses}", self::LOG_CATEGORY_BONUS); + } + // TO8-22: При промо-списании кэшбек НЕ начисляется + if ($usePromoWriteOff) { + Yii::info("PROMO write_off={$write_off_bonuses}, no cashback for {$phone}", self::LOG_CATEGORY_BONUS); + + // Обновляем поля пользователя (аналогично стандартному пути) + $sale_price += $check_amount; + $sale_avg_price = round($sale_price / ($sale_cnt + 1)); + + $user->keycode = "" . rand(1000, 9999); + $user->password = ClientHelper::generatePassword(8); + $user->date_last_sale = date('Y-m-d H:i:s'); + $user->sale_cnt = $sale_cnt + 1; + if ($user->sale_cnt == 1) { + $user->date_first_sale = $user->date_last_sale; + } + $user->sale_store_id = $store_id; + $user->sale_price = $sale_price; + $user->sale_avg_price = $sale_avg_price; + $user->check_id_last_sale = $check_id; + if (!$user->date) { + $user->date = (new \DateTime('now', new \DateTimeZone('Europe/Moscow')))->format('Y-m-d H:i:sP'); + } + $user->balans = ClientHelper::getBonusBalance($phone); + $user->save(); + + if ($user->getErrors()) { + LogService::apiErrorLog(json_encode(["error_id" => 6, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE)); + return $this->asJson(["error_id" => 6, "error" => $user->getErrors()]); + } + + $this->updateUserBonusLevel($user, $sale_price, $check_id, $check_name); + + $mess["write_off_bonuses"] = $write_off_bonuses; + $mess["summa_chek"] = $summa_chek; + $mess["bonus_back"] = 0; + $mess["user_balans"] = $user_balans_new; + $mess["user_balans_actual"] = $user->balans; + $mess["promo_writeoff"] = true; + + return $this->asJson($mess); + } + + //начисляем кэшбек клиенту 10% от покупки - с базы за вычитом бонусов которые он списывает + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $userFound = Users::find()->where(['phone' => $result['phone']])->one(); + /** @var $userFound Users */ + $salesCount = -1; /* Из-за нулевого значения по умолчанию куча клиентов получило бонус 20% за покупку */ + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($userFound && $userFound->telegram_created_at) { + $salesCount = intval(Sales::find()->where(['phone' => $result['phone'], 'operation' => Sales::OPERATION_SALE]) + ->andWhere(['>=', 'date', $userFound->telegram_created_at])->count()); + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + } + $credit_procent_index = $userFound && $userFound->source > 0 && $salesCount == 0 ? 1 : 0; + + + $back10 = $back20 = 0; + $back1 = $back = round($baza_back * $cashback_rate); + $nm = "Возврат с покупки " . (100 * $cashback_rate) . "% $check_name сумма чека $check_amount"; + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $userBonus2 = UsersBonus::find()->where(['phone' => $phone])->andWhere(['check_id' => $check_id])->andWhere(['site_id' => $site_id]) + ->andWhere(['store_id' => $store_id])->andWhere(['tip' => 'plus'])->andWhere(['bonus' => $back])->andWhere(['name' => $nm])->one(); + if (!$userBonus2) { + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $user_balans_new += $back; + + $userBonus2 = new UsersBonus; + $userBonus2->tip = 'plus'; + $userBonus2->tip_sale = 'sale'; + $userBonus2->date = date('Y-m-d H:i:s'); + $userBonus2->date_start = date('Y-m-d H:i:s', strtotime('+1 day', time())); + $userBonus2->date_end = date('Y-m-d H:i:s', strtotime('+' . self::$YEAR_PERIOD . ' day', time())); + $userBonus2->phone = $phone; + $userBonus2->name = $nm; + $userBonus2->check_id = $check_id; + $userBonus2->store_id = $store_id; + $userBonus2->bonus = $back; + $userBonus2->ip = $ip; + $userBonus2->site_id = $site_id; + $userBonus2->referal_id = 0; // $referal_id; + $userBonus2->admin_id = $admin_id; + $userBonus2->price = $summa_chek; + $userBonus2->store_id_1c = $store_id_1c; + $userBonus2->seller_id_1c = $seller_id; + $userBonus2->user_id = $user_id; + $userBonus2->lid_id = $lid_id; + $userBonus2->price_skidka = 0; + $userBonus2->date_dell = $userBonus2->date_end; + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $userBonus2->save(); + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($userBonus2->getErrors()) { + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + LogService::apiErrorLog(json_encode(["error_id" => 5, "error" => $userBonus2->getErrors()], JSON_UNESCAPED_UNICODE)); + + return $this->asJson(["error_id" => 5, "error" => $userBonus2->getErrors()]); + } else { + $back10 = $back; + } + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + Yii::info("PLUS bonus={$back}", self::LOG_CATEGORY_BONUS); + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($credit_procent_index) { + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $back = round($baza_back * self::$CREDIT_HIGH_PROCENT_PART20); + $nm = "Возврат с покупки " . (100 * self::$CREDIT_HIGH_PROCENT_PART20) . "% $check_name сумма чека $check_amount"; + + $user_balans_new += $back; + + $userBonus2 = new UsersBonus(); + $userBonus2->tip = 'plus'; + $userBonus2->tip_sale = 'sale'; + $userBonus2->date = date('Y-m-d H:i:s'); + $userBonus2->date_start = date('Y-m-d H:i:s', strtotime('+1 day', time())); + $userBonus2->date_end = date('Y-m-d H:i:s', strtotime('+3 month', time())); + $userBonus2->phone = $phone; + $userBonus2->name = $nm; + $userBonus2->check_id = $check_id; + $userBonus2->store_id = $store_id; + $userBonus2->bonus = $back; + $userBonus2->ip = $ip; + $userBonus2->site_id = $site_id; + $userBonus2->referal_id = 0; + $userBonus2->admin_id = $admin_id; + $userBonus2->price = $summa_chek; + $userBonus2->store_id_1c = $store_id_1c; + $userBonus2->seller_id_1c = $seller_id; + $userBonus2->user_id = $user_id; + $userBonus2->lid_id = $lid_id; + $userBonus2->price_skidka = 0; + $userBonus2->date_dell = $userBonus2->date_end; + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $userBonus2->save(); + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($userBonus2->getErrors()) { + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + LogService::apiErrorLog(json_encode(["error_id" => 5.2, "error" => $userBonus2->getErrors()], JSON_UNESCAPED_UNICODE)); + + return $this->asJson(["error_id" => 5.2, "error" => $userBonus2->getErrors()]); + } else { + $back20 = $back; + } + if ($userFound->telegram_created_at == null) { + $userFound->telegram_created_at = date("Y-m-d H:i:s"); + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $userFound->save(); + if ($userFound->getErrors()) { + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + + LogService::apiErrorLog(json_encode(["error_id" => 5.3, "error" => $userFound->getErrors()], JSON_UNESCAPED_UNICODE)); + + return $this->asJson(["error_id" => 5.3, "error" => $userFound->getErrors()]); + } + } + + $notifiableUser = new NotifiableUser; + $notifiableUser->phone = $phone; + $notifiableUser->type = "first_given_bonus"; + $notifiableUser->data = "" . ($back1 + $back); + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $notifiableUser->save(); + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($notifiableUser->getErrors()) { + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + return $this->asJson(["error_id" => 5.4, "error" => $notifiableUser->getErrors()]); + } + } + } + +// /////// Добавляем бонусов рефералу +// if ($referal_id && $back) { +// $name = "Вознаграждение за приведенного друга"; +// $referalBonus = UsersBonus::find()->where(['phone' => $phone])->andWhere(['referal_id' => $referal_id])->andWhere(['tip' => 'plus'])->one(); // phone = referal_id ??? +// if (!$referalBonus) { +// $referalBonus = new UsersBonus; +// $referalBonus->tip = 'plus'; +// $referalBonus->tip_sale = 'referal'; +// $referalBonus->date = date('Y-m-d H:i:s'); +// $referalBonus->date_start = date('Y-m-d H:i:s', strtotime('+1 day', time())); +// $referalBonus->date_end = date('Y-m-d H:i:s', strtotime('+' . self::$YEAR_PERIOD . ' day', time())); +// $referalBonus->phone = $phone; +// $referalBonus->name = $name; +// $referalBonus->check_id = $check_id; +// $referalBonus->store_id = $store_id; +// $referalBonus->bonus = $back; +// $referalBonus->ip = $ip; +// $referalBonus->site_id = $site_id; // ??? $user_id_referal +// $referalBonus->referal_id = $referal_id; // ??? +// $referalBonus->admin_id = $admin_id; +// $referalBonus->price = $summa_chek; +// $referalBonus->store_id_1c = $store_id_1c; +// $referalBonus->seller_id_1c = $seller_id; +// $referalBonus->save(); +// if ($referalBonus->getErrors()) { +// return $this->asJson(["error_id" => 3, "error" => $referalBonus->getErrors()]); +// } +// } +// } + + /////// +// $itogo = 0; +// foreach ($result["items"] as $k => $mass) { +// $seller_id_item = $mass["seller_id"]; +// $product_id = $mass["product_id"]; +// $price = $mass["price"]; +// $quantity = $mass["quantity"]; +// $sm = $price * $quantity; +// //$info .=" id=$product_id ($quantity шт. x $price руб.) = $sm руб.,"; +// $itogo += $sm; +// +// //получаем внутренний ID товара +// $item_id = ClientHelper::get_export_id($product_id, "products",1); +// +// //товары к продаже +// $salesItem = new SalesItems; +// $salesItem->date = date('Y-m-d H:i:s'); +// $salesItem->phone = $phone; +// $salesItem->check_id = $check_id; +// $salesItem->store_id = $store_id; +// $salesItem->store_id_1c = $store_id_1c; +// $salesItem->seller_id = $seller_id_item; +// $salesItem->admin_id = $admin_id; +// $salesItem->id_1c = $product_id; +// $salesItem->item_id = $item_id; +// $salesItem->kol = $quantity; +// $salesItem->summa = $sm; +// $salesItem->referal_id = 0; // $referal_id; +// $salesItem->color_id = 0; // $color_id ??? +// $salesItem->lid_id = $lid_id; +// $salesItem->complect_id = 0; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $salesItem->name = '???'; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $salesItem->skidka = $mass['discount'] ?? 0; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $salesItem->vozvrat = 0; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $salesItem->save(); +// if ($salesItem->getErrors()) { +// return $this->asJson(["error_id" => 4, "error" => $salesItem->getErrors()]); +// } +// } + // sale_avg_price sale_price + $sale_price += $check_amount; + $sale_avg_price = round($sale_price / ($sale_cnt + 1)); + + $user->keycode = "" . rand(1000, 9999); + $user->password = ClientHelper::generatePassword(8); + $user->date_last_sale = date('Y-m-d H:i:s'); + $user->sale_cnt = $sale_cnt + 1; + if ($user->sale_cnt == 1) { + $user->date_first_sale = $user->date_last_sale; + } + $user->sale_store_id = $store_id; + $user->sale_price = $sale_price; + $user->sale_avg_price = $sale_avg_price; + $user->check_id_last_sale = $check_id; + if (!$user->date) { + $user->date = (new DateTime('now', new DateTimeZone('Europe/Moscow')))->format('Y-m-d H:i:sP'); + } + $user->balans = ClientHelper::getBonusBalance($phone); + +// $user->email_old = "example@example.ru"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $user->phone_old = "71111111111"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $user->check_id_forgot = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $user->sid_forgot = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $user->alerts_balans = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $user->alerts_date = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $user->alerts_reklama = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $user->seller_id = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $user->save(); + if ($user->getErrors()) { + + + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + LogService::apiErrorLog(json_encode(["error_id" => 6, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE)); + + Yii::info("BEFORE END errors=" . json_encode($user->getErrors(), JSON_UNESCAPED_UNICODE), self::LOG_CATEGORY_BONUS); + + return $this->asJson(["error_id" => 6, "error" => $user->getErrors()]); + } else { + $this->updateUserBonusLevel($user, $sale_price, $check_id, $check_name); + } + Yii::info("BEFORE END", self::LOG_CATEGORY_BONUS); +// $itogo -= $write_off_bonuses; + +// // продажа заносим в таблицу +// $sale = new Sales; +// $sale->date = date("Y-m-d H:i:s"); +// $sale->phone = $phone; +// $sale->operation = 'Продажа'; +// $sale->store_id = $store_id; +// $sale->admin_id = $admin_id; +// $sale->seller_id = $seller_id; +// $sale->store_id_1c = $store_id_1c; +// $sale->id = $check_id; +// $sale->number = $check_name; +// $sale->summ = $amount_all; +// $sale->skidka = $write_off_bonuses; +// $sale->status = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $sale->payments = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $sale->pay_arr = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $sale->sales_check = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $sale->order_id = ""; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $sale->terminal_id = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $sale->terminal = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $sale->kkm_id = "???"; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $sale->held = 0; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $sale->date_up = $sale->date; // Поле не заполнялось в старом апи, но без него бд выдаёт ошибку при сохранении +// $sale->save(); +// if ($sale->getErrors()) { +// return $this->asJson(["error_id" => 6, "error" => $sale->getErrors()]); +// } + + $mess["result"] = true; + $mess["message_cashier"] = "Бонусы списаны"; + $mess["user_balans_old"] = $user_balans; + $mess["user_balans_new"] = $user_balans_new; + $mess["user_balans_actual"] = $user->balans; + LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); + file_put_contents(self::OUT_DIR . '/sale_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__ . ' OK ', FILE_APPEND); + + $totalBonus = $back10 + $back20; + + $input = [ + 'phone' => $phone, + 'bonusCount' => $totalBonus, + 'purchaseDate' => date("Y-m-d H:i:s"), + 'orderId' => $check_id, + ]; + + $userBonusSendToTgLogs = new UserBonusSendToTgLogs; + $userBonusSendToTgLogs->input_hash = md5(Json::encode($input)); + $userBonusSendToTgLogs->input = Json::encode($input); + $userBonusSendToTgLogs->check_id = $check_id; + $userBonusSendToTgLogs->phone = $phone; + $userBonusSendToTgLogs->bonusCount = $totalBonus; + $userBonusSendToTgLogs->status = 1; + $userBonusSendToTgLogs->date = date('Y-m-d H:I:s'); + $userBonusSendToTgLogs->save(); + if ($userBonusSendToTgLogs->getErrors()) { + LogService::apiErrorLog(json_encode(["error_id" => 100.001, "error" => $userBonusSendToTgLogs->getErrors()], JSON_UNESCAPED_UNICODE)); + } + Yii::$app->queue->push(new SendBonusInfoToSiteJob($input)); + +// SiteService::notifySiteAboutBonuses($phone, $totalBonus, date("Y-m-d H:i:s"), $check_id); + + return $this->asJson($mess); + } + + /** + * Создаёт новую запись в таблице UsersBonusLevels. + * + * @param Users $user + * @param string $bonusLevel + * @param string $check_id + * @param string $check_name + * @param string $createdAt + * @return bool + */ + protected function createBonusHistoryRecord($user, $bonusLevel, $check_id, $check_name, $createdAt) + { + $bonusRecord = new UsersBonusLevels(); + $bonusRecord->phone = $user->phone; + $bonusRecord->user_id = $user->id; + $bonusRecord->bonus_level = $bonusLevel; + $bonusRecord->date_from = $createdAt; + $bonusRecord->check_id = $check_id; + $bonusRecord->check_name = $check_name; + $bonusRecord->active = 1; + + if (!$bonusRecord->save()) { + LogService::apiErrorLog( + json_encode(["error_id" => 100, "error" => $bonusRecord->getErrors()], JSON_UNESCAPED_UNICODE) + ); + return false; + } + return true; + } + + /** + * Обновляет бонусный уровень пользователя. + * + * @param Users $user Модель пользователя. + * @param float $sale_price Текущая сумма покупок. + * @param string $check_id Идентификатор чека. + * @param string $check_name Имя (номер) чека. + */ + protected function updateUserBonusLevel($user, $sale_price, $check_id, $check_name) + { + $bonusLevels = BonusLevels::find() + ->where(['active' => 1]) + ->orderBy(['threshold' => SORT_ASC]) + ->all(); + + $computedBonusLevel = null; + foreach ($bonusLevels as $level) { + if ($sale_price > $level->threshold) { + $computedBonusLevel = $level->alias; + } + } + $newBonusLevel = $computedBonusLevel ?? 'silver'; + + $existingHistoryLevel = UsersBonusLevels::find() + ->where(['or', ['phone' => $user->phone], ['user_id' => $user->id]]) + ->andWhere(['active' => 1]) + ->one(); + + $now = date('Y-m-d H:i:s'); + + if (empty($user->bonus_level) || $user->bonus_level !== $newBonusLevel) { + $user->bonus_level = $newBonusLevel; + if (!$user->save()) { + LogService::apiErrorLog( + json_encode(["error_id" => 6.1, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE) + ); + } + + if ($existingHistoryLevel) { + $existingHistoryLevel->active = 0; + $existingHistoryLevel->date_to = $now; + if (!$existingHistoryLevel->save()) { + LogService::apiErrorLog( + json_encode( + ["error_id" => 6.2, "error" => $existingHistoryLevel->getErrors()], + JSON_UNESCAPED_UNICODE + ) + ); + } + } + + $this->createBonusHistoryRecord($user, $newBonusLevel, $check_id, $check_name, $now); + } + } + + public function actionGetClientInfo() + { + Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; + $data = file_get_contents('php://input'); + $result = json_decode($data, true); + + $__API_PARAMS = ['phone']; + + foreach ($__API_PARAMS as $paramName) { + if (empty($result[$paramName])) { + + if ($paramName != 'phone') { + LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); + } + + return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); + } + } + + $phone = ClientHelper::phoneClear($result['phone']); + if (!ClientHelper::phoneVerify($phone)) { + return $this->asJson(["error_id" => 1.2, "error" => "phone is required"]); + } + + $mess = []; + $user = Users::find()->select(['id', 'keycode', 'bonus_level', 'burn_balans', 'name', 'referral_id', 'bdate', 'comment', 'pol', 'extract(epoch FROM date) as date']) + ->where(['phone' => $phone])->one(); + if (!$user) { + $mess["error"] = "Покупателя " . $phone . " нет в бонусной программе!"; + + return $this->asJson($mess); + } + $name = explode(" ", $user->name); + $birth_day = $user->bdate; + $first_name = $name[0] ?? ''; + $second_name = $name[1] ?? ''; + $comment = $user->comment; + $pol = "male"; + if ($user->pol == "women") { + $pol = "female"; + } + // если с момента добавления клиента прошло не более 5 часов позволяем редактировать даты иначе запрещаем редактирование + if ($user->date > time() - 3600 * 5) { + $mess["birth_day_readonly"] = true; + $mess["events_readonly"] = false; + } + if ($birth_day) { + $mess["birth_day_readonly"] = true; + } + + $data = UsersEvents::find()->where(['phone' => $phone])->orderBy(['date' => SORT_DESC])->all(); + foreach ($data as $row) { + if (strlen($row->date_day) == 1) { + $row->date_day = "0" . $row->date_day; + } + if (strlen($row->date_month) == 1) { + $row->date_month = "0" . $row->date_month; + } + if (!isset($mess["events"])) { + $mess["events"] = []; + } + $mess["events"][] = ["date" => $row->date, "event_id" => $row->tip_id]; + } + + $user_balance = ClientHelper::getBonusBalance($phone); + + $mess["result"] = true; + $mess["sex"] = $pol; + $mess["first_name"] = $first_name; + $mess["second_name"] = $second_name; + $mess["birth_day"] = $birth_day; + $mess["comment"] = $comment; + $mess["balance"] = $user_balance; + $mess["bonus_level"] = $user->bonus_level; + $mess["burn_balans"] = $user->burn_balans; + + LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); + + return $this->asJson($mess); + } + + public function actionReturn() + { + Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; + $data = file_get_contents('php://input'); + $result = json_decode($data, true); + + $__API_PARAMS = ['store_id', 'check_id']; // check_name, seller_id + + foreach ($__API_PARAMS as $paramName) { + if (!isset($result[$paramName])) { + + LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); + + return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); + } + } + + $store_id = $result["store_id"]; +// $seller_id = $result["seller_id"] ?? ''; + $check_id = $result["check_id"]; +// $check_name = $result["check_name"] ?? ''; + + UsersBonus::deleteAll(['and', ['check_id' => $check_id], + ['>', 'date', date('Y-m-d H:i:s', strtotime('-3 day', time()))]]); + + // api_logs... event: return, seller_id, when, check + + LogService::apiLogs(1, json_encode(['Удачный возврат'], JSON_UNESCAPED_UNICODE)); + + return $this->asJson(['ok']); + } + + public function actionAuthCodeFail() + { + Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; + $data = file_get_contents('php://input'); + $result = json_decode($data, true); + + $__API_PARAMS = [/*'store_id', 'seller_id',*/ + 'phone']; + + foreach ($__API_PARAMS as $paramName) { + if (empty($result[$paramName])) { + + if ($paramName != 'phone') { + LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); + } + + return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); + } + } + + $phone = ClientHelper::phoneClear($result['phone']); + if (!ClientHelper::phoneVerify($phone)) { + return $this->asJson(["error_id" => 1.2, "error" => "phone is required"]); + } + $result['phone'] = $phone; + +// $seller_id = $result['seller_id']; +// $store_id = $result['store_id']; + + $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->one(); + if (!$user) { + $mess["error"] = "Покупателя $phone нет в бонусной программе!"; + + return $this->asJson($mess); + } + + $user->keycode = "" . rand(1000, 9999); + $user->save(); + + if ($user->getErrors()) { + + LogService::apiErrorLog(json_encode(["error_id" => 3, "error" => $user->getErrors()], JSON_UNESCAPED_UNICODE)); + + return $this->asJson(["error_id" => 3, "error" => $user->getErrors()]); + } + + $mess = []; + $mess["result"] = true; + // api_logs seller, store + + LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); + + return $this->asJson($mess); + } + + public function actionCurrentItems() + { + Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; + $data = file_get_contents('php://input'); + $result = json_decode($data, true); + + $__API_PARAMS = ['store_id', 'seller_id', 'phone', 'amount_no_discount', 'amount_to_pay']; + + foreach ($__API_PARAMS as $paramName) { + if (empty($result[$paramName])) { + + if ($paramName != 'phone') { + LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); + } + + return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); + } + } + + $phone = ClientHelper::phoneClear($result['phone']); + if (!ClientHelper::phoneVerify($phone)) { + return $this->asJson(["error_id" => 1.2, "error" => "phone is required"]); + } + $result['phone'] = $phone; + +// $store_id = $result["store_id"]; +// $seller_id = $result["seller_id"]; + $phone = $result["phone"]; +// $check_amount = intval($result["check_amount"] ?? 0); +// $check_id = $result["check_id"] ?? ''; +// $check_name = $result["check_name"] ?? ''; +// $items = $result['items'] ?? []; + + $mess = []; + + $user = Users::find()->where(['phone' => $result['phone']])->andWhere(['phone_true' => '1'])->one(); + if (!$user) { + $mess["error"] = "Покупателя $phone нет в бонусной программе!"; + + return $this->asJson($mess); + } + +// $user_balans = ClientHelper::getBonusBalance($phone); +// $max = $itogo * self::$MAX_PROCENT; // максимально можем разрешить списывать до 30 процентов от суммы заказа +// $max = ceil($max); +// $available_bonus = $user_balans; +// if ($available_bonus > $max) { // если баллов бонусов больше чем 30 процентов списываем по максимуму 30 +// $available_bonus = $max; +// } +// $baza = $check_amount - $available_bonus; +// $back = ceil(self::$CREDIT_PROCENT * $baza); + + +// foreach ($items as $k => $mass) { +// $seller_id_item = $mass["seller_id"]; +// $product_id = $mass["product_id"]; +// $price = $mass["price"]; +// $quantity = $mass["quantity"]; +// $sm = $price * $quantity; +// $info .=" id=$product_id ($quantity шт. x $price руб.) = $sm руб.,"; +// $itogo += $sm; +// +// //получаем внутренний ID товара +// $item_id = ClientHelper::getExportId($product_id, "products",1); +// } + + LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); + + return $this->asJson(["NOT IMPLEMENTED"]); + } + + public function actionGetSettings() + { + Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; + $data = file_get_contents('php://input'); + $result = json_decode($data, true); + + $__API_PARAMS = ['store_id', 'seller_id']; + + foreach ($__API_PARAMS as $paramName) { + if (!isset($result[$paramName])) { + + LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); + + return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); + } + } + + $store_id = $result['store_id']; + $seller_id = $result['seller_id']; + + $mess = []; + $mess["result"] = true; + $mess["attempts_auth_code"] = 5; // Количество возможных попыток ввода кода подтверждения + $mess["list_events"] = [ + ["id" => 1, "name" => "День рождения"], + ["id" => 2, "name" => "8 марта"], + ["id" => 3, "name" => "День матери"], + ["id" => 4, "name" => "День влюбленных"], + ["id" => 5, "name" => "День свадьбы"], + ["id" => 6, "name" => "Другое"] + ]; + $mess["send_current_items"] = false; // Отправлять или нет текущие позиции чека + + LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); + + return $this->asJson($mess); + } + + public function actionGetUnusedNumenclatur() { + $items_arr_no = array_values(ArrayHelper::map( + UniversalCatalogItem::find()->where(['catalog_alias' => 'unused_nomenclature'])->all(), 'guid', 'guid')); + return $this->asJson(['unused_nomenclature' => $items_arr_no]); + } + + public function actionGetContest001Participant() { + Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; + + $request = Yii::$app->request->getRawBody(); + + try { + $result = Json::decode($request); + } catch (\Exception $ex) { + return $this->asJson(['error' => ['code' => 400, 'message' => 'Json body invalid']]); + } + + if (!isset($result['phone'])) { + return $this->asJson(["error_id" => 1, "error" => "phone is required"]); + } + + $phone = ClientHelper::phoneClear($result['phone']); + if (!ClientHelper::phoneVerify($phone)) { + return $this->asJson(["error_id" => 1.2, "error" => "phone is required"]); + } + $result['phone'] = $phone; + + $mess = []; + + $contestants = Contest001::find()->where(['phone' => $phone])->all(); + if (count($contestants) > 0) { + $mess['is_participant'] = true; + $raffle_numbers = []; + foreach ($contestants as $contestant) { + $raffle_numbers[] = $contestant->number; + } + $mess['raffle_numbers'] = implode(', ', $raffle_numbers); + } else { + $mess['is_participant'] = false; + } + + LogService::apiLogs(1, json_encode($mess, JSON_UNESCAPED_UNICODE)); + + return $this->asJson(['response' => $mess]); + } + + public function actionAdd() { + Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; + + $request = Yii::$app->request->getRawBody(); + + $requestTest = json_decode(\Yii::$app->getRequest()->getRawBody(), true); + + $fl = date('_Y_m_d__H_i_s_'); + + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', implode(',', $requestTest)); + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + try { + $result = Json::decode($request); + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + } catch (\Exception $ex) { + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + return $this->asJson(['error' => ['code' => 400, 'message' => 'Json body invalid']]); + } + + $__API_PARAMS = ['phone', 'description', 'tip_sale', 'bonus', 'date_end']; + + foreach ($__API_PARAMS as $paramName) { + if (!isset($result[$paramName])) { + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($paramName != 'phone') { + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + LogService::apiErrorLog(json_encode(["error_id" => 1, "error" => "$paramName is required"], JSON_UNESCAPED_UNICODE)); + } + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + return $this->asJson(["error_id" => 1, "error" => "$paramName is required"]); + } + } + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + // + if (!in_array($result['tip_sale'], ['podarok', 'senat', 'nino802', 'sale', '14feb', '23feb', '8mar', 'quest001'])) { + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + return $this->asJson(["error_id" => 1.1, "error" => "tip_sale не разрешён (podarok, senat, nino802)"]); + } + + $phone = ClientHelper::phoneClear($result['phone']); + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if (!ClientHelper::phoneVerify($phone)) { + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + return $this->asJson(["error_id" => 1.2, "error" => "phone is required"]); + } + $result['phone'] = $phone; + + $stop = UsersStopList::find()->select(['phone'])->where(['phone' => $result['phone']])->one(); + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($stop) { + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + return $this->asJson(["error_id" => 4, "error" => 'Номер телефона числится в стоп листе']); + } + + $bonus = min((int)$result['bonus'], 1000); + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $found = UsersBonus::find()->where(['phone' => $phone])->andWhere(['>=', 'date_start', date('Y-m-d H:i:s', time() - self::$YEAR_PERIOD * 86400)]) + ->andWhere(['tip_sale' => $result['tip_sale']])->andWhere(['tip' => 'plus'])->andWhere(['bonus' => $bonus])->one(); + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($found) { + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + return $this->asJson(["error_id" => 3, "error" => 'Бонусы уже начисляли']); + } + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + $userBonus = new UsersBonus; + $userBonus->phone = $phone; + $userBonus->name = $result['description']; + $userBonus->date = date('Y-m-d H:i:s'); + $userBonus->site_id = 1; + $userBonus->setka_id = 1; + $userBonus->tip = 'plus'; + $userBonus->tip_sale = $result['tip_sale']; + $userBonus->bonus = $bonus; + + $userBonusDateStart = $result['date_start'] ?? $userBonus->date; + $userBonus->date_start = date('Y-m-d H:i:s', strtotime($userBonusDateStart)); + + $userBonusDateEnd = $result['date_end']; + $userBonus->date_end = date('Y-m-d H:i:s', strtotime($userBonusDateEnd)); + + $userBonus->save(); + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + if ($userBonus->getErrors()) { + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__, FILE_APPEND); + LogService::apiErrorLog(json_encode(["error_id" => 2, "error" => $userBonus->getErrors()], JSON_UNESCAPED_UNICODE)); + return $this->asJson(["error_id" => 2, "error" => $userBonus->getErrors()]); + } + file_put_contents(self::OUT_DIR . '/add_bonuses_' . $fl . '.json', PHP_EOL . '--' . __LINE__ . ' OK', FILE_APPEND); + return $this->asJson(['response' => true]); + } + + /** + * TO8-22: Активация промокода БЛАГО. + * POST /bonus/activate-promocode + * Параметры: phone, code + * + * Логика: + * 1. Найти промокод по коду + * 2. Проверить isActivatable() + * 3. Найти клиента по телефону + * 4. В транзакции: начислить 350 промо-бонусов (tip_sale='promobonus'), пометить промокод used=1 + */ + public function actionActivatePromocode() + { + Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; + $data = file_get_contents('php://input'); + $result = json_decode($data, true); + + if (empty($result['code'])) { + return $this->asJson(["error_id" => 1, "error" => "code is required"]); + } + if (empty($result['phone'])) { + return $this->asJson(["error_id" => 4, "error" => "phone is required"]); + } + + $phone = ClientHelper::phoneClear($result['phone']); + if (!ClientHelper::phoneVerify($phone)) { + return $this->asJson(["error_id" => 4, "error" => "phone is not valid"]); + } + + $user = Users::find() + ->where(['phone' => $phone]) + ->andWhere(['phone_true' => '1']) + ->one(); + + if (!$user) { + return $this->asJson(["error_id" => 4, "error" => "Пользователь с телефоном $phone не найден"]); + } + + $transaction = Yii::$app->db->beginTransaction(); + try { + // SELECT FOR UPDATE — блокируем строку промокода от параллельной активации + $promocode = Promocode::find() + ->where(['code' => $result['code']]) + ->forUpdate() + ->one(); + + if (!$promocode) { + $transaction->rollBack(); + return $this->asJson(["error_id" => 1, "error" => "Промокод не найден"]); + } + + $activatable = $promocode->isActivatable(); + if ($activatable !== true) { + $transaction->rollBack(); + $errorMessages = [ + 1 => "Промокод неактивен", + 2 => "Промокод уже использован", + 3 => "Срок действия промокода истёк", + ]; + return $this->asJson([ + "error_id" => $activatable, + "error" => $errorMessages[$activatable] ?? "Промокод недоступен", + ]); + } + + $bonusAmount = $promocode->bonus ?: 350; + $duration = $promocode->duration ?: self::$YEAR_PERIOD; + + $usersBonus = new UsersBonus(); + $usersBonus->phone = $phone; + $usersBonus->name = "Активация промокода {$promocode->code}"; + $usersBonus->date = date('Y-m-d H:i:s'); + $usersBonus->tip = 'plus'; + $usersBonus->tip_sale = Promocode::TIP_SALE_PROMOBONUS; + $usersBonus->bonus = $bonusAmount; + $usersBonus->price = 0; + $usersBonus->price_skidka = 0; + $usersBonus->user_id = $user->id; + $usersBonus->store_id = 0; + $usersBonus->site_id = 0; + $usersBonus->setka_id = 0; + $usersBonus->referal_id = 0; + $usersBonus->admin_id = 0; + $usersBonus->lid_id = 0; + $usersBonus->date_start = date('Y-m-d H:i:s'); + $usersBonus->date_end = date('Y-m-d H:i:s', strtotime('+' . $duration . ' day')); + $usersBonus->date_dell = $usersBonus->date_end; + $usersBonus->ip = $_SERVER['REMOTE_ADDR'] ?? ''; + + if (!$usersBonus->save()) { + throw new \Exception('Ошибка сохранения бонуса: ' . json_encode($usersBonus->getErrors())); + } + + $promocode->used = Promocode::USED_YES; + $promocode->activated_by = $user->id; + $promocode->activated_at = date('Y-m-d H:i:s'); + if (!$promocode->save(false)) { + throw new \Exception('Ошибка обновления промокода: ' . json_encode($promocode->getErrors())); + } + + $transaction->commit(); + + Yii::info("Промокод {$promocode->code} активирован для {$phone}, бонус={$bonusAmount}", self::LOG_CATEGORY_BONUS); + + return $this->asJson([ + "success" => true, + "bonus" => $bonusAmount, + "message" => "Промокод активирован, начислено {$bonusAmount} бонусов", + ]); + } catch (\Exception $e) { + $transaction->rollBack(); + LogService::apiErrorLog(json_encode([ + "error_id" => 10, + "error" => $e->getMessage(), + "phone" => $phone, + "code" => $result['code'], + ], JSON_UNESCAPED_UNICODE)); + return $this->asJson(["error_id" => 10, "error" => "Ошибка активации промокода"]); + } + } +} diff --git a/plan_email.md b/plan_email.md deleted file mode 100644 index fc1ac924..00000000 --- a/plan_email.md +++ /dev/null @@ -1,293 +0,0 @@ -План: Улучшение системы регистрации и обработки писем Flowwow - -Контекст - -Крон-задача php yii marketplace/get-flowwow-orders читает письма от Flowwow через IMAP и создаёт/обновляет заказы в системе. Текущие проблемы: - -1. Нет разделения "регистрация" / "обработка" — email_status=1 ставится ДО processMessage() (строка 2203-2205), поэтому при сбое обработки письмо уже помечено как - обработанное -2. Повторная обработка невозможна — если saveEmailIfNotExists вернул null (письмо есть), processMessage() всё равно вызывается, но нет контроля завершённости обработки -3. SEEN ставится только для новых заказов — processFlowwowOrders возвращает счётчик только для NEW (строка 2721), а SEEN зависит от $output > 0 (строка 2218). Для - APPROVED/CHANGED/CANCELLED/DELIVERED SEEN не ставится -4. Нет индексов на таблице marketplace_flowwow_emails для проверки дубликатов -5. Нет отслеживания ошибок — если обработка упала, нет записи почему и сколько раз пытались -6. Нет связки письмо↔заказ — невозможно из интерфейса писем найти, какие письма относятся к конкретному заказу - -Файлы для изменения -┌──────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────┐ -│ Файл │ Изменение │ -├──────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤ -│ erp24/migrations/m260218_*_improve_flowwow_emails.php │ Новый — миграция: колонки + индексы │ -├──────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤ -│ erp24/records/MarketplaceFlowwowEmails.php │ Константы статусов, новые поля, relation, helper-методы │ -├──────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤ -│ erp24/records/MarketplaceFlowwowEmailsSearch.php │ Фильтрация по новым полям + поиск по заказу │ -├──────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤ -│ erp24/services/MarketplaceService.php │ Рефакторинг getFlowwowOrdersFromMail, saveEmailIfNotExists, fix processFlowwowOrders │ -├──────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤ -│ erp24/commands/MarketplaceController.php │ Новый action actionRetryFlowwowEmails │ -├──────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤ -│ erp24/media/controllers/FlowwowController.php │ Fix actionCheckMail (несовместимый вызов) │ -├──────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤ -│ erp24/views/marketplace-flowwow-emails/index.php │ Обновление GridView: статусы, связка с заказом, поиск │ -├──────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────┤ -│ erp24/controllers/MarketplaceFlowwowEmailsController.php │ Мелкие правки (если нужны для view) │ -└──────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────┘ - --- -Шаг 1. Миграция БД - -Файл: erp24/migrations/m260218_000001_improve_marketplace_flowwow_emails.php - -Добавить в таблицу marketplace_flowwow_emails: -┌──────────────────────┬────────────────────┬─────────────────────────────────────────────────────────────────────┐ -│ Колонка │ Тип │ Описание │ -├──────────────────────┼────────────────────┼─────────────────────────────────────────────────────────────────────┤ -│ subject_type │ smallint, NULL │ Тип письма (1=NEW, 2=APPROVED, 3=CHANGED, 4=CANCELLED, 5=DELIVERED) │ -├──────────────────────┼────────────────────┼─────────────────────────────────────────────────────────────────────┤ -│ processing_attempts │ integer, default 0 │ Счётчик попыток обработки │ -├──────────────────────┼────────────────────┼─────────────────────────────────────────────────────────────────────┤ -│ processed_at │ timestamp, NULL │ Время успешной обработки │ -├──────────────────────┼────────────────────┼─────────────────────────────────────────────────────────────────────┤ -│ error_message │ text, NULL │ Текст последней ошибки │ -├──────────────────────┼────────────────────┼─────────────────────────────────────────────────────────────────────┤ -│ marketplace_order_id │ varchar(50), NULL │ ID заказа (связка с marketplace_orders) │ -└──────────────────────┴────────────────────┴─────────────────────────────────────────────────────────────────────┘ -Индексы: -- idx_flowwow_emails_dedup на (subject, "from", date) — ускорение проверки дубликатов -- idx_flowwow_emails_status на (email_status) — выборка необработанных -- idx_flowwow_emails_order на (marketplace_order_id) — связка с заказами - -Важно: from — зарезервированное слово PostgreSQL, использовать "from" в кавычках через raw SQL. - - --- -Шаг 2. Модель MarketplaceFlowwowEmails - -Файл: erp24/records/MarketplaceFlowwowEmails.php - -2.1 Константы статусов -public const STATUS_NEW = 0; // Зарегистрировано, ожидает обработки -public const STATUS_PROCESSED = 1; // Успешно обработано -public const STATUS_ERROR = 2; // Ошибка (исчерпаны попытки) -public const STATUS_RETRY = 3; // Ожидает повторной обработки - -public const MAX_PROCESSING_ATTEMPTS = 5; - -2.2 Relation с MarketplaceOrders (связка письмо↔заказ) -/** -* Связь с заказом маркетплейса по marketplace_order_id. -* Поле marketplace_order_id в emails хранит ID заказа из маркетплейса (например "123456"), -* которому соответствует marketplace_orders.marketplace_order_id. - */ - public function getOrder(): \yii\db\ActiveQuery - { - return $this->hasOne(MarketplaceOrders::class, ['marketplace_order_id' => 'marketplace_order_id']) - ->andWhere(['marketplace_id' => \yii_app\records\MarketplaceStore::FLOWWOW_WAREHOUSE_ID]); - } - -Это позволит: -- Из письма перейти к заказу: $email->order -- Из GridView показать ссылку на заказ - -2.3 Helper-методы - -- markAsProcessed(): bool — ставит STATUS_PROCESSED + processed_at -- markAsError(string $errorMessage): bool — ставит STATUS_ERROR + error_message + инкремент attempts -- markForRetry(string $reason): bool — ставит STATUS_RETRY + инкремент attempts -- isRetryAllowed(): bool — processing_attempts < MAX_PROCESSING_ATTEMPTS -- static findUnprocessed(): ActiveQuery — WHERE email_status IN (0, 3) AND processing_attempts < MAX -- static statusLabels(): array — массив текстовых меток статусов -- static subjectTypeLabels(): array — [1 => 'Новый заказ', 2 => 'Принят', 3 => 'Изменён', 4 => 'Отменён', 5 => 'Доставлен'] - -2.4 Обновить rules() и attributeLabels() - -Добавить правила валидации и метки для новых полей. - - --- -Шаг 3. Рефакторинг MarketplaceService - -3.1 Новый метод detectSubjectType -private static function detectSubjectType(string $subject): ?int - -Определяет тип письма по теме через SUBJECT_INDEX regex patterns. - -3.2 Изменение saveEmailIfNotExists (строка 2260) - -При создании нового письма дополнительно заполнять subject_type через detectSubjectType(). Остальная логика без изменений — метод по-прежнему возвращает объект если -создано, null если уже существует. - -3.3 Рефакторинг getFlowwowOrdersFromMail (строки 2198-2242) - -Ключевое изменение — логика цикла обработки каждого письма: -ДЛЯ КАЖДОГО ПИСЬМА ИЗ IMAP: -├── saveEmailIfNotExists() → регистрация -├── Если письмо новое (savedEmail != null): -│ └── emailRecord = savedEmail -├── Иначе: загружаем из БД: -│ └── emailRecord = MarketplaceFlowwowEmails::find()->where(...) -│ -├── Если emailRecord.email_status == STATUS_PROCESSED: -│ ├── Только ставим SEEN на IMAP (чтобы не читать повторно) -│ └── continue (пропускаем обработку) -│ -├── Определяем паттерн темы → subject_index -├── try: -│ ├── processMessage($message) → обработка заказа -│ ├── emailRecord->markAsProcessed() → статус ПОСЛЕ успешной обработки -│ ├── emailRecord->marketplace_order_id = key($order) -│ ├── imap_setflag_full(SEEN) → для ВСЕХ успешных, не только NEW -│ └── countProcessedMessages++ -├── catch (Throwable): -│ ├── Логирование ошибки -│ ├── Если isRetryAllowed() → markForRetry() -│ └── Иначе → markAsError() - -Что убираем: -- Строки 2203-2205: преждевременная установка email_status = 1 ДО обработки -- Строки 2218: условие if ($output > 0) для SEEN — теперь ставится безусловно - -3.4 Fix processFlowwowOrders (строка 2677) - -Добавить переменную $processingSuccess = false. Устанавливать в true при: -- Создании нового заказа (строка 2719, $marketplaceOrder->save()) -- Успешном обновлении статуса (строки 2749, 2783, 2798) - -Изменить return (строка 2808): -return $processingSuccess ? max($newOrdersCount, 1) : 0; - -Это обеспечит что $output > 0 для всех типов писем, а не только NEW. - -3.5 Новый метод processUnprocessedEmails -public static function processUnprocessedEmails(?callable $progressCallback = null): array - -Выбирает из БД все письма со статусом STATUS_NEW или STATUS_RETRY (через findUnprocessed()), у которых заполнен subject_type, и обрабатывает каждое через -processMessage(). - -Возвращает: ['processed' => int, 'failed' => int, 'total' => int] - -Фильтр andWhere(['not', ['subject_type' => null]]) гарантирует, что старые записи (до миграции) не будут затронуты. - - --- -Шаг 4. Консольная команда для retry - -Файл: erp24/commands/MarketplaceController.php - -Новый action: -php yii marketplace/retry-flowwow-emails - -Вызывает MarketplaceService::processUnprocessedEmails() с progress callback в консоль. Выводит итоговую статистику. - - --- -Шаг 5. Fix FlowwowController::actionCheckMail - -Файл: erp24/media/controllers/FlowwowController.php (строки 64-82) - -Текущий код несовместим: getFlowwowOrdersFromMail возвращает ['processed' => N, 'all' => M], а actionCheckMail вызывает count($messages) (вернёт 2) и -processMessages($messages) (передаст массив с ключами 'processed'/'all' вместо писем). - -Исправить: убрать вызов processMessages, использовать возвращённый массив напрямую. - - --- -Шаг 6. Обновить Search-модель (поиск писем по заказу) - -Файл: erp24/records/MarketplaceFlowwowEmailsSearch.php - -6.1 Новые правила фильтрации - -- В rules(): добавить subject_type, processing_attempts как integer; processed_at, error_message, marketplace_order_id как safe - -6.2 Поиск в search() -// Фильтрация по новым полям -$query->andFilterWhere([ -'subject_type' => $this->subject_type, -'processing_attempts' => $this->processing_attempts, -]); -$query->andFilterWhere(['ilike', 'error_message', $this->error_message]); - -// Поиск писем по ID заказа маркетплейса -$query->andFilterWhere(['ilike', 'marketplace_order_id', $this->marketplace_order_id]); - -Это позволит: -- Ввести номер заказа Flowwow в фильтр → увидеть все письма, связанные с этим заказом -- Фильтровать по типу письма (новый, принят, отменён и т.д.) -- Фильтровать по статусу обработки - - --- -Шаг 7. Обновить View (связка с заказами и поиск) - -Файл: erp24/views/marketplace-flowwow-emails/index.php - -7.1 Обновить статусы - -- Обновить отображение email_status — добавить статусы "Ошибка" (красный) и "Повтор" (синий) -- Заменить текстовый фильтр email_status на dropdown: - 'filter' => MarketplaceFlowwowEmails::statusLabels(), - -7.2 Добавить колонку "Тип письма" -[ -'attribute' => 'subject_type', -'value' => fn($model) => MarketplaceFlowwowEmails::subjectTypeLabels()[$model->subject_type] ?? '—', -'filter' => MarketplaceFlowwowEmails::subjectTypeLabels(), -], - -7.3 Добавить колонку "Заказ" со ссылкой -[ -'attribute' => 'marketplace_order_id', -'format' => 'raw', -'value' => function ($model) { -if (!$model->marketplace_order_id) { -return '—'; -} -$order = $model->order; -if ($order) { -return Html::a( -'№' . $model->marketplace_order_id, -['/marketplace-orders/view', 'id' => $order->id], -['class' => 'btn btn-xs btn-outline-primary', 'target' => '_blank'] -); -} -return $model->marketplace_order_id . ' (не найден)'; -}, -'filter' => Html::input('text', 'MarketplaceFlowwowEmailsSearch[marketplace_order_id]', -$searchModel->marketplace_order_id, ['class' => 'form-control', 'placeholder' => '№ заказа']), -], - -Это даёт: -- Поиск писем по заказу: ввести номер заказа в фильтр → увидеть ВСЕ связанные письма (создание, принятие, изменения, отмена, доставка) -- Клик по номеру заказа → переход на страницу заказа в ERP -- Фильтр по типу письма → например, увидеть все "отменённые" письма - -7.4 Добавить колонку "Попытки" -'processing_attempts', - -7.5 Eager loading для оптимизации - -В контроллере MarketplaceFlowwowEmailsController::actionIndex добавить: -$dataProvider->query->with(['order']); - - --- -Порядок реализации -1. Миграция БД (независимый) -2. Модель MarketplaceFlowwowEmails (зависит от 1) -3. MarketplaceFlowwowEmailsSearch (зависит от 2) -4. MarketplaceService::detectSubjectType (независимый) -5. MarketplaceService::saveEmailIfNotExists (зависит от 2, 4) -6. MarketplaceService::processFlowwowOrders (независимый fix) -7. MarketplaceService::getFlowwowOrdersFromMail (зависит от 5, 6) -8. MarketplaceService::processUnprocessedEmails (зависит от 2) -9. MarketplaceController::actionRetryFlowwowEmails (зависит от 8) -10. FlowwowController::actionCheckMail fix (зависит от 7) -11. MarketplaceFlowwowEmailsController (eager loading) -12. View index.php (зависит от 2, 3, 11) - - --- -Верификация - -1. Миграция: php yii migrate — проверить создание колонок и индексов -2. Регистрация: запустить php yii marketplace/get-flowwow-orders, проверить что новые письма получили email_status=0, subject_type заполнен -3. Обработка: проверить что после processMessage — email_status=1, processed_at заполнен, marketplace_order_id заполнен -4. Повторный запуск: запустить команду снова — уже обработанные письма получают SEEN и пропускаются -5. Retry: вручную UPDATE marketplace_flowwow_emails SET email_status=3 WHERE id=..., запустить php yii marketplace/retry-flowwow-emails — повторная обработка -6. SEEN для всех типов: проверить что SEEN ставится для APPROVED, CANCELLED, DELIVERED (ранее не ставился) -7. Поиск по заказу: в UI /marketplace-flowwow-emails/index ввести номер заказа Flowwow в фильтр → отображаются все связанные письма -8. Ссылка на заказ: кликнуть по номеру заказа в колонке → переход на /marketplace-orders/view?id=... -9. Фильтр по типу: выбрать "Отменён" в dropdown типа письма → только отменённые письма -- 2.39.5