public function calcBalances($currentBalances, $transactions) { $result = []; foreach ($transactions as $one) { $accDebit = $one[Transaction::ATTR_DEBIT_ACC_ID]; $accCredit = $one[Transaction::ATTR_CREDIT_ACC_ID]; $timestamp = $one[Transaction::ATTR_DATE_APPLIED]; $date = $this->_toolPeriod->getPeriodCurrent($timestamp, IPeriod::TYPE_DAY); $changeValue = $one[Transaction::ATTR_VALUE]; /** * process debit account */ /* get calculated balance on the date*/ if (isset($result[$accDebit][$date])) { /* there is data for this account on this date */ $data = $result[$accDebit][$date]; } else { /* there is NO data for this account on this date */ $data = [Balance::ATTR_ACCOUNT_ID => $accDebit, Balance::ATTR_DATE => $date, Balance::ATTR_BALANCE_OPEN => 0, Balance::ATTR_TOTAL_DEBIT => 0, Balance::ATTR_TOTAL_CREDIT => 0, Balance::ATTR_BALANCE_CLOSE => 0]; /* we need to update opening balance */ if (isset($result[$accDebit])) { $last = end($result[$accDebit]); $data[Balance::ATTR_BALANCE_OPEN] = $last[Balance::ATTR_BALANCE_CLOSE]; } elseif (isset($currentBalances[$accDebit])) { $data[Balance::ATTR_BALANCE_OPEN] = $currentBalances[$accDebit][Balance::ATTR_BALANCE_CLOSE]; } } /* change debit related values */ $data[Balance::ATTR_TOTAL_DEBIT] += $changeValue; $data[Balance::ATTR_BALANCE_CLOSE] -= $changeValue; $result[$accDebit][$date] = $data; /** * process credit account */ /* get calculated balance on the date*/ if (isset($result[$accCredit][$date])) { /* there is data for this account on this date */ $data = $result[$accCredit][$date]; } else { /* there is NO data for this account on this date */ $data = [Balance::ATTR_ACCOUNT_ID => $accCredit, Balance::ATTR_DATE => $date, Balance::ATTR_BALANCE_OPEN => 0, Balance::ATTR_TOTAL_DEBIT => 0, Balance::ATTR_TOTAL_CREDIT => 0, Balance::ATTR_BALANCE_CLOSE => 0]; /* we need to update opening balance */ if (isset($result[$accCredit])) { $last = end($result[$accCredit]); $data[Balance::ATTR_BALANCE_OPEN] = $last[Balance::ATTR_BALANCE_CLOSE]; } elseif (isset($currentBalances[$accCredit])) { $data[Balance::ATTR_BALANCE_OPEN] = $currentBalances[$accCredit][Balance::ATTR_BALANCE_CLOSE]; } } /* change credit related values */ $data[Balance::ATTR_TOTAL_CREDIT] += $changeValue; $data[Balance::ATTR_BALANCE_CLOSE] += $changeValue; $result[$accCredit][$date] = $data; } return $result; }
/** * @param Request\GetForWriteOff $request * * @return Response\GetForWriteOff */ public function getForWriteOff(Request\GetForWriteOff $request) { $result = new Response\GetForWriteOff(); $this->_logger->info("'Get latest period for Write Off calculation' operation is started."); /* get the last Write Off period data */ $calcWriteOffCode = Cfg::CODE_TYPE_CALC_PV_WRITE_OFF; $calcWriteOffId = $this->_subDb->getCalcIdByCode($calcWriteOffCode); $respWriteOffLastPeriod = $this->_subDb->getLastPeriodData($calcWriteOffId); $periodWriteOffData = $respWriteOffLastPeriod->getPeriodData(); if (is_null($periodWriteOffData)) { $this->_logger->info("There is no period for PV Write Off calculation yet."); /* calc period for PV Write Off */ $tsFirstPv = $this->_subDb->getFirstDateForPvTransactions(); if ($tsFirstPv === false) { $this->_logger->info("There is no PV transactions yet. Nothing to do."); $result->setHasNoPvTransactionsYet(); } else { $this->_logger->info("First PV transaction was performed at '{$tsFirstPv}'."); $periodMonth = $this->_toolPeriod->getPeriodCurrent($tsFirstPv, ToolPeriod::TYPE_MONTH); $dsBegin = $this->_toolPeriod->getPeriodFirstDate($periodMonth); $dsEnd = $this->_toolPeriod->getPeriodLastDate($periodMonth); $periodWriteOffData = $this->_subDb->addNewPeriodAndCalc($calcWriteOffId, $dsBegin, $dsEnd); $result->setPeriodData($periodWriteOffData->getData(Sub\Db::DATA_PERIOD)); $result->setCalcData($periodWriteOffData->getData(Sub\Db::DATA_CALC)); $result->markSucceed(); } } else { $result->setPeriodData($periodWriteOffData); $periodId = $periodWriteOffData->getId(); $this->_logger->info("There is registered period #{$periodId} for '{$calcWriteOffCode}' calculation."); $calcData = $respWriteOffLastPeriod->getCalcData(); if ($calcData === false) { $this->_logger->info("There is no calculation data for existing period. Use existing period data."); $result->markSucceed(); } else { if ($calcData && $calcData->getState() == Cfg::CALC_STATE_COMPLETE) { $this->_logger->info("There is complete calculation for existing period. Create new period."); $periodEnd = $periodWriteOffData->getDstampEnd(); /* calculate new period bounds */ $periodNext = $this->_toolPeriod->getPeriodNext($periodEnd, ToolPeriod::TYPE_MONTH); $dsNextBegin = $this->_toolPeriod->getPeriodFirstDate($periodNext); $dsNextEnd = $this->_toolPeriod->getPeriodLastDate($periodNext); $periodWriteOffData = $this->_subDb->addNewPeriodAndCalc($calcWriteOffId, $dsNextBegin, $dsNextEnd); $result->setPeriodData($periodWriteOffData->getData(Sub\Db::DATA_PERIOD)); $result->setCalcData($periodWriteOffData->getData(Sub\Db::DATA_CALC)); $result->markSucceed(); } else { $this->_logger->info("There is no complete calculation for existing period. Use existing period data."); $result->setCalcData($calcData); $result->markSucceed(); } } } $this->_logger->info("'Get latest period for Write Off calculation' operation is completed."); return $result; }
/** * @param string $dateBegin datestamp (YYYYMMDD) for the date when the first customer should be created. * @param bool $switchDateOnNewCustomer 'true' - create customers day by day, 'false' - create all customers * in one day. */ protected function _createDownlineCustomers($dateBegin = self::DATE_PERIOD_BEGIN, $switchDateOnNewCustomer = true) { $dtToday = $dateBegin; foreach ($this->DEFAULT_DWNL_TREE as $customerRef => $parentRef) { $customerMageId = $this->_mapCustomerMageIdByIndex[$customerRef]; /* get magento customer data */ $request = new CustomerAddRequest(); $request->setCustomerId($customerMageId); $request->setParentId($this->_mapCustomerMageIdByIndex[$parentRef]); $request->setReference($this->_mapCustomerMageIdByIndex[$customerRef]); $request->setCountryCode(self::DEFAULT_DOWNLINE_COUNTRY_CODE); $request->setDate($this->_toolPeriod->getTimestampFrom($dtToday)); /* Create customer per day or all customers in the same day. */ if ($switchDateOnNewCustomer) { $dtToday = $this->_toolPeriod->getPeriodNext($dtToday); } $response = $this->_callDownlineCustomer->add($request); if ($response->isSucceed()) { $path = $response->getData(Customer::ATTR_PATH); $depth = $response->getData(Customer::ATTR_DEPTH); $this->_logger->debug("New customer #{$customerMageId} is added to path '{$path}' on depth {$depth} at '{$dtToday}'."); } else { $this->_logger->error("Cannot add new customer #{$customerMageId} to downline tree."); } } }
public function getLastDate(Request\GetLastDate $request) { $result = new Response\GetLastDate(); $assetTypeId = $request->getAssetTypeId(); $assetTypeCode = $request->getAssetTypeCode(); if (is_null($assetTypeId)) { $assetTypeId = $this->_repoTypeAsset->getIdByCode($assetTypeCode); } /* get the maximal date for balance */ $balanceMaxDate = $this->_repoMod->getBalanceMaxDate($assetTypeId); if ($balanceMaxDate) { /* there is balance data */ $dayBefore = $this->_toolPeriod->getPeriodPrev($balanceMaxDate, IPeriod::TYPE_DAY); $result->setData([Response\GetLastDate::LAST_DATE => $dayBefore]); $result->markSucceed(); } else { /* there is no balance data yet, get transaction with minimal date */ $transactionMinDate = $this->_repoMod->getTransactionMinDateApplied($assetTypeId); if ($transactionMinDate) { $period = $this->_toolPeriod->getPeriodCurrent($transactionMinDate); $dayBefore = $this->_toolPeriod->getPeriodPrev($period, IPeriod::TYPE_DAY); $result->setData([Response\GetLastDate::LAST_DATE => $dayBefore]); $result->markSucceed(); } } return $result; }
/** * * Get PV related period data if no period yet exist. * * @param \Praxigento\BonusBase\Service\Period\Response\GetForPvBasedCalc $result * @param string $periodType * @param int $calcTypeId * @return \Praxigento\BonusBase\Service\Period\Response\GetForPvBasedCalc */ public function getNewPeriodDataForPv(\Praxigento\BonusBase\Service\Period\Response\GetForPvBasedCalc $result, $periodType, $calcTypeId) { /* we should lookup for first PV transaction and calculate first period range */ $firstDate = $this->_repoService->getFirstDateForPvTransactions(); if ($firstDate === false) { $this->_logger->warning("There is no PV transactions yet. Nothing to do."); $result->setErrorCode($result::ERR_HAS_NO_PV_TRANSACTIONS_YET); } else { $this->_logger->info("First PV transaction was performed at '{$firstDate}'."); $periodMonth = $this->_toolPeriod->getPeriodCurrent($firstDate, $periodType); $dsBegin = $this->_toolPeriod->getPeriodFirstDate($periodMonth); $dsEnd = $this->_toolPeriod->getPeriodLastDate($periodMonth); /* create new period for given calculation type */ $period = new EPeriod(); $period->setCalcTypeId($calcTypeId); $period->setDstampBegin($dsBegin); $period->setDstampEnd($dsEnd); $periodId = $this->_repoPeriod->create($period); $period->setId($periodId); /* create related calculation */ $calc = new ECalculation(); $calc->setPeriodId($periodId); $dateStarted = $this->_toolDate->getUtcNowForDb(); $calc->setDateStarted($dateStarted); $calc->setState(Cfg::CALC_STATE_STARTED); $calcId = $this->_repoCalc->create($calc); $calc->setId($calcId); /* place newly created objects into the response */ $result->setPeriodData($period); $result->setCalcData($calc); } return $result; }
/** * SELECT * SUM(pps.total) * FROM `prxgt_pv_sale` AS `pps` * WHERE (pps.date_paid >= '2016-01-01 08:00:00') * AND (pps.date_paid <= '2017-01-01 07:59:59') * * @param string $dsFrom * @param string $dsTo */ function getSalesOrdersPvForPeriod($dsFrom, $dsTo) { $tsFrom = $this->_toolPeriod->getTimestampFrom($dsFrom); $tsTo = $this->_toolPeriod->getTimestampTo($dsTo); /* aliases and tables */ $asSummary = 'summary'; $asPv = 'pps'; $tblPv = $this->_resource->getTableName(PvSale::ENTITY_NAME); // SELECT FROM prxgt_pv_sale pps $query = $this->_conn->select(); $query->from([$asPv => $tblPv], [$asSummary => 'SUM(' . PvSale::ATTR_TOTAL . ')']); // where $whereFrom = $asPv . '.' . PvSale::ATTR_DATE_PAID . '>=' . $this->_conn->quote($tsFrom); $whereTo = $asPv . '.' . PvSale::ATTR_DATE_PAID . '<=' . $this->_conn->quote($tsTo); $query->where("{$whereFrom} AND {$whereTo}"); // $sql = (string)$query; $result = $this->_conn->fetchOne($query); return $result; }
/** * @param Request\PvWriteOff $request * * @return Response\PvWriteOff */ public function pvWriteOff(Request\PvWriteOff $request) { $result = new Response\PvWriteOff(); $datePerformed = $request->getDatePerformed(); $this->_logger->info("'PV Write Off' calculation is started."); $reqGetPeriod = new PeriodGetForWriteOffRequest(); $respGetPeriod = $this->_callPeriod->getForWriteOff($reqGetPeriod); if ($respGetPeriod->isSucceed()) { if ($respGetPeriod->hasNoPvTransactionsYet()) { $this->_logger->info("There is no PV transactions yet. Nothing to calculate."); $result->markSucceed(); } else { $def = $this->_manTrans->begin(); try { /* working vars */ $periodData = $respGetPeriod->getPeriodData(); $periodId = $periodData[Period::ATTR_ID]; $calcData = $respGetPeriod->getCalcData(); $calcId = $calcData[Calculation::ATTR_ID]; $periodBegin = $periodData[Period::ATTR_DSTAMP_BEGIN]; $periodEnd = $periodData[Period::ATTR_DSTAMP_END]; $this->_logger->info("Processing period #{$periodId} ({$periodBegin}-{$periodEnd}), calculation #{$calcId}."); $transData = $this->_subDb->getDataForWriteOff($calcId, $periodBegin, $periodEnd); $updates = $this->_subCalc->pvWriteOff($transData); $dateApplied = $this->_toolPeriod->getTimestampTo($periodEnd); $operId = $this->_subDb->saveOperationPvWriteOff($updates, $datePerformed, $dateApplied); $this->_subDb->saveLogPvWriteOff($transData, $operId, $calcId); $this->_subDb->markCalcComplete($calcId); $this->_manTrans->commit($def); $result->setPeriodId($periodId); $result->setCalcId($calcId); $result->markSucceed(); } finally { $this->_manTrans->end($def); } } } $this->_logMemoryUsage(); $this->_logger->info("'PV Write Off' calculation is completed."); return $result; }
/** * SELECT * pps.sale_id, * pps.date_paid, * sfo.base_grand_total, * ce.entity_id * FROM prxgt_pv_sale pps * LEFT JOIN sales_flat_order sfo * ON pps.sale_id = sfo.entity_id * LEFT JOIN customer_entity ce * ON sfo.customer_id = ce.entity_id * WHERE pps.date_paid >= '2016-01-01 00:00:00' * AND pps.date_paid <= '2016-01-31 23:59:59' * * @param $dsBegin - '20160101' * @param $dsEnd - '20160131' * * @return array [ $custId => [$orderId=>[$amount], ... ], ... ] */ public function getSaleOrdersForRebate($dsBegin, $dsEnd) { $result = []; /* aliases and tables */ $asPvSale = 'pps'; $asMageSale = 'sfo'; $asMageCust = 'ce'; $tblPvSale = $this->_resource->getTableName(PvSale::ENTITY_NAME); $tblMageSale = $this->_resource->getTableName(Cfg::ENTITY_MAGE_SALES_ORDER); $tblMageCust = $this->_resource->getTableName(Cfg::ENTITY_MAGE_CUSTOMER); // FROM prxgt_pv_sale pps $query = $this->_conn->select(); $cols = [PvSale::ATTR_SALE_ID, PvSale::ATTR_DATE_PAID]; $query->from([$asPvSale => $tblPvSale], $cols); // LEFT JOIN sales_flat_order sfo ON pps.sale_id = sfo.entity_id $on = "{$asPvSale}." . PvSale::ATTR_SALE_ID . "={$asMageSale}." . Cfg::E_COMMON_A_ENTITY_ID; $cols = [Cfg::E_SALE_ORDER_A_BASE_GRAND_TOTAL]; $query->joinLeft([$asMageSale => $tblMageSale], $on, $cols); // LEFT JOIN customer_entity ce ON sfo.customer_id = ce.entity_id $on = "{$asMageSale}." . Cfg::E_SALE_ORDER_A_CUSTOMER_ID . "={$asMageCust}." . Cfg::E_CUSTOMER_A_ENTITY_ID; $cols = [Cfg::E_CUSTOMER_A_ENTITY_ID]; $query->joinLeft([$asMageCust => $tblMageCust], $on, $cols); // where $from = $this->_toolPeriod->getTimestampFrom($dsBegin); $to = $this->_toolPeriod->getTimestampTo($dsEnd); $whereFrom = PvSale::ATTR_DATE_PAID . '>=' . $this->_conn->quote($from); $whereTo = PvSale::ATTR_DATE_PAID . '<=' . $this->_conn->quote($to); $wherePv = PvSale::ATTR_TOTAL . ">0"; $query->where("{$whereFrom} AND {$whereTo} AND {$wherePv}"); // $sql = (string)$query; $data = $this->_conn->fetchAll($query); foreach ($data as $item) { $custId = $item[Cfg::E_CUSTOMER_A_ENTITY_ID]; $saleId = $item[PvSale::ATTR_SALE_ID]; $amount = $item[Cfg::E_SALE_ORDER_A_BASE_GRAND_TOTAL]; $result[$custId][$saleId] = $amount; } return $result; }
/** * SELECT * `pps`.`sale_id`, * `sfo`.`customer_id` * FROM `prxgt_pv_sale` AS `pps` * LEFT JOIN `sales_flat_order` AS `sfo` * ON pps.sale_id = sfo.entity_id * WHERE (pps.date_paid >= '2016-01-01 08:00:00' * AND pps.date_paid <= '2017-01-01 07:59:59') * * @param string $dsFrom * @param string $dsTo * * @return array */ function getSalesOrdersForPeriod($dsFrom, $dsTo) { $tsFrom = $this->_toolPeriod->getTimestampFrom($dsFrom); $tsTo = $this->_toolPeriod->getTimestampTo($dsTo); /* aliases and tables */ $asPv = 'pps'; $asOrder = 'sfo'; $tblPv = $this->_resource->getTableName(PvSale::ENTITY_NAME); $tblOrder = $this->_resource->getTableName(Cfg::ENTITY_MAGE_SALES_ORDER); // SELECT FROM prxgt_pv_sale pps $query = $this->_conn->select(); $query->from([$asPv => $tblPv], [PvSale::ATTR_SALE_ID, PvSale::ATTR_TOTAL]); // LEFT JOIN sales_flat_order sfo ON pps.sale_id = sfo.entity_id $on = "{$asPv}." . PvSale::ATTR_SALE_ID . "={$asOrder}." . Cfg::E_SALE_ORDER_A_ENTITY_ID; $cols = [Cfg::E_SALE_ORDER_A_CUSTOMER_ID]; $query->joinLeft([$asOrder => $tblOrder], $on, $cols); // where $whereFrom = $asPv . '.' . PvSale::ATTR_DATE_PAID . '>=' . $this->_conn->quote($tsFrom); $whereTo = $asPv . '.' . PvSale::ATTR_DATE_PAID . '<=' . $this->_conn->quote($tsTo); $query->where("{$whereFrom} AND {$whereTo}"); // $sql = (string)$query; $result = $this->_conn->fetchAll($query); return $result; }
/** * Calculate the last date for existing downline snap or the "yesterday" for the first change log entry. * * @param Request\GetLastDate $request * * @return Response\GetLastDate */ public function getLastDate(Request\GetLastDate $request) { $result = new Response\GetLastDate(); $this->_logger->info("'Get Last Data' operation is requested."); /* get the maximal date for existing snapshot */ $snapMaxDate = $this->_repoSnap->getMaxDatestamp(); if ($snapMaxDate) { /* there is snapshots data */ $result->setData([Response\GetLastDate::LAST_DATE => $snapMaxDate]); $result->markSucceed(); } else { /* there is no snapshot data yet, get change log minimal date */ $changelogMinDate = $this->_repoChange->getChangelogMinDate(); if ($changelogMinDate) { $period = $this->_toolPeriod->getPeriodCurrent($changelogMinDate); $dayBefore = $this->_toolPeriod->getPeriodPrev($period); $this->_logger->info("The last date for downline snapshot is '{$dayBefore}'."); $result->setData([Response\GetLastDate::LAST_DATE => $dayBefore]); $result->markSucceed(); } } $this->_logger->info("'Get Last Data' operation is completed."); return $result; }
/** * Calculate downline snapshots by date basing on the last snapshot and change log. * * We use $currentState array to trace actual state during the changes. Target updates are placed in the $result. * * @param $currentState * @param $changes * * @return array */ public function calcSnapshots($currentState, $changes) { $result = []; foreach ($changes as $downCustomer) { $customerId = $downCustomer[Change::ATTR_CUSTOMER_ID]; $parentId = $downCustomer[Change::ATTR_PARENT_ID]; $tsChanged = $downCustomer[Change::ATTR_DATE_CHANGED]; $dsChanged = $this->_toolPeriod->getPeriodCurrent($tsChanged); /* $currentState contains actual state that is updated with changes */ if (isset($currentState[$customerId])) { /* this is update of the existing customer */ /* write down existing state */ $currCustomer = $currentState[$customerId]; $currDepth = $currCustomer[Snap::ATTR_DEPTH]; $currPath = $currCustomer[Snap::ATTR_PATH]; /* write down new state */ if ($customerId == $parentId) { /* this is root node customer */ $newDepth = Cfg::INIT_DEPTH; $newPath = Cfg::DTPS; } else { /* this is NOT root node customer */ $newParent = $currentState[$parentId]; $newDepth = $newParent[Snap::ATTR_DEPTH] + 1; $newPath = $newParent[Snap::ATTR_PATH] . $parentId . Cfg::DTPS; } $customer = [Snap::ATTR_DATE => $dsChanged, Snap::ATTR_CUSTOMER_ID => $customerId, Snap::ATTR_PARENT_ID => $parentId, Snap::ATTR_DEPTH => $newDepth, Snap::ATTR_PATH => $newPath]; /* we need to update downline's depths & paths for changed customer */ /* TODO slow code, add ndx if too much slow */ $key = $currPath . $customerId . Cfg::DTPS; $depthDelta = $newDepth - $currDepth; $pathReplace = $newPath . $customerId . Cfg::DTPS; foreach ($currentState as $downCustomer) { $downPath = $downCustomer[Snap::ATTR_PATH]; if (false !== strrpos($downPath, $key, -strlen($downPath))) { /* this is customer from downlilne, we need to change depth & path */ $downCustId = $downCustomer[Snap::ATTR_CUSTOMER_ID]; $downParentId = $downCustomer[Snap::ATTR_PARENT_ID]; $downNewDepth = $downCustomer[Snap::ATTR_DEPTH] + $depthDelta; $downNewPath = str_replace($key, $pathReplace, $downCustomer[Snap::ATTR_PATH]); $downCustomer[Snap::ATTR_DEPTH] = $downNewDepth; $downCustomer[Snap::ATTR_PATH] = $downNewPath; /* add to result updates */ $result[$dsChanged][$downCustId] = [Snap::ATTR_DATE => $dsChanged, Snap::ATTR_CUSTOMER_ID => $downCustId, Snap::ATTR_PARENT_ID => $downParentId, Snap::ATTR_DEPTH => $downNewDepth, Snap::ATTR_PATH => $downNewPath]; } } } else { /* there is no data for this customer, this is new customer; just add new customer to results */ if ($customerId == $parentId) { /* this is root node customer */ $customer = [Snap::ATTR_DATE => $dsChanged, Snap::ATTR_CUSTOMER_ID => $customerId, Snap::ATTR_PARENT_ID => $customerId, Snap::ATTR_DEPTH => Cfg::INIT_DEPTH, Snap::ATTR_PATH => Cfg::DTPS]; } else { /* this is NOT root node customer */ $parent = $currentState[$parentId]; $customer = [Snap::ATTR_DATE => $dsChanged, Snap::ATTR_CUSTOMER_ID => $customerId, Snap::ATTR_PARENT_ID => $parentId, Snap::ATTR_DEPTH => $parent[Snap::ATTR_DEPTH] + 1, Snap::ATTR_PATH => $parent[Snap::ATTR_PATH] . $parentId . Cfg::DTPS]; } } $currentState[$customerId] = $customer; $result[$dsChanged][$customerId] = $customer; } return $result; }