]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
[ERP-381] fix price
authorAlexander Smirnov <fredeom@mail.ru>
Mon, 31 Mar 2025 18:59:52 +0000 (21:59 +0300)
committerAlexander Smirnov <fredeom@mail.ru>
Mon, 31 Mar 2025 18:59:52 +0000 (21:59 +0300)
erp24/controllers/BouquetController.php
erp24/migrations/m250331_125814_create_Table_bouquet_composition_price.php [new file with mode: 0755]
erp24/records/BouquetComposition.php
erp24/records/BouquetCompositionPrice.php [new file with mode: 0644]
erp24/views/bouquet/_product_edit.php
erp24/views/bouquet/_product_list.php
erp24/views/bouquet/index.php
erp24/views/bouquet/update.php
erp24/web/js/bouquet/bouquet.js

index 1d73c585c030198fbf27d79cdd493667f6941aa3..a38552a4ac4f576d4895318a4c58c15b1a2b4264 100644 (file)
@@ -150,17 +150,13 @@ class BouquetController extends Controller
         $data = json_decode(Yii::$app->request->getRawBody(), true);
         $model = BouquetComposition::findOne($data['bouquetId']);
 
-        $cost = [];
-        $markup = [];
+        $costModels = [];
         foreach (BouquetComposition::getRegions() as $region_id) {
-            $cost[$region_id] = round($model->getBouquetCost($region_id, $data, true));
-            $markup[$region_id] = round($model->getBouquetCostMarkup($region_id, $data, true), 2);
+            $costModels[$region_id] = $model->getCostModel($region_id, $data['data']);
         }
 
         return [
-            'selfcost' => round($model->getSelfCost($data), 2),
-            'cost' => $cost,
-            'markup' => $markup,
+            'costModels' => $costModels,
         ];
     }
 
