Files
grzanieplus.pl/plugins/stPriceHistoryPlugin/lib/stPriceHistory.class.php
2025-03-12 17:06:23 +01:00

613 lines
27 KiB
PHP

<?php
/**
* Obsługa danych związanych z hiustorią cen produktów price-history
*
* @author Marek Jakubowicz m@sote.pl
*/
/**
* Zapis/odczyt danych z historii cen
*/
class stPriceHistory
{
/**
* @var $product obiekt reprezentujący dane ceny produktu stProduct (lub stProductPriceHistory)
*/
var $product;
/**
* Konstruktor
* @return true
*/
public function __construct($id = null)
{
if (!empty($id))
{
$this->product = $this->getProductPriceHistoryLast($id);
}
return true;
}
/**
* Ustaw typ zapisywania histroii cen
* @return string simple|full - typ zapisywania histroii cen wg konfiguracji
*/
public function getType()
{
// $product_config = stConfig::getInstance('stProduct');
// $price_history_type=$product_config->get('price_history_type');
// if ($price_history_type=='full') return 'full';
// else return 'simple';
// po zmianach w STX-1439 system zawsze zwaraca full
return 'full';
}
/**
* Ustaw info jak jest wykonywane zadania z Taska czy Save
* @param string $mode task, save
*/
public function setMode($mode)
{
$this->mode = $mode;
}
/**
* Odczytaj info jak jest wykonywane zadanie z Taska czy Save
* @param string $mode task, save, other
*/
public function getMode()
{
if (empty($this->mode)) return 'other';
return $this->mode;
}
/**
* Odczytaj domyślną walutę w sklepie
* @return int ID domyślnej waluty w sklepie
* @var $default_currency_id
*/
private function getShopDefaultCurrencyId()
{
if (!empty($this->default_currency_id)) return $this->default_currency_id;
$c = new Criteria();
$c->add(CurrencyPeer::MAIN, 1);
$currency = CurrencyPeer::doSelectOne($c);
$default_currency_id = $currency->getID();
$this->default_currency_id = $default_currency_id;
return $default_currency_id;
}
/**
* Odczytaj Id waluty produktu
* Waluta aktualna np. z formularza produktu. Uwaga! Może tej wartosci nie być przy wywołaniu webapi - wtedy wez walutę domyślną
* @return int ID waluty lub domyślna sklepu, jeśli wartość nie jest podana
*/
private function getProductCurrencyId()
{
$price_currency_id = $this->product->getCurrencyId();
if (empty($price_currency_id)) $price_currency_id = $this->getShopDefaultCurrencyId();
return $price_currency_id;
}
/**
* Sprawdz czy jest wykonywana edycja czy dodawanie produktu
* @return string 'add' - dodanie, 'edit' - edycja
*/
private function getProductAction()
{
if ($this->product->getID() > 0) return 'edit';
else return 'add';
}
/**
* Zwróć cenę w walucie.
* @return float cena w walucie
*/
private function getProductPriceBruttoCurrency()
{
// Jeśli product na walutę domyślną, to ustaw cenę brutto
// this_product->getCurrencyPrice() zwraca NULL jeśli nie był wybrana waluta inna niż domyślna
// price_brutto_currency ma zawierać cenę brutto w domyślnej walucie
$price_brutto_currency = $this->getFrontendPrice($this->product,$this->product->getCurrencyPrice(),'brutto');
if (empty($price_brutto_currency)) {
$price_brutto_currency = $this->getFrontendPrice($this->product,$this->product->getPriceBrutto(),'brutto');
}
return $price_brutto_currency;
}
/**
* Znajdź ostatni wpis z ceną produktu z stProductPriceHistory
* @return object stProductPriceHistory | NULL
*/
private function getLastProductPriceHistory()
{
$product_id = $this->product->getId();
$c = new Criteria();
$c->add(ProductPriceHistoryPeer::PRODUCT_ID, $product_id);
$c->addDescendingOrderByColumn(ProductPriceHistoryPeer::CREATED_AT);
$last_product = ProductPriceHistoryPeer::doSelectOne($c);
if (empty($last_product)) return NULL;
return $last_product;
}
/**
* Oczytaj rekord z danymi ostatniej ceny prodfuktu
* @return object stProductPriceHistoryLast | NULL
*/
private function getProductPriceHistoryLast()
{
$product_id = $this->product->getId();
$price_history_last = new stPriceHistoryLast();
if ($price_history_last->isLastProductPrice($product_id)) {
$product_last = $price_history_last->getLastProduct($product_id);
} else return NULL;
return $product_last;
}
/**
* Sprawdz jaka sytuacja jest dla danego produktu. Od tych danych zależą dalsze wykonywane operacje.
* change (pph_change):
* - true: zmieniła się cena produktu
* - false: nie było zmiany ceny
* update (pph_update):
* - true: aktualizuj ostatni rekord w historii ceny - dotyczy to kolejnej zmiany w tym samym dniu
* - false: dodaj nowy wpis, ostatnia zmiana nie była wykonywana dzisiaj
* @return array 'product' object stProductPriceHistory, 'change' bool, 'update' bool
*/
private function checkPriceHistoryChangeUpdate()
{
$price_history_last = new stPriceHistoryLast();
$price_currency_id = $this->getProductCurrencyId();
$price_brutto_currency = $this->getProductPriceBruttoCurrency();
$is_product_from_last = $price_history_last->isLastProductPrice($this->product->getId());
$product_price_history_last_record = $this->getLastProductPriceHistory();
if (!empty($is_product_from_last))
{
$pph_price_brutto_currency = $price_history_last->getLastProductPriceBrutto($this->product->getId());
$pph_currency = $price_history_last->getCurrencyId();
$pph_price_brutto_currency = floatval($pph_price_brutto_currency);
if (($pph_price_brutto_currency != $price_brutto_currency) || ($price_currency_id != $pph_currency)) {
// cena uległa zmianie
// $price_brutto_currency - nowa cena
// $pph_price_brutto_currency - cena produktu przed zmianą
// odczytaj cenę z wczoraj
$price_brutto_yesterday = $this->getPriceBruttoYesterday($this->product->getId(), $pph_price_brutto_currency);
if ($price_brutto_currency < $price_brutto_yesterday) {
// cena ulegla zmniejszeniu
$this->saveLowestPrice($this->product, NULL, 'down');
} elseif ($price_brutto_currency > $price_brutto_yesterday) {
// cena ulegla zwiększeniu
$this->saveLowestPrice($this->product, NULL, 'up');
}
// zapamietaj, ze cena sie zmienila
$pph_change = true;
// czy ostatni wpis jest z dzisiaj, odczytaj dane z st_product_price_history
if (! empty($product_price_history_last_record))
{
$pph_date = $product_price_history_last_record->getUpdatedAt();
} else $pph_date = null;
$today = date('Y-m-d') . " 00:00:00";
if (($pph_date >= $today) && ($pph_date != null)) {
// aktualizuj rekord osttani
$pph_update = true;
} else {
// dodaj nowy rekord
$pph_update = false;
}
} else {
$pph_change = false;
}
} else {
// Kiedy nie ma wpisu to dodaj nowy
$pph_change = true;
$pph_update = false;
}
return array(
'product'=> $product_price_history_last_record,
'change' => $pph_change,
'update' => $pph_update,
);
}
/**
* Dodaj wpis z aktualną ceną, przed zapisaniem nowej
* Jeśli produkt ma cenę np. 100 i dodajemy 150, to musimy zapisać jaka cena była wcześniej i jaka jest nowa cena
* cenę wcześniejszą zapisujemy z datą o jeden dzień wcześniej, chyba że produkt był dodany
* dzisiaj i ma wartość pola, created_at = today, wtedy nie dodajemy dodatkowego wpisu przez zmianą.
*
* 1. Pobieramy dane produktu z bazy
* 2. Sprawdzamy created_at
* 3. jesli created_at = today nic nie robimy
* 4. jesli created_at < today sprawdzamy czy możemy dodać nowy wpis
* ...
* @param object $last ostatni wpis produktu stProduct, stProductPriceHistory
* @return NULL;
*/
private function addDataToPriceHistory($last)
{
$price_history_last = new stPriceHistoryLast($this->product->getId());
$today = date("Y-m-d H:i:s");
$today_begin_time = date("Y-m-d 00:00:00"); // 00:00:00 oznacza początek dnia, ważne!
$yesterday = date('Y-m-d', strtotime('-1 day', strtotime(date('Y-m-d')))) . ' 00:00:00';
$day_before_yesterday = date('Y-m-d', strtotime('-2 day', strtotime(date('Y-m-d')))) .
' 23:59:59'; // 23:59:59 oznacza koniec dnia
// Sprawdź, czy $last został przekazany w wywołaniu. Parametr $last jest przekazywany przy zapisie produktu.
// Dotyczy sytuacji wywołania zapisania z cron, tam parametr $last nie jest ustawiony.
// $product_before - zawiera aktualne dane produktu
if (!empty($last)) {
$product_before = $last;
} else {
// odczytaj aktualne dane produktu z bazy
$c = new Criteria();
$c->add(ProductPeer::ID, $this->product->getId());
$product_before = ProductPeer::doSelectOne($c);
}
// Jeśli nie ma produktu w bazie nic nie wykonuj - security option.
if (empty($product_before)) return NULL;
$product_before_created_at = $product_before->getCreatedAt();
if (!empty($last)) {
// dla danych z (last) ProductPriceHistoryLast
// wywołaj inną metodę, gdyż cena w walucie jest przechowywana w innym polu w tym modelu
$price_brutto_currency_before = $product_before->getPrice();
} else {
// dla Product
$price_brutto_currency_before = $product_before->getFrontendCurrencyPrice();
}
// if (empty($price_brutto_currency_before))
// {
// $price_brutto_currency_before = $product_before->getFrontendCurrencyPrice();
// }
// Sprawedzamy czy cena wczesniejsza zmieniła się, jeśli nie, to nie dodajemy wpisu z dnia wcześniejszego
// Chodzi o uniknięcie sytuacji z STX-893 kiedy cena się nie zmienia i zapisywane sa 2 rekordy z ta sama cena
// Dodaj rekord "dzien przed" jesli produkt nie byl oddany dzisiaj, zmienila sie cena i nie ma wpisow dla danego produktu
// $product_price_before = $product_before->getPriceBrutto();
$product_price_before = $price_history_last->getLastProductPriceBrutto($this->product->getId());
if (! $product_price_before) $product_price_before = $product_before->getPriceBrutto();
$product_price_before=floatval($product_price_before);
$product_price_current = floatval($this->getFrontendPrice($this->product,$this->product->getPriceBrutto(),'brutto'));
if (($product_before_created_at < $today_begin_time) && ($product_price_before != $product_price_current)) {
// Sprawdz, czy w price_history jest juz wpis z dnia wczorajszego, jak jest to nie dodawaj ddoatkowego wpisu,
// gdyz oznacza to, ze wczoraj cena byla juz zapisana. Sprawdzamy datę > 2 dni temu i < dzisiaj.
$c = new Criteria();
$c->addAnd(ProductPriceHistoryPeer::PRODUCT_ID, $this->product->getId());
// $c->addAnd(ProductPriceHistoryPeer::CREATED_AT,$day_before_yesterday,Criteria::GREATER_THAN);
// $c->addAnd(ProductPriceHistoryPeer::CREATED_AT,$today,Criteria::LESS_THAN);
$criterion = $c->getNewCriterion(ProductPriceHistoryPeer::CREATED_AT, $day_before_yesterday, Criteria::GREATER_THAN);
$criterion->addAnd($c->getNewCriterion(ProductPriceHistoryPeer::CREATED_AT, $today_begin_time, Criteria::LESS_THAN));
$c->add($criterion);
$product_price_history_before_check = ProductPriceHistoryPeer::doSelectOne($c);
if (empty($product_price_history_before_check)) {
// Nie ma wpsiu z dnia wczorajszego, oznacza to ze zmiana ceny byla wczeniej i trzeba zapisac
// cenę poprzednia dzień wcześniej.
$product_price_history_before = new ProductPriceHistory();
$product_before_currency_id = $product_before->getCurrencyId();
if (empty($product_before_currency_id)) $product_before_currency_id = $this->product->getCurrencyId();
if (empty($product_before_currency_id)) $product_before_currency_id = $default_currency_id;
// Zapisanie poprzedniej ceny
// poprawka: te dane odczytaj z last
// $frontend_price_before = $this->getFrontendPrice($product_before, $price_brutto_currency_before, 'price');
// $frontend_price_netto_before = $this->getFrontendPrice($product_before, $product_before->getPriceNetto(), 'netto');
// $frontend_price_brutto_before = $this->getFrontendPrice($product_before, $product_before->getPriceBrutto(), 'brutto');
// $main_price_before = $product_before->getMainCurrencyPrice();
// $main_price_netto_before = $product_before->getMainPriceNetto();
// $main_price_brutto_before = $product_before->getMainPriceBrutto();
//
// $product_price_history_before->setProductId($this->product->getId());
// $product_price_history_before->setCreatedAt($yesterday);
// $product_price_history_before->setUpdatedAt($yesterday);
// $product_price_history_before->setCurrencyId($product_before_currency_id);
// $product_price_history_before->setPrice($frontend_price_before);
// $product_price_history_before->setPriceNetto($frontend_price_netto_before);
// $product_price_history_before->setPriceBrutto($frontend_price_brutto_before);
// $product_price_history_before->setMainPrice($main_price_before);
// $product_price_history_before->setMainPriceNetto($main_price_netto_before);
// $product_price_history_before->setMainPriceBrutto($main_price_brutto_before);
// $product_price_history_before->save();
$product_price_history_before->setProductId($this->product->getId());
$product_price_history_before->setCreatedAt($yesterday);
$product_price_history_before->setUpdatedAt($yesterday);
if (! empty($price_history_last->getLastProduct($this->product->getId())))
{
// jest wpis w st_product_price_history_last dla danego product_id
$product_price_history_before->setCurrencyId($price_history_last->getCurrencyId());
$product_price_history_before->setPrice($price_history_last->getPrice());
$product_price_history_before->setPriceNetto($price_history_last->getPriceNetto());
$product_price_history_before->setPriceBrutto($price_history_last->getPriceBrutto());
$product_price_history_before->setMainPrice($price_history_last->getMainPrice());
$product_price_history_before->setMainPriceNetto($price_history_last->getMainPriceNetto());
$product_price_history_before->setMainPriceBrutto($price_history_last->getMainPriceBrutto());
} else {
// nie ma wpisu w st_product_price_history_last dla danego product_id, wez dane dostarczone
$frontend_price_before = $this->getFrontendPrice($product_before, $price_brutto_currency_before, 'price');
$frontend_price_netto_before = $this->getFrontendPrice($product_before, $product_before->getPriceNetto(), 'netto');
$frontend_price_brutto_before = $this->getFrontendPrice($product_before, $product_before->getPriceBrutto(), 'brutto');
$main_price_before = $product_before->getMainCurrencyPrice();
$main_price_netto_before = $product_before->getMainPriceNetto();
$main_price_brutto_before = $product_before->getMainPriceBrutto();
$product_price_history_before->setCurrencyId($this->product->getCurrencyId());
$product_price_history_before->setPrice($frontend_price_before);
$product_price_history_before->setPriceNetto($frontend_price_netto_before);
$product_price_history_before->setPriceBrutto($frontend_price_brutto_before);
$product_price_history_before->setMainPrice($main_price_before);
$product_price_history_before->setMainPriceNetto($main_price_netto_before);
$product_price_history_before->setMainPriceBrutto($main_price_brutto_before);
}
$product_price_history_before->save();
}
// STX-1435 Poprawka zapisania ceny min, jesli nie bylo wpisow w st_product_price_history
if ($product_price_before > $product_price_current) {
// cena się zmniejszyła
$this->saveLowestPrice($this->product, NULL, 'down');
} else {
// cena się zwiększyła
$this->saveLowestPrice($this->product, NULL, 'up');
}
// end STX-1435
}
}
/**
* Zapisz cenę w historii cen produktu. Funkcja przeniesiona z modelu Product.php, gdzie była na początku zaimplementowana.
* Wywoływana jest przy zapisie produktu -> odwołanie w Product.php.
*
* @param object $this_product obiekt produktu $this z modelu Product. Zawiera nowe dane jakie będą zapisane.
* @param object $last obiekt produktu z modelu ProductPriceHistoryLast. Zawiera poprzednie wartości - ostatnio zapisane w cron.
*
* @return NULL
*/
public function modelUpdate($this_product, $last = NULL)
{
$this->product = $this_product;
// tonen - optyamlizacja ilości wykonań
$price_history_token = new stPriceHistoryToken($this->product);
$token = $price_history_token->getExecuteToken();
if (!$price_history_token->allowExecuteToken($token)) return NULL;
// add czy edit
if ($this->getProductAction() == 'add') return NULL;
// currency data
$default_currency_id = $this->getShopDefaultCurrencyId();
$price_currency_id = $this->getProductCurrencyId();
$price_brutto_currency = $this->getProductPriceBruttoCurrency();
// analiza zapisanych danych
$price_history_analyse = $this->checkPriceHistoryChangeUpdate();
$product_price_history_last_record = $price_history_analyse['product'];
$pph_change = $price_history_analyse['change'];
$pph_update = $price_history_analyse['update'];
$mode = $this->getMode(); // mode: save, task, other
$type = $this->gettype(); // type: simple, full (dla nowych wersji jest tylko 'full')
if (!$pph_change) {
// nie było zmiany ceny
$price_history_token->saveExecuteToken($token);
return NULL;
}
if ($pph_update) {
// zaktualizuj istniejący wpis z dzisiaj
$product_price_history = $product_price_history_last_record;
} else {
// dodaj nowy wpis do st_product_price_history
$this->addDataToPriceHistory($last);
$product_price_history = new ProductPriceHistory();
}
// STX-1439
// Zapisz dane w st_product_price_history_last przy zapisie produktu - ostatnia cena
if ($this->getMode() == 'save') {
$price_history_last = new stPriceHistoryLast();
$price_history_last->setDefaultCurrency($default_currency_id);
// Sprawdzamy, czy jest juz wpis w st_prodcu_price_history_last (last)
if ($price_history_last->isLastProductPrice($this->product->getId())) {
// Jest wpis w st_product_history_last dla danego id, aktualizuj dane.
$price_history_last->updateLastProductPrice($this->product);
} else {
// Nie ma rekordu w st_product_history_last dla danego id, dodaj wpis.
$price_history_last->addLastProductPrice($this->product);
}
}
// Dodaj/aktualizuj cenę dzisiejszą
$frontend_price = $this->getFrontendPrice($this_product, $price_brutto_currency, 'price');
$frontend_price_netto = $this->getFrontendPrice($this_product, $this_product->getPriceNetto(), 'netto');
$frontend_price_brutto = $this->getFrontendPrice($this_product, $this_product->getPriceBrutto(), 'brutto');
$main_price = $this_product->getMainCurrencyPrice();
$main_price_netto = $this_product->getMainPriceNetto();
$main_price_brutto = $this_product->getMainPriceBrutto();
$product_price_history->setProductId($this_product->getId());
$product_price_history->setCurrencyId($price_currency_id);
$product_price_history->setPrice($frontend_price);
$product_price_history->setPriceNetto($frontend_price_netto);
$product_price_history->setPriceBrutto($frontend_price_brutto);
$product_price_history->setMainPrice($main_price);
$product_price_history->setMainPriceNetto($main_price_netto);
$product_price_history->setMainPriceBrutto($main_price_brutto);
$product_price_history->save();
$price_history_token->saveExecuteToken($token);
return NULL;
}
/**
* Zapisz zajniższą cenę produktu z ostatanich 30 dni, po obniżce. Metoda jest wywoływana, kiedy aktualna cena jest obniżona.
*
* @param object $this_product obiekt produktu $this z modelu Product. Zawiera nowe dane jakie będą zapisane.
* @param integer $id product id
* @param string $change down - cena spadła, up - cena wzrosła
*
* @return NULL
*/
public function saveLowestPrice($this_product, $product_id = NULL, $change)
{
// Wyszukaj najnizsza cene produktu z ostatnich 30 dni
// Dane nowe nie są jeszzcze zapisane, więc w st_price_history najniższa cena z ostatanich 30 dni będzie tą, której szukamy.
// data -30 dni
$day_before_30d = date('Y-m-d', strtotime('-30 day', strtotime(date('Y-m-d')))) . ' 00:00:00';
$today = date("Y-m-d 00:00:00");
if (empty($product_id)) $product_id = $this_product->getID(); // Id produktu
// 1. Wyszukaj wpisy dla danego produktu z ostatnich 30 dni
$c = new Criteria();
$c->addAnd(ProductPriceHistoryPeer::PRODUCT_ID, $product_id);
$c->addAnd(ProductPriceHistoryPeer::CREATED_AT, $day_before_30d, Criteria::GREATER_EQUAL);
$c->addAnd(ProductPriceHistoryPeer::CREATED_AT, $today, Criteria::LESS_THAN);
$c->addDescendingOrderByColumn(ProductPriceHistoryPeer::CREATED_AT);
$products = ProductPriceHistoryPeer::doSelect($c);
if (empty($products)) return NULL; // nie ma wpisów w st_price_history dla danego produktu
$min_price = NULL;
foreach ($products as $product_price_history) {
$pph_id = $product_price_history->getId();
$pph_price_brutto = $product_price_history->getPriceBrutto();
if (empty($min_price)) {
// na początku przyjmij pierwszą odczytaną wartość, jako najmniejszą.
$min_price = $pph_price_brutto;
$product_price_history_min = $product_price_history; // zapamiętaj obiekt z danymi najmniejszej ceny
} elseif ($pph_price_brutto < $min_price) {
// Jesli cena jest mnniejsza od poprzedniej zapamietanej, staje się ona najmniejszą.
$min_price = $pph_price_brutto;
$product_price_history_min = $product_price_history; // zapamiętaj obiekt z danymi najmniejszej ceny
}
}
// $min_price zawiera najnizsza cene produktu z ostatnich 30 dni
// $product_price_history_min zawiera obiekt product_price_history z najniższą ceną
// Zapisz najnizsza cene w tebeli st_product_price_history_min
$price_history_min = new stPriceHistoryMin($product_id);
// czy jest wpis w st_product_price_history_min dla id produktu
if ($price_history_min->isMinProductPrice()) {
// jest już wpis, akulizuj
$price_history_min->updateMinProductPrice($product_price_history_min, $change);
} else {
// nie ma wpisu
$price_history_min->addMinProductPrice($product_price_history_min, $change);
}
return NULL;
}
/**
* Zwraca cenę produktu z wczoraj. Sprawdza historię cen i aktualną cenę.
*
* @param integer $product_id - ID produktu
* @param float $price brutto - cena produktu przed zmianą
* @return float cena brutto - do porównania brana jest cena brutto
*
*/
public function getPriceBruttoYesterday($product_id, $price_brutto)
{
// odczytaj cenę produktu jaka obowiązywała wczoraj
// sprawdz czy sa wpisy w st_product_price_history
$today = date('Y-m-d') . " 00:00:00";
$day_before_30d = date('Y-m-d', strtotime('-30 day', strtotime(date('Y-m-d')))) . ' 00:00:00';
$c = new Criteria();
$c->addAnd(ProductPriceHistoryPeer::PRODUCT_ID, $product_id);
$c->addAnd(ProductPriceHistoryPeer::CREATED_AT, $today, Criteria::LESS_THAN);
$c->addAnd(ProductPriceHistoryPeer::CREATED_AT, $day_before_30d, Criteria::GREATER_EQUAL);
$c->addDescendingOrderByColumn(ProductPriceHistoryPeer::CREATED_AT);
$pph_product = ProductPriceHistoryPeer::doSelectOne($c);
if (!empty($pph_product)) {
// jest wpis w st_product_price_history
// pstatni wpis poza dzniem dzisiejszym, reprezentuje ostatnio zapisaną cenę produktu, czyli cenę, która obowiązywała wczoraj
$pph_price_brutto = $pph_product->getPriceBrutto();
return $pph_price_brutto;
} else {
// nie ma wpisu w st_product_price_history, zwróć aktualną cenę
return $price_brutto;
}
return NULL;
}
/**
* Odczytaj cenę po rabacie dla danego produktu STX-1439
*
* @param object obiekt produktu z tabeli st_product (nie z historii cen)
* @param float $price cena do rabatowania
* @param string $mode price, netto, brutto
* @return float cena po rabacie
*/
public function getFrontendPrice($product, $price, $mode)
{
switch ($mode) {
case "netto":
$price = $product->getFrontendPriceNetto();
break;
case "brutto":
$price = $product->getFrontendPriceBrutto();
break;
case "price":
$price = $product->getFrontendCurrencyPrice();
break;
}
return $price;
}
}