$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,
];
}
--- /dev/null
+<?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);
+ }
+ }
+}
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
];
}
$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) {
}
return (int)date('d') > 10;
}
-}
\ No newline at end of file
+}
--- /dev/null
+<?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',
+ ];
+ }
+}
-<?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',
<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>
<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>
+<?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">
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 = 'Содержание матрицы';
$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>
<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>
<?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', [
'selectedItems' => $selectedItems,
'isCreate' => false,
'listContainerSize' => [],
- 'selfCost' => $model->getSelfCost(),
- 'cost' => $cost,
- 'markUp' => $markup,
+ 'costModelsByRegionId' => $costModels,
]); ?>
<?php ActiveForm::end(); ?>
</div>
\ No newline at end of file
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) {