diff --git a/erp24/migrations/m250331_125814_create_Table_bouquet_composition_price.php b/erp24/migrations/m250331_125814_create_Table_bouquet_composition_price.php
new file mode 100755 (executable)
index 0000000..1f092d2
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+use yii\db\Migration;
+
+/**
+ * Class m250331_125814_create_Table_bouquet_composition_price
+ */
+class m250331_125814_create_Table_bouquet_composition_price extends Migration
+{
+    const TABLE_NAME = 'erp24.bouquet_composition_price';
+    /**
+     * {@inheritdoc}
+     */
+    public function safeUp()
+    {
+        $tableSchema = $this->db->getTableSchema(self::TABLE_NAME);
+
+        if (!isset($tableSchema)) {
+            $this->createTable(self::TABLE_NAME, [
+                'id' => $this->primaryKey(),
+                'bouquet_id' => $this->integer()->notNull()->comment('ID букета'),
+                'region_id' => $this->integer()->notNull()->comment('52 - Нижний Новгород, 77 - Москва'),
+                'selfcost' => $this->float()->notNull()->comment('Себестоимость'),
+                'selfcost_markup' => $this->float()->notNull()->defaultValue(30.0)
+                    ->comment('Наценка над себестоимостью. По умолчанию 30%'),
+                'selfcost_markup_price' => $this->float()->notNull()
+                    ->comment('Наценка над себестоимостью в рублёвом эквиваленте'),
+                'price' => $this->float()->notNull()->comment('Цена + наценка ~= selfcost * 130% * 115%'),
+                'price_markup' => $this->float()->notNull()->defaultValue(15.0)
+                    ->comment('Наценка над базовой ценой = себестоимостью * 130%. По умолчанию 15%'),
+            ]);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function safeDown()
+    {
+        $tableSchema = $this->db->getTableSchema(self::TABLE_NAME);
+
+        if (isset($tableSchema)) {
+            $this->dropTable(self::TABLE_NAME);
+        }
+    }
+}
index 34f367af8c7246f88c4fcba3acb0d9139b122329..23cd7b725de3a5d5ea684989048d131ff0418eec 100644 (file)
@@ -40,13 +40,12 @@ use yii_app\services\FileService;
 class BouquetComposition extends ActiveRecord
 {
     const REGION_NN = 52;
-    const REGIONO_MSK = 77;
-    const PROCENT_30 = 0.3;
+    const REGION_MSK = 77;
 
     public static function getRegions() {
         return [
             self::REGION_NN,
-            self::REGIONO_MSK
+            self::REGION_MSK
         ];
     }
 
@@ -412,6 +411,48 @@ class BouquetComposition extends ActiveRecord
         $newPriceDynamic->price = round($cost);
         $newPriceDynamic->region_id = $region_id;
         $newPriceDynamic->save();
+
+        $pricesRegion = PricesRegion::find()
+            ->where(['product_id' => $this->guid, 'region_id' => $region_id])
+            ->one();
+        /* @var $pricesRegion PricesRegion */
+        if (!$pricesRegion) {
+            $pricesRegion = new PricesRegion;
+            $pricesRegion->product_id = $this->guid;
+            $pricesRegion->region_id = $region_id;
+        }
+        $pricesRegion->price = $newPriceDynamic->price;
+        $pricesRegion->save();
+
+        $costModel = BouquetCompositionPrice::find()->where(['bouquet_id' => $this->id, 'region_id' => $region_id])->one();
+        /* @var $costModel BouquetCompositionPrice */
+        if (!$costModel) {
+            $costModel = new BouquetCompositionPrice;
+            $costModel->bouquet_id = $this->id;
+            $costModel->region_id = $region_id;
+            $costModel->selfcost = $this->getSelfCost();
+            $costModel->selfcost_markup = 30;
+            $costModel->selfcost_markup_price = 0.3 * $costModel->selfcost;
+        }
+        $costModel->price = round($cost);
+        $costModel->price_markup = $costModel->selfcost > 0 ? 100 * $costModel->price / ($costModel->selfcost_markup_price + $costModel->selfcost) : 0;
+        $costModel->save();
+    }
+
+    public function getCostModel($region_id, $data = null, $force = false) {
+        $costModel = BouquetCompositionPrice::find()->where(['bouquet_id' => $this->id, 'region_id' => $region_id])->one();
+        if (!$costModel || $force) {
+            $costModel = new BouquetCompositionPrice;
+            $costModel->bouquet_id = $this->id;
+            $costModel->region_id = $region_id;
+            $costModel->selfcost = $this->getSelfCost($data);
+            $costModel->selfcost_markup = 30;
+            $costModel->selfcost_markup_price = 0.3 * $costModel->selfcost;
+            $costModel->price = round(1.15 * ($costModel->selfcost_markup_price + $costModel->selfcost));
+            $costModel->price_markup = $costModel->selfcost > 0 ? 100 * $costModel->price / ($costModel->selfcost_markup_price + $costModel->selfcost) : 0;
+            $costModel->save();
+        }
+        return $costModel;
     }
 
     public function getBouquetCost($region_id, $data = null, $forceDefault = false) {
@@ -535,4 +576,4 @@ class BouquetComposition extends ActiveRecord
         }
         return (int)date('d') > 10;
     }
-}
\ No newline at end of file
+}
diff --git a/erp24/records/BouquetCompositionPrice.php b/erp24/records/BouquetCompositionPrice.php
new file mode 100644 (file)
index 0000000..03588cb
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+
+namespace yii_app\records;
+
+use Yii;
+
+/**
+ * This is the model class for table "bouquet_composition_price".
+ *
+ * @property int $id
+ * @property int $bouquet_id ID букета
+ * @property int $region_id 52 - Нижний Новгород, 77 - Москва
+ * @property float $selfcost Себестоимость
+ * @property float $selfcost_markup Наценка над себестоимостью. По умолчанию 30%
+ * @property float $selfcost_markup_price Наценка над себестоимостью в рублёвом эквиваленте
+ * @property float $price Цена + наценка ~= selfcost * 130% * 115%
+ * @property float $price_markup Наценка над базовой ценой = себестоимостью * 130%. По умолчанию 15%
+ */
+class BouquetCompositionPrice extends \yii\db\ActiveRecord
+{
+    /**
+     * {@inheritdoc}
+     */
+    public static function tableName()
+    {
+        return 'bouquet_composition_price';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rules()
+    {
+        return [
+            [['bouquet_id', 'region_id', 'selfcost', 'selfcost_markup_price', 'price'], 'required'],
+            [['bouquet_id', 'region_id'], 'default', 'value' => null],
+            [['bouquet_id', 'region_id'], 'integer'],
+            [['selfcost', 'selfcost_markup', 'selfcost_markup_price', 'price', 'price_markup'], 'number'],
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function attributeLabels()
+    {
+        return [
+            'id' => 'ID',
+            'bouquet_id' => 'Bouquet ID',
+            'region_id' => 'Region ID',
+            'selfcost' => 'Selfcost',
+            'selfcost_markup' => 'Selfcost Markup',
+            'selfcost_markup_price' => 'Selfcost Markup Price',
+            'price' => 'Price',
+            'price_markup' => 'Price Markup',
+        ];
+    }
+}
index 7bb4460d77df1cff18357ad1bdeb52b60677e1b8..3f59eb2ff866a4d2582d671aefcd5b8cb62dbb4f 100644 (file)
@@ -1,9 +1,14 @@
-<?php use app\widgets\DualList;
+<?php
+
+use app\widgets\DualList;
 use yii\helpers\Html;
 use yii_app\records\BouquetComposition;
+use yii_app\records\BouquetCompositionPrice;
 
 $this->registerCSS('.cost-value {max-width: 75px}');
 
+/* @var $costModelsByRegionId array */
+
 ?>
 <?= DualList::widget([
     'name' => 'products',
@@ -27,10 +32,13 @@ $this->registerCSS('.cost-value {max-width: 75px}');
                 <div class="spinner-border text-primary" role="status"></div>
             </div>
             <strong style="display: inline-block; font-size: 1.1rem;" class="mb-2">Нижегородская обл.</strong><br>
-            <p class="mb-1"><strong>Себестоимость:</strong> <span class="selfcost-value"><?= round($selfCost ?? 0) ?></span> ₽</p>
-            <p class="mb-1"><strong>Наценка:</strong> +30% / +<span class="markup-value"><?= round(BouquetComposition::PROCENT_30 * ($selfCost ?? 0)) ?></span>₽</p>
-            <p class="mb-0"><strong>Цена:</strong> <input type="number" name="cost-value[52]" class="cost-value region52" value="<?= round($cost[BouquetComposition::REGION_NN]) ?>"
-                                                          min="<?= round((1 + BouquetComposition::PROCENT_30) * ($selfCost ?? 0)) ?>" step="1" />₽. +<span class="markup-cost-value region52"><?= round($markUp[BouquetComposition::REGION_NN], 2) ?></span>%</p>
+            <?php $costModelNN = $costModelsByRegionId[BouquetComposition::REGION_NN]; /* @var $costModelNN BouquetCompositionPrice */ ?>
+            <p class="mb-1"><strong>Себестоимость:</strong> <span class="selfcost-value region<?= BouquetComposition::REGION_NN ?>"><?= round($costModelNN->selfcost) ?></span> ₽</p>
+            <p class="mb-1"><strong>Наценка:</strong> +<?= $costModelNN->selfcost_markup ?>% / +<span class="markup-value region<?= BouquetComposition::REGION_NN ?>"><?= round($costModelNN->selfcost_markup_price) ?></span>₽</p>
+            <p class="mb-0"><strong>Цена:</strong> <?= Html::textInput("cost-value[" . BouquetComposition::REGION_NN . "]", round($costModelNN->price), [
+                    "type" => "number", "class" => "cost-value region" . BouquetComposition::REGION_NN,
+                    "min" => round($costModelNN->selfcost_markup_price + $costModelNN->selfcost),
+                    "step" => 1]) ?>₽. +<span class="markup-cost-value region<?= BouquetComposition::REGION_NN ?>"><?= round($costModelNN->price_markup, 2) ?></span>%</p>
         </div>
     </div>
 
@@ -43,10 +51,13 @@ $this->registerCSS('.cost-value {max-width: 75px}');
                 <div class="spinner-border text-primary" role="status"></div>
             </div>
             <strong style="display: inline-block; font-size: 1.1rem;" class="mb-2">Московская обл.</strong><br>
-            <p class="mb-1"><strong>Себестоимость:</strong> <span class="selfcost-value"><?= round($selfCost ?? 0) ?></span> ₽</p>
-            <p class="mb-1"><strong>Наценка:</strong> +30% / +<span class="markup-value"><?= round(BouquetComposition::PROCENT_30 * ($selfCost ?? 0)) ?></span>₽</p>
-            <p class="mb-0"><strong>Цена:</strong> <input type="number" name="cost-value[77]" class="cost-value region77" value="<?= round($cost[BouquetComposition::REGIONO_MSK]) ?>"
-                                                          min="<?= round((1 + BouquetComposition::PROCENT_30) * ($selfCost ?? 0)) ?>" step="1" />₽. +<span class="markup-cost-value region77"><?= round($markUp[BouquetComposition::REGIONO_MSK], 2) ?></span>%</p>
+            <?php $costModelMsk = $costModelsByRegionId[BouquetComposition::REGION_MSK]; /* @var $costModelMsk BouquetCompositionPrice */ ?>
+            <p class="mb-1"><strong>Себестоимость:</strong> <span class="selfcost-value region<?= BouquetComposition::REGION_MSK ?>"><?= round($costModelMsk->selfcost) ?></span> ₽</p>
+            <p class="mb-1"><strong>Наценка:</strong> +<?= $costModelMsk->selfcost_markup ?>% / +<span class="markup-value region<?= BouquetComposition::REGION_MSK ?>"><?= round($costModelMsk->selfcost_markup_price) ?></span>₽</p>
+            <p class="mb-0"><strong>Цена:</strong> <?= Html::textInput("cost-value[" . BouquetComposition::REGION_MSK . "]", round($costModelMsk->price), [
+                    "type" => "number", "class" => "cost-value region" . BouquetComposition::REGION_MSK,
+                    "min" => round($costModelMsk->selfcost_markup_price + $costModelMsk->selfcost),
+                    "step" => 1]) ?>₽. +<span class="markup-cost-value region<?= BouquetComposition::REGION_MSK ?>"><?= round($costModelMsk->price_markup, 2) ?></span>%</p>
         </div>
     </div>
 
index 8d8986385239503aef36ddeac716547808bea008..d9b99bd7f24e68bcd532341bfd2b9f950720d846 100644 (file)
@@ -1,3 +1,9 @@
+<?php
+
+/* @var $model BouquetComposition */
+
+?>
+
 <div class="row pt-3 pb-2">
     <div class="col-md-4 text-center font-weight-bold">Название</div>
     <div class="col-md-2 text-center font-weight-bold">Кол-во</div>
     <div class="col-md-8">
         <strong>Нижегородская обл.</strong>
         <div class='self-cost pt-3' style='display: flex; gap: 10px;'>
-            Себестоимость: <?= round($model->getSelfCost()) ?>₽<br>
-            Наценка: +30% / +<?= round(BouquetComposition::PROCENT_30 * $model->getSelfCost()) ?>₽<br>
-            Цена: <?= round($model->getBouquetCost(BouquetComposition::REGION_NN)) ?>₽. +<?= round($model->getBouquetCostMarkup(BouquetComposition::REGION_NN), 2) ?>%<br>
+            <?php $cmNN = $model->getCostModel(BouquetComposition::REGION_NN); ?>
+            Себестоимость: <?= round($cmNN->selfcost) ?>₽<br>
+            Наценка: +<?= round($cmNN->selfcost_markup) ?>% / +<?= round($cmNN->selfcost_markup_price) ?>₽<br>
+            Цена: <?= round($cmNN->price) ?>₽. +<?= round($cmNN->price_markup, 2) ?>%<br>
         </div>
     </div>
 </div>
     <div class="col-md-8">
         <strong>Московская обл.</strong>
         <div class='self-cost pt-3' style='display: flex; gap: 10px;'>
-            Себестоимость: <?= round($model->getSelfCost()) ?>₽<br>
-            Наценка: +30% / +<?= round(BouquetComposition::PROCENT_30 * $model->getSelfCost()) ?>₽<br>
-            Цена: <?= round($model->getBouquetCost(BouquetComposition::REGIONO_MSK)) ?>₽. +<?= round($model->getBouquetCostMarkup(BouquetComposition::REGIONO_MSK), 2) ?>%<br>
+            <?php $cmMsk = $model->getCostModel(BouquetComposition::REGION_MSK); ?>
+            Себестоимость: <?= round($cmMsk->selfcost) ?>₽<br>
+            Наценка: +<?= round($cmMsk->selfcost_markup) ?>% / +<?= round($cmMsk->selfcost_markup_price) ?>₽<br>
+            Цена: <?= round($cmMsk->price) ?>₽. +<?= round($cmMsk->price_markup, 2) ?>%<br>
         </div>
     </div>
     <div class="col-md-4">
index 587401ae512f719f708bec77c9be180b4f3e2bc4..54ff566228f104be6aaee93183538f6f317b3566 100644 (file)
@@ -10,6 +10,7 @@ use yii_app\records\Files;
 use yii_app\records\MatrixType;
 use yii_app\records\Products1c;
 use yii_app\records\WriteOffsErp;
+use yii_app\records\BouquetCompositionPrice;
 
 /** @var yii\web\View $this */
 $this->title = 'Содержание матрицы';
@@ -101,6 +102,10 @@ $this->title = 'Содержание матрицы';
                         $imageUrls += array_fill(0, 3 - count($imageUrls), null);
                         $compositionHtml .= "</div>";
 
+                        $cmNN = $model->getCostModel(BouquetComposition::REGION_NN);
+                        $cmMsk = $model->getCostModel(BouquetComposition::REGION_MSK);
+                        /* @var $cmNN BouquetCompositionPrice */
+                        /* @var $cmMsk BouquetCompositionPrice */
                         return "
                         <div class='matrix-data mx-3'>
                         <div class='col-md-1'></div>
@@ -113,17 +118,17 @@ $this->title = 'Содержание матрицы';
                                 <div class='self-cost pt-3'>
                                     <div><strong>Нижегородская обл.</strong></div>
                                     <div>
-                                    Себестоимость: " . round($model->getSelfCost()) . "₽<br>
-                                    Наценка: +30% / +" . round(BouquetComposition::PROCENT_30 * $model->getSelfCost()) . "₽<br>
-                                    Цена: " . round($model->getBouquetCost(BouquetComposition::REGION_NN)) . "₽. +" . round($model->getBouquetCostMarkup(BouquetComposition::REGION_NN), 2) . "%<br>
+                                    Себестоимость: " . round($cmNN->selfcost) . "₽<br>
+                                    Наценка: +". round($cmNN->selfcost_markup)  ."% / +" . round($cmNN->selfcost_markup_price) . "₽<br>
+                                    Цена: " . round($cmNN->price) . "₽. +" . round($cmNN->price_markup, 2) . "%<br>
                                     </div>
                                 </div>
                                 <div class='self-cost pt-3'>
                                     <div><strong>Московская обл.</strong></div>
                                     <div>
-                                    Себестоимость: " . round($model->getSelfCost()) . "₽<br>
-                                    Наценка: +30% / +" . round(BouquetComposition::PROCENT_30 * $model->getSelfCost()) . "₽<br>
-                                    Цена: " . round($model->getBouquetCost(BouquetComposition::REGIONO_MSK)) . "₽. +" . round($model->getBouquetCostMarkup(BouquetComposition::REGIONO_MSK), 2) . "%<br>
+                                    Себестоимость: " . round($cmMsk->selfcost) . "₽<br>
+                                    Наценка: +". round($cmMsk->selfcost_markup)  ."% / +" . round($cmMsk->selfcost_markup_price) . "₽<br>
+                                    Цена: " . round($cmMsk->price) . "₽. +" . round($cmMsk->price_markup, 2) . "%<br>
                                     </div>
                                 </div>
                             </div>
index a185d669943bebb823638c2d89157f034529a054..4961e29b2028fc5a1b3fcd936ce398037505075d 100644 (file)
@@ -57,11 +57,9 @@ $this->registerJsFile('/js/bouquet/bouquet.js', ['position' => \yii\web\View::PO
             <?php $form = ActiveForm::begin(['id' => 'dual-list-form']); ?>
             <div class="row mb-5"></div>
             <?php
-            $cost = [];
-            $markup = [];
+            $costModels = [];
             foreach (BouquetComposition::getRegions() as $region_id) {
-                $cost[$region_id] = $model->getBouquetCost($region_id);
-                $markup[$region_id] = $model->getBouquetCostMarkup($region_id);
+                $costModels[$region_id] = $model->getCostModel($region_id);
             }
             ?>
             <?= $this->render('_product_edit', [
@@ -69,9 +67,7 @@ $this->registerJsFile('/js/bouquet/bouquet.js', ['position' => \yii\web\View::PO
                 'selectedItems' => $selectedItems,
                 'isCreate' => false,
                 'listContainerSize' => [],
-                'selfCost' => $model->getSelfCost(),
-                'cost' => $cost,
-                'markUp' => $markup,
+                'costModelsByRegionId' => $costModels,
             ]); ?>
             <?php ActiveForm::end(); ?>
         </div>
\ No newline at end of file
index 56cbcb277cfcb5b6d60c7d95bf5980c9026ab827..3f469c22ad69b59681fe21b28a2cfe6c87ada93b 100644 (file)
@@ -167,23 +167,12 @@ $('.calculate-btn').on('click', function () {
         dataType: 'json',
         success: function (response) {
             if (response) {
-                if (response.cost[52] !== undefined) {
-                    $('.cost-value.region52').val(+response.cost[52]);
-                }
-                if (response.cost[77] !== undefined) {
-                    $('.cost-value.region77').val(+response.cost[77]);
-                }
-                if (response.selfcost !== undefined) {
-                    $('.selfcost-value').text(Math.round(response.selfcost));
-                    $('.markup-value').text(Math.round(0.3 * (+response.selfcost)));
-                    $('.cost-value').attr('min', Math.round(1.3 * (+response.selfcost)));
-                }
-                if (response.markup[52] !== undefined) {
-                    $('.markup-cost-value.region52').text(Math.round(+response.markup[52]));
-                }
-                if (response.markup[77] !== undefined) {
-                    $('.markup-cost-value.region77').text(Math.round(+response.markup[77]));
-                }
+                $.each(response.costModels, function (regionId, costModel) {
+                    $('.cost-value.region' + regionId).val(+costModel.price).attr('min', Math.round(costModel.selfcost + costModel.selfcost_markup_price));
+                    $('.selfcost-value.region' + regionId).text(Math.round(costModel.selfcost));
+                    $('.markup-value.region' + regionId).text(Math.round(costModel.selfcost_markup));
+                    $('.markup-cost-value.region' + regionId).text(Math.round(costModel.price_markup));
+                })
             }
         },
         error: function (xhr, status, error) {