]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
[ERP-283] редактируемые категории планов
authorAlexander Smirnov <fredeom@mail.ru>
Mon, 10 Feb 2025 15:43:53 +0000 (18:43 +0300)
committerAlexander Smirnov <fredeom@mail.ru>
Mon, 10 Feb 2025 15:43:53 +0000 (18:43 +0300)
erp24/controllers/CategoryPlanController.php
erp24/controllers/SalesWriteOffsPlanController.php
erp24/records/CategoryPlan.php [new file with mode: 0644]
erp24/views/category-plan/index.php
erp24/web/js/category-plan/index.js

index 57d9806f359f7f03b6b7a55e798b9ab23d95f7aa..85a148fb39943eefd246fdc62d7bfedcfbab4d43 100644 (file)
@@ -4,9 +4,12 @@ namespace app\controllers;
 
 use Yii;
 use yii\base\DynamicModel;
+use yii\db\Exception;
 use yii\helpers\ArrayHelper;
+use yii\helpers\Json;
 use yii\web\Controller;
 use yii\web\Response;
+use yii_app\records\CategoryPlan;
 use yii_app\records\CityStore;
 use yii_app\records\CityStoreParams;
 use yii_app\records\ExportImportTable;
@@ -38,6 +41,11 @@ class CategoryPlanController extends Controller {
 
         $model->load(Yii::$app->request->get());
 
+        $isEditable = date($model->year . '-' . $model->month . '-d') > date('Y-m-d') && (
+            (date('d') < 25) || (date('Y-m-d', strtotime('-1 month', 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();
+
         /////////////////////////// CHAT GPT STUFF ///////////////////////////////////
 
         $currentDate = new \DateTime();
@@ -129,6 +137,30 @@ class CategoryPlanController extends Controller {
         $types = array_keys($types);
         /////////////////////////////////////////////////////////////////////////////////////
 
+        foreach ($types as $type) {
+            if (!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 = $table[$model->store_id][$type] ?? 0;
+                $categoryPlanNew->internet_shop = $tableOnline[$model->store_id][$type] ?? 0;
+                $categoryPlanNew->marketplace = 0;
+                $categoryPlanNew->write_offs = $tableWriteOffs[$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();
+
         $salesWriteOffsPlan = SalesWriteOffsPlan::find()->where(['year' => $model->year, 'month' => $model->month, 'store_id' => $model->store_id])->one();
 
         $years = [];
@@ -139,7 +171,7 @@ class CategoryPlanController extends Controller {
         $stores = ArrayHelper::map(CityStore::find()->andWhere(['visible' => '1'])->all(), 'id', 'name');
 
         return $this->render('index', compact('model', 'years', 'stores', 'table', 'tableOnline',
-            'tableWriteOffs', 'types', 'salesWriteOffsPlan'));
+            'tableWriteOffs', 'types', 'salesWriteOffsPlan', 'isEditable', 'categoryPlan'));
     }
 
     public function actionGetStores() {
@@ -203,4 +235,36 @@ class CategoryPlanController extends Controller {
 
         return ArrayHelper::map($stores, 'id', 'name');
     }
+
+    public function actionSaveFields() {
+        $data = Yii::$app->request->post();
+        $year = $data['year'];
+        $month = $data['month'];
+        $storeId = $data['store_id'];
+        $productType = $data['type'];
+        $offline = $data['offline'];
+        $internet_shop = $data['internet_shop'];
+        $write_offs = $data['write_offs'];
+
+        $categoryPlan = CategoryPlan::find()->where(['year' => $year, 'month' => $month, 'store_id' => $storeId, 'category' => $productType])->one();
+        if (!$categoryPlan) {
+            $categoryPlan = new CategoryPlan;
+            $categoryPlan->year = $year;
+            $categoryPlan->month = $month;
+            $categoryPlan->store_id = $storeId;
+            $categoryPlan->category = $productType;
+            $categoryPlan->created_by = Yii::$app->user->id;
+            $categoryPlan->created_at = date('Y-m-d H:i:s');
+        }
+        $categoryPlan->offline = $offline;
+        $categoryPlan->internet_shop = $internet_shop;
+        $categoryPlan->write_offs = $write_offs;
+        $categoryPlan->updated_by = Yii::$app->user->id;
+        $categoryPlan->updated_at = date('Y-m-d H:i:s');
+        $categoryPlan->save();
+        if ($categoryPlan->getErrors()) {
+            throw new \Exception(Json::encode($categoryPlan->getErrors()));
+        }
+        return 'ok';
+    }
 }
index e52cca2e4cdcb3e20aa5f5dff87ab5cc601a217f..031461fbaa6f54bd30ded28c33db041bd7a55e3d 100644 (file)
@@ -35,7 +35,6 @@ class SalesWriteOffsPlanController extends Controller
                 (date('d') < 25) ||
                 (date('Y-m-d', strtotime('-1 month', date($model->year . '-' . $model->month . '-d'))) > date('Y-m-d')
             ));
-//var_dump($isEditable); die;
 
         $prevPrevMonthStart = date("Y-m-01", strtotime("-2 month", strtotime($model->year . "-" . $model->month . "-01")));
         $prevPrevMonthEnd = date("Y-m-t", strtotime("-2 month", strtotime($model->year . "-" . $model->month . "-01")));
diff --git a/erp24/records/CategoryPlan.php b/erp24/records/CategoryPlan.php
new file mode 100644 (file)
index 0000000..1475b48
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+
+namespace yii_app\records;
+
+use Yii;
+use yii\behaviors\BlameableBehavior;
+use yii\behaviors\TimestampBehavior;
+use yii\db\Expression;
+
+/**
+ * This is the model class for table "category_plan".
+ *
+ * @property int $id
+ * @property int $year Год создания отчёта
+ * @property int $month Месяц создания отчёта
+ * @property int $store_id id магазина в ERP
+ * @property string $category Название категории: срезка, горшечные, сопутствующие товары
+ * @property float|null $offline Оффлайн план процент
+ * @property float|null $internet_shop Интернет-магазин план процент
+ * @property float|null $marketplace Маркетплейс план процент
+ * @property float|null $write_offs Списания план процент
+ * @property string $created_at Дата создания
+ * @property string $updated_at Дата обновления
+ * @property int $created_by ID создателя записи
+ * @property int $updated_by ID обновителя записи
+ */
+class CategoryPlan extends \yii\db\ActiveRecord
+{
+    /**
+     * {@inheritdoc}
+     */
+    public static function tableName()
+    {
+        return 'category_plan';
+    }
+
+    public function behaviors() {
+        return [
+            [
+                'class' => TimestampBehavior::class,
+                'createdAtAttribute' => 'created_at',
+                'updatedAtAttribute' => 'updated_at',
+                'value' => new Expression('NOW()'),
+            ],
+            [
+                'class' => BlameableBehavior::class,
+                'createdByAttribute' => 'created_by',
+                'updatedByAttribute' => 'updated_by',
+            ],
+        ];
+    }
+
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rules()
+    {
+        return [
+            [['year', 'month', 'store_id', 'category', 'created_at', 'updated_at', 'created_by', 'updated_by'], 'required'],
+            [['year', 'month', 'store_id', 'created_by', 'updated_by'], 'default', 'value' => null],
+            [['year', 'month', 'store_id', 'created_by', 'updated_by'], 'integer'],
+            [['offline', 'internet_shop', 'marketplace', 'write_offs'], 'number'],
+            [['created_at', 'updated_at'], 'safe'],
+            [['category'], 'string', 'max' => 100],
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function attributeLabels()
+    {
+        return [
+            'id' => 'ID',
+            'year' => 'Year',
+            'month' => 'Month',
+            'store_id' => 'Store ID',
+            'category' => 'Category',
+            'offline' => 'Offline',
+            'internet_shop' => 'Internet Shop',
+            'marketplace' => 'Marketplace',
+            'write_offs' => 'Write Offs',
+            'created_at' => 'Created At',
+            'updated_at' => 'Updated At',
+            'created_by' => 'Created By',
+            'updated_by' => 'Updated By',
+        ];
+    }
+}
index d2818af88e218fadd979cdf343706864af4281ab..24615c836eedb3962bf89ffb59b65b96ab2b3193 100644 (file)
@@ -21,9 +21,17 @@ use yii_app\records\StoreType;
 /* @var $types array */
 /* @var $tableWriteOffs array */
 /* @var $salesWriteOffsPlan SalesWriteOffsPlan */
+/* @var $isEditable boolean */
+/* @var $categoryPlan array */
 
 $this->registerJsFile('/js/category-plan/index.js', ['position' => \yii\web\View::POS_END]);
 
+$this->registerCss('
+input[readonly] {
+  background: lightgray;
+}
+');
+
 ?>
 
 <div class="categoryPlanIndex m-5">
@@ -63,21 +71,6 @@ $this->registerJsFile('/js/category-plan/index.js', ['position' => \yii\web\View
                     ])->label(false) ?>
                 </div>
             </div>
-            <?php
-            //            widget(Select2::class, [
-            //                'data' => $stores,
-            //                'language' => 'ru',
-            //                'options' => ['placeholder' => 'Магазин...'],
-            //                'pluginOptions' => [
-            //                    'allowClear' => true
-            //                ],
-            //                'pluginEvents' => [
-            //                    'change' => 'function(e) {
-            //                        $("#filter-form").get(0).submit();
-            //                    }'
-            //                ]
-            //            ])->label(false)
-            ?>
             <div class="row mt-1">
                 <div class="col-4">
                     <?= $form->field($model, 'month')->dropDownList(HtmlHelper::getMonthNames(), ['onchange' => '//this.form.submit();'])->label(false) ?>
@@ -179,27 +172,35 @@ $this->registerJsFile('/js/category-plan/index.js', ['position' => \yii\web\View
                 ?>
                 <thead>
                     <tr><th rowspan="4" class="text-center align-middle border">Категории</th><th colspan="4" class="text-center border">План продаж</th><th colspan="2" class="text-center border">Списания</th></tr>
-                    <tr><th colspan="2" class="text-center border">Оффлайн</th><th colspan="2" class="text-center border">Интернет-Магазин</th><!--th colspan="2" class="text-center border">Маркетплейс</th--><th colspan="2" class="text-center border">Списания</th></tr>
-                    <tr><th colspan="2" class="text-center border"><?= number_format($offline_sale, 0, '.', ' ') ?></th>
-                        <th colspan="2" class="text-center border"><?= number_format($online_sale, 0, '.', ' ') ?></th><!--th colspan="2" class="text-center border">0</th-->
-                        <th colspan="2" class="text-center border"><?= number_format($write_offs, 0, '.', ' ') ?></th></tr>
-                    <tr><th class="text-center border">%</th><th class="text-center border">Сумма</th><th class="text-center border">%</th><th class="text-center border">Cумма</th><!--th class="text-center border">%</th><th class="text-center border">Cумма</th--><th class="text-center border">%</th><th class="text-center border">Cумма</th></tr>
+                    <tr><th colspan="2" class="text-center border">Оффлайн</th><th colspan="2" class="text-center border">Интернет-Магазин</th><th colspan="2" class="text-center border">Списания</th></tr>
+                    <tr><th colspan="2" class="text-center border" data-value="<?= $offline_sale ?>"><?= number_format($offline_sale, 0, '.', ' ') ?></th>
+                        <th colspan="2" class="text-center border" data-value="<?= $online_sale ?>"><?= number_format($online_sale, 0, '.', ' ') ?></th>
+                        <th colspan="2" class="text-center border" data-value="<?= $write_offs ?>"><?= number_format($write_offs, 0, '.', ' ') ?></th></tr>
+                    <tr><th class="text-center border">%</th><th class="text-center border">Сумма</th><th class="text-center border">%</th><th class="text-center border">Cумма</th><th class="text-center border">%</th><th class="text-center border">Cумма</th></tr>
                 </thead>
                 <tbody>
                     <?php foreach ($types as $type): ?>
-                        <?php $data = $table[$model->store_id][$type] ?? 0; ?>
                         <tr>
-                            <th><?= $type ?></th>
-                            <td><?= number_format($offline_sale <= 0 ? 0 : $data / $offline_sale * 100, 0, '.', ' ') ?>%</td>
-                            <td><?= number_format($data, 0, '.', ' ') ?></td>
-                            <?php $data2 = $tableOnline[$model->store_id][$type] ?? 0; ?>
-                            <td><?= number_format($online_sale <= 0 ? 0 : $data2 / $online_sale * 100, 0, '.', ' ') ?>%</td>
-                            <td><?= number_format($data2, 0, '.', ' ') ?></td>
-                            <!--td></td>
-                            <td></td-->
-                            <?php $data4 = $tableWriteOffs[$type] ?? 0; ?>
-                            <td><?= number_format($write_offs <= 0 ? 0 : $data4 / $write_offs * 100, 0, '.', ' ') ?>%</td>
-                            <td><?= number_format($data4, 0, '.', ' ') ?></td>
+                            <th data-type><?= $type ?></th>
+                            <?php
+                            $data = $categoryPlan[$type]['offline'];
+                            $p1 = $offline_sale <= 0 ? 0 : 1.0 * $data / $offline_sale * 100;
+                            ?>
+                            <td data-p1 data-offline="<?= $offline_sale ?>"><?= number_format($p1, 0, '.', ' ') ?>%</td>
+                            <td><?= Html::textInput('offline', number_format($categoryPlan[$type]['offline'], 0, '.', ''), ['type' => 'number', 'readonly' => !$isEditable, 'onchange' => 'editField(this);']) ?></td>
+                            <?php
+                            $data2 = $categoryPlan[$type]['internet_shop'];
+                            $p2 = $online_sale <= 0 ? 0 : $data2 / $online_sale * 100;
+                            ?>
+                            <td data-p2 data-online="<?= $online_sale ?>"><?= number_format($p2, 0, '.', ' ') ?>%</td>
+                            <td><?= Html::textInput('internet_shop', number_format($categoryPlan[$type]['internet_shop'], 0, '.', ''), ['type' => 'number', 'readonly' => !$isEditable, 'onchange' => 'editField(this);']) ?></td>
+                            <?php //<td></td><td></td> ?>
+                            <?php
+                            $data4 = $categoryPlan[$type]['write_offs'];
+                            $p3 = $write_offs <= 0 ? 0 : $data4 / $write_offs * 100;
+                            ?>
+                            <td data-p3 data-writeoffs="<?= $write_offs ?>"><?= number_format($p3, 0, '.', ' ') ?>%</td>
+                            <td><?= Html::textInput('write_offs', number_format($categoryPlan[$type]['write_offs'], 0, '.', ''), ['type' => 'number',  'readonly' => !$isEditable, 'onchange' => 'editField(this);']) ?></td>
                         </tr>
                     <?php endforeach; ?>
                 </tbody>
index 9687070b2f40b4f7bd07e97a73bb386b70df4385..ee30d0ccf77add5fe480b86f3f4e95a58e486f40 100644 (file)
@@ -3,6 +3,10 @@
 const param26 = $('meta[name=csrf-param]').attr('content');
 const token26 = $('meta[name=csrf-token]').attr('content');
 
+function isNumeric(value) {
+    return /^-?\d+\.?\d*$/.test(value);
+}
+
 function updateStores() {
     const city_id = $('#dynamicmodel-city_id').val();
     const region_id = $('#dynamicmodel-region_id').val();
@@ -27,6 +31,111 @@ function updateStores() {
     });
 }
 
+function editField(zis) {
+    const tr = zis.parentNode.parentNode;
+    const store_id = document.querySelector("#selected-store").value;
+    const type = tr.querySelector("[data-type]").textContent;
+    const offline = tr.querySelector("input[name=offline]").value;
+    const internet_shop = tr.querySelector("input[name=internet_shop]").value;
+    const write_offs = tr.querySelector("input[name=write_offs]").value;
+
+    const editFields = [
+        offline,
+        internet_shop,
+        write_offs
+    ];
+
+    let succ = true;
+    editFields.forEach((el) => {
+        if (!isNumeric(el)) {
+            succ = false;
+            alert(el + " не число");
+            console.log(el);
+        }
+    });
+
+    if (succ) {
+        const p1 = tr.querySelector("[data-p1]");
+        const p2 = tr.querySelector("[data-p2]");
+        const p3 = tr.querySelector("[data-p3]");
+        p1.textContent = (p1.dataset.offline > 0 ? Math.round(100.0 * offline / +p1.dataset.offline) : 0) + '%';
+        p2.textContent = (p2.dataset.online > 0 ? Math.round(100.0 * internet_shop / +p2.dataset.online) : 0) + '%';
+        p3.textContent = (p3.dataset.writeoffs > 0 ? Math.round(100.0 * write_offs / +p3.dataset.writeoffs) : 0) + '%';
+
+        $.ajax({
+            method: "POST",
+            url: '/category-plan/save-fields',
+            data: {
+                year: document.querySelector('#dynamicmodel-year').value,
+                month: document.querySelector('#dynamicmodel-month').value,
+                store_id,
+                type,
+                offline,
+                internet_shop,
+                write_offs,
+                [param26]: token26
+            },
+            dataType: "text",
+            success: function(data) {
+                console.log(data);
+            },
+        });
+    }
+
+    return;
+    /*
+    if (succ) {
+        total_sales_plan.value = +offline_sales_plan.value + (+online_sales_shop_plan.value) + (+online_sales_marketplace_plan.value);
+        ////////////////////////////////////////////////
+        const p1 = tr.querySelector('.p1');
+        const p1Value = total_sales_fact.value > 0 ? total_sales_plan.value / total_sales_fact.value : 0;
+        p1.innerHTML = new Intl.NumberFormat().format(Number((p1Value * 100).toFixed(0)));
+        p1.style.color = colorScheme1(p1Value);
+
+        const p2 = tr.querySelector('.p2');
+        const p2Value = total_sales_plan.value > 0 ? write_offs_plan.value / total_sales_plan.value : 0;
+        p2.innerHTML = new Intl.NumberFormat().format(Number((p2Value * 100).toFixed(1)));
+        p2.style.color = colorScheme2(p2Value);
+
+        const p3 = tr.querySelector('.p3');
+        const p3Value = total_sales_fact.value > 0 ? offline_sales_plan.value / total_sales_fact.value : 0;
+        p3.innerHTML = new Intl.NumberFormat().format(Number((p3Value * 100).toFixed(0)));
+        p3.style.color = colorScheme1(p3Value);
+
+        const p4 = tr.querySelector('.p4');
+        const p4Value = total_sales_fact.value > 0 ? online_sales_shop_plan.value / total_sales_fact.value : 0;
+        p4.innerHTML = new Intl.NumberFormat().format(Number((p4Value * 100).toFixed(0)));
+        p4.style.color = colorScheme1(p4Value);
+
+        const p5 = tr.querySelector('.p5');
+        const p5Value = total_sales_fact.value > 0 ? online_sales_marketplace_plan.value / total_sales_fact.value : 0;
+        p5.innerHTML = new Intl.NumberFormat().format(Number((p5Value * 100).toFixed(0)));
+        p5.style.color = colorScheme1(p5Value);
+        ////////////////////////////////////////////////
+        $.ajax({
+            method: "POST",
+            url: '/sales-write-offs-plan/save-fields',
+            data: {
+                year: document.querySelector('#dynamicmodel-year').value,
+                month: document.querySelector('#dynamicmodel-month').value,
+                store_id,
+                total_sales_plan : total_sales_plan.value,
+                total_sales_fact : total_sales_fact.value,
+                write_offs_plan : write_offs_plan.value,
+                offline_sales_plan : offline_sales_plan.value,
+                online_sales_shop_plan : online_sales_shop_plan.value,
+                online_sales_marketplace_plan : online_sales_marketplace_plan.value,
+                [param25]: token25
+            },
+            dataType: "text",
+            success: function(data) {
+                console.log(data);
+            },
+        });
+    }
+    */
+}
+
 $(document).ready(() => {
     $('#categoryPlan').DataTable({
         sorting: false,