return $this->asJson(['response' => true]);
}
+
+ /**
+ * Возвращает JSON со списком магазинов (id, name)
+ * GET /api2/data-test/get-stores
+ */
+ public function actionGetStores() {
+ Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
+
+ try {
+ $stores = \yii_app\records\CityStore::find()
+ ->select(['id', 'name'])
+ ->orderBy(['name' => SORT_ASC])
+ ->asArray()
+ ->all();
+
+ return $this->asJson([
+ 'success' => true,
+ 'data' => $stores
+ ]);
+ } catch (Exception $e) {
+ return $this->asJson([
+ 'success' => false,
+ 'error' => $e->getMessage()
+ ]);
+ }
+ }
+
+ /**
+ * Возвращает JSON с агрегированными покупками по дате, смене и магазину
+ * POST /api2/data-test/get-sales
+ *
+ * Тело запроса:
+ * {
+ * "date": "2025-10-10",
+ * "shift_type": 1,
+ * "store_id": 5 (опционально)
+ * }
+ *
+ * shift_type:
+ * 0 = с 8:00 до 8:00 следующего дня (все)
+ * 1 = с 8:00 до 20:00 (день)
+ * 2 = с 20:00 до 8:00 (ночь)
+ */
+ public function actionGetSales() {
+ set_time_limit(300);
+ Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
+
+ $request = Yii::$app->request->getRawBody();
+
+ try {
+ $input = Json::decode($request);
+ } catch (Exception $ex) {
+ return $this->asJson([
+ 'success' => false,
+ 'error' => 'Invalid JSON body'
+ ]);
+ }
+
+ // Валидация обязательных параметров
+ if (empty($input['date'])) {
+ return $this->asJson([
+ 'success' => false,
+ 'error' => 'Date is required'
+ ]);
+ }
+
+ $date = $input['date'];
+ $shiftType = $input['shift_type'] ?? 0;
+ $storeIdFilter = $input['store_id'] ?? null;
+
+ try {
+ // Определяем диапазон времени для смены
+ $dateObj = new \DateTime($date);
+ $currentDateStr = $dateObj->format('Y-m-d');
+
+ // Получаем дату следующего дня
+ $nextDateObj = clone $dateObj;
+ $nextDateStr = $nextDateObj->modify('+1 day')->format('Y-m-d');
+
+ $dayStart = $currentDateStr . ' 08:00:00';
+ $dayEnd = $currentDateStr . ' 20:00:00';
+ $nightStart = $currentDateStr . ' 20:00:00';
+ $nightEnd = $currentDateStr . ' 23:59:59';
+ $nextDayStart = $nextDateStr . ' 00:00:00';
+ $nextDayEnd = $nextDateStr . ' 08:00:00';
+
+ // Построение базового запроса продаж
+ $query = \yii_app\records\Sales::find()
+ ->where(['or', ['order_id' => ''], ['order_id' => 0]]) // Только офлайн продажи
+ ->orderBy(['store_id' => SORT_ASC, 'date' => SORT_ASC]);
+
+ // Фильтр по смене
+ if ($shiftType === 1) {
+ // День: 8:00 - 20:00
+ $query->andWhere(['between', 'date', $dayStart, $dayEnd]);
+ } elseif ($shiftType === 2) {
+ // Ночь: 20:00 - 8:00 следующего дня
+ $query->andWhere(['OR',
+ ['between', 'date', $nightStart, $nightEnd],
+ ['between', 'date', $nextDayStart, $nextDayEnd]
+ ]);
+ } else {
+ // Все смены (вся дата)
+ $query->andWhere(['between', 'date', $currentDateStr . ' 00:00:00', $currentDateStr . ' 23:59:59']);
+ }
+
+ // Фильтр по магазину если задан
+ if (!empty($storeIdFilter)) {
+ $query->andWhere(['store_id' => $storeIdFilter]);
+ }
+
+ $sales = $query->with('products')->asArray()->all();
+
+ if (empty($sales)) {
+ return $this->asJson([
+ 'success' => true,
+ 'data' => []
+ ]);
+ }
+
+ // Агрегируем данные и присоединяем информацию о товарах
+ $aggregated = [];
+ $storeRegions = [];
+
+ foreach ($sales as $sale) {
+ $saleStoreId = $sale['store_id'];
+
+ // Кэшируем регион магазина
+ if (!isset($storeRegions[$saleStoreId])) {
+ $storeParams = \yii_app\records\CityStoreParams::findOne(['store_id' => $saleStoreId]);
+ $storeRegions[$saleStoreId] = $storeParams ? $storeParams->address_region : null;
+ }
+
+ $saleKey = $saleStoreId . '_' . $sale['date']; // Ключ для агрегации
+
+ if (!isset($aggregated[$saleKey])) {
+ $aggregated[$saleKey] = [
+ 'id' => $sale['id'],
+ 'date' => $sale['date'],
+ 'store_id' => (int)$saleStoreId,
+ 'summ' => (float)$sale['summ'],
+ 'operation' => $sale['operation'],
+ 'status' => $sale['status'],
+ 'skidka' => (float)$sale['skidka'],
+ 'products' => []
+ ];
+ } else {
+ // Суммируем значения
+ $aggregated[$saleKey]['summ'] += (float)$sale['summ'];
+ $aggregated[$saleKey]['skidka'] += (float)$sale['skidka'];
+ }
+
+ // Получаем товары из этого чека
+ $products = \yii_app\records\SalesProducts::find()
+ ->where(['check_id' => $sale['id']])
+ ->asArray()
+ ->all();
+
+ foreach ($products as $product) {
+ // Получаем информацию о товаре
+ $productInfo = \yii_app\records\Products1c::find()
+ ->where(['id' => $product['product_id']])
+ ->select(['id', 'name'])
+ ->asArray()
+ ->one();
+
+ // Получаем актуальную цену товара
+ $regionId = $storeRegions[$saleStoreId];
+ $priceQuery = \yii_app\records\PricesDynamic::find()
+ ->where(['product_id' => $product['product_id']])
+ ->andWhere(['active' => \yii_app\records\PricesDynamic::ACTIVE]);
+
+ // Приоритет: сначала ищем с регионом, потом без
+ if ($regionId) {
+ $priceQuery->andWhere(['region_id' => $regionId]);
+ }
+
+ $priceData = $priceQuery->select(['price'])
+ ->orderBy(['date_from' => SORT_DESC])
+ ->asArray()
+ ->one();
+
+ $productData = [
+ 'product_id' => $product['product_id'],
+ 'name' => $productInfo['name'] ?? 'Unknown',
+ 'quantity' => (float)$product['quantity'],
+ 'price' => $priceData ? (float)$priceData['price'] : (float)$product['price'],
+ 'discount' => (float)$product['discount'],
+ 'summ' => (float)$product['summ']
+ ];
+
+ $aggregated[$saleKey]['products'][] = $productData;
+ }
+ }
+
+ // Группируем по магазину
+ $grouped = [];
+ foreach ($aggregated as $sale) {
+ $storeId = $sale['store_id'];
+ // Убираем store_id из объекта продажи, так как он будет в ключе
+ unset($sale['store_id']);
+ $grouped[$storeId][] = $sale;
+ }
+
+ return $this->asJson([
+ 'success' => true,
+ 'data' => $grouped
+ ]);
+
+ } catch (Exception $e) {
+ return $this->asJson([
+ 'success' => false,
+ 'error' => $e->getMessage()
+ ]);
+ }
+ }
}