From: fomichev Date: Fri, 27 Jun 2025 13:39:06 +0000 (+0300) Subject: Расчет по основной функции X-Git-Url: https://gitweb.erp-flowers.ru/?a=commitdiff_plain;h=6b45ab1033113f2ca2b936dc107181cfd39bf355;p=erp24_rep%2Fyii-erp24%2F.git Расчет по основной функции --- diff --git a/erp24/controllers/CategoryPlanController.php b/erp24/controllers/CategoryPlanController.php index 69b0fe38..dacf448d 100644 --- a/erp24/controllers/CategoryPlanController.php +++ b/erp24/controllers/CategoryPlanController.php @@ -47,7 +47,7 @@ class CategoryPlanController extends Controller { $service = new AutoPlannogrammaService(); $isEditable = date($model->year . '-' . $model->month . '-d') > date('Y-m-d') && ( - (date('d') < 27) || (date('Y-m-d', strtotime('-1 month', strtotime(date($model->year . '-' . $model->month . '-d')))) > date('Y-m-d'))); + (date('d') < 28) || (date('Y-m-d', strtotime('-1 month', strtotime(date($model->year . '-' . $model->month . '-d')))) > date('Y-m-d'))); $categoryPlan = CategoryPlan::find()->where(['year' => $model->year, 'month' => $model->month, 'store_id' => $model->store_id])->indexBy('category')->asArray()->all(); $types = []; @@ -421,6 +421,265 @@ class CategoryPlanController extends Controller { return $this->render('index', compact('model', 'years', 'stores', 'table', 'tableOnline', 'tableWriteOffs', 'types', 'salesWriteOffsPlan', 'isEditable', 'categoryPlan')); } + public function actionNew() + { + $model = DynamicModel::validateData([ + 'year' => date('Y'), + 'month' => date('m'), + 'store_id' => null, + 'city_id' => null, + 'region_id' => null, + 'raion_id' => null, + 'store_type_id' => null, + 'territory_manager_id' => null, + 'kshf_id' => null, + ], [ + [[ + 'year', 'month', 'store_id', 'city_id', 'region_id', 'raion_id', 'store_type_id', + 'territory_manager_id', 'kshf_id' + ], 'safe'] + ]); + + $model->load(Yii::$app->request->get()); + + $service = new AutoPlannogrammaService(); + $isEditable = date($model->year . '-' . $model->month . '-d') > date('Y-m-d') && ( + (date('d') < 28) || (date('Y-m-d', strtotime('-1 month', strtotime(date($model->year . '-' . $model->month . '-d')))) > date('Y-m-d'))); + + $categoryPlan = CategoryPlan::find()->where(['year' => $model->year, 'month' => $model->month, 'store_id' => $model->store_id])->indexBy('category')->asArray()->all(); + $types = []; + $table = []; + $tableOnline = []; + $tableWriteOffs = []; + $years = []; + for ($i = 3; $i >= 0; $i--) { + $year = date("Y") - $i; + $years [$year] = $year; + } + $stores = ArrayHelper::map(CityStore::find()->andWhere(['visible' => '1'])->all(), 'id', 'name'); + $salesWriteOffsPlan = SalesWriteOffsPlan::find()->where(['year' => $model->year, 'month' => $model->month, 'store_id' => $model->store_id])->one(); + /////////////////////////// CHAT GPT STUFF /////////////////////////////////// + //var_dump(Yii::$app->request->get()); die(); + if ( + $model->year + && $model->month + && $model->store_id + ) { + if (Yii::$app->request->get('delete') === '1' + ) { + $count = Yii::$app->db + ->createCommand() + ->delete('erp24.category_plan', [ + 'year' => $model->year, + 'month' => $model->month, + 'store_id' => $model->store_id, + ]) + ->execute(); + + Yii::$app->session->setFlash( + $count ? 'success' : 'info', + $count + ? "Удалено {$count} записей." + : 'Ничего не удалено.' + ); + + + $params = [ + 'DynamicModel' => $model->attributes, + ]; + + $params['DynamicModel'] = array_filter($params['DynamicModel'], function($v) { + return $v !== null && $v !== ''; + }); + + return $this->redirect(array_merge( + ['index'], + $params + )); + + } + $currentDate = new \DateTime(date($model->year . '-' . $model->month . '-01')); + + $startDate = (clone $currentDate)->modify('-4 months')->modify('first day of this month'); + + $endDate = (clone $startDate)->modify('+2 months')->modify('last day of this month'); + + $date_start = $startDate->format('Y-m-d'); + $date_end = $endDate->format('Y-m-d'); + + + $sales = $service->getMonthCategoryShareOrWriteOff($currentDate->format('Y-m-d'), ['sales_type' => 'offline', 'store_id' => $model->store_id], AutoPlannogrammaService::TYPE_SALES); + + $salesMatrixOffline = Sales::find()->alias('s')->select([ + "COUNT(*) as cnt", + "SUM(CASE WHEN operation='Продажа' THEN sp.summ ELSE (CASE WHEN operation='Возврат' THEN -sp.summ ELSE 0 END) END) as total", + "s.store_id", + "p1c.type as type", + "TO_CHAR(s.date, 'YYYY-MM') as month" + ]) + ->leftJoin('sales_products sp', 's.id = sp.check_id') + ->leftJoin('products_1c p1c', 'p1c.id = sp.product_id') + ->where(['between', 's.date', $date_start, $date_end]) + ->andWhere(['order_id' => ['', '0']]) + ->andWhere(['p1c.type' => 'Матрица']) + ->andWhere(['s.store_id' => $model->store_id]) + ->groupBy([ + 's.store_id', + "TO_CHAR(s.date, 'YYYY-MM')", + "p1c.type" + ]) + ->orderBy(['month' => SORT_ASC, 'type' => SORT_ASC]) + ->asArray() + ->all(); + + + + + + $mnths = []; + foreach ($salesMatrixOffline as $saleMatrixOffline) { + $mnths[$saleMatrixOffline['month']] = 1; + $types[$saleMatrixOffline['type']] = 1; + } + $mnths = array_keys($mnths); + + sort($mnths); + + $weights = []; + foreach ($mnths as $ind => $month) { + $weights[$month] = $ind + 1; + } + + foreach ($sales[$model->store_id] as $sale) { + $types[$sale['category']] = 1; + $table[$model->store_id][$sale['category']] = $sale['total_sum'] ; + + } + foreach ($salesMatrixOffline as $saleMatrix) { + $types[$saleMatrix['type']] = 1; + $table[$saleMatrix['store_id']][$saleMatrix['type']] = ($table[$saleMatrix['store_id']][$saleMatrix['type']] ?? 0) + $weights[$saleMatrix['month']] * $saleMatrix['total'] ; + } + + $offlinePlannedSales = self::calculatePlannedSales($table, $salesWriteOffsPlan->offline_sales_plan); + + $salesOnline = $service->getMonthCategoryShareOrWriteOff($currentDate->format('Y-m-d'), ['sales_type' => 'online', 'store_id' => $model->store_id], AutoPlannogrammaService::TYPE_SALES); + + $salesMatrixOnline = Sales::find()->alias('s')->select([ + "COUNT(*) as cnt", + "SUM(CASE WHEN operation='Продажа' THEN sp.summ ELSE (CASE WHEN operation='Возврат' THEN -sp.summ ELSE 0 END) END) as total", + "s.store_id", + "p1c.type as type", + "TO_CHAR(s.date, 'YYYY-MM') as month" + ]) + ->leftJoin('sales_products sp', 's.id = sp.check_id') + ->leftJoin('products_1c p1c', 'p1c.id = sp.product_id') + ->where(['between', 's.date', $date_start, $date_end]) + ->andWhere(['not in', 'order_id', ['', '0']]) + ->andWhere(['p1c.type' => 'Матрица']) + ->andWhere(['s.store_id' => $model->store_id]) + ->groupBy([ + 's.store_id', + "TO_CHAR(s.date, 'YYYY-MM')", + "p1c.type" + ]) + ->orderBy(['month' => SORT_ASC, 'type' => SORT_ASC]) + ->asArray() + ->all(); + + + foreach ($salesOnline[$model->store_id] as $sale) { + $types[$sale['category']] = 1; + $tableOnline[$model->store_id][$sale['category']] = $sale['total_sum'] ; + } + foreach ($salesMatrixOnline as $saleMatrix) { + $types[$saleMatrix['type']] = 1; + $tableOnline[$saleMatrix['store_id']][$saleMatrix['type']] = ($tableOnline[$saleMatrix['store_id']][$saleMatrix['type']] ?? 0) + $weights[$saleMatrix['month']] * $saleMatrix['total'] ; + } + + $onlinePlannedSales = self::calculatePlannedSales($tableOnline, $salesWriteOffsPlan->online_sales_shop_plan); + + $eit = ExportImportTable::find()->where(['entity' => 'city_store', 'export_id' => 1, 'entity_id' => $model->store_id])->one(); + $store_id = $eit->export_val ?? ''; + $writeOffs = $service->getMonthCategoryShareOrWriteOff($currentDate->format('Y-m-d'), ['store_id' => $model->store_id], AutoPlannogrammaService::TYPE_WRITE_OFFS); + + $writeOffsMatrix = WriteOffs::find()->alias('wo')->select([ + 'sum(wop.summ) as total', + 'ex.entity_id AS store_id', + "p1c.type as p1ctype", + "TO_CHAR(wo.date, 'YYYY-MM') as month" + ]) + ->leftJoin('write_offs_products wop', 'wop.write_offs_id = wo.id') + ->leftJoin('products_1c p1c', 'p1c.id = wop.product_id') + ->leftJoin('products_1c_nomenclature p1cn', 'p1cn.id = wop.product_id') + ->leftJoin('export_import_table ex', 'ex.export_val = wo.store_id') + ->where(['between', 'wo.date', $date_start, $date_end]) + ->andWhere(['wo.type' => WriteOffsErp::WRITE_OFFS_TYPE_BRAK]) + ->andWhere(['p1c.type' => 'Матрица']) + ->andWhere(['wo.store_id' => $store_id]) + ->groupBy([ + "month", + 'ex.entity_id', + "p1c.type" + ]) + ->asArray()->all(); + + foreach ($writeOffs[$model->store_id] as $writeoff) { + $types[$writeoff['category']] = 1; + $tableWriteOffs[$model->store_id][$writeoff['category']] = $writeoff['total_sum'] ; + } + + foreach ($writeOffsMatrix as $writeOffMatrix) { + $types[$writeOffMatrix['p1ctype']] = 1; + $tableWriteOffs[$writeOffMatrix['store_id']][$writeOffMatrix['p1ctype']] = ($tableWriteOffs[$writeOffMatrix['store_id']][$writeOffMatrix['p1ctype']] ?? 0) + $weights[$writeOffMatrix['month']] * $writeOffMatrix['total']; + } + + $plannedWriteOffs = self::calculatePlannedSales($tableWriteOffs, $salesWriteOffsPlan->write_offs_plan); + $types = array_keys($types); + ///////////////////////////////////////////////////////////////////////////////////// + + foreach ($types as $type) { + if (!empty($type) && !isset($categoryPlan[$type])) { + $categoryPlanNew = new CategoryPlan; + $categoryPlanNew->year = $model->year; + $categoryPlanNew->month = $model->month; + $categoryPlanNew->store_id = $model->store_id; + $categoryPlanNew->category = $type; + $categoryPlanNew->offline = $offlinePlannedSales[$model->store_id][$type] ?? 0; + $categoryPlanNew->internet_shop = $onlinePlannedSales[$model->store_id][$type] ?? 0; + $categoryPlanNew->marketplace = 0; + $categoryPlanNew->write_offs = $plannedWriteOffs[$model->store_id][$type] ?? 0; + $categoryPlanNew->created_at = date('Y-m-d HH:i:s'); + $categoryPlanNew->updated_at = date('Y-m-d HH:i:s'); + $categoryPlanNew->created_by = Yii::$app->user->id; + $categoryPlanNew->updated_by = Yii::$app->user->id; + $categoryPlanNew->save(); + if ($categoryPlanNew->getErrors()) { + throw new Exception(Json::encode($categoryPlanNew->getErrors())); + } + } + } + + $categoryPlan = CategoryPlan::find()->where(['year' => $model->year, 'month' => $model->month, 'store_id' => $model->store_id])->indexBy('category')->asArray()->all(); + + + $order = [ + 'Срезка' => 1, + 'Сухоцветы' => 2, + 'Горшечные_растения' => 3, + 'Сопутствующие_товары' => 4, + 'Упаковка' => 5, + 'Матрица' => 6, + ]; + + usort($types, function ($a, $b) use ($order) { + return ($order[$a] ?? 999) <=> ($order[$b] ?? 999); + }); + + } + + return $this->render('new', compact('model', 'years', 'stores', 'table', 'tableOnline', + 'tableWriteOffs', 'types', 'salesWriteOffsPlan', 'isEditable', 'categoryPlan')); + } public function actionGetStores() { Yii::$app->response->format = Response::FORMAT_JSON; diff --git a/erp24/views/category-plan/new.php b/erp24/views/category-plan/new.php new file mode 100644 index 00000000..4b58e8d2 --- /dev/null +++ b/erp24/views/category-plan/new.php @@ -0,0 +1,250 @@ +registerJsFile('/js/category-plan/index.js', ['position' => \yii\web\View::POS_END]); + +$this->registerCss(' +input[readonly] { + background: lightgray; +} +'); + +?> + +
+ +

План по категориям

+

Yii::$app->request->url] + ), + ['class' => 'ms-3 mb-3 pb-3', 'target' => '_blank', 'title' => 'Открыть документацию'] + ) ?>

+ + + 'filter-form', + 'method' => 'GET', + 'action' => '/category-plan/new' + ]) ?> + +
+
+
+
+ field($model, 'year')->dropDownList($years, ['onchange' => '//this.form.submit();'])->label(false) ?> +
+
+ field($model, 'city_id')->widget(Select2::class, [ + 'data' => ArrayHelper::map(StoreCityList::findAll(['type' => StoreCityList::TYPE_CITY]), 'id', 'name'), + 'language' => 'ru', + 'options' => ['placeholder' => 'Города...'], + 'pluginOptions' => [ + 'allowClear' => true, + ] + ])->label(false) ?> +
+
+ field($model, 'store_type_id')->widget(Select2::class, [ + 'data' => ArrayHelper::map(StoreType::find()->all(), 'id', 'name'), + 'language' => 'ru', + 'options' => ['placeholder' => 'Тип магазина...'], + 'pluginOptions' => [ + 'allowClear' => true, + ] + ])->label(false) ?> +
+
+
+
+ field($model, 'month')->dropDownList(HtmlHelper::getMonthNames(), ['onchange' => '//this.form.submit();'])->label(false) ?> +
+
+ field($model, 'region_id')->widget(Select2::class, [ + 'data' => ArrayHelper::map(StoreCityList::findAll(['type' => StoreCityList::TYPE_REGION]), 'id', 'name'), + 'language' => 'ru', + 'options' => ['placeholder' => 'Регион...'], + 'pluginOptions' => [ + 'allowClear' => true, + ] + ])->label(false) ?> +
+
+ field($model, 'territory_manager_id')->widget(Select2::class, [ + 'data' => ArrayHelper::map(Admin::findAll(['group_id' => AdminGroup::GROUP_BUSH_DIRECTOR]), 'id', 'name_full'), + 'language' => 'ru', + 'options' => ['placeholder' => 'Тер. Управляющий...'], + 'pluginOptions' => [ + 'allowClear' => true, + ] + ])->label(false) ?> +
+
+ +
+
+
+
+ field($model, 'raion_id')->widget(Select2::class, [ + 'data' => ArrayHelper::map(StoreCityList::findAll(['type' => StoreCityList::TYPE_DISTRICT]), 'id', 'name'), + 'language' => 'ru', + 'options' => ['placeholder' => 'Район...'], + 'pluginOptions' => [ + 'allowClear' => true, + ] + ])->label(false) ?> +
+
+ field($model, 'kshf_id')->widget(Select2::class, [ + 'data' => ArrayHelper::map(Admin::findAll(['group_id' => AdminGroup::GROUP_BUSH_CHEF_FLORIST]), 'id', 'name'), + 'language' => 'ru', + 'options' => ['placeholder' => 'кШФ...'], + 'pluginOptions' => [ + 'allowClear' => true, + ] + ])->label(false) ?> +
+
+
+
+
+
+
+
+ +
+
+
+
+ field($model, 'store_id')->dropDownList($stores, [ + 'multiple' => false, + 'size' => 6, + 'class' => 'form-control', + 'id' => 'selected-store', + 'onchange' => '//this.form.submit()', + 'style'=> 'overflow-y: scroll, height: auto; max-height: 200px; overflow-x: hidden;', + ])->label(false) ?> +
+
+
+
+
+ 'btn btn-secondary'])?> + month && $model->year && $model->store_id) { ?> + 'btn btn-danger ms-2', + 'name' => 'delete', + 'value' => 1, + 'data' => [ + 'confirm' => 'Вы уверены, что хотите вернуть автоплан за ' + . $model->month . '.' . $model->year . '?', + ], + ]) + ?> + 'btn btn-success ms-2', + 'disabled' => true, + 'id' => 'rebuild', + 'name' => 'rebuild', + 'value' => 1, + 'data' => [ + 'confirm' => 'Вы уверены, что хотите пересчитать автопланограмму за ' + . $model->month . '.' . $model->year . ' для магазина ' . $stores[$model->store_id] . '?', + ], + ]) + ?> + +
+
+
+
+ + + store_id)): ?> +
+
+

store_id]?>

+
+
+ +
+ + offline_sales_plan; + $online_sale = $salesWriteOffsPlan->online_sales_shop_plan; + $write_offs = $salesWriteOffsPlan->write_offs_plan; + ?> + + + + + + + + + + + + + + + + + + + ?> + + + + + + +
КатегорииПлан продажСписания
ОффлайнИнтернет-МагазинСписания
%Сумма%Cумма%Cумма
data-offline="" data-offline-type=""> 'number', 'style' => 'max-width: 80px;', 'readonly' => !$isEditable, 'onchange' => 'editProcent(this);']) ?>% 'number', 'readonly' => true, ]) ?> data-online="" data-online-type=""> 'number', 'style' => 'max-width: 80px;', 'readonly' => !$isEditable, 'onchange' => 'editProcent(this);']) ?>% 'number', 'readonly' => true, ]) ?> data-writeoffs="" data-writeoffs-type=""> 'number', 'style' => 'max-width: 80px;', 'readonly' => !$isEditable, 'onchange' => 'editProcent(this);']) ?>% 'number', 'readonly' => true, ]) ?>
+
+ + + Не задан план по магазину. Чтобы задать план пройдите на страницу + . + + + + +