use yii_app\services\LogService;
use yii_app\services\MarketplaceService;
use yii_app\services\TelegramService;
-use yii_app\services\SelfCostProductDinamicService;
+use yii_app\services\SelfCostProductDynamicService;
class DataController extends BaseController
{
if (!empty($values)) {
$this->setSelfCostUpdate($values);
- SelfCostProductDinamicService::UpdateResult($values);
+ SelfCostProductDynamicService::UpdateResult($values);
}
}
use yii\console\Controller;
use yii\console\ExitCode;
use yii_app\records\SelfCostProduct;
-use yii_app\services\SelfCostProductDinamicService;
+use yii_app\services\SelfCostProductDynamicService;
class SelfCostController extends Controller
->asArray()
->all();
// var_dump($selfCostProduct);
- $resultRow = SelfCostProductDinamicService::PrepareResult($selfCostProduct);
+ $resultRow = SelfCostProductDynamicService::PrepareResult($selfCostProduct);
// print_r($resultRow);
- SelfCostProductDinamicService::SaveResult($resultRow);
+ SelfCostProductDynamicService::SaveResult($resultRow);
// echo " Line " . __LINE__ . "\n";
{
echo "Start merging duplicates " . date('Y-m-d H:i:s') . "\n";
- SelfCostProductDinamicService::MergeDuplicates();
+ SelfCostProductDynamicService::MergeDuplicates();
echo "Duplicates merged successfully " . date('Y-m-d H:i:s') . "\n";
return ExitCode::OK;
use yii_app\records\Products1c;
use yii_app\records\ProductsClass;
use yii_app\records\ReplacementInvoice;
-use yii_app\records\ReplacementInvoiceProducts;
-use yii_app\records\SelfCostProduct;
-use yii_app\records\Shift;
+use yii_app\records\SelfCostProductDynamic;
use yii_app\records\ShiftRemains;
use yii_app\records\ShiftTransfer;
use yii_app\records\StoreBalance;
use yii_app\records\WaybillWriteOffs;
use yii_app\records\WriteOffsErp;
use yii_app\services\TaskService;
+use yii_app\services\SelfCostProductDynamicService;
class ShiftTransferController extends Controller
{
}
$price = ArrayHelper::map(Prices::find()->select(['product_id', 'price'])->where(['product_id' => array_keys($products)])->all(), 'product_id', 'price');
$storeEIT = ExportImportTable::find()->select(['entity_id'])->where(['entity' => 'city_store', 'export_val' => $storeGuid, 'export_id' => 1])->one();
- $selfCost = ArrayHelper::map(SelfCostProduct::find()->select(['product_guid', 'price'])->where(['product_guid' =>
- array_keys($products), 'store_id' => $storeEIT->entity_id /*, 'date' => date('Y-m-d', strtotime($shiftDate))*/])->orderBy(['date' => SORT_ASC])->all(), 'product_guid', 'price');
+ $selfCost = ArrayHelper::map(
+ SelfCostProductDynamic::find()
+ ->select(['product_guid', 'price'])
+ ->where(['product_guid' => array_keys($products), 'store_id' => $storeEIT->entity_id])
+ ->andWhere(new Expression(":d BETWEEN date_from AND date_to", [':d' => date('Y-m-d', strtotime($shiftDate))]))
+ ->orderBy(['date_from' => SORT_DESC])
+ ->all(),
+ 'product_guid', 'price'
+ );
// $a = [['a' => 1, 'b' => 2, 'c' => 3], ['a' => 1, 'b' => 5, 'c' => 4], ['a' => 1, 'b' => 7, 'c' => 5],];
// var_dump(ArrayHelper::map($a, 'a', 'b')); die;
// result: array(1) { [1]=> int(7) }
$storeEIT = ExportImportTable::find()->select(['entity_id'])->where(['entity' => 'city_store', 'export_val' => $storeGuid, 'export_id' => 1])->one();
- $selfCost = SelfCostProduct::find()->select(['price'])->where(['product_guid' => $productGuid, 'store_id' => $storeEIT->entity_id])->orderBy(['date' => SORT_DESC])->one();
+ $selfCost = SelfCostProductDynamicService::getPrice($productGuid, $storeEIT->entity_id, date('Y-m-d'));
$balance = Balances::find()->select(['quantity'])->where(['store_id' => $storeGuid, 'product_id' => $productGuid])->one();
$shiftTransfer = ShiftTransfer::findOne($shiftTransferId);
$shiftRemains = ShiftRemains::findOne(['shift_transfer_id' => $shiftTransferId, 'product_guid' => $productGuid]);
$storeId = array_flip(array_map('strval', CityStore::getAllActiveGuidId()))[$shiftTransfer->store_guid];
- $productSelfCost = SelfCostProduct::findOne(['product_guid' => $productGuid, 'date' => $shiftTransfer->date, 'store_id' => $storeId]);
+ $productSelfCost = SelfCostProductDynamicService::getPrice($productGuid, $storeId, $shiftTransfer->date);
$productReplacement = ArrayHelper::map(
Products1c::find()
$productPrice = Prices::findOne(['product_id' => $productGuid]);
$shiftTransfer = ShiftTransfer::findOne($shiftTransferId);
$storeId = array_flip(array_map('strval', CityStore::getAllActiveGuidId()))[$shiftTransfer->store_guid];
- $productSelfCost = SelfCostProduct::findOne(['product_guid' => $productGuid, 'date' => $shiftTransfer->date, 'store_id' => $storeId]);
+ $productSelfCost = SelfCostProductDynamicService::getPrice($productGuid, $storeId, $shiftTransfer->date);
$count = ShiftRemains::findOne(['shift_transfer_id' => $shiftTransferId, 'product_guid' => $productGuid]);
if (!$productPrice) {
*/
public function getProductPriceSelfCost()
{
- return $this->hasOne(SelfCostProduct::class, ['product_guid' => 'product_id', 'date' => $this->shiftRemains->shiftTransfer->date]);
+ return $this->hasOne(SelfCostProductDynamic::class, ['product_guid' => 'product_id'])
+ ->andOnCondition(":d BETWEEN date_from AND date_to", [
+ ':d' => date('Y-m-d', strtotime($this->shiftRemains->shiftTransfer->date))
+ ]);
}
/**
*/
public function getProductReplacementPriceSelfCost()
{
- return $this->hasOne(SelfCostProduct::class, ['product_guid' => 'product_replacement_id', 'date' => $this->shiftRemains->shiftTransfer->date]);
+ return $this->hasOne(SelfCostProductDynamic::class, ['product_guid' => 'product_replacement_id'])
+ ->andOnCondition(":d BETWEEN date_from AND date_to", [
+ ':d' => date('Y-m-d', strtotime($this->shiftRemains->shiftTransfer->date))
+ ]);
}
use yii_app\records\MotivationCostsItem;
use yii_app\records\MotivationValue;
use yii_app\records\SchedulerTaskLog;
-use yii_app\records\SelfCostProduct;
+use yii_app\services\SelfCostProductDynamicService;
use yii_app\records\WriteOffs;
use yii_app\records\WriteOffsProducts;
continue;
}
- $price = SelfCostProduct::find()
- ->andWhere(['product_guid' => $offProduct->product_id])
- ->andWhere(['store_id' => array_search($writeOff->store_id, $stores)])
- ->andWhere(['date' => date('Y-m-d', strtotime($writeOff->date))])
- ->one();
+ $price = SelfCostProductDynamicService::getPrice(
+ $offProduct->product_id,
+ array_search($writeOff->store_id, $stores),
+ $writeOff->date
+ );
if (empty($price)) {
$price = $offProduct;
+++ /dev/null
-<?php
-
-namespace yii_app\services;
-
-use yii_app\records\SelfCostProductDynamic;
-
-class SelfCostProductDinamicService
-{
-
- public static function PrepareResult($selfCostProduct):array
- {
- $result = [];
-
- foreach ($selfCostProduct as $row) {
- $dates = array_map('trim', explode(',', $row['dates']));
- sort($dates); // сортировка дат на всякий случай
-
- $start = null;
- $prev = null;
-
- foreach ($dates as $date) {
- if ($start === null) {
- // начало интервала
- $start = $date;
- $prev = $date;
- } else {
- // проверим, что дата идёт на следующий день за предыдущей
- $expectedNext = date('Y-m-d', strtotime($prev . ' +1 day'));
- if ($date === $expectedNext) {
- // продолжаем интервал
- $prev = $date;
- } else {
- // закрываем интервал
- $result[] = [
- 'product_guid' => $row['product_guid'],
- 'store_id' => $row['store_id'],
- 'price' => $row['price'],
- 'date_from' => $start,
- 'date_to' => $prev,
- ];
- // новый интервал
- $start = $date;
- $prev = $date;
- }
- }
- }
-
- // закрываем последний интервал
- if ($start !== null) {
- $result[] = [
- 'product_guid' => $row['product_guid'],
- 'store_id' => $row['store_id'],
- 'price' => $row['price'],
- 'date_from' => $start,
- 'date_to' => $prev,
- ];
- }
- }
-
- // Вывод
- return($result);
- }
-
- public static function SaveResult($selfCostProduct)
- {
- foreach ($selfCostProduct as $row) {
- // Ищем ВСЕ существующие записи с теми же параметрами (product_guid, store_id, price)
- $existingRecords = SelfCostProductDynamic::find()
- ->where([
- 'product_guid' => $row['product_guid'],
- 'store_id' => $row['store_id'],
- 'price' => $row['price']
- ])
- ->orderBy(['date_from' => SORT_ASC])
- ->all();
-
- $newDateFrom = strtotime($row['date_from']);
- $newDateTo = strtotime($row['date_to']);
- $recordUpdated = false;
-
- if (!empty($existingRecords)) {
- // Проверяем каждый существующий интервал
- foreach ($existingRecords as $existingRecord) {
- $existingDateFrom = strtotime($existingRecord->date_from);
- $existingDateTo = strtotime($existingRecord->date_to);
-
- // Проверяем, примыкают ли интервалы (разница <= 1 день) или пересекаются
- $daysBetween = min(
- abs($newDateFrom - $existingDateTo) / 86400,
- abs($existingDateFrom - $newDateTo) / 86400
- );
-
- if ($daysBetween <= 1 ||
- ($newDateFrom <= $existingDateTo && $newDateTo >= $existingDateFrom)) {
- // Вычисляем новые границы интервала
- $newCalculatedDateFrom = date('Y-m-d', min($existingDateFrom, $newDateFrom));
- $newCalculatedDateTo = date('Y-m-d', max($existingDateTo, $newDateTo));
-
- // Проверяем, изменились ли даты
- if ($existingRecord->date_from !== $newCalculatedDateFrom ||
- $existingRecord->date_to !== $newCalculatedDateTo) {
- // Расширяем интервал только если данные изменились
- $existingRecord->date_from = $newCalculatedDateFrom;
- $existingRecord->date_to = $newCalculatedDateTo;
- $existingRecord->updated_at = date('Y-m-d H:i:s');
-
- if ($existingRecord->validate()) {
- $existingRecord->save();
- }
- }
-
- $recordUpdated = true;
- break; // Нашли подходящий интервал, выходим из цикла
- }
- }
- }
-
- // Если не удалось обновить ни одну запись - создаем новую
- if (!$recordUpdated) {
- $rowSelfCost = new SelfCostProductDynamic();
- $rowSelfCost->product_guid = $row['product_guid'];
- $rowSelfCost->store_id = $row['store_id'];
- $rowSelfCost->price = $row['price'];
- $rowSelfCost->date_from = $row['date_from'];
- $rowSelfCost->date_to = $row['date_to'];
- $rowSelfCost->updated_at = date('Y-m-d H:i:s');
-
- if ($rowSelfCost->validate()) {
- $rowSelfCost->save();
- }
- }
- }
- }
-
- public static function UpdateResult($values)
- {
- foreach ($values as $row) {
- // Ищем ВСЕ существующие записи с теми же параметрами (product_guid, store_id, price)
- $existingRecords = SelfCostProductDynamic::find()
- ->where([
- 'product_guid' => $row['product_guid'],
- 'store_id' => $row['store_id'],
- 'price' => $row['price']
- ])
- ->orderBy(['date_from' => SORT_ASC])
- ->all();
-
- $newDate = strtotime($row['date']);
- $recordUpdated = false;
-
- if (!empty($existingRecords)) {
- // Проверяем каждый существующий интервал
- foreach ($existingRecords as $existingRecord) {
- $existingDateFrom = strtotime($existingRecord->date_from);
- $existingDateTo = strtotime($existingRecord->date_to);
-
- // Проверяем, примыкает ли новая дата (±1 день от границ интервала) или находится внутри
- $dayBeforeDateFrom = strtotime('-1 day', $existingDateFrom);
- $dayAfterDateTo = strtotime('+1 day', $existingDateTo);
-
- if ($newDate >= $dayBeforeDateFrom && $newDate <= $dayAfterDateTo) {
- // Новая дата примыкает к интервалу или находится внутри него
- $newCalculatedDateFrom = date('Y-m-d', min($existingDateFrom, $newDate));
- $newCalculatedDateTo = date('Y-m-d', max($existingDateTo, $newDate));
-
- // Проверяем, изменились ли даты
- if ($existingRecord->date_from !== $newCalculatedDateFrom ||
- $existingRecord->date_to !== $newCalculatedDateTo) {
- // Расширяем интервал только если данные изменились
- $existingRecord->date_from = $newCalculatedDateFrom;
- $existingRecord->date_to = $newCalculatedDateTo;
- $existingRecord->updated_at = $row['updated_at'];
-
- if ($existingRecord->validate()) {
- $existingRecord->save();
- }
- }
-
- $recordUpdated = true;
- break; // Нашли подходящий интервал, выходим из цикла
- }
- }
- }
-
- // Если не удалось обновить ни одну запись - создаем новую
- if (!$recordUpdated) {
- $newRecord = new SelfCostProductDynamic();
- $newRecord->product_guid = $row['product_guid'];
- $newRecord->store_id = $row['store_id'];
- $newRecord->price = $row['price'];
- $newRecord->date_from = $row['date'];
- $newRecord->date_to = $row['date'];
- $newRecord->updated_at = $row['updated_at'];
-
- if ($newRecord->validate()) {
- $newRecord->save();
- }
- }
- }
- }
-
- /**
- * Объединяет дублирующиеся записи с одинаковыми параметрами в единые интервалы
- * Используется для очистки дублей, созданных старой версией UpdateResult
- */
- public static function MergeDuplicates()
- {
- // Находим все уникальные комбинации product_guid, store_id, price
- $uniqueCombinations = SelfCostProductDynamic::find()
- ->select(['product_guid', 'store_id', 'price'])
- ->distinct()
- ->asArray()
- ->all();
-
- foreach ($uniqueCombinations as $combination) {
- // Получаем все записи для этой комбинации, отсортированные по дате
- $records = SelfCostProductDynamic::find()
- ->where([
- 'product_guid' => $combination['product_guid'],
- 'store_id' => $combination['store_id'],
- 'price' => $combination['price']
- ])
- ->orderBy(['date_from' => SORT_ASC])
- ->all();
-
- if (count($records) <= 1) {
- continue; // Нет дублей, переходим к следующей комбинации
- }
-
- // Объединяем интервалы
- $mergedIntervals = [];
- $currentInterval = null;
-
- foreach ($records as $record) {
- $dateFrom = strtotime($record->date_from);
- $dateTo = strtotime($record->date_to);
-
- if ($currentInterval === null) {
- // Первый интервал
- $currentInterval = [
- 'date_from' => $dateFrom,
- 'date_to' => $dateTo,
- 'record' => $record
- ];
- } else {
- // Проверяем, примыкает ли или пересекается ли с текущим интервалом
- $dayAfterCurrentEnd = strtotime('+1 day', $currentInterval['date_to']);
-
- if ($dateFrom <= $dayAfterCurrentEnd) {
- // Интервалы примыкают или пересекаются - объединяем
- $currentInterval['date_to'] = max($currentInterval['date_to'], $dateTo);
- } else {
- // Интервалы не примыкают - сохраняем текущий и начинаем новый
- $mergedIntervals[] = $currentInterval;
- $currentInterval = [
- 'date_from' => $dateFrom,
- 'date_to' => $dateTo,
- 'record' => $record
- ];
- }
- }
- }
-
- // Добавляем последний интервал
- if ($currentInterval !== null) {
- $mergedIntervals[] = $currentInterval;
- }
-
- // Обновляем базу данных: оставляем только объединенные интервалы
- // Первый интервал обновляем, остальные записи удаляем
- $keepIds = [];
- foreach ($mergedIntervals as $interval) {
- $record = $interval['record'];
- $record->date_from = date('Y-m-d', $interval['date_from']);
- $record->date_to = date('Y-m-d', $interval['date_to']);
- $record->updated_at = date('Y-m-d H:i:s');
-
- if ($record->validate()) {
- $record->save();
- }
- $keepIds[] = $record->id;
- }
-
- // Удаляем все остальные записи для этой комбинации
- SelfCostProductDynamic::deleteAll([
- 'and',
- [
- 'product_guid' => $combination['product_guid'],
- 'store_id' => $combination['store_id'],
- 'price' => $combination['price']
- ],
- ['not in', 'id', $keepIds]
- ]);
- }
- }
-
-
-
-}
--- /dev/null
+<?php
+
+namespace yii_app\services;
+
+use yii\db\Expression;
+use yii_app\records\SelfCostProductDynamic;
+
+class SelfCostProductDynamicService
+{
+
+ public static function PrepareResult($selfCostProduct):array
+ {
+ $result = [];
+
+ foreach ($selfCostProduct as $row) {
+ $dates = array_map('trim', explode(',', $row['dates']));
+ sort($dates); // сортировка дат на всякий случай
+
+ $start = null;
+ $prev = null;
+
+ foreach ($dates as $date) {
+ if ($start === null) {
+ // начало интервала
+ $start = $date;
+ $prev = $date;
+ } else {
+ // проверим, что дата идёт на следующий день за предыдущей
+ $expectedNext = date('Y-m-d', strtotime($prev . ' +1 day'));
+ if ($date === $expectedNext) {
+ // продолжаем интервал
+ $prev = $date;
+ } else {
+ // закрываем интервал
+ $result[] = [
+ 'product_guid' => $row['product_guid'],
+ 'store_id' => $row['store_id'],
+ 'price' => $row['price'],
+ 'date_from' => $start,
+ 'date_to' => $prev,
+ ];
+ // новый интервал
+ $start = $date;
+ $prev = $date;
+ }
+ }
+ }
+
+ // закрываем последний интервал
+ if ($start !== null) {
+ $result[] = [
+ 'product_guid' => $row['product_guid'],
+ 'store_id' => $row['store_id'],
+ 'price' => $row['price'],
+ 'date_from' => $start,
+ 'date_to' => $prev,
+ ];
+ }
+ }
+
+ // Вывод
+ return($result);
+ }
+
+ public static function SaveResult($selfCostProduct)
+ {
+ foreach ($selfCostProduct as $row) {
+ // Ищем ВСЕ существующие записи с теми же параметрами (product_guid, store_id, price)
+ $existingRecords = SelfCostProductDynamic::find()
+ ->where([
+ 'product_guid' => $row['product_guid'],
+ 'store_id' => $row['store_id'],
+ 'price' => $row['price']
+ ])
+ ->orderBy(['date_from' => SORT_ASC])
+ ->all();
+
+ $newDateFrom = strtotime($row['date_from']);
+ $newDateTo = strtotime($row['date_to']);
+ $recordUpdated = false;
+
+ if (!empty($existingRecords)) {
+ // Проверяем каждый существующий интервал
+ foreach ($existingRecords as $existingRecord) {
+ $existingDateFrom = strtotime($existingRecord->date_from);
+ $existingDateTo = strtotime($existingRecord->date_to);
+
+ // Проверяем, примыкают ли интервалы (разница <= 1 день) или пересекаются
+ $daysBetween = min(
+ abs($newDateFrom - $existingDateTo) / 86400,
+ abs($existingDateFrom - $newDateTo) / 86400
+ );
+
+ if ($daysBetween <= 1 ||
+ ($newDateFrom <= $existingDateTo && $newDateTo >= $existingDateFrom)) {
+ // Вычисляем новые границы интервала
+ $newCalculatedDateFrom = date('Y-m-d', min($existingDateFrom, $newDateFrom));
+ $newCalculatedDateTo = date('Y-m-d', max($existingDateTo, $newDateTo));
+
+ // Проверяем, изменились ли даты
+ if ($existingRecord->date_from !== $newCalculatedDateFrom ||
+ $existingRecord->date_to !== $newCalculatedDateTo) {
+ // Расширяем интервал только если данные изменились
+ $existingRecord->date_from = $newCalculatedDateFrom;
+ $existingRecord->date_to = $newCalculatedDateTo;
+ $existingRecord->updated_at = date('Y-m-d H:i:s');
+
+ if ($existingRecord->validate()) {
+ $existingRecord->save();
+ }
+ }
+
+ $recordUpdated = true;
+ break; // Нашли подходящий интервал, выходим из цикла
+ }
+ }
+ }
+
+ // Если не удалось обновить ни одну запись - создаем новую
+ if (!$recordUpdated) {
+ $rowSelfCost = new SelfCostProductDynamic();
+ $rowSelfCost->product_guid = $row['product_guid'];
+ $rowSelfCost->store_id = $row['store_id'];
+ $rowSelfCost->price = $row['price'];
+ $rowSelfCost->date_from = $row['date_from'];
+ $rowSelfCost->date_to = $row['date_to'];
+ $rowSelfCost->updated_at = date('Y-m-d H:i:s');
+
+ if ($rowSelfCost->validate()) {
+ $rowSelfCost->save();
+ }
+ }
+ }
+ }
+
+ public static function UpdateResult($values)
+ {
+ foreach ($values as $row) {
+ // Ищем ВСЕ существующие записи с теми же параметрами (product_guid, store_id, price)
+ $existingRecords = SelfCostProductDynamic::find()
+ ->where([
+ 'product_guid' => $row['product_guid'],
+ 'store_id' => $row['store_id'],
+ 'price' => $row['price']
+ ])
+ ->orderBy(['date_from' => SORT_ASC])
+ ->all();
+
+ $newDate = strtotime($row['date']);
+ $recordUpdated = false;
+
+ if (!empty($existingRecords)) {
+ // Проверяем каждый существующий интервал
+ foreach ($existingRecords as $existingRecord) {
+ $existingDateFrom = strtotime($existingRecord->date_from);
+ $existingDateTo = strtotime($existingRecord->date_to);
+
+ // Проверяем, примыкает ли новая дата (±1 день от границ интервала) или находится внутри
+ $dayBeforeDateFrom = strtotime('-1 day', $existingDateFrom);
+ $dayAfterDateTo = strtotime('+1 day', $existingDateTo);
+
+ if ($newDate >= $dayBeforeDateFrom && $newDate <= $dayAfterDateTo) {
+ // Новая дата примыкает к интервалу или находится внутри него
+ $newCalculatedDateFrom = date('Y-m-d', min($existingDateFrom, $newDate));
+ $newCalculatedDateTo = date('Y-m-d', max($existingDateTo, $newDate));
+
+ // Проверяем, изменились ли даты
+ if ($existingRecord->date_from !== $newCalculatedDateFrom ||
+ $existingRecord->date_to !== $newCalculatedDateTo) {
+ // Расширяем интервал только если данные изменились
+ $existingRecord->date_from = $newCalculatedDateFrom;
+ $existingRecord->date_to = $newCalculatedDateTo;
+ $existingRecord->updated_at = $row['updated_at'];
+
+ if ($existingRecord->validate()) {
+ $existingRecord->save();
+ }
+ }
+
+ $recordUpdated = true;
+ break; // Нашли подходящий интервал, выходим из цикла
+ }
+ }
+ }
+
+ // Если не удалось обновить ни одну запись - создаем новую
+ if (!$recordUpdated) {
+ $newRecord = new SelfCostProductDynamic();
+ $newRecord->product_guid = $row['product_guid'];
+ $newRecord->store_id = $row['store_id'];
+ $newRecord->price = $row['price'];
+ $newRecord->date_from = $row['date'];
+ $newRecord->date_to = $row['date'];
+ $newRecord->updated_at = $row['updated_at'];
+
+ if ($newRecord->validate()) {
+ $newRecord->save();
+ }
+ }
+ }
+ }
+
+ /**
+ * Объединяет дублирующиеся записи с одинаковыми параметрами в единые интервалы
+ * Используется для очистки дублей, созданных старой версией UpdateResult
+ */
+ public static function MergeDuplicates()
+ {
+ // Находим все уникальные комбинации product_guid, store_id, price
+ $uniqueCombinations = SelfCostProductDynamic::find()
+ ->select(['product_guid', 'store_id', 'price'])
+ ->distinct()
+ ->asArray()
+ ->all();
+
+ foreach ($uniqueCombinations as $combination) {
+ // Получаем все записи для этой комбинации, отсортированные по дате
+ $records = SelfCostProductDynamic::find()
+ ->where([
+ 'product_guid' => $combination['product_guid'],
+ 'store_id' => $combination['store_id'],
+ 'price' => $combination['price']
+ ])
+ ->orderBy(['date_from' => SORT_ASC])
+ ->all();
+
+ if (count($records) <= 1) {
+ continue; // Нет дублей, переходим к следующей комбинации
+ }
+
+ // Объединяем интервалы
+ $mergedIntervals = [];
+ $currentInterval = null;
+
+ foreach ($records as $record) {
+ $dateFrom = strtotime($record->date_from);
+ $dateTo = strtotime($record->date_to);
+
+ if ($currentInterval === null) {
+ // Первый интервал
+ $currentInterval = [
+ 'date_from' => $dateFrom,
+ 'date_to' => $dateTo,
+ 'record' => $record
+ ];
+ } else {
+ // Проверяем, примыкает ли или пересекается ли с текущим интервалом
+ $dayAfterCurrentEnd = strtotime('+1 day', $currentInterval['date_to']);
+
+ if ($dateFrom <= $dayAfterCurrentEnd) {
+ // Интервалы примыкают или пересекаются - объединяем
+ $currentInterval['date_to'] = max($currentInterval['date_to'], $dateTo);
+ } else {
+ // Интервалы не примыкают - сохраняем текущий и начинаем новый
+ $mergedIntervals[] = $currentInterval;
+ $currentInterval = [
+ 'date_from' => $dateFrom,
+ 'date_to' => $dateTo,
+ 'record' => $record
+ ];
+ }
+ }
+ }
+
+ // Добавляем последний интервал
+ if ($currentInterval !== null) {
+ $mergedIntervals[] = $currentInterval;
+ }
+
+ // Обновляем базу данных: оставляем только объединенные интервалы
+ // Первый интервал обновляем, остальные записи удаляем
+ $keepIds = [];
+ foreach ($mergedIntervals as $interval) {
+ $record = $interval['record'];
+ $record->date_from = date('Y-m-d', $interval['date_from']);
+ $record->date_to = date('Y-m-d', $interval['date_to']);
+ $record->updated_at = date('Y-m-d H:i:s');
+
+ if ($record->validate()) {
+ $record->save();
+ }
+ $keepIds[] = $record->id;
+ }
+
+ // Удаляем все остальные записи для этой комбинации
+ SelfCostProductDynamic::deleteAll([
+ 'and',
+ [
+ 'product_guid' => $combination['product_guid'],
+ 'store_id' => $combination['store_id'],
+ 'price' => $combination['price']
+ ],
+ ['not in', 'id', $keepIds]
+ ]);
+ }
+ }
+
+
+ /**
+ * Возвращает запись себестоимости по продукту/магазину на указанную дату.
+ */
+ public static function getPrice($productGuid, $storeId, $date)
+ {
+ $dateStr = is_int($date) ? date('Y-m-d', $date) : date('Y-m-d', strtotime($date));
+
+ return SelfCostProductDynamic::find()
+ ->andWhere(['product_guid' => $productGuid])
+ ->andWhere(['store_id' => $storeId])
+ ->andWhere(new Expression(":d BETWEEN date_from AND date_to", [':d' => $dateStr]))
+ ->orderBy(['date_from' => SORT_DESC])
+ ->one();
+ }
+}