Initial commit: apps.supersamsev.ru

This commit is contained in:
root committed 2026-10-21 23:46:31 +03:00
commit 54475e7d3d
17284 files changed
+98268

No files matched your search

+52
View File
@@ -0,0 +1,52 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.3.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App;
use Cake\Core\Configure;
use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Http\BaseApplication;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;
/**
* Application setup class.
*
* This defines the bootstrapping logic and middleware layers you
* want to use in your application.
*/
class Application extends BaseApplication
{
/**
* Setup the middleware queue your application will use.
*
* @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
* @return \Cake\Http\MiddlewareQueue The updated middleware queue.
*/
public function middleware($middlewareQueue)
{
$middlewareQueue
// Catch any exceptions in the lower layers,
// and make an error page/response
->add(ErrorHandlerMiddleware::class)
// Handle plugin/theme assets like CakePHP normally does.
->add(AssetMiddleware::class)
// Add routing middleware.
->add(new RoutingMiddleware($this));
return $middlewareQueue;
}
}
+246
View File
@@ -0,0 +1,246 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Console;
if (!defined('STDIN')) {
define('STDIN', fopen('php://stdin', 'r'));
}
use Cake\Utility\Security;
use Composer\Script\Event;
use Exception;
/**
* Provides installation hooks for when this application is installed via
* composer. Customize this class to suit your needs.
*/
class Installer
{
/**
* An array of directories to be made writable
*/
const WRITABLE_DIRS = [
'logs',
'tmp',
'tmp/cache',
'tmp/cache/models',
'tmp/cache/persistent',
'tmp/cache/views',
'tmp/sessions',
'tmp/tests'
];
/**
* Does some routine installation tasks so people don't have to.
*
* @param \Composer\Script\Event $event The composer event object.
* @throws \Exception Exception raised by validator.
* @return void
*/
public static function postInstall(Event $event)
{
$io = $event->getIO();
$rootDir = dirname(dirname(__DIR__));
static::createAppConfig($rootDir, $io);
static::createWritableDirectories($rootDir, $io);
// ask if the permissions should be changed
if ($io->isInteractive()) {
$validator = function ($arg) {
if (in_array($arg, ['Y', 'y', 'N', 'n'])) {
return $arg;
}
throw new Exception('This is not a valid answer. Please choose Y or n.');
};
$setFolderPermissions = $io->askAndValidate(
'<info>Set Folder Permissions ? (Default to Y)</info> [<comment>Y,n</comment>]? ',
$validator,
10,
'Y'
);
if (in_array($setFolderPermissions, ['Y', 'y'])) {
static::setFolderPermissions($rootDir, $io);
}
} else {
static::setFolderPermissions($rootDir, $io);
}
static::setSecuritySalt($rootDir, $io);
if (class_exists('\Cake\Codeception\Console\Installer')) {
\Cake\Codeception\Console\Installer::customizeCodeceptionBinary($event);
}
}
/**
* Create the config/app.php file if it does not exist.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function createAppConfig($dir, $io)
{
$appConfig = $dir . '/config/app.php';
$defaultConfig = $dir . '/config/app.default.php';
if (!file_exists($appConfig)) {
copy($defaultConfig, $appConfig);
$io->write('Created `config/app.php` file');
}
}
/**
* Create the `logs` and `tmp` directories.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function createWritableDirectories($dir, $io)
{
foreach (static::WRITABLE_DIRS as $path) {
$path = $dir . '/' . $path;
if (!file_exists($path)) {
mkdir($path);
$io->write('Created `' . $path . '` directory');
}
}
}
/**
* Set globally writable permissions on the "tmp" and "logs" directory.
*
* This is not the most secure default, but it gets people up and running quickly.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function setFolderPermissions($dir, $io)
{
// Change the permissions on a path and output the results.
$changePerms = function ($path, $perms, $io) {
// Get permission bits from stat(2) result.
$currentPerms = fileperms($path) & 0777;
if (($currentPerms & $perms) == $perms) {
return;
}
$res = chmod($path, $currentPerms | $perms);
if ($res) {
$io->write('Permissions set on ' . $path);
} else {
$io->write('Failed to set permissions on ' . $path);
}
};
$walker = function ($dir, $perms, $io) use (&$walker, $changePerms) {
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = $dir . '/' . $file;
if (!is_dir($path)) {
continue;
}
$changePerms($path, $perms, $io);
$walker($path, $perms, $io);
}
};
$worldWritable = bindec('0000000111');
$walker($dir . '/tmp', $worldWritable, $io);
$changePerms($dir . '/tmp', $worldWritable, $io);
$changePerms($dir . '/logs', $worldWritable, $io);
}
/**
* Set the security.salt value in the application's config file.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function setSecuritySalt($dir, $io)
{
$newKey = hash('sha256', Security::randomBytes(64));
static::setSecuritySaltInFile($dir, $io, $newKey, 'app.php');
}
/**
* Set the security.salt value in a given file
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @param string $newKey key to set in the file
* @param string $file A path to a file relative to the application's root
* @return void
*/
public static function setSecuritySaltInFile($dir, $io, $newKey, $file)
{
$config = $dir . '/config/' . $file;
$content = file_get_contents($config);
$content = str_replace('__SALT__', $newKey, $content, $count);
if ($count == 0) {
$io->write('No Security.salt placeholder to replace.');
return;
}
$result = file_put_contents($config, $content);
if ($result) {
$io->write('Updated Security.salt value in config/' . $file);
return;
}
$io->write('Unable to update Security.salt value.');
}
/**
* Set the APP_NAME value in a given file
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @param string $appName app name to set in the file
* @param string $file A path to a file relative to the application's root
* @return void
*/
public static function setAppNameInFile($dir, $io, $appName, $file)
{
$config = $dir . '/config/' . $file;
$content = file_get_contents($config);
$content = str_replace('__APP_NAME__', $appName, $content, $count);
if ($count == 0) {
$io->write('No __APP_NAME__ placeholder to replace.');
return;
}
$result = file_put_contents($config, $content);
if ($result) {
$io->write('Updated __APP_NAME__ value in config/' . $file);
return;
}
$io->write('Unable to update __APP_NAME__ value.');
}
}
File diff suppressed because it is too large. Load diff
+370
View File
@@ -0,0 +1,370 @@
<?php
namespace App\Controller;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
class CalcDoorsController extends AppController {
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow(['display', 'calcDoors', 'calcDoorsPrint']);
$this->viewBuilder()->setLayout('calc_doors');
}
public function display() {
$savedOrderData = null;
if($encOrderId = $this->request->getQuery('oid')) {
$ordersTable = TableRegistry::getTableLocator()->get('CalcDoorsOrders');
$decryptedData = TableRegistry::getTableLocator()->get('CutOrders')->decryptCustomData($encOrderId, $this->config);
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $ordersTable->find('all')->where(['id' => $decryptedData['orderId']])->first();
if (!empty($savedOrderData)) {
$savedOrderData['params'] = @json_decode($savedOrderData['params'], true);
$savedOrderData['created'] = date_timestamp_get(date_create($savedOrderData['created'])) * 1000;
$savedOrderData['order_calc_results'] = @json_decode($savedOrderData['order_calc_results'], true);
$savedOrderData['eOId'] = $encOrderId;
$savedOrderData['printOrderUrl'] = str_replace("/calc-doors", "/calc-doors-print", $savedOrderData['view_order_url']);
unset($savedOrderData['view_order_url']);
$this->set(compact('savedOrderData'));
} else {
$savedOrderData = null;
}
}
}
if (empty($savedOrderData)) {
$this->redirect(['controller' => 'Cut', 'action' => 'display']);
//temporary
}
$this->set('jsConfig', $this->getAppJsConfig("calcDoors", false, $savedOrderData));
}
public function calcDoors() {
$result = [
'status' => 'error'
];
if ($this->request->is('post')) {
ini_set('max_execution_time', 0);
ini_set('memory_limit', '2048M');
ini_set('max_input_time', '0');
$data = $this->request->getData();
if (!empty($data['selectedDoorData'])) {
if (!isset($data['selectedDoorData']['params'])) {
$data['selectedDoorData']['params'] = [];
}
$ordersTable = TableRegistry::getTableLocator()->get('CalcDoorsOrders');
$additionalActionsAllowedIPs = explode("\n", $this->config['global']['additionalActionsAllowedIPs']['value']);
$this->config['global']['additionalActionsAllowed'] = empty($additionalActionsAllowedIPs) || in_array($this->request->clientIP(), $additionalActionsAllowedIPs);
if(!empty($data['selectedDoorData']['eOId'])) {
$encryptedOrderId = $data['selectedDoorData']['eOId'];
$decryptedData = TableRegistry::getTableLocator()->get('CutOrders')->decryptCustomData($encryptedOrderId, $this->config);
if (!empty($decryptedData['orderId'])) {
$order = $ordersTable->find('all')->where(['id' => $decryptedData['orderId']])->first();
if (!empty($order)) {
$this->config['global']['additionalActionsAllowed'] = true;
}
}
}
if (empty($this->config['global']['additionalActionsAllowed'])) {
$data['selectedDoorData']['params']['discount'] = 0;
}
$result['selectedDoorData'] = $data['selectedDoorData'];
if (!isset($result['selectedDoorData']['order_calc_results'])) {
$result['selectedDoorData']['order_calc_results'] = [];
}
if (!empty($data['selectedDoorData']['params']['doorId'])) {
$configDoor = TableRegistry::getTableLocator()->get('CalcDoors')->find('all')->where([
'id' => $data['selectedDoorData']['params']['doorId']
])->first();
$doorPrices = [];
$manufacturerPrices = [];
$manufacturerLimits = [];
if (!empty($configDoor)) {
$configDoor = $configDoor->toArray();
$doorPrices = @json_decode($configDoor['params'], true);
$configManufacturer = TableRegistry::getTableLocator()->get('CalcDoorsSettings')->find('all')->where([
'group' => 'prices_' . $configDoor['manufacturer_id']
])->all()->toArray();
foreach($configManufacturer as $li) {
$manufacturerPrices[$li['key']] = $li['value'];
}
$configManufacturer = TableRegistry::getTableLocator()->get('CalcDoorsSettings')->find('all')->where([
'group' => 'limits_' . $configDoor['manufacturer_id']
])->all()->toArray();
foreach($configManufacturer as $li) {
$manufacturerLimits[$li['key']] = $li['value'];
}
}
$params = $data['selectedDoorData']['params'];
$warnings = [];
switch($configDoor['manufacturer_id']) {
case "aristo":
$width = isset($params['width']) ? (int)str_replace(",", ".", $params['width']) : 0;
$height = isset($params['height']) ? (int)str_replace(",", ".", $params['height']) : 0;
$doorsCount = isset($params['doorsCount']) ? (int)str_replace(",", ".", $params['doorsCount']) : 0;
if ($height > 0) {
if ($height < $manufacturerLimits['minDoorHeight']) {
$warnings[] = "Минимальная высота проёма - " . $manufacturerLimits['minDoorHeight'] . "мм";
}
if (empty($doorPrices['profileVertHigherLimit']) && ($height > $doorPrices['profileVertHeight'])) {
$warnings[] = "Проём не должен превышать высоту " . $doorPrices['profileVertHeight'] . "мм";
} else {
if ($height > $manufacturerLimits['maxDoorHeight']) {
$warnings[] = "Проём не должен превышать высоту " . $manufacturerLimits['maxDoorHeight'] . "мм";
}
}
}
if ($width > 0) {
if ($width < $manufacturerLimits['minDoorWidth']) {
$warnings[] = "Минимальная ширина проёма - " . $manufacturerLimits['minDoorWidth'] . "мм";
} else {
if ($doorsCount > 0) {
$widthPerDoor = $width / $doorsCount;
if ($widthPerDoor < $manufacturerLimits['minDoorWidth']) {
$warnings[] = "Минимальная ширина двери - " . $manufacturerLimits['minDoorWidth'] . "мм";
}
if ($widthPerDoor > $manufacturerLimits['maxDoorWidth']) {
$warnings[] = "Максимальная ширина двери - " . $manufacturerLimits['maxDoorWidth'] . "мм, на такой проём ставится " . counterPluralForm(ceil($width / $manufacturerLimits['maxDoorWidth']), ["", "", ""], ["дверь", "двери", "дверей"]);
}
}
}
}
}
if (empty($data['orderEmailNotify']) && empty($data['printOnly'])) { //do not recount on email send or print
$orderCalcResults = [];
switch($configDoor['manufacturer_id']) {
case "aristo":
if (empty($warnings)) {
if ($doorsCount > 0) {
$orderCalcResults['zerkalo'] = ($width * $height / 1000000) * 1.1;
$orderCalcResults['plenka'] = ($width / 500) * ($height / 1000) * 1.22;
$orderCalcResults['plenkaorakal'] = ($width * $height / 1000000);
$orderCalcResults['steklo'] = ($width * $height / 1000000) * 1.1;
$orderCalcResults['zerkalo'] = toFixed2($orderCalcResults['zerkalo'] / $doorsCount) . "/" . toFixed2($orderCalcResults['zerkalo']);
$orderCalcResults['plenka'] = toFixed2($orderCalcResults['plenka'] / $doorsCount) . "/" . toFixed2($orderCalcResults['plenka']);
$orderCalcResults['uplotnitelPK80'] = toFixed2($height * 2 / 1000 + $width / $doorsCount / 1000 * 2) . "/" . toFixed2($height * 2 / 1000 + $width / 1000 * 2);
$orderCalcResults['plenkaorakal'] = toFixed2($orderCalcResults['plenkaorakal'] / $doorsCount) . "/" . toFixed2($orderCalcResults['plenkaorakal']);
$orderCalcResults['steklo'] = toFixed2($orderCalcResults['steklo'] / $doorsCount) . "/" . toFixed2($orderCalcResults['steklo']);
$orderCalcResults['dsp10mm'] = ($widthPerDoor <= $manufacturerLimits['maxDoorWidth'] ? "0.5 листа" : "1 лист");
}
$discount = isset($params['discount']) ? min((int)(str_replace(",", ".", $params['discount'])), 100) : 0;
$orderCalcResults['ramaSrednayaCount'] = isset($params['ramaSrednaya']) ? (float)str_replace(",", ".", $params['ramaSrednaya']) : 0;
$orderCalcResults['zagimDlyaShlegelyaCount'] = isset($params['zagimDlyaShlegelya']) ? (float)str_replace(",", ".", $params['zagimDlyaShlegelya']) : 0;
$orderCalcResults['uplotnitelPK80Count'] = isset($params['uplotnitelPK80']) ? (float)str_replace(",", ".", $params['uplotnitelPK80']) : 0;
$orderCalcResults['zerkaloSerebroCount'] = isset($params['zerkaloSerebroCount']) ? (float)str_replace(",", ".", $params['zerkaloSerebroCount']) : 0; //round($height * $width / 1000000*1.1 ,2);
$orderCalcResults['plenkaBezopasnostiZerkalCount'] = isset($params['plenkaBezopasnostiZerkalCount']) ? (float)str_replace(",", ".", $params['plenkaBezopasnostiZerkalCount']) : 0;
//isset($_POST['dsp10mm']) ? (float) stripslashes(htmlspecialchars($_POST['dsp10mm'])) : 0;
$orderCalcResults['dsp10mmCount'] = isset($params['dsp10mm']) ? (float)str_replace(",", ".", $params['dsp10mm']) : 0;
$orderCalcResults['plenkaOrakalCount'] = isset($params['plenkaOrakalCount']) ? (float)str_replace(",", ".", $params['plenkaOrakalCount']) : 0;
$orderCalcResults['StekloCount'] = isset($params['StekloCount']) ? (float)str_replace(",", ".", $params['StekloCount']) : 0;
$orderCalcResults['price'] = 0;
$orderCalcResults['profilePrice'] = 0;
$orderCalcResults['profileVertCount'] = $doorsCount * 2;
$orderCalcResults['profilePrice'] += $orderCalcResults['profileVertPrice'] = $orderCalcResults['profileVertCount'] * (($height > $doorPrices['profileVertHeight']) && !empty($doorPrices['profileVertHigherLimit']) ? $doorPrices['profileVertHigherLimit'] : $doorPrices['profileVertBelowLimit']);
$orderCalcResults['napravlyaushayaVerhCount'] = $width / 1000;
$orderCalcResults['profilePrice'] += $orderCalcResults['napravlyaushayaVerhPrice'] = $orderCalcResults['napravlyaushayaVerhCount'] * $doorPrices['napravlyaushayaVerh'] / 5.4;
$orderCalcResults['napravlyaushayaNizCount'] = $width / 1000;
$orderCalcResults['profilePrice'] += $orderCalcResults['napravlyaushayaNizPrice'] = $orderCalcResults['napravlyaushayaNizCount'] * $doorPrices['napravlyaushayaNiz'] / 5.4;
$orderCalcResults['ramaGorizontNizCount'] = $width / 1000 * 1.1;
$orderCalcResults['profilePrice'] += $orderCalcResults['ramaGorizontNizPrice'] = $orderCalcResults['ramaGorizontNizCount'] * $doorPrices['ramaGorizontNiz'] / 5.4;
$orderCalcResults['ramaGorizontVerhCount'] = $width / 1000 * 1.1;
$orderCalcResults['profilePrice'] += $orderCalcResults['ramaGorizontVerhPrice'] = $orderCalcResults['ramaGorizontVerhCount'] * $doorPrices['ramaGorizontVerh'] / 5.4;
$orderCalcResults['profilePrice'] += $orderCalcResults['ramaSrednayaPrice'] = $orderCalcResults['ramaSrednayaCount'] * $doorPrices['ramaSrednaya'] / 5.4;
$orderCalcResults['komplektRolikovCount'] = $doorsCount;
$orderCalcResults['profilePrice'] += $orderCalcResults['komplektRolikovPrice'] = $orderCalcResults['komplektRolikovCount'] * $manufacturerPrices['komplektRolikov'];
$orderCalcResults['stoporCount'] = $doorsCount;
$orderCalcResults['profilePrice'] += $orderCalcResults['stoporPrice'] = $orderCalcResults['stoporCount'] * $manufacturerPrices['stopor'];
$orderCalcResults['shlegelCount'] = $height * 4 / 1000;
$orderCalcResults['profilePrice'] += $orderCalcResults['shlegelPrice'] = $orderCalcResults['shlegelCount'] * $manufacturerPrices['shlegel'];
$orderCalcResults['profilePrice'] += $orderCalcResults['zagimDlyaShlegelyaPrice'] = $orderCalcResults['zagimDlyaShlegelyaCount'] * $manufacturerPrices['zagimDlyaShlegelya'];
$orderCalcResults['profilePrice'] += $orderCalcResults['uplotnitelPK80Price'] = $orderCalcResults['uplotnitelPK80Count'] * $manufacturerPrices['uplotnitelPK80'];
$orderCalcResults['profilePrice'] = $orderCalcResults['profilePrice'] - $orderCalcResults['profilePrice'] * $discount / 100;
$orderCalcResults['price'] += $orderCalcResults['profilePrice'];
$orderCalcResults['napolneniePrice'] = 0;
$orderCalcResults['napolneniePrice'] += $orderCalcResults['zerkaloSerebroPrice'] = $orderCalcResults['zerkaloSerebroCount'] * $manufacturerPrices['zerkaloSerebro'];
$orderCalcResults['napolneniePrice'] += $orderCalcResults['plenkaBezopasnostiZerkalPrice'] = $orderCalcResults['plenkaBezopasnostiZerkalCount'] * $manufacturerPrices['plenkaBezopasnostiZerkal'];
$orderCalcResults['napolneniePrice'] += $orderCalcResults['dsp10mmPrice'] = $orderCalcResults['dsp10mmCount'] * $manufacturerPrices['dsp10mm'];
$orderCalcResults['napolneniePrice'] += $orderCalcResults['plenkaOrakalPrice'] = $orderCalcResults['plenkaOrakalCount'] * $manufacturerPrices['plenkaOrakal'];
$orderCalcResults['napolneniePrice'] += $orderCalcResults['StekloPrice'] = $orderCalcResults['StekloCount'] * $manufacturerPrices['Steklo'];
$orderCalcResults['price'] += $orderCalcResults['napolneniePrice'];
$orderCalcResults['sborkeDveriCount'] = $doorsCount;
$orderCalcResults['sverlenieOtverstiyNaDverCount'] = isset($params['sverlenieOtverstiyNaDver']) ? (float)str_replace(",", ".", $params['sverlenieOtverstiyNaDver']) : 0;
$orderCalcResults['rabotaSlognayaRamkaPramayaCount'] = isset($params['rabotaSlognayaRamkaPramaya']) ? (float)str_replace(",", ".", $params['rabotaSlognayaRamkaPramaya']) : 0;
$orderCalcResults['rabotaSlognayaRamkaKosayaCount'] = isset($params['rabotaSlognayaRamkaKosaya']) ? (float)str_replace(",", ".", $params['rabotaSlognayaRamkaKosaya']) : 0;
$orderCalcResults['rabotaSlognayaRamkaZigzagCount'] = isset($params['rabotaSlognayaRamkaZigzag']) ? (float)str_replace(",", ".", $params['rabotaSlognayaRamkaZigzag']) : 0;
$orderCalcResults['porezkaMaterialaCount'] = 1;
$orderCalcResults['porezkaMaterialaPercent'] = 5;
$orderCalcResults['porezkaMaterialaPrice'] = ($orderCalcResults['profilePrice'] + $orderCalcResults['napolneniePrice']) * $orderCalcResults['porezkaMaterialaPercent'] / 100; //5%
$orderCalcResults['peremichkiCount'] = isset($params['peremichki']) ? (float)str_replace(",", ".", $params['peremichki']) : 0;
$orderCalcResults['rabotaSlognayaCount'] = $width / ($doorsCount > 0 ? $doorsCount : 1) * $orderCalcResults['peremichkiCount'] / 1000;
$orderCalcResults['rabotaPrice'] = 0;
$orderCalcResults['rabotaPrice'] += $orderCalcResults['sborkeDveriPrice'] = $orderCalcResults['sborkeDveriCount'] * $manufacturerPrices['sborkeDveri'];
$orderCalcResults['rabotaPrice'] += $orderCalcResults['porezkaMaterialaTotalPrice'] = $orderCalcResults['porezkaMaterialaCount'] * $orderCalcResults['porezkaMaterialaPrice'];
$orderCalcResults['rabotaPrice'] += $orderCalcResults['sverlenieOtverstiyNaDverPrice'] = $orderCalcResults['sverlenieOtverstiyNaDverCount'] * $manufacturerPrices['sverlenieOtverstiyNaDver'];
$orderCalcResults['rabotaPrice'] += $orderCalcResults['rabotaSlognayaPrice'] = $orderCalcResults['rabotaSlognayaCount'] * $manufacturerPrices['rabotaSlognaya'];
$orderCalcResults['price'] += $orderCalcResults['rabotaPrice'];
$orderCalcResults['price'] = toFixed2Price($orderCalcResults['price']);
$orderCalcResults['rabotaPrice'] = toFixed2($orderCalcResults['rabotaPrice']);
}
break;
}
$result['selectedDoorData']['order_calc_results'] = $orderCalcResults;
} else {
$result['selectedDoorData']['order_calc_results'] = $data['selectedDoorData']['order_calc_results'];
}
if (isset($result['selectedDoorData']['params']['width']) && empty($result['selectedDoorData']['params']['width'])) {
$result['selectedDoorData']['params']['width'] = "";
}
if (isset($result['selectedDoorData']['params']['height']) && empty($result['selectedDoorData']['params']['height'])) {
$result['selectedDoorData']['params']['height'] = "";
}
$result['status'] = 'success';
$result['warnings'] = $warnings;
if (empty($warnings)) {
if (!empty($data['orderEmailNotify']) || !empty($data['printOnly'])) {
$sendOrderEmailNotify = true;
if (!empty($data['branchId']) && ($data['branchId'] == "printOnly")) { //send print notify only from allowed ips, hide button for others
if (empty($this->config['global']['additionalActionsAllowed'])) {
$sendOrderEmailNotify = false;
}
}
if ($sendOrderEmailNotify) {
$authUserId = $this->Auth->user('id');
$authUserId = (!empty($authUserId) ? $authUserId : null);
$data['selectedDoorData'] = $result['selectedDoorData'];
$orderEmailNotifySentResults = $ordersTable->orderEmailNotify($data, $this->config, $this->request->clientIP(), $authUserId, $configDoor);
$result['orderEmailNotifySent'] = $orderEmailNotifySentResults['orderEmailNotifySent'];
$result['printOrderUrl'] = $orderEmailNotifySentResults['printOrderUrl'];
}
}
}
} else {
$result['message'] = 'Не выбран производитель и цвет дверей.';
}
} else {
$result['message'] = 'Нет данных.';
}
} else {
$result['message'] = 'Нет данных.';
}
echo json_encode($result);
exit;
}
public function calcDoorsPrint() {
$isOrderFound = false;
if($encOrderId = $this->request->getQuery('oid')) {
$ordersTable = TableRegistry::getTableLocator()->get('CalcDoorsOrders');
$decryptedData = TableRegistry::getTableLocator()->get('CutOrders')->decryptCustomData($encOrderId, $this->config);
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $ordersTable->find('all')->where(['id' => $decryptedData['orderId']])->first();
if (!empty($savedOrderData)) {
$orderFileName = $savedOrderData->order_num . '.zip';
$orderFileDir = ROOT . '/files/calc_doors_orders/';
if (!is_dir($orderFileDir) || !file_exists($orderFileDir.$orderFileName)) {
} else {
$zip = new \ZipArchive;
if ($zip->open($orderFileDir . $orderFileName) === true) {
$unpackedDir = $orderFileDir . 'unpacked/';
$zip->extractTo($unpackedDir);
$zip->close();
$pdfFilename = $savedOrderData->order_num. "_specification" . ".pdf";
header('Content-Type: application/pdf');
header('Content-disposition: inline; filename="' . $pdfFilename . '"');
header('Content-Length: ' . filesize($unpackedDir.$pdfFilename));
header('Cache-Control: public, must-revalidate, max-age=0');
header('Pragma: public');
readfile($unpackedDir.$pdfFilename);
array_map("unlink", glob($unpackedDir.$savedOrderData->order_num."*"));
$isOrderFound = true;
}
}
}
}
}
if (!$isOrderFound) {
$this->redirect(['controller' => 'CalcDoors', 'action' => 'display']);
} else {
exit;
}
}
}
View File
Whitespace-only changes.
+682
View File
@@ -0,0 +1,682 @@
<?php
namespace App\Controller;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
class CutController extends AppController {
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow(['display', 'cut', 'getDetailsListFromPost', 'cutPrint']);
$this->viewBuilder()->setLayout('cut');
}
public function display() {
$savedOrderData = null;
if($encOrderId = $this->request->getQuery('oid')) {
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$decryptedData = $ordersTable->decryptCustomData($encOrderId, $this->config);
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $ordersTable->find('all')->where(['id' => $decryptedData['orderId']])->first();
if (!empty($savedOrderData)) {
$savedOrderData['sheet_data'] = @json_decode($savedOrderData['sheet_data'], true);
$savedOrderData['created'] = date_timestamp_get(date_create($savedOrderData['created'])) * 1000;
$savedOrderData['details'] = @json_decode($savedOrderData['details'], true);
if (!empty($savedOrderData['details']['details'])) {
foreach ($savedOrderData['details']['details'] as $key => $detail) {
if (!empty($detail['label'])) {
$savedOrderData['details']['details'][$key]['label'] = html_entity_decode($detail['label']);
}
}
}
$savedOrderData['order_cut_results'] = @json_decode($savedOrderData['order_cut_results'], true);
if (!empty($savedOrderData['order_cut_results']['price'])) {
$savedOrderData['order_cut_results']['price'] = toFixed2Price(str_replace(" ", "", $savedOrderData['order_cut_results']['price']));
}
$savedOrderData['eOId'] = $encOrderId;
$savedOrderData['printOrderSpecUrl'] = str_replace("/cut", "/cut-print/spec", $savedOrderData['view_order_url']);
$savedOrderData['printOrderCutUrl'] = str_replace("/cut", "/cut-print/cut", $savedOrderData['view_order_url']);
unset($savedOrderData['view_order_url']);
$this->set(compact('savedOrderData'));
} else {
$savedOrderData = null;
}
}
}
$this->set('jsConfig', $this->getAppJsConfig("cut", false, $savedOrderData));
}
public function cut() {
$result = [
'status' => 'error'
];
if ($this->request->is('post')) {
ini_set('max_execution_time', 0);
ini_set('memory_limit', '2048M');
ini_set('max_input_time', '0');
$data = $this->request->getData();
if (!empty($data['details'])) {
$sheetPrice = 0;
$sheetWidth = 0;
$sheetHeight = 0;
$notchPrice = 0;
$facePrice = 0;
$groovePrice = 0;
$data['sheetData'] = [];
if (!empty($data['selectedSheetData']['sheetId'])) {
$additionalActionsAllowedIPs = explode("\n", $this->config['global']['additionalActionsAllowedIPs']['value']);
$this->config['global']['additionalActionsAllowed'] = empty($additionalActionsAllowedIPs) || in_array($this->request->clientIP(), $additionalActionsAllowedIPs);
if(!empty($data['eOId'])) {
$decryptedData = TableRegistry::getTableLocator()->get('CutOrders')->decryptCustomData($data['eOId'], $this->config);
if (!empty($decryptedData['orderId'])) {
$order = TableRegistry::getTableLocator()->get('CalcDoorsOrders')->find('all')->where(['id' => $decryptedData['orderId']])->first();
if (!empty($order)) {
$this->config['global']['additionalActionsAllowed'] = true;
}
}
}
$configSheet = TableRegistry::getTableLocator()->get('CutSheets')->find('all')->where([
'id' => $data['selectedSheetData']['sheetId']
])->first();
if (!empty($configSheet)) {
$configSheet = $configSheet->toArray();
$sheetDimension = TableRegistry::getTableLocator()->get('CutSheetDimensions')->find('all')->where([
'id' => $configSheet['sheet_dimension_id']
])->first();
if (!empty($sheetDimension)) {
$configSheet['fullWidth'] = $sheetDimension['width'];
$configSheet['fullHeight'] = $sheetDimension['height'];
$face = $this->config['common']['face']['value'];
$sheetWidth = $configSheet['fullWidth'] - 2 * $face;
$sheetHeight = $configSheet['fullHeight'] - 2 * $face;
$sheetPrice = $configSheet['price'];
$notchPrice = $this->config['prices']['notchPrice']['value'];
$facePrice = $this->config['prices']['facePrice']['value'];
$groovePrice = $this->config['groovePrices']['groove']['value'];
$data['sheetData']['sheetData'] = $configSheet;
$data['sheetData']['sheetDimension'] = $sheetDimension->toArray();
}
}
$price = 0;
$data['selectedBordersData'] = [];
foreach ([1, 2] as $borderId) {
if (!empty($data['selectedSheetData']['border' . $borderId . 'Id'])) {
$bCount = (!empty($data['selectedSheetData']['border' . $borderId . 'Count']) ? $data['selectedSheetData']['border' . $borderId . 'Count'] : 0);
if (!empty($bCount)) {
$border = TableRegistry::getTableLocator()->get('CutBorders')->find('all')->where([
'id' => $data['selectedSheetData']['border' . $borderId . 'Id']
])->first();
if (!empty($border)) {
if (!empty($data['selectedSheetData']['border' . $borderId . 'Height'])) {
$borderDimension = TableRegistry::getTableLocator()->get('CutBorderDimensions')->find('all')->where([
'id' => $data['selectedSheetData']['border' . $borderId . 'Height']
])->first();
if (!empty($borderDimension)) {
$selectBorderDimension = TableRegistry::getTableLocator()->get('CutBorderAvailableDimensions')->find('all')->where([
'border_id' => $data['selectedSheetData']['border' . $borderId . 'Id'],
'border_dimension_id' => $data['selectedSheetData']['border' . $borderId . 'Height'],
])->first();
if (!empty($selectBorderDimension)) {
$price += ($bCount / 1000 * $borderDimension['gluing_price']) + ($bCount / 1000 * 1.1 * $selectBorderDimension['price']);
$data['selectedBordersData']['border' . $borderId . 'SelectBorderDimension'] = $selectBorderDimension->toArray();
$data['selectedBordersData']['border' . $borderId . 'BorderDimension'] = $borderDimension->toArray();
$data['selectedBordersData']['border' . $borderId . 'Border'] = $border->toArray();
}
}
}
}
}
}
}
$cornersText = [];
if (!empty($data['detailsCorners'])) {
foreach ($data['details'] as $detailKey => $detail) {
if (!empty($detail['width']) && ((int)$detail['width'] > 0) &&
!empty($detail['height']) && ((int)$detail['height'] > 0) &&
!empty($detail['count']) && ((int)$detail['count'] > 0) &&
!empty($data['detailsCorners'][$detail['id']])) {
$detailCorners = $data['detailsCorners'][$detail['id']];
$currentCornerText = [];
$currentCornerPrice = [
'cut' => 0,
'radius' => 0
];
foreach ($this->config['cornerPositions'] as $cornerPositionKey => $cornerPosition) {
if (!empty($detailCorners[$cornerPositionKey]) && ($detailCorners[$cornerPositionKey]['value'] != 'none')) {
if ($detailCorners[$cornerPositionKey]['value'] == 'cut') {
if (!empty($detailCorners[$cornerPositionKey]['diagonalCut'])) {
$currentCornerText[] = $cornerPosition["shortLabel"] . " - диагональный срез";
} else {
$currentCornerText[] = $cornerPosition["shortLabel"] . " - срез " . $detailCorners[$cornerPositionKey]['x'] . "x" . $detailCorners[$cornerPositionKey]['y'] . " мм";
}
if (empty($currentCornerPrice['cut'])) {
$currentCornerPrice['cut'] = $this->config['cornerPrices']['cut']['value']; //одна цена за любое кол-во срезов
}
}
if ($detailCorners[$cornerPositionKey]['value'] == 'radius') {
$currentCornerText[] = $cornerPosition["shortLabel"] . " - скругление " . $detailCorners[$cornerPositionKey]['radius'] . "мм";
foreach ($this->config['cornerPrices'] as $rpKey => $rpPrice) {
if (strpos($rpKey, "radius-") === 0) {
$rpKey = str_replace("radius-", "", $rpKey);
if (is_numeric($rpKey)) {
if ($detailCorners[$cornerPositionKey]['radius'] <= $rpKey) {
$currentCornerPrice['radius'] += $rpPrice['value'];
break;
}
} else {
$rpKey = (int)str_replace("+", "", $rpKey);
if ($detailCorners[$cornerPositionKey]['radius'] > $rpKey) {
$currentCornerPrice['radius'] += $rpPrice['value'];
break;
}
}
}
}
}
}
}
if (!empty($currentCornerText)) {
$cornersText[$detailKey + 1] = [
'detailLabel' => (!empty($detail['label']) ? $detail['label'] : ''),
'corners' => join(", ", $currentCornerText)
];
}
$price += ($currentCornerPrice['cut'] + $currentCornerPrice['radius']) * (int)$detail['count'];
}
}
}
$groovesText = [];
$groovesTotal = [];
$grooveCount = 0;
if (!empty($data['detailsGrooves'])) {
foreach ($data['details'] as $detailKey => $detail) {
if (!empty($detail['width']) && ((int)$detail['width'] > 0) &&
!empty($detail['height']) && ((int)$detail['height'] > 0) &&
!empty($detail['count']) && ((int)$detail['count'] > 0) &&
!empty($data['detailsGrooves'][$detail['id']])) {
$detailGrooves = $data['detailsGrooves'][$detail['id']];
$currentGrooveText = $currentGrooveText2 = $currentGrooveText3 = [];
foreach ($this->config['groovePositions'] as $groovePositionKey => $groovePosition) {
if (!empty($detailGrooves[$groovePositionKey]) && !empty($detailGrooves[$groovePositionKey]['enabled'])) {
$currentGrooveDepth = $detailGrooves[$groovePositionKey]['depth'];
$currentGrooveWidth = $detailGrooves[$groovePositionKey]['width'];
$currentGrooveText[] = mb_strtolower($groovePosition["label"]) . " - отступ от края детали " . $detailGrooves[$groovePositionKey]['offset'] . "мм, глубина пропила " . $currentGrooveDepth . "мм, ширина пропила " . $currentGrooveWidth . "мм";
if (!isset($groovesTotal[$currentGrooveDepth."_".$currentGrooveWidth])) {
$groovesTotal[$currentGrooveDepth."_".$currentGrooveWidth] = [
'label' => "глубина пропила " . $currentGrooveDepth . "мм, ширина пропила " . $currentGrooveWidth . "мм",
'count' => 0
];
}
if (in_array($groovePositionKey, ["top", "bottom"])) {
$grooveCount += (int)$detail['width'] * (int)$detail['count'];
$groovesTotal[$currentGrooveDepth."_".$currentGrooveWidth]['count'] += (int)$detail['width'] * (int)$detail['count'];
$currentGrooveText2Tmp = 'по.'.$groovePosition["labelShort2"]." " . (int)$detail['width'];
} else {
$grooveCount += (int)$detail['height'] * (int)$detail['count'];
$groovesTotal[$currentGrooveDepth."_".$currentGrooveWidth]['count'] += (int)$detail['height'] * (int)$detail['count'];
$currentGrooveText2Tmp = 'по.'.$groovePosition["labelShort2"]." " . (int)$detail['height'];
}
$currentGrooveText2Tmp .= "_" . (!empty($detailGrooves[$groovePositionKey]['offset']) ? $detailGrooves[$groovePositionKey]['offset'] . '*' : '') . $currentGrooveWidth . '*' . $currentGrooveDepth;
$currentGrooveText2[] = $currentGrooveText2Tmp;
$currentGrooveText3[] = 'паз.' . $currentGrooveText2Tmp;
}
}
if (!empty($currentGrooveText)) {
$groovesText[$detailKey + 1] = [
'detailLabel' => (!empty($detail['label']) ? $detail['label'] : ''),
'grooves' => join(", ", $currentGrooveText),
'grooves2' => join(", ", $currentGrooveText2),
'grooves3' => join(", ", $currentGrooveText3)
];
}
}
}
}
$sheets = [];
$notchCount = 0;
$initialDetails = $this->getDetailsListFromPost($data['details']);
if (empty($data['orderEmailNotify']) && empty($data['printOnly'])) { //do not recount on email send
$fitOrders = ["toTop"/*, "toRight", "toBottom"*/];
foreach($fitOrders as $fitOrder) {
if (in_array($fitOrder, ['toRight', 'toBottom'])) {
$detailsSortFunctions = [
'cmpDetailsByBothSides',
'cmpDetailsByBothSidesReversed',
'cmpDetailsByWidth',
'cmpDetailsByWidthReversed',
'cmpDetailsByHeight',
'cmpDetailsByHeightReversed',
''
];
} else {
if (in_array($fitOrder, ['toTop'])) {
$detailsSortFunctions = [
'cmpDetailsByBothSidesForToTop'
];
}
}
foreach ($detailsSortFunctions as $detailsSortFunction) {
//$detailsToFitCheckArray = [];
//$details = $this->getDetailsListFromPost($data['details']);
$details = $initialDetails;
foreach ($details as $key => $detail) {
$details[$key] = (object)$detail;
}
if (!empty($detailsSortFunction)) {
usort($details, $detailsSortFunction);
}
//debug($details);exit;
//$detailsToFitCheckArray[] = $postDetails;
$currentSheets = [];
$currentSheetIndex = 0;
$currentNotchCount = 0;
//foreach ($detailsToFitCheckArray as $details) {
while (!empty($details)) {
$packer = new Packer();
$packer->fitDetails($details, $sheetWidth, $sheetHeight, $this->config['common']['notch']['value'], $currentNotchCount, $fitOrder);
$currentSheets[$currentSheetIndex] = [
'id' => $currentSheetIndex + 1,
'details' => []
];
foreach ($details as $key => $detail) {
if (!empty($detail->fit)) {
if (in_array($fitOrder, ['toRight', 'toBottom'])) {
$detail->top = $detail->fit->top;
} else {
if (in_array($fitOrder, ['toTop'])) {
$detail->top = $detail->fit->bottom - $detail->height;
}
}
$detail->left = $detail->fit->left;
unset($detail->fit);
$currentSheets[$currentSheetIndex]['details'][] = $detail;
unset($details[$key]);
}
}
$currentSheetIndex++;
}
if (empty($sheets) || (count($currentSheets) < count($sheets))) {
$sheets = $currentSheets;
$notchCount = $currentNotchCount;
if (count($sheets) == 1) { //no need to calculate more cause of minimal sheets count
break;
}
}
//}
//exit;
}
}
$faceCount = count($sheets) * ($configSheet['fullWidth'] + $configSheet['fullHeight']) * 2;
$price += $sheetPrice * count($sheets) +
$notchCount / 1000 * $notchPrice + // цена пропила
$faceCount / 1000 * $facePrice + // цена торцевания
$grooveCount / 1000 * $groovePrice // цена пазов
;
$sheets = array_values($sheets);
$notchCount = str_replace(".00", "", number_format(ceil($notchCount + $faceCount) / 1000, 2, ".", ""));
$grooveCount = ($grooveCount > 0 ? str_replace(".00", "", number_format(ceil($grooveCount) / 1000, 2, ".", "")) : "");
$result['price'] = str_replace(".00", "", number_format(ceil($price * 100) / 100, 2, ".", " "));
} else {
$sheets = $data['sheets'];
$notchCount = $data['notchCount'];
$grooveCount = $data['grooveCount'];
$result['price'] = $data['price'];
}
$result['sheets'] = $sheets;
$result['notchCount'] = $notchCount;
$result['grooveCount'] = $grooveCount;
$result['status'] = 'success';
if (!empty($data['orderEmailNotify']) || !empty($data['printOnly'])) {
$sendOrderEmailNotify = true;
if (!empty($data['branchId']) && in_array($data['branchId'], ["printNotify", "printOnly"])) { //send print notify only from allowed ips, hide button for others
if (empty($this->config['global']['additionalActionsAllowed'])) {
$sendOrderEmailNotify = false;
}
}
if ($sendOrderEmailNotify) {
$authUserId = $this->Auth->user('id');
$authUserId = (!empty($authUserId) ? $authUserId : null);
$orderEmailNotifySentResults = TableRegistry::getTableLocator()->get('CutOrders')->orderEmailNotify($data, $this->config, count($sheets), $cornersText, $groovesText, $groovesTotal, $this->request->clientIP(), $authUserId, $result['price']);
$result['orderEmailNotifySent'] = $orderEmailNotifySentResults['orderEmailNotifySent'];
$result['printOrderSpecUrl'] = $orderEmailNotifySentResults['printOrderSpecUrl'];
$result['printOrderCutUrl'] = $orderEmailNotifySentResults['printOrderCutUrl'];
if(!empty($result['orderEmailNotifySent'])
&& ($result['orderEmailNotifySent'] == "success")
&& (in_array($this->request->clientIP(), ["78.30.242.25"]))
) {
$result['fullCleanupOnSuccess'] = true;
}
}
}
} else {
$result['message'] = 'Не выбрана фактура листа.';
}
} else {
$result['message'] = 'Нет данных.';
}
} else {
$result['message'] = 'Нет данных.';
}
echo json_encode($result);
exit;
}
public function getDetailsListFromPost($postDetails = []) {
$details = [];
$detailsCount = 0;
foreach ($postDetails as $detail) {
if (!empty($detail['width']) && ((int)$detail['width'] > 0) &&
!empty($detail['height']) && ((int)$detail['height'] > 0) &&
!empty($detail['count']) && ((int)$detail['count'] > 0)) {
for ($i = 1; $i <= (int)$detail['count']; $i++) {
$detailsCount++;
$details[$detailsCount] = [
'id' => $detailsCount,
'detailId' => $detail['id'],
'width' => $detail['width'],
'height' => $detail['height'],
];
}
}
}
return $details;
}
public function cutPrint($printType) {
$isOrderFound = false;
if($encOrderId = $this->request->getQuery('oid')) {
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$decryptedData = $ordersTable->decryptCustomData($encOrderId, $this->config);
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $ordersTable->find('all')->where(['id' => $decryptedData['orderId']])->first();
if (!empty($savedOrderData)) {
$orderFileName = $savedOrderData->order_num . '.zip';
$orderFileDir = ROOT . '/files/cut_orders/';
if (!is_dir($orderFileDir) || !file_exists($orderFileDir.$orderFileName)) {
} else {
$zip = new \ZipArchive;
if ($zip->open($orderFileDir . $orderFileName) === true) {
$unpackedDir = $orderFileDir . 'unpacked/';
$zip->extractTo($unpackedDir);
$zip->close();
$fileAdd = ($printType == "cut" ? "cut" : "specification");
$pdfFilename = $savedOrderData->order_num. "_" . $fileAdd . ".pdf";
header('Content-Type: application/pdf');
header('Content-disposition: inline; filename="' . $pdfFilename . '"');
header('Content-Length: ' . filesize($unpackedDir.$pdfFilename));
header('Cache-Control: public, must-revalidate, max-age=0');
header('Pragma: public');
readfile($unpackedDir.$pdfFilename);
@unlink($unpackedDir.$pdfFilename);
array_map("unlink", glob($unpackedDir.$savedOrderData->order_num."*"));
$isOrderFound = true;
}
}
}
}
}
if (!$isOrderFound) {
$this->redirect(['controller' => 'Cut', 'action' => 'display']);
} else {
exit;
}
}
}
class Packer {
public $root;
public $notch;
public function fitDetails(&$blocks, $sheetWidth, $sheetHeight, $notch, &$notchCount, $fitOrder = "toRight") {
//debug($blocks);exit;
if (in_array($fitOrder, ['toRight', 'toBottom'])) {
$this->root = (object)array(
'left' => 0,
'top' => 0,
'width' => $sheetWidth,
'height' => $sheetHeight,
'isUsed' => false,
);
} else {
if (in_array($fitOrder, ['toTop'])) {
$this->root = (object)array(
'left' => 0,
'bottom' => $sheetHeight,
'width' => $sheetWidth,
'height' => $sheetHeight,
'isUsed' => false,
);
}
}
$this->notch = $notch;
foreach ($blocks as &$block) {
$node = $this->findNode($this->root, $block->width, $block->height, $fitOrder);
if ($node) {
$block->fit = $this->splitNode($node, $block->width, $block->height, $fitOrder);
//пропил
if ($block->fit->left + $block->width < $sheetWidth) {
$notchCount += $block->height;
}
if (in_array($fitOrder, ['toRight', 'toBottom'])) {
if ($block->fit->top + $block->height < $sheetHeight) {
$notchCount += $block->width;
}
if (($block->fit->left + $block->width < $sheetWidth) && ($block->fit->top + $block->height < $sheetHeight)) {
//деталь не полностью прилегает к краям - добавляем угол пропила
$notchCount += $this->notch;
}
} else {
if (in_array($fitOrder, ['toTop'])) {
if ($block->fit->bottom - $block->height > 0) {
$notchCount += $block->width;
}
if (($block->fit->left + $block->width < $sheetWidth) && ($block->fit->bottom - $block->height > 0)) {
//деталь не полностью прилегает к краям - добавляем угол пропила
$notchCount += $this->notch;
}
}
}
}
}
}
public function findNode(&$root, $width, $height, $fitOrder = "toRight") {
if (@$root->isUsed) {
if ($fitOrder == "toRight") {
$node = $this->findNode($root->right, $width, $height, $fitOrder);
if ($node) {
return $node;
}
$node = $this->findNode($root->bottom, $width, $height, $fitOrder);
if ($node) {
return $node;
}
} else {
if ($fitOrder == "toBottom") {
$node = $this->findNode($root->bottom, $width, $height, $fitOrder);
if ($node) {
return $node;
}
$node = $this->findNode($root->right, $width, $height, $fitOrder);
if ($node) {
return $node;
}
} else {
if ($fitOrder == "toTop") {
$node = $this->findNode($root->right, $width, $height, $fitOrder);
if ($node) {
return $node;
}
$node = $this->findNode($root->top, $width, $height, $fitOrder);
if ($node) {
return $node;
}
}
}
}
} else {
if (($width <= $root->width) && ($height <= $root->height)) {
return $root;
} else {
return null;
}
}
return null;
}
public function splitNode(&$node, $width, $height, $fitOrder) {
$node->isUsed = true;
if (in_array($fitOrder, ['toRight', 'toBottom'])) {
$node->right = (object)array(
'left' => $node->left + $width + $this->notch,
'top' => $node->top,
'width' => $node->width - $width - $this->notch,
'height' => $height,
);
$node->bottom = (object)array(
'left' => $node->left,
'top' => $node->top + $height + $this->notch,
'width' => $node->width,
'height' => $node->height - $height - $this->notch,
);
} else {
if (in_array($fitOrder, ['toTop'])) {
$node->right = (object)array(
'left' => $node->left + $width + $this->notch,
'bottom' => $node->bottom,
'width' => $node->width - $width - $this->notch,
'height' => $height,
);
$node->top = (object)array(
'left' => $node->left,
'bottom' => $node->bottom - $height - $this->notch,
'width' => $node->width,
'height' => $node->height - $height - $this->notch,
);
}
}
return $node;
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace App\Controller;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
class DeliveryController extends AppController {
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow(['display']);
$this->viewBuilder()->setLayout('delivery');
}
public function display($city = null) {
$isCityFound = false;
if (!empty($city)) {
$deliveryTable = TableRegistry::getTableLocator()->get('Delivery');
$delivery = $deliveryTable->find('all')->where(['slug' => $city, 'is_deleted' => false])->first();
if (!empty($delivery)) {
$delivery = $delivery->toArray();
$this->set('apiKey', $this->config['global']['yandexMapApiKey']['value']);
$this->set('siteTitle', $delivery['label']);
$this->set('delivery', $delivery);
$isCityFound = true;
}
//var_dump($this->getAppJsConfig("delivery", false));
$this->set('jsConfig', $this->getAppJsConfig("delivery", false));
}
if (empty($isCityFound)) {
$this->redirect('/404');
}
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Controller;
use Cake\Event\Event;
class ErrorController extends AppController {
public $config = null;
public function initialize() {
$this->loadComponent('RequestHandler');
}
public function beforeFilter(Event $event) {
}
public function beforeRender(Event $event) {
parent::beforeRender($event);
$this->viewBuilder()->setTemplatePath('Error');
$this->config = $this->getAppConfig("");
$this->set('jsConfig', $this->getAppJsConfig("", false));
}
public function afterFilter(Event $event) {
}
}
+682
View File
@@ -0,0 +1,682 @@
<?php
namespace App\Controller;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
class FasadController extends AppController {
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow(['display', 'fasad', 'getDetailsListFromPost', 'fasadPrint']);
$this->viewBuilder()->setLayout('fasad');
}
public function display() {
$savedOrderData = null;
if($encOrderId = $this->request->getQuery('oid')) {
$ordersTable = TableRegistry::getTableLocator()->get('FasadOrders');
$decryptedData = $ordersTable->decryptCustomData($encOrderId, $this->config);
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $ordersTable->find('all')->where(['id' => $decryptedData['orderId']])->first();
if (!empty($savedOrderData)) {
$savedOrderData['sheet_data'] = @json_decode($savedOrderData['sheet_data'], true);
$savedOrderData['created'] = date_timestamp_get(date_create($savedOrderData['created'])) * 1000;
$savedOrderData['details'] = @json_decode($savedOrderData['details'], true);
if (!empty($savedOrderData['details']['details'])) {
foreach ($savedOrderData['details']['details'] as $key => $detail) {
if (!empty($detail['label'])) {
$savedOrderData['details']['details'][$key]['label'] = html_entity_decode($detail['label']);
}
}
}
$savedOrderData['order_fasad_results'] = @json_decode($savedOrderData['order_fasad_results'], true);
if (!empty($savedOrderData['order_fasad_results']['price'])) {
$savedOrderData['order_fasad_results']['price'] = toFixed2Price(str_replace(" ", "", $savedOrderData['order_fasad_results']['price']));
}
$savedOrderData['eOId'] = $encOrderId;
$savedOrderData['printOrderSpecUrl'] = str_replace("/fasad", "/fasad-print/spec", $savedOrderData['view_order_url']);
$savedOrderData['printOrderFasadUrl'] = str_replace("/fasad", "/fasad-print/fasad", $savedOrderData['view_order_url']);
unset($savedOrderData['view_order_url']);
$this->set(compact('savedOrderData'));
} else {
$savedOrderData = null;
}
}
}
$this->set('jsConfig', $this->getAppJsConfig("fasad", false, $savedOrderData));
}
public function fasad() {
$result = [
'status' => 'error'
];
if ($this->request->is('post')) {
ini_set('max_execution_time', 0);
ini_set('memory_limit', '2048M');
ini_set('max_input_time', '0');
$data = $this->request->getData();
if (!empty($data['details'])) {
$sheetPrice = 0;
$sheetWidth = 0;
$sheetHeight = 0;
$notchPrice = 0;
$facePrice = 0;
$groovePrice = 0;
$data['sheetData'] = [];
if (!empty($data['selectedSheetData']['sheetId'])) {
$additionalActionsAllowedIPs = explode("\n", $this->config['global']['additionalActionsAllowedIPs']['value']);
$this->config['global']['additionalActionsAllowed'] = empty($additionalActionsAllowedIPs) || in_array($this->request->clientIP(), $additionalActionsAllowedIPs);
if(!empty($data['eOId'])) {
$decryptedData = TableRegistry::getTableLocator()->get('FasadOrders')->decryptCustomData($data['eOId'], $this->config);
if (!empty($decryptedData['orderId'])) {
$order = TableRegistry::getTableLocator()->get('CalcDoorsOrders')->find('all')->where(['id' => $decryptedData['orderId']])->first();
if (!empty($order)) {
$this->config['global']['additionalActionsAllowed'] = true;
}
}
}
$configSheet = TableRegistry::getTableLocator()->get('FasadSheets')->find('all')->where([
'id' => $data['selectedSheetData']['sheetId']
])->first();
if (!empty($configSheet)) {
$configSheet = $configSheet->toArray();
$sheetDimension = TableRegistry::getTableLocator()->get('FasadSheetDimensions')->find('all')->where([
'id' => $configSheet['sheet_dimension_id']
])->first();
if (!empty($sheetDimension)) {
$configSheet['fullWidth'] = $sheetDimension['width'];
$configSheet['fullHeight'] = $sheetDimension['height'];
$face = $this->config['common']['face']['value'];
$sheetWidth = $configSheet['fullWidth'] - 2 * $face;
$sheetHeight = $configSheet['fullHeight'] - 2 * $face;
$sheetPrice = $configSheet['price'];
$notchPrice = $this->config['prices']['notchPrice']['value'];
$facePrice = $this->config['prices']['facePrice']['value'];
$groovePrice = $this->config['groovePrices']['groove']['value'];
$data['sheetData']['sheetData'] = $configSheet;
$data['sheetData']['sheetDimension'] = $sheetDimension->toArray();
}
}
$price = 0;
$data['selectedBordersData'] = [];
foreach ([1, 2] as $borderId) {
if (!empty($data['selectedSheetData']['border' . $borderId . 'Id'])) {
$bCount = (!empty($data['selectedSheetData']['border' . $borderId . 'Count']) ? $data['selectedSheetData']['border' . $borderId . 'Count'] : 0);
if (!empty($bCount)) {
$border = TableRegistry::getTableLocator()->get('FasadBorders')->find('all')->where([
'id' => $data['selectedSheetData']['border' . $borderId . 'Id']
])->first();
if (!empty($border)) {
if (!empty($data['selectedSheetData']['border' . $borderId . 'Height'])) {
$borderDimension = TableRegistry::getTableLocator()->get('FasadBorderDimensions')->find('all')->where([
'id' => $data['selectedSheetData']['border' . $borderId . 'Height']
])->first();
if (!empty($borderDimension)) {
$selectBorderDimension = TableRegistry::getTableLocator()->get('FasadBorderAvailableDimensions')->find('all')->where([
'border_id' => $data['selectedSheetData']['border' . $borderId . 'Id'],
'border_dimension_id' => $data['selectedSheetData']['border' . $borderId . 'Height'],
])->first();
if (!empty($selectBorderDimension)) {
$price += ($bCount / 1000 * $borderDimension['gluing_price']) + ($bCount / 1000 * 1.1 * $selectBorderDimension['price']);
$data['selectedBordersData']['border' . $borderId . 'SelectBorderDimension'] = $selectBorderDimension->toArray();
$data['selectedBordersData']['border' . $borderId . 'BorderDimension'] = $borderDimension->toArray();
$data['selectedBordersData']['border' . $borderId . 'Border'] = $border->toArray();
}
}
}
}
}
}
}
$cornersText = [];
if (!empty($data['detailsCorners'])) {
foreach ($data['details'] as $detailKey => $detail) {
if (!empty($detail['width']) && ((int)$detail['width'] > 0) &&
!empty($detail['height']) && ((int)$detail['height'] > 0) &&
!empty($detail['count']) && ((int)$detail['count'] > 0) &&
!empty($data['detailsCorners'][$detail['id']])) {
$detailCorners = $data['detailsCorners'][$detail['id']];
$currentCornerText = [];
$currentCornerPrice = [
'cut' => 0,
'radius' => 0
];
foreach ($this->config['cornerPositions'] as $cornerPositionKey => $cornerPosition) {
if (!empty($detailCorners[$cornerPositionKey]) && ($detailCorners[$cornerPositionKey]['value'] != 'none')) {
if ($detailCorners[$cornerPositionKey]['value'] == 'cut') {
if (!empty($detailCorners[$cornerPositionKey]['diagonalCut'])) {
$currentCornerText[] = $cornerPosition["shortLabel"] . " - диагональный срез";
} else {
$currentCornerText[] = $cornerPosition["shortLabel"] . " - срез " . $detailCorners[$cornerPositionKey]['x'] . "x" . $detailCorners[$cornerPositionKey]['y'] . " мм";
}
if (empty($currentCornerPrice['cut'])) {
$currentCornerPrice['cut'] = $this->config['cornerPrices']['cut']['value']; //одна цена за любое кол-во срезов
}
}
if ($detailCorners[$cornerPositionKey]['value'] == 'radius') {
$currentCornerText[] = $cornerPosition["shortLabel"] . " - скругление " . $detailCorners[$cornerPositionKey]['radius'] . "мм";
foreach ($this->config['cornerPrices'] as $rpKey => $rpPrice) {
if (strpos($rpKey, "radius-") === 0) {
$rpKey = str_replace("radius-", "", $rpKey);
if (is_numeric($rpKey)) {
if ($detailCorners[$cornerPositionKey]['radius'] <= $rpKey) {
$currentCornerPrice['radius'] += $rpPrice['value'];
break;
}
} else {
$rpKey = (int)str_replace("+", "", $rpKey);
if ($detailCorners[$cornerPositionKey]['radius'] > $rpKey) {
$currentCornerPrice['radius'] += $rpPrice['value'];
break;
}
}
}
}
}
}
}
if (!empty($currentCornerText)) {
$cornersText[$detailKey + 1] = [
'detailLabel' => (!empty($detail['label']) ? $detail['label'] : ''),
'corners' => join(", ", $currentCornerText)
];
}
$price += ($currentCornerPrice['cut'] + $currentCornerPrice['radius']) * (int)$detail['count'];
}
}
}
$groovesText = [];
$groovesTotal = [];
$grooveCount = 0;
if (!empty($data['detailsGrooves'])) {
foreach ($data['details'] as $detailKey => $detail) {
if (!empty($detail['width']) && ((int)$detail['width'] > 0) &&
!empty($detail['height']) && ((int)$detail['height'] > 0) &&
!empty($detail['count']) && ((int)$detail['count'] > 0) &&
!empty($data['detailsGrooves'][$detail['id']])) {
$detailGrooves = $data['detailsGrooves'][$detail['id']];
$currentGrooveText = $currentGrooveText2 = $currentGrooveText3 = [];
foreach ($this->config['groovePositions'] as $groovePositionKey => $groovePosition) {
if (!empty($detailGrooves[$groovePositionKey]) && !empty($detailGrooves[$groovePositionKey]['enabled'])) {
$currentGrooveDepth = $detailGrooves[$groovePositionKey]['depth'];
$currentGrooveWidth = $detailGrooves[$groovePositionKey]['width'];
$currentGrooveText[] = mb_strtolower($groovePosition["label"]) . " - отступ от края детали " . $detailGrooves[$groovePositionKey]['offset'] . "мм, глубина пропила " . $currentGrooveDepth . "мм, ширина пропила " . $currentGrooveWidth . "мм";
if (!isset($groovesTotal[$currentGrooveDepth."_".$currentGrooveWidth])) {
$groovesTotal[$currentGrooveDepth."_".$currentGrooveWidth] = [
'label' => "глубина пропила " . $currentGrooveDepth . "мм, ширина пропила " . $currentGrooveWidth . "мм",
'count' => 0
];
}
if (in_array($groovePositionKey, ["top", "bottom"])) {
$grooveCount += (int)$detail['width'] * (int)$detail['count'];
$groovesTotal[$currentGrooveDepth."_".$currentGrooveWidth]['count'] += (int)$detail['width'] * (int)$detail['count'];
$currentGrooveText2Tmp = 'по.'.$groovePosition["labelShort2"]." " . (int)$detail['width'];
} else {
$grooveCount += (int)$detail['height'] * (int)$detail['count'];
$groovesTotal[$currentGrooveDepth."_".$currentGrooveWidth]['count'] += (int)$detail['height'] * (int)$detail['count'];
$currentGrooveText2Tmp = 'по.'.$groovePosition["labelShort2"]." " . (int)$detail['height'];
}
$currentGrooveText2Tmp .= "_" . (!empty($detailGrooves[$groovePositionKey]['offset']) ? $detailGrooves[$groovePositionKey]['offset'] . '*' : '') . $currentGrooveWidth . '*' . $currentGrooveDepth;
$currentGrooveText2[] = $currentGrooveText2Tmp;
$currentGrooveText3[] = 'паз.' . $currentGrooveText2Tmp;
}
}
if (!empty($currentGrooveText)) {
$groovesText[$detailKey + 1] = [
'detailLabel' => (!empty($detail['label']) ? $detail['label'] : ''),
'grooves' => join(", ", $currentGrooveText),
'grooves2' => join(", ", $currentGrooveText2),
'grooves3' => join(", ", $currentGrooveText3)
];
}
}
}
}
$sheets = [];
$notchCount = 0;
$initialDetails = $this->getDetailsListFromPost($data['details']);
if (empty($data['orderEmailNotify']) && empty($data['printOnly'])) { //do not recount on email send
$fitOrders = ["toTop"/*, "toRight", "toBottom"*/];
foreach($fitOrders as $fitOrder) {
if (in_array($fitOrder, ['toRight', 'toBottom'])) {
$detailsSortFunctions = [
'cmpDetailsByBothSides',
'cmpDetailsByBothSidesReversed',
'cmpDetailsByWidth',
'cmpDetailsByWidthReversed',
'cmpDetailsByHeight',
'cmpDetailsByHeightReversed',
''
];
} else {
if (in_array($fitOrder, ['toTop'])) {
$detailsSortFunctions = [
'cmpDetailsByBothSidesForToTop'
];
}
}
foreach ($detailsSortFunctions as $detailsSortFunction) {
//$detailsToFitCheckArray = [];
//$details = $this->getDetailsListFromPost($data['details']);
$details = $initialDetails;
foreach ($details as $key => $detail) {
$details[$key] = (object)$detail;
}
if (!empty($detailsSortFunction)) {
usort($details, $detailsSortFunction);
}
//debug($details);exit;
//$detailsToFitCheckArray[] = $postDetails;
$currentSheets = [];
$currentSheetIndex = 0;
$currentNotchCount = 0;
//foreach ($detailsToFitCheckArray as $details) {
while (!empty($details)) {
$packer = new Packer();
$packer->fitDetails($details, $sheetWidth, $sheetHeight, $this->config['common']['notch']['value'], $currentNotchCount, $fitOrder);
$currentSheets[$currentSheetIndex] = [
'id' => $currentSheetIndex + 1,
'details' => []
];
foreach ($details as $key => $detail) {
if (!empty($detail->fit)) {
if (in_array($fitOrder, ['toRight', 'toBottom'])) {
$detail->top = $detail->fit->top;
} else {
if (in_array($fitOrder, ['toTop'])) {
$detail->top = $detail->fit->bottom - $detail->height;
}
}
$detail->left = $detail->fit->left;
unset($detail->fit);
$currentSheets[$currentSheetIndex]['details'][] = $detail;
unset($details[$key]);
}
}
$currentSheetIndex++;
}
if (empty($sheets) || (count($currentSheets) < count($sheets))) {
$sheets = $currentSheets;
$notchCount = $currentNotchCount;
if (count($sheets) == 1) { //no need to calculate more cause of minimal sheets count
break;
}
}
//}
//exit;
}
}
$faceCount = count($sheets) * ($configSheet['fullWidth'] + $configSheet['fullHeight']) * 2;
$price += $sheetPrice * count($sheets) +
$notchCount / 1000 * $notchPrice + // цена пропила
$faceCount / 1000 * $facePrice + // цена торцевания
$grooveCount / 1000 * $groovePrice // цена пазов
;
$sheets = array_values($sheets);
$notchCount = str_replace(".00", "", number_format(ceil($notchCount + $faceCount) / 1000, 2, ".", ""));
$grooveCount = ($grooveCount > 0 ? str_replace(".00", "", number_format(ceil($grooveCount) / 1000, 2, ".", "")) : "");
$result['price'] = str_replace(".00", "", number_format(ceil($price * 100) / 100, 2, ".", " "));
} else {
$sheets = $data['sheets'];
$notchCount = $data['notchCount'];
$grooveCount = $data['grooveCount'];
$result['price'] = $data['price'];
}
$result['sheets'] = $sheets;
$result['notchCount'] = $notchCount;
$result['grooveCount'] = $grooveCount;
$result['status'] = 'success';
if (!empty($data['orderEmailNotify']) || !empty($data['printOnly'])) {
$sendOrderEmailNotify = true;
if (!empty($data['branchId']) && in_array($data['branchId'], ["printNotify", "printOnly"])) { //send print notify only from allowed ips, hide button for others
if (empty($this->config['global']['additionalActionsAllowed'])) {
$sendOrderEmailNotify = false;
}
}
if ($sendOrderEmailNotify) {
$authUserId = $this->Auth->user('id');
$authUserId = (!empty($authUserId) ? $authUserId : null);
$orderEmailNotifySentResults = TableRegistry::getTableLocator()->get('FasadOrders')->orderEmailNotify($data, $this->config, count($sheets), $cornersText, $groovesText, $groovesTotal, $this->request->clientIP(), $authUserId, $result['price']);
$result['orderEmailNotifySent'] = $orderEmailNotifySentResults['orderEmailNotifySent'];
$result['printOrderSpecUrl'] = $orderEmailNotifySentResults['printOrderSpecUrl'];
$result['printOrderFasadUrl'] = $orderEmailNotifySentResults['printOrderFasadUrl'];
if(!empty($result['orderEmailNotifySent'])
&& ($result['orderEmailNotifySent'] == "success")
&& (in_array($this->request->clientIP(), ["78.30.242.25"]))
) {
$result['fullCleanupOnSuccess'] = true;
}
}
}
} else {
$result['message'] = 'Не выбрана фактура листа.';
}
} else {
$result['message'] = 'Нет данных.';
}
} else {
$result['message'] = 'Нет данных.';
}
echo json_encode($result);
exit;
}
public function getDetailsListFromPost($postDetails = []) {
$details = [];
$detailsCount = 0;
foreach ($postDetails as $detail) {
if (!empty($detail['width']) && ((int)$detail['width'] > 0) &&
!empty($detail['height']) && ((int)$detail['height'] > 0) &&
!empty($detail['count']) && ((int)$detail['count'] > 0)) {
for ($i = 1; $i <= (int)$detail['count']; $i++) {
$detailsCount++;
$details[$detailsCount] = [
'id' => $detailsCount,
'detailId' => $detail['id'],
'width' => $detail['width'],
'height' => $detail['height'],
];
}
}
}
return $details;
}
public function fasadPrint($printType) {
$isOrderFound = false;
if($encOrderId = $this->request->getQuery('oid')) {
$ordersTable = TableRegistry::getTableLocator()->get('FasadOrders');
$decryptedData = $ordersTable->decryptCustomData($encOrderId, $this->config);
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $ordersTable->find('all')->where(['id' => $decryptedData['orderId']])->first();
if (!empty($savedOrderData)) {
$orderFileName = $savedOrderData->order_num . '.zip';
$orderFileDir = ROOT . '/files/fasad_orders/';
if (!is_dir($orderFileDir) || !file_exists($orderFileDir.$orderFileName)) {
} else {
$zip = new \ZipArchive;
if ($zip->open($orderFileDir . $orderFileName) === true) {
$unpackedDir = $orderFileDir . 'unpacked/';
$zip->extractTo($unpackedDir);
$zip->close();
$fileAdd = ($printType == "fasad" ? "fasad" : "specification");
$pdfFilename = $savedOrderData->order_num. "_" . $fileAdd . ".pdf";
header('Content-Type: application/pdf');
header('Content-disposition: inline; filename="' . $pdfFilename . '"');
header('Content-Length: ' . filesize($unpackedDir.$pdfFilename));
header('Cache-Control: public, must-revalidate, max-age=0');
header('Pragma: public');
readfile($unpackedDir.$pdfFilename);
@unlink($unpackedDir.$pdfFilename);
array_map("unlink", glob($unpackedDir.$savedOrderData->order_num."*"));
$isOrderFound = true;
}
}
}
}
}
if (!$isOrderFound) {
$this->redirect(['controller' => 'Fasad', 'action' => 'display']);
} else {
exit;
}
}
}
class Packer {
public $root;
public $notch;
public function fitDetails(&$blocks, $sheetWidth, $sheetHeight, $notch, &$notchCount, $fitOrder = "toRight") {
//debug($blocks);exit;
if (in_array($fitOrder, ['toRight', 'toBottom'])) {
$this->root = (object)array(
'left' => 0,
'top' => 0,
'width' => $sheetWidth,
'height' => $sheetHeight,
'isUsed' => false,
);
} else {
if (in_array($fitOrder, ['toTop'])) {
$this->root = (object)array(
'left' => 0,
'bottom' => $sheetHeight,
'width' => $sheetWidth,
'height' => $sheetHeight,
'isUsed' => false,
);
}
}
$this->notch = $notch;
foreach ($blocks as &$block) {
$node = $this->findNode($this->root, $block->width, $block->height, $fitOrder);
if ($node) {
$block->fit = $this->splitNode($node, $block->width, $block->height, $fitOrder);
//пропил
if ($block->fit->left + $block->width < $sheetWidth) {
$notchCount += $block->height;
}
if (in_array($fitOrder, ['toRight', 'toBottom'])) {
if ($block->fit->top + $block->height < $sheetHeight) {
$notchCount += $block->width;
}
if (($block->fit->left + $block->width < $sheetWidth) && ($block->fit->top + $block->height < $sheetHeight)) {
//деталь не полностью прилегает к краям - добавляем угол пропила
$notchCount += $this->notch;
}
} else {
if (in_array($fitOrder, ['toTop'])) {
if ($block->fit->bottom - $block->height > 0) {
$notchCount += $block->width;
}
if (($block->fit->left + $block->width < $sheetWidth) && ($block->fit->bottom - $block->height > 0)) {
//деталь не полностью прилегает к краям - добавляем угол пропила
$notchCount += $this->notch;
}
}
}
}
}
}
public function findNode(&$root, $width, $height, $fitOrder = "toRight") {
if (@$root->isUsed) {
if ($fitOrder == "toRight") {
$node = $this->findNode($root->right, $width, $height, $fitOrder);
if ($node) {
return $node;
}
$node = $this->findNode($root->bottom, $width, $height, $fitOrder);
if ($node) {
return $node;
}
} else {
if ($fitOrder == "toBottom") {
$node = $this->findNode($root->bottom, $width, $height, $fitOrder);
if ($node) {
return $node;
}
$node = $this->findNode($root->right, $width, $height, $fitOrder);
if ($node) {
return $node;
}
} else {
if ($fitOrder == "toTop") {
$node = $this->findNode($root->right, $width, $height, $fitOrder);
if ($node) {
return $node;
}
$node = $this->findNode($root->top, $width, $height, $fitOrder);
if ($node) {
return $node;
}
}
}
}
} else {
if (($width <= $root->width) && ($height <= $root->height)) {
return $root;
} else {
return null;
}
}
return null;
}
public function splitNode(&$node, $width, $height, $fitOrder) {
$node->isUsed = true;
if (in_array($fitOrder, ['toRight', 'toBottom'])) {
$node->right = (object)array(
'left' => $node->left + $width + $this->notch,
'top' => $node->top,
'width' => $node->width - $width - $this->notch,
'height' => $height,
);
$node->bottom = (object)array(
'left' => $node->left,
'top' => $node->top + $height + $this->notch,
'width' => $node->width,
'height' => $node->height - $height - $this->notch,
);
} else {
if (in_array($fitOrder, ['toTop'])) {
$node->right = (object)array(
'left' => $node->left + $width + $this->notch,
'bottom' => $node->bottom,
'width' => $node->width - $width - $this->notch,
'height' => $height,
);
$node->top = (object)array(
'left' => $node->left,
'bottom' => $node->bottom - $height - $this->notch,
'width' => $node->width,
'height' => $node->height - $height - $this->notch,
);
}
}
return $node;
}
}
@@ -0,0 +1,206 @@
<?php
namespace App\Controller\Manager;
use Cake\Event\Event;
use App\Controller\AppController;
use Cake\ORM\TableRegistry;
use Cake\Core\Configure;
class CalcDoorsOrdersController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["calcDoors"]));
}
function index($clientId = null) {
$this->set('jsConfig', $this->getAppJsConfig("calcDoors", true, null, $clientId));
}
function getOrders() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
$result['orders'] = $this->getOrdersList("calcDoors", $data);
echo $this->jsonSimpleResponse($result);
}
function downloadOrderFiles($orderId = null) {
if (!empty($orderId)) {
$ordersTable = TableRegistry::getTableLocator()->get('CalcDoorsOrders');
$order = $ordersTable->find()->where(['id' => $orderId, 'order_type' => 'order'])->first();
if (!empty($order)) {
$orderFileName = $order->order_num . '.zip';
$orderFileDir = ROOT . '/files/calc_doors_orders/';
$fullOrderFileName = $orderFileDir.$orderFileName;
if (!is_dir($orderFileDir) || !file_exists($fullOrderFileName)) {
} else {
header('Content-Length: ' . filesize($fullOrderFileName));
header('Content-Type: ' . mime_content_type($fullOrderFileName));
header('Content-Disposition: attachment; filename="' . $orderFileName . '"');
header('Content-Transfer-Encoding: binary');
readfile($fullOrderFileName);
exit;
}
}
}
$this->redirect(['controller' => 'CalcDoorsOrders', 'action' => 'index', 'prefix' => 'manager']);
}
function takeOrder() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data['id']) && !empty($data['ready_date'])) {
$ordersTable = TableRegistry::getTableLocator()->get('CalcDoorsOrders');
$order = $ordersTable->find()->where(['id' => $data['id'], 'order_type' => 'order'])->first();
if (!empty($order)) {
if (!empty($order->manager_id)) {
if ($order->manager_id == $this->Auth->user('id')) {
$result = [
'status' => 'error',
'warning' => 'Заказ уже взят Вами на исполнение'
];
} else {
$user = TableRegistry::getTableLocator()->get('Users')->get($order->manager_id);
$result = [
'status' => 'error',
'warning' => 'Заказ уже взят на исполнение пользователем ' . $user['name']
];
}
} else {
$result["manager_id"] = $order->manager_id = $this->Auth->user('id');
$order->taken_date = $result["taken_date"] = date('Y-m-d H:i:s'); //todo
if (is_string($data['ready_date'])) {
$data['ready_date'] = strtotime($data['ready_date']);
} else {
$data['ready_date'] = $data['ready_date'] / 1000;
}
$order->ready_date = $result["ready_date"] = date('Y-m-d H:i:s', $data['ready_date']);
$result["taken_date"] = $this->patchTimestampForJS($result['taken_date'], 0);
$result["ready_date"] = date_timestamp_get($this->patchTimestampForJS($result['ready_date'], 0, false)) * 1000;
$ordersTable->save($order);
}
} else {
$result = [
'status' => 'error',
'error' => 'Заказ не найден'
];
}
}
echo $this->jsonSimpleResponse($result);
}
function adminDeleteOrder() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$ordersTable = TableRegistry::getTableLocator()->get('CalcDoorsOrders');
$order = $ordersTable->find()->where(['id' => $data['id'], 'order_type' => 'order'])->first();
if (!empty($order)) {
$ordersTable->delete($order);
@unlink(ROOT . '/files/calc_doors_orders/'.$order->order_num . '.zip');
//$ordersTable->revertOrderDbStatistics($order);
} else {
$result = [
'status' => 'error',
'warning' => 'Заказ не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для удаления данного заказа'
];
}
echo $this->jsonSimpleResponse($result);
}
function adminTakeOrder() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$ordersTable = TableRegistry::getTableLocator()->get('CalcDoorsOrders');
$order = $ordersTable->find()->where(['id' => $data['id'], 'order_type' => 'order'])->first();
if (!empty($order)) {
if (empty($data['manager_id'])) {
$data['manager_id'] = null;
$data['ready_date'] = null;
} else {
if ($data['manager_id'] != $order->manager_id) {
$manager = TableRegistry::getTableLocator()->get('Users')->find()->where(['id' => $data['manager_id']])->first();
if (!empty($manager)) {
$ordersTable->resendOrderEmailNotify($order, $manager['email'], $this->config);
}
}
}
$result["manager_id"] = $order->manager_id = $data['manager_id'];
$order->taken_date = $result["taken_date"] = date('Y-m-d H:i:s');
if ($data['ready_date'] != null) {
if (is_string($data['ready_date'])) {
$data['ready_date'] = strtotime($data['ready_date']);
} else {
$data['ready_date'] = $data['ready_date'] / 1000;
}
$order->ready_date = $result["ready_date"] = date('Y-m-d H:i:s', $data['ready_date']);
} else {
$order->ready_date = $result["ready_date"] = null;
}
$result["taken_date"] = $this->patchTimestampForJS($result['taken_date'], 0);
if ($data['ready_date'] != null) {
$result["ready_date"] = date_timestamp_get($this->patchTimestampForJS($result['ready_date'], 0, false)) * 1000;
}
$ordersTable->save($order);
} else {
$result = [
'status' => 'error',
'warning' => 'Заказ не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для изменения данного заказа'
];
}
echo $this->jsonSimpleResponse($result);
}
}
@@ -0,0 +1,225 @@
<?php
namespace App\Controller\Manager;
use Cake\Event\Event;
use Cake\Core\Configure;
use Cake\ORM\TableRegistry;
use App\Controller\AppController;
use Cake\Utility\Hash;
class CalcDoorsSettingsController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["calcDoors"]));
}
function index() {
$this->set('jsConfig', $this->getAppJsConfig("calcDoors", true));
}
function saveSettings() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data['action'])) {
$settingsTable = TableRegistry::getTableLocator()->get('CalcDoorsSettings');
switch($data['action']) {
case 'saveGroup':
if (!empty($data['settings'])) {
foreach($data['settings'] as $groupId => $groupData) {
foreach($groupData as $s) {
if (!in_array($s['id'], ['lastUpdateDate', 'lastUpdateManagerId'])) {
$currentSetting = $settingsTable->find()->where([
'key' => $s['id'],
'group' => $groupId
])->first();
if (!empty($currentSetting)) {
$currentSetting->value = $s['value'];
$settingsTable->save($currentSetting);
} else {
$currentSetting = $settingsTable->newEntity([
'key' => $s['id'],
'value' => $s['value'],
'group' => $groupId
]);
$settingsTable->save($currentSetting);
}
}
}
}
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("calcDoors", $data['settingsGroup'], $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
break;
}
}
echo $this->jsonSimpleResponse($result);
}
function addUpdateDoor() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data)) {
$doorsTable = TableRegistry::getTableLocator()->get('CalcDoors');
$isOk = false;
unset($data['image']);
$data['params'] = @json_encode($data['params']);
if (empty($data['id'])) { //add
$door = $doorsTable->newEntity();
$door = $doorsTable->patchEntity($door, $data);
if ($doorsTable->save($door)) {
$isOk = true;
$result['newDoorId'] = $door->id;
}
} else { //update
$door = $doorsTable->find()->where(['id' => $data['id']])->first();
if (!empty($door)) {
$doorsTable->patchEntity($door, $data);
if ($doorsTable->save($door)) {
$isOk = true;
}
} else {
$result = [
'status' => 'error',
'warning' => 'Дверь не найдена'
];
}
}
if ($isOk) {
$result['doors'] = $this->getDoorsListForJS();
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("calcDoors", 'doors', $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
echo $this->jsonSimpleResponse($result);
}
function deleteDoor() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$doorsTable = TableRegistry::getTableLocator()->get('CalcDoors');
$door = $doorsTable->find()->where(['id' => $data['id']])->first();
if (!empty($door)) {
$doorsTable->delete($door);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("calcDoors", 'doors', $this->Auth->user('id'));
$result = array_merge($result, $updates);
if (!empty($door->image)) {
@unlink(WWW_ROOT . 'img' . DS . 'doors' . DS . $door->image);
}
} else {
$result = [
'status' => 'error',
'error' => 'Дверь не найдена'
];
}
}
} else {
$result = [
'status' => 'error',
'warning' => 'Недостаточно прав для удаления данной двери'
];
}
echo $this->jsonSimpleResponse($result);
}
function uploadImage() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data)) {
$isOk = false;
if (!empty($data['type']) && !empty($data['file']["name"]) && empty($data['file']["error"])) {
$fileInfo = getimagesize($data['file']["tmp_name"]);
$extension = image_type_to_extension($fileInfo[2]);
$extension = str_replace("jpeg", "jpg", $extension);
switch($data['type']) {
case "doorImage":
$doorsTable = TableRegistry::getTableLocator()->get('CalcDoors');
$door = $doorsTable->find()->where(['id' => $data['id']])->first();
$subFolder = "doors";
if (!empty($door)) {
$uploadDir = WWW_ROOT . 'img' . DS . $subFolder . DS;
if (!file_exists($uploadDir) && !is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
if (!empty($door->image)) {
@unlink($uploadDir.$door->image);
}
$tmpFileName = sha1(sha1($data['file']["name"] . time()) . time()) . $extension;
if (move_uploaded_file($data['file']["tmp_name"], $uploadDir . $tmpFileName)) {
$door->image = $tmpFileName;
$doorsTable->patchEntity($door, $data);
if ($doorsTable->save($door)) {
$isOk = true;
$result['image'] = $tmpFileName;
$updateGroup = "doors";
}
};
} else {
$result = [
'status' => 'error',
'warning' => 'Дверь не найдена'
];
}
break;
}
}
if ($isOk && (!empty($updateGroup))) {
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("calcDoors", $updateGroup, $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
echo $this->jsonSimpleResponse($result);
}
}
@@ -0,0 +1,95 @@
<?php
namespace App\Controller\Manager;
use Cake\Event\Event;
use App\Controller\AppController;
use Cake\ORM\TableRegistry;
use PHPExcel;
use PHPExcel_Writer_Excel5;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xls;
class CalcDoorsStatisticsController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["calcDoors"]));
}
function index() {
$jsConfig = $this->getAppJsConfig("calcDoors", true);
$jsConfig['statistics']['data'] = $this->formatStatisticsPrice($jsConfig['statistics']['data']);
$jsConfig['statistics']['total'] = $this->formatStatisticsPrice([$jsConfig['statistics']['total']])[0];
$this->set('jsConfig', $jsConfig);
}
function getStatistics() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
$result['statistics'] = $this->getCalcDoorsStatisticsData($data);
$result['statistics']['data'] = $this->formatStatisticsPrice($result['statistics']['data']);
$result['statistics']['total'] = $this->formatStatisticsPrice([$result['statistics']['total']])[0];
echo $this->jsonSimpleResponse($result);
}
function exportClients($year) {
$statistics = $this->getCalcDoorsStatisticsData([
"select" => "clients",
"year" => $year
]);
$xlsData = [
[
"ФИО",
"Email",
"Телефон",
"Кол-во заказов",
"Сумма, руб.",
"Фактическая сумма, руб.",
]
];
foreach($statistics["data"] as $li) {
$xlsData[] = [
"name" => $li["name"],
"email" => $li["email"],
"phone" => $li["phone"],
"count" => $li["count"],
"price" => $li["price"],
"actual_price" => $li["actual_price"]
];
}
$xlsSubject = "Clients Export";
$objPHPExcel = new Spreadsheet();
$objPHPExcel->setActiveSheetIndex(0);
$objPHPExcel->getActiveSheet()->setTitle('База клиентов');
$objPHPExcel->getProperties()->setCreator($xlsSubject)
->setLastModifiedBy($xlsSubject)
->setTitle($xlsSubject);
$objPHPExcel->getActiveSheet()->fromArray($xlsData, null, 'A1');
$xlsWriter = new Xls($objPHPExcel);
$filename = "doors-clients-export_".date('d-m-Y')."_".date("H-i").".xls";
header('Content-type: application/vnd.ms-excel');
header('Content-disposition: attachment; filename="' . $filename . '"');
$xlsWriter->save('php://output');
ob_flush();
exit;
}
}
@@ -0,0 +1,219 @@
<?php
namespace App\Controller\Manager;
use Cake\Event\Event;
use App\Controller\AppController;
use Cake\ORM\TableRegistry;
use Cake\Core\Configure;
class CutOrdersController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["cut"]));
}
function index($clientId = null) {
$this->set('jsConfig', $this->getAppJsConfig("cut", true, null, $clientId));
}
function getOrders() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
$result['orders'] = $this->getOrdersList("cut", $data);
echo $this->jsonSimpleResponse($result);
}
function downloadOrderFiles($orderId = null) {
if (!empty($orderId)) {
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$order = $ordersTable->find()->where(['id' => $orderId, 'order_type' => 'order'])->first();
if (!empty($order)) {
$orderFileName = $order->order_num . '.zip';
$orderFileDir = ROOT . '/files/cut_orders/';
$fullOrderFileName = $orderFileDir.$orderFileName;
if (!is_dir($orderFileDir) || !file_exists($fullOrderFileName)) {
} else {
header('Content-Length: ' . filesize($fullOrderFileName));
header('Content-Type: ' . mime_content_type($fullOrderFileName));
header('Content-Disposition: attachment; filename="' . $orderFileName . '"');
header('Content-Transfer-Encoding: binary');
readfile($fullOrderFileName);
exit;
}
}
}
$this->redirect(['controller' => 'CutOrders', 'action' => 'index', 'prefix' => 'manager']);
}
function takeOrder() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data['id'])) {
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$order = $ordersTable->find()->where(['id' => $data['id'], 'order_type' => 'order'])->first();
if (!empty($order)) {
if (!empty($order->manager_id)) {
if ($order->manager_id == $this->Auth->user('id')) {
$result = [
'status' => 'error',
'warning' => 'Заказ уже взят Вами на исполнение'
];
} else {
$user = TableRegistry::getTableLocator()->get('Users')->get($order->manager_id);
$result = [
'status' => 'error',
'warning' => 'Заказ уже взят на исполнение пользователем ' . $user['name']
];
}
} else {
$result["manager_id"] = $order->manager_id = $this->Auth->user('id');
$order->taken_date = $result["taken_date"] = date('Y-m-d H:i:s');
$result["taken_date"] = $this->patchTimestampForJS($result['taken_date'], 0);
$ordersTable->save($order);
}
} else {
$result = [
'status' => 'error',
'error' => 'Заказ не найден'
];
}
}
echo $this->jsonSimpleResponse($result);
}
function adminDeleteOrder() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$order = $ordersTable->find()->where(['id' => $data['id'], 'order_type' => 'order'])->first();
if (!empty($order)) {
$ordersTable->delete($order);
@unlink(ROOT . '/files/cut_orders/'.$order->order_num . '.zip');
$ordersTable->revertOrderDbStatistics($order);
} else {
$result = [
'status' => 'error',
'warning' => 'Заказ не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для удаления данного заказа'
];
}
echo $this->jsonSimpleResponse($result);
}
function adminTakeOrder() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$order = $ordersTable->find()->where(['id' => $data['id'], 'order_type' => 'order'])->first();
if (!empty($order)) {
if (empty($data['manager_id'])) {
$data['manager_id'] = null;
} else {
if ($data['manager_id'] != $order->manager_id) {
$manager = TableRegistry::getTableLocator()->get('Users')->find()->where(['id' => $data['manager_id']])->first();
if (!empty($manager)) {
$ordersTable->resendOrderEmailNotify($order, $manager['email'], $this->config);
}
}
}
$result["manager_id"] = $order->manager_id = $data['manager_id'];
$order->taken_date = $result["taken_date"] = date('Y-m-d H:i:s');
$result["taken_date"] = $this->patchTimestampForJS($result['taken_date'], 0);
$ordersTable->save($order);
} else {
$result = [
'status' => 'error',
'warning' => 'Заказ не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для изменения данного заказа'
];
}
echo $this->jsonSimpleResponse($result);
}
function convertAstraToBazis() {
$data = $this->request->getData();
$result = [
'status' => 'error',
'errors' => []
];
if (!empty($data['file']['tmp_name'])) {
$importResult = TableRegistry::getTableLocator()->get('CutOrders')->importConvertAstraToBazisFile($data['file']);
if ($importResult['status'] == 'success') {
$result = $importResult;
} else {
$result['errors'] = $importResult['errors'];
}
} else {
$result['errors'][] = 'Ошибка загрузки файла.';
}
echo $this->jsonSimpleResponse($result);
}
function getConvertedAstraToBazisFile($filename, $name) {
$fileDir = ROOT . '/files/astra_to_bazis_convert/';
$fullFileName = $fileDir.$filename;
if (!is_dir($fileDir) || !file_exists($fullFileName)) {
} else {
header('Content-Length: ' . filesize($fullFileName));
header('Content-Type: ' . mime_content_type($fullFileName));
header('Content-Disposition: attachment; filename="' . $name . '"');
header('Content-Transfer-Encoding: binary');
readfile($fullFileName);
exit;
}
$this->redirect(['controller' => 'CutOrders', 'action' => 'index', 'prefix' => 'manager']);
}
}
@@ -0,0 +1,625 @@
<?php
namespace App\Controller\Manager;
use Cake\Event\Event;
use Cake\Core\Configure;
use Cake\ORM\TableRegistry;
use App\Controller\AppController;
use Cake\Utility\Hash;
class CutSettingsController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["cut"]));
}
function index() {
$this->set('jsConfig', $this->getAppJsConfig("cut", true));
}
function saveSettings() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data['action'])) {
$settingsTable = TableRegistry::getTableLocator()->get('CutSettings');
switch($data['action']) {
case 'saveGroup':
if (!empty($data['settings'])) {
foreach($data['settings'] as $groupId => $groupData) {
foreach($groupData as $s) {
if (!in_array($s['id'], ['lastUpdateDate', 'lastUpdateManagerId'])) {
$currentSetting = $settingsTable->find()->where([
'key' => $s['id'],
'group' => $groupId
])->first();
if (!empty($currentSetting)) {
$currentSetting->value = $s['value'];
$settingsTable->save($currentSetting);
} else {
$currentSetting = $settingsTable->newEntity([
'key' => $s['id'],
'value' => $s['value'],
'group' => $groupId
]);
$settingsTable->save($currentSetting);
}
}
}
}
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("cut", $data['settingsGroup'], $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
break;
}
}
echo $this->jsonSimpleResponse($result);
}
function addUpdateBorderDimension() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data)) {
$borderDimensionsTable = TableRegistry::getTableLocator()->get('CutBorderDimensions');
$settingsTable = TableRegistry::getTableLocator()->get('Settings');
$isOk = false;
if (empty($data['id'])) { //add
$borderDimension = $borderDimensionsTable->newEntity();
$borderDimension = $borderDimensionsTable->patchEntity($borderDimension, $data);
if ($borderDimensionsTable->save($borderDimension)) {
$isOk = true;
}
} else { //update
$borderDimension = $borderDimensionsTable->find()->where(['id' => $data['id']])->first();
if (!empty($borderDimension)) {
$currentIsActive = $borderDimension->is_active;
$borderDimensionsTable->patchEntity($borderDimension, $data);
if ($borderDimensionsTable->save($borderDimension)) {
$isOk = true;
if (!empty($currentIsActive) && empty($borderDimension['is_active'])) {
TableRegistry::getTableLocator()->get('CutBorderAvailableDimensions')->deleteAll([
'border_dimension_id' => $data['id']
]);
TableRegistry::getTableLocator()->get('CutBorders')->updateAll(
['is_active' => false],
['id not in (select distinct border_id from cut_border_available_dimensions)']
);
TableRegistry::getTableLocator()->get('CutSheets')->updateAll(
['default_border_id' => null],
['default_border_id not in (select distinct border_id from cut_border_available_dimensions)']
);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("cut", 'borders', $this->Auth->user('id'));
$result = array_merge($result, $updates);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("cut", 'sheets', $this->Auth->user('id'));
$result = array_merge($result, $updates);
$result['borders'] = $this->getCutBordersListForJS();
$result['sheets'] = $this->getCutSheetsListForJS();
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Толщина кромки не найдена'
];
}
}
if ($isOk) {
$result['borderDimensions'] = $this->getCutBorderDimensionsListForJS();
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("cut", 'borderDimensions', $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
echo $this->jsonSimpleResponse($result);
}
function deleteBorderDimension() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$borderAvailableDimensionsTable = TableRegistry::getTableLocator()->get('CutBorderAvailableDimensions');
$borderAvailableDimension = $borderAvailableDimensionsTable->find()->where(['border_dimension_id' => $data['id']])->count();
if (empty($borderAvailableDimension)) {
$borderDimensionsTable = TableRegistry::getTableLocator()->get('CutBorderDimensions');
$borderDimension = $borderDimensionsTable->find()->where(['id' => $data['id']])->first();
if (!empty($borderDimension)) {
$borderDimensionsTable->delete($borderDimension);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("cut", 'borderDimensions', $this->Auth->user('id'));
$result = array_merge($result, $updates);
} else {
$result = [
'status' => 'error',
'error' => 'Толщина кромки не найдена'
];
}
} else {
$result = [
'status' => 'warning',
'error' => 'Удаление невозможно, так как существуют кромки с данной толщиной.'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для удаления данный толщины кромки'
];
}
echo $this->jsonSimpleResponse($result);
}
function addUpdateSheetDimension() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data)) {
$sheetDimensionsTable = TableRegistry::getTableLocator()->get('CutSheetDimensions');
$settingsTable = TableRegistry::getTableLocator()->get('Settings');
$isOk = false;
if (empty($data['id'])) { //add
$sheetDimension = $sheetDimensionsTable->newEntity();
$sheetDimension = $sheetDimensionsTable->patchEntity($sheetDimension, $data);
if ($sheetDimensionsTable->save($sheetDimension)) {
$isOk = true;
}
} else { //update
$sheetDimension = $sheetDimensionsTable->find()->where(['id' => $data['id']])->first();
if (!empty($sheetDimension)) {
$currentIsActive = $sheetDimension->is_active;
$sheetDimensionsTable->patchEntity($sheetDimension, $data);
if ($sheetDimensionsTable->save($sheetDimension)) {
$isOk = true;
if (!empty($currentIsActive) && empty($sheetDimension['is_active'])) {
TableRegistry::getTableLocator()->get('CutSheets')->updateAll(
['is_active' => false],
['sheet_dimension_id' => $data['id']]
);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("cut", 'sheets', $this->Auth->user('id'));
$result = array_merge($result, $updates);
$result['sheets'] = $this->getCutSheetsListForJS();;
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Размер листа не найден'
];
}
}
if ($isOk) {
$result['sheetDimensions'] = $this->getCutSheetDimensionsListForJS();
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("cut", 'sheetDimensions', $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
echo $this->jsonSimpleResponse($result);
}
function deleteSheetDimension() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$sheetsTable = TableRegistry::getTableLocator()->get('CutSheets');
$sheets = $sheetsTable->find()->where(['sheet_dimension_id' => $data['id']])->count();
if (empty($sheets)) {
$sheetDimensionsTable = TableRegistry::getTableLocator()->get('CutSheetDimensions');
$sheetDimension = $sheetDimensionsTable->find()->where(['id' => $data['id']])->first();
if (!empty($sheetDimension)) {
$sheetDimensionsTable->delete($sheetDimension);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("cut", 'sheetDimensions', $this->Auth->user('id'));
$result = array_merge($result, $updates);
} else {
$result = [
'status' => 'error',
'error' => 'Размер листа не найден'
];
}
} else {
$result = [
'status' => 'warning',
'error' => 'Удаление невозможно, так как существуют ДСП с данным размером.'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для удаления данный толщины кромки'
];
}
echo $this->jsonSimpleResponse($result);
}
function addUpdateSheet() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data)) {
$sheetsTable = TableRegistry::getTableLocator()->get('CutSheets');
$isOk = false;
if (empty($data['default_border_id'])) {
$data['default_border_id'] = null;
}
unset($data['image']);
unset($data['thumb']);
unset($data['quantityIn1C']);
if (empty($data['id'])) { //add
$checkFor1C = $sheetsTable->find()->where(['sheetIdFor1C' => $data['sheetIdFor1C']])->first();
if (empty($checkFor1C)) {
$sheet = $sheetsTable->newEntity();
$sheet = $sheetsTable->patchEntity($sheet, $data);
if ($sheetsTable->save($sheet)) {
$isOk = true;
$result['newSheetId'] = $sheet->id;
}
} else {
$result = [
'status' => 'error',
'error' => 'ДСП с таким 1C ID уже есть в базе'
];
}
} else { //update
$checkFor1C = $sheetsTable->find()->where([
'sheetIdFor1C' => $data['sheetIdFor1C'],
'id !=' => $data['id']
])->first();
if (empty($checkFor1C)) {
$sheet = $sheetsTable->find()->where(['id' => $data['id']])->first();
if (!empty($sheet)) {
$sheetsTable->patchEntity($sheet, $data);
if ($sheetsTable->save($sheet)) {
$isOk = true;
//todo process images if required
}
} else {
$result = [
'status' => 'error',
'error' => 'ДСП не найдена'
];
}
} else {
$result = [
'status' => 'error',
'error' => 'ДСП с таким 1C ID уже есть в базе'
];
}
}
if ($isOk) {
$result['sheets'] = $this->getCutSheetsListForJS();
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("cut", 'sheets', $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
echo $this->jsonSimpleResponse($result);
}
function deleteSheet() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$sheetsTable = TableRegistry::getTableLocator()->get('CutSheets');
$sheet = $sheetsTable->find()->where(['id' => $data['id']])->first();
if (!empty($sheet)) {
$sheetsTable->delete($sheet);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("cut", 'sheets', $this->Auth->user('id'));
$result = array_merge($result, $updates);
if (!empty($sheet->image)) {
@unlink(WWW_ROOT . 'img' . DS . 'sheets' . DS . $sheet->image);
}
if (!empty($sheet->thumb)) {
@unlink(WWW_ROOT . 'img' . DS . 'sheets' . DS . $sheet->thumb);
}
} else {
$result = [
'status' => 'error',
'error' => 'ДСП не найдена'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для удаления данной ДСП'
];
}
echo $this->jsonSimpleResponse($result);
}
function deleteBorder() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$bordersTable = TableRegistry::getTableLocator()->get('CutBorders');
$border = $bordersTable->find()->where(['id' => $data['id']])->first();
if (!empty($border)) {
$bordersTable->delete($border);
TableRegistry::getTableLocator()->get('CutBorderAvailableDimensions')->deleteAll([
'border_id' => $data['id']
]);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("cut", 'borders', $this->Auth->user('id'));
$result = array_merge($result, $updates);
if (!empty($border->image)) {
@unlink(WWW_ROOT . 'img' . DS . 'borders' . DS . $border->image);
}
} else {
$result = [
'status' => 'error',
'error' => 'Кромка не найдена'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для удаления данной кромки'
];
}
echo $this->jsonSimpleResponse($result);
}
function addUpdateBorder() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data)) {
$bordersTable = TableRegistry::getTableLocator()->get('CutBorders');
$isOk = false;
unset($data['image']);
if (empty($data['id'])) { //add
$border = $bordersTable->newEntity();
$border = $bordersTable->patchEntity($border, $data);
if ($bordersTable->save($border)) {
$isOk = true;
$result['newBorderId'] = $border->id;
}
} else { //update
$border = $bordersTable->find()->where(['id' => $data['id']])->first();
if (!empty($border)) {
$bordersTable->patchEntity($border, $data);
if ($bordersTable->save($border)) {
$isOk = true;
//todo process images if required
}
} else {
$result = [
'status' => 'error',
'error' => 'Кромка не найдена'
];
}
}
if ($isOk) {
$availableDimensions = Hash::extract($data['available_dimensions'], '{n}.border_dimension_id');
TableRegistry::getTableLocator()->get('CutBorderAvailableDimensions')->deleteAll([
'border_id' => $border->id,
'border_dimension_id not in ' => $availableDimensions
]);
$result['borders'] = $this->getCutBordersListForJS();
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("cut", 'borders', $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
echo $this->jsonSimpleResponse($result);
}
function uploadImage() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data)) {
$isOk = false;
if (!empty($data['type']) && !empty($data['file']["name"]) && empty($data['file']["error"])) {
$fileInfo = getimagesize($data['file']["tmp_name"]);
$extension = image_type_to_extension($fileInfo[2]);
$extension = str_replace("jpeg", "jpg", $extension);
switch($data['type']) {
case "sheetImage":
case "sheetThumb":
$sheetsTable = TableRegistry::getTableLocator()->get('CutSheets');
$sheet = $sheetsTable->find()->where(['id' => $data['id']])->first();
$subFolder = ($data['type'] == "sheetImage" ? "sheets" : "sheet_thumbs");
if (!empty($sheet)) {
$uploadDir = WWW_ROOT . 'img' . DS . $subFolder . DS;
if (!file_exists($uploadDir) && !is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
if (($data['type'] == "sheetImage") && !empty($sheet->image)) {
@unlink($uploadDir.$sheet->image);
}
if (($data['type'] == "sheetThumb") && !empty($sheet->thumb)) {
@unlink($uploadDir.$sheet->thumb);
}
$tmpFileName = sha1(sha1($data['file']["name"] . time()) . time()) . $extension;
if (move_uploaded_file($data['file']["tmp_name"], $uploadDir . $tmpFileName)) {
if ($data['type'] == "sheetImage") {
$sheet->image = $tmpFileName;
} else {
$sheet->thumb = $tmpFileName;
}
$sheetsTable->patchEntity($sheet, $data);
if ($sheetsTable->save($sheet)) {
$isOk = true;
$result['image'] = $tmpFileName;
$updateGroup = "sheets";
}
};
} else {
$result = [
'status' => 'error',
'error' => 'ДСП не найдена'
];
}
break;
case "borderImage":
$bordersTable = TableRegistry::getTableLocator()->get('CutBorders');
$border = $bordersTable->find()->where(['id' => $data['id']])->first();
if (!empty($border)) {
$uploadDir = WWW_ROOT . 'img' . DS . 'borders' . DS;
if (!file_exists($uploadDir) && !is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
if (!empty($border->image)) {
@unlink($uploadDir.$border->image);
}
$tmpFileName = sha1(sha1($data['file']["name"] . time()) . time()) . $extension;
if (move_uploaded_file($data['file']["tmp_name"], $uploadDir . $tmpFileName)) {
$border->image = $tmpFileName;
$bordersTable->patchEntity($border, $data);
if ($bordersTable->save($border)) {
$isOk = true;
$result['image'] = $tmpFileName;
$updateGroup = "borders";
}
};
} else {
$result = [
'status' => 'error',
'error' => 'Кромка не найдена'
];
}
break;
}
}
if ($isOk && (!empty($updateGroup))) {
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("cut", $updateGroup, $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
echo $this->jsonSimpleResponse($result);
}
}
@@ -0,0 +1,90 @@
<?php
namespace App\Controller\Manager;
use Cake\Event\Event;
use App\Controller\AppController;
use Cake\ORM\TableRegistry;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xls;
class CutStatisticsController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["cut"]));
}
function index() {
$this->set('jsConfig', $this->getAppJsConfig("cut", true));
}
function getStatistics() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
$result['statistics'] = $this->getCutStatisticsData($data);
$result['statistics']['data'] = $this->formatStatisticsPrice($result['statistics']['data']);
$result['statistics']['total'] = $this->formatStatisticsPrice([$result['statistics']['total']])[0];
$result['statistics']['totalByMonth'] = $this->formatStatisticsPrice([$result['statistics']['totalByMonth']])[0];
echo $this->jsonSimpleResponse($result);
}
function exportClients($year) {
$statistics = $this->getCutStatisticsData([
"select" => "clients",
"year" => $year
]);
$xlsData = [
[
"ФИО",
"Email",
"Телефон",
"Кол-во заказов",
"Сумма, руб."
]
];
foreach($statistics["data"] as $li) {
$xlsData[] = [
"name" => $li["name"],
"email" => $li["email"],
"phone" => $li["phone"],
"count" => $li["count"],
"price" => $li["price"]
];
}
$xlsSubject = "Clients Export";
$objPHPExcel = new Spreadsheet();
$objPHPExcel->setActiveSheetIndex(0);
$objPHPExcel->getActiveSheet()->setTitle('База клиентов');
$objPHPExcel->getProperties()->setCreator($xlsSubject)
->setLastModifiedBy($xlsSubject)
->setTitle($xlsSubject);
$objPHPExcel->getActiveSheet()->fromArray($xlsData, null, 'A1');
$xlsWriter = new Xls($objPHPExcel);
$filename = "cut-clients-export_".date('d-m-Y')."_".date("H-i").".xls";
header('Content-type: application/vnd.ms-excel');
header('Content-disposition: attachment; filename="' . $filename . '"');
$xlsWriter->save('php://output');
ob_flush();
exit;
}
}
@@ -0,0 +1,49 @@
<?php
namespace App\Controller\Manager;
use Cake\Core\Configure;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
use App\Controller\AppController;
class DeliveryJournalController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["deliveryJournal"]));
}
public function beforeRender(Event $event)
{
}
public function index() {
$jsConfig = $this->getAppJsConfig("deliveryJournal", true);
$data = $this->request->getData();
if (!empty($data['action'])) {
$result = [
'status' => 'error',
'errors' => []
];
switch($data['action']) {
}
echo $this->jsonSimpleResponse($result);
} else {
$this->set('jsConfig', $jsConfig);
}
}
}
@@ -0,0 +1,379 @@
<?php
namespace App\Controller\Manager;
use Cake\Core\Configure;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
use App\Controller\AppController;
class EmailSubscribesController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["emailSubscribes"]));
}
public function beforeRender(Event $event)
{
}
public function index() {
$jsConfig = $this->getAppJsConfig("emailSubscribes", true);
$data = $this->request->getData();
if (!empty($data['action'])) {
$result = [
'status' => 'error',
'errors' => []
];
$subscribesTable = TableRegistry::getTableLocator()->get("EmailSubscribes");
$subscribesRecipientsTable = TableRegistry::getTableLocator()->get("EmailSubscribeRecipients");
$subscribesAttachmentsTable = TableRegistry::getTableLocator()->get("EmailSubscribeAttachments");
switch($data['action']) {
case "getSubscribes":
$subscribeId = (int) $data['subscribeId'];
if (!empty($subscribeId)) {
} else {
$result['subscribes'] = $this->getSubscribesList("EmailSubscribes", $data);
}
$result['status'] = "success";
break;
case "setSubscribeStatus":
$subscribeId = (int) $data['subscribeId'];
$newStatus = $data['newStatus'];
if (!empty($subscribeId) && !empty($newStatus)) {
$subscribe = $subscribesTable->find('all')->where(['id' => $subscribeId])->first();
if (!empty($subscribe)) {
if ($this->isSubscribeUpdateAllowed($subscribe['status'])) {
$isStatusChangeAllowed = true;
if ($newStatus == "deleted") {
if (($subscribe['createdBy'] != $this->Auth->user('id')) && ($this->Auth->user('role') != 'admin')) {
$isStatusChangeAllowed = false;
}
}
if ($isStatusChangeAllowed) {
$subscribe->status = $newStatus;
if ($newStatus == "queued") {
$subscribe->queuedBy = $this->Auth->user('id');
$subscribe->stoppedBy = null;
}
if ($newStatus == "stopped") {
$subscribe->stoppedBy = $this->Auth->user('id');
}
if ($subscribesTable->save($subscribe)) {
$result['status'] = "success";
}
} else {
$result['errors'][] = 'Недостаточно прав для изменения статуса рассылки';
}
} else {
$result['errors'][] = 'Редактирование активной или завершенной рассылки недоступно';
}
} else {
$result['errors'][] = 'Рассылка не найдена!';
}
}
break;
case "addSubscribe":
if (!empty($data['subject'])) {
$data['created'] = date('Y-m-d H:i:s');
$data['createdBy'] = $this->Auth->user('id');
$data['status'] = "stopped";
$subscribe = $subscribesTable->newEntity();
$subscribe = $subscribesTable->patchEntity($subscribe, $data);
if ($subscribesTable->save($subscribe)) {
$result['subscribe'] = $this->getSubscribeData("EmailSubscribes", $subscribe->id);
$result['status'] = "success";
}
}
break;
case "updateSubscribe":
if (!empty($data['id'])) {
$subscribe = $this->getSubscribeData("EmailSubscribes", $data['id']);
if (!empty($subscribe)) {
if ($this->isSubscribeUpdateAllowed($subscribe['status'])) {
unset($data['created']);
unset($data['createdBy']);
unset($data['queuedBy']);
unset($data['stoppedBy']);
unset($data['errorsCount']);
unset($data['sentCount']);
unset($data['startTime']);
unset($data['status']);
unset($data['successCount']);
$data['recipientsCount'] = count($data['email_subscribe_recipients']);
if (!empty($data['email_subscribe_recipients'])) {
foreach ($data['email_subscribe_recipients'] as $recipientKey => $recipient) {
unset($data['email_subscribe_recipients'][$recipientKey]['sendStatus']);
unset($data['email_subscribe_recipients'][$recipientKey]['statusMessage']);
unset($data['email_subscribe_recipients'][$recipientKey]['sendDate']);
}
}
$subscribe = $subscribesTable->get($data['id']);
$subscribe = $subscribesTable->patchEntity($subscribe, $data);
if ($subscribesTable->save($subscribe)) {
if (!empty($data['recipientsToDelete'])) {
foreach ($data['recipientsToDelete'] as $recipientToDelete) {
$subscribesRecipientsTable->deleteAll([
'subscribeId' => $data['id'],
'id' => $recipientToDelete
]);
}
}
if (!empty($data['attachmentsToDelete'])) {
foreach ($data['attachmentsToDelete'] as $attachmentToDelete) {
$currentAttachment = $subscribesAttachmentsTable->find()->where([
'subscribeId' => $data['id'],
'id' => $attachmentToDelete
])->first();
if (!empty($currentAttachment)) {
if (!empty($currentAttachment['filename'])) {
@unlink(ROOT . DS . 'files' . DS . 'email_subscribes' . DS . $currentAttachment['filename']);
}
$subscribesAttachmentsTable->delete($currentAttachment);
}
$subscribesAttachmentsTable->deleteAll([
'subscribeId' => $data['id'],
'id' => $attachmentToDelete
]);
}
}
$result['subscribe'] = $subscribe;
$result['status'] = "success";
}
$result['status'] = "success";
} else {
$result['errors'][] = 'Редактирование активной или завершенной рассылки недоступно';
}
} else {
$result['errors'][] = 'Рассылка не найдена!';
}
}
break;
case "getSubscribeData":
if (!empty($data['subscribeId'])) {
$subscribe = $this->getSubscribeData("EmailSubscribes", $data['subscribeId']);
if (!empty($subscribe)) {
$result['subscribe'] = $subscribe;
$result['status'] = "success";
} else {
$result['errors'][] = 'Рассылка не найдена!';
}
}
break;
case "importSubscribeRecipients":
$source = !empty($data['file']['tmp_name']) ? $data['file']['tmp_name'] : '';
$subscribeId = (!empty($data['subscribeId']) ? (int) $data['subscribeId'] : null);
if (!empty($source) && !empty($subscribeId)) {
$importResult = $subscribesTable->importSubscribesList($source);
$result['importNewSubscribersCount'] = 0;
$result['importNewSubscribersCountTotal'] = 0;
if (count($importResult['list']) > 0) {
$result['status'] = 'success';
$result['importNewSubscribersCountTotal'] = count($importResult['list']);
$subscribers = (!empty($data['email_subscribe_recipients']) ? $data['email_subscribe_recipients'] : []);
if (!empty($subscribers)) {
foreach($subscribers as $subscriber) {
unset($importResult['list'][$subscriber['email']]); //cleanup duplicates
}
}
$result['importNewSubscribersCount'] = count($importResult['list']);
if (!empty($importResult['list'])) {
foreach($importResult['list'] as $newSubscriberEmail => $newSubscriberName) {
$subscribers[] = [
"name" => $newSubscriberName,
"email" => $newSubscriberEmail,
"sendStatus" => "queued"
];
}
}
usort($subscribers, function($a, $b) {
return strcmp($a['name'], $b['name']);
});
foreach($subscribers as $key => $subscriber) { //fix "null" due to incorrect upload field values
foreach($subscriber as $subKey => $value) {
if ($value == "null") {
$subscriber[$subKey] = null;
}
}
$subscribers[$key] = $subscriber;
}
$result['importSubscribers'] = $subscribers;
} else {
$result['errors'][] = 'Неверный формат файла или список получателей пуст.';
}
$result['incorrectList'] = $importResult['incorrectList'];
$result['importNewSubscribersCountTotal'] += count($importResult['incorrectList']);
if (count($result['incorrectList'])) {
$result['status'] = 'success';
}
} else {
$result['errors'][] = 'Ошибка загрузки файла.';
}
//debug($result);exit;
break;
case "importSubscribeAttachments":
$subscribeId = (!empty($data['subscribeId']) ? (int) $data['subscribeId'] : null);
if (!empty($data['file']) && !empty($subscribeId)) {
$importResult = $subscribesTable->importSubscribeAttachments($data['file']);
if (count($importResult['list']) > 0) {
$result['status'] = 'success';
$result['importNewAttachmentsCountTotal'] = count($importResult['list']);
$attachments = (!empty($data['email_subscribe_attachments']) ? $data['email_subscribe_attachments'] : []);
if (!empty($attachments)) {
foreach($attachments as $attachment) {
$b64 = base64_encode($attachment['name']);
@unlink(ROOT . DS . 'files' . DS . 'email_subscribes' . DS . $importResult['list'][$b64]['filename']);
unset($importResult['list'][$b64]); //cleanup duplicates
}
}
$result['importNewAttachmentsCount'] = count($importResult['list']);
if (!empty($importResult['list'])) {
foreach($importResult['list'] as $importFileName => $importFile) {
$attachments[] = [
"name" => $importFile['name'],
"filename" => $importFile['filename'],
"filesize" => $importFile['filesize'],
"mime" => $importFile['mime']
];
}
}
usort($attachments, function($a, $b) {
return strcmp($a['name'], $b['name']);
});
foreach($attachments as $key => $attachment) { //fix "null" due to incorrect upload field values
foreach($attachment as $subKey => $value) {
if ($value == "null") {
$attachment[$subKey] = null;
}
}
$attachments[$key] = $attachment;
}
$result['importAttachments'] = $attachments;
} else {
$result['errors'][] = 'Ошибка загрузки файла.';
}
} else {
$result['errors'][] = 'Ошибка загрузки файла.';
}
break;
case "uploadSubscribeImage":
$subscribeId = (!empty($data['subscribeId']) ? (int) $data['subscribeId'] : null);
if (!empty($data['file']) && !empty($subscribeId)) {
$importResult = $subscribesTable->uploadSubscribeImage($data['file']);
if (count($importResult['list']) > 0) {
$result['status'] = 'success';
$result['image'] = $importResult['list'][0];
} else {
$result['errors'][] = 'Ошибка загрузки файла.';
}
} else {
$result['errors'][] = 'Ошибка загрузки файла.';
}
break;
}
echo $this->jsonSimpleResponse($result);
} else {
$this->set('jsConfig', $jsConfig);
}
}
public function getSubscribeAttachment($filename, $name) {
if (!empty($filename)) {
$filename = ROOT . DS . 'files' . DS . 'email_subscribes' . DS . $filename;
if (file_exists($filename)) {
header('Content-Length: ' . filesize($filename));
header('Content-Type: ' . mime_content_type($filename));
header('Content-Disposition: attachment; filename="' . $name . '"');
header('Content-Transfer-Encoding: binary');
readfile($filename);
exit;
}
}
$this->redirect(['controller' => 'EmailSubscribes', 'action' => 'index', 'prefix' => 'manager']);
exit;
}
public function getSubscribeAttachmentImage($filename) {
if (!empty($filename)) {
$filename = ROOT . DS . 'files' . DS . 'email_subscribes' . DS . $filename;
if (file_exists($filename)) {
header('Content-Length: ' . filesize($filename));
header('Content-Type: ' . mime_content_type($filename));
header('Content-Transfer-Encoding: binary');
header("Cache-control: public");
header("Expires: " . gmdate("D, d M Y H:i:s", time() + 60*60*24*365) . " GMT");
readfile($filename);
exit;
}
}
$this->redirect(['controller' => 'EmailSubscribes', 'action' => 'index', 'prefix' => 'manager']);
exit;
}
}
@@ -0,0 +1,219 @@
<?php
namespace App\Controller\Manager;
use Cake\Event\Event;
use App\Controller\AppController;
use Cake\ORM\TableRegistry;
use Cake\Core\Configure;
class FasadOrdersController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["fasad"]));
}
function index($clientId = null) {
$this->set('jsConfig', $this->getAppJsConfig("fasad", true, null, $clientId));
}
function getOrders() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
$result['orders'] = $this->getOrdersList("fasad", $data);
echo $this->jsonSimpleResponse($result);
}
function downloadOrderFiles($orderId = null) {
if (!empty($orderId)) {
$ordersTable = TableRegistry::getTableLocator()->get('FasadOrders');
$order = $ordersTable->find()->where(['id' => $orderId, 'order_type' => 'order'])->first();
if (!empty($order)) {
$orderFileName = $order->order_num . '.zip';
$orderFileDir = ROOT . '/files/fasad_orders/';
$fullOrderFileName = $orderFileDir.$orderFileName;
if (!is_dir($orderFileDir) || !file_exists($fullOrderFileName)) {
} else {
header('Content-Length: ' . filesize($fullOrderFileName));
header('Content-Type: ' . mime_content_type($fullOrderFileName));
header('Content-Disposition: attachment; filename="' . $orderFileName . '"');
header('Content-Transfer-Encoding: binary');
readfile($fullOrderFileName);
exit;
}
}
}
$this->redirect(['controller' => 'FasadOrders', 'action' => 'index', 'prefix' => 'manager']);
}
function takeOrder() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data['id'])) {
$ordersTable = TableRegistry::getTableLocator()->get('FasadOrders');
$order = $ordersTable->find()->where(['id' => $data['id'], 'order_type' => 'order'])->first();
if (!empty($order)) {
if (!empty($order->manager_id)) {
if ($order->manager_id == $this->Auth->user('id')) {
$result = [
'status' => 'error',
'warning' => 'Заказ уже взят Вами на исполнение'
];
} else {
$user = TableRegistry::getTableLocator()->get('Users')->get($order->manager_id);
$result = [
'status' => 'error',
'warning' => 'Заказ уже взят на исполнение пользователем ' . $user['name']
];
}
} else {
$result["manager_id"] = $order->manager_id = $this->Auth->user('id');
$order->taken_date = $result["taken_date"] = date('Y-m-d H:i:s');
$result["taken_date"] = $this->patchTimestampForJS($result['taken_date'], 0);
$ordersTable->save($order);
}
} else {
$result = [
'status' => 'error',
'error' => 'Заказ не найден'
];
}
}
echo $this->jsonSimpleResponse($result);
}
function adminDeleteOrder() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$ordersTable = TableRegistry::getTableLocator()->get('FasadOrders');
$order = $ordersTable->find()->where(['id' => $data['id'], 'order_type' => 'order'])->first();
if (!empty($order)) {
$ordersTable->delete($order);
@unlink(ROOT . '/files/fasad_orders/'.$order->order_num . '.zip');
$ordersTable->revertOrderDbStatistics($order);
} else {
$result = [
'status' => 'error',
'warning' => 'Заказ не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для удаления данного заказа'
];
}
echo $this->jsonSimpleResponse($result);
}
function adminTakeOrder() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$ordersTable = TableRegistry::getTableLocator()->get('FasadOrders');
$order = $ordersTable->find()->where(['id' => $data['id'], 'order_type' => 'order'])->first();
if (!empty($order)) {
if (empty($data['manager_id'])) {
$data['manager_id'] = null;
} else {
if ($data['manager_id'] != $order->manager_id) {
$manager = TableRegistry::getTableLocator()->get('Users')->find()->where(['id' => $data['manager_id']])->first();
if (!empty($manager)) {
$ordersTable->resendOrderEmailNotify($order, $manager['email'], $this->config);
}
}
}
$result["manager_id"] = $order->manager_id = $data['manager_id'];
$order->taken_date = $result["taken_date"] = date('Y-m-d H:i:s');
$result["taken_date"] = $this->patchTimestampForJS($result['taken_date'], 0);
$ordersTable->save($order);
} else {
$result = [
'status' => 'error',
'warning' => 'Заказ не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для изменения данного заказа'
];
}
echo $this->jsonSimpleResponse($result);
}
function convertAstraToBazis() {
$data = $this->request->getData();
$result = [
'status' => 'error',
'errors' => []
];
if (!empty($data['file']['tmp_name'])) {
$importResult = TableRegistry::getTableLocator()->get('FasadOrders')->importConvertAstraToBazisFile($data['file']);
if ($importResult['status'] == 'success') {
$result = $importResult;
} else {
$result['errors'] = $importResult['errors'];
}
} else {
$result['errors'][] = 'Ошибка загрузки файла.';
}
echo $this->jsonSimpleResponse($result);
}
function getConvertedAstraToBazisFile($filename, $name) {
$fileDir = ROOT . '/files/astra_to_bazis_convert/';
$fullFileName = $fileDir.$filename;
if (!is_dir($fileDir) || !file_exists($fullFileName)) {
} else {
header('Content-Length: ' . filesize($fullFileName));
header('Content-Type: ' . mime_content_type($fullFileName));
header('Content-Disposition: attachment; filename="' . $name . '"');
header('Content-Transfer-Encoding: binary');
readfile($fullFileName);
exit;
}
$this->redirect(['controller' => 'FasadOrders', 'action' => 'index', 'prefix' => 'manager']);
}
}
@@ -0,0 +1,625 @@
<?php
namespace App\Controller\Manager;
use Cake\Event\Event;
use Cake\Core\Configure;
use Cake\ORM\TableRegistry;
use App\Controller\AppController;
use Cake\Utility\Hash;
class FasadSettingsController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["fasad"]));
}
function index() {
$this->set('jsConfig', $this->getAppJsConfig("fasad", true));
}
function saveSettings() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data['action'])) {
$settingsTable = TableRegistry::getTableLocator()->get('FasadSettings');
switch($data['action']) {
case 'saveGroup':
if (!empty($data['settings'])) {
foreach($data['settings'] as $groupId => $groupData) {
foreach($groupData as $s) {
if (!in_array($s['id'], ['lastUpdateDate', 'lastUpdateManagerId'])) {
$currentSetting = $settingsTable->find()->where([
'key' => $s['id'],
'group' => $groupId
])->first();
if (!empty($currentSetting)) {
$currentSetting->value = $s['value'];
$settingsTable->save($currentSetting);
} else {
$currentSetting = $settingsTable->newEntity([
'key' => $s['id'],
'value' => $s['value'],
'group' => $groupId
]);
$settingsTable->save($currentSetting);
}
}
}
}
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("fasad", $data['settingsGroup'], $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
break;
}
}
echo $this->jsonSimpleResponse($result);
}
function addUpdateBorderDimension() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data)) {
$borderDimensionsTable = TableRegistry::getTableLocator()->get('FasadBorderDimensions');
$settingsTable = TableRegistry::getTableLocator()->get('Settings');
$isOk = false;
if (empty($data['id'])) { //add
$borderDimension = $borderDimensionsTable->newEntity();
$borderDimension = $borderDimensionsTable->patchEntity($borderDimension, $data);
if ($borderDimensionsTable->save($borderDimension)) {
$isOk = true;
}
} else { //update
$borderDimension = $borderDimensionsTable->find()->where(['id' => $data['id']])->first();
if (!empty($borderDimension)) {
$currentIsActive = $borderDimension->is_active;
$borderDimensionsTable->patchEntity($borderDimension, $data);
if ($borderDimensionsTable->save($borderDimension)) {
$isOk = true;
if (!empty($currentIsActive) && empty($borderDimension['is_active'])) {
TableRegistry::getTableLocator()->get('FasadBorderAvailableDimensions')->deleteAll([
'border_dimension_id' => $data['id']
]);
TableRegistry::getTableLocator()->get('FasadBorders')->updateAll(
['is_active' => false],
['id not in (select distinct border_id from fasad_border_available_dimensions)']
);
TableRegistry::getTableLocator()->get('FasadSheets')->updateAll(
['default_border_id' => null],
['default_border_id not in (select distinct border_id from fasad_border_available_dimensions)']
);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("fasad", 'borders', $this->Auth->user('id'));
$result = array_merge($result, $updates);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("fasad", 'sheets', $this->Auth->user('id'));
$result = array_merge($result, $updates);
$result['borders'] = $this->getFasadBordersListForJS();
$result['sheets'] = $this->getFasadSheetsListForJS();
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Толщина кромки не найдена'
];
}
}
if ($isOk) {
$result['borderDimensions'] = $this->getFasadBorderDimensionsListForJS();
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("fasad", 'borderDimensions', $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
echo $this->jsonSimpleResponse($result);
}
function deleteBorderDimension() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$borderAvailableDimensionsTable = TableRegistry::getTableLocator()->get('FasadBorderAvailableDimensions');
$borderAvailableDimension = $borderAvailableDimensionsTable->find()->where(['border_dimension_id' => $data['id']])->count();
if (empty($borderAvailableDimension)) {
$borderDimensionsTable = TableRegistry::getTableLocator()->get('FasadBorderDimensions');
$borderDimension = $borderDimensionsTable->find()->where(['id' => $data['id']])->first();
if (!empty($borderDimension)) {
$borderDimensionsTable->delete($borderDimension);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("fasad", 'borderDimensions', $this->Auth->user('id'));
$result = array_merge($result, $updates);
} else {
$result = [
'status' => 'error',
'error' => 'Толщина кромки не найдена'
];
}
} else {
$result = [
'status' => 'warning',
'error' => 'Удаление невозможно, так как существуют кромки с данной толщиной.'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для удаления данный толщины кромки'
];
}
echo $this->jsonSimpleResponse($result);
}
function addUpdateSheetDimension() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data)) {
$sheetDimensionsTable = TableRegistry::getTableLocator()->get('FasadSheetDimensions');
$settingsTable = TableRegistry::getTableLocator()->get('Settings');
$isOk = false;
if (empty($data['id'])) { //add
$sheetDimension = $sheetDimensionsTable->newEntity();
$sheetDimension = $sheetDimensionsTable->patchEntity($sheetDimension, $data);
if ($sheetDimensionsTable->save($sheetDimension)) {
$isOk = true;
}
} else { //update
$sheetDimension = $sheetDimensionsTable->find()->where(['id' => $data['id']])->first();
if (!empty($sheetDimension)) {
$currentIsActive = $sheetDimension->is_active;
$sheetDimensionsTable->patchEntity($sheetDimension, $data);
if ($sheetDimensionsTable->save($sheetDimension)) {
$isOk = true;
if (!empty($currentIsActive) && empty($sheetDimension['is_active'])) {
TableRegistry::getTableLocator()->get('FasadSheets')->updateAll(
['is_active' => false],
['sheet_dimension_id' => $data['id']]
);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("fasad", 'sheets', $this->Auth->user('id'));
$result = array_merge($result, $updates);
$result['sheets'] = $this->getFasadSheetsListForJS();;
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Размер листа не найден'
];
}
}
if ($isOk) {
$result['sheetDimensions'] = $this->getFasadSheetDimensionsListForJS();
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("fasad", 'sheetDimensions', $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
echo $this->jsonSimpleResponse($result);
}
function deleteSheetDimension() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$sheetsTable = TableRegistry::getTableLocator()->get('FasadSheets');
$sheets = $sheetsTable->find()->where(['sheet_dimension_id' => $data['id']])->count();
if (empty($sheets)) {
$sheetDimensionsTable = TableRegistry::getTableLocator()->get('FasadSheetDimensions');
$sheetDimension = $sheetDimensionsTable->find()->where(['id' => $data['id']])->first();
if (!empty($sheetDimension)) {
$sheetDimensionsTable->delete($sheetDimension);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("fasad", 'sheetDimensions', $this->Auth->user('id'));
$result = array_merge($result, $updates);
} else {
$result = [
'status' => 'error',
'error' => 'Размер листа не найден'
];
}
} else {
$result = [
'status' => 'warning',
'error' => 'Удаление невозможно, так как существуют ДСП с данным размером.'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для удаления данный толщины кромки'
];
}
echo $this->jsonSimpleResponse($result);
}
function addUpdateSheet() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data)) {
$sheetsTable = TableRegistry::getTableLocator()->get('FasadSheets');
$isOk = false;
if (empty($data['default_border_id'])) {
$data['default_border_id'] = null;
}
unset($data['image']);
unset($data['thumb']);
unset($data['quantityIn1C']);
if (empty($data['id'])) { //add
$checkFor1C = $sheetsTable->find()->where(['sheetIdFor1C' => $data['sheetIdFor1C']])->first();
if (empty($checkFor1C)) {
$sheet = $sheetsTable->newEntity();
$sheet = $sheetsTable->patchEntity($sheet, $data);
if ($sheetsTable->save($sheet)) {
$isOk = true;
$result['newSheetId'] = $sheet->id;
}
} else {
$result = [
'status' => 'error',
'error' => 'ДСП с таким 1C ID уже есть в базе'
];
}
} else { //update
$checkFor1C = $sheetsTable->find()->where([
'sheetIdFor1C' => $data['sheetIdFor1C'],
'id !=' => $data['id']
])->first();
if (empty($checkFor1C)) {
$sheet = $sheetsTable->find()->where(['id' => $data['id']])->first();
if (!empty($sheet)) {
$sheetsTable->patchEntity($sheet, $data);
if ($sheetsTable->save($sheet)) {
$isOk = true;
//todo process images if required
}
} else {
$result = [
'status' => 'error',
'error' => 'ДСП не найдена'
];
}
} else {
$result = [
'status' => 'error',
'error' => 'ДСП с таким 1C ID уже есть в базе'
];
}
}
if ($isOk) {
$result['sheets'] = $this->getFasadSheetsListForJS();
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("fasad", 'sheets', $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
echo $this->jsonSimpleResponse($result);
}
function deleteSheet() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$sheetsTable = TableRegistry::getTableLocator()->get('FasadSheets');
$sheet = $sheetsTable->find()->where(['id' => $data['id']])->first();
if (!empty($sheet)) {
$sheetsTable->delete($sheet);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("fasad", 'sheets', $this->Auth->user('id'));
$result = array_merge($result, $updates);
if (!empty($sheet->image)) {
@unlink(WWW_ROOT . 'img' . DS . 'sheets' . DS . $sheet->image);
}
if (!empty($sheet->thumb)) {
@unlink(WWW_ROOT . 'img' . DS . 'sheets' . DS . $sheet->thumb);
}
} else {
$result = [
'status' => 'error',
'error' => 'ДСП не найдена'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для удаления данной ДСП'
];
}
echo $this->jsonSimpleResponse($result);
}
function deleteBorder() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$bordersTable = TableRegistry::getTableLocator()->get('FasadBorders');
$border = $bordersTable->find()->where(['id' => $data['id']])->first();
if (!empty($border)) {
$bordersTable->delete($border);
TableRegistry::getTableLocator()->get('FasadBorderAvailableDimensions')->deleteAll([
'border_id' => $data['id']
]);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("fasad", 'borders', $this->Auth->user('id'));
$result = array_merge($result, $updates);
if (!empty($border->image)) {
@unlink(WWW_ROOT . 'img' . DS . 'borders' . DS . $border->image);
}
} else {
$result = [
'status' => 'error',
'error' => 'Кромка не найдена'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для удаления данной кромки'
];
}
echo $this->jsonSimpleResponse($result);
}
function addUpdateBorder() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data)) {
$bordersTable = TableRegistry::getTableLocator()->get('FasadBorders');
$isOk = false;
unset($data['image']);
if (empty($data['id'])) { //add
$border = $bordersTable->newEntity();
$border = $bordersTable->patchEntity($border, $data);
if ($bordersTable->save($border)) {
$isOk = true;
$result['newBorderId'] = $border->id;
}
} else { //update
$border = $bordersTable->find()->where(['id' => $data['id']])->first();
if (!empty($border)) {
$bordersTable->patchEntity($border, $data);
if ($bordersTable->save($border)) {
$isOk = true;
//todo process images if required
}
} else {
$result = [
'status' => 'error',
'error' => 'Кромка не найдена'
];
}
}
if ($isOk) {
$availableDimensions = Hash::extract($data['available_dimensions'], '{n}.border_dimension_id');
TableRegistry::getTableLocator()->get('FasadBorderAvailableDimensions')->deleteAll([
'border_id' => $border->id,
'border_dimension_id not in ' => $availableDimensions
]);
$result['borders'] = $this->getFasadBordersListForJS();
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("fasad", 'borders', $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
echo $this->jsonSimpleResponse($result);
}
function uploadImage() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data)) {
$isOk = false;
if (!empty($data['type']) && !empty($data['file']["name"]) && empty($data['file']["error"])) {
$fileInfo = getimagesize($data['file']["tmp_name"]);
$extension = image_type_to_extension($fileInfo[2]);
$extension = str_replace("jpeg", "jpg", $extension);
switch($data['type']) {
case "sheetImage":
case "sheetThumb":
$sheetsTable = TableRegistry::getTableLocator()->get('FasadSheets');
$sheet = $sheetsTable->find()->where(['id' => $data['id']])->first();
$subFolder = ($data['type'] == "sheetImage" ? "fasad_sheets" : "fasad_sheet_thumbs");
if (!empty($sheet)) {
$uploadDir = WWW_ROOT . 'img' . DS . $subFolder . DS;
if (!file_exists($uploadDir) && !is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
if (($data['type'] == "sheetImage") && !empty($sheet->image)) {
@unlink($uploadDir.$sheet->image);
}
if (($data['type'] == "sheetThumb") && !empty($sheet->thumb)) {
@unlink($uploadDir.$sheet->thumb);
}
$tmpFileName = sha1(sha1($data['file']["name"] . time()) . time()) . $extension;
if (move_uploaded_file($data['file']["tmp_name"], $uploadDir . $tmpFileName)) {
if ($data['type'] == "sheetImage") {
$sheet->image = $tmpFileName;
} else {
$sheet->thumb = $tmpFileName;
}
$sheetsTable->patchEntity($sheet, $data);
if ($sheetsTable->save($sheet)) {
$isOk = true;
$result['image'] = $tmpFileName;
$updateGroup = "sheets";
}
};
} else {
$result = [
'status' => 'error',
'error' => 'ДСП не найдена'
];
}
break;
case "borderImage":
$bordersTable = TableRegistry::getTableLocator()->get('FasadBorders');
$border = $bordersTable->find()->where(['id' => $data['id']])->first();
if (!empty($border)) {
$uploadDir = WWW_ROOT . 'img' . DS . 'fasad_borders' . DS;
if (!file_exists($uploadDir) && !is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
if (!empty($border->image)) {
@unlink($uploadDir.$border->image);
}
$tmpFileName = sha1(sha1($data['file']["name"] . time()) . time()) . $extension;
if (move_uploaded_file($data['file']["tmp_name"], $uploadDir . $tmpFileName)) {
$border->image = $tmpFileName;
$bordersTable->patchEntity($border, $data);
if ($bordersTable->save($border)) {
$isOk = true;
$result['image'] = $tmpFileName;
$updateGroup = "borders";
}
};
} else {
$result = [
'status' => 'error',
'error' => 'Кромка не найдена'
];
}
break;
}
}
if ($isOk && (!empty($updateGroup))) {
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("fasad", $updateGroup, $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
echo $this->jsonSimpleResponse($result);
}
}
@@ -0,0 +1,89 @@
<?php
namespace App\Controller\Manager;
use Cake\Event\Event;
use App\Controller\AppController;
use Cake\ORM\TableRegistry;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xls;
class FasadStatisticsController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["fasad"]));
}
function index() {
$this->set('jsConfig', $this->getAppJsConfig("fasad", true));
}
function getStatistics() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
$result['statistics'] = $this->getFasadStatisticsData($data);
$result['statistics']['data'] = $this->formatStatisticsPrice($result['statistics']['data']);
$result['statistics']['total'] = $this->formatStatisticsPrice([$result['statistics']['total']])[0];
echo $this->jsonSimpleResponse($result);
}
function exportClients($year) {
$statistics = $this->getFasadStatisticsData([
"select" => "clients",
"year" => $year
]);
$xlsData = [
[
"ФИО",
"Email",
"Телефон",
"Кол-во заказов",
"Сумма, руб."
]
];
foreach($statistics["data"] as $li) {
$xlsData[] = [
"name" => $li["name"],
"email" => $li["email"],
"phone" => $li["phone"],
"count" => $li["count"],
"price" => $li["price"]
];
}
$xlsSubject = "Clients Export";
$objPHPExcel = new Spreadsheet();
$objPHPExcel->setActiveSheetIndex(0);
$objPHPExcel->getActiveSheet()->setTitle('База клиентов');
$objPHPExcel->getProperties()->setCreator($xlsSubject)
->setLastModifiedBy($xlsSubject)
->setTitle($xlsSubject);
$objPHPExcel->getActiveSheet()->fromArray($xlsData, null, 'A1');
$xlsWriter = new Xls($objPHPExcel);
$filename = "fasad-clients-export_".date('d-m-Y')."_".date("H-i").".xls";
header('Content-type: application/vnd.ms-excel');
header('Content-disposition: attachment; filename="' . $filename . '"');
$xlsWriter->save('php://output');
ob_flush();
exit;
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Controller\Manager;
use Cake\Event\Event;
use App\Controller\AppController;
class ManagersController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["cut", "calcDoors", "fasad", "smsSubscribes", "emailSubscribes"]));
}
function index() {
//TODO redirect by default app from profile
$this->redirect(['controller' => 'CutOrders', 'action' => 'index', 'prefix' => 'manager']);
}
}
@@ -0,0 +1,711 @@
<?php
namespace App\Controller\Manager;
use App\Controller\ElplatMerchantAPI;
use App\Controller\CloudPaymentsMerchantAPI;
use Cake\Core\Configure;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
use App\Controller\AppController;
use SMSRU;
class OrderPaymentController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["orderPayment"]));
}
public function beforeRender(Event $event) {
}
private function searchOrderPaymentOrders($result, $data) {
/*$orderPaymentTable = TableRegistry::getTableLocator()->get("OrderPayment");
$ordersList = $orderPaymentTable->find('all')->all();
foreach($ordersList as $key => $order) {
$order['data'] = json_decode($order['data'], true);
if (!empty($order['data']['telephone'])) {
$order->telephone = cleanupPhone($order['data']['telephone']);
if (strlen($order->telephone) == 10) {
$order->telephone = "7" . $order->telephone;
}
//$order->telephone = formatPhone($order->telephone);
} else {
$order->telephone = '';
}
unset($order['data']);
$order->name = trim($order->name);
if (!empty($order->payment)) {
$order->payment = json_decode($order->payment, true);
$order->paid_date = $order->payment[count($order->payment) - 1]['DateTime'];
unset($order->payment);
}
$orderPaymentTable->save($order);
}*/
$search = preg_replace("/%{2,}/", "%", "%" . preg_replace("/[^\w\d]/ui", "%" , $data['search']) . "%");
if ($search == "%") {
$search = "";
}
$status = (!empty($data['paymentStatus']) ? trim($data['paymentStatus']) : null);
$searchPhone = "%".cleanupPhone(trim($data['search']))."%";
$paymentGateway = (!empty($data['paymentGateway']) ? trim($data['paymentGateway']) : null);
//var_dump($search);
$conditions = [];
if (!empty($search)) {
$searchSum = trim(str_replace(["р.", "р", "₽", " "], "", mb_strtolower($data['search'])));
$conditions[] = ['OR' => [
'order_num LIKE' => $search,
'name LIKE' => $search,
'telephone LIKE' => $searchPhone,
'summa_order_decimal' => $searchSum
]];
}
if (!empty($data['dateFrom'])) {
if (is_string($data['dateFrom'])) {
$data['dateFrom'] = strtotime($data['dateFrom']);
} else {
$data['dateFrom'] = $data['dateFrom'] / 1000;
}
$conditions[] = ['expiry_date >=' => $this->patchTimestampForJS(date('Y-m-d 00:00:00', $data['dateFrom']), -24, true, 'Y-m-d H:i:s')];
}
if (!empty($data['dateTo'])) {
if (is_string($data['dateTo'])) {
$data['dateTo'] = strtotime($data['dateTo']);
} else {
$data['dateTo'] = $data['dateTo'] / 1000;
}
$conditions[] = ['expiry_date <=' => $this->patchTimestampForJS(date('Y-m-d 23:59:59', $data['dateTo']), -24, true, 'Y-m-d H:i:s')];
}
if (!empty($status)) {
if ($status == 'Оплачен') {
$conditions[] = ['payment is not null'];
}
if ($status == 'Не оплачен') {
$conditions[] = ['payment is null'];
}
}
if (!empty($paymentGateway)) {
$conditions[] = ['paymentGateway' => $paymentGateway];
}
$result['totalOrders'] = 0;
$conditions = array_merge($conditions, ['isDeleted' => false]);
$currentPage = (!empty($data['currentPage']) ? (int)$data['currentPage']: 1);
$numPerPage = (!empty($data['numPerPage']) ? (int)$data['numPerPage']: 25);
$sort = [
'field' => 'expiry_date',
'direction' => 'desc'
];
if (!empty($data['sortFields'])) {
switch($data['sortFields']['field']) {
case 'order_num':
case 'name':
case 'summa_order_decimal':
case 'telephone':
case 'expiry_date':
case 'paymentGateway':
$sort['field'] = $data['sortFields']['field'];
break;
case 'statusJs':
$sort['field'] = 'paid_date';
break;
}
}
if (!in_array($data['sortFields']['direction'], ['asc', 'desc'])) {
$sort['direction'] = 'desc';
} else {
$sort['direction'] = $data['sortFields']['direction'];
}
//var_dump($conditions);exit;
$orderPaymentTable = TableRegistry::getTableLocator()->get("OrderPayment");
$ordersList = $orderPaymentTable->find('all')->where($conditions)
->order([$sort['field'] => $sort['direction']])->page($currentPage, $numPerPage)->all();
if (!empty($ordersList)) {
$ordersList = $ordersList->toArray();
foreach($ordersList as $key => $order) {
$order['data'] = json_decode($order['data'], true);
$ordersList[$key]['expiry_date'] = date_timestamp_get($this->patchTimestampForJS($order['expiry_date'], 24, false)) * 1000;
$ordersList[$key]['summa_order_decimal'] = $order['summa_order_decimal'];
$ordersList[$key]['summa_order'] = $order['data']['summa_order'];
$ordersList[$key]['name'] = $order['name'];
if (!empty($order['telephone'])) {
$ordersList[$key]['telephoneOriginal'] = $order['data']['telephone'];
$order['telephone'] = formatPhone($order['telephone']);
}
unset($ordersList[$key]['data']);
if (!empty($order['paid_date'])) {
$ordersList[$key]['status'] = 'Оплачен';
//$ordersList[$key]['paidDate'] = $this->patchTimestampForJS($order['payment'][count($order['payment']) - 1]['DateTime'], -3, true, 'd.m.Y H:i');
$ordersList[$key]['paidDate'] = $this->patchTimestampForJS($order['paid_date'], -3, true, 'd.m.Y H:i');
$ordersList[$key]['statusJs'] = $this->patchTimestampForJS($order['paid_date'], -3, true, 'U');;
unset($ordersList[$key]['payment']);
} else {
$ordersList[$key]['status'] = 'Не оплачен';
$ordersList[$key]['statusJs'] = 0;
}
}
$result['totalOrders'] = $orderPaymentTable->find('all')->where($conditions)->count();
$result['unpaidCount'] = $orderPaymentTable->find('all')->where(array_merge($conditions, [
'OR' => [
['payment is null'],
['payment is not null', 'summa_order_decimal_paid' => 0]
]
]))->count();
$result['unpaidTotal'] = str_replace(".00", "", toFixed2Price($orderPaymentTable->find('all')->where(array_merge($conditions, [
'OR' => [
['payment is null'],
['payment is not null', 'summa_order_decimal_paid' => 0]
]
]))->sumOf('summa_order_decimal')));
$result['paidCount'] = $orderPaymentTable->find('all')->where(array_merge($conditions, [
'payment is not null',
'summa_order_decimal_paid > ' => 0
]))->count();
$result['paidTotal'] = str_replace(".00", "", toFixed2Price($orderPaymentTable->find('all')->where(array_merge($conditions, [
'payment is not null',
'summa_order_decimal_paid >' => 0
]))->sumOf('summa_order_decimal_paid')));
}
$result['ordersList'] = (!empty($ordersList) ? $ordersList : []);
$result['status'] = "success";
return $result;
}
public function index() {
$jsConfig = $this->getAppJsConfig("orderPayment", true);
$data = $this->request->getData();
if (!empty($data['action'])) {
$result = [
'status' => 'error',
'errors' => []
];
switch($data['action']) {
case "searchOrders":
$result = $this->searchOrderPaymentOrders($result, $data);
break;
}
echo $this->jsonSimpleResponse($result);
} else {
$this->set('jsConfig', $jsConfig);
}
}
function adminDeleteOrder() {
/*$orderPaymentTable = TableRegistry::getTableLocator()->get("OrderPayment");
$ordersList = $orderPaymentTable->find('all')->where(['payment is not null'])->select([
'id','payment'
])->all();
foreach($ordersList as $order) {
$order->summa_order_decimal_paid = 0;
$order['payment'] = json_decode($order['payment'], true);
foreach($order['payment'] as $payment) {
$order->summa_order_decimal_paid += $payment['Amount'];
unset($order['payment']);
$orderPaymentTable->save($order);
}
}*/
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$orderPaymentTable = TableRegistry::getTableLocator()->get("OrderPayment");
$order = $orderPaymentTable->find()->where(['id' => $data['id']])->first();
if (!empty($order)) {
$order->isDeleted = true;
$orderPaymentTable->save($order);
$result = $this->searchOrderPaymentOrders($result, $data);
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для удаления данного счета'
];
}
echo $this->jsonSimpleResponse($result);
}
function getElplatPaymentHistory() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin', 'kassir'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$elplatPaymentsTable = TableRegistry::getTableLocator()->get('ElplatPayments');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $data['id'],
'isDeleted' => false,
])->first();
if (!empty($savedOrderData)) {
$history = $elplatPaymentsTable->find()->where([
'orderPaymentId' => $data['id'],
'status IN' => ["success", "refund", "failure"]
])->order(['id' => 'asc'])->all()->toArray();
if (!empty($history)) {
$usersTable = TableRegistry::getTableLocator()->get('Users');
$result['history'] = [];
foreach($history as $li) {
$tmp = [
'date' => $this->patchTimestampForJS($li['updated'], 0, true, 'd.m.Y H:i'),
];
$li['responseData'] = @json_decode($li['responseData'], true);
switch($li['status']) {
case 'success':
$tmp['description'] = 'Оплата счета ' . $savedOrderData['order_num'];
$tmp['amount'] = str_replace(".00", "", toFixed2Price($li['responseData']['allAmount'] / 100)).'р.';
break;
case 'failure':
$tmp['description'] = 'Ошибка оплаты счета ' . $savedOrderData['order_num'];
$tmp['amount'] = '';
break;
case 'refund':
$tmp['description'] = $li['responseData']['refundDescription'];
$tmp['amount'] = str_replace(".00", "", toFixed2Price($li['responseData']['refundAmount'])).'р.';
if (!empty($li['responseData']['refundManagerId'])) {
$refundManager = $usersTable->find()->where([
'id' => $li['responseData']['refundManagerId']
])->first();
if (!empty($refundManager)) {
$tmp['refundManager'] = $refundManager->name;
}
}
break;
}
$result['history'][] = $tmp;
}
$result['refundAmount'] = $savedOrderData['summa_order_decimal_paid'];
$result['refundDescription'] = "Возрат средств по счету ".$savedOrderData['order_num']." - ".str_replace(".00", "", toFixed2Price($savedOrderData['summa_order_decimal_paid']))."р.";
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для возврата средств по данному счету'
];
}
echo $this->jsonSimpleResponse($result);
}
public function makeElplatRefund() {
$result = [
'status' => 'error'
];
if ($this->isAuthorized(['admin', 'kassir'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$elplatPaymentsTable = TableRegistry::getTableLocator()->get('ElplatPayments');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $data['id'],
'isDeleted' => false,
])->first();
if (!empty($savedOrderData)) {
$orderPayment = $elplatPaymentsTable->find()->where([
'orderPaymentId' => $data['id'],
'qrcId is not null',
"qrcId != ''",
'status' => 'success'
])->first();
if (!empty($orderPayment) && !empty($orderPayment->responseData)) {
$orderPayment->responseData = @json_decode($orderPayment->responseData, true);
$elplatCredentials = Configure::read('elplat');
$elplatCredentials = $elplatCredentials[$savedOrderData['domain']];
$elplat = new ElplatMerchantAPI();
$refundData = [
'login' => $elplatCredentials['login'],
"passwd" => $elplatCredentials['passwd'],
"ebl27" => $orderPayment->responseData['ebl27'],
"amount" => round($data['refundAmount'] * 100),
"orgId" => $elplatCredentials['orgId'],
"descr" => $data['refundDescription'],
];
$hashId = $elplat->getElplatHashId($refundData);
unset($refundData['passwd']);
$request = $elplat->createQr($elplatCredentials['apiUrl'], array_merge($refundData, [
"type" => "undone",
'hashId' => $hashId,
'vTerminal' => $elplatCredentials['login']
]));
if (!empty($request['info']['status'])) {
$request['info']['refundDescription'] = $data['refundDescription'];
$request['info']['refundAmount'] = $data['refundAmount'];
$request['info']['refundManagerId'] = $this->Auth->user('id');
$now = date('Y-m-d H:i:s');
$elplatRefund = $elplatPaymentsTable->newEntity([
'orderPaymentId' => $data['id'],
'qrData' => '',
'qrcId' => '',
'responseData' => json_encode($request['info']),
'status' => 'refund',
'created' => $now,
'updated' => $now,
]);
$elplatPaymentsTable->save($elplatRefund);
//////////////////////////
$tmpOrder = $orderPaymentTable->get($data['id']);
$tmpOrder->summa_order_decimal_paid -= $data['refundAmount'];
$xml = '<?xml version="1.0" encoding="windows-1251"?><orders><invoice_no>' . $savedOrderData['order_num'] . '</invoice_no>';
$xml .= '<time>' . date('Y-m-d H:i:s') . '</time><paid>refund</paid><paid_summa>' . $data['refundAmount'] . '</paid_summa><gateway>СБП</gateway>';
$xml .= '</orders>';
$xml = mb_convert_encoding(str_replace("\n", "", $xml), "Windows-1251", "UTF-8");
$filename = 'acquiring_in_' . date('Y-m-d_H-i-s') . '_' . bin2hex(random_bytes(4)) . '.xml';
if (empty($tmpOrder->summa_order_decimal_paid)) {
$tmpOrder->payment = null;
$tmpOrder->paid_date = null;
}
$orderPaymentTable->save($tmpOrder);
file_put_contents(TMP . $filename, $xml);
TableRegistry::getTableLocator()->get('CutSheets')->putImportFileToArchive(TMP . $filename, false, $filename);
///////////
$ftpParams = Configure::read("ftpParams");
$ftpConnectionId = ftp_connect($ftpParams['ip'], (!empty($ftpParams['port']) ? $ftpParams['port'] : 21), 5);
if (!empty($ftpConnectionId)) {
$ftpLoginResult = ftp_login($ftpConnectionId, $ftpParams['username'], $ftpParams['password']);
ftp_pasv($ftpConnectionId, true);
if ($ftpLoginResult) {
$ftpTargetDir = "";
switch ($savedOrderData['domain']) {
case "apps.supersam24.ru":
$ftpTargetDir = $ftpParams['acquiring24Out'];
break;
case "apps.supersamsev.ru":
$ftpTargetDir = $ftpParams['acquiringOut'];
break;
case "apps.prometeysev.ru":
$ftpTargetDir = $ftpParams['acquiringPrometeyOut'];
break;
}
ftp_chdir($ftpConnectionId, $ftpTargetDir);
$tmpFile = tmpfile();
fwrite($tmpFile, $xml);
rewind($tmpFile);
/*if (empty($tmpOrder->files)) {
$tmpOrder->files = [];
} else {
$tmpOrder->files = json_decode($tmpOrder->files, true);
}
if (empty($tmpOrder->files["refund"])) {
$tmpOrder->files["refund"] = [];
}
$tmpOrder->files["refund"][] = [
"date" => date('d.m.Y H:i'),
"filename" => $filename,
"xml" => $xml
];
$tmpOrder->files = json_encode($tmpOrder->files);*/
$result['status'] = 'success';
if (ftp_fput($ftpConnectionId, $filename, $tmpFile, FTP_BINARY, 0)) {
} else {
//todo some notify and queue here !!!
$orderPaymentTable->ftpOrderPaymentUploadErrorNotify($xml, $filename, $savedOrderData, "refund", "возврата средств по счету");
file_put_contents(TMP . 'callbackElplatRefundData' . date('Y-m-d_H-i-s') . '.error.ftpuploaderror.txt', json_encode($data));
};
fclose($tmpFile);
}
ftp_close($ftpConnectionId);
} else {
//todo some notify and queue here !!!
$orderPaymentTable->ftpOrderPaymentUploadErrorNotify($xml, $filename, $savedOrderData);
file_put_contents(TMP . 'callbackElplatRefundData' . date('Y-m-d_H-i-s') . '.error.ftpconnecterror.txt', json_encode($data));
}
} else {
$result = [
'status' => 'error',
'warning' => $request['info']['descr']
];
}
}
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для возврата средств по данному счету'
];
}
echo $this->jsonSimpleResponse($result);
}
function getCloudPaymentsPaymentHistory() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin', 'kassir'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $data['id'],
'isDeleted' => false,
])->first();
if (!empty($savedOrderData)) {
$history = json_decode($savedOrderData->payment, true);
if (!empty($history)) {
$usersTable = TableRegistry::getTableLocator()->get('Users');
$result['history'] = [];
foreach($history as $li) {
$tmp = [
//'date' => $this->patchTimestampForJS($li['updated'], 0, true, 'd.m.Y H:i'),
'date' => $this->patchTimestampForJS($li['DateTime'], -3, true, 'd.m.Y H:i')
];
$li['responseData'] = @json_decode($li['responseData'], true);
switch($li['OperationType']) {
case 'Payment':
$tmp['description'] = 'Оплата счета ' . $savedOrderData['order_num'];
$tmp['amount'] = str_replace(".00", "", toFixed2Price($li['Amount'])).'р.';
break;
case 'Refund':
$tmp['description'] = 'Возврат средств по счету ' . $savedOrderData['order_num'];
$tmp['amount'] = str_replace(".00", "", toFixed2Price($li['Amount'])).'р.';
//refundAmount add to response data
if (!is_array($li['Data'])) {
$li['Data'] = @json_decode($li['Data'], true);
}
if (!empty($li['Data']['refundManagerId'])) {
$refundManager = $usersTable->find()->where([
'id' => $li['Data']['refundManagerId']
])->first();
if (!empty($refundManager)) {
$tmp['refundManager'] = $refundManager->name;
}
}
break;
}
$result['history'][] = $tmp;
}
$result['refundAmount'] = $savedOrderData['summa_order_decimal_paid'];
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для возврата средств по данному счету'
];
}
echo $this->jsonSimpleResponse($result);
}
public function makeCloudPaymentsRefund() {
$result = [
'status' => 'error'
];
if ($this->isAuthorized(['admin', 'kassir'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $data['id'],
'isDeleted' => false,
])->first();
if (!empty($savedOrderData)) {
$cloudPaymentsCredentials = Configure::read('cloudPayments');
$cloudPaymentsCredentials = $cloudPaymentsCredentials[$savedOrderData['domain']];
$cloudPayments = new CloudPaymentsMerchantAPI();
$paymentTransactionId = null;
$savedOrderData->payment = json_decode($savedOrderData->payment, true);
foreach ($savedOrderData->payment as $payment) {
if ($payment["OperationType"] == "Payment") {
$paymentTransactionId = $payment['TransactionId'];
}
}
if (!empty($paymentTransactionId)) {
$refundData = [
'paymentPublicId' => $cloudPaymentsCredentials['paymentPublicId'],
"apiKey" => $cloudPaymentsCredentials['apiKey'],
"TransactionId" => $paymentTransactionId,
"Amount" => $data['refundAmount'],
'requestLocation' => 'payments/refund',
'JsonData' => [
'encOrderId' => $savedOrderData['encryptOrderId'],
'refundManagerId' => $this->Auth->user('id'),
]
];
$request = $cloudPayments->createRefund($cloudPaymentsCredentials['apiUrl'], $refundData);
if (!empty($request["Success"])) {
$result['status'] = 'success';
} else {
$result = [
'status' => 'error',
'warning' => (!empty($request['Message']) ? $request['Message'] : "Произошла ошибка при возврате средств. Проверьте данные и попробуйте снова.")
];
}
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для возврата средств по данному счету'
];
}
echo $this->jsonSimpleResponse($result);
}
}
//$this->db->query("INSERT INTO `" . DB_PREFIX . "elplat_payments` SET order_id = '" . $orderId . "', qrData = '', qrcId = '', responseData = '" . $this->db->escape(json_encode($refundInfo)) . "', status = 'refund', created = NOW(), updated = NOW()");
@@ -0,0 +1,694 @@
<?php
namespace App\Controller\Manager;
use App\Controller\ElplatMerchantAPI;
use App\Controller\CloudPaymentsMerchantAPI;
use Cake\Core\Configure;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
use App\Controller\AppController;
use SMSRU;
class OrderPaymentController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["orderPayment"]));
}
public function beforeRender(Event $event) {
}
private function searchOrderPaymentOrders($result, $data) {
/*$orderPaymentTable = TableRegistry::getTableLocator()->get("OrderPayment");
$ordersList = $orderPaymentTable->find('all')->all();
foreach($ordersList as $key => $order) {
$order['data'] = json_decode($order['data'], true);
if (!empty($order['data']['telephone'])) {
$order->telephone = cleanupPhone($order['data']['telephone']);
if (strlen($order->telephone) == 10) {
$order->telephone = "7" . $order->telephone;
}
//$order->telephone = formatPhone($order->telephone);
} else {
$order->telephone = '';
}
unset($order['data']);
$order->name = trim($order->name);
if (!empty($order->payment)) {
$order->payment = json_decode($order->payment, true);
$order->paid_date = $order->payment[count($order->payment) - 1]['DateTime'];
unset($order->payment);
}
$orderPaymentTable->save($order);
}*/
$search = preg_replace("/%{2,}/", "%", "%" . preg_replace("/[^\w\d]/ui", "%" , $data['search']) . "%");
$status = (!empty($data['paymentStatus']) ? trim($data['paymentStatus']) : null);
$searchPhone = "%".cleanupPhone(trim($data['search']))."%";
$paymentGateway = (!empty($data['paymentGateway']) ? trim($data['paymentGateway']) : null);
$conditions = [];
if (!empty($search)) {
$searchSum = trim(str_replace(["р.", "р", "₽", " "], "", mb_strtolower($data['search'])));
$conditions[] = ['OR' => [
'order_num LIKE' => $search,
'name LIKE' => $search,
'telephone LIKE' => $searchPhone,
'summa_order_decimal' => $searchSum
]];
}
if (!empty($data['dateFrom'])) {
if (is_string($data['dateFrom'])) {
$data['dateFrom'] = strtotime($data['dateFrom']);
} else {
$data['dateFrom'] = $data['dateFrom'] / 1000;
}
$conditions[] = ['expiry_date >=' => $this->patchTimestampForJS(date('Y-m-d 00:00:00', $data['dateFrom']), -24, true, 'Y-m-d H:i:s')];
}
if (!empty($data['dateTo'])) {
if (is_string($data['dateTo'])) {
$data['dateTo'] = strtotime($data['dateTo']);
} else {
$data['dateTo'] = $data['dateTo'] / 1000;
}
$conditions[] = ['expiry_date <=' => $this->patchTimestampForJS(date('Y-m-d 23:59:59', $data['dateTo']), -24, true, 'Y-m-d H:i:s')];
}
if (!empty($status)) {
if ($status == 'Оплачен') {
$conditions[] = ['payment is not null'];
}
if ($status == 'Не оплачен') {
$conditions[] = ['payment is null'];
}
}
if (!empty($paymentGateway)) {
$conditions[] = ['paymentGateway' => $paymentGateway];
}
$result['totalOrders'] = 0;
$conditions = array_merge($conditions, ['isDeleted' => false]);
$currentPage = (!empty($data['currentPage']) ? (int)$data['currentPage']: 1);
$numPerPage = (!empty($data['numPerPage']) ? (int)$data['numPerPage']: 25);
$sort = [
'field' => 'expiry_date',
'direction' => 'desc'
];
if (!empty($data['sortFields'])) {
switch($data['sortFields']['field']) {
case 'order_num':
case 'name':
case 'summa_order_decimal':
case 'telephone':
case 'expiry_date':
case 'paymentGateway':
$sort['field'] = $data['sortFields']['field'];
break;
case 'statusJs':
$sort['field'] = 'paid_date';
break;
}
}
if (!in_array($data['sortFields']['direction'], ['asc', 'desc'])) {
$sort['direction'] = 'desc';
} else {
$sort['direction'] = $data['sortFields']['direction'];
}
$orderPaymentTable = TableRegistry::getTableLocator()->get("OrderPayment");
$ordersList = $orderPaymentTable->find('all')->where($conditions)
->order([$sort['field'] => $sort['direction']])->page($currentPage, $numPerPage)->all();
if (!empty($ordersList)) {
$ordersList = $ordersList->toArray();
foreach($ordersList as $key => $order) {
$order['data'] = json_decode($order['data'], true);
$ordersList[$key]['expiry_date'] = date_timestamp_get($this->patchTimestampForJS($order['expiry_date'], 24, false)) * 1000;
$ordersList[$key]['summa_order_decimal'] = $order['summa_order_decimal'];
$ordersList[$key]['summa_order'] = $order['data']['summa_order'];
$ordersList[$key]['name'] = $order['name'];
if (!empty($order['telephone'])) {
$ordersList[$key]['telephoneOriginal'] = $order['data']['telephone'];
$order['telephone'] = formatPhone($order['telephone']);
}
unset($ordersList[$key]['data']);
if (!empty($order['paid_date'])) {
$ordersList[$key]['status'] = 'Оплачен';
//$ordersList[$key]['paidDate'] = $this->patchTimestampForJS($order['payment'][count($order['payment']) - 1]['DateTime'], -3, true, 'd.m.Y H:i');
$ordersList[$key]['paidDate'] = $this->patchTimestampForJS($order['paid_date'], -3, true, 'd.m.Y H:i');
$ordersList[$key]['statusJs'] = $this->patchTimestampForJS($order['paid_date'], -3, true, 'U');;
unset($ordersList[$key]['payment']);
} else {
$ordersList[$key]['status'] = 'Не оплачен';
$ordersList[$key]['statusJs'] = 0;
}
}
$result['totalOrders'] = $orderPaymentTable->find('all')->where($conditions)->count();
$result['unpaidCount'] = $orderPaymentTable->find('all')->where(array_merge($conditions, [
'OR' => [
['payment is null'],
['payment is not null', 'summa_order_decimal_paid' => 0]
]
]))->count();
$result['unpaidTotal'] = str_replace(".00", "", toFixed2Price($orderPaymentTable->find('all')->where(array_merge($conditions, [
'OR' => [
['payment is null'],
['payment is not null', 'summa_order_decimal_paid' => 0]
]
]))->sumOf('summa_order_decimal')));
$result['paidCount'] = $orderPaymentTable->find('all')->where(array_merge($conditions, [
'payment is not null',
'summa_order_decimal_paid > ' => 0
]))->count();
$result['paidTotal'] = str_replace(".00", "", toFixed2Price($orderPaymentTable->find('all')->where(array_merge($conditions, [
'payment is not null',
'summa_order_decimal_paid >' => 0
]))->sumOf('summa_order_decimal_paid')));
}
$result['ordersList'] = (!empty($ordersList) ? $ordersList : []);
$result['status'] = "success";
return $result;
}
public function index() {
$jsConfig = $this->getAppJsConfig("orderPayment", true);
$data = $this->request->getData();
if (!empty($data['action'])) {
$result = [
'status' => 'error',
'errors' => []
];
switch($data['action']) {
case "searchOrders":
$result = $this->searchOrderPaymentOrders($result, $data);
break;
}
echo $this->jsonSimpleResponse($result);
} else {
$this->set('jsConfig', $jsConfig);
}
}
function adminDeleteOrder() {
/*$orderPaymentTable = TableRegistry::getTableLocator()->get("OrderPayment");
$ordersList = $orderPaymentTable->find('all')->where(['payment is not null'])->select([
'id','payment'
])->all();
foreach($ordersList as $order) {
$order->summa_order_decimal_paid = 0;
$order['payment'] = json_decode($order['payment'], true);
foreach($order['payment'] as $payment) {
$order->summa_order_decimal_paid += $payment['Amount'];
unset($order['payment']);
$orderPaymentTable->save($order);
}
}*/
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$orderPaymentTable = TableRegistry::getTableLocator()->get("OrderPayment");
$order = $orderPaymentTable->find()->where(['id' => $data['id']])->first();
if (!empty($order)) {
$order->isDeleted = true;
$orderPaymentTable->save($order);
$result = $this->searchOrderPaymentOrders($result, $data);
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для удаления данного счета'
];
}
echo $this->jsonSimpleResponse($result);
}
function getElplatPaymentHistory() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin', 'kassir'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$elplatPaymentsTable = TableRegistry::getTableLocator()->get('ElplatPayments');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $data['id'],
'isDeleted' => false,
])->first();
if (!empty($savedOrderData)) {
$history = $elplatPaymentsTable->find()->where([
'orderPaymentId' => $data['id'],
'status IN' => ["success", "refund", "failure"]
])->order(['id' => 'asc'])->all()->toArray();
if (!empty($history)) {
$usersTable = TableRegistry::getTableLocator()->get('Users');
$result['history'] = [];
foreach($history as $li) {
$tmp = [
'date' => $this->patchTimestampForJS($li['updated'], 0, true, 'd.m.Y H:i'),
];
$li['responseData'] = @json_decode($li['responseData'], true);
switch($li['status']) {
case 'success':
$tmp['description'] = 'Оплата счета ' . $savedOrderData['order_num'];
$tmp['amount'] = str_replace(".00", "", toFixed2Price($li['responseData']['allAmount'] / 100)).'р.';
break;
case 'failure':
$tmp['description'] = 'Ошибка оплаты счета ' . $savedOrderData['order_num'];
$tmp['amount'] = '';
break;
case 'refund':
$tmp['description'] = $li['responseData']['refundDescription'];
$tmp['amount'] = str_replace(".00", "", toFixed2Price($li['responseData']['refundAmount'])).'р.';
if (!empty($li['responseData']['refundManagerId'])) {
$refundManager = $usersTable->find()->where([
'id' => $li['responseData']['refundManagerId']
])->first();
if (!empty($refundManager)) {
$tmp['refundManager'] = $refundManager->name;
}
}
break;
}
$result['history'][] = $tmp;
}
$result['refundAmount'] = $savedOrderData['summa_order_decimal_paid'];
$result['refundDescription'] = "Возрат средств по счету ".$savedOrderData['order_num']." - ".str_replace(".00", "", toFixed2Price($savedOrderData['summa_order_decimal_paid']))."р.";
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для возврата средств по данному счету'
];
}
echo $this->jsonSimpleResponse($result);
}
public function makeElplatRefund() {
$result = [
'status' => 'error'
];
if ($this->isAuthorized(['admin', 'kassir'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$elplatPaymentsTable = TableRegistry::getTableLocator()->get('ElplatPayments');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $data['id'],
'isDeleted' => false,
])->first();
if (!empty($savedOrderData)) {
$orderPayment = $elplatPaymentsTable->find()->where([
'orderPaymentId' => $data['id'],
'qrcId is not null',
"qrcId != ''",
'status' => 'success'
])->first();
if (!empty($orderPayment) && !empty($orderPayment->responseData)) {
$orderPayment->responseData = @json_decode($orderPayment->responseData, true);
$elplatCredentials = Configure::read('elplat');
$elplatCredentials = $elplatCredentials[$savedOrderData['domain']];
$elplat = new ElplatMerchantAPI();
$refundData = [
'login' => $elplatCredentials['login'],
"passwd" => $elplatCredentials['passwd'],
"ebl27" => $orderPayment->responseData['ebl27'],
"amount" => round($data['refundAmount'] * 100),
"orgId" => $elplatCredentials['orgId'],
"descr" => $data['refundDescription'],
];
$hashId = $elplat->getElplatHashId($refundData);
unset($refundData['passwd']);
$request = $elplat->createQr($elplatCredentials['apiUrl'], array_merge($refundData, [
"type" => "undone",
'hashId' => $hashId,
'vTerminal' => $elplatCredentials['login']
]));
if (!empty($request['info']['status'])) {
$request['info']['refundDescription'] = $data['refundDescription'];
$request['info']['refundAmount'] = $data['refundAmount'];
$request['info']['refundManagerId'] = $this->Auth->user('id');
$now = date('Y-m-d H:i:s');
$elplatRefund = $elplatPaymentsTable->newEntity([
'orderPaymentId' => $data['id'],
'qrData' => '',
'qrcId' => '',
'responseData' => json_encode($request['info']),
'status' => 'refund',
'created' => $now,
'updated' => $now,
]);
$elplatPaymentsTable->save($elplatRefund);
$ftpParams = Configure::read("ftpParams");
$ftpConnectionId = ftp_connect($ftpParams['ip'], (!empty($ftpParams['port']) ? $ftpParams['port'] : 21), 5);
if (!empty($ftpConnectionId)) {
$ftpLoginResult = ftp_login($ftpConnectionId, $ftpParams['username'], $ftpParams['password']);
ftp_pasv($ftpConnectionId, true);
if ($ftpLoginResult) {
$ftpTargetDir = "";
switch ($savedOrderData['domain']) {
case "apps.supersam24.ru":
$ftpTargetDir = $ftpParams['acquiring24Out'];
break;
case "apps.supersamsev.ru":
$ftpTargetDir = $ftpParams['acquiringOut'];
break;
case "apps.prometeysev.ru":
$ftpTargetDir = $ftpParams['acquiringPrometeyOut'];
break;
}
ftp_chdir($ftpConnectionId, $ftpTargetDir);
$tmpOrder = $orderPaymentTable->get($data['id']);
$tmpOrder->summa_order_decimal_paid -= $data['refundAmount'];
$xml = '<?xml version="1.0" encoding="windows-1251"?><orders><invoice_no>' . $savedOrderData['order_num'] . '</invoice_no>';
$xml .= '<time>' . date('Y-m-d H:i:s') . '</time><paid>refund</paid><paid_summa>' . $data['refundAmount'] . '</paid_summa><gateway>СБП</gateway>';
$xml .= '</orders>';
$xml = mb_convert_encoding(str_replace("\n", "", $xml), "Windows-1251", "UTF-8");
$tmpFile = tmpfile();
fwrite($tmpFile, $xml);
rewind($tmpFile);
$filename = 'acquiring_in_' . date('Y-m-d_H-i-s') . '_' . bin2hex(random_bytes(4)) . '.xml';
if (ftp_fput($ftpConnectionId, $filename, $tmpFile, FTP_BINARY, 0)) {
if (empty($tmpOrder->summa_order_decimal_paid)) {
$tmpOrder->payment = null;
$tmpOrder->paid_date = null;
}
/*if (empty($tmpOrder->files)) {
$tmpOrder->files = [];
} else {
$tmpOrder->files = json_decode($tmpOrder->files, true);
}
if (empty($tmpOrder->files["refund"])) {
$tmpOrder->files["refund"] = [];
}
$tmpOrder->files["refund"][] = [
"date" => date('d.m.Y H:i'),
"filename" => $filename,
"xml" => $xml
];
$tmpOrder->files = json_encode($tmpOrder->files);*/
$orderPaymentTable->save($tmpOrder);
$result['status'] = 'success';
};
fclose($tmpFile);
file_put_contents(TMP . $filename, $xml);
TableRegistry::getTableLocator()->get('CutSheets')->putImportFileToArchive(TMP . $filename, false, $filename);
}
ftp_close($ftpConnectionId);
}
} else {
$result = [
'status' => 'error',
'warning' => $request['info']['descr']
];
}
}
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для возврата средств по данному счету'
];
}
echo $this->jsonSimpleResponse($result);
}
function getCloudPaymentsPaymentHistory() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin', 'kassir'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $data['id'],
'isDeleted' => false,
])->first();
if (!empty($savedOrderData)) {
$history = json_decode($savedOrderData->payment, true);
if (!empty($history)) {
$usersTable = TableRegistry::getTableLocator()->get('Users');
$result['history'] = [];
foreach($history as $li) {
$tmp = [
//'date' => $this->patchTimestampForJS($li['updated'], 0, true, 'd.m.Y H:i'),
'date' => $this->patchTimestampForJS($li['DateTime'], -3, true, 'd.m.Y H:i')
];
$li['responseData'] = @json_decode($li['responseData'], true);
switch($li['OperationType']) {
case 'Payment':
$tmp['description'] = 'Оплата счета ' . $savedOrderData['order_num'];
$tmp['amount'] = str_replace(".00", "", toFixed2Price($li['Amount'])).'р.';
break;
case 'Refund':
$tmp['description'] = 'Возврат средств по счету ' . $savedOrderData['order_num'];
$tmp['amount'] = str_replace(".00", "", toFixed2Price($li['Amount'])).'р.';
//refundAmount add to response data
if (!is_array($li['Data'])) {
$li['Data'] = @json_decode($li['Data'], true);
}
if (!empty($li['Data']['refundManagerId'])) {
$refundManager = $usersTable->find()->where([
'id' => $li['Data']['refundManagerId']
])->first();
if (!empty($refundManager)) {
$tmp['refundManager'] = $refundManager->name;
}
}
break;
}
$result['history'][] = $tmp;
}
$result['refundAmount'] = $savedOrderData['summa_order_decimal_paid'];
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для возврата средств по данному счету'
];
}
echo $this->jsonSimpleResponse($result);
}
public function makeCloudPaymentsRefund() {
$result = [
'status' => 'error'
];
if ($this->isAuthorized(['admin', 'kassir'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $data['id'],
'isDeleted' => false,
])->first();
if (!empty($savedOrderData)) {
$cloudPaymentsCredentials = Configure::read('cloudPayments');
$cloudPaymentsCredentials = $cloudPaymentsCredentials[$savedOrderData['domain']];
$cloudPayments = new CloudPaymentsMerchantAPI();
$paymentTransactionId = null;
$savedOrderData->payment = json_decode($savedOrderData->payment, true);
foreach ($savedOrderData->payment as $payment) {
if ($payment["OperationType"] == "Payment") {
$paymentTransactionId = $payment['TransactionId'];
}
}
if (!empty($paymentTransactionId)) {
$refundData = [
'paymentPublicId' => $cloudPaymentsCredentials['paymentPublicId'],
"apiKey" => $cloudPaymentsCredentials['apiKey'],
"TransactionId" => $paymentTransactionId,
"Amount" => $data['refundAmount'],
'requestLocation' => 'payments/refund',
'JsonData' => [
'encOrderId' => $savedOrderData['encryptOrderId'],
'refundManagerId' => $this->Auth->user('id'),
]
];
$request = $cloudPayments->createRefund($cloudPaymentsCredentials['apiUrl'], $refundData);
if (!empty($request["Success"])) {
$result['status'] = 'success';
} else {
$result = [
'status' => 'error',
'warning' => (!empty($request['Message']) ? $request['Message'] : "Произошла ошибка при возврате средств. Проверьте данные и попробуйте снова.")
];
}
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для возврата средств по данному счету'
];
}
echo $this->jsonSimpleResponse($result);
}
}
//$this->db->query("INSERT INTO `" . DB_PREFIX . "elplat_payments` SET order_id = '" . $orderId . "', qrData = '', qrcId = '', responseData = '" . $this->db->escape(json_encode($refundInfo)) . "', status = 'refund', created = NOW(), updated = NOW()");
@@ -0,0 +1,149 @@
<?php
namespace App\Controller\Manager;
use Cake\Core\Configure;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
use App\Controller\AppController;
class OrdersInfoController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["ordersInfo"]));
}
public function beforeRender(Event $event) {
}
private function searchOrdersInfoOrders($result, $data) {
$search = preg_replace("/%{2,}/", "%", "%" . preg_replace("/[^\w\d]/ui", "%" , $data['search']) . "%");
$searchPhone = "%".cleanupPhone(trim($data['search']))."%";
//if (strlen($searchPhone) == 10) {
// $searchPhone = "7" . $searchPhone;
//}
//$searchPhone = formatPhone($searchPhone);
$conditions = [];
if (!empty($search)) {
$conditions[] = ['OR' => [
'nom LIKE' => $search,
'fio LIKE' => $search,
'tel LIKE' => $searchPhone,
//'tel' => $searchPhone,
//'(tel LIKE "' . $search . '") OR (tel LIKE "' => $searchPhone . '")'
]];
}
$result['totalOrders'] = 0;
$conditions = array_merge($conditions, ['isDeleted' => false]);
$currentPage = (!empty($data['currentPage']) ? (int)$data['currentPage']: 1);
$numPerPage = (!empty($data['numPerPage']) ? (int)$data['numPerPage']: 25);
$sort = [
'field' => 'created',
'direction' => 'desc'
];
if (!empty($data['sortFields'])) {
switch($data['sortFields']['field']) {
case 'nom':
case 'fio':
case 'tel':
case 'totalSum':
case 'created':
$sort['field'] = $data['sortFields']['field'];
break;
}
}
if (!in_array($data['sortFields']['direction'], ['asc', 'desc'])) {
$sort['direction'] = 'desc';
} else {
$sort['direction'] = $data['sortFields']['direction'];
}
$ordersInfoTable = TableRegistry::getTableLocator()->get("OrdersInfo");
$ordersList = $ordersInfoTable->find('all')->where($conditions)->order([$sort['field'] => $sort['direction']])->page($currentPage, $numPerPage)->all();
if (!empty($ordersList)) {
$ordersList = $ordersList->toArray();
foreach($ordersList as $key => $order) {
$order['data'] = json_decode($order['data'], true);
$order['tel'] = formatPhone($order['tel']);
$order['created'] = $this->patchTimestampForJS($order['created'], 0, true, 'd.m.Y');
}
$result['totalOrders'] = $ordersInfoTable->find('all')->where($conditions)->count();
$result['ordersTotalSum'] = str_replace(".00", "", toFixed2Price($ordersInfoTable->find('all')->where($conditions)->sumOf('totalSum')));
}
$result['ordersList'] = (!empty($ordersList) ? $ordersList : []);
$result['status'] = "success";
return $result;
}
public function index() {
$jsConfig = $this->getAppJsConfig("ordersInfo", true);
$data = $this->request->getData();
if (!empty($data['action'])) {
$result = [
'status' => 'error',
'errors' => []
];
switch($data['action']) {
case "searchOrders":
$result = $this->searchOrdersInfoOrders($result, $data);
break;
}
echo $this->jsonSimpleResponse($result);
} else {
$this->set('jsConfig', $jsConfig);
}
}
function adminDeleteOrder() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$ordersInfoTable = TableRegistry::getTableLocator()->get("OrdersInfo");
$order = $ordersInfoTable->find()->where(['id' => $data['id']])->first();
if (!empty($order)) {
$order->isDeleted = true;
$ordersInfoTable->save($order);
$result = $this->searchOrdersInfoOrders($result, $data);
} else {
$result = [
'status' => 'error',
'warning' => 'Счет не найден'
];
}
}
} else {
$result = [
'status' => 'error',
'error' => 'Недостаточно прав для удаления данного счета'
];
}
echo $this->jsonSimpleResponse($result);
}
}
@@ -0,0 +1,72 @@
<?php
namespace App\Controller\Manager;
use Cake\Event\Event;
use Cake\Core\Configure;
use Cake\ORM\TableRegistry;
use App\Controller\AppController;
use Cake\Utility\Hash;
class OrdersInfoSettingsController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["ordersInfo"]));
}
function index() {
$this->set('jsConfig', $this->getAppJsConfig("ordersInfo", true));
}
function saveSettings() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data['action'])) {
$settingsTable = TableRegistry::getTableLocator()->get('OrdersInfoSettings');
switch($data['action']) {
case 'saveGroup':
if (!empty($data['settings'])) {
foreach($data['settings'] as $groupId => $groupData) {
foreach($groupData as $s) {
if (!in_array($s['id'], ['lastUpdateDate', 'lastUpdateManagerId'])) {
$currentSetting = $settingsTable->find()->where([
'key' => $s['id'],
'group' => $groupId
])->first();
if (!empty($currentSetting)) {
$currentSetting->value = $s['value'];
$settingsTable->save($currentSetting);
} else {
$currentSetting = $settingsTable->newEntity([
'key' => $s['id'],
'value' => $s['value'],
'group' => $groupId
]);
$settingsTable->save($currentSetting);
}
}
}
}
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("ordersInfo", $data['settingsGroup'], $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
break;
}
}
echo $this->jsonSimpleResponse($result);
}
}
@@ -0,0 +1,254 @@
<?php
namespace App\Controller\Manager;
use Cake\Event\Event;
use Cake\Core\Configure;
use Cake\ORM\TableRegistry;
use App\Controller\AppController;
use Cake\Utility\Hash;
class SettingsController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["cut", "calcDoors", "fasad"]));
}
function index() {
$this->set('jsConfig', $this->getAppJsConfig("", true));
}
function saveSettings() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data['action'])) {
$settingsTable = TableRegistry::getTableLocator()->get('Settings');
switch($data['action']) {
case 'saveGroup':
if (!empty($data['settings'])) {
foreach($data['settings'] as $groupId => $groupData) {
foreach($groupData as $s) {
if (!in_array($s['id'], ['lastUpdateDate', 'lastUpdateManagerId'])) {
$currentSetting = $settingsTable->find()->where([
'key' => $s['id'],
'group' => $groupId
])->first();
if (in_array($s['id'], ["smtpParams"])) {
$s['value'] = @json_encode($s['value']);
}
if (!empty($currentSetting)) {
$currentSetting->value = $s['value'];
$settingsTable->save($currentSetting);
} else {
$currentSetting = $settingsTable->newEntity([
'key' => $s['id'],
'value' => $s['value'],
'group' => $groupId
]);
$settingsTable->save($currentSetting);
}
}
}
}
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("", $data['settingsGroup'], $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
break;
}
}
echo $this->jsonSimpleResponse($result);
}
function addUpdateBranch() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data)) {
$branchesTable = TableRegistry::getTableLocator()->get('Branches');
$isOk = false;
if (empty($data['id'])) { //add
$currentBranch = $branchesTable->newEntity();
$currentBranch = $branchesTable->newEntity();
$currentBranch = $branchesTable->patchEntity($currentBranch, $data);
if ($branchesTable->save($currentBranch)) {
$isOk = true;
}
} else { //update
$currentBranch = $branchesTable->find()->where(['id' => $data['id']])->first();
if (!empty($currentBranch)) {
$branchesTable->patchEntity($currentBranch, $data);
if ($branchesTable->save($currentBranch)) {
$isOk = true;
}
} else {
$result = [
'status' => 'error',
'warning' => 'Контакт-менеджер не найден'
];
}
}
if ($isOk) {
if (!empty($data['is_default'])) {
$branchesTable->updateAll(
['is_default' => false],
['id !=' => $currentBranch->id]
);
}
$result['branches'] = $this->getBranchesListForJS();
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("", 'branches', $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
echo $this->jsonSimpleResponse($result);
}
function deleteBranch() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data['id'])) {
$branchesTable = TableRegistry::getTableLocator()->get('Branches');
$branch = $branchesTable->find()->where(['id' => $data['id']])->first();
if (!empty($branch)) {
$branchesTable->updateAll(
['is_deleted' => true],
['id' => $data['id']]
);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("", 'branches', $this->Auth->user('id'));
$result = array_merge($result, $updates);
} else {
$result = [
'status' => 'error',
'error' => 'Контакт-менеджер не найден'
];
}
}
echo $this->jsonSimpleResponse($result);
}
function addUpdateDelivery() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data)) {
$deliveryTable = TableRegistry::getTableLocator()->get('Delivery');
$isOk = false;
if (empty($data['id'])) { //add
$currentDelivery = $deliveryTable->newEntity();
$currentDelivery = $deliveryTable->patchEntity($currentDelivery, $data);
if ($deliveryTable->save($currentDelivery)) {
$isOk = true;
}
} else { //update
$currentDelivery = $deliveryTable->find()->where(['id' => $data['id']])->first();
if (!empty($currentDelivery)) {
$deliveryTable->patchEntity($currentDelivery, $data);
if ($deliveryTable->save($currentDelivery)) {
$isOk = true;
if (!empty($currentDelivery['shopShippingId'])) {
$updateArray = [
$currentDelivery->shopShippingId,
$currentDelivery->shopOrderMinTotal,
$currentDelivery->costPerKm,
$currentDelivery->minimalCost,
$currentDelivery->location
];
exec("/var/www/supersam24.ru/impex/bin/cake updateDelivery ".join(" ", $updateArray));
exec("/var/www/supersamsev.ru/impex/bin/cake updateDelivery ".join(" ", $updateArray));
}
}
} else {
$result = [
'status' => 'error',
'warning' => 'Доставка не найдена'
];
}
}
if ($isOk) {
$result['delivery'] = $this->getDeliveryListForJS();
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("", 'delivery', $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
echo $this->jsonSimpleResponse($result);
}
function deleteDelivery() {
$result = [
'status' => 'success'
];
$data = $this->request->getData();
if (!empty($data['id'])) {
$deliveryTable = TableRegistry::getTableLocator()->get('Delivery');
$delivery = $deliveryTable->find()->where(['id' => $data['id']])->first();
if (!empty($delivery)) {
$deliveryTable->updateAll(
['is_deleted' => true],
['id' => $data['id']]
);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("", 'delivery', $this->Auth->user('id'));
$result = array_merge($result, $updates);
} else {
$result = [
'status' => 'error',
'error' => 'Доставка не найдена'
];
}
}
echo $this->jsonSimpleResponse($result);
}
}
@@ -0,0 +1,156 @@
<?php
namespace App\Controller\Manager;
use Cake\Core\Configure;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
use App\Controller\AppController;
use SMSRU;
class SmsOrdersReadyController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["smsOrdersReady"]));
}
public function beforeRender(Event $event)
{
}
public function index() {
$jsConfig = $this->getAppJsConfig("smsOrdersReady", true);
$data = $this->request->getData();
if (!empty($data['action'])) {
$result = [
'status' => 'error',
'errors' => []
];
$smsOrdersReadTable = TableRegistry::getTableLocator()->get("SmsOrdersReady");
$settingsTable = TableRegistry::getTableLocator()->get('Settings');
$currentPage = (!empty($data['currentPage']) ? (int)$data['currentPage']: 1);
$numPerPage = (!empty($data['numPerPage']) ? (int)$data['numPerPage']: 25);
$sort = [
'field' => 'created',
'direction' => 'desc'
];
if (!empty($data['sortFields'])) {
switch($data['sortFields']['field']) {
case 'message':
case 'phone':
case 'sentDate':
case 'status':
$sort['field'] = $data['sortFields']['field'];
break;
}
}
if (!in_array($data['sortFields']['direction'], ['asc', 'desc'])) {
$sort['direction'] = 'desc';
} else {
$sort['direction'] = $data['sortFields']['direction'];
}
switch($data['action']) {
case "searchSms":
$search = preg_replace("/%{2,}/", "%", "%" . preg_replace("/[^\w\d]/ui", "%" , $data['search']) . "%");
$smsType = (!empty($data['smsType']) ? trim($data['smsType']) : 'order');
$status = (!empty($data['smsStatus']) ? trim($data['smsStatus']) : null);
//$search = "%" . str_replace([" ", "-", "+", "(", ")", ".", ",", "/", "*"], "%", trim($data['search'])) . "%";
$conditions = [];
if ($smsType != 'any') {
$conditions[] = ['smsType' => $smsType];
};
$searchPhone = "%".cleanupPhone(trim($data['search']))."%";
if (!empty($search)) {
$conditions[] = ['OR' => [
'phone LIKE' => $search,
'message LIKE' => $search,
'status LIKE' => $searchPhone,
]];
}
if (!empty($status)) {
$conditions[] = ['status' => $status];
}
$smsList = $smsOrdersReadTable->find('all')->where($conditions)->order([$sort['field'] => $sort['direction']])->page($currentPage, $numPerPage)->all();
if (!empty($smsList)) {
$smsList = $smsList->toArray();
foreach($smsList as $key => $sms) {
$smsList[$key]['phone'] = formatPhone($sms['phone']);
$smsList[$key]['sentDate'] = date_timestamp_get($sms['sentDate']) * 1000;
if (mb_stripos($sms['message'], 'Ваш код подтверждения') !== false) {
$smsList[$key]['message'] = preg_replace('/[0-9]/', '*', $sms['message']);
}
if (!empty($sms['statusTime'])) {
$smsList[$key]['statusTime'] = date_timestamp_get($sms['statusTime']) * 1000;
}
$result['totalSms'] = $smsOrdersReadTable->find('all')->where($conditions)->count();
}
}
$result['smsList'] = (!empty($smsList) ? $smsList : []);
$result['status'] = "success";
$updates = $settingsTable->getSettingsUpdateDate("", 'smsOrdersReady', false);
$result = array_merge($result, $updates);
break;
case "importSms":
if ($this->isAuthorized(['admin'])) {
$source = !empty($data['file']['tmp_name']) ? $data['file']['tmp_name'] : '';
if (!empty($source)) {
$importResult = $smsOrdersReadTable->importSms($source);
if ($importResult['totalCount'] > 0) {
$result['status'] = 'success';
$result['importCount'] = $importResult['importCount'];
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("", 'smsOrdersReady', $this->Auth->user('id'));
$result = array_merge($result, $updates);
} else {
$result['errors'][] = 'Неверный формат файла или список информационных sms пуст.';
}
} else {
$result['errors'][] = 'Ошибка загрузки файла.';
}
} else {
$result['errors'][] = 'Недостаточно прав';
}
break;
}
echo $this->jsonSimpleResponse($result);
} else {
$this->set('jsConfig', $jsConfig);
}
}
}
@@ -0,0 +1,268 @@
<?php
namespace App\Controller\Manager;
use Cake\Core\Configure;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
use App\Controller\AppController;
use SMSRU;
class SmsSubscribesController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized([], ["smsSubscribes"]));
}
public function beforeRender(Event $event)
{
}
public function index() {
$jsConfig = $this->getAppJsConfig("smsSubscribes", true);
$data = $this->request->getData();
if (!empty($data['action'])) {
$result = [
'status' => 'error',
'errors' => []
];
$subscribesTable = TableRegistry::getTableLocator()->get("SmsSubscribes");
$subscribesRecipientsTable = TableRegistry::getTableLocator()->get("SmsSubscribeRecipients");
switch($data['action']) {
case "getSubscribes":
$subscribeId = (int) $data['subscribeId'];
if (!empty($subscribeId)) {
} else {
$result['subscribes'] = $this->getSubscribesList("SmsSubscribes", $data);
}
$result['status'] = "success";
break;
case "setSubscribeStatus":
$subscribeId = (int) $data['subscribeId'];
$newStatus = $data['newStatus'];
if (!empty($subscribeId) && !empty($newStatus)) {
$subscribe = $subscribesTable->find('all')->where(['id' => $subscribeId])->first();
if (!empty($subscribe)) {
if ($this->isSubscribeUpdateAllowed($subscribe['status'])) {
$isStatusChangeAllowed = true;
if ($newStatus == "deleted") {
if (($subscribe['createdBy'] != $this->Auth->user('id')) && ($this->Auth->user('role') != 'admin')) {
$isStatusChangeAllowed = false;
}
}
if ($isStatusChangeAllowed) {
$subscribe->status = $newStatus;
if ($newStatus == "queued") {
$subscribe->queuedBy = $this->Auth->user('id');
$subscribe->stoppedBy = null;
}
if ($newStatus == "stopped") {
$subscribe->stoppedBy = $this->Auth->user('id');
}
if ($subscribesTable->save($subscribe)) {
$result['status'] = "success";
}
} else {
$result['errors'][] = 'Недостаточно прав для изменения статуса рассылки';
}
} else {
$result['errors'][] = 'Редактирование активной или завершенной рассылки недоступно';
}
} else {
$result['errors'][] = 'Рассылка не найдена!';
}
}
break;
case "addSubscribe":
if (!empty($data['message'])) {
$data['created'] = date('Y-m-d H:i:s');
$data['createdBy'] = $this->Auth->user('id');
$data['status'] = "stopped";
$subscribe = $subscribesTable->newEntity();
$subscribe = $subscribesTable->patchEntity($subscribe, $data);
if ($subscribesTable->save($subscribe)) {
$result['subscribe'] = $this->getSubscribeData("SmsSubscribes", $subscribe->id);
$result['status'] = "success";
}
}
break;
case "updateSubscribe":
if (!empty($data['id'])) {
$subscribe = $this->getSubscribeData("SmsSubscribes", $data['id']);
if (!empty($subscribe)) {
if ($this->isSubscribeUpdateAllowed($subscribe['status'])) {
unset($data['created']);
unset($data['createdBy']);
unset($data['queuedBy']);
unset($data['stoppedBy']);
unset($data['errorsCount']);
unset($data['sentCount']);
unset($data['startTime']);
unset($data['status']);
unset($data['successCount']);
$data['recipientsCount'] = count($data['sms_subscribe_recipients']);
if (!empty($data['sms_subscribe_recipients'])) {
foreach ($data['sms_subscribe_recipients'] as $recipientKey => $recipient) {
unset($data['sms_subscribe_recipients'][$recipientKey]['sendStatus']);
unset($data['sms_subscribe_recipients'][$recipientKey]['statusMessage']);
unset($data['sms_subscribe_recipients'][$recipientKey]['smsRuMessageId']);
unset($data['sms_subscribe_recipients'][$recipientKey]['sendDate']);
}
}
$subscribe = $subscribesTable->get($data['id']);
$subscribe = $subscribesTable->patchEntity($subscribe, $data);
if ($subscribesTable->save($subscribe)) {
if (!empty($data['recipientsToDelete'])) {
foreach ($data['recipientsToDelete'] as $recipientToDelete) {
$subscribesRecipientsTable->deleteAll([
'subscribeId' => $data['id'],
'id' => $recipientToDelete
]);
}
}
$result['subscribe'] = $subscribe;
$result['status'] = "success";
}
$result['status'] = "success";
} else {
$result['errors'][] = 'Редактирование активной или завершенной рассылки недоступно';
}
} else {
$result['errors'][] = 'Рассылка не найдена!';
}
}
break;
case "getSubscribeData":
if (!empty($data['subscribeId'])) {
$subscribe = $this->getSubscribeData("SmsSubscribes", $data['subscribeId']);
if (!empty($subscribe)) {
$result['subscribe'] = $subscribe;
$result['status'] = "success";
} else {
$result['errors'][] = 'Рассылка не найдена!';
}
}
break;
case "importSubscribeRecipients":
$source = !empty($data['file']['tmp_name']) ? $data['file']['tmp_name'] : '';
$subscribeId = (!empty($data['subscribeId']) ? (int) $data['subscribeId'] : null);
if (!empty($source) && !empty($subscribeId)) {
$importResult = $subscribesTable->importSubscribesList($source);
$result['importNewSubscribersCount'] = 0;
$result['importNewSubscribersCountTotal'] = 0;
if (count($importResult['list']) > 0) {
$result['status'] = 'success';
$result['importNewSubscribersCountTotal'] = count($importResult['list']);
$subscribers = (!empty($data['sms_subscribe_recipients']) ? $data['sms_subscribe_recipients'] : []);
if (!empty($subscribers)) {
foreach($subscribers as $subscriber) {
unset($importResult['list'][$subscriber['phone']]); //cleanup duplicates
}
}
$result['importNewSubscribersCount'] = count($importResult['list']);
if (!empty($importResult['list'])) {
foreach($importResult['list'] as $newSubscriberPhone => $newSubscriberName) {
$subscribers[] = [
"name" => $newSubscriberName,
"phone" => $newSubscriberPhone,
"sendStatus" => "queued"
];
}
}
usort($subscribers, function($a, $b) {
return strcmp($a['name'], $b['name']);
});
foreach($subscribers as $key => $subscriber) { //fix "null" due to incorrect upload field values
foreach($subscriber as $subKey => $value) {
if ($value == "null") {
$subscriber[$subKey] = null;
}
}
$subscribers[$key] = $subscriber;
}
$result['importSubscribers'] = $subscribers;
} else {
$result['errors'][] = 'Неверный формат файла или список получателей пуст.';
}
$result['incorrectList'] = $importResult['incorrectList'];
$result['importNewSubscribersCountTotal'] += count($importResult['incorrectList']);
if (count($result['incorrectList'])) {
$result['status'] = 'success';
}
} else {
$result['errors'][] = 'Ошибка загрузки файла.';
}
break;
case "getSmsRuBalance":
if ($this->Auth->user('role') == 'admin') {
$smsRu = new SMSRU($jsConfig['global']['smsRuApiKey']['value']);
$balance = $smsRu->getBalance();
if ($balance->status == "OK") {
$result['status'] = 'success';
$result['balance'] = str_replace(".00", "", number_format(max($balance->balance - Configure::read('smsMinimalBalance'), 0), 2, '.', '')); //as requested
//$result['balance'] = max($balance->balance - Configure::read('smsMinimalBalance'), 0); //as requested
} else {
$result['errors'][] = $balance->status_text;
}
} else {
$result['errors'][] = 'Произошла ошибка при запросе баланса, повторите позднее!';
}
break;
}
echo $this->jsonSimpleResponse($result);
} else {
$this->set('jsConfig', $jsConfig);
}
}
}
+272
View File
@@ -0,0 +1,272 @@
<?php
namespace App\Controller\Manager;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
use Cake\Auth\DefaultPasswordHasher;
use App\Controller\AppController;
class UsersController extends AppController {
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('manager');
$this->set('isAuthorized', $this->isAuthorized());
}
public function beforeRender(Event $event)
{
$this->Auth->allow(['*']);
}
public function login() {
if ($this->isAuthorized()) {
$this->redirect([
'controller' => 'Managers',
'action' => 'index',
'prefix' => 'manager'
]);
} else {
$this->viewBuilder()->setLayout('manager_login');
$jsConfig = $this->getAppJsConfig("", false);
if ($this->request->is('post')) {
$data = $this->request->getData();
$jsConfig["defaultLoginApp"] = $data['defaultLoginApp'] = str_replace("string:", "", $data['defaultLoginApp']);
$user = $this->Auth->identify();
$usersTable = TableRegistry::getTableLocator()->get('Users');
if ($user) {
if (!empty($user['is_active'])) {
$user["available_apps"] = @json_decode($user["available_apps"], true);
$this->Auth->setUser($user);
$usersTable->updateAll([
'logging_attempts' => 0
],[
'id' => $user['id']
]);
if (!empty($data['defaultLoginApp']) && in_array($data['defaultLoginApp'], $user["available_apps"])) {
$this->request->getSession()->write('currentApp', $data['defaultLoginApp']);
return $this->redirect($this->config["appsList"][$data['defaultLoginApp']]["adminUrl"]);
} else {
$this->request->getSession()->write('currentApp', "cut");
return $this->redirect($this->Auth->redirectUrl());
}
} else {
$this->Flash->error('Ваш аккаунт неактивен.<br/>Обратитесь к администратору для активации.');
}
} else {
$existsUsername = $usersTable->find()->where([
'username' => $data['username']
])->first();
if (!empty($existsUsername)) {
$existsUsername->logging_attempts = $existsUsername->logging_attempts + 1;
if ($existsUsername->logging_attempts >= 5) {
$existsUsername->is_active = false;
}
$usersTable->save($existsUsername);
}
$this->Flash->error('Неверное имя пользователя или пароль.');
}
} else {
$jsConfig["defaultLoginApp"] = "cut";
}
$this->set('jsConfig', $jsConfig);
}
}
public function logout() {
$this->request->getSession()->delete('currentApp');
return $this->redirect($this->Auth->logout());
}
public function index() {
}
public function usersList() {
$this->checkRegisteredAsAdmin();
$this->set('jsConfig', $this->getAppJsConfig("", true));
}
function addUpdateUser() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data)) {
$usersTable = TableRegistry::getTableLocator()->get('Users');
if (!empty($data['password']) && ($data['password'] != $data['passwordRepeat'])) {
$result = [
'status' => 'warning',
'warning' => 'Пароль и подтверждение пароля не совпадают'
];
} else {
$isOk = false;
unset($data['last_active']);
if (empty($data['id'])) { //add
$similarUsernameUser = $usersTable->find()->where(['username' => $data['username']])->first();
if (empty($similarUsernameUser)) {
$newUser = $usersTable->newEntity();
$data["available_apps"] = json_encode($data["available_apps"]);
$newUser = $usersTable->patchEntity($newUser, $data);
if ($usersTable->save($newUser)) {
$isOk = true;
}
} else {
$result = [
'status' => 'error',
'warning' => 'Такое имя пользователя/email уже зарегистрированы'
];
}
} else { //update
$currentUser = $usersTable->find()->where(['id' => $data['id']])->first();
if (!empty($currentUser)) {
$similarUsernameUser = $usersTable->find()->where(['username' => $data['username'], 'id !=' => $data['id']])->first();
if (empty($similarUsernameUser)) {
if ($data['id'] == $this->Auth->user('id')) {
unset($data['role']);
unset($data['is_active']);
if (!empty($data['password']) && empty($data['currentPassword'])) {
$result = [
'status' => 'error',
'error' => 'Необходимо указать текущий пароль'
];
}
if (!empty($data['password']) && !empty($data['currentPassword'])) {
if (!(new DefaultPasswordHasher)->check($data['currentPassword'], $currentUser->password)) {
$result = [
'status' => 'error',
'error' => 'Неверно указан текущий пароль'
];
}
}
}
if (!empty($data['is_active']) && empty($currentUser['is_active'])) {
$data['logging_attempts'] = 0;
}
if ($result['status'] == "success") {
$data["available_apps"] = json_encode($data["available_apps"]);
$usersTable->patchEntity($currentUser, $data);
if ($usersTable->save($currentUser)) {
$isOk = true;
}
}
} else {
$result = [
'status' => 'error',
'warning' => 'Такое имя пользователя/email уже зарегистрированы'
];
}
} else {
$result = [
'status' => 'error',
'warning' => 'Пользователь не найден'
];
}
}
if ($isOk) {
if (!empty($data['availableToOrderDefault'])) {
$usersTable->updateAll(
['availableToOrderDefault' => false],
['id !=' => $currentUser->id]
);
}
$result['users'] = $this->getUsersListForJS();
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("", 'users', $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
}
} else {
$result = [
'status' => 'error',
'warning' => 'Недостаточно прав для добавление пользователя'
];
}
} else {
$result = [
'status' => 'error',
'warning' => 'Недостаточно прав для добавление пользователя'
];
}
echo $this->jsonSimpleResponse($result);
}
function delete() {
$result = [
'status' => 'success'
];
if ($this->isAuthorized(['admin'])) {
$data = $this->request->getData();
if (!empty($data['id'])) {
$orderCut = TableRegistry::getTableLocator()->get('CutOrders')->find()->where(['manager_id' => $data['id'], 'order_type' => 'order'])->count();
$orderCalcDoors = TableRegistry::getTableLocator()->get('CalcDoorsOrders')->find()->where(['manager_id' => $data['id'], 'order_type' => 'order'])->count();
$orderFasad = TableRegistry::getTableLocator()->get('FasadOrders')->find()->where(['manager_id' => $data['id'], 'order_type' => 'order'])->count();
if (empty($orderCut) && empty($orderCalcDoors) && empty($orderFasad)) {
$usersTable = TableRegistry::getTableLocator()->get('Users');
$user = $usersTable->find()->where(['id' => $data['id']])->first();
if (!empty($user)) {
if ($data['id'] == $this->Auth->user('id')) {
$result = [
'status' => 'error',
'error' => 'Вы не можете удалить свой аккаунт'
];
} else {
$usersTable->delete($user);
$updates = TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("", 'users', $this->Auth->user('id'));
$result = array_merge($result, $updates);
}
} else {
$result = [
'status' => 'error',
'error' => 'Пользователь не найден'
];
}
} else {
$result = [
'status' => 'warning',
'warning' => 'Удаление пользователя невозможно, так как у него есть заказы на исполнении.'
];
}
}
} else {
$result = [
'status' => 'error',
'warning' => 'Недостаточно прав для удаления данного пользователя'
];
}
echo $this->jsonSimpleResponse($result);
}
}
+961
View File
@@ -0,0 +1,961 @@
<?php
namespace App\Controller;
use Cake\Core\Configure;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
use Cake\Routing\Router;
class OrderPaymentController extends AppController
{
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow(
[
'display',
'getPaymentStatus',
'callbackPay',
'callbackRefund',
'createElplatPaymentQr',
'callbackElplat',
'getElpatPaymentStatus'
]
);
$this->viewBuilder()->setLayout('order_payment');
}
public function display($encOrderId = null)
{
if (!empty($encOrderId)) {
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$decryptedData = $ordersTable->decryptCustomData($encOrderId, $this->config);
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $decryptedData['orderId'],
'isDeleted' => false,
])->first();
if (!empty($savedOrderData)) {
$savedOrderData['data'] = json_decode($savedOrderData['data'], true);
$appsDomain = $savedOrderData['domain'];
if (empty($_SERVER['HTTP_CUSTOMHOST']) && !empty($_SERVER['SERVER_NAME']) && ($appsDomain != $_SERVER['SERVER_NAME'])) {
$this->redirect("https://" . $appsDomain . "/order-payment/" . $encOrderId, 301);
}
if (!empty($_SERVER['HTTP_CUSTOMHOST']) && ($_SERVER['HTTP_CUSTOMHOST'] != $appsDomain)) {
$this->set("customAppsRedirect", "https://" . $appsDomain . "/order-payment/" . $encOrderId);
}
if (!empty($savedOrderData['expiry_date'])) {
// Рассчитываем время истечения заявки: 23:59 предыдущего дня от expiry_date
$expiryTimestamp = strtotime($savedOrderData['expiry_date']);
$endOfPreviousDay = strtotime('-1 day', strtotime(date('Y-m-d 23:59:59', $expiryTimestamp)));
// Проверяем, истек ли срок заявки
$isExpired = time() > $endOfPreviousDay;
} else {
// Если expiry_date отсутствует, помечаем заявку как истекшую
$isExpired = true;
}
if (!$isExpired
|| $this->isAuthorized(['admin'], ["orderPayment"], false)
) {
$savedOrderData['encOrderId'] = $encOrderId;
if (!empty($savedOrderData['paid_date'])) {
//var_dump($savedOrderData['paid_date']);
//$savedOrderData['payment'] = json_decode($savedOrderData['payment'], true);
$savedOrderData['payment'] = [
//'DateTime' => $this->patchTimestampForJS($savedOrderData['payment'][count($savedOrderData['payment']) - 1]['DateTime'], -3, true, 'd.m.Y H:i')
'DateTime' => $this->patchTimestampForJS(
$savedOrderData['paid_date'],
-3,
true,
'd.m.Y H:i'
)
];
} else {
$savedOrderData['payment'] = null;
}
} else {
$savedOrderData = [
'data' => [
'manager_tel' => $savedOrderData['data']['manager_tel']
],
'link_expired' => true,
'order_num' => '',
];
}
$jsConfig = $this->getAppJsConfig("orderPayment", false, $savedOrderData);
$jsConfig["appsDomain"] = $appsDomain;
$jsConfig["rootUrl"] = "https://" . str_replace("apps.", "", $appsDomain);
$jsConfig["rootAppsUrl"] = "https://" . $appsDomain;
$paymentPublicId = "";
if (!empty($savedOrderData['targetGateway']) && ($savedOrderData['targetGateway'] == 'РНКБ')) {
if (!empty($savedOrderData['domain'])) {
$paymentPublicIds = Configure::read("cloudPayments");
switch ($savedOrderData['domain']) {
case "apps.supersamsev.ru":
case "apps.supersam24.ru":
$paymentPublicId = $paymentPublicIds[$savedOrderData['domain']]['paymentPublicId'];
break;
}
}
}
$jsConfig["paymentPublicId"] = $paymentPublicId;
$elplatQrEnabled = false;
if (!empty($savedOrderData['targetGateway']) && ($savedOrderData['targetGateway'] == 'СБП')) {
if (!empty($savedOrderData['domain'])) {
switch ($savedOrderData['domain']) {
case "apps.prometeysev.ru":
case "apps.supersam24.ru":
case "apps.supersamsev.ru":
$elplatQrEnabled = true;
$this->config['common']['siteTitle']['value'] = $jsConfig["primarySiteTitle"];
break;
}
}
}
$jsConfig["elplatQrEnabled"] = $elplatQrEnabled;
$this->set('jsConfig', $jsConfig);
$this->set(
'siteTitle',
$this->config['common']['siteTitle']['value'] . " - " . 'Оплата счета ' . $savedOrderData['order_num']
);
} else {
$savedOrderData = null;
}
}
}
if (empty($savedOrderData)) {
$this->redirect('/404');
}
}
public function getPaymentStatus()
{
$result = [
'status' => true
];
$data = $this->request->getData();
if (!empty($data['encOrderId'])) {
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$decryptedData = $ordersTable->decryptCustomData($data['encOrderId'], $this->config);
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $decryptedData['orderId'],
'isDeleted' => false,
])->first();
if (!empty($savedOrderData)) {
if (!empty($savedOrderData['paid_date'])) {
$result['status'] = true;
//$savedOrderData['payment'] = json_decode($savedOrderData['payment'], true);
$result['payment'] = [
//'DateTime' => $this->patchTimestampForJS($savedOrderData['payment'][count($savedOrderData['payment']) - 1]['DateTime'], -3, true, 'd.m.Y H:i')
'paid_date' => $this->patchTimestampForJS(
$savedOrderData['paid_date'],
-3,
true,
'd.m.Y H:i'
)
];
} else {
$result['status'] = false;
}
} else {
$savedOrderData = null;
}
}
}
echo $this->jsonSimpleResponse($result);
}
public function callbackPay()
{
$result = [
'code' => 13
];
$data = $this->request->getData();
if (!empty($data['Data'])) {
$data['Data'] = @json_decode(html_entity_decode($data['Data']), true);
file_put_contents(TMP . 'callbackPayData' . date('Y-m-d_H-i-s') . '.txt', json_encode($data));
if (!empty($data['Status']) && ($data['Status'] == 'Completed')) {
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$decryptedData = $ordersTable->decryptCustomData($data['Data']['encOrderId'], $this->config);
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $decryptedData['orderId'],
])->first();
if (!empty($savedOrderData)) {
$savedOrderData['data'] = json_decode($savedOrderData['data'], true);
$savedOrderData['payment'] = json_decode($savedOrderData['payment'], true);
if ($savedOrderData['data']['summa_order_decimal'] == $data['Amount']) {
$uploadXmlToFtp = false;
$xml = '<?xml version="1.0" encoding="windows-1251"?><orders><invoice_no>' . $savedOrderData['order_num'] . '</invoice_no>';
$isTransactionAlreadyExists = false;
if (!empty($savedOrderData['payment'])) {
foreach ($savedOrderData['payment'] as $payment) {
if ($payment['TransactionId'] == $data['TransactionId']) {
$isTransactionAlreadyExists = true;
$result['code'] = 0;
}
}
}
if (empty($savedOrderData['payment'])) {
$uploadXmlToFtp = true;
$xml .= '<time>' . $this->patchTimestampForJS(
$data['DateTime'],
-3,
true,
'Y-m-d H:i:s'
) . '</time><paid>ok</paid><paid_summa>' . $data['Amount'] . '</paid_summa><gateway>РНКБ</gateway>';
} else {
if (!$isTransactionAlreadyExists) {
$uploadXmlToFtp = true;
$xml .= '<time>' . $this->patchTimestampForJS(
$data['DateTime'],
-3,
true,
'Y-m-d H:i:s'
) . '</time><paid>order_already</paid><paid_summa>' . $data['Amount'] . '</paid_summa><gateway>РНКБ</gateway>';
}
}
$xml .= '</orders>';
$xml = mb_convert_encoding(str_replace("\n", "", $xml), "Windows-1251", "UTF-8");
////////////////
$filename = 'acquiring_in_' . date('Y-m-d_H-i-s') . '_' . bin2hex(random_bytes(4)) . '.xml';
$tmpOrder = $orderPaymentTable->get($decryptedData['orderId']);
if (empty($tmpOrder->payment)) {
$tmpOrder->payment = [];
} else {
$tmpOrder->payment = json_decode($tmpOrder->payment, true);
}
if (!$isTransactionAlreadyExists) {
$tmpOrder->payment[] = $data;
$tmpOrder->summa_order_decimal_paid += $data['Amount'];
}
$tmpOrder->paid_date = $tmpOrder->payment[count($tmpOrder->payment) - 1]['DateTime'];
$tmpOrder->payment = json_encode($tmpOrder->payment);
$tmpOrder->paymentGateway = "CloudPayments";
$orderPaymentTable->save($tmpOrder);
$result['code'] = 0;
file_put_contents(TMP . $filename, $xml);
TableRegistry::getTableLocator()->get('CutSheets')->putImportFileToArchive(
TMP . $filename,
false,
$filename
);
///////////////
if ($uploadXmlToFtp) {
$ftpParams = Configure::read("ftpParams");
$ftpConnectionId = ftp_connect(
$ftpParams['ip'],
(!empty($ftpParams['port']) ? $ftpParams['port'] : 21),
5
);
if (!empty($ftpConnectionId)) {
$ftpLoginResult = ftp_login(
$ftpConnectionId,
$ftpParams['username'],
$ftpParams['password']
);
ftp_pasv($ftpConnectionId, true);
if ($ftpLoginResult) {
$ftpTargetDir = "";
switch ($savedOrderData['domain']) {
case "apps.supersam24.ru":
$ftpTargetDir = $ftpParams['acquiring24Out'];
break;
case "apps.supersamsev.ru":
$ftpTargetDir = $ftpParams['acquiringOut'];
break;
case "apps.prometeysev.ru":
$ftpTargetDir = $ftpParams['acquiringPrometeyOut'];
break;
}
ftp_chdir($ftpConnectionId, $ftpTargetDir);
$tmpFile = tmpfile();
fwrite($tmpFile, $xml);
rewind($tmpFile);
/*if (empty($tmpOrder->files)) {
$tmpOrder->files = [];
} else {
$tmpOrder->files = json_decode($tmpOrder->files, true);
}
if (empty($tmpOrder->files["payment"])) {
$tmpOrder->files["payment"] = [];
}
$tmpOrder->files["payment"][] = [
"date" => date('d.m.Y H:i'),
"filename" => $filename,
"xml" => $xml
];
$tmpOrder->files = json_encode($tmpOrder->files);*/
if (ftp_fput($ftpConnectionId, $filename, $tmpFile, FTP_BINARY, 0)) {
} else {
//$result['code'] = 1;
//todo some notify and queue here !!!
$orderPaymentTable->ftpOrderPaymentUploadErrorNotify(
$xml,
$filename,
$savedOrderData
);
file_put_contents(
TMP . 'callbackCloudPaymentsPayData' . date(
'Y-m-d_H-i-s'
) . '.error.ftpuploaderror.txt',
json_encode($data)
);
};
fclose($tmpFile);
}
ftp_close($ftpConnectionId);
} else {
//$result['code'] = 1;
//todo some notify and queue here !!!
$orderPaymentTable->ftpOrderPaymentUploadErrorNotify(
$xml,
$filename,
$savedOrderData
);
file_put_contents(
TMP . 'callbackCloudPaymentsPayData' . date(
'Y-m-d_H-i-s'
) . '.error.ftpconnecterror.txt',
json_encode($data)
);
}
}
} else {
$savedOrderData = null;
}
}
}
}
}
echo $this->jsonSimpleResponse($result);
}
public function callbackRefund()
{
$result = [
'code' => 13
];
$data = $this->request->getData();
if (!empty($data['Data'])) {
if (!is_array($data['Data'])) {
$data['Data'] = @json_decode(html_entity_decode($data['Data']), true);
}
file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.txt', json_encode($data));
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$decryptedData = $ordersTable->decryptCustomData($data['Data']['encOrderId'], $this->config);
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.1.txt', json_encode($decryptedData));
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $decryptedData['orderId'],
])->first();
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.2.txt', json_encode($savedOrderData));
if (!empty($savedOrderData)) {
/////////////////
//$refundAmount = 0;
$tmpOrder = $orderPaymentTable->get($decryptedData['orderId']);
$isTransactionAlreadyExists = false;
if (!empty($tmpOrder->payment)) {
$tmpOrder->payment = json_decode($tmpOrder->payment, true);
//foreach ($tmpOrder->payment as $paymentKey => $payment) {
// if ($payment['TransactionId'] == $data['PaymentTransactionId']) {
// unset($tmpOrder->payment[$paymentKey]);
$isTransactionAlreadyExists = false;
foreach ($tmpOrder->payment as $payment) {
if ($payment['TransactionId'] == $data['TransactionId']) {
$isTransactionAlreadyExists = true;
}
}
// }
//}
} else {
$tmpOrder->payment = [];
}
$tmpOrder->payment[] = $data;
if (!$isTransactionAlreadyExists) {
$tmpOrder->summa_order_decimal_paid -= $data['Amount'];
$refundAmount = $data['Amount'];
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.6.txt', json_encode($tmpOrder));
$xml = '<?xml version="1.0" encoding="windows-1251"?><orders><invoice_no>' . $savedOrderData['order_num'] . '</invoice_no>';
$xml .= '<time>' . $this->patchTimestampForJS(
$data['DateTime'],
-3,
true,
'Y-m-d H:i:s'
) . '</time><paid>refund</paid><paid_summa>' . $refundAmount . '</paid_summa><gateway>РНКБ</gateway>';
$xml .= '</orders>';
$xml = mb_convert_encoding(str_replace("\n", "", $xml), "Windows-1251", "UTF-8");
$filename = 'acquiring_in_' . date('Y-m-d_H-i-s') . '_' . bin2hex(random_bytes(4)) . '.xml';
if (empty($tmpOrder->summa_order_decimal_paid)) {
//$tmpOrder->payment = null;
$tmpOrder->paid_date = null;
}
$tmpOrder->payment = json_encode($tmpOrder->payment);
$orderPaymentTable->save($tmpOrder);
$result['code'] = 0;
file_put_contents(TMP . $filename, $xml);
TableRegistry::getTableLocator()->get('CutSheets')->putImportFileToArchive(
TMP . $filename,
false,
$filename
);
}
/////////////////
$ftpParams = Configure::read("ftpParams");
$ftpConnectionId = ftp_connect(
$ftpParams['ip'],
(!empty($ftpParams['port']) ? $ftpParams['port'] : 21),
5
);
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.3.txt', json_encode($ftpConnectionId));
if (!empty($ftpConnectionId)) {
$ftpLoginResult = ftp_login($ftpConnectionId, $ftpParams['username'], $ftpParams['password']);
ftp_pasv($ftpConnectionId, true);
if ($ftpLoginResult) {
$ftpTargetDir = "";
switch ($savedOrderData['domain']) {
case "apps.supersam24.ru":
$ftpTargetDir = $ftpParams['acquiring24Out'];
break;
case "apps.supersamsev.ru":
$ftpTargetDir = $ftpParams['acquiringOut'];
break;
case "apps.prometeysev.ru":
$ftpTargetDir = $ftpParams['acquiringPrometeyOut'];
break;
}
ftp_chdir($ftpConnectionId, $ftpTargetDir);
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.4.txt', json_encode($ftpTargetDir));
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.5.txt', json_encode($isTransactionAlreadyExists));
if (!$isTransactionAlreadyExists) {
$tmpFile = tmpfile();
fwrite($tmpFile, $xml);
rewind($tmpFile);
/*if (empty($tmpOrder->payment)) {
$tmpOrder->payment = null;
$tmpOrder->paid_date = null;
} else {
$tmpOrder->paid_date = $tmpOrder->payment[count($tmpOrder->payment) - 1]['DateTime'];
$tmpOrder->payment = json_encode($tmpOrder->payment);
}*/
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.7.txt', json_encode($tmpOrder));
/*if (empty($tmpOrder->files)) {
$tmpOrder->files = [];
} else {
$tmpOrder->files = json_decode($tmpOrder->files, true);
}
if (empty($tmpOrder->files["refund"])) {
$tmpOrder->files["refund"] = [];
}
$tmpOrder->files["refund"][] = [
"date" => date('d.m.Y H:i'),
"filename" => $filename,
"xml" => $xml
];
$tmpOrder->files = json_encode($tmpOrder->files);*/
if (ftp_fput($ftpConnectionId, $filename, $tmpFile, FTP_BINARY, 0)) {
} else {
//$result['code'] = 1;
$orderPaymentTable->ftpOrderPaymentUploadErrorNotify(
$xml,
$filename,
$savedOrderData,
"refund",
"возврата средств по счету"
);
file_put_contents(
TMP . 'callbackCloudPaymentsRefundData' . date(
'Y-m-d_H-i-s'
) . '.error.ftpuploaderror.txt',
json_encode($data)
);
//todo some notify and queue here !!!
};
} else {
$result['code'] = 0;
}
fclose($tmpFile);
}
ftp_close($ftpConnectionId);
} else {
//$result['code'] = 1;
$orderPaymentTable->ftpOrderPaymentUploadErrorNotify(
$xml,
$filename,
$savedOrderData,
"refund",
"возврата средств по счету"
);
file_put_contents(
TMP . 'callbackCloudPaymentsRefundData' . date(
'Y-m-d_H-i-s'
) . '.error.ftpconnecterror.txt',
json_encode($data)
);
//todo some notify and queue here !!!
}
} else {
$savedOrderData = null;
}
}
}
echo $this->jsonSimpleResponse($result);
}
public function createElplatPaymentQr()
{
$result = [
'status' => false
];
$data = $this->request->getData();
if (!empty($data['encOrderId'])) {
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$decryptedData = $ordersTable->decryptCustomData($data['encOrderId'], $this->config);
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $decryptedData['orderId'],
'isDeleted' => false,
])->first();
if (!empty($savedOrderData)) {
if (empty($savedOrderData['paid_date'])) {
$elplatCredentials = Configure::read('elplat');
$elplatCredentials = $elplatCredentials[$savedOrderData['domain']];
$elplat = new ElplatMerchantAPI();
$orderData = json_decode($savedOrderData['data'], true);
$paymentPurposeL1 = "";
$paymentPurposeL2 = "";
switch ($savedOrderData['domain']) {
case "apps.supersam24.ru":
$paymentPurposeL1 = 'Оплата счета ' . $orderData['invoice_no'] . ', СуперСам';
break;
case "apps.supersamsev.ru":
$paymentPurposeL1 = 'Оплата счета ' . $orderData['invoice_no'] . ', СуперСам';
break;
case "apps.prometeysev.ru":
$paymentPurposeL1 = 'Оплата счета ' . $orderData['invoice_no'] . ', ';
$paymentPurposeL2 = 'ООО ТЦ "Прометей"';
break;
}
$data = [
'login' => $elplatCredentials['login'],
"passwd" => $elplatCredentials['passwd'],
"callback" => "https://" . $savedOrderData['domain'] . Router::url(
['controller' => 'OrderPayment', 'action' => 'callback_elplat']
),
"qrcType" => "02",
"currency" => "RUB",
"orgId" => $elplatCredentials['orgId'],
"amount" => round($savedOrderData['summa_order_decimal'] * 100),
"paymentPurpose" => $paymentPurposeL1 . $paymentPurposeL2,
"email" => (!empty($orderData['email']) ? $orderData['email'] : '')
];
$hashId = $elplat->getElplatHashId($data);
unset($data['passwd']);
$request = $elplat->createQr($elplatCredentials['apiUrl'], array_merge($data, [
"type" => "createQr",
'elplatPhone' => $elplatCredentials['elplatPhone'],
'hashId' => $hashId
]));
if (!empty($request['info']['status'])) {
$elplatPaymentsTable = TableRegistry::getTableLocator()->get('ElplatPayments');
$now = date('Y-m-d H:i:s');
$elplatPayment = $elplatPaymentsTable->newEntity([
'orderPaymentId' => $decryptedData['orderId'],
'qrData' => $request['info']['qrData'],
'qrcId' => $request['info']['qrcId'],
'responseData' => null,
'status' => 'pending',
'created' => $now,
'updated' => $now,
]);
$elplatPaymentsTable->save($elplatPayment);
$result['status'] = true;
$result['qrInfo'] = $request['info'];
$result['paymentPurposeL1'] = $paymentPurposeL1;
$result['paymentPurposeL2'] = $paymentPurposeL2;
$result['paymentAmount'] = $orderData['summa_order'] . 'р.';
}
}
} else {
$savedOrderData = null;
}
}
}
echo $this->jsonSimpleResponse($result);
}
public function getElpatPaymentStatus()
{
$result = [
'status' => 'failure'
];
$data = $this->request->getData();
if (!empty($data['qrcId'])) {
$elplatPaymentsTable = TableRegistry::getTableLocator()->get('ElplatPayments');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$qrData = $elplatPaymentsTable->find('all')->where([
'qrcId ' => $data['qrcId'],
])->first();
if (!empty($qrData)) {
$result['status'] = $qrData['status'];
$orderData = $orderPaymentTable->find('all')->where([
'id' => $qrData['orderPaymentId'],
'isDeleted' => false,
])->first();
if ($result['status'] != 'success') {
//check for payments by other qr
if (!empty($orderData) && !empty($orderData['paid_date'])) {
$result['status'] = 'success';
$result['payment'] = [
'paid_date' => $this->patchTimestampForJS($orderData['paid_date'], -3, true, 'd.m.Y H:i')
];
}
} else {
$result['payment'] = [
'paid_date' => $this->patchTimestampForJS($orderData['paid_date'], -3, true, 'd.m.Y H:i')
];
}
}
}
//$result['status'] = 'success';
echo $this->jsonSimpleResponse($result);
}
public function callbackElplat()
{
if (!$this->checkIsElplatCallbackIpAllowed()) {
die('NOTOK');
}
$data = $this->request->getData();
if (empty($data)) {
$data = json_decode(file_get_contents("php://input"), true);
}
file_put_contents(TMP . 'callbackElplatPayData' . date('Y-m-d_H-i-s') . '.txt', json_encode($data));
if (isset($data['payStatus'])) {
$elplatPaymentsTable = TableRegistry::getTableLocator()->get('ElplatPayments');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$qrData = $elplatPaymentsTable->find('all')->where([
'qrcId ' => $data['qrcId'],
])->first();
if (empty($qrData)) {
die('NOTOK');
}
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $qrData['orderPaymentId'],
'isDeleted' => false,
])->first();
if (empty($savedOrderData)) {
die('NOTOK');
}
if (((int)$data['allAmount']) < ((int)($savedOrderData['summa_order_decimal'] * 100))) {
die('NOTOK');
}
$qrStatus = "failure";
if ($data['payStatus'] == 1) {
$qrStatus = "success";
$xml = '<?xml version="1.0" encoding="windows-1251"?><orders><invoice_no>' . $savedOrderData['order_num'] . '</invoice_no>';
$xml .= '<time>' . date(
'Y-m-d H:i:s'
) . '</time><paid>ok</paid><paid_summa>' . ($data['allAmount'] / 100) . '</paid_summa><gateway>СБП</gateway>';
$xml .= '</orders>';
$xml = mb_convert_encoding(str_replace("\n", "", $xml), "Windows-1251", "UTF-8");
////////////////////
$filename = 'acquiring_in_' . date('Y-m-d_H-i-s') . '_' . bin2hex(random_bytes(4)) . '.xml';
$tmpOrder = $orderPaymentTable->get($savedOrderData['id']);
$tmpOrder->payment = [];
$tmpOrder->payment[] = $data;
$tmpOrder->summa_order_decimal_paid = $data['allAmount'] / 100;
$tmpOrder->paid_date = date('Y-m-d H:i:s', strtotime("-3 hours"));
$tmpOrder->payment = json_encode($tmpOrder->payment);
$tmpOrder->paymentGateway = "Elplat";
$orderPaymentTable->save($tmpOrder);
file_put_contents(TMP . $filename, $xml);
TableRegistry::getTableLocator()->get('CutSheets')->putImportFileToArchive(
TMP . $filename,
false,
$filename
);
////////////////////
$ftpParams = Configure::read("ftpParams");
$ftpConnectionId = ftp_connect(
$ftpParams['ip'],
(!empty($ftpParams['port']) ? $ftpParams['port'] : 21),
5
);
if (!empty($ftpConnectionId)) {
$ftpLoginResult = ftp_login($ftpConnectionId, $ftpParams['username'], $ftpParams['password']);
ftp_pasv($ftpConnectionId, true);
if ($ftpLoginResult) {
$ftpTargetDir = "";
switch ($savedOrderData['domain']) {
case "apps.supersam24.ru":
$ftpTargetDir = $ftpParams['acquiring24Out'];
break;
case "apps.supersamsev.ru":
$ftpTargetDir = $ftpParams['acquiringOut'];
break;
case "apps.prometeysev.ru":
$ftpTargetDir = $ftpParams['acquiringPrometeyOut'];
break;
}
ftp_chdir($ftpConnectionId, $ftpTargetDir);
$tmpFile = tmpfile();
fwrite($tmpFile, $xml);
rewind($tmpFile);
/*if (empty($tmpOrder->files)) {
$tmpOrder->files = [];
} else {
$tmpOrder->files = json_decode($tmpOrder->files, true);
}
if (empty($tmpOrder->files["payment"])) {
$tmpOrder->files["payment"] = [];
}
$tmpOrder->files["payment"][] = [
"date" => date('d.m.Y H:i'),
"filename" => $filename,
"xml" => $xml
];
$tmpOrder->files = json_encode($tmpOrder->files);*/
if (ftp_fput($ftpConnectionId, $filename, $tmpFile, FTP_BINARY, 0)) {
} else {
//todo some notify and queue here !!!
$orderPaymentTable->ftpOrderPaymentUploadErrorNotify($xml, $filename, $savedOrderData);
file_put_contents(
TMP . 'callbackElplatPayData' . date('Y-m-d_H-i-s') . '.error.ftpuploaderror.txt',
json_encode($data)
);
};
fclose($tmpFile);
}
ftp_close($ftpConnectionId);
} else {
$orderPaymentTable->ftpOrderPaymentUploadErrorNotify($xml, $filename, $savedOrderData);
file_put_contents(
TMP . 'callbackElplatPayData' . date('Y-m-d_H-i-s') . '.error.ftpconnecterror.txt',
json_encode($data)
);
//todo some notify and queue here !!!
}
}
$qrData->responseData = json_encode($data);
$qrData->status = $qrStatus;
$qrData->updated = date('Y-m-d H:i:s');
$elplatPaymentsTable->save($qrData);
ob_clean();
header_remove();
header("Content-type: application/json");
http_response_code(200);
echo json_encode(["status" => true]);
exit;
} else {
die('NOTOK');
}
}
public function checkIsElplatCallbackIpAllowed()
{
$allow = false;
$allowedIps = ['91.223.44.0/24'];
$ipToCheck = $this->request->clientIP();
if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
$ipToCheck = $_SERVER['HTTP_X_REAL_IP'];
}
foreach ($allowedIps as $ip_allow) {
// If IP has / means CIDR notation
if (strpos($ip_allow, '/') === false) {
// Check Single IP
if (inet_pton($ipToCheck) == inet_pton($ip_allow)) {
$allow = true;
break;
}
} else {
// Check IP range
list($subnet, $bits) = explode('/', $ip_allow);
// Convert subnet to binary string of $bits length
$subnet = unpack('H*', inet_pton($subnet)); // Subnet in Hex
foreach ($subnet as $i => $h) {
$subnet[$i] = base_convert($h, 16, 2);
} // Array of Binary
$subnet = substr(implode('', $subnet), 0, $bits); // Subnet in Binary, only network bits
// Convert remote IP to binary string of $bits length
$ip = unpack('H*', inet_pton($ipToCheck)); // IP in Hex
foreach ($ip as $i => $h) {
$ip[$i] = base_convert($h, 16, 2);
} // Array of Binary
$ip = substr(implode('', $ip), 0, $bits); // IP in Binary, only network bits
// Check network bits match
if ($subnet == $ip) {
$allow = true;
break;
}
}
}
return $allow;
}
}
+803
View File
@@ -0,0 +1,803 @@
<?php
namespace App\Controller;
use Cake\Core\Configure;use Cake\Event\Event;
use Cake\ORM\TableRegistry;
use Cake\Routing\Router;
class OrderPaymentController extends AppController {
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow(['display', 'getPaymentStatus', 'callbackPay', 'callbackRefund', 'createElplatPaymentQr', 'callbackElplat', 'getElpatPaymentStatus']);
$this->viewBuilder()->setLayout('order_payment');
}
public function display($encOrderId = null) {
if(!empty($encOrderId)) {
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$decryptedData = $ordersTable->decryptCustomData($encOrderId, $this->config);
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $decryptedData['orderId'],
'isDeleted' => false,
])->first();
if (!empty($savedOrderData)) {
$savedOrderData['data'] = json_decode($savedOrderData['data'], true);
$appsDomain = $savedOrderData['domain'];
if (empty($_SERVER['HTTP_CUSTOMHOST']) && !empty($_SERVER['SERVER_NAME']) && ($appsDomain != $_SERVER['SERVER_NAME'])) {
$this->redirect("https://".$appsDomain."/order-payment/".$encOrderId, 301);
}
if (!empty($_SERVER['HTTP_CUSTOMHOST']) && ($_SERVER['HTTP_CUSTOMHOST'] != $appsDomain)) {
$this->set("customAppsRedirect", "https://".$appsDomain."/order-payment/".$encOrderId);
}
if (!empty($savedOrderData['paid_date'])
|| !empty($savedOrderData['expiry_date']) && (date('Y-m-d H:i:s', strtotime($savedOrderData['expiry_date'])) >= date('Y-m-d H:i:s'))
|| ($this->isAuthorized([], ["orderPayment"], false))
) {
$savedOrderData['encOrderId'] = $encOrderId;
if (!empty($savedOrderData['paid_date'])) {
//var_dump($savedOrderData['paid_date']);
//$savedOrderData['payment'] = json_decode($savedOrderData['payment'], true);
$savedOrderData['payment'] = [
//'DateTime' => $this->patchTimestampForJS($savedOrderData['payment'][count($savedOrderData['payment']) - 1]['DateTime'], -3, true, 'd.m.Y H:i')
'DateTime' => $this->patchTimestampForJS($savedOrderData['paid_date'], -3, true, 'd.m.Y H:i')
];
} else {
$savedOrderData['payment'] = null;
}
} else {
$savedOrderData = [
'data' => [
'manager_tel' => $savedOrderData['data']['manager_tel']
],
'link_expired' => true,
'order_num' => '',
];
}
$jsConfig = $this->getAppJsConfig("orderPayment", false, $savedOrderData);
$jsConfig["appsDomain"] = $appsDomain;
$jsConfig["rootUrl"] = "https://" . str_replace("apps.", "", $appsDomain);
$jsConfig["rootAppsUrl"] = "https://" . $appsDomain;
$paymentPublicId = "";
if (!empty($savedOrderData['targetGateway']) && ($savedOrderData['targetGateway'] == 'РНКБ')) {
if (!empty($savedOrderData['domain'])) {
$paymentPublicIds = Configure::read("cloudPayments");
switch ($savedOrderData['domain']) {
case "apps.supersamsev.ru":
case "apps.supersam24.ru":
$paymentPublicId = $paymentPublicIds[$savedOrderData['domain']]['paymentPublicId'];
break;
}
}
}
$jsConfig["paymentPublicId"] = $paymentPublicId;
$elplatQrEnabled = false;
if (!empty($savedOrderData['targetGateway']) && ($savedOrderData['targetGateway'] == 'СБП')) {
if (!empty($savedOrderData['domain'])) {
switch ($savedOrderData['domain']) {
case "apps.prometeysev.ru":
case "apps.supersam24.ru":
case "apps.supersamsev.ru":
$elplatQrEnabled = true;
$this->config['common']['siteTitle']['value'] = $jsConfig["primarySiteTitle"];
break;
}
}
}
$jsConfig["elplatQrEnabled"] = $elplatQrEnabled;
$this->set('jsConfig', $jsConfig);
$this->set('siteTitle', $this->config['common']['siteTitle']['value'] . " - " . 'Оплата счета ' . $savedOrderData['order_num']);
} else {
$savedOrderData = null;
}
}
}
if (empty($savedOrderData)) {
$this->redirect('/404');
}
}
public function getPaymentStatus() {
$result = [
'status' => true
];
$data = $this->request->getData();
if(!empty($data['encOrderId'])) {
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$decryptedData = $ordersTable->decryptCustomData($data['encOrderId'], $this->config);
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $decryptedData['orderId'],
'isDeleted' => false,
])->first();
if (!empty($savedOrderData)) {
if (!empty($savedOrderData['paid_date'])) {
$result['status'] = true;
//$savedOrderData['payment'] = json_decode($savedOrderData['payment'], true);
$result['payment'] = [
//'DateTime' => $this->patchTimestampForJS($savedOrderData['payment'][count($savedOrderData['payment']) - 1]['DateTime'], -3, true, 'd.m.Y H:i')
'paid_date' => $this->patchTimestampForJS($savedOrderData['paid_date'], -3, true, 'd.m.Y H:i')
];
} else {
$result['status'] = false;
}
} else {
$savedOrderData = null;
}
}
}
echo $this->jsonSimpleResponse($result);
}
public function callbackPay() {
$result = [
'code' => 13
];
$data = $this->request->getData();
if (!empty($data['Data'])) {
$data['Data'] = @json_decode(html_entity_decode($data['Data']), true);
file_put_contents(TMP . 'callbackPayData' . date('Y-m-d_H-i-s') . '.txt', json_encode($data));
if (!empty($data['Status']) && ($data['Status'] == 'Completed')) {
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$decryptedData = $ordersTable->decryptCustomData($data['Data']['encOrderId'], $this->config);
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $decryptedData['orderId'],
])->first();
if (!empty($savedOrderData)) {
$savedOrderData['data'] = json_decode($savedOrderData['data'], true);
$savedOrderData['payment'] = json_decode($savedOrderData['payment'], true);
if ($savedOrderData['data']['summa_order_decimal'] == $data['Amount']) {
$uploadXmlToFtp = false;
$xml = '<?xml version="1.0" encoding="windows-1251"?><orders><invoice_no>' . $savedOrderData['order_num'] . '</invoice_no>';
$isTransactionAlreadyExists = false;
if (!empty($savedOrderData['payment'])) {
foreach ($savedOrderData['payment'] as $payment) {
if ($payment['TransactionId'] == $data['TransactionId']) {
$isTransactionAlreadyExists = true;
$result['code'] = 0;
}
}
}
if (empty($savedOrderData['payment'])) {
$uploadXmlToFtp = true;
$xml .= '<time>' . $this->patchTimestampForJS($data['DateTime'], -3, true, 'Y-m-d H:i:s') . '</time><paid>ok</paid><paid_summa>'.$data['Amount'].'</paid_summa><gateway>РНКБ</gateway>';
} else {
if (!$isTransactionAlreadyExists) {
$uploadXmlToFtp = true;
$xml .= '<time>' . $this->patchTimestampForJS($data['DateTime'], -3, true, 'Y-m-d H:i:s') . '</time><paid>order_already</paid><paid_summa>'.$data['Amount'].'</paid_summa><gateway>РНКБ</gateway>';
}
}
$xml .= '</orders>';
$xml = mb_convert_encoding(str_replace("\n", "", $xml), "Windows-1251", "UTF-8");
if ($uploadXmlToFtp) {
$ftpParams = Configure::read("ftpParams");
$ftpConnectionId = ftp_connect($ftpParams['ip'], (!empty($ftpParams['port']) ? $ftpParams['port'] : 21), 5);
if (!empty($ftpConnectionId)) {
$ftpLoginResult = ftp_login($ftpConnectionId, $ftpParams['username'], $ftpParams['password']);
ftp_pasv($ftpConnectionId, true);
if ($ftpLoginResult) {
$ftpTargetDir = "";
switch($savedOrderData['domain']) {
case "apps.supersam24.ru":
$ftpTargetDir = $ftpParams['acquiring24Out'];
break;
case "apps.supersamsev.ru":
$ftpTargetDir = $ftpParams['acquiringOut'];
break;
case "apps.prometeysev.ru":
$ftpTargetDir = $ftpParams['acquiringPrometeyOut'];
break;
}
ftp_chdir($ftpConnectionId, $ftpTargetDir);
$tmpFile = tmpfile();
fwrite($tmpFile, $xml);
rewind($tmpFile);
$filename = 'acquiring_in_' . date('Y-m-d_H-i-s') . '_' . bin2hex(random_bytes(4)) . '.xml';
if (ftp_fput($ftpConnectionId, $filename, $tmpFile, FTP_BINARY, 0)) {
$tmpOrder = $orderPaymentTable->get($decryptedData['orderId']);
if (empty($tmpOrder->payment)) {
$tmpOrder->payment = [];
} else {
$tmpOrder->payment = json_decode($tmpOrder->payment, true);
}
if (!$isTransactionAlreadyExists) {
$tmpOrder->payment[] = $data;
$tmpOrder->summa_order_decimal_paid += $data['Amount'];
}
$tmpOrder->paid_date = $tmpOrder->payment[count($tmpOrder->payment) - 1]['DateTime'];
$tmpOrder->payment = json_encode($tmpOrder->payment);
$tmpOrder->paymentGateway = "CloudPayments";
/*if (empty($tmpOrder->files)) {
$tmpOrder->files = [];
} else {
$tmpOrder->files = json_decode($tmpOrder->files, true);
}
if (empty($tmpOrder->files["payment"])) {
$tmpOrder->files["payment"] = [];
}
$tmpOrder->files["payment"][] = [
"date" => date('d.m.Y H:i'),
"filename" => $filename,
"xml" => $xml
];
$tmpOrder->files = json_encode($tmpOrder->files);*/
$orderPaymentTable->save($tmpOrder);
$result['code'] = 0;
} else {
//$result['code'] = 1;
};
fclose($tmpFile);
file_put_contents(TMP . $filename, $xml);
TableRegistry::getTableLocator()->get('CutSheets')->putImportFileToArchive(TMP . $filename, false, $filename);
}
ftp_close($ftpConnectionId);
} else {
//$result['code'] = 1;
}
}
} else {
$savedOrderData = null;
}
}
}
}
}
echo $this->jsonSimpleResponse($result);
}
public function callbackRefund() {
$result = [
'code' => 13
];
$data = $this->request->getData();
if (!empty($data['Data'])) {
if (!is_array($data['Data'])) {
$data['Data'] = @json_decode(html_entity_decode($data['Data']), true);
}
file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.txt', json_encode($data));
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$decryptedData = $ordersTable->decryptCustomData($data['Data']['encOrderId'], $this->config);
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.1.txt', json_encode($decryptedData));
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $decryptedData['orderId'],
])->first();
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.2.txt', json_encode($savedOrderData));
if (!empty($savedOrderData)) {
$ftpParams = Configure::read("ftpParams");
$ftpConnectionId = ftp_connect($ftpParams['ip'], (!empty($ftpParams['port']) ? $ftpParams['port'] : 21), 5);
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.3.txt', json_encode($ftpConnectionId));
if (!empty($ftpConnectionId)) {
$ftpLoginResult = ftp_login($ftpConnectionId, $ftpParams['username'], $ftpParams['password']);
ftp_pasv($ftpConnectionId, true);
if ($ftpLoginResult) {
$ftpTargetDir = "";
switch($savedOrderData['domain']) {
case "apps.supersam24.ru":
$ftpTargetDir = $ftpParams['acquiring24Out'];
break;
case "apps.supersamsev.ru":
$ftpTargetDir = $ftpParams['acquiringOut'];
break;
case "apps.prometeysev.ru":
$ftpTargetDir = $ftpParams['acquiringPrometeyOut'];
break;
}
ftp_chdir($ftpConnectionId, $ftpTargetDir);
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.4.txt', json_encode($ftpTargetDir));
//$refundAmount = 0;
$tmpOrder = $orderPaymentTable->get($decryptedData['orderId']);
$isTransactionAlreadyExists = false;
if (!empty($tmpOrder->payment)) {
$tmpOrder->payment = json_decode($tmpOrder->payment, true);
//foreach ($tmpOrder->payment as $paymentKey => $payment) {
// if ($payment['TransactionId'] == $data['PaymentTransactionId']) {
// unset($tmpOrder->payment[$paymentKey]);
$isTransactionAlreadyExists = false;
foreach ($tmpOrder->payment as $payment) {
if ($payment['TransactionId'] == $data['TransactionId']) {
$isTransactionAlreadyExists = true;
}
}
// }
//}
} else {
$tmpOrder->payment = [];
}
$tmpOrder->payment[] = $data;
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.5.txt', json_encode($isTransactionAlreadyExists));
if (!$isTransactionAlreadyExists) {
$tmpOrder->summa_order_decimal_paid -= $data['Amount'];
$refundAmount = $data['Amount'];
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.6.txt', json_encode($tmpOrder));
$xml = '<?xml version="1.0" encoding="windows-1251"?><orders><invoice_no>' . $savedOrderData['order_num'] . '</invoice_no>';
$xml .= '<time>' . $this->patchTimestampForJS($data['DateTime'], -3, true, 'Y-m-d H:i:s') . '</time><paid>refund</paid><paid_summa>' . $refundAmount . '</paid_summa><gateway>РНКБ</gateway>';
$xml .= '</orders>';
$xml = mb_convert_encoding(str_replace("\n", "", $xml), "Windows-1251", "UTF-8");
$tmpFile = tmpfile();
fwrite($tmpFile, $xml);
rewind($tmpFile);
$filename = 'acquiring_in_' . date('Y-m-d_H-i-s') . '_' . bin2hex(random_bytes(4)) . '.xml';
if (ftp_fput($ftpConnectionId, $filename, $tmpFile, FTP_BINARY, 0)) {
/*if (empty($tmpOrder->payment)) {
$tmpOrder->payment = null;
$tmpOrder->paid_date = null;
} else {
$tmpOrder->paid_date = $tmpOrder->payment[count($tmpOrder->payment) - 1]['DateTime'];
$tmpOrder->payment = json_encode($tmpOrder->payment);
}*/
if (empty($tmpOrder->summa_order_decimal_paid)) {
//$tmpOrder->payment = null;
$tmpOrder->paid_date = null;
}
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.7.txt', json_encode($tmpOrder));
/*if (empty($tmpOrder->files)) {
$tmpOrder->files = [];
} else {
$tmpOrder->files = json_decode($tmpOrder->files, true);
}
if (empty($tmpOrder->files["refund"])) {
$tmpOrder->files["refund"] = [];
}
$tmpOrder->files["refund"][] = [
"date" => date('d.m.Y H:i'),
"filename" => $filename,
"xml" => $xml
];
$tmpOrder->files = json_encode($tmpOrder->files);*/
$tmpOrder->payment = json_encode($tmpOrder->payment);
$orderPaymentTable->save($tmpOrder);
$result['code'] = 0;
} else {
//$result['code'] = 1;
//file_put_contents(TMP . 'callbackRefundData' . date('Y-m-d_H-i-s') . '.8.txt', json_encode($tmpOrder));
};
} else {
$result['code'] = 0;
}
fclose($tmpFile);
file_put_contents(TMP . $filename, $xml);
TableRegistry::getTableLocator()->get('CutSheets')->putImportFileToArchive(TMP . $filename, false, $filename);
}
ftp_close($ftpConnectionId);
} else {
//$result['code'] = 1;
}
} else {
$savedOrderData = null;
}
}
}
echo $this->jsonSimpleResponse($result);
}
public function createElplatPaymentQr() {
$result = [
'status' => false
];
$data = $this->request->getData();
if(!empty($data['encOrderId'])) {
$ordersTable = TableRegistry::getTableLocator()->get('CutOrders');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$decryptedData = $ordersTable->decryptCustomData($data['encOrderId'], $this->config);
if (!empty($decryptedData['orderId'])) {
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $decryptedData['orderId'],
'isDeleted' => false,
])->first();
if (!empty($savedOrderData)) {
if (empty($savedOrderData['paid_date'])) {
$elplatCredentials = Configure::read('elplat');
$elplatCredentials = $elplatCredentials[$savedOrderData['domain']];
$elplat = new ElplatMerchantAPI();
$orderData = json_decode($savedOrderData['data'], true);
$paymentPurposeL1 = "";
$paymentPurposeL2 = "";
switch($savedOrderData['domain']) {
case "apps.supersam24.ru":
$paymentPurposeL1 = 'Оплата счета ' . $orderData['invoice_no'] . ', СуперСам';
break;
case "apps.supersamsev.ru":
$paymentPurposeL1 = 'Оплата счета ' . $orderData['invoice_no'] . ', СуперСам';
break;
case "apps.prometeysev.ru":
$paymentPurposeL1 = 'Оплата счета ' . $orderData['invoice_no'] . ', ';
$paymentPurposeL2 = 'ООО ТЦ "Прометей"';
break;
}
$data = [
'login' => $elplatCredentials['login'],
"passwd" => $elplatCredentials['passwd'],
"callback" => "https://" . $savedOrderData['domain'] . Router::url(['controller' => 'OrderPayment', 'action' => 'callback_elplat']),
"qrcType" => "02",
"currency" => "RUB",
"orgId" => $elplatCredentials['orgId'],
"amount" => round($savedOrderData['summa_order_decimal'] * 100),
"paymentPurpose" => $paymentPurposeL1.$paymentPurposeL2,
"email" => (!empty($orderData['email']) ? $orderData['email'] : '')
];
$hashId = $elplat->getElplatHashId($data);
unset($data['passwd']);
$request = $elplat->createQr($elplatCredentials['apiUrl'], array_merge($data, [
"type" => "createQr",
'elplatPhone' => $elplatCredentials['elplatPhone'],
'hashId' => $hashId
]));
if (!empty($request['info']['status'])) {
$elplatPaymentsTable = TableRegistry::getTableLocator()->get('ElplatPayments');
$now = date('Y-m-d H:i:s');
$elplatPayment = $elplatPaymentsTable->newEntity([
'orderPaymentId' => $decryptedData['orderId'],
'qrData' => $request['info']['qrData'],
'qrcId' => $request['info']['qrcId'],
'responseData' => null,
'status' => 'pending',
'created' => $now,
'updated' => $now,
]);
$elplatPaymentsTable->save($elplatPayment);
$result['status'] = true;
$result['qrInfo'] = $request['info'];
$result['paymentPurposeL1'] = $paymentPurposeL1;
$result['paymentPurposeL2'] = $paymentPurposeL2;
$result['paymentAmount'] = $orderData['summa_order'].'р.';
}
}
} else {
$savedOrderData = null;
}
}
}
echo $this->jsonSimpleResponse($result);
}
public function getElpatPaymentStatus() {
$result = [
'status' => 'failure'
];
$data = $this->request->getData();
if(!empty($data['qrcId'])) {
$elplatPaymentsTable = TableRegistry::getTableLocator()->get('ElplatPayments');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$qrData = $elplatPaymentsTable->find('all')->where([
'qrcId ' => $data['qrcId'],
])->first();
if (!empty($qrData)) {
$result['status'] = $qrData['status'];
$orderData = $orderPaymentTable->find('all')->where([
'id' => $qrData['orderPaymentId'],
'isDeleted' => false,
])->first();
if ($result['status'] != 'success') {
//check for payments by other qr
if (!empty($orderData) && !empty($orderData['paid_date'])) {
$result['status'] = 'success';
$result['payment'] = [
'paid_date' => $this->patchTimestampForJS($orderData['paid_date'], -3, true, 'd.m.Y H:i')
];
}
} else {
$result['payment'] = [
'paid_date' => $this->patchTimestampForJS($orderData['paid_date'], -3, true, 'd.m.Y H:i')
];
}
}
}
//$result['status'] = 'success';
echo $this->jsonSimpleResponse($result);
}
public function callbackElplat() {
if (!$this->checkIsElplatCallbackIpAllowed()) {
$ipToCheck = $this->request->clientIP();
if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
$ipToCheck = $_SERVER['HTTP_X_REAL_IP'];
}
file_put_contents(TMP . 'callbackElplatPayData' . date('Y-m-d_H-i-s') . '.error.txt', json_encode(['incorrect callback ip ' . $ipToCheck]));
die('NOTOK');
}
$data = $this->request->getData();
if (empty($data)) {
$data = json_decode(file_get_contents("php://input"), true);
}
file_put_contents(TMP . 'callbackElplatPayData' . date('Y-m-d_H-i-s') . '.txt', json_encode($data));
if(isset($data['payStatus'])) {
$elplatPaymentsTable = TableRegistry::getTableLocator()->get('ElplatPayments');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$qrData = $elplatPaymentsTable->find('all')->where([
'qrcId ' => $data['qrcId'],
])->first();
if (empty($qrData)) {
file_put_contents(TMP . 'callbackElplatPayData' . date('Y-m-d_H-i-s') . '.error.qrnotfoundindb.txt', json_encode($data));
die('NOTOK');
}
$savedOrderData = $orderPaymentTable->find('all')->where([
'id' => $qrData['orderPaymentId'],
'isDeleted' => false,
])->first();
if (empty($savedOrderData)) {
file_put_contents(TMP . 'callbackElplatPayData' . date('Y-m-d_H-i-s') . '.error.ordernotfoundindb.txt', json_encode($data));
die('NOTOK');
}
if (((int)$data['allAmount']) < ((int)($savedOrderData['summa_order_decimal'] * 100))) {
file_put_contents(TMP . 'callbackElplatPayData' . date('Y-m-d_H-i-s') . '.error.incorrectallamount.txt', json_encode($data));
die('NOTOK');
}
$qrStatus = "failure";
if ($data['payStatus'] == 1) {
$qrStatus = "success";
$xml = '<?xml version="1.0" encoding="windows-1251"?><orders><invoice_no>' . $savedOrderData['order_num'] . '</invoice_no>';
$xml .= '<time>' . date('Y-m-d H:i:s') . '</time><paid>ok</paid><paid_summa>'.($data['allAmount']/100).'</paid_summa><gateway>СБП</gateway>';
$xml .= '</orders>';
$xml = mb_convert_encoding(str_replace("\n", "", $xml), "Windows-1251", "UTF-8");
$ftpParams = Configure::read("ftpParams");
$ftpConnectionId = ftp_connect($ftpParams['ip'], (!empty($ftpParams['port']) ? $ftpParams['port'] : 21), 5);
if (!empty($ftpConnectionId)) {
$ftpLoginResult = ftp_login($ftpConnectionId, $ftpParams['username'], $ftpParams['password']);
ftp_pasv($ftpConnectionId, true);
if ($ftpLoginResult) {
$ftpTargetDir = "";
switch($savedOrderData['domain']) {
case "apps.supersam24.ru":
$ftpTargetDir = $ftpParams['acquiring24Out'];
break;
case "apps.supersamsev.ru":
$ftpTargetDir = $ftpParams['acquiringOut'];
break;
case "apps.prometeysev.ru":
$ftpTargetDir = $ftpParams['acquiringPrometeyOut'];
break;
}
ftp_chdir($ftpConnectionId, $ftpTargetDir);
$tmpFile = tmpfile();
fwrite($tmpFile, $xml);
rewind($tmpFile);
$filename = 'acquiring_in_' . date('Y-m-d_H-i-s') . '_' . bin2hex(random_bytes(4)) . '.xml';
if (ftp_fput($ftpConnectionId, $filename, $tmpFile, FTP_BINARY, 0)) {
$tmpOrder = $orderPaymentTable->get($savedOrderData['id']);
$tmpOrder->payment = [];
$tmpOrder->payment[] = $data;
$tmpOrder->summa_order_decimal_paid = $data['allAmount'] / 100;
$tmpOrder->paid_date = date('Y-m-d H:i:s', strtotime("-3 hours"));
$tmpOrder->payment = json_encode($tmpOrder->payment);
$tmpOrder->paymentGateway = "Elplat";
/*if (empty($tmpOrder->files)) {
$tmpOrder->files = [];
} else {
$tmpOrder->files = json_decode($tmpOrder->files, true);
}
if (empty($tmpOrder->files["payment"])) {
$tmpOrder->files["payment"] = [];
}
$tmpOrder->files["payment"][] = [
"date" => date('d.m.Y H:i'),
"filename" => $filename,
"xml" => $xml
];
$tmpOrder->files = json_encode($tmpOrder->files);*/
$orderPaymentTable->save($tmpOrder);
} else {
file_put_contents(TMP . 'callbackElplatPayData' . date('Y-m-d_H-i-s') . '.error.ftpuploaderror.txt', json_encode($data));
};
fclose($tmpFile);
file_put_contents(TMP . $filename, $xml);
TableRegistry::getTableLocator()->get('CutSheets')->putImportFileToArchive(TMP . $filename, false, $filename);
}
ftp_close($ftpConnectionId);
} else {
file_put_contents(TMP . 'callbackElplatPayData' . date('Y-m-d_H-i-s') . '.error.ftpconnecterror.txt', json_encode($data));
}
}
$qrData->responseData = json_encode($data);
$qrData->status = $qrStatus;
$qrData->updated = date('Y-m-d H:i:s');
$elplatPaymentsTable->save($qrData);
//file_put_contents(TMP . 'callbackElplatPayData' . date('Y-m-d_H-i-s') . '.success.ftpuploadok.txt', json_encode($data));
ob_clean();
header_remove();
header("Content-type: application/json");
http_response_code(200);
echo json_encode(["status" => true]);
exit;
} else {
file_put_contents(TMP . 'callbackElplatPayData' . date('Y-m-d_H-i-s') . '.error.paystatust0.txt', json_encode($data));
die('NOTOK');
}
}
public function checkIsElplatCallbackIpAllowed() {
$allow = false;
$allowedIps = ['91.223.44.0/24'];
$ipToCheck = $this->request->clientIP();
if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
$ipToCheck = $_SERVER['HTTP_X_REAL_IP'];
}
foreach($allowedIps as $ip_allow) {
// If IP has / means CIDR notation
if(strpos($ip_allow, '/') === false) {
// Check Single IP
if(inet_pton($ipToCheck) == inet_pton($ip_allow)) {
$allow = true;
break;
}
} else {
// Check IP range
list($subnet, $bits) = explode('/', $ip_allow);
// Convert subnet to binary string of $bits length
$subnet = unpack('H*', inet_pton($subnet)); // Subnet in Hex
foreach($subnet as $i => $h) $subnet[$i] = base_convert($h, 16, 2); // Array of Binary
$subnet = substr(implode('', $subnet), 0, $bits); // Subnet in Binary, only network bits
// Convert remote IP to binary string of $bits length
$ip = unpack('H*', inet_pton($ipToCheck)); // IP in Hex
foreach($ip as $i => $h) $ip[$i] = base_convert($h, 16, 2); // Array of Binary
$ip = substr(implode('', $ip), 0, $bits); // IP in Binary, only network bits
// Check network bits match
if($subnet == $ip) {
$allow = true;
break;
}
}
}
return $allow;
}
}
+231
View File
@@ -0,0 +1,231 @@
<?php
namespace App\Controller;
use Cake\Core\Configure;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
class OrdersInfoController extends AppController {
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow(['display', 'authorize']);
$this->viewBuilder()->setLayout('orders_info');
}
public function display() {
$jsConfig = $this->getAppJsConfig("ordersInfo", false);
$jsConfig['isPhoneAuthorized'] = false;
if (!empty($_COOKIE['oip'])) {
$decryptedData = TableRegistry::getTableLocator()->get('CutOrders')->decryptCustomData($_COOKIE['oip'], $this->config);
if (!empty($decryptedData['phone'])) {
$jsConfig['isPhoneAuthorized'] = true;
$jsConfig['authorizedPhone'] = $decryptedData['phone'];
} else {
@setcookie("oip", "", time() - 3600, "/", NULL, true, true);
}
}
$data = $this->request->getData();
if (!empty($data['action'])) {
$result = [
'status' => 'error',
'errors' => [],
'isPhoneAuthorized' => $jsConfig['isPhoneAuthorized']
];
if (!empty($jsConfig['authorizedPhone'])) {
$result['authorizedPhone'] = $jsConfig['authorizedPhone'];
switch($data['action']) {
case "searchOrders":
$result = $this->searchOrdersInfoOrders($result, $data);
break;
}
}
echo $this->jsonSimpleResponse($result);
}
$this->set('jsConfig', $jsConfig);
}
private function searchOrdersInfoOrders($result, $data) {
//$result['authorizedPhone'] = '+117 (978) 260-77-53';
$searchPhone = "%".cleanupPhone(trim($result['authorizedPhone']))."%";
$conditions = [];
if (!empty($searchPhone)) {
$conditions[] = [
'tel LIKE' => $searchPhone,
];
}
$result['totalOrders'] = 0;
$conditions = array_merge($conditions, ['isDeleted' => false]);
$currentPage = (!empty($data['currentPage']) ? (int)$data['currentPage']: 1);
$numPerPage = (!empty($data['numPerPage']) ? (int)$data['numPerPage']: 25);
$sort = [
'field' => 'created',
'direction' => 'desc'
];
if (!empty($data['sortFields'])) {
switch($data['sortFields']['field']) {
case 'nom':
case 'fio':
case 'totalSum':
case 'created':
$sort['field'] = $data['sortFields']['field'];
break;
}
}
if (!in_array($data['sortFields']['direction'], ['asc', 'desc'])) {
$sort['direction'] = 'desc';
} else {
$sort['direction'] = $data['sortFields']['direction'];
}
$ordersInfoTable = TableRegistry::getTableLocator()->get("OrdersInfo");
$ordersList = $ordersInfoTable->find('all')->where($conditions)->order([$sort['field'] => $sort['direction']])->page($currentPage, $numPerPage)->all();
if (!empty($ordersList)) {
$ordersList = $ordersList->toArray();
foreach($ordersList as $key => $order) {
$order['data'] = json_decode($order['data'], true);
$order['tel'] = formatPhone($order['tel']);
$order['created'] = $this->patchTimestampForJS($order['created'], 0, true, 'd.m.Y');
}
$result['totalOrders'] = $ordersInfoTable->find('all')->where($conditions)->count();
$result['ordersTotalSum'] = str_replace(".00", "", toFixed2Price($ordersInfoTable->find('all')->where($conditions)->sumOf('totalSum')));
}
$result['ordersList'] = (!empty($ordersList) ? $ordersList : []);
$result['status'] = "success";
return $result;
}
public function authorize() {
$result = [
'status' => 'error'
];
$data = $this->request->getData();
if(!empty($data['action'])) {
switch($data['action']) {
case "sendConfirmCode":
if (!empty($data['phoneToAuthorize'])) {
$ordersInfoAuthTable = TableRegistry::getTableLocator()->get("OrdersInfoAuth");
if (!empty($data['confirmCodeHash'])) {
$confirmCode = $ordersInfoAuthTable->find('all')->where([
'hash' => $data['confirmCodeHash'],
'phone' => $data['phoneToAuthorize']
])->first();
if (!empty($confirmCode)) {
if ((date('Y-m-d H:i:s', strtotime('-120 seconds')) > date('Y-m-d H:i:s', strtotime($confirmCode['created'])))) {
$ordersInfoAuthTable->delete($confirmCode);
} else {
$result['errors'] = ['Ошибка отправки SMS с кодом подтверждения. Пожалуйста, повторите позднее.'];
}
}
}
if (empty($result['errors'])) {
$confirmCode = $ordersInfoAuthTable->generateConfirmCode();
$result['errors'] = ['Ошибка отправки SMS с кодом подтверждения. Пожалуйста, повторите позднее.'];
$settingsTable = TableRegistry::getTableLocator()->get('Settings');
$smsRuApiKey = $settingsTable->find()->where([
'key' => 'smsRuApiKey',
'group' => 'global'
])->first();
if (!empty($smsRuApiKey)) {
$smsResult = TableRegistry::getTableLocator()->get('CutOrders')->sendSms([$data['phoneToAuthorize']], "СуперСам. Ваш код подтверждения: " . $confirmCode, $smsRuApiKey->value, "", true);
//$smsResult = TableRegistry::getTableLocator()->get('CutOrders')->sendSms(['+7 (978) 1462978'], "Ваш код подтвеждения: " . $confirmCode, $smsRuApiKey->value, "");
if (!empty($smsResult->status) && ($smsResult->status == 'OK')) {
$created = date('Y-m-d H:i:s');
$currentConfirmCode = $ordersInfoAuthTable->newEntity([
'phone' => $data['phoneToAuthorize'],
'confirm_code' => $confirmCode,
'created' => $created,
'hash' => $ordersInfoAuthTable->generateCustomHash($data['phoneToAuthorize'] . $created)
]);
if ($ordersInfoAuthTable->save($currentConfirmCode)) {
$result['status'] = "success";
$result['confirmCodeIsSent'] = true;
$result['confirmCodeHash'] = $currentConfirmCode['hash'];
unset($result['errors']);
};
}
}
}
}
break;
case "checkConfirmCode":
if (!empty($data['phoneToAuthorize'])) {
$ordersInfoAuthTable = TableRegistry::getTableLocator()->get("OrdersInfoAuth");
$confirmCode = $ordersInfoAuthTable->find('all')->where([
'hash' => $data['confirmCodeHash'],
'phone' => $data['phoneToAuthorize']
])->first();
if (!empty($confirmCode)) {
if ($confirmCode['try_count'] >= 3) {
$result['errors'] = ['Вы слишком много раз неверно ввели код подтверждения. Повторите попытку позже.'];
$result['tooManyCodeInputRetries'] = true;
} else {
if ($confirmCode['confirm_code'] == $data['confirmCode']) {
$encryptedPhone = TableRegistry::getTableLocator()->get('CutOrders')->encryptCustomData(
$data['phoneToAuthorize'],
'phone',
["encrypt" => Configure::read("encrypt")]
);
@setcookie("oip", $encryptedPhone, time() + 3 * 30 * 24 * 3600, "/", NULL, true, false); //3 months
$ordersInfoAuthTable->delete($confirmCode);
$result['isPhoneAuthorized'] = true;
$result['status'] = "success";
} else {
$confirmCode->try_count++;
$ordersInfoAuthTable->save($confirmCode);
if ($confirmCode['try_count'] >= 3) {
$result['errors'] = ['Вы слишком много раз неверно ввели код подтверждения. Повторите попытку позже.'];
$result['tooManyCodeInputRetries'] = true;
} else {
$result['errors'] = ['Неверно указан код подтверждения. Повторите попытку.'];
}
}
}
} else {
$result['errors'] = ['Неверно указан код подтверждения. Повторите попытку.'];
}
}
break;
}
}
echo $this->jsonSimpleResponse($result);
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace App\Controller;
use Cake\Event\Event;
class PagesController extends AppController {
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow(['services']);
$this->viewBuilder()->setLayout('pages');
}
public function services() {
$this->set('jsConfig', $this->getAppJsConfig("", false));
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
use Cake\Auth\DefaultPasswordHasher;
class User extends Entity {
protected $_accessible = [
'*' => true,
'id' => false
];
protected function _setPassword($password) {
return (new DefaultPasswordHasher)->hash($password);
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class BranchesTable extends Table {
public function initialize(array $config) {
$this->setTable('branches');
}
}
+613
View File
@@ -0,0 +1,613 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use Cake\Routing\Router;
use DBF;
use PHPExcel;
use PHPExcel_Writer_Excel5;
use SMSRU;
//use mPDF;
use Mpdf\Mpdf;
use Cake\Core\Configure;
class CalcDoorsOrdersTable extends Table {
public function initialize(array $config) {
$this->setTable('calc_doors_orders');
}
public function orderEmailNotify($data, $cutConfig, $clientIP, $authUserId = null, $configDoor) {
$result = 'error';
$selectedBranch = (!empty($data['branchId']) && !empty($cutConfig['branches'][$data['branchId']]) ? $cutConfig['branches'][$data['branchId']] : null);
$selectedBranchId = (!empty($data['branchId']) && !empty($cutConfig['branches'][$data['branchId']]) ? $data['branchId'] : null);
if (empty($selectedBranch)) {
foreach($cutConfig['branches'] as $key => $li) {
if (!empty($li['is_default'])) {
$selectedBranch = $li;
$selectedBranchId = $key;
break;
}
}
}
$orderPhone = (!empty($data['orderPhone']) ? htmlentities($data['orderPhone']) : "");
$orderAddress = (!empty($data['orderAddress']) ? htmlentities($data['orderAddress']) : "не указан");
$orderName = (!empty($data['orderName']) ? htmlentities($data['orderName']) : 'не указано');
$orderComment = (!empty($data['orderComment']) ? nl2br(htmlentities($data['orderComment'])) : 'не указан');
$newOrderData = [
'name' => $data['orderName'],
'email' => $data['orderEmail'],
'phone' => $data['orderPhone'],
'address' => $data['orderAddress'],
'comment' => $data['orderComment'],
'price' => (!empty($data['price']) ? $data['price'] : str_replace(" ", "", $data['selectedDoorData']['order_calc_results']['price'])),
'actual_price' => (!empty($data['actualPrice']) ? $data['actualPrice'] : str_replace(" ", "", $data['selectedDoorData']['order_calc_results']['price'])),
'params' => json_encode($data['selectedDoorData']['params']),
'order_calc_results' => json_encode($data['selectedDoorData']['order_calc_results'])
];
if ($selectedBranchId == "printOnly") {
$newOrderData["order_type"] = "print";
} else {
$newOrderData["order_type"] = "order";
$newOrderData["branch_id"] = $selectedBranchId;
}
$orderNum = "";
if(!empty($data['eOId'])) {
$encryptedOrderId = $data['eOId'];
$decryptedData = TableRegistry::getTableLocator()->get('CutOrders')->decryptCustomData($encryptedOrderId, $cutConfig);
if (!empty($decryptedData['orderId'])) {
$order = $this->find('all')->where(['id' => $decryptedData['orderId']])->first();
if (!empty($order)) {
$this->patchEntity($order, $newOrderData);
$orderNum = $order['order_num'];
}
}
}
if (empty($order)) {
$newOrderData['order_num'] = '';
$newOrderData['created'] = date('Y-m-d H:i:s');
$order = $this->newEntity($newOrderData);
$data['eOId'] = false;
}
if ($this->save($order)) {
$orderId = $order->id;
if (empty($encryptedOrderId)) {
$encryptedOrderId = TableRegistry::getTableLocator()->get('CutOrders')->encryptCustomData($orderId, 'orderId', $cutConfig);
if (!empty($encryptedOrderId)) {
$viewOrderUrlBase = '/calc-doors?oid=' . $encryptedOrderId;
}
}
if (empty($order->order_num)) {
$cutSettingsTable = TableRegistry::getTableLocator()->get('CalcDoorsSettings');
$orderNumMonth = date('Y-m-01');
$orderNumDefaultCounters = [
'order' => 0,
'print' => 0
];
$orderNumSetting = $cutSettingsTable->find()->where([
'key' => 'orderNumCounter',
'group' => 'common'
])->first();
if (empty($orderNumSetting)) {
$orderNumDefaultCounters[$newOrderData["order_type"]]++;
$orderNumCounter = 1;
$orderNumSetting = $cutSettingsTable->newEntity();
$orderNumSetting = $cutSettingsTable->patchEntity($orderNumSetting, [
'key' => 'orderNumCounter',
'value' => json_encode([
'month' => $orderNumMonth,
'counters' => $orderNumDefaultCounters
]),
'group' => 'common'
]);
} else {
$orderNumSetting->value = @json_decode($orderNumSetting->value, true);
if ($orderNumSetting->value['month'] != $orderNumMonth) {
$orderNumSetting->value['month'] = $orderNumMonth;
$orderNumDefaultCounters[$newOrderData["order_type"]]++;
$orderNumCounter = 1;
$orderNumSetting->value['counters'] = $orderNumDefaultCounters;
} else {
$orderNumSetting->value['counters'][$newOrderData["order_type"]]++;
$orderNumCounter = $orderNumSetting->value['counters'][$newOrderData["order_type"]];
}
$orderNumSetting->value = json_encode($orderNumSetting->value);
}
$cutSettingsTable->save($orderNumSetting);
$orderNum = $cutConfig['common']['orderNumPrefix']['value'] . date('dmY') . "-" . str_pad($orderNumCounter, $cutConfig['common']['orderNumLength']['value'], "0", STR_PAD_LEFT);
if ($newOrderData["order_type"] == "print") {
$orderNum .= "-P";
}
$order->order_num = $orderNum;
}
if (!empty($viewOrderUrlBase)) {
$viewOrderUrl = '/calc-doors?oid=' . $encryptedOrderId;
$order->view_order_url = $viewOrderUrlBase;
}
$this->save($order);
$orderDate = date('d.m.Y H:i');
$orderSpec['order'] = '<br/>
Номер заказа: ' . ($order->order_type == "order" ? $orderNum : "-"). '<br/>' . '
Дата заказа: ' . $orderDate . '<br/>';
if (($selectedBranchId != "printOnly") && !empty($selectedBranch)) {
$orderSpec['order'] .= 'Контакт-менеджер: ' . $selectedBranch['label'] . '<br/>';
}
$orderSpec['client'] = 'Данные клиента: <br/>
ФИО: ' . $orderName . '<br/>' ;
if (!empty($data['orderEmail'])) {
$orderSpec['client'] .= 'E-mail: ' . htmlentities($data['orderEmail']) . '<br/>';
}
if (!empty($orderPhone)) {
$orderSpec['client'] .= 'Телефон: ' . $orderPhone . '<br/>';
}
if (!empty($orderAddress)) {
$orderSpec['client'] .= 'Адрес доставки: ' . $orderAddress . '<br/>';
}
if (!empty($data['selectedDoorData']['params']['discount'])) {
$orderSpec['client'] .= 'Скидка: ' . min((int) $data['selectedDoorData']['params']['discount'], 100) . '%' . '<br/>';
}
if (!empty($data['orderComment'])) {
$orderSpec['client'] .= 'Комментарий к заказу: ' . nl2br(htmlentities($data['orderComment'])) . '<br/>';
}
$orderSpec['client'] .= '<br/>
ПрофильС: ' . $configDoor['profile'] . " " . $cutConfig["doorsManufacturers"][$configDoor['manufacturer_id']]["label"] . '<br/>' . '
Цвет системы: ' . $configDoor['color'] . '<br/>' . '
Направ.верх: ' . $configDoor['colorNapravlyaushayaVerh'] . '<br/>' . '
Направ.низ: ' . $configDoor['colorNapravlyaushayaNiz'] . '<br/>';
$filename = $orderNum; //. "_" . date('d-m-Y');
$specFilename = $filename. "_specification" . ".pdf";
$tmpSpecFile = $this->generateSpecFile($data, $cutConfig, $orderSpec, $orderNum, $order["order_type"]);
if (empty($data['eOId']) && ($selectedBranchId != "printOnly")) { //new order, send email
$mail = TableRegistry::getTableLocator()->get('CutOrders')->configurePHPMailer($cutConfig);
foreach (explode("\n", $selectedBranch['order_notify_manager_emails']) as $managerEmail) {
$managerEmail = trim($managerEmail);
if (!empty($managerEmail)) {
$mail->addAddress(trim($managerEmail));
}
}
$mail->addAttachment($tmpSpecFile, $specFilename);
$mail->Subject = str_replace("{OrderNum}", $orderNum, $cutConfig['common']['orderNotifySubject']['value']);
$mail->Body = $cutConfig['common']['orderNotifyBodyStart']['value'];
$mail->AltBody = $cutConfig['common']['orderNotifyBodyStart']['value'];
$mail->Body .= '<br/>
Номер заказа: ' . ($order->order_type == "order" ? $orderNum : "-") . '<br/>' . '
Дата заказа: ' . $orderDate . '<br/>';
$mail->AltBody .= '
Номер заказа: ' . ($order->order_type == "order" ? $orderNum : "-") . '
Дата заказа: ' . $orderDate;
if (!empty($selectedBranch)) {
$mail->Body .= 'Контакт-менеджер: ' . $selectedBranch['label'] . '<br/>';
$mail->AltBody .= '
Контакт-менеджер: ' . $selectedBranch['label'];
}
$mail->Body .= '<br/><br/>
ФИО: ' . $orderName . '<br/>' . '
E-mail: ' . (!empty($data['orderEmail']) ? '<a href="mailto:' . htmlentities($data['orderEmail']) . '">' . htmlentities($data['orderEmail']) . '</a>' : 'не указан') . '<br/>' . '
Телефон: ' . (!empty($orderPhone) ? '<a href="tel:' . preg_replace("/[^0-9.]/", "", $orderPhone) . '">' . $orderPhone . '</a>' : 'не указан') . '<br/>' . '
Адрес доставки: ' . (!empty($orderAddress) ? $orderAddress : 'не указан') . '<br/>' . '
' . (!empty($data['selectedDoorData']['params']['discount']) ? 'Скидка: ' . $data['selectedDoorData']['params']['discount'] . '%' . '<br/>' : '') . '
Комментарий: ' . $orderComment . '<br/>';
$mail->AltBody .= '
ФИО: ' . $orderName . '
E-mail: ' . (!empty($data['orderEmail']) ? htmlentities($data['orderEmail']) : 'не указан') . '
Телефон: ' . (!empty($orderPhone) ? $orderPhone : 'не указан') . '
Адрес доставки: ' . (!empty($orderAddress) ? $orderAddress : 'не указан') . '
' . (!empty($data['selectedDoorData']['params']['discount']) ? 'Скидка: ' . $data['selectedDoorData']['params']['discount'] . '%' : '') . '
Комментарий: ' . $orderComment;
$mail->Body .= '<br/>
ПрофильС: ' . $configDoor['profile'] . " " . $cutConfig["doorsManufacturers"][$configDoor['manufacturer_id']]["label"] . '<br/>' . '
Цвет системы: ' . $configDoor['color'] . '<br/>' . '
Направ.верх: ' . $configDoor['color'] . '<br/>' . '
Направ.низ: ' . $configDoor['color'] . '<br/>';
$mail->AltBody .= '<br/>
ПрофильС: ' . $configDoor['profile'] . " " . $cutConfig["doorsManufacturers"][$configDoor['manufacturer_id']]["label"] . '
Цвет системы: ' . $configDoor['color'] . '
Направ.верх: ' . $configDoor['color'] . '
Направ.низ: ' . $configDoor['color'] ;
if (!empty($viewOrderUrl)) {
$mail->Body .= '<br/><br/>
<a href="' . Router::fullBaseUrl() . $viewOrderUrl . '">Ссылка для печати заказа</a>';
$mail->AltBody .= '
Ссылка для печати заказа: ' . Router::fullBaseUrl() . $viewOrderUrl;
}
$order->html_letter = $mail->Body;
$order->text_letter = $mail->AltBody;
$this->save($order);
$mail->send();
}
$result = 'success';
if (!empty($orderNum)) {
$zip = new \ZipArchive;
if ($zip->open(ROOT . '/files/calc_doors_orders/' . $orderNum . '.zip', \ZipArchive::CREATE | \ZipArchive::OVERWRITE) === true) {
$zip->addFile($tmpSpecFile, $specFilename);
$zip->close();
}
}
@unlink($tmpSpecFile);
if (empty($data['eOId'])) { //new order, send sms
if (!empty($selectedBranch['order_notify_manager_sms'])) {
TableRegistry::getTableLocator()->get('CutOrders')->sendSms(explode("\n", $selectedBranch['order_notify_manager_sms']), "Появился новый заказ " . $orderNum, $cutConfig['global']['smsRuApiKey']['value'], $clientIP);
}
}
if ($newOrderData["order_type"] == "order") {
if (!empty($data['eOId'])) { //save order, revert stat
$order->updated_date = date('Y-m-d H:i:s');
$order->updated_by_manager = $authUserId;
$this->save($order);
}
}
}
if (empty($cutConfig['global']['additionalActionsAllowed'])) {
$viewOrderUrl = "";
}
return [
'orderEmailNotifySent' => $result,
'printOrderUrl' => (!empty($viewOrderUrl) ? str_replace("/calc-doors", "/calc-doors-print", $viewOrderUrl): "")
];
}
public function resendOrderEmailNotify($order, $managerEmail, $cutConfig) {
$mail = TableRegistry::getTableLocator()->get('CutOrders')->configurePHPMailer($cutConfig);
$mail->addAddress(trim($managerEmail));
$mail->Subject = str_replace("{OrderNum}", $order->order_num, $cutConfig['common']['orderNotifySubject']['value']);
$mail->Body = (!empty($order->html_letter) ? $order->html_letter : $cutConfig['common']['orderNotifyBodyStart']['value']);
$mail->AltBody = (!empty($order->text_letter) ? $order->text_letter : $cutConfig['common']['orderNotifyBodyStart']['value']);
$zip = @zip_open(ROOT . '/files/calc_doors_orders/' . $order->order_num . '.zip');
if (!empty($zip)) {
while ($zip_entry = zip_read($zip)) {
$mail->addStringAttachment(zip_entry_read($zip_entry, zip_entry_filesize($zip_entry)), zip_entry_name($zip_entry));
}
@zip_close($zip);
}
$mail->send();
}
function generateSpecFile($data, $cutConfig, $orderSpec, $orderNum, $orderType) {
$tmpSpecFile = @tempnam("/tmp", "calc_doors_order_email_spec_pdf");
$html = "<style>
body { font-size: 12px; font-family: arial, sans-serif; }
table { width: 100%; border: 1px solid #000; border-collapse: collapse; }
table th, table td { padding: 5px; border: 1px solid #000; text-align: left; }
table td { padding: 3px 5px; }
table th { font-weight: normal; }
table th.text-center, table td.text-center { text-align: center; }
.borderDiv { font-size: 2px; line-height: 10px; }
.border1DivTop { border-top: 1px solid #000; }
.border1DivBottom { border-bottom: 1px solid #000; }
.border2DivTop { border-top: 1px dashed #000;}
.border2DivBottom { border-bottom: 1px dashed #000;}
.border1divWide { border-top: 2px solid #000; }
.border2divWide { border-top: 2px dashed #000; }
.border1divWide, .border2divWide { font-size: 4px; }
table.table-footer, table.table-header { margin-top: 0; }
table.table-footer td, table.table-header td { font-size: 12px; padding: 5px 0 5px 0; }
table.table-footer td { padding: 3px 0 3px 0; }
table.table-border-less, table.table-border-less td { border: 0 none; }
</style>";
$html .= $orderSpec['order'] . '<br/>';
$html .= $orderSpec['client'] . '<br/>';
$params = $data['selectedDoorData']['params'];
$calc = $data['selectedDoorData']['order_calc_results'];
$html .= "Высота проема: " . $params['height'] . "мм" . "<br/>" .
"Ширина проема: " . $params['width'] . "мм" . "<br/>" .
"Количество дверей: " . $params['doorsCount'] . "шт" . "<br/>";
$configDoor = TableRegistry::getTableLocator()->get('CalcDoors')->find('all')->where([
'id' => $params['doorId']
])->first();
$doorPrices = [];
$manufacturerPrices = [];
$manufacturerLimits = [];
if (!empty($configDoor)) {
$configDoor = $configDoor->toArray();
$doorPrices = @json_decode($configDoor['params'], true);
$configManufacturer = TableRegistry::getTableLocator()->get('CalcDoorsSettings')->find('all')->where([
'group' => 'prices_' . $configDoor['manufacturer_id']
])->all()->toArray();
foreach($configManufacturer as $li) {
$manufacturerPrices[$li['key']] = $li['value'];
}
$configManufacturer = TableRegistry::getTableLocator()->get('CalcDoorsSettings')->find('all')->where([
'group' => 'limits_' . $configDoor['manufacturer_id']
])->all()->toArray();
foreach($configManufacturer as $li) {
$manufacturerLimits[$li['key']] = $li['value'];
}
}
$html .= '<newpage> <table>
<thead><tr><th style="width: 300px;"><b>Наименование</b></th><th><b>Ед.изм</b></th><th><b>Кол-во</b></th><th><b>Цена (руб.)</b></th><th><b>Стоимость (руб.)</b></th></tr></thead><tbody>';
$html .= '
<tr>
<td>Профиль вертикальный С</td>
<td>шт</td>
<td>'.toFixed2($calc['profileVertCount']).'</td>
<td>'.toFixed2Price(($params['height'] > $doorPrices['profileVertHeight']) && !empty($doorPrices['profileVertHigherLimit']) ? $doorPrices['profileVertHigherLimit'] : $doorPrices['profileVertBelowLimit']).'</td>
<td>'.toFixed2Price($calc['profileVertPrice']).'</td>
</tr>
<tr>
<td>Направляющая верхняя</td>
<td>м</td>
<td>'.toFixed2($calc['napravlyaushayaVerhCount']).'</td>
<td>'.toFixed2Price($doorPrices['napravlyaushayaVerh']).'</td>
<td>'.toFixed2Price($calc['napravlyaushayaVerhPrice']).'</td>
</tr>
<tr>
<td>Направляющая нижняя</td>
<td>м</td>
<td>'.toFixed2($calc['napravlyaushayaNizCount']).'</td>
<td>'.toFixed2Price($doorPrices['napravlyaushayaNiz']).'</td>
<td>'.toFixed2Price($calc['napravlyaushayaNizPrice']).'</td>
</tr>
<tr>
<td>Рама горизонт.низ</td>
<td>м</td>
<td>'.toFixed2($calc['ramaGorizontNizCount']).'</td>
<td>'.toFixed2Price($doorPrices['ramaGorizontNiz']).'</td>
<td>'.toFixed2Price($calc['ramaGorizontNizPrice']).'</td>
</tr>
<tr>
<td>Рама горизонт.верх.</td>
<td>м</td>
<td>'.toFixed2($calc['ramaGorizontVerhCount']).'</td>
<td>'.toFixed2Price($doorPrices['ramaGorizontVerh']).'</td>
<td>'.toFixed2Price($calc['ramaGorizontVerhPrice']).'</td>
</tr>
<tr>
<td>Комплект роликов на 1-у дверь</td>
<td>комп</td>
<td>'.$calc['komplektRolikovCount'].'</td>
<td>'.toFixed2Price($manufacturerPrices['komplektRolikov']).'</td>
<td>'.toFixed2Price($calc['komplektRolikovPrice']).'</td>
</tr>
<tr>
<td>Стопор</td>
<td>шт</td>
<td>'.$calc['stoporCount'].'</td>
<td>'.toFixed2Price($manufacturerPrices['stopor']).'</td>
<td>'.toFixed2Price($calc['stoporPrice']).'</td>
</tr>
<tr>
<td>Шлегель</td>
<td>м</td>
<td>'.toFixed2($calc['shlegelCount']).'</td>
<td>'.toFixed2Price($manufacturerPrices['shlegel']).'</td>
<td>'.toFixed2Price($calc['shlegelPrice']).'</td>
</tr>
<tr>
<td>Зажим д\шлегеля</td>
<td>шт</td>
<td>'.$calc['zagimDlyaShlegelyaCount'].'</td>
<td>'.toFixed2Price($manufacturerPrices['zagimDlyaShlegelya']).'</td>
<td>'.toFixed2Price($calc['zagimDlyaShlegelyaPrice']).'</td>
</tr>
<tr>
<td>Уплотнитель ПК-80-0104</td>
<td>м</td>
<td>'.toFixed2($calc['uplotnitelPK80Count']).'</td>
<td>'.toFixed2Price($manufacturerPrices['uplotnitelPK80']).'</td>
<td>'.toFixed2Price($calc['uplotnitelPK80Price']).'</td>
</tr>
<tr>
<td colspan="4"><b>Итого профиля</b></td>
<td><b>'.toFixed2Price($calc['profilePrice']).'</b></td>
</tr>
<tr>
<td>Зеркало серебро</td>
<td>кв.м.</td>
<td>'.toFixed2($calc['zerkaloSerebroCount']).'</td>
<td>'.toFixed2Price($manufacturerPrices['zerkaloSerebro']).'</td>
<td>'.toFixed2Price($calc['zerkaloSerebroPrice']).'</td>
</tr>
<tr>
<td>Пленка безопасности д\зеркал</td>
<td>пог.м.</td>
<td>'.toFixed2($calc['plenkaBezopasnostiZerkalCount']).'</td>
<td>'.toFixed2Price($manufacturerPrices['plenkaBezopasnostiZerkal']).'</td>
<td>'.toFixed2Price($calc['plenkaBezopasnostiZerkalPrice']).'</td>
</tr>
<tr>
<td>Оракал белый</td>
<td>м. кв.</td>
<td>'.toFixed2($calc['plenkaOrakalCount']).'</td>
<td>'.toFixed2Price($manufacturerPrices['plenkaOrakal']).'</td>
<td>'.toFixed2Price($calc['plenkaOrakalPrice']).'</td>
</tr>
<tr>
<td>Стекло</td>
<td>кв.м.</td>
<td>'.toFixed2($calc['StekloCount']).'</td>
<td>'.toFixed2Price($manufacturerPrices['Steklo']).'</td>
<td>'.toFixed2Price($calc['StekloPrice']).'</td>
</tr>
<tr>
<td>ДСП 10мм лист</td>
<td>лист</td>
<td>'.toFixed2($calc['dsp10mmCount']).'</td>
<td>'.toFixed2Price($manufacturerPrices['dsp10mm']).'</td>
<td>'.toFixed2Price($calc['dsp10mmPrice']).'</td>
</tr>
<tr>
<td colspan="4"><b>Итого наполнение</b></td>
<td><b>'.toFixed2Price($calc['napolneniePrice']).'</b></td>
</tr>
<tr>
<td>Сборка 1-ой двери</td>
<td>кв.м.</td>
<td>'.toFixed2($calc['sborkeDveriCount']).'</td>
<td>'.toFixed2Price($manufacturerPrices['sborkeDveri']).'</td>
<td>'.toFixed2Price($calc['sborkeDveriPrice']).'</td>
</tr>
<tr>
<td>Порезка '.$calc['porezkaMaterialaPercent'].'% от стоимости материала</td>
<td>'.$calc['porezkaMaterialaPercent'].'%</td>
<td>'.$calc['porezkaMaterialaCount'].'</td>
<td>'.toFixed2Price($calc['porezkaMaterialaPrice']).'</td>
<td>'.toFixed2Price($calc['porezkaMaterialaTotalPrice']).'</td>
</tr>
<tr>
<td>Сверление отверстий на одну дверь</td>
<td>1 дверь</td>
<td>'.$calc['sverlenieOtverstiyNaDverCount'].'</td>
<td>'.toFixed2Price($manufacturerPrices['sverlenieOtverstiyNaDver']).'</td>
<td>'.toFixed2Price($calc['sverlenieOtverstiyNaDverPrice']).'</td>
</tr>
<tr>
<td>Работа сложная</td>
<td></td>
<td>'.toFixed2($calc['rabotaSlognayaCount']).'</td>
<td>'.toFixed2Price($manufacturerPrices['rabotaSlognaya']).'</td>
<td>'.toFixed2Price($calc['rabotaSlognayaPrice']).'</td>
</tr>
<tr>
<td colspan="4"><b>Итого работа, руб.</b></td>
<td><b>'.toFixed2Price($calc['rabotaPrice']).'</b></td>
</tr>
<tr>
<td colspan="4"><b>Итого, руб.</b></td>
<td class="total-price"><b>'.toFixed2Price($calc['price']).'</b></td>
</tr>
';
$html .= '</tbody></table><br/>';
$mpdf = new \Mpdf\Mpdf(Configure::read('mpdfPageParams'));
$mpdf->SetHTMLHeader('
<table width="100%" class="table-header table-border-less">
<tr>
<td width="80%" style="text-align: left;"><b>Спецификация заказа на изготовление дверей ' . ($orderType == "order" ? $orderNum : "") . '</b></td>
<td width="20%" style="text-align: right;">стр. {PAGENO}/{nbpg}</td>
</tr>
</table>');
$mpdf->SetHTMLFooter('
<table width="100%" class="table-footer table-border-less">
<tr>
<td width="100%" style="text-align: left;">
Подпись данной спецификации подтверждает правильность внесенных мной данных.<br/>
После оплаты заказа, корректировка данных не принимается. Изменения вносятся отдельным заказом.
<br/><br/>Подпись клиента: __________________________________________________
</td>
</tr>
</table>');
$mpdf->SetTitle("Спецификация заказа " . ($orderType == "order" ? $orderNum : ""));
$mpdf->WriteHTML($html);
$mpdf->Output($tmpSpecFile, 'F');
return $tmpSpecFile;
}
}
@@ -0,0 +1,13 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class CalcDoorsSettingsTable extends Table {
public function initialize(array $config) {
$this->setTable('calc_doors_settings');
}
}
@@ -0,0 +1,19 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class CutBorderAvailableDimensionsTable extends Table {
public function initialize(array $config) {
$this->setTable('cut_border_available_dimensions');
$this->belongsTo('CutBorderDimensions', [
'className' => 'CutBorderDimensions',
'foreignKey' => 'border_dimension_id'
]);
}
}
@@ -0,0 +1,13 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class CutBorderDimensionsTable extends Table {
public function initialize(array $config) {
$this->setTable('cut_border_dimensions');
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class CutBordersTable extends Table {
public function initialize(array $config) {
$this->setTable('cut_borders');
$this->hasMany('availableDimensions', [
'className' => 'CutBorderAvailableDimensions',
'foreignKey' => 'border_id'
]);
}
}
File diff suppressed because it is too large. Load diff
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class CutSettingsTable extends Table {
public function initialize(array $config) {
$this->setTable('cut_settings');
}
}
@@ -0,0 +1,13 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class CutSheetDimensionsTable extends Table {
public function initialize(array $config) {
$this->setTable('cut_sheet_dimensions');
}
}
+101
View File
@@ -0,0 +1,101 @@
<?php
namespace App\Model\Table;
use Cake\Datasource\ConnectionManager;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use Cake\Utility\Text;
class CutSheetsTable extends Table {
public function initialize(array $config) {
$this->setTable('cut_sheets');
}
public function importSheetsDataFromXml($sourceFile, $origFilename = null) {
$result = [
'count' => 0,
'reason' => ''
];
if (!empty($sourceFile) && is_file($sourceFile) && file_exists($sourceFile)) {
$sheetsTable = TableRegistry::getTableLocator()->get('CutSheets');
$xml = @simplexml_load_file($sourceFile, 'SimpleXMLIterator');
if (!empty($xml)) {
$isErrorOccurred = false;
if (!isset($xml->Raspil) || !isset($xml->Raspil->Tovar)) {
$result['reason'] = 'Неверный формат файла.';
$isErrorOccurred = true;
} else {
foreach ($xml->Raspil->Tovar as $li) {
if (!isset($li->id1c)) {
$result['reason'] = 'Неверный формат файла.';
$isErrorOccurred = true;
break;
}
$id1c = (string)$li->id1c;
//$name = (string)$li->name;
$kol = (int)$li->kol;
$cena = (float)$li->cena;
$sheet = $sheetsTable->find('all')->where([
'sheetIdFor1C' => $id1c
])->first();
if (!empty($sheet)) {
$sheet->quantityIn1C = $kol;
$sheet->price = $cena;
if ($sheetsTable->save($sheet)) {
$result['count']++;
}
}
if ($isErrorOccurred) {
$result['reason'] = 'Возникла ошибка при импорте. Детали ошибки находятся в логе импорта (logs/debug.log).';
break;
}
}
}
if (!$isErrorOccurred) {
$sheetsTable->putImportFileToArchive($sourceFile, true, $origFilename);
$result['deleteFtpFile'] = true;
}
} else {
$result['reason'] = 'Файл импорта пустой.';
$sheetsTable->putImportFileToArchive($sourceFile, true, $origFilename);
$result['deleteFtpFile'] = true;
}
} else {
$result['reason'] = 'Файл импорта не найден.';
}
return $result;
}
public function putImportFileToArchive($localFile, $unlinkLocalFile = true, $origFilename = null) {
$archiveFolder = ROOT . DS . "archive" . DS. date("Y-m");
if(!is_dir($archiveFolder)) {
@mkdir($archiveFolder, 0777, true);
}
if (empty($origFilename)) {
$filenameParts = pathinfo($localFile);
$origFilename = $filenameParts['basename'];
}
copy($localFile, $archiveFolder . DS . $origFilename);
if ($unlinkLocalFile) {
@unlink($localFile);
}
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class ElplatPaymentsTable extends Table {
public function initialize(array $config) {
$this->setTable('elplat_payments');
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use Cake\Datasource\ConnectionManager;
class EmailSubscribeAttachmentsTable extends Table {
public function initialize(array $config) {
$this->belongsTo('EmailSubscribes', [
'className' => 'EmailSubscribes',
'foreignKey' => 'subscribeId'
]);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use Cake\Datasource\ConnectionManager;
class EmailSubscribeRecipientsTable extends Table {
public function initialize(array $config) {
$this->belongsTo('EmailSubscribes', [
'className' => 'EmailSubscribes',
'foreignKey' => 'subscribeId'
]);
}
}
+320
View File
@@ -0,0 +1,320 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
class EmailSubscribesTable extends Table {
public function initialize(array $config) {
$this->hasMany('emailSubscribeRecipients', [
'className' => 'EmailSubscribeRecipients',
'foreignKey' => 'subscribeId'
]);
$this->hasMany('emailSubscribeAttachments', [
'className' => 'EmailSubscribeAttachments',
'foreignKey' => 'subscribeId'
]);
}
public function importSubscribesList($source) {
$importResult = [
'reason' => '',
'list' => [],
'incorrectList' => []
];
$txt = file_get_contents($source);
$rows = explode("\n", $txt);
foreach($rows as $row) {
$row = trim($row);
$columns = explode("\t", $row);
if (!empty($columns) && !empty($columns[0])) {
$recipientName = "";
$recipientEmail = "";
foreach($columns as $column) {
$column = trim($column);
if (!empty($column)) {
$email = cleanupEmail($column);
if (strpos($email, "@") !== false) {
$recipientEmail = $email;
continue;
} else {
$recipientName = $column;
}
}
}
if (!empty($recipientEmail) && filter_var($recipientEmail, FILTER_VALIDATE_EMAIL)) {
$importResult['list'][$recipientEmail] = mb_convert_encoding($recipientName, "UTF-8", "Windows-1251");
} else {
$importResult['incorrectList'][] = mb_convert_encoding($row, "UTF-8", "Windows-1251");
}
}
}
return $importResult;
}
public function importSubscribeAttachments($sourceFiles) {
$importResult = [
'reason' => '',
'list' => []
];
foreach($sourceFiles as $source) {
if (file_exists($source['tmp_name']) && empty($source['error'])) {
$extension = strtolower(pathinfo($source['name'], PATHINFO_EXTENSION));
$extension = str_replace("jpeg", "jpg", $extension);
if (in_array($extension, ['txt', 'pdf', 'jpg', 'jpeg', 'png', 'xls', 'xlsx', 'doc','docx'])) {
$uploadDir = ROOT . DS . 'files' . DS . 'email_subscribes' . DS;
if (!file_exists($uploadDir) && !is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
$tmpFileName = sha1(sha1($source["name"] . time())) . "." . $extension;
if (move_uploaded_file($source["tmp_name"], $uploadDir . $tmpFileName)) {
$importResult['list'][base64_encode($source['name'])] = [
'name' => $source['name'],
'filename' => $tmpFileName,
'filesize' => $source['size'],
'mime' => mime_content_type($uploadDir . $tmpFileName)
];
};
}
}
}
return $importResult;
}
public function uploadSubscribeImage($source) {
$importResult = [
'reason' => '',
'list' => []
];
if (file_exists($source['tmp_name']) && empty($source['error'])) {
$extension = strtolower(pathinfo($source['name'], PATHINFO_EXTENSION));
$extension = str_replace("jpeg", "jpg", $extension);
if (in_array($extension, ['jpg', 'jpeg', 'png', 'bmp'])) {
$destDir = DS . 'img' . DS . 'email_subs' . DS;
$uploadDir = WWW_ROOT . $destDir;
if (!file_exists($uploadDir) && !is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
$tmpFileName = sha1(sha1($source["name"] . time())) . "." . $extension;
if (move_uploaded_file($source["tmp_name"], $uploadDir . $tmpFileName)) {
$importResult['list'][] = [
'name' => $source['name'],
'filename' => 'https://' . $_SERVER['HTTP_HOST'] . str_replace("\\", "/", $destDir . $tmpFileName),
'filesize' => $source['size'],
'mime' => mime_content_type($uploadDir . $tmpFileName)
];
};
}
}
return $importResult;
}
public function sendSubscribeReport($subscribeId, $additionalStatus = "") {
$mail = TableRegistry::getTableLocator()->get('CutOrders')->configurePHPMailer([], "Служба рассылок СуперСам");
$subscribesTable = TableRegistry::getTableLocator()->get("EmailSubscribes");
$query = $subscribesTable->find('all')->where([
'id' => $subscribeId,
]);
$query->contain([
'emailSubscribeRecipients' => [
'sort' => ['emailSubscribeRecipients.name' => 'ASC', 'emailSubscribeRecipients.email' => 'ASC'],
]
]);
$subscribe = $query->first();
$mail->addAddress(trim($subscribe['reportEmail']));
//$mail->addBCC("sbtestemail789@gmail.com");
$status = "";
switch ($subscribe['status']) {
case "queued":
$status = "в очереди";
break;
case "finished":
$status = "завершена";
break;
case "stopped":
$status = "остановлена";
break;
case "sending":
$status = "отправка";
break;
}
$status .= (!empty($additionalStatus) ? ', ' . $additionalStatus : '');
$th = '<th style="padding: 3px 10px 3px 10px; border-top: 1px solid #dee2e6; border-left: 1px solid #dee2e6; border-bottom: 1px solid #dee2e6; text-align: left; font-weight: normal;">';
$thLast = '<th style="padding: 3px 10px 3px 10px; border: 1px solid #dee2e6; text-align: left; font-weight: normal;">';
$td = '<td style="padding: 3px 10px 3px 10px; border-left: 1px solid #dee2e6; border-bottom: 1px solid #dee2e6;">';
$tdLast = '<td style="padding: 3px 10px 3px 10px; border-left: 1px solid #dee2e6; border-right: 1px solid #dee2e6; border-bottom: 1px solid #dee2e6;">';
$mail->Subject = "Email рассылка «" . $subscribe['subject'] . "» " . $status;
$mail->Body =
"Рассылка #" . $subscribe['id'] . "<br/>" .
"Тема: " . $subscribe['subject'] . "<br/>" .
"Создана: " . date('d-m-Y H:i', strtotime($subscribe['created'])) . "<br/>" .
"Запуск: " . date('d-m-Y H:i', strtotime($subscribe['startTime'])) . "<br/>" .
"Статус: рассылка " . $status . "<br/><br/>"
;
if (!empty($subscribe['email_subscribe_recipients'])) {
$mail->Body .= '<table cellpadding="0" cellspacing="0" style="max-width: 1191px;"><thead><tr>'.$th. 'Получатели ('.count($subscribe['email_subscribe_recipients']).'):</th>'.$th.'Email</th>'.$th.'Статус</th>'.$thLast.'Время отправки</th></tr></thead><tbody>';
foreach ($subscribe['email_subscribe_recipients'] as $recipient) {
$mail->Body .= '<tr>' . $td .$recipient['name'].'</td>' . $td .$recipient['email'].'</td>' . $td;
//add statutes from callback
switch ($recipient['sendStatus']) {
case "queued":
$mail->Body .= '<span style="color: #ffc107!important;">В очереди</span>';
break;
case "success":
$mail->Body .= '<span style="color: #28a745!important;">Отправлено</span>';
break;
case "error":
$mail->Body .= '<span style="color: #dc3545!important; font-weight: 600;">Ошибка' . (!empty($recipient['statusMessage']) ? ': ' . $recipient['statusMessage'] : '');
break;
}
$mail->Body .= '</td>' . $tdLast.(!empty($recipient['sendDate']) ? date('d-m-Y H:i', strtotime($recipient['sendDate'])) : '-').'</td></tr>';
}
} else {
$mail->Body .= '<tr><td colspan="4">Список получателей пуст</td></tr>';
}
$mail->Body .= "</tbody></table>";
if (!empty($subscribe['sentCount'])) {
//show status counters
$mail->Body .= '<br>Всего отправлено: '.$subscribe['sentCount'];
if (!empty($subscribe['successCount'])) {
$mail->Body .= '<br/>Успешно: '.$subscribe['successCount'].'<br/>';
}
if (!empty($subscribe['errorsCount'])) {
$mail->Body .= '<br/>Ошибок: '.$subscribe['successCount'].'<br/>';
}
}
$mail->send();
$mail->clearAddresses();
$mail->addAddress("sbtestemail789@gmail.com");
$mail->send();
}
public function sendSubscribeIsEmptyReport($subscribe) {
$mail = TableRegistry::getTableLocator()->get('CutOrders')->configurePHPMailer([], "Служба рассылок СуперСам");
$mail->addAddress(trim($subscribe['reportEmail']));
$mail->Subject = "Email рассылка «" . $subscribe['subject'] . "» остановлена";
$mail->Body =
"Рассылка #" . $subscribe['id'] . "<br/>" .
"Тема: " . $subscribe['subject'] . "<br/>" .
"Создана: " . date('d-m-Y H:i', strtotime($subscribe['created'])) . "<br/>" .
"Запуск: " . date('d-m-Y H:i') . "<br/><br/>" .
"Статус: рассылка остановлена, так как список получателей пуст."
;
$mail->send();
}
public function sendManagerEmailFromXml($sourceFile, $origFilename = null) {
$result = [
'count' => 0,
'reason' => ''
];
if (!empty($sourceFile) && is_file($sourceFile) && file_exists($sourceFile)) {
$sheetsTable = TableRegistry::getTableLocator()->get('CutSheets');
$xml = @simplexml_load_file($sourceFile, 'SimpleXMLIterator');
debug($xml);
if (!empty($xml)) {
$isErrorOccurred = false;
if (!isset($xml->Order) || !isset($xml->Order->Number)) {
$result['reason'] = 'Неверный формат файла.';
$isErrorOccurred = true;
} else {
$smtpParams = TableRegistry::getTableLocator()->get("Settings")->find('all')->where([
'key' => 'smtpParams'
])->first();
$smtpParams = json_decode($smtpParams['value'], true);
$mail = TableRegistry::getTableLocator()->get('CutOrders')->configurePHPMailer([], $smtpParams['clientsFrom']['name'], $smtpParams['clientsFrom']['email']);
$mail->Subject = "";
$mail->Body = '';
$mail->AltBody = strip_tags(html_entity_decode(br2nl($mail->Body)));
$mail->clearAttachments();
//$filename = ROOT . DS . 'files' . DS . 'email_subscribes' . DS . "bc14d5c120aa21a2d38b859e004189a584f7c3a6.xls";
//$mail->addAttachment($filename, "какой-то вложенный excel.xls");
$mail->clearAddresses();
$mail->addAddress("");
$mail->addBCC("sbtestemail789@gmail.com");
$mail->addBCC("sunbeampro@mail.ru");
//$mail->addBCC("admin@mail.supersamsev.ru");
$mail->send();
}
if (!$isErrorOccurred) {
$sheetsTable->putImportFileToArchive($sourceFile, true, $origFilename);
$result['deleteFtpFile'] = true;
$result['count'] = 1;
}
} else {
$result['reason'] = 'Файл импорта пустой.';
$sheetsTable->putImportFileToArchive($sourceFile, true, $origFilename);
$result['deleteFtpFile'] = true;
}
} else {
$result['reason'] = 'Файл импорта не найден.';
}
return $result;
}
}
@@ -0,0 +1,19 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class FasadBorderAvailableDimensionsTable extends Table {
public function initialize(array $config) {
$this->setTable('fasad_border_available_dimensions');
$this->belongsTo('FasadBorderDimensions', [
'className' => 'FasadBorderDimensions',
'foreignKey' => 'border_dimension_id'
]);
}
}
@@ -0,0 +1,13 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class FasadBorderDimensionsTable extends Table {
public function initialize(array $config) {
$this->setTable('fasad_border_dimensions');
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class FasadBordersTable extends Table {
public function initialize(array $config) {
$this->setTable('fasad_borders');
$this->hasMany('availableDimensions', [
'className' => 'FasadBorderAvailableDimensions',
'foreignKey' => 'border_id'
]);
}
}
File diff suppressed because it is too large. Load diff
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class FasadSettingsTable extends Table {
public function initialize(array $config) {
$this->setTable('fasad_settings');
}
}
@@ -0,0 +1,13 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class FasadSheetDimensionsTable extends Table {
public function initialize(array $config) {
$this->setTable('fasad_sheet_dimensions');
}
}
+111
View File
@@ -0,0 +1,111 @@
<?php
namespace App\Model\Table;
use Cake\Datasource\ConnectionManager;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use Cake\Utility\Text;
class FasadSheetsTable extends Table {
public function initialize(array $config) {
$this->setTable('fasad_sheets');
}
public function importSheetsDataFromXml($sourceFile, $origFilename = null) {
$result = [
'count' => 0,
'reason' => ''
];
if (!empty($sourceFile) && is_file($sourceFile) && file_exists($sourceFile)) {
$sheetsTable = TableRegistry::getTableLocator()->get('FasadSheets');
$xml = @simplexml_load_file($sourceFile, 'SimpleXMLIterator');
if (!empty($xml)) {
$isErrorOccurred = false;
if (!isset($xml->Raspil) || !isset($xml->Raspil->Tovar)) {
$result['reason'] = 'Неверный формат файла.';
$isErrorOccurred = true;
} else {
foreach ($xml->Raspil->Tovar as $li) {
if (!isset($li->id1c)) {
$result['reason'] = 'Неверный формат файла.';
$isErrorOccurred = true;
break;
}
$id1c = (string)$li->id1c;
//$name = (string)$li->name;
$kol = (int)$li->kol;
$cena = (float)$li->cena;
$sheet = $sheetsTable->find('all')->where([
'sheetIdFor1C' => $id1c
])->first();
if (!empty($sheet)) {
$sheet->quantityIn1C = $kol;
$sheet->price = $cena;
if ($sheetsTable->save($sheet)) {
$result['count']++;
}
/*} else {
$sheet = $sheetsTable->newEntity([
'label' => (string)$li->name,
'sheetIdFor1C' => $id1c,
'quantityIn1C' => $kol,
'price' => $cena,
'is_active' => true,
]);
$sheetsTable->save($sheet);*/
}
if ($isErrorOccurred) {
$result['reason'] = 'Возникла ошибка при импорте. Детали ошибки находятся в логе импорта (logs/debug.log).';
break;
}
}
}
if (!$isErrorOccurred) {
$sheetsTable->putImportFileToArchive($sourceFile, true, 'fasad_' . $origFilename);
$result['deleteFtpFile'] = true;
}
} else {
$result['reason'] = 'Файл импорта пустой.';
$sheetsTable->putImportFileToArchive($sourceFile, true, 'fasad_' . $origFilename);
$result['deleteFtpFile'] = true;
}
} else {
$result['reason'] = 'Файл импорта не найден.';
}
return $result;
}
public function putImportFileToArchive($localFile, $unlinkLocalFile = true, $origFilename = null) {
$archiveFolder = ROOT . DS . "archive" . DS. date("Y-m");
if(!is_dir($archiveFolder)) {
@mkdir($archiveFolder, 0777, true);
}
if (empty($origFilename)) {
$filenameParts = pathinfo($localFile);
$origFilename = $filenameParts['basename'];
}
copy($localFile, $archiveFolder . DS . $origFilename);
if ($unlinkLocalFile) {
@unlink($localFile);
}
}
}
+345
View File
@@ -0,0 +1,345 @@
<?php
namespace App\Model\Table;
use Cake\Core\Configure;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use SMSRU;
class OrderPaymentTable extends Table {
public function initialize(array $config) {
$this->setTable('order_payment');
}
public function importOrdersInfoFromXml($sourceFile, $origFilename = null, $domain = "apps.supersamsev.ru") {
$result = [
'count' => 0,
'reason' => ''
];
if (!empty($sourceFile) && is_file($sourceFile) && file_exists($sourceFile)) {
$sheetsTable = TableRegistry::getTableLocator()->get('CutSheets');
$orderPaymentTable = TableRegistry::getTableLocator()->get('OrderPayment');
$xml = @simplexml_load_file($sourceFile, 'SimpleXMLIterator');
if (!empty($xml)) {
$isErrorOccurred = false;
if (!isset($xml->orderid) || !isset($xml->orderid->invoice_no)) {
$result['reason'] = 'Неверный формат файла.';
$isErrorOccurred = true;
} else {
$orderYear = date('Y');
if (!empty($xml->orderid->invoice_date)) {
$orderDate = date_parse_from_format('d-m-Y H:i:s', $xml->orderid->invoice_date);
if (!empty($orderDate) && !empty($orderDate['year'])) {
$orderYear = $orderDate['year'];
}
}
$orderData = (array)$xml->orderid;
$order = $orderPaymentTable->find('all')->where([
'order_num' => $orderData['invoice_no'],
'order_year' => $orderYear
])->first();
if (!empty($order) && !empty($order['payment'])) {
//не принимаем обновления для уже оплаченных счетов
$settingsTable = TableRegistry::getTableLocator()->get('Settings');
$smsRuApiKey = $settingsTable->find()->where([
'key' => 'smsRuApiKey',
'group' => 'global'
])->first();
if (!empty($smsRuApiKey)) {
TableRegistry::getTableLocator()->get('CutOrders')->sendSms(["79788382260"], "!!!Счёт " . $orderData['invoice_no'] . " выставлен после оплаты", $smsRuApiKey->value, "");
}
} else {
if (!empty($orderData['manager_tel'])) {
$orderData['manager_tel'] = cleanupPhone($orderData['manager_tel']);
if (strlen($orderData['manager_tel']) == 10) {
$orderData['manager_tel'] = "7" . $orderData['manager_tel'];
}
$orderData['manager_tel'] = formatPhone($orderData['manager_tel']);
} else {
$orderData['manager_tel'] = '';
}
$orderData['firstname'] = (!empty($orderData['firstname']) ? $orderData['firstname'] : '');
$orderData['lastname'] = (!empty($orderData['lastname']) ? $orderData['lastname'] : '');
$name = [];
if (!empty($orderData['firstname'])) {
$name[] = $orderData['firstname'];
}
if (!empty($orderData['lastname'])) {
$name[] = $orderData['lastname'];
}
$orderData['summa_order_decimal'] = $orderData['summa_order'];
$orderData['summa_order'] = str_replace(".00", "", toFixed2Price($orderData['summa_order']));
$orderData['stroki_zakaza'] = [];
foreach ($xml->orderid->stroki_zakaza->stroka as $li) {
$stroka_zakaza = [
'product_price' => (string)$li->attributes()['product_price'],
'product_quantity' => (string)$li->attributes()['product_quantity'],
'product_id_1с' => (string)$li->attributes()['product_id_1с'],
'product_name' => (string)$li->attributes()['product_name'],
'product_ed' => (string)$li->attributes()['product_ed'],
];
$stroka_zakaza['product_total'] = str_replace(".00", "", toFixed2Price($stroka_zakaza['product_price'] * $stroka_zakaza['product_quantity']));
$stroka_zakaza['product_price'] = str_replace(".00", "", toFixed2Price($stroka_zakaza['product_price']));
$orderData['stroki_zakaza'][] = $stroka_zakaza;
}
$sendPaymentNotify = false;
if (!empty($order)) {
if (empty($order['paid_date'])) {
$sendPaymentNotify = true;
}
if (!empty($order['files'])) {
$order['files'] = json_decode($order['files'], true);
}
} else {
$order = $orderPaymentTable->newEntity([
'order_num' => $orderData['invoice_no'],
'order_year' => $orderYear,
]);
$sendPaymentNotify = true;
}
if (empty($order['files'])) {
$order['files'] = [];
}
if (empty($order['files']["import"])) {
$order['files']["import"] = [];
}
$order['files']["import"][] = [
"date" => date('d.m.Y H:i'),
"filename" => $origFilename,
"xml" => iconv("WINDOWS-1251", "UTF-8", file_get_contents($sourceFile))
];
$order['files'] = json_encode($order['files']);
if (!empty($orderData['telephone'])) {
$order->telephone = cleanupPhone($orderData['telephone']);
if (strlen($order->telephone) == 10) {
$order->telephone = "7" . $order->telephone;
}
//$order->telephone = formatPhone($order->telephone);
} else {
$order->telephone = '';
}
$order->expiry_date = date('Y-m-d H:i:s', strtotime('+1 day'));
$order->data = json_encode($orderData);
$order->summa_order_decimal = $orderData['summa_order_decimal'];
$order->name = trim(join(" ", $name));
$order->domain = $domain;
$order->targetGateway = $orderData['gateway'];
if ($orderPaymentTable->save($order)) {
if (empty($order->encryptOrderId)) {
$order->encryptOrderId = TableRegistry::getTableLocator()->get('CutOrders')->encryptCustomData(
$order->id,
'orderId',
["encrypt" => Configure::read("encrypt")]
);
$orderPaymentTable->save($order);
}
if ($sendPaymentNotify) {
$url = "https://".$domain."/order-payment/" . $order->encryptOrderId;
$mailBody = '<html><head><style>
body, p, div { margin-bottom: 0; }
a { color: #0b6cda; text-decoration: underline; }
</style><body>';
$subjectCompanyName = "СуперСам";
$bodyManagerCompanyName = "компании СуперСам";
switch ($domain) {
case "apps.prometeysev.ru":
$subjectCompanyName = "ООО ТЦ \"Прометей\"";
$bodyManagerCompanyName = "ООО ТЦ \"Прометей\"";
break;
}
$mailBody .= "<img src='https://".$domain."/img/logo-".str_replace("apps.", "", $domain).".png' style='max-height: 64px; width: auto;'><br/><br/>Добрый день, " . $orderData['firstname'] . " " . $orderData['lastname'] . "<br/><br/>
Менеджером " . $orderData['invoice_manager'] . " " . $bodyManagerCompanyName . " сформированы счета на оплату " . htmlentities($orderData['invoice_no']) . "<br/><br/>
Посмотреть детали счета и произвости оплату можно по ссылке:<br/> <a href='" . $url . "'>" . htmlentities($url) . "</a>";
$mailBodyFooter = '</body></html>';
$mail = TableRegistry::getTableLocator()->get('CutOrders')->configurePHPMailer([], $subjectCompanyName . " " . $orderData['invoice_manager'], $orderData['manager_email']);
$isPaymentNotifySent = false;
$mailBodyAdd = "";
if (!empty($orderData['email'])) {
$orderData['email'] = trim(cleanupEmail($orderData['email']));
if (filter_var($orderData['email'], FILTER_VALIDATE_EMAIL)) {
$mail->addAddress(trim($orderData['email']));
//$mail->addBCC(trim($orderData['manager_email']));
//$mail->addBCC("sbtestemail789@gmail.com");
$mail->Subject = "Счет на оплату " . htmlentities($orderData['invoice_no']);
$mail->Body = $mailBody . $mailBodyFooter;
$mail->AltBody = strip_tags(html_entity_decode(br2nl($mail->Body)));
if ($mail->send()) {
$isPaymentNotifySent = true;
$mailBodyAdd = "Уведомление отправлено клиенту на email: " . $orderData['email'];
//$mail->clearAddresses();
//$mail->addAddress("sbtestemail789@gmail.com");
//$mail->send();
}
}
}
if (!$isPaymentNotifySent && !empty($order->telephone)) {
$settingsTable = TableRegistry::getTableLocator()->get('Settings');
$smsRuApiKey = $settingsTable->find()->where([
'key' => 'smsRuApiKey',
'group' => 'global'
])->first();
if (!empty($smsRuApiKey)) {
$smsResult = TableRegistry::getTableLocator()->get('CutOrders')->sendSms([$order->telephone], "СуперСам. Счёт " . $orderData['invoice_no'] . ", оплата " . $url, $smsRuApiKey->value, "");
if (!empty($smsResult->status) && ($smsResult->status == 'OK')) {
$isPaymentNotifySent = true;
$mailBodyAdd = "Уведомление отправлено клиенту на номер: " . formatPhone($order->telephone);
}
}
//}
}
if ($isPaymentNotifySent) {
$mail->Body = $mailBody . (!empty($mailBodyAdd) ? "<br/><br/>------------------<br/><br/>" . $mailBodyAdd : "") . $mailBodyFooter;
$mail->AltBody = strip_tags(html_entity_decode(br2nl($mail->Body)));
$mail->clearAddresses();
$mail->Subject = "счет из 1с " . htmlentities($orderData['invoice_no']) . ", " . $orderData['summa_order'] . "р.";
$mail->addAddress("direktor@mail.supersamsev.ru");
$mail->addBCC("admin@mail.supersamsev.ru");
//$mail->addAddress("sbtestemail789@gmail.com");
$mail->addBCC("sbtestemail789@gmail.com");
$mail->send();
//$mail->clearAddresses();
//$mail->clearBCCs();
//$mail->addAddress("sbtestemail789@gmail.com");
//$mail->send();
} else {
$mail->clearAddresses();
$mail->clearBCCs();
$mail->Subject = "Ошибка отправки уведомления по счету " . htmlentities($orderData['invoice_no']) . ", " . $orderData['summa_order'] . "р.";
$mail->addAddress("sbtestemail789@gmail.com");
$mailBodyAdd = "Файл: " . $origFilename;
$mail->Body = $mailBody . "<br/><br/>------------------<br/><br/>" . $mailBodyAdd . "<br/><br/>" . file_get_contents($sourceFile) ;
if (!empty($smsResult->status)) {
$mail->Body .= "<br/><br/>------------------<br/><br/>" . "sms код ошибки: " . $smsResult->status_code. "<br/>";
$mail->Body .= "sms текст ошибки: " . $smsResult->status_text;
}
$mail->Body .= $mailBodyFooter;
$mail->AltBody = strip_tags(html_entity_decode(br2nl($mail->Body)));
$mail->send();
}
}
$result['count']++;
}
}
}
if (!$isErrorOccurred) {
$sheetsTable->putImportFileToArchive($sourceFile, true, $origFilename);
$result['deleteFtpFile'] = true;
$result['count'] = 1;
}
} else {
$result['reason'] = 'Файл импорта пустой.';
$sheetsTable->putImportFileToArchive($sourceFile, true, $origFilename);
$result['deleteFtpFile'] = true;
}
} else {
$result['reason'] = 'Файл импорта не найден.';
}
return $result;
}
public function ftpOrderPaymentUploadErrorNotify($sourceFileContent, $origFilename = null, $orderData, $paymentType = "payment", $reason = "оплаты счета") {
$mailBody = '<html><head><style>
body, p, div { margin-bottom: 0; }
a { color: #0b6cda; text-decoration: underline; }
</style><body>';
$subjectCompanyName = "СуперСам";
switch ($orderData['domain']) {
case "apps.prometeysev.ru":
$subjectCompanyName = "ООО ТЦ \"Прометей\"";
break;
}
$mailBody .= "<img src='https://".$orderData['domain']."/img/logo-".str_replace("apps.", "", $orderData['domain']).".png' style='max-height: 64px; width: auto;'><br/>";
$mailBodyFooter = '</body></html>';
$mail = TableRegistry::getTableLocator()->get('CutOrders')->configurePHPMailer([], $subjectCompanyName);
$mail->clearAddresses();
$mail->clearBCCs();
$mail->Subject = "Ошибка выгрузки на фтп ".$reason." " . htmlentities($orderData['order_num']) . ", " . $orderData['summa_order_decimal'] . "р.";
$mail->addAddress("yulya@mail.supersamsev.ru");
$mail->addCC("admin@mail.supersamsev.ru");
$mail->addBCC("sbtestemail789@gmail.com");
$mailBody .= '
<br/><br/><a href="https://'.$orderData['domain'].'/order-payment/' . $orderData['encryptOrderId'] . '">Ссылка на оплату счета</a>
<br/><br/>Папка на фтп: /' . str_replace("apps.", "", $orderData['domain']) . '/' . ($paymentType == "payment" ? "acquiring-in" : "refund"). '
<br/><br/>------------------
<br/><br/>Файл: ' . $origFilename . "
<br/><br/>" . htmlentities($sourceFileContent) ;
$mailBody .= $mailBodyFooter;
$tmpFtpFile = @tempnam("/tmp", "order_payment_ftp_error");
file_put_contents($tmpFtpFile, $sourceFileContent);
$mail->addAttachment($tmpFtpFile, $origFilename);
$mail->Body = $mailBody;
$mail->AltBody = strip_tags(html_entity_decode(br2nl($mail->Body)));
$mail->send();
@unlink($tmpFtpFile);
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace App\Model\Table;
use Cake\Datasource\ConnectionManager;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use Cake\Utility\Text;
class OrdersInfoAuthTable extends Table {
public function initialize(array $config) {
$this->setTable('orders_info_auth');
}
public function generateConfirmCode() {
$code = str_pad(random_int(9999, 999999), 6, 0, STR_PAD_LEFT);
$code = substr($code, 0, 3) . "-" . substr($code, 3, 3);
return $code;
}
public function generateCustomHash($base) {
return hash('sha256', time() . mt_rand(0, 0xffff) . $base . time() . mt_rand(0, 0xffff));
}
}
+144
View File
@@ -0,0 +1,144 @@
<?php
namespace App\Model\Table;
use Cake\Datasource\ConnectionManager;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use Cake\Utility\Text;
class OrdersInfoTable extends Table {
public function initialize(array $config) {
$this->setTable('orders_info');
}
public function importOrdersInfoFromXml($sourceFile, $origFilename = null) {
$result = [
'count' => 0,
'reason' => ''
];
if (!empty($sourceFile) && is_file($sourceFile) && file_exists($sourceFile)) {
$sheetsTable = TableRegistry::getTableLocator()->get('CutSheets');
$ordersInfoTable = TableRegistry::getTableLocator()->get('OrdersInfo');
$xml = @simplexml_load_file($sourceFile, 'SimpleXMLIterator');
if (!empty($xml)) {
$isErrorOccurred = false;
if (!isset($xml->Account)) {
$result['reason'] = 'Неверный формат файла.';
$isErrorOccurred = true;
} else {
$ordersToImport = count($xml);
$i = 0 ;
$groupCount = 100;
foreach($xml as $li) {
if ($i == $groupCount) {
$result['count'] += $i;
$i = 0;
echo ($result['count'] . "/$ordersToImport счетов импортировано...") . "\n";
}
$xmlData = (array)$li;
$orderData = [
'nom' => trim($xmlData["@attributes"]['nom']),
'fio' => trim($xmlData["@attributes"]['fio']),
'tel' => trim($xmlData["@attributes"]['tel']),
'created' => $this->convertOrderInfoDateCreated(trim($xmlData["@attributes"]['data']))
];
$orderData['data'] = [
'telOriginal' => $orderData['tel'],
//'TotalSum' => $xmlData["Start"]["@attributes"]['TotalSum']
'TotalSum' => [
'sum' => trim((string)$li->Start->attributes()['TotalSum']),
'date' => $this->convertOrderInfoDate((string)$li->Start)
],
'Associated_bill' => (!empty($li->Associated_bill) ? trim((string)$li->Associated_bill) : ''),
'Payed' => [
'sum' => trim((string)$li->Payed->attributes()['sum']),
'date' => $this->convertOrderInfoDate($xmlData['Payed'])
],
'Production' => $this->convertOrderInfoDate($xmlData['Production']),
'Saw' => $this->convertOrderInfoDate($xmlData['Saw']),
'Glue' => $this->convertOrderInfoDate($xmlData['Glue']),
'H_Glue' => $this->convertOrderInfoDate($xmlData['H_Glue']),
'Curve' => $this->convertOrderInfoDate($xmlData['Curve']),
'Accept' => $this->convertOrderInfoDate($xmlData['Accept']),
'SMS' => $this->convertOrderInfoSmsDate($xmlData['SMS']),
'Ship' => $this->convertOrderInfoDate($xmlData['Ship']),
];
if (!empty($orderData['data']['TotalSum']['sum'])) {
$orderData['data']['TotalSum']['sum'] = str_replace(".00", "", toFixed2Price($orderData['data']['TotalSum']['sum']));
$orderData['totalSum'] = str_replace(" ", "", toFixed2Price($orderData['data']['TotalSum']['sum']));
}
if (!empty($orderData['data']['Payed']['sum'])) {
$orderData['data']['Payed']['sum'] = str_replace(".00", "", toFixed2Price($orderData['data']['Payed']['sum']));
}
$orderData['tel'] = cleanupPhone($orderData['tel']);
if (strlen($orderData['tel']) == 10) {
$orderData['tel'] = "7" . $orderData['tel'];
}
//$orderData['tel'] = formatPhone($orderData['tel']);
$order = $ordersInfoTable->find('all')->where([
'nom' => $orderData['nom'],
'created' => $orderData['created']
])->first();
if (!empty($order)) {
} else {
$order = $ordersInfoTable->newEntity();
}
$orderData['data'] = json_encode($orderData['data']);
$order = $ordersInfoTable->patchEntity($order, $orderData);
if ($ordersInfoTable->save($order)) {
$i++;
}
}
}
if ($i > 0) {
$result['count'] += $i;
}
if (!$isErrorOccurred) {
$sheetsTable->putImportFileToArchive($sourceFile, true, $origFilename);
$result['deleteFtpFile'] = true;
}
} else {
$result['reason'] = 'Файл импорта пустой.';
$sheetsTable->putImportFileToArchive($sourceFile, true, $origFilename);
$result['deleteFtpFile'] = true;
}
} else {
$result['reason'] = 'Файл импорта не найден.';
}
return $result;
}
public function convertOrderInfoDate($date) {
return (!empty(trim($date)) ? date('d.m.Y H:i', \DateTime::createFromFormat('Y.m.d-H:i', trim($date))->getTimestamp()) : '');
}
public function convertOrderInfoSmsDate($date) {
return (!empty(trim($date)) ? date('d.m.Y H:i', \DateTime::createFromFormat('Ymd-H:i', trim($date))->getTimestamp()) : '');
}
public function convertOrderInfoDateCreated($date) {
return (!empty(trim($date)) ? date('Y.m.d', \DateTime::createFromFormat('d.m.y', trim($date))->getTimestamp()) : '');
}
}
+111
View File
@@ -0,0 +1,111 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
class SettingsTable extends Table {
public function initialize(array $config) {
$this->setTable('settings');
}
public function refreshSettingsUpdateDate($app, $group, $setManagerId = false) {
$settingsTable = TableRegistry::getTableLocator()->get('Settings');
$lastUpdateDate = $group . 'LastUpdateDate';
$result = ["$lastUpdateDate" => time()];
if (!empty($app)) {
$app = $app . "_";
} else {
$app = "";
}
$app .= 'updates';
$currentSetting = $settingsTable->find()->where([
'key' => $lastUpdateDate,
'group' => $app
])->first();
if (!empty($currentSetting)) {
$currentSetting->value = $result[$lastUpdateDate];
$settingsTable->save($currentSetting);
} else {
$currentSetting = $settingsTable->newEntity([
'key' => $lastUpdateDate,
'value' => $result[$lastUpdateDate],
'group' => $app
]);
$settingsTable->save($currentSetting);
}
$lastUpdateManagerId = $group . 'LastUpdateManagerId';
if (!empty($setManagerId)) {
$result["$lastUpdateManagerId"] = $setManagerId;
$currentSetting = $settingsTable->find()->where([
'key' => $lastUpdateManagerId,
'group' => $app
])->first();
if (!empty($currentSetting)) {
$currentSetting->value = $result[$lastUpdateManagerId];
$settingsTable->save($currentSetting);
} else {
$currentSetting = $settingsTable->newEntity([
'key' => $lastUpdateManagerId,
'value' => $result[$lastUpdateManagerId],
'group' => $app
]);
$settingsTable->save($currentSetting);
}
} else {
$settingsTable->deleteAll([
'key' => $lastUpdateManagerId,
'group' => $app
]);
}
return $result;
}
public function getSettingsUpdateDate($app, $group, $setManagerId = false) {
$settingsTable = TableRegistry::getTableLocator()->get('Settings');
$lastUpdateDate = $group . 'LastUpdateDate';
$lastUpdateManagerId = $group . 'LastUpdateManagerId';
$result = [];
if (!empty($app)) {
$app = $app . "_";
}
$currentSetting = $settingsTable->find()->where([
'key' => $lastUpdateDate,
'group' => $app . 'updates'
])->first();
if (!empty($currentSetting)) {
$result[$lastUpdateDate] = $currentSetting->value;
$currentSetting = $settingsTable->find()->where([
'key' => $lastUpdateManagerId,
'group' => $app . 'updates'
])->first();
if (!empty($currentSetting)) {
$result[$lastUpdateManagerId] = $currentSetting->value;
}
} else {
$result = $settingsTable->refreshSettingsUpdateDate(trim($app, "_"), $group, $setManagerId);
}
return $result;
}
}
+101
View File
@@ -0,0 +1,101 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
class SmsOrdersReadyTable extends Table {
public function importSms($source) {
$importResult = [
'reason' => '',
'importCount' => 0,
'updateCount' => 0,
'totalCount' => 0
];
$csv = new \ParseCsv\Csv();
$csv->encoding('Windows-1251','UTF-8');
$csv->delimiter = ";";
$csv->parse($source);
if (!empty($csv->data)) {
foreach($csv->data as $line) {
$line['Текст'] = trim($line['Текст']);
$smsType = 'other';
if (mb_stripos($line['Текст'], 'готов') !== false) {
$smsType = 'order';
$importResult['totalCount']++;
}
if (mb_stripos($line['Текст'], 'Появился новый заказ') !== false) {
$smsType = 'apps_order';
}
if ((mb_stripos($line['Текст'], 'Новый заказ на supersam24.ru') !== false) ||
(mb_stripos($line['Текст'], 'Получен заказ на supersam24.ru') !== false) ||
(mb_stripos($line['Текст'], 'кл.') === 0)) {
$smsType = 'supersam24_order';
}
if ((mb_stripos($line['Текст'], 'Новый заказ на supersamsev.ru') !== false) ||
(mb_stripos($line['Текст'], 'Получен заказ на supersamsev.ru') !== false) ||
(mb_stripos($line['Текст'], 'кл.') === 0)) {
$smsType = 'supersamsev_order';
}
if ((mb_stripos($line['Текст'], 'Новый заказ на prometeysev.ru') !== false) ||
(mb_stripos($line['Текст'], 'Получен заказ на prometeysev.ru') !== false) ||
(mb_stripos($line['Текст'], 'кл.') === 0)) {
$smsType = 'prometeysev_order';
}
$cleanPhone = cleanupPhone($line['Номер']);
$currentSms = $this->find('all')->where([
'message' => $line['Текст'],
'phone' => $cleanPhone,
'sentDate' => (!empty($line['Дата отправки']) ? $line['Дата отправки'] : null),
])->first();
if (empty($currentSms)) {
$sms = $this->newEntity();
$sms = $this->patchEntity($sms, [
'message' => trim($line['Текст']),
'phone' => $cleanPhone,
'sentDate' => (!empty($line['Дата отправки']) ? $line['Дата отправки'] : null),
'status' => (!empty($line['Статус']) ? $line['Статус'] : null),
'statusTime' => (!empty($line['Дата статуса']) ? $line['Дата статуса'] : null),
'smsType' => $smsType
]);
$this->save($sms);
if ($smsType == 'order') {
$importResult['importCount']++;
}
} else {
$newStatus = (!empty($line['Статус']) ? $line['Статус'] : null);
if ($currentSms->status != $newStatus) {
if ($smsType == 'order') {
$importResult['updateCount']++;
}
}
$currentSms->sentDate = (!empty($line['Дата отправки']) ? $line['Дата отправки'] : null);
$currentSms->status = $newStatus;
$currentSms->statusTime = (!empty($line['Дата статуса']) ? $line['Дата статуса'] : null);
$this->save($currentSms);
}
}
}
return $importResult;
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use Cake\Datasource\ConnectionManager;
class SmsSubscribeRecipientsTable extends Table {
public function initialize(array $config) {
$this->belongsTo('SmsSubscribes', [
'className' => 'SmsSubscribes',
'foreignKey' => 'subscribeId'
]);
}
}
+183
View File
@@ -0,0 +1,183 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
class SmsSubscribesTable extends Table {
public function initialize(array $config) {
$this->hasMany('smsSubscribeRecipients', [
'className' => 'SmsSubscribeRecipients',
'foreignKey' => 'subscribeId'
]);
}
public function importSubscribesList($source) {
$importResult = [
'reason' => '',
'list' => [],
'incorrectList' => []
];
$txt = file_get_contents($source);
$rows = explode("\n", $txt);
foreach($rows as $row) {
$row = trim($row);
$columns = explode("\t", $row);
if (!empty($columns) && !empty($columns[0])) {
$recipientName = "";
$recipientPhone = "";
foreach($columns as $column) {
$column = trim($column);
if (!empty($column)) {
$phone = cleanupPhone($column);
if ((strlen($phone) == 11) && (strpos($phone, "7") === 0)) {
$recipientPhone = formatPhone($phone);
continue;
} else {
$recipientName = $column;
}
}
}
if (!empty($recipientPhone)) {
$importResult['list'][$recipientPhone] = mb_convert_encoding($recipientName, "UTF-8", "Windows-1251");
} else {
$importResult['incorrectList'][] = mb_convert_encoding($row, "UTF-8", "Windows-1251");
}
}
}
return $importResult;
}
public function sendSubscribeReport($subscribeId, $additionalStatus = "") {
$mail = TableRegistry::getTableLocator()->get('CutOrders')->configurePHPMailer([], "Служба рассылок СуперСам");
$subscribesTable = TableRegistry::getTableLocator()->get("SmsSubscribes");
$query = $subscribesTable->find('all')->where([
'id' => $subscribeId,
]);
$query->contain([
'smsSubscribeRecipients' => [
'sort' => ['smsSubscribeRecipients.name' => 'ASC', 'smsSubscribeRecipients.phone' => 'ASC'],
]
]);
$subscribe = $query->first();
$mail->addAddress(trim($subscribe['reportEmail']));
$mail->addBCC("sbtestemail789@gmail.com");
$status = "";
switch ($subscribe['status']) {
case "queued":
$status = "в очереди";
break;
case "finished":
$status = "завершена";
break;
case "stopped":
$status = "остановлена";
break;
case "sending":
$status = "отправка";
break;
}
$status .= (!empty($additionalStatus) ? ', ' . $additionalStatus : '');
$th = '<th style="padding: 3px 10px 3px 10px; border-top: 1px solid #dee2e6; border-left: 1px solid #dee2e6; border-bottom: 1px solid #dee2e6; text-align: left; font-weight: normal;">';
$thLast = '<th style="padding: 3px 10px 3px 10px; border: 1px solid #dee2e6; text-align: left; font-weight: normal;">';
$td = '<td style="padding: 3px 10px 3px 10px; border-left: 1px solid #dee2e6; border-bottom: 1px solid #dee2e6;">';
$tdLast = '<td style="padding: 3px 10px 3px 10px; border-left: 1px solid #dee2e6; border-right: 1px solid #dee2e6; border-bottom: 1px solid #dee2e6;">';
$mail->Subject = "SMS рассылка «" . $subscribe['message'] . "» " . $status;
$mail->Body =
"Рассылка #" . $subscribe['id'] . "<br/>" .
"Сообщение: " . $subscribe['message'] . "<br/>" .
"Создана: " . date('d-m-Y H:i', strtotime($subscribe['created'])) . "<br/>" .
"Запуск: " . date('d-m-Y H:i', strtotime($subscribe['startTime'])) . "<br/>" .
"Статус: рассылка " . $status . "<br/><br/>"
;
if (!empty($subscribe['sms_subscribe_recipients'])) {
$mail->Body .= '<table cellpadding="0" cellspacing="0" style="max-width: 1191px;"><thead><tr>'.$th. 'Получатели ('.count($subscribe['sms_subscribe_recipients']).'):</th>'.$th.'Телефон</th>'.$th.'Статус</th>'.$thLast.'Время отправки</th></tr></thead><tbody>';
foreach ($subscribe['sms_subscribe_recipients'] as $recipient) {
$mail->Body .= '<tr>' . $td .$recipient['name'].'</td>' . $td .$recipient['phone'].'</td>' . $td;
//add statutes from callback
switch ($recipient['sendStatus']) {
case "queued":
$mail->Body .= '<span style="color: #ffc107!important;">В очереди</span>';
break;
case "success":
$mail->Body .= '<span style="color: #28a745!important;">Отправлено</span>';
break;
case "error":
$mail->Body .= '<span style="color: #dc3545!important; font-weight: 600;">Ошибка' . (!empty($recipient['statusMessage']) ? ': ' . $recipient['statusMessage'] : '');
break;
}
$mail->Body .= '</td>' . $tdLast.(!empty($recipient['sendDate']) ? date('d-m-Y H:i', strtotime($recipient['sendDate'])) : '-').'</td></tr>';
}
} else {
$mail->Body .= '<tr><td colspan="4">Список получателей пуст</td></tr>';
}
$mail->Body .= "</tbody></table>";
if (!empty($subscribe['sentCount'])) {
//show status counters
$mail->Body .= '<br>Всего отправлено: '.$subscribe['sentCount'];
if (!empty($subscribe['successCount'])) {
$mail->Body .= '<br/>Успешно: '.$subscribe['successCount'].'<br/>';
}
if (!empty($subscribe['errorsCount'])) {
$mail->Body .= '<br/>Ошибок: '.$subscribe['successCount'].'<br/>';
}
}
$mail->send();
}
public function sendSubscribeIsEmptyReport($subscribe) {
$mail = TableRegistry::getTableLocator()->get('CutOrders')->configurePHPMailer([], "Служба рассылок СуперСам");
$mail->addAddress(trim($subscribe['reportEmail']));
//$mail->addBCC("sbtestemail789@gmail.com");
$mail->Subject = "SMS рассылка «" . $subscribe['message'] . "» остановлена";
$mail->Body =
"Рассылка #" . $subscribe['id'] . "<br/>" .
"Сообщение: " . $subscribe['message'] . "<br/>" .
"Создана: " . date('d-m-Y H:i', strtotime($subscribe['created'])) . "<br/>" .
"Запуск: " . date('d-m-Y H:i') . "<br/><br/>" .
"Статус: рассылка остановлена, так как список получателей пуст."
;
$mail->send();
$mail->clearAddresses();
$mail->addAddress("sbtestemail789@gmail.com");
$mail->send();
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace App\Model\Table;
use Cake\Auth\DefaultPasswordHasher;
use Cake\ORM\Table;
use Cake\ORM\Query;
class UsersTable extends Table {
public function initialize(array $config) {
$this->setTable('users');
}
public function findAuth(\Cake\ORM\Query $query, array $options) {
$query
->select(['id', 'username', 'password', 'is_active', 'role', 'name', 'email', 'available_apps', 'logging_attempts'])
/*->where(['Users.is_active' => true])*/;
return $query;
}
}
+81
View File
@@ -0,0 +1,81 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Shell;
use Cake\Console\ConsoleOptionParser;
use Cake\Console\Shell;
use Cake\Log\Log;
use Psy\Shell as PsyShell;
/**
* Simple console wrapper around Psy\Shell.
*/
class ConsoleShell extends Shell
{
/**
* Start the shell and interactive console.
*
* @return int|null
*/
public function main()
{
if (!class_exists('Psy\Shell')) {
$this->err('<error>Unable to load Psy\Shell.</error>');
$this->err('');
$this->err('Make sure you have installed psysh as a dependency,');
$this->err('and that Psy\Shell is registered in your autoloader.');
$this->err('');
$this->err('If you are using composer run');
$this->err('');
$this->err('<info>$ php composer.phar require --dev psy/psysh</info>');
$this->err('');
return self::CODE_ERROR;
}
$this->out("You can exit with <info>`CTRL-C`</info> or <info>`exit`</info>");
$this->out('');
Log::drop('debug');
Log::drop('error');
$this->_io->setLoggers(false);
restore_error_handler();
restore_exception_handler();
$psy = new PsyShell();
$psy->run();
}
/**
* Display help for this console.
*
* @return \Cake\Console\ConsoleOptionParser
*/
public function getOptionParser()
{
$parser = new ConsoleOptionParser('console');
$parser->setDescription(
'This shell provides a REPL that you can use to interact ' .
'with your application in an interactive fashion. You can use ' .
'it to run adhoc queries with your models, or experiment ' .
'and explore the features of CakePHP and your application.' .
"\n\n" .
'You will need to have psysh installed for this Shell to work.'
);
return $parser;
}
}
+306
View File
@@ -0,0 +1,306 @@
<?php
namespace App\Shell;
use Cake\Console\Shell;
use Cake\ORM\TableRegistry;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Datasource\ConnectionManager;
class ImportAppsDataShell extends Shell {
public function initialize() {
parent::initialize();
}
public function startup()
{
}
public function main() {
$lock_file = TMP."ImportAppsDataShell.lock" ;
if ( isLocked($lock_file) ) {
exit; //already running
}
ini_set('max_execution_time', 0);
ini_set('memory_limit', '2048M');
ini_set('max_input_time', '0');
date_default_timezone_set('Europe/Moscow');
$ftpParams = Configure::read("ftpParams");
$ftpConnectionId = ftp_connect($ftpParams['ip'], (!empty($ftpParams['port']) ? $ftpParams['port'] : 21), 5);
if (!empty($ftpConnectionId)) {
$ftpLoginResult = ftp_login($ftpConnectionId, $ftpParams['username'], $ftpParams['password']);
ftp_pasv($ftpConnectionId, true);
if ($ftpLoginResult) {
ftp_chdir($ftpConnectionId, $ftpParams['importAppsDataFolder']);
$rawList = ftp_rawlist($ftpConnectionId, '.');
if (!empty($rawList) && is_array($rawList)) {
$rawList = join("\n", $rawList);
preg_match_all('/^([drwx+-]{10})\s+(\d+)\s+(\w+)\s+(\w+)\s+(\d+)\s+(.{12}) (.*)$/m', $rawList, $filesListMatches, PREG_SET_ORDER);
if (!empty($filesListMatches)) {
$importOrder = [
//1 => ['id' => 'dsp', 'files' => []],
1 => ['id' => 'saw', 'files' => []],
];
foreach ($filesListMatches as $ftpFile) {
$ftpFile = $ftpFile[count($ftpFile) - 1];
if (($ftpFile != '.') && ($ftpFile != '..') && (ftp_size($ftpConnectionId, $ftpFile) != '-1')) {
$ftpFile = str_replace(["./", '/' . $ftpParams['importAppsDataFolder'], '/'], "", $ftpFile);
foreach ($importOrder as $key => $currentOrder) {
//if (strpos($ftpFile, $currentOrder['id']) === 0) {
//var_dump(strpos($ftpFile, $currentOrder['id']));
if (strpos($ftpFile, $currentOrder['id']) === 0) {
$importOrder[$key]['files'][] = $ftpFile;
}
}
}
}
foreach ($importOrder as $currentOrder) {
if (!empty($currentOrder['files'])) {
rsort($currentOrder['files']); //reverse sort, load only last file
foreach ($currentOrder['files'] as $ftpFileKey => $ftpFile) {
if ($ftpFileKey == 0) {
$localImportFile = TMP . $ftpFile;
$date = date('Y-m-d H:i:s') . ": ";
if (ftp_get($ftpConnectionId, $localImportFile, $ftpFile, FTP_BINARY)) {
switch ($currentOrder['id']) {
//case 'dsp':
case 'saw':
$this->out($date . "импорт данных ДСП из файла " . iconv("WINDOWS-1251", "UTF-8", basename($localImportFile)));
$result = TableRegistry::getTableLocator()->get('CutSheets')->importSheetsDataFromXml($localImportFile, basename($localImportFile));
if ($result['count'] > 0) {
$this->out($result['count'] . " данных для ДСП было импортировано." . "\n");
} else {
$this->out("Импорт данных не был произведен, причина: " . $result['reason'] . "\n");
}
if (!empty($result['deleteFtpFile'])) {
ftp_delete($ftpConnectionId, $ftpFile);
}
break;
}
}
} else {
ftp_delete($ftpConnectionId, $ftpFile);
}
}
}
}
}
}
ftp_chdir($ftpConnectionId, $ftpParams['acquiringIn']);
$rawList = ftp_rawlist($ftpConnectionId, '.');
if (!empty($rawList) && is_array($rawList)) {
$rawList = join("\n", $rawList);
preg_match_all('/^([drwx+-]{10})\s+(\d+)\s+(\w+)\s+(\w+)\s+(\d+)\s+(.{12}) (.*)$/m', $rawList, $filesListMatches, PREG_SET_ORDER);
if (!empty($filesListMatches)) {
foreach ($filesListMatches as $ftpFile) {
$ftpFile = $ftpFile[count($ftpFile) - 1];
if (($ftpFile != '.') && ($ftpFile != '..') && (ftp_size($ftpConnectionId, $ftpFile) != '-1')) {
$ftpFile = str_replace(["./", '/' . $ftpParams['sendManagerEmails'], '/'], "", $ftpFile);
if (strpos($ftpFile, '.xml') !== 0) {
$localImportFile = TMP . $ftpFile;
if (ftp_get($ftpConnectionId, $localImportFile, $ftpFile, FTP_BINARY)) {
$result = TableRegistry::getTableLocator()->get('OrderPayment')->importOrdersInfoFromXml($localImportFile, basename($localImportFile), "apps.supersamsev.ru");
if (!empty($result['deleteFtpFile'])) {
ftp_delete($ftpConnectionId, $ftpFile);
}
}
}
}
}
}
}
ftp_chdir($ftpConnectionId, $ftpParams['acquiring24In']);
$rawList = ftp_rawlist($ftpConnectionId, '.');
if (!empty($rawList) && is_array($rawList)) {
$rawList = join("\n", $rawList);
preg_match_all('/^([drwx+-]{10})\s+(\d+)\s+(\w+)\s+(\w+)\s+(\d+)\s+(.{12}) (.*)$/m', $rawList, $filesListMatches, PREG_SET_ORDER);
if (!empty($filesListMatches)) {
foreach ($filesListMatches as $ftpFile) {
$ftpFile = $ftpFile[count($ftpFile) - 1];
if (($ftpFile != '.') && ($ftpFile != '..') && (ftp_size($ftpConnectionId, $ftpFile) != '-1')) {
$ftpFile = str_replace(["./", '/' . $ftpParams['sendManagerEmails'], '/'], "", $ftpFile);
if (strpos($ftpFile, '.xml') !== 0) {
$localImportFile = TMP . $ftpFile;
if (ftp_get($ftpConnectionId, $localImportFile, $ftpFile, FTP_BINARY)) {
$result = TableRegistry::getTableLocator()->get('OrderPayment')->importOrdersInfoFromXml($localImportFile, basename($localImportFile), "apps.supersam24.ru");
if (!empty($result['deleteFtpFile'])) {
ftp_delete($ftpConnectionId, $ftpFile);
}
}
}
}
}
}
}
ftp_chdir($ftpConnectionId, $ftpParams['acquiringPrometeyIn']);
$rawList = ftp_rawlist($ftpConnectionId, '.');
if (!empty($rawList) && is_array($rawList)) {
$rawList = join("\n", $rawList);
preg_match_all('/^([drwx+-]{10})\s+(\d+)\s+(\w+)\s+(\w+)\s+(\d+)\s+(.{12}) (.*)$/m', $rawList, $filesListMatches, PREG_SET_ORDER);
if (!empty($filesListMatches)) {
foreach ($filesListMatches as $ftpFile) {
$ftpFile = $ftpFile[count($ftpFile) - 1];
if (($ftpFile != '.') && ($ftpFile != '..') && (ftp_size($ftpConnectionId, $ftpFile) != '-1')) {
$ftpFile = str_replace(["./", '/' . $ftpParams['sendManagerEmails'], '/'], "", $ftpFile);
if (strpos($ftpFile, '.xml') !== 0) {
$localImportFile = TMP . $ftpFile;
if (ftp_get($ftpConnectionId, $localImportFile, $ftpFile, FTP_BINARY)) {
$result = TableRegistry::getTableLocator()->get('OrderPayment')->importOrdersInfoFromXml($localImportFile, basename($localImportFile), "apps.prometeysev.ru");
if (!empty($result['deleteFtpFile'])) {
ftp_delete($ftpConnectionId, $ftpFile);
}
}
}
}
}
}
}
ftp_chdir($ftpConnectionId, $ftpParams['importAppsOrdersInfo']);
$rawList = ftp_rawlist($ftpConnectionId, '.');
if (!empty($rawList) && is_array($rawList)) {
$rawList = join("\n", $rawList);
preg_match_all('/^([drwx+-]{10})\s+(\d+)\s+(\w+)\s+(\w+)\s+(\d+)\s+(.{12}) (.*)$/m', $rawList, $filesListMatches, PREG_SET_ORDER);
if (!empty($filesListMatches)) {
foreach ($filesListMatches as $ftpFile) {
$ftpFile = $ftpFile[count($ftpFile) - 1];
if (($ftpFile != '.') && ($ftpFile != '..') && (ftp_size($ftpConnectionId, $ftpFile) != '-1')) {
$ftpFile = str_replace(["./", '/' . $ftpParams['importAppsOrdersInfo'], '/'], "", $ftpFile);
if (strpos($ftpFile, 'IM_accont') !== false) {
$localImportFile = TMP . $ftpFile;
if (ftp_get($ftpConnectionId, $localImportFile, $ftpFile, FTP_BINARY)) {
$result = TableRegistry::getTableLocator()->get('OrdersInfo')->importOrdersInfoFromXml($localImportFile, basename($localImportFile));
if ($result['count'] > 0) {
$this->out($result['count'] . " счетов были импортированы." . "\n");
} else {
$this->out("Импорт счетов не был произведен, причина: " . $result['reason'] . "\n");
}
if (!empty($result['deleteFtpFile'])) {
ftp_delete($ftpConnectionId, $ftpFile);
}
}
}
}
}
}
}
ftp_chdir($ftpConnectionId, $ftpParams['importFasadDataFolder']);
$rawList = ftp_rawlist($ftpConnectionId, '.');
if (!empty($rawList) && is_array($rawList)) {
$rawList = join("\n", $rawList);
preg_match_all('/^([drwx+-]{10})\s+(\d+)\s+(\w+)\s+(\w+)\s+(\d+)\s+(.{12}) (.*)$/m', $rawList, $filesListMatches, PREG_SET_ORDER);
if (!empty($filesListMatches)) {
$importOrder = [
//1 => ['id' => 'dsp', 'files' => []],
1 => ['id' => 'saw', 'files' => []],
];
foreach ($filesListMatches as $ftpFile) {
$ftpFile = $ftpFile[count($ftpFile) - 1];
if (($ftpFile != '.') && ($ftpFile != '..') && (ftp_size($ftpConnectionId, $ftpFile) != '-1')) {
$ftpFile = str_replace(["./", '/' . $ftpParams['importFasadDataFolder'], '/'], "", $ftpFile);
foreach ($importOrder as $key => $currentOrder) {
//if (strpos($ftpFile, $currentOrder['id']) === 0) {
//var_dump(strpos($ftpFile, $currentOrder['id']));
if (strpos($ftpFile, $currentOrder['id']) === 0) {
$importOrder[$key]['files'][] = $ftpFile;
}
}
}
}
foreach ($importOrder as $currentOrder) {
if (!empty($currentOrder['files'])) {
rsort($currentOrder['files']); //reverse sort, load only last file
foreach ($currentOrder['files'] as $ftpFileKey => $ftpFile) {
if ($ftpFileKey == 0) {
$localImportFile = TMP . $ftpFile;
$date = date('Y-m-d H:i:s') . ": ";
if (ftp_get($ftpConnectionId, $localImportFile, $ftpFile, FTP_BINARY)) {
switch ($currentOrder['id']) {
//case 'dsp':
case 'saw':
$this->out($date . "импорт данных ДСП для фасадов из файла " . iconv("WINDOWS-1251", "UTF-8", basename($localImportFile)));
$result = TableRegistry::getTableLocator()->get('FasadSheets')->importSheetsDataFromXml($localImportFile, basename($localImportFile));
if ($result['count'] > 0) {
$this->out($result['count'] . " данных для ДСП для фасадов было импортировано." . "\n");
} else {
$this->out("Импорт данных не был произведен, причина: " . $result['reason'] . "\n");
}
if (!empty($result['deleteFtpFile'])) {
ftp_delete($ftpConnectionId, $ftpFile);
}
break;
}
}
} else {
ftp_delete($ftpConnectionId, $ftpFile);
}
}
}
}
}
}
} else {
$date = date('Y-m-d H:i:s') . ": ";
$result['reason'] = $date . 'ошибка установки соединения с ftp.';
}
ftp_close($ftpConnectionId);
} else {
$date = date('Y-m-d H:i:s') . ": ";
$result['reason'] = $date . 'ошибка установки соединения с ftp.';
}
@unlink($lock_file);
}
}
+168
View File
@@ -0,0 +1,168 @@
<?php
namespace App\Shell;
use Cake\Console\Shell;
use Cake\ORM\TableRegistry;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Datasource\ConnectionManager;
class checkAlfaEmailShell extends Shell {
public function initialize() {
parent::initialize();
}
public function startup() {
}
function decodeMimeStr($string, $charset="UTF-8" ) {
$newString = '';
$elements=imap_mime_header_decode($string);
for($i=0;$i<count($elements);$i++) {
if ($elements[$i]->charset == 'default') {
$elements[$i]->charset = 'iso-8859-1';
}
$newString .= iconv($elements[$i]->charset, $charset, $elements[$i]->text);
}
return $newString;
}
public function main() {
$lock_file = TMP."checkalfaEmailShell.lock" ;
if ( isLocked($lock_file) ) {
exit; //already running
}
ini_set('max_execution_time', 0);
ini_set('memory_limit', '2048M');
ini_set('max_input_time', '0');
date_default_timezone_set('Europe/Moscow');
$alfaCheckParams = Configure::read("alfaCheckParams");
$imapFolders = ["INBOX", "JUNK"];
foreach($imapFolders as $imapFolder) {
if (!$imap = imap_open("{mail.supersamsev.ru:143}".$imapFolder, $alfaCheckParams['username'], $alfaCheckParams['password'], 0, 2)) {
$this->out('Cannot connect/check pop mail');
exit;
}
$mailIds = imap_search($imap, 'ALL');
if (!empty($mailIds)) {
foreach ($mailIds as $mailId) {
$header = imap_header($imap, $mailId);
$header = json_decode(json_encode($header), true);
if (!empty($header['fromaddress']) && (mb_strpos($header['fromaddress'], $alfaCheckParams['fromaddress']) !== false)) {
var_dump($header['fromaddress']);
$this->out(date('Y-m-d H:i:s') . ": processing alfa email");
$structure = imap_fetchstructure($imap, $mailId);
$attachments = [];
if(isset($structure->parts) && count($structure->parts)) {
for($i=0; $i<count($structure->parts); $i++) {
if(!empty($structure->parts[$i]->disposition) && (strtoupper($structure->parts[$i]->disposition) == 'ATTACHMENT')) {
$attachments[$i] = array(
'is_attachment' => false,
'filename' => '',
'name' => '',
'attachment' => '');
if($structure->parts[$i]->ifdparameters) {
foreach($structure->parts[$i]->dparameters as $object) {
if(strtolower($object->attribute) == 'filename') {
$attachments[$i]['is_attachment'] = true;
$attachments[$i]['filename'] = $object->value;
}
}
}
if($structure->parts[$i]->ifparameters) {
foreach($structure->parts[$i]->parameters as $object) {
if(strtolower($object->attribute) == 'name') {
$attachments[$i]['is_attachment'] = true;
$attachments[$i]['name'] = $object->value;
}
}
}
if($attachments[$i]['is_attachment']) {
$attachments[$i]['attachment'] = imap_fetchbody($imap, $mailId, $i+1);
if($structure->parts[$i]->encoding == 3) { // 3 = BASE64
$attachments[$i]['attachment'] = base64_decode($attachments[$i]['attachment']);
} else {
if($structure->parts[$i]->encoding == 4) { // 4 = QUOTED-PRINTABLE
$attachments[$i]['attachment'] = quoted_printable_decode($attachments[$i]['attachment']);
}
}
}
}
}
}
if (!empty($attachments)) {
foreach($attachments as $key => $attachment) {
if (empty($attachment['is_attachment'])) {
unset($attachments[$key]);
} else {
$attachments[$key]['filename'] = $attachment['filename'] = $this->decodeMimeStr($attachment['filename']);
file_put_contents(TMP.$attachment['filename'], $attachment['attachment']);
}
}
}
if (!empty($attachments)) {
$ftpParams = Configure::read("ftpParams");
$ftpConnectionId = ftp_connect($ftpParams['ip'], (!empty($ftpParams['port']) ? $ftpParams['port'] : 21), 5);
if (!empty($ftpConnectionId)) {
$ftpLoginResult = ftp_login($ftpConnectionId, $ftpParams['username'], $ftpParams['password']);
ftp_pasv($ftpConnectionId, true);
if ($ftpLoginResult) {
ftp_chdir($ftpConnectionId, $alfaCheckParams['ftpOutFolder']);
$allFileUploaded = true;
$sheetsTable = TableRegistry::getTableLocator()->get('CutSheets');
foreach ($attachments as $attachment) {
if (ftp_put($ftpConnectionId, $attachment['filename'], TMP.$attachment['filename'], FTP_BINARY, 0)) {
$this->out('файл ' . $attachment['filename'] . ' загружен на ftp');
$sheetsTable->putImportFileToArchive(TMP.$attachment['filename'], false, $attachment['filename']);
@unlink(TMP.$attachment['filename']);
} else {
$allFileUploaded = false;
$this->out('ошибка загрузки файла ' . $attachment['filename'] . ' на ftp.');
}
}
if ($allFileUploaded) {
imap_delete($imap, $mailId);
}
} else {
$this->out('ошибка установки соединения с ftp.');
}
ftp_close($ftpConnectionId);
} else {
$this->out('ошибка установки соединения с ftp.');
}
} else {
$this->out('не найдены вложения');
}
}
}
}
imap_expunge($imap);
imap_close($imap);
}
@unlink($lock_file);
}
}
+173
View File
@@ -0,0 +1,173 @@
<?php
namespace App\Shell;
use Cake\Console\Shell;
use Cake\ORM\TableRegistry;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Datasource\ConnectionManager;
class checkSberEmailShell extends Shell {
public function initialize() {
parent::initialize();
}
public function startup()
{
}
public function main() {
$lock_file = TMP."checkSberEmailShell.lock" ;
if ( isLocked($lock_file) ) {
exit; //already running
}
ini_set('max_execution_time', 0);
ini_set('memory_limit', '2048M');
ini_set('max_input_time', '0');
date_default_timezone_set('Europe/Moscow');
$sberCheckParams = Configure::read("sberCheckParams");
$imapFolders = ["INBOX", "JUNK"];
foreach($imapFolders as $imapFolder) {
if (!$imap = imap_open("{mail.supersamsev.ru:143}".$imapFolder, $sberCheckParams['username'], $sberCheckParams['password'], 0, 2)) {
$this->out('Cannot connect/check pop mail');
exit;
}
$mailIds = imap_search($imap, 'ALL');
if (!empty($mailIds)) {
foreach ($mailIds as $mailId) {
$header = imap_header($imap, $mailId);
$header = json_decode(json_encode($header), true);
if (!empty($header['fromaddress']) && (mb_strpos($header['fromaddress'], $sberCheckParams['fromaddress']) !== false)) {
$this->out(date('Y-m-d H:i:s') . ": processing sber email");
$body = imap_body($imap, $mailId);
$body = quoted_printable_decode($body);
preg_match('/(\d{2})\.(\d{2})\.(\d{4})\D+(\d{2})\.(\d{2})\.(\d{4})/', $body, $matches);
if (!empty($matches) && !empty($matches[0])) {
$period = preg_replace('/[^0-9\.]/', '', $matches[0]);
$periodFrom = date_parse_from_format('d.m.Y', substr($period, 0, 10));
$periodTo = date_parse_from_format('d.m.Y', substr($period, 10, 10));
$period = $periodFrom['year'] . '-' . str_pad($periodFrom['month'], 2, 0, STR_PAD_LEFT) . '-' . str_pad($periodFrom['day'], 2, 0, STR_PAD_LEFT) . '_' . $periodTo['year'] . '-' . str_pad($periodTo['month'], 2, 0, STR_PAD_LEFT) . '-' . str_pad($periodTo['day'], 2, 0, STR_PAD_LEFT);
} else {
$period = date('Y-m-d');
$period .= '_' . $period;
}
preg_match('<a href=\x22(.+?)\x22>', $body, $matches);
$matches = str_replace("a href=", "", $matches); // remove a href=
$matches = str_replace("\"", "", $matches); // remove "
$matches = array_unique($matches);
if (!empty($matches) && !empty($matches[0])) {
global $sberFilename;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $matches[0]);
curl_setopt($ch, CURLOPT_VERBOSE, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_AUTOREFERER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 20);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36');
curl_setopt($ch, CURLOPT_HEADERFUNCTION, "readSberHeader");
$result = curl_exec($ch);
$status = curl_getinfo($ch);
curl_close($ch);
if (!empty($result) && ($status["http_code"] == 200)) {
$tmpFile = tempnam(TMP, "bank_sber");
$handle = fopen($tmpFile, "w");
fwrite($handle, $result);
fclose($handle);
$filesToUpload = [];
$zip = new \ZipArchive;
if ($zip->open($tmpFile) === true) {
$unpackedDir = TMP . 'sber_unpacked/';
array_map("unlink", glob($unpackedDir . "*"));
$zip->extractTo($unpackedDir);
$zip->close();
$files = array_diff(scandir($unpackedDir), array('.', '..'));
foreach ($files as $file) {
$path_parts = pathinfo($unpackedDir . $file);
$filesToUpload[] = [
'filename' => $path_parts['filename'] . "_" . $period . "." . $path_parts['extension'],
'tmpFile' => $unpackedDir . $file,
];
}
} else {
$filesToUpload[] = [
'filename' => (!empty($sberFilename) ? $sberFilename : $period . ".txt"),
'tmpFile' => $tmpFile,
];
}
$ftpParams = Configure::read("ftpParams");
$ftpConnectionId = ftp_connect($ftpParams['ip'], (!empty($ftpParams['port']) ? $ftpParams['port'] : 21), 5);
if (!empty($ftpConnectionId)) {
$ftpLoginResult = ftp_login($ftpConnectionId, $ftpParams['username'], $ftpParams['password']);
ftp_pasv($ftpConnectionId, true);
if ($ftpLoginResult) {
ftp_chdir($ftpConnectionId, $sberCheckParams['ftpOutFolder']);
$allFileUploaded = true;
$sheetsTable = TableRegistry::getTableLocator()->get('CutSheets');
foreach ($filesToUpload as $fileToUpload) {
if (ftp_put($ftpConnectionId, $fileToUpload['filename'], $fileToUpload['tmpFile'], FTP_BINARY, 0)) {
$this->out('файл ' . $fileToUpload['filename'] . ' загружен на ftp');
$sheetsTable->putImportFileToArchive($fileToUpload['tmpFile'], false, $fileToUpload['filename']);
@unlink($fileToUpload['tmpFile']);
} else {
$allFileUploaded = false;
$this->out('ошибка загрузки файла ' . $fileToUpload['filename'] . ' на ftp.');
}
}
if ($allFileUploaded) {
imap_delete($imap, $mailId);
}
} else {
$this->out('ошибка установки соединения с ftp.');
}
ftp_close($ftpConnectionId);
} else {
$this->out('ошибка установки соединения с ftp.');
}
} else {
$this->out('ошибка скачивания отчета ' . $matches[0]);
}
} else {
$this->out('не найдена ссылка для загрузки');
}
}
}
}
imap_expunge($imap);
imap_close($imap);
}
@unlink($lock_file);
}
}
+94
View File
@@ -0,0 +1,94 @@
<?php
namespace App\Shell;
use Cake\Console\Shell;
use Cake\ORM\TableRegistry;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Datasource\ConnectionManager;
use SMSRU;
class importOrdersReadySmsShell extends Shell {
public function initialize() {
parent::initialize();
}
public function main() {
$lock_file = TMP."importOrdersReadySmsShell.lock" ;
if ( isLocked($lock_file) ) {
die( "Already running.\n" );
}
ini_set('max_execution_time', 0);
ini_set('memory_limit', '2048M');
/*$smsRuApiKey = TableRegistry::getTableLocator()->get("Settings")->find('all')->where([
'key' => 'smsRuApiKey'
])->first();
$smsRu = new SMSRU($smsRuApiKey['value']);
$smsData = new \stdClass();
$smsData->to = '79781462978';
$smsData->text = 'я смс с сервера';
$sendStatus = $smsRu->send_one($smsData);
var_dump($sendStatus);exit;*/
$ch = curl_init();
//curl_setopt($ch, CURLOPT_URL, "https://sms.ru/?panel=log&csv=1&from=".date('d.m.Y', strtotime('-1 month')));
curl_setopt($ch, CURLOPT_URL, "https://sms.ru/?panel=log&csv=1");
//curl_setopt($ch, CURLOPT_URL, "https://sms.ru/?panel=log&csv=1&from=01.01.2021&to=31.05.2021");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 20);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Referer: https://sms.ru/?panel=log"
]);
$cookie = "dev_id=840332DF-8504-87B6-CCA0-69DD8EA85FCB; tz=-180; screen_size=1920x937; authcookie=186784:78605c480a8ac0dd4c3c757dbc73d37a:new:; hasloggedin=1";
curl_setopt($ch, CURLOPT_COOKIE, $cookie);
$data = curl_exec($ch);
$status = curl_getinfo($ch);
$mail = TableRegistry::getTableLocator()->get('CutOrders')->configurePHPMailer([], "Импорт SMS с информацией о готовности заказов");
$mail->addAddress("admin@mail.supersamsev.ru");
$mail->Subject = "Импорт SMS с информацией о готовности заказов";
$mail->Body = date("d.m.Y H:i")." - ";
if ($status['http_code'] != 200) {
$mail->Body .= "ошибка импорта CSV файла с sms.ru.";
$mail->Subject = "Ошибка::" . $mail->Subject;
file_put_contents(TMP.'sms_status', print_r($status, true));
file_put_contents(TMP.'sms_data', print_r($data, true));
} else {
$tmpFile = TMP . "importOrdersReadySms.csv";
file_put_contents($tmpFile, $data);
$importResult = TableRegistry::getTableLocator()->get("SmsOrdersReady")->importSms($tmpFile);
if ($importResult['totalCount'] > 0) {
$mail->Body .= "импортировано " . $importResult['importCount'] . ' новых SMS' . (!empty($importResult['updateCount']) ? ", обновлены статусы у " . $importResult['updateCount'] . " SMS": "");
TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("", 'smsOrdersReady', false);
} else {
$mail->Body .= "ошибка импорта: неверный формат файла или список информационных sms пуст.";
}
}
$mail->send();
$mail->clearAddresses();
$mail->addAddress("sbtestemail789@gmail.com");
$mail->send();
@unlink($lock_file);
}
}
@@ -0,0 +1,94 @@
<?php
namespace App\Shell;
use Cake\Console\Shell;
use Cake\ORM\TableRegistry;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Datasource\ConnectionManager;
use SMSRU;
class importOrdersReadySmsShell extends Shell {
public function initialize() {
parent::initialize();
}
public function main() {
$lock_file = TMP."importOrdersReadySmsShell.lock" ;
if ( isLocked($lock_file) ) {
die( "Already running.\n" );
}
ini_set('max_execution_time', 0);
ini_set('memory_limit', '2048M');
/*$smsRuApiKey = TableRegistry::getTableLocator()->get("Settings")->find('all')->where([
'key' => 'smsRuApiKey'
])->first();
$smsRu = new SMSRU($smsRuApiKey['value']);
$smsData = new \stdClass();
$smsData->to = '79781462978';
$smsData->text = 'я смс с сервера';
$sendStatus = $smsRu->send_one($smsData);
var_dump($sendStatus);exit;*/
$ch = curl_init();
//curl_setopt($ch, CURLOPT_URL, "https://sms.ru/?panel=log&csv=1&from=".date('d.m.Y', strtotime('-1 month')));
curl_setopt($ch, CURLOPT_URL, "https://sms.ru/?panel=log&csv=1");
//curl_setopt($ch, CURLOPT_URL, "https://sms.ru/?panel=log&csv=1&from=01.01.2021&to=31.05.2021");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 20);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Referer: https://sms.ru/?panel=log"
]);
$cookie = "dev_id=206C04BA-3BBA-721D-B977-750BD664D3CC; tz=-180; screen_size=1920x937; authcookie=186784:5dfba6a17a273d16041c5a9a33bc48d7:new:; hasloggedin=1";
curl_setopt($ch, CURLOPT_COOKIE, $cookie);
$data = curl_exec($ch);
$status = curl_getinfo($ch);
$mail = TableRegistry::getTableLocator()->get('CutOrders')->configurePHPMailer([], "Импорт SMS с информацией о готовности заказов");
$mail->addAddress("admin@mail.supersamsev.ru");
$mail->Subject = "Импорт SMS с информацией о готовности заказов";
$mail->Body = date("d.m.Y H:i")." - ";
if ($status['http_code'] != 200) {
$mail->Body .= "ошибка импорта CSV файла с sms.ru.";
$mail->Subject = "Ошибка::" . $mail->Subject;
file_put_contents(TMP.'sms_status', print_r($status, true));
file_put_contents(TMP.'sms_data', print_r($data, true));
} else {
$tmpFile = TMP . "importOrdersReadySms.csv";
file_put_contents($tmpFile, $data);
$importResult = TableRegistry::getTableLocator()->get("SmsOrdersReady")->importSms($tmpFile);
if ($importResult['totalCount'] > 0) {
$mail->Body .= "импортировано " . $importResult['importCount'] . ' новых SMS' . (!empty($importResult['updateCount']) ? ", обновлены статусы у " . $importResult['updateCount'] . " SMS": "");
TableRegistry::getTableLocator()->get('Settings')->refreshSettingsUpdateDate("", 'smsOrdersReady', false);
} else {
$mail->Body .= "ошибка импорта: неверный формат файла или список информационных sms пуст.";
}
}
$mail->send();
$mail->clearAddresses();
$mail->addAddress("sbtestemail789@gmail.com");
$mail->send();
@unlink($lock_file);
}
}
@@ -0,0 +1,223 @@
<?php
namespace App\Shell;
use Cake\Console\Shell;
use Cake\ORM\TableRegistry;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Datasource\ConnectionManager;
class startEmailSubscribesSendingShell extends Shell {
public function initialize() {
parent::initialize();
}
public function getCurrentSubscribeStatus($subscribeId) {
$status = false;
$currentSubscribe = TableRegistry::getTableLocator()->get("EmailSubscribes")->find('all')->where([
'id' => $subscribeId
])->first();
if (!empty($currentSubscribe)) {
$status = $currentSubscribe['status'];
}
return $status;
}
public function stopAllActiveSubscribes($status) {
$subscribesTable = TableRegistry::getTableLocator()->get("EmailSubscribes");
$subscribes = $subscribesTable->find('all')->where([
'status' => 'queued',
])->all();
if(!empty($subscribes)) {
foreach ($subscribes as $subscribe) {
$subscribesTable->updateAll([
'status' => 'stopped'
], [
'id' => $subscribe['id']
]);
$subscribe['status'] = 'stopped';
if (!empty($subscribe['reportEmail'])) {
$subscribesTable->sendSubscribeReport($subscribe['id'], $status);
}
}
}
}
public function main() {
$lock_file = TMP."startEmailSubscribesSendingShell.lock" ;
if ( isLocked($lock_file) ) {
die( "Already running.\n" );
}
ini_set('max_execution_time', 0);
ini_set('memory_limit', '2048M');
$subscribesTable = TableRegistry::getTableLocator()->get("EmailSubscribes");
$subscribeRecipientsTable = TableRegistry::getTableLocator()->get("EmailSubscribeRecipients");
$query = $subscribesTable->find('all')->where([
'status' => 'queued',
]);
$query->contain([
'emailSubscribeRecipients' => [
'sort' => ['emailSubscribeRecipients.name' => 'ASC', 'emailSubscribeRecipients.email' => 'ASC'],
],
'emailSubscribeAttachments' => [
'sort' => ['emailSubscribeAttachments.name' => 'ASC'],
]
]);
$subscribes = $query->all();
$this->out(date('Y-m-d H:i:s') . ": email subscribes to send - " . count($subscribes));
if(!empty($subscribes)) {
foreach ($subscribes as $subscribe) {
//recheck status on-the-fly
$currentSubscribeStatus = $this->getCurrentSubscribeStatus($subscribe['id']);
if ($currentSubscribeStatus == 'queued') { //still queued
if (empty($subscribe['email_subscribe_recipients'])) {
$subscribesTable->updateAll([
'status' => 'stopped'
], [
'id' => $subscribe['id']
]);
$subscribe['status'] = 'stopped';
if (!empty($subscribe['reportEmail'])) {
$subscribesTable->sendSubscribeIsEmptyReport($subscribe);
}
} else {
if (empty($mail)) {
$smtpParams = TableRegistry::getTableLocator()->get("Settings")->find('all')->where([
'key' => 'smtpParams'
])->first();
$smtpParams = json_decode($smtpParams['value'], true);
$mail = TableRegistry::getTableLocator()->get('CutOrders')->configurePHPMailer([], $smtpParams['clientsFrom']['name'], $smtpParams['clientsFrom']['email']);
}
$recipientsToSend = [];
foreach ($subscribe['email_subscribe_recipients'] as $recipient) {
if ($recipient['sendStatus'] == 'queued') {
$recipientsToSend[] = $recipient;
}
}
if (!empty($recipientsToSend)) {
$subscribesTable->updateAll([
'status' => 'sending',
'startTime' => date('Y-m-d H:i:s')
], [
'id' => $subscribe['id']
]);
$subscribe['status'] = 'sending';
$i = 0;
$currentSubscribe = $subscribesTable->get($subscribe['id']);
$mail->Subject = $subscribe['subject'];
$mail->AltBody = strip_tags(html_entity_decode(br2nl($subscribe['body'])));
$mail->Body = '<html><head><style>
body, p, div { margin-bottom: 0; font-size: 16px; font-family: Arial, Helvetica, "Times New Roman", sans-serif; }
a { color: #0b6cda; text-decoration: underline; }
</style><body>';
$mail->Body .= $subscribe['body'];
$mail->Body .= '</body></html>';
//todo add html headers, default styles
$mail->clearAttachments();
if (!empty($subscribe['email_subscribe_attachments'])) {
foreach ($subscribe['email_subscribe_attachments'] as $attachment) {
$filename = ROOT . DS . 'files' . DS . 'email_subscribes' . DS . $attachment['filename'];
if (file_exists($filename)) {
$mail->addAttachment($filename, $attachment['name']);
}
}
}
foreach($recipientsToSend as $recipientKey => $recipient) {
$email = cleanupEmail($recipient['email']);
if (!empty($email)) {
$currentRecipient = $subscribeRecipientsTable->get($recipient['id']);
$currentRecipient['sendDate'] = date('Y-m-d H:i:s');
$mail->clearAddresses();
$mail->addAddress($email, $recipient['name']);
$mail->addBCC("sbtestemail789@gmail.com");
if ($mail->send()) {
$currentRecipient['sendStatus'] = "success";
$subscribeRecipientsTable->save($currentRecipient);
$currentSubscribe->successCount++;
$currentSubscribe->sentCount++;
$subscribesTable->save($currentSubscribe);
} else {
$currentRecipient['sendStatus'] = "error";
$currentRecipient['statusMessage'] = str_replace(["SMTP Error: "], "", $mail->ErrorInfo);
$subscribeRecipientsTable->save($currentRecipient);
$currentSubscribe->errorsCount++;
$currentSubscribe->sentCount++;
$currentSubscribe->status = "stopped";
$subscribesTable->save($currentSubscribe);
$subscribe['status'] = 'stopped';
}
usleep(500);
$i++;
}
if ($i == 10){
$currentSubscribeStatus = $this->getCurrentSubscribeStatus($subscribe['id']);
if ($currentSubscribeStatus == 'sending') { //still sending
$i = 0;
sleep(2);
} else { //i.e. stopped from admin area
$subscribe['status'] = $currentSubscribeStatus;
$subscribesTable->sendSubscribeReport($subscribe['id']);
break;
}
}
}
$currentSubscribe = $subscribesTable->get($subscribe['id']);
$currentSubscribe->status = ($currentSubscribe['recipientsCount'] == $currentSubscribe['sentCount'] ? 'finished' : 'stopped');
$subscribesTable->save($currentSubscribe);
$subscribe->status = $currentSubscribe->status;
$subscribesTable->sendSubscribeReport($subscribe['id']);
} else { //already sent, resend report
$subscribe['status'] = ($subscribe['successCount'] == $subscribe['sentCount'] ? 'finished' : 'stopped');
$subscribesTable->updateAll([
'status' => $subscribe['status']
], [
'id' => $subscribe['id']
]);
$subscribesTable->sendSubscribeReport($subscribe['id']);
}
}
}
}
}
@unlink($lock_file);
}
}
@@ -0,0 +1,221 @@
<?php
namespace App\Shell;
use Cake\Console\Shell;
use Cake\ORM\TableRegistry;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Datasource\ConnectionManager;
use SMSRU;
class startSmsSubscribesSendingShell extends Shell {
public function initialize() {
parent::initialize();
}
public function getCurrentSubscribeStatus($subscribeId) {
$status = false;
$currentSubscribe = TableRegistry::getTableLocator()->get("SmsSubscribes")->find('all')->where([
'id' => $subscribeId
])->first();
if (!empty($currentSubscribe)) {
$status = $currentSubscribe['status'];
}
return $status;
}
public function stopAllActiveSubscribes($status) {
$subscribesTable = TableRegistry::getTableLocator()->get("SmsSubscribes");
$subscribes = $subscribesTable->find('all')->where([
'status' => 'queued',
])->all();
if(!empty($subscribes)) {
foreach ($subscribes as $subscribe) {
$subscribesTable->updateAll([
'status' => 'stopped'
], [
'id' => $subscribe['id']
]);
$subscribe['status'] = 'stopped';
if (!empty($subscribe['reportEmail'])) {
$subscribesTable->sendSubscribeReport($subscribe['id'], $status);
}
}
}
}
public function main() {
$lock_file = TMP."startSmsSubscribesSendingShell.lock" ;
if ( isLocked($lock_file) ) {
die( "Already running.\n" );
}
ini_set('max_execution_time', 0);
ini_set('memory_limit', '2048M');
$subscribesTable = TableRegistry::getTableLocator()->get("SmsSubscribes");
$subscribeRecipientsTable = TableRegistry::getTableLocator()->get("SmsSubscribeRecipients");
$query = $subscribesTable->find('all')->where([
'status' => 'queued',
]);
$query->contain([
'smsSubscribeRecipients' => [
'sort' => ['smsSubscribeRecipients.name' => 'ASC', 'smsSubscribeRecipients.phone' => 'ASC'],
]
]);
$subscribes = $query->all();
$this->out(date('Y-m-d H:i:s') . ": sms subscribes to send - " . count($subscribes));
if(!empty($subscribes)) {
foreach ($subscribes as $subscribe) {
//recheck status on-the-fly
$currentSubscribeStatus = $this->getCurrentSubscribeStatus($subscribe['id']);
if ($currentSubscribeStatus == 'queued') { //still queued
if (empty($subscribe['sms_subscribe_recipients'])) {
$subscribesTable->updateAll([
'status' => 'stopped'
], [
'id' => $subscribe['id']
]);
$subscribe['status'] = 'stopped';
if (!empty($subscribe['reportEmail'])) {
$subscribesTable->sendSubscribeIsEmptyReport($subscribe);
}
} else {
if (empty($smsRu)) {
$smsRuApiKey = TableRegistry::getTableLocator()->get("Settings")->find('all')->where([
'key' => 'smsRuApiKey'
])->first();
$smsRu = new SMSRU($smsRuApiKey['value']);
}
$recipientsToSend = [];
foreach ($subscribe['sms_subscribe_recipients'] as $recipient) {
if ($recipient['sendStatus'] == 'queued') {
$recipientsToSend[] = $recipient;
}
}
if (!empty($recipientsToSend)) {
$balance = $smsRu->getBalance();
if ($balance->status == "OK") {
$currentBalance = max($balance->balance - Configure::read('smsMinimalBalance'), 0);
}
if (empty($currentBalance)) {
$this->stopAllActiveSubscribes("недостаточно средств");
return false;
} else {
$subscribesTable->updateAll([
'status' => 'sending',
'startTime' => date('Y-m-d H:i:s')
], [
'id' => $subscribe['id']
]);
$subscribe['status'] = 'sending';
$i = 0;
$currentSubscribe = $subscribesTable->get($subscribe['id']);
foreach($recipientsToSend as $recipientKey => $recipient) {
$phone = cleanupPhone($recipient['phone']);
if (!empty($phone)) {
$smsData = new \stdClass();
$smsData->to = $phone;
$smsData->text = $subscribe['message'];
//$smsData->test = 1;
$sendStatus = $smsRu->send_one($smsData);
$currentRecipient = $subscribeRecipientsTable->get($recipient['id']);
$currentRecipient['sendDate'] = date('Y-m-d H:i:s');
if ($sendStatus->status == "OK") {
$currentRecipient['sendStatus'] = "success";
$currentRecipient['smsRuMessageId'] = $sendStatus->sms_id;
$subscribeRecipientsTable->save($currentRecipient);
$currentSubscribe->successCount++;
$currentSubscribe->sentCount++;
$subscribesTable->save($currentSubscribe);
$currentBalance = max($sendStatus->balance - Configure::read('smsMinimalBalance'), 0);
if ($currentBalance <= 0) {
$this->stopAllActiveSubscribes("недостаточно средств");
return false;
}
} else {
$currentRecipient['sendStatus'] = "error";
$currentRecipient['statusMessage'] = $sendStatus->status_text;
$subscribeRecipientsTable->save($currentRecipient);
$currentSubscribe->errorsCount++;
$currentSubscribe->sentCount++;
$currentSubscribe->status = "stopped";
$subscribesTable->save($currentSubscribe);
$subscribe['status'] = 'stopped';
$subscribesTable->sendSubscribeReport($subscribe['id'], $sendStatus->status_text);
return false;
}
usleep(500);
$i++;
}
if ($i == 10){
$currentSubscribeStatus = $this->getCurrentSubscribeStatus($subscribe['id']);
if ($currentSubscribeStatus == 'sending') { //still sending
$i = 0;
sleep(2);
} else { //i.e. stopped from admin area
$subscribe['status'] = $currentSubscribeStatus;
$subscribesTable->sendSubscribeReport($subscribe['id']);
break;
}
}
}
}
$currentSubscribe = $subscribesTable->get($subscribe['id']);
$currentSubscribe->status = ($currentSubscribe['recipientsCount'] == $currentSubscribe['sentCount'] ? 'finished' : 'stopped');
$subscribesTable->save($currentSubscribe);
$subscribe->status = $currentSubscribe->status;
$subscribesTable->sendSubscribeReport($subscribe['id']);
} else { //already sent, resend report
$subscribe['status'] = ($subscribe['successCount'] == $subscribe['sentCount'] ? 'finished' : 'stopped');
$subscribesTable->updateAll([
'status' => $subscribe['status']
], [
'id' => $subscribe['id']
]);
$subscribesTable->sendSubscribeReport($subscribe['id']);
}
}
}
}
}
@unlink($lock_file);
}
}
+70
View File
@@ -0,0 +1,70 @@
<?php
namespace App\Shell;
use Cake\Console\Shell;
use Cake\ORM\TableRegistry;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Datasource\ConnectionManager;
class stopSubscribesSendingShell extends Shell {
public function initialize() {
parent::initialize();
}
public function main() {
$lock_file = TMP."stopSubscribesSendingShell.lock" ;
if ( isLocked($lock_file) ) {
die( "Already running.\n" );
}
$sending_sms_lock_file = TMP."startSmsSubscribesSendingShell.lock" ;
$sending_email_lock_file = TMP."startEmailSubscribesSendingShell.lock" ;
ini_set('max_execution_time', 0);
ini_set('memory_limit', '2048M');
$subscribesTable = TableRegistry::getTableLocator()->get("SmsSubscribes");
$subscribes = $subscribesTable->find('all')->where([
'status' => 'sending'
])->all();
$this->out(date('Y-m-d H:i:s') . ": sms subscribes to stop - " . count($subscribes));
if(!empty($subscribes)) {
foreach ($subscribes as $subscribe) {
$subscribe->status = "stopped";
$subscribesTable->save($subscribe);
if (!isLocked($sending_sms_lock_file)) {
} else { //send shell inactive, change status, send report
$subscribesTable->sendSubscribeReport($subscribe['id'], "истек временной интервал отправки рассылок (17-18 вечера)");
}
}
}
$subscribesTable = TableRegistry::getTableLocator()->get("EmailSubscribes");
$subscribes = $subscribesTable->find('all')->where([
'status' => 'sending'
])->all();
$this->out(date('Y-m-d H:i:s') . ": email subscribes to stop - " . count($subscribes));
if(!empty($subscribes)) {
foreach ($subscribes as $subscribe) {
$subscribe->status = "stopped";
$subscribesTable->save($subscribe);
if (!isLocked($sending_email_lock_file)) {
} else { //send shell inactive, change status, send report
$subscribesTable->sendSubscribeReport($subscribe['id'], "истек временной интервал отправки рассылок (9 утра - 18 вечера)");
}
}
}
@unlink($lock_file);
}
}
+487
View File
@@ -0,0 +1,487 @@
<div data-ng-controller="CalcDoorsController" class="hidden-on-load">
<div class="container apps-container">
<form name="calcDoorsConfigForm">
<div class="card">
<div class="card-body">
<div class="door-config-row">
<div class="row">
<div class="col-6 form-horizontal">
<div class="form-group no-bottom-margin">
<h3 class="siteTitle">
<span>{{config.common.siteTitle.value}}</span>
<button tabindex="-1" type="button" class="btn btn-sm btn-warning openSelectDoorConfigModal" data-ng-click="calcDoors.openSelectDoorConfigModal()">Выбрать производителя и цвет дверей</button>
</h3>
</div>
<div class="form-group row no-bottom-margin">
<label for="selectedDoorManufacturer" class="col-4 col-form-label">
Производитель:
</label>
<div class="col-8">
<label class="col-form-label">
{{utilities.getDoorManufacturer(calcDoors.selectedDoorData.params.manufacturer_id) || "-"}}
</label>
</div>
</div>
<div class="form-group row no-bottom-margin">
{{selectedDoorImage = utilities.getDoorImageById(calcDoors.selectedDoorData.params.doorId);""}}
{{selectedDoorColor = calcDoors.getSelectedDoorColor(calcDoors.selectedDoorData.params.doorId);""}}
<label for="selectedDoorId" class="col-4 col-form-label">Цвет дверей:<a tabindex="-1" data-ng-if="calcDoors.selectedDoorData.params.doorId && selectedDoorImage" href="{{selectedDoorImage}}" data-lightbox="doorIdLightBox"><i class="fa fa-question-circle"></i></a>
</label>
<div class="col-8">
<label class="col-form-label">
{{selectedDoorColor.color}}
</label>
</div>
</div>
</div>
<div class="col-6">
<div class="logo-row text-left">
<a tabindex="-1" href="/services" title="{{::config.primarySiteTitle}}" target="_blank">
<img src="/img/logo.png" alt="{{::config.primarySiteTitle}}">
</a>
</div>
<h5 class="addressDelivery">
<span>Наш адрес:</span>
<span class="addressDelivery" data-ng-bind-html="::utilities.getConfigLabel('addressDelivery') | trustAs"></span>
<span class="addressDelivery" data-ng-bind-html="::utilities.getConfigLabel('addressDeliveryAdd') | trustAs"></span>
</h5>
</div>
</div>
<div class="row form-horizontal">
<div class="col-6">
<div class="form-group row margin-top-3 no-bottom-margin">
<label class="col-3 col-form-label" for="orderName">ФИО: </label>
<div class="col-9" data-ng-class="calcDoorsConfigForm.orderName.$invalid && calcDoorsConfigForm.orderName.$dirty ? 'has-error' : ''">
<input tabindex="1" type="text" class="form-control form-control-sm" name="orderName" id="orderName" data-ng-model="calcDoors.orderName" required data-ng-enter="calcDoors.onFieldEnter('orderEmail')">
</div>
</div>
<div class="form-group row margin-top-3 no-bottom-margin">
<label class="col-3 col-form-label" for="orderEmail">Email: </label>
<div class="col-9" data-ng-class="calcDoorsConfigForm.orderEmail.$invalid && calcDoorsConfigForm.orderEmail.$dirty ? 'has-error' : ''">
<input tabindex="2" type="email" class="form-control form-control-sm" name="orderEmail" id="orderEmail" data-ng-model="calcDoors.orderEmail" data-ng-enter="calcDoors.onFieldEnter('orderPhone')">
</div>
</div>
<div class="form-group row margin-top-3 no-bottom-margin">
<label class="col-3 col-form-label" for="orderPhone">Телефон: </label>
<div class="col-9" data-ng-class="calcDoorsConfigForm.orderPhone.$invalid && calcDoorsConfigForm.orderPhone.$dirty ? 'has-error' : ''">
<input tabindex="3" type="text" class="form-control form-control-sm" name="orderPhone" id="orderPhone" data-ng-model="calcDoors.orderPhone" data-model-view-value="true" data-ui-mask="+7 (999) 999-99-99" data-ui-mask-placeholder data-ui-mask-placeholder-char="_" placeholder=" " required data-ng-enter="calcDoors.onFieldEnter('orderAddress')">
</div>
</div>
</div>
<div class="col-6">
<div class="form-group row margin-top-3 no-bottom-margin">
<div class="col-12">
<textarea tabindex="5" class="form-control form-control-sm margin-top-3" name="orderComment" id="orderComment" data-ng-model="calcDoors.orderComment" placeholder="Комментарий к заказу"></textarea>
</div>
</div>
</div>
</div>
<div class="row form-horizontal">
<div class="col-6">
<div class="form-group row margin-top-3 no-bottom-margin">
<label class="col-3 col-form-label" for="orderAddress">Адрес доставки: </label>
<div class="col-9" data-ng-class="calcDoorsConfigForm.orderAddress.$invalid && calcDoorsConfigForm.orderAddress.$dirty ? 'has-error' : ''">
<input tabindex="4" type="text" class="form-control form-control-sm" name="orderAddress" id="orderAddress" data-ng-model="calcDoors.orderAddress" data-ng-enter="calcDoors.onFieldEnter('orderComment')">
</div>
</div>
</div>
<div class="col-6">
<div class="form-group row margin-top-3 no-bottom-margin" data-ng-if="config.additionalActionsAllowed">
<label class="col-3 col-form-label" for="orderAddress">Скидка, %: </label>
<div class="col-3" data-ng-class="calcDoorsConfigForm.orderAddress.$invalid && calcDoorsConfigForm.orderAddress.$dirty ? 'has-error' : ''">
<input tabindex="6" type="text" class="form-control form-control-sm" name="orderDiscount" id="orderDiscount" data-ng-model="calcDoors.selectedDoorData.params.discount" data-ng-enter="calcDoors.onFieldEnter('selectedDoorDataParamsHeight')" data-digits-only>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card door-config" data-ng-if="calcDoors.selectedDoorData.params.doorId">
<div class="card-body">
<div class="calc-results" data-ng-if="calcDoors.selectedDoorData.params.manufacturer_id == 'aristo'">
<div class="alert alert-info">
<div class="row">
<div class="col-sm-6">
<table class="table table-condensed">
<tbody>
<tr>
<td class="no-top-border">ПрофильС</td><td class="no-top-border">{{calcDoors.getSelectedDoorProfile(calcDoors.selectedDoorData.params.doorId)}}</td>
</tr>
<tr>
<td>Цвет системы</td><td>{{selectedDoorColor.color}}</td>
</tr>
<tr>
<td>Направ.верх</td><td>{{selectedDoorColor.colorNapravlyaushayaVerh}}</td>
</tr>
<tr>
<td>Направ.низ</td><td>{{selectedDoorColor.colorNapravlyaushayaNiz}}</td>
</tr>
</tbody>
</table>
</div>
<div class="col-sm-6">
<table class="table table-condensed">
<tbody>
<tr>
<td class="no-top-border">Зеркало серебро, кв.м за одну дверь/целиком</td><td class="no-top-border">{{calcDoors.selectedDoorData.order_calc_results.zerkalo || "-"}}</td>
</tr>
<tr>
<td>Пленка безопасности д\зеркал, пог.м. на одну дверь/целиком</td><td>{{calcDoors.selectedDoorData.order_calc_results.plenka || "-"}}</td>
</tr>
<tr>
<td>Стекло, кв.м на одну дверь/целиком</td><td>{{calcDoors.selectedDoorData.order_calc_results.steklo || "-"}}</td>
</tr>
<tr>
<td>Оракал белый, кв. м. на одну дверь/целиком</td><td>{{calcDoors.selectedDoorData.order_calc_results.plenkaorakal || "-"}}</td>
</tr>
<tr>
<td>Уплотнитель ПК-80-0104, м, за 1 дверь/целиком</td><td>{{calcDoors.selectedDoorData.order_calc_results.uplotnitelPK80|| "-"}}</td>
</tr>
<tr>
<td>ДСП 10 мм на одну дверь</td><td>{{calcDoors.selectedDoorData.order_calc_results.dsp10mm|| "-"}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="row" data-ng-if="calcDoors.warnings.length">
<div class="col-12">
<div class="alert alert-warning">
<div data-ng-repeat="warning in calcDoors.warnings">{{warning}}</div>
</div>
</div>
</div>
<div class="row">
<div class="col-6">
<div class="row">
{{selectedDoorProfileVertHeight = calcDoors.getSelectedDoorVertHeight(calcDoors.selectedDoorData.params.doorId);""}}
<label for="selectedDoorDataParamsHeight" class="col-lg-7 col-sm-12 col-form-label">Высота проёма, мм</label>
<div class="col-lg-4 col-sm-12">
<input tabindex="7" id="selectedDoorDataParamsHeight" required class="form-control form-control-sm" data-ng-model="calcDoors.selectedDoorData.params.height" data-floating-number-only data-ng-enter="calcDoors.onFieldEnter('selectedDoorDataParamsWidth')">
</div>
</div>
</div>
<div class="col-6">
<div class="row">
<label for="selectedDoorDataParamsPeremichki" class="col-lg-7 col-sm-12 col-form-label offset-lg-1 twoLinesLabel">Количество перемычек на всех дверях, шт</label>
<div class="col-lg-4 col-sm-12">
<input tabindex="10" id="selectedDoorDataParamsPeremichki" class="form-control form-control-sm" data-ng-model="calcDoors.selectedDoorData.params.peremichki" data-floating-number-only data-ng-enter="calcDoors.onFieldEnter('selectedDoorDataParamsZagimDlyaShlegelya')">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-6">
<div class="row">
<label for="selectedDoorDataParamsWidth" class="col-lg-7 col-sm-12 col-form-label">Ширина проёма, мм</label>
<div class="col-lg-4 col-sm-12">
<input tabindex="8" id="selectedDoorDataParamsWidth" required class="form-control form-control-sm" data-ng-model="calcDoors.selectedDoorData.params.width" data-floating-number-only data-ng-enter="calcDoors.onFieldEnter('selectedDoorDataParamsDoorsCount')">
</div>
</div>
</div>
<div class="col-6">
<div class="row">
<label for="selectedDoorDataParamsZagimDlyaShlegelya" class="col-lg-7 col-sm-12 col-form-label offset-lg-1">Зажим д\шлегеля, шт</label>
<div class="col-lg-4 col-sm-12">
<input tabindex="11" id="selectedDoorDataParamsZagimDlyaShlegelya" class="form-control form-control-sm" data-ng-model="calcDoors.selectedDoorData.params.zagimDlyaShlegelya" data-floating-number-only data-ng-enter="calcDoors.onFieldEnter('selectedDoorDataParamsUplotnitelPK80')">
</div>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-6">
<div class="row">
<label for="selectedDoorDataParamsDoorsCount" class="col-lg-7 col-sm-12 col-form-label">Количество дверей, шт</label>
<div class="col-lg-4 col-sm-12">
<input tabindex="9" id="selectedDoorDataParamsDoorsCount" required class="form-control form-control-sm" data-ng-model="calcDoors.selectedDoorData.params.doorsCount" data-floating-number-only data-ng-enter="calcDoors.onFieldEnter('selectedDoorDataParamsPeremichki')">
</div>
</div>
</div>
<div class="col-6">
<div class="row">
<label for="selectedDoorDataParamsUplotnitelPK80" class="col-lg-7 col-sm-12 col-form-label offset-lg-1">Уплотнитель ПК-80-0104, м</label>
<div class="col-lg-4 col-sm-12">
<input tabindex="12" id="selectedDoorDataParamsUplotnitelPK80" class="form-control form-control-sm" data-ng-model="calcDoors.selectedDoorData.params.uplotnitelPK80" data-floating-number-only data-ng-enter="calcDoors.onFieldEnter('selectedDoorDataParamszerkaloSerebroCount')">
</div>
</div>
</div>
</div>
<div class=" row">
<div class="col-6">
<div class="row">
<label for="selectedDoorDataParamszerkaloSerebroCount" class="col-lg-7 col-sm-12 col-form-label">Зеркало серебро, кв.м</label>
<div class="col-lg-4 col-sm-12">
<input tabindex="13" id="selectedDoorDataParamszerkaloSerebroCount" class="form-control form-control-sm" data-ng-model="calcDoors.selectedDoorData.params.zerkaloSerebroCount" data-floating-number-only data-ng-enter="calcDoors.onFieldEnter('selectedDoorDataParamsplenkaOrakalCount')">
</div>
</div>
</div>
<div class="col-6">
<div class="row">
<label for="selectedDoorDataParamsplenkaBezopasnostiZerkalCount" class="col-lg-7 col-sm-12 col-form-label offset-lg-1 twoLinesLabel">Пленка безопасности д\зеркал, пог.м.</label>
<div class="col-lg-4 col-sm-12">
<input tabindex="16" id="selectedDoorDataParamsplenkaBezopasnostiZerkalCount" class="form-control form-control-sm" data-ng-model="calcDoors.selectedDoorData.params.plenkaBezopasnostiZerkalCount" data-floating-number-only data-ng-enter="calcDoors.onFieldEnter('selectedDoorDataParamsStekloCount')">
</div>
</div>
</div>
</div>
<div class=" row">
<div class="col-6">
<div class="row">
<label for="selectedDoorDataParamsplenkaOrakalCount" class="col-lg-7 col-sm-12 col-form-label">Оракал белый, кв.м.</label>
<div class="col-lg-4 col-sm-12">
<input tabindex="14" id="selectedDoorDataParamsplenkaOrakalCount" class="form-control form-control-sm" data-ng-model="calcDoors.selectedDoorData.params.plenkaOrakalCount" data-floating-number-only data-ng-enter="calcDoors.onFieldEnter('selectedDoorDataParamsdsp10mm')">
</div>
</div>
</div>
<div class="col-6">
<div class="row">
<label for="selectedDoorDataParamsStekloCount" class="col-lg-7 col-sm-12 col-form-label offset-lg-1">Стекло, кв.м</label>
<div class="col-lg-4 col-sm-12">
<input tabindex="17" id="selectedDoorDataParamsStekloCount" class="form-control form-control-sm" data-ng-model="calcDoors.selectedDoorData.params.StekloCount" data-floating-number-only data-ng-enter="calcDoors.onFieldEnter('selectedDoorDataParamssverlenieOtverstiyNaDver')">
</div>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-6">
<div class="row">
<label for="selectedDoorDataParamsdsp10mm" class="col-lg-7 col-sm-12 col-form-label">ДСП 10мм лист, лист</label>
<div class="col-lg-4 col-sm-12">
<input tabindex="15" id="selectedDoorDataParamsdsp10mm" class="form-control form-control-sm" data-ng-model="calcDoors.selectedDoorData.params.dsp10mm" data-floating-number-only data-ng-enter="calcDoors.onFieldEnter('selectedDoorDataParamsplenkaBezopasnostiZerkalCount')">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-6">
<div class="form-group row">
<label for="selectedDoorDataParamssverlenieOtverstiyNaDver" class="col-lg-7 col-sm-12 col-form-label twoLinesLabel">Сверление отверстий на одну дверь, 1 дверь</label>
<div class="col-lg-4 col-sm-12">
<input tabindex="18" id="selectedDoorDataParamssverlenieOtverstiyNaDver" class="form-control form-control-sm" data-ng-model="calcDoors.selectedDoorData.params.sverlenieOtverstiyNaDver" data-floating-number-only data-ng-enter="">
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12 calc-results">
<div>
<div data-ng-if="calcDoors.orderNum && (calcDoors.orderNum.length > 0)">Заказ: {{calcDoors.orderNum}}</div>
<div class="form-group"><span class="text-strong-danger">Ориентировочная</span> <span class="no-wrap">стоимость: {{calcDoors.selectedDoorData.order_calc_results.price || 0}} руб.</span></div>
<div data-ng-if="calcDoors.orderNum && (calcDoors.orderNum.length > 0)" class="row">
<div class="col-6">
<div class="form-group row">
<label for="selectedDoorDataActualPrice" class="col-lg-7 col-sm-12 col-form-label">Фактическая стоимость:</label>
<div class="col-lg-4 col-sm-12">
<input tabindex="19" id="selectedDoorDataActualPrice" required class="form-control form-control-sm" data-ng-model="calcDoors.actualPrice" data-floating-number-only>
</div>
</div>
</div>
<div class="col-6" data-ng-if="!calcDoors.isActualPriceCorrect() || !calcDoors.actualPrice">
<label class="col-form-label"><i class="fa fa-warning text-warning"></i> Фактическая стоимость меньше расчетной!</label>
</div>
</div>
<button tabindex="-1" type="button" class="btn btn-sm btn-secondary print-order-button" data-ng-click="calcDoors.printOrder()" data-ng-disabled="!calcDoors.selectedDoorData.params.doorId || calcDoorsConfigForm.$invalid || calcDoors.isSendOrderEmailSubmitting || !calcDoors.isActualPriceCorrect() || calcDoors.warnings.length" data-ng-if="config.additionalActionsAllowed">
<i class="fa fa-print"></i>&nbsp;Печать
</button>
<button tabindex="-1" data-ng-if="!config.savedOrderData && (config.branches.length == 1)" data-ng-disabled="!calcDoors.selectedDoorData.params.doorId || calcDoorsConfigForm.$invalid || calcDoors.isSendOrderEmailSubmitting || !calcDoors.isActualPriceCorrect() || calcDoors.warnings.length" type="button" class="btn btn-sm btn-primary send-order-button" data-ng-click="calcDoors.sendOrderEmailNotify(config.branches[0].id)">Отправить менеджеру</button>
<div class="btn-group send-order-button" data-ng-if="!config.savedOrderData && (config.branches.length > 1)">
<button tabindex="-1" data-ng-disabled="!calcDoors.selectedDoorData.params.doorId || calcDoorsConfigForm.$invalid || calcDoors.isSendOrderEmailSubmitting || !calcDoors.isActualPriceCorrect() || calcDoors.warnings.length" type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Отправить менеджеру <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li data-ng-repeat="branch in config.branches"><a tabindex="-1" data-ng-click="calcDoors.sendOrderEmailNotify(branch.id)" class="dropdown-item">{{branch.label}}</a></li>
</ul>
</div>
<button tabindex="-1" data-ng-if="config.savedOrderData" data-ng-disabled="!calcDoors.selectedDoorData.params.doorId || calcDoorsConfigForm.$invalid || calcDoors.isSendOrderEmailSubmitting || !calcDoors.isActualPriceCorrect() || calcDoors.warnings.length" type="button" class="btn btn-sm btn-primary send-order-button" data-ng-click="calcDoors.sendOrderEmailNotify(false)">Сохранить заказ</button>
<button tabindex="-1" class="btn btn-danger btn-sm clear-button float-right" data-ng-click="calcDoors.confirmCleanup()" data-click-prevent="true">
Очистить
</button>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="modal fade" id="notifyModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<div>{{calcDoors.notifyMessage}}</div>
</div>
<div class="modal-footer">
<button class="btn btn-sm btn-info" data-dismiss="modal" href="#">OK</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="notifySendOrderEmailModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<div data-ng-if="config.savedOrderData" data-ng-bind-html="::utilities.getConfigLabel('notifySaveOrderDataMessage', true) | trustAs"></div>
<div data-ng-if="!config.savedOrderData" data-ng-bind-html="::utilities.getConfigLabel('notifySendOrderEmailMessage') | trustAs"></div>
</div>
<div class="modal-footer">
<button class="btn btn-sm btn-info" data-dismiss="modal" href="#">OK</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="confirmCleanupModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<div class="alert alert-warning" data-ng-bind-html="::utilities.getConfigLabel('confirmCleanupMessage') | trustAs">
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="fullOrderCleanupCheckbox" data-ng-model="calcDoors.fullOrderCleanup">
<label class="form-check-label" for="fullOrderCleanupCheckbox">
Очистить всё
</label>
</div>
</div>
<div class="modal-footer justify-content-between">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Отмена</button>
<button type="button" class="btn btn-primary" data-ng-click="calcDoors.cleanup(true)">Продолжить</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="selectDoorConfigModal" data-backdrop="static">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<span>Выбор производителя и цвета дверей </span>
</h5>
<div class="input-group input-group-sm search-input-group">
<div class="input-group-prepend">
<label class="input-group-text" for="searchFilter" >
<i class="fa fa-search"></i>
</label>
</div>
<input placeholder="Поиск.." id="searchFilter" class="form-control " type="text" data-ng-model="calcDoors.searchFilter">
</div>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-12">
<div class="alert alert-info">
Внимание! Цвет на мониторе может отличаться от реального цвета дверей.
</div>
</div>
</div>
<div class="form-group row justify-content-center">
<label class="col-form-label">Производитель:</label>
<div class="col-4">
<select class="custom-select" data-ng-model="calcDoors.manufacturersSelect" data-ng-options="v.id as v.label for v in config.doorsManufacturers">
<option value="">Все</option>
</select>
</div>
</div>
<div class="row">
<div id="selectDoorId{{door.id}}" class="select-door-data col-4" data-ng-repeat="door in filteredDoors = (config.doors | filterData:calcDoors.searchFilter)" data-ng-show="!calcDoors.manufacturersSelect || (calcDoors.manufacturersSelect == door.manufacturer_id)">
{{currentDoorImage = utilities.getDoorImageById(door.id);""}}
<div data-ng-click="calcDoors.selectDoorId(door)" data-ng-class="{'selected' : (calcDoors.selectDoorConfigModalData.params.doorId == door.id)}">
<div data-ng-if="door.image" class="select-door-image img-thumbnail">
<img data-src="{{currentDoorImage}}" class="img-lazy" >
</div>
<div class="select-door-label">
<span><b>{{door.color}}</b></span>
<span>{{door.profile}} {{utilities.getDoorManufacturer(door.manufacturer_id)}}</span>
</div>
</div>
<a data-ng-if="door.image" tabindex="-1" href="{{currentDoorImage}}" data-lightbox="door{{door.id}}LightBox" class="select-door-preview" data-ng-class="{'no-door-image' : !door.image}">
<img src="/img/lightbox/zoom-in.png">
</a>
</div>
<div data-ng-if="!filteredDoors.length" class="col-12">Не найдено результатов по вашему запросу.</div>
</div>
</div>
<div class="modal-footer justify-content-between">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Закрыть</button>
<button class="btn btn-sm btn-success"
data-ng-disabled="!calcDoors.selectDoorConfigModalData.params.doorId"
data-ng-click="calcDoors.confirmDoorSelection()">
Сохранить
</button>
</div>
</div>
</div>
</div>
</div>
<?php $this->Assign('title', $siteTitle);?>
+934
View File
@@ -0,0 +1,934 @@
<div data-ng-controller="CutController" class="hidden-on-load">
<div class="container apps-container">
<form name="cutConfigForm">
<div class="card">
<div class="card-body">
<div class="sheet-config-row">
<div class="row">
<div class="col-6 form-horizontal">
<div class="form-group no-bottom-margin">
<h3 class="siteTitle">
<span>{{config.common.siteTitle.value}}</span>
<button tabindex="-1" type="button" class="btn btn-sm btn-warning openSelectSheetConfigModal" data-ng-click="cut.openSelectSheetConfigModal()">Выбрать фактуру листа и кромки</button>
</h3>
</div>
<div class="form-group row no-bottom-margin">
{{selectedSheetImage = utilities.getSheetImageById(cut.selectedSheetData.sheetId, 'image');""}}
<label for="selectedSheetDimension" class="col-4 col-form-label">Фактура листа:<a tabindex="-1" data-ng-if="cut.selectedSheetData.sheetId && selectedSheetImage" href="{{selectedSheetImage}}" data-lightbox="sheetIdLightBox"><i class="fa fa-question-circle"></i></a>
</label>
<div class="col-8">
<label class="col-form-label selected-sheet-dimension">
{{cut.getSelectedSheetLabel(cut.selectedSheetData.sheetId)}}
</label>
</div>
</div>
<div class="form-group row no-bottom-margin">
<label for="selectedSheetDimension" class="col-4 col-form-label">Размер листа: </label>
<div class="col-8">
<label class="col-form-label selected-sheet-dimension">{{cut.getSelectedSheetFullDimensions()}}</label>
</div>
</div>
<div class="form-group row no-bottom-margin">
<label class="col-4 col-form-label">Размер раскроя:<a class="popover-button cut-size-desc" tabindex="-1" role="button" data-ng-show="cut.selectedSheetData.sheetId" data-container="body" data-toggle="popover" data-trigger="focus" data-placement="right" data-content="{{cut.faceDescriptionWithPrice | trustAs}}" data-html="true"><i class="fa fa-question-circle"></i></a> </label>
<div class="col-8">
<label class="col-form-label selected-sheet-dimension">{{cut.getSelectedSheetDimensions()}}</label>
</div>
</div>
</div>
<div class="col-6">
<div class="row logo-row text-left">
<div class="col-5">
<a tabindex="-1" href="/services" title="{{::config.primarySiteTitle}}" target="_blank">
<img src="/img/logo.png" alt="{{::config.primarySiteTitle}}">
</a>
</div>
<div class="col-7">
<h5 class="addressDelivery">
<span>Наш адрес:</span>
<span class="addressDelivery" data-ng-bind-html="::utilities.getConfigLabel('addressDelivery') | trustAs"></span>
<span class="addressDelivery" data-ng-bind-html="::utilities.getConfigLabel('addressDeliveryAdd') | trustAs"></span>
</h5>
</div>
</div>
<h5 class="detailWithBordersWarning text-danger">{{::utilities.getConfigLabel('detailWithBordersWarning')}}</h5>
<div class="headerInfoLabel">{{::utilities.getConfigLabel('headerInfoLabel') | trustAs}}</div>
</div>
</div>
<div class="row form-horizontal checkbox-group no-bottom-margin">
<div class="col-6">
<div class="form-group row no-bottom-margin">
<label class="col-8 col-form-label" data-ng-click="cut.mergeSimilarDetailsToggle()">Объединение одинаковых деталей: </label>
<div class="col-4" id="mergeSimilarDetails">
<i class="fa fa-square-o" data-ng-if="!cut.mergeSimilarDetails" data-ng-click="cut.mergeSimilarDetailsToggle()"></i>
<i class="fa fa-check-square-o" data-ng-if="cut.mergeSimilarDetails" data-ng-click="cut.mergeSimilarDetailsToggle()"></i>
</div>
</div>
</div>
<div class="col-6">
<div class="form-group row no-bottom-margin">
<label class="col-6 col-form-label" data-ng-click="cut.packCuttingsToOrderToggle()">Вкладывать остатки в заказ: </label>
<div class="col-4">
<i class="fa fa-square-o" data-ng-if="!cut.packCuttingsToOrder" data-ng-click="cut.packCuttingsToOrderToggle()"></i>
<i class="fa fa-check-square-o" data-ng-if="cut.packCuttingsToOrder" data-ng-click="cut.packCuttingsToOrderToggle()"></i>
</div>
</div>
</div>
</div>
<div class="row form-horizontal">
<div class="col-6">
<div class="form-group row no-bottom-margin">
{{selectedBorder1Image = utilities.getBorderImageById(cut.selectedSheetData.border1Id);""}}
<label class="col-3 col-form-label">
Кромка №1:<a tabindex="-1" class="preview-button" data-ng-if="cut.selectedSheetData.border1Id && selectedBorder1Image" href="{{selectedBorder1Image}}" data-lightbox="border1IdLightBox"><i class="fa fa-question-circle"></i></a>
</label>
<div class="col-9">
<label class="col-form-label selected-sheet-dimension">
<span data-ng-bind-html="cut.getBorderLabel(cut.selectedSheetData.border1Id, cut.selectedSheetData.border1Height) | trustAs"></span>
</label>
</div>
</div>
</div>
<div class="col-6">
<div class="form-group row no-bottom-margin">
{{selectedBorder2Image = utilities.getBorderImageById(cut.selectedSheetData.border2Id);""}}
<label class="col-3 col-form-label">
Кромка №2:<a tabindex="-1" class="preview-button" data-ng-if="cut.selectedSheetData.border2Id && selectedBorder2Image" href="{{utilities.getBorderImageById(cut.selectedSheetData.border2Id)}}" data-lightbox="border2IdLightBox"><i class="fa fa-question-circle"></i></a>
</label>
<div class="col-9">
<label class="col-form-label selected-sheet-dimension">
<span data-ng-bind-html="cut.getBorderLabel(cut.selectedSheetData.border2Id, cut.selectedSheetData.border2Height) | trustAs"></span>
</label>
</div>
</div>
</div>
</div>
<div class="row form-horizontal">
<div class="col-6">
<div class="form-group row margin-top-3 no-bottom-margin">
<label class="col-3 col-form-label" for="orderName">ФИО: <a class="popover-button" tabindex="-1" role="button" data-container="body" data-toggle="popover" data-trigger="focus" data-placement="right" data-content="{{utilities.getConfigLabel('fioIsRequired', true) | trustAs}}" data-html="true"><i class="fa fa-question-circle"></i></a></label>
<div class="col-9" data-ng-class="cutConfigForm.orderName.$invalid && cutConfigForm.orderName.$dirty ? 'has-error' : ''">
<input type="text" class="form-control form-control-sm" name="orderName" id="orderName" data-ng-model="cut.orderName" required data-ng-enter="cut.onFieldEnter('orderPhone')">
</div>
</div>
<div class="form-group row margin-top-3 no-bottom-margin">
<label class="col-3 col-form-label" for="orderPhone">Телефон: <a class="popover-button" tabindex="-1" role="button" data-container="body" data-toggle="popover" data-trigger="focus" data-placement="right" data-content="{{utilities.getConfigLabel('phoneIsRequired', true) | trustAs}}" data-html="true"><i class="fa fa-question-circle"></i></a></label>
<div class="col-9" data-ng-class="cutConfigForm.orderPhone.$invalid && cutConfigForm.orderPhone.$dirty ? 'has-error' : ''">
<input type="text" class="form-control form-control-sm" name="orderPhone" id="orderPhone" data-ng-model="cut.orderPhone" data-model-view-value="true" data-digits-only placeholder=" " required data-ng-enter="cut.onFieldEnter('orderEmail')">
</div>
</div>
<div class="form-group row margin-top-3 no-bottom-margin">
<label class="col-3 col-form-label" for="orderEmail">Email: <a class="popover-button" tabindex="-1" role="button" data-container="body" data-toggle="popover" data-trigger="focus" data-placement="right" data-content="{{utilities.getConfigLabel('emailIsRequired', true) | trustAs}}" data-html="true"><i class="fa fa-question-circle"></i></a></label>
<div class="col-9" data-ng-class="cutConfigForm.orderEmail.$invalid && cutConfigForm.orderEmail.$dirty ? 'has-error' : ''">
<input type="email" class="form-control form-control-sm" name="orderEmail" id="orderEmail" data-ng-model="cut.orderEmail" required data-ng-enter="cut.onFieldEnter('orderAddress')">
</div>
</div>
<div class="form-group row margin-top-3 no-bottom-margin">
<label class="col-3 col-form-label" for="orderAddress">Адрес доставки: </label>
<div class="col-9" data-ng-class="cutConfigForm.orderAddress.$invalid && cutConfigForm.orderAddress.$dirty ? 'has-error' : ''">
<input type="text" class="form-control form-control-sm" name="orderAddress" id="orderAddress" data-ng-model="cut.orderAddress" data-ng-enter="cut.onFieldEnter('orderComment')">
</div>
</div>
</div>
<div class="col-6">
<div class="form-group row margin-top-3 no-bottom-margin">
<div class="col-12">
<textarea class="form-control form-control-sm margin-top-3" name="orderComment" id="orderComment" data-ng-model="cut.orderComment" placeholder="Комментарий к заказу"></textarea>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card details-config">
<div class="card-body">
<div class="row">
<div class="col-4 cut-results">
<div class="print-left">
<div data-ng-if="cut.orderNum && (cut.orderNum.length > 0)">Заказ: {{cut.orderNum}}</div>
<div>Листов в раскрое: {{cut.sheets.length}}</div>
{{selectedSheetsInStock = utilities.getSelectedSheetsInStock(cut.selectedSheetData.sheetId);""}}
<div data-ng-if="(selectedSheetsInStock != null) && (selectedSheetsInStock < cut.sheets.length)" class="text-warning">Листов на складе: {{selectedSheetsInStock}}</div>
<div>Всего деталей: {{cut.getDetailsCount()}}</div>
<div>Площадь деталей: {{(cut.getDetailsArea()/1000000).toFixed(2).replace(".00", "")}}м<sup>2</sup></div>
<div>Длина пропила: {{cut.notchCount}}м.</div>
<div data-ng-if="cut.grooveCount.length > 0">Длина пропила пазов: {{cut.grooveCount}}м.</div>
<div data-ng-if="cut.selectedSheetData.border1Id || cut.selectedSheetData.border2Id">Длина кромок:
<ul>
<li data-ng-repeat="borderId in [1, 2]" data-ng-if="cut.selectedSheetData['border'+borderId+'Id'] > 0">
кромка №{{borderId}} - {{((cut.selectedSheetData["border" + borderId + "Count"] ? cut.selectedSheetData["border" + borderId + "Count"] : 0)/1000).toFixed(2).replace(".00", "")}}м.
</li>
</ul>
</div>
<div><span class="text-strong-danger">Ориентировочная</span> <span class="no-wrap">стоимость: {{cut.price}} руб.</span></div>
<br/>
<div class="order-buttons">
<div class="" data-ng-if="config.createdForManagerId">
Если вы знаете менеджера,<br/> выберите его в поле ниже<br/>
<div class="btn-group order-available-managers">
<button tabindex="-1" data-ng-disabled="!cut.selectedSheetData.sheetId || cutConfigForm.$invalid || cut.isSendOrderEmailSubmitting" type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ utilities.getAvailableToOrderManagerNameById(cut.createdForManagerId) }} <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li data-ng-repeat="manager in config.availableToOrderManagers">
<a tabindex="-1" data-ng-click="cut.setCreatedForManagerId(manager.id)" class="dropdown-item">{{manager.name}}</a>
</li>
</ul>
</div>
</div>
<br/>
<div class="btn-group print-order-button" data-ng-if="config.additionalActionsAllowed">
<button tabindex="-1" data-ng-disabled="!cut.selectedSheetData.sheetId || cutConfigForm.$invalid || cut.isSendOrderEmailSubmitting" type="button" class="btn btn-sm btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-print"></i>&nbsp;Печать <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a tabindex="-1" data-ng-click="cut.printOrder('Spec')" class="dropdown-item">Печать спецификации</a></li>
<li><a tabindex="-1" data-ng-click="cut.printOrder('Cut')" class="dropdown-item">Печать карт раскроя</a></li>
</ul>
</div>
<button tabindex="-1" data-ng-if="!config.savedOrderData && (config.branches.length == 1)" data-ng-disabled="!cut.selectedSheetData.sheetId || cutConfigForm.$invalid || cut.isSendOrderEmailSubmitting" type="button" class="btn btn-sm btn-primary send-order-button" data-ng-click="cut.sendOrderEmailNotify(config.branches[0].id)">Отправить менеджеру</button>
<div class="btn-group send-order-button" data-ng-if="!config.savedOrderData && (config.branches.length > 1)">
<button tabindex="-1" data-ng-disabled="!cut.selectedSheetData.sheetId || cutConfigForm.$invalid || cut.isSendOrderEmailSubmitting" type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Отправить менеджеру <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li data-ng-repeat="branch in config.branches"><a tabindex="-1" data-ng-click="cut.sendOrderEmailNotify(branch.id)" class="dropdown-item">{{branch.label}}</a></li>
</ul>
</div>
<button tabindex="-1" data-ng-if="config.savedOrderData" data-ng-disabled="!cut.selectedSheetData.sheetId || cutConfigForm.$invalid || cut.isSendOrderEmailSubmitting" type="button" class="btn btn-sm btn-primary send-order-button" data-ng-click="cut.sendOrderEmailNotify(false)">Сохранить заказ</button>
</div>
</div>
</div>
<div class="col-8 details-list">
<h5 class="justify-content-between">
<span>Параметры деталей, мм</span>
</h5>
<div>
<table class="table table-striped">
<tbody>
<tr data-ng-repeat="detail in cut.details">
<td colspan="7">
<div>
<table class="table table-details-list table-no-borders">
<thead>
<tr>
<th class="detail-number-label">№</th>
<th>Длина</th>
<th>Ширина</th>
<th>Кол-во, шт</th>
<th class="detail-borders"><span data-ng-if="cut.selectedSheetData.border1Id > 0">Кромка</span></th>
<th class="detailCorners detailGrooves text-center">Доп.</th>
<th class=""></th>
</tr>
</thead>
<tbody>
<tr>
<td class="detail-number-label">
<span>{{$index+1}}</span>
&nbsp;
<a tabindex="-1" href="#" data-ng-click="cut.openDetailLabelConfigModal(detail.id)" data-click-prevent="true" title="Наименование детали">
<i class="fa fa-pencil" data-ng-class="{'text-success' : detail.label && (detail.label.length > 0) }"></i>
</a>
</td>
<td>
<input id="detail{{detail.id}}width" class="form-control form-control-sm" type="text" data-ng-model="detail.width" data-digits-only data-ng-enter="cut.onDetailEnter(detail, 'width')" pattern="[0-9]*">
</td>
<td>
<input id="detail{{detail.id}}height" class="form-control form-control-sm" type="text" data-ng-model="detail.height" data-digits-only data-ng-enter="cut.onDetailEnter(detail, 'height')" pattern="[0-9]*">
</td>
<td>
<input id="detail{{detail.id}}count" class="form-control form-control-sm" type="text" data-ng-model="detail.count" data-digits-only data-ng-enter="cut.onDetailEnter(detail, 'count')" pattern="[0-9]*">
</td>
<td class="detail-borders">
<span class="detail-color-box" style="background-color: {{detail.color}}" data-ng-if="!cut.selectedSheetData.border1Id"></span>
<div data-ng-if="cut.selectedSheetData.border1Id > 0" data-ng-class="{'single-border-config' : !cut.selectedSheetData.border2Id}">
<div class="left-detail-border" >
<div class="detail-border detail-border-{{borderId}} {{cut.detailsBorders[detail.id].left == borderId ? 'active' : ''}}" data-ng-repeat="borderId in [1, 2]" data-ng-click="cut.toggleDetailBorder(detail, 'left', borderId)" title="{{cut.detailsBorders[detail.id].left == borderId ? 'Удалить кромку' : 'Добавить кромку'}}" data-ng-if="cut.selectedSheetData['border'+borderId+'Id'] > 0">
{{borderId}}
</div>
</div>
<div class="detail-border-center">
<div class="top-detail-border">
<div class="detail-border detail-border-{{borderId}} {{cut.detailsBorders[detail.id].top == borderId ? 'active' : ''}}" data-ng-repeat="borderId in [1, 2]" data-ng-click="cut.toggleDetailBorder(detail, 'top', borderId)" title="{{cut.detailsBorders[detail.id].top == borderId ? 'Удалить кромку' : 'Добавить кромку'}}" data-ng-if="cut.selectedSheetData['border'+borderId+'Id'] > 0">
{{borderId}}
</div>
</div>
<div class="detail-borders {{cut.getDetailBordersClass(detail)}}" style="background-color: {{detail.color}}; border-color: {{detail.color}};"></div>
<div class="bottom-detail-border">
<div class="detail-border detail-border-{{borderId}} {{cut.detailsBorders[detail.id].bottom == borderId ? 'active' : ''}}" data-ng-repeat="borderId in [1, 2]" data-ng-click="cut.toggleDetailBorder(detail, 'bottom', borderId)" title="{{cut.detailsBorders[detail.id].bottom == borderId ? 'Удалить кромку' : 'Добавить кромку'}}" data-ng-if="cut.selectedSheetData['border'+borderId+'Id'] > 0">
{{borderId}}
</div>
</div>
</div>
<div class="right-detail-border">
<div class="detail-border detail-border-{{borderId}} {{cut.detailsBorders[detail.id].right == borderId ? 'active' : ''}}" data-ng-repeat="borderId in [1, 2]" data-ng-click="cut.toggleDetailBorder(detail, 'right', borderId)" title="{{cut.detailsBorders[detail.id].right == borderId ? 'Удалить кромку' : 'Добавить кромку'}}" data-ng-if="cut.selectedSheetData['border'+borderId+'Id'] > 0">
{{borderId}}
</div>
</div>
</div>
</td>
<td class="config-detailCorners-button">
<a tabindex="-1" href="#" data-ng-click="cut.openDetailCornersConfigModal(detail, $index+1)" data-click-prevent="true" title="Скругление/срез/паз" data-ng-disabled="!cut.isDetailDimensionsFilled(detail)">
<img src="/img/corners.png" alt="{{::config.siteTitle}}">
</a>
</td>
<td class="remove-detail-button">
<a tabindex="-1" href="#" class="text-danger remove-detail" data-ng-if="cut.details.length > 1" data-ng-click="cut.removeDetail(detail)" data-click-prevent="true">x</a>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<div data-ng-bind-html="cut.getDetailCornersText(detail) | trustAs"></div>
</div>
<div>
<div data-ng-bind-html="cut.getDetailGroovesText(detail) | trustAs"></div>
</div>
</td>
</tr>
</tbody>
</table>
<div class="d-flex justify-content-between">
<button tabindex="-1" class="btn btn-sm btn-success" data-ng-click="cut.addDetail()" data-click-prevent="true">
Добавить деталь
</button>
<button tabindex="-1" class="btn btn-danger btn-sm clear-button" data-ng-click="cut.confirmCleanup()" data-click-prevent="true">
Очистить
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
<div class="sheets-wrap" data-ng-if="(cut.selectedSheetData.sheetId) && cut.selectedSheetDimension">
<div class="card" data-ng-repeat="sheet in cut.sheets">
<div class="card-body">
<div class="sheet" data-ng-class="cut.sheetClass" data-ng-style="cut.sheetStyle">
<div class="info sheet-title">Лист {{sheet.id}}</div>
<div class="info sheet-width">
<span class="sheet-width-left">|&lsaquo;</span>
<span class="sheet-width-right">&rsaquo;|</span>
{{cut.selectedSheetDimension.width}} мм
</div>
<div class="info sheet-height">
<span class="sheet-height-top">&rsaquo;|</span>
<span class="sheet-height-bottom">|&lsaquo;</span>
<span data-ng-style="cut.sheetHeightStyle">{{cut.selectedSheetDimension.height}} мм</span>
</div>
<div data-ng-repeat="detail in sheet.details" data-ng-style="cut.getDetailStyle(detail)" class="detail detail-with-entire-borders" data-ng-class="cut.getDetailClass(detail)" data-ng-click="cut.rotateDetail(detail)">
<div class="detail-height">{{detail.height}}</div>
<div class="detail-width">{{detail.width}}</div>
<div class=""></div>
<a class="detail-label-edit" tabindex="-1" href="#" data-ng-click="cut.openDetailLabelConfigModal(detail.detailId)" data-click-prevent="true" title="{{cut.getDetailLabel(detail.detailId)}}" data-ng-if="cut.getDetailLabel(detail.detailId).length > 0">
<i class="fa fa-edit"></i>
</a>
<div data-ng-repeat="borderPosition in config.borderPositions" class="entire-border {{borderPosition}}-border" data-ng-if="cut.detailsBorders[detail.detailId][borderPosition]"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="notifyModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<div>{{cut.notifyMessage}}</div>
</div>
<div class="modal-footer">
<button class="btn btn-sm btn-info" data-dismiss="modal" href="#">OK</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="notifySendOrderEmailModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<div data-ng-if="config.savedOrderData" data-ng-bind-html="::utilities.getConfigLabel('notifySaveOrderDataMessage', true) | trustAs"></div>
<div data-ng-if="!config.savedOrderData" data-ng-bind-html="::utilities.getConfigLabel('notifySendOrderEmailMessage') | trustAs"></div>
</div>
<div class="modal-footer">
<button class="btn btn-sm btn-info" data-dismiss="modal" href="#">OK</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="confirmCleanupModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<div class="alert alert-warning" data-ng-bind-html="::utilities.getConfigLabel('confirmCleanupMessage') | trustAs">
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="fullOrderCleanupCheckbox" data-ng-model="cut.fullOrderCleanup">
<label class="form-check-label" for="fullOrderCleanupCheckbox">
Очистить всё
</label>
</div>
</div>
<div class="modal-footer justify-content-between">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Отмена</button>
<button type="button" class="btn btn-primary" data-ng-click="cut.cleanup(true)">Продолжить</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="selectSheetConfigModal" data-backdrop="static">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<span data-ng-show="cut.activeSheetSelectStep == 1">Шаг 1/4: Выбор фактуры и размера листа</span>
<span data-ng-show="cut.activeSheetSelectStep == 2">Шаг 2/4: Выбор цвета и толщины кромки №1</span>
<span data-ng-show="cut.activeSheetSelectStep == 3">Шаг 3/4: Выбор цвета и толщины кромки №2</span>
<span data-ng-show="cut.activeSheetSelectStep == 4">Шаг 4/4: Подтверждение выбранной конфигурации</span>
</h5>
<div class="input-group input-group-sm search-input-group" data-ng-show="(cut.activeSheetSelectStep != 4)">
<div class="input-group-prepend">
<label class="input-group-text" for="searchFilter{{cut.activeSheetSelectStep}}" >
<i class="fa fa-search"></i>
</label>
</div>
<input placeholder="Поиск.." id="searchFilter{{cut.activeSheetSelectStep}}" class="form-control " type="text" data-ng-model="cut.searchFilters[cut.activeSheetSelectStep]">
</div>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div data-ng-show="cut.activeSheetSelectStep == 1">
<div class="row">
<div class="col-12">
<div class="alert alert-info">
Внимание! Цвет на мониторе может отличаться от реального цвета ДСП.
</div>
</div>
</div>
<div class="row">
<div id="selectSheetId{{sheet.id}}" class="select-sheet-data col-6 col-xl-4 col-lg-4 col-md-4 col-sm-6" data-ng-repeat="sheet in filteredSheets = (config.sheets | filterData:cut.searchFilters[1])" data-ng-if="sheet.is_active || config.savedOrderData && config.savedOrderData.sheet_data && (config.savedOrderData.sheet_data.sheetId == sheet.id)">
<div data-ng-click="cut.selectSheetId(sheet)" data-ng-class="{'selected' : (cut.selectSheetConfigModalData.sheetId == sheet.id)}">
<div class="select-sheet-image img-thumbnail" data-ng-if="cut.isSelectSheetConfigModalTriggered && sheet.thumb">
<img data-ng-src="{{utilities.getSheetImageById(sheet.id, 'thumb')}}" class="img-lazy">
</div>
<div class="select-sheet-label">
<span><b>{{sheet.label}}</b></span>
<span>Размеры:</> {{cut.getSheetFullDimensions(sheet.sheet_dimension_id)}}</span>
<span>Цена: {{sheet.price}} руб. <span data-ng-if="sheet.sheetIdFor1C && (sheet.quantityIn1C != null)" class="sheet-1c-quantity" data-ng-class="{'text-danger': (sheet.quantityIn1C < 1)}">Ост. {{sheet.quantityIn1C > 0 ? sheet.quantityIn1C : 0}}л</span></span>
</div>
</div>
<a tabindex="-1" href="{{utilities.getSheetImageById(sheet.id, 'image')}}" data-lightbox="sheet{{sheet.id}}LightBox" class="select-sheet-preview" data-ng-if="sheet.image" data-ng-class="{'no-sheet-thumb' : !sheet.thumb}">
<img src="/img/lightbox/zoom-in.png">
</a>
</div>
<div data-ng-if="!filteredSheets.length" class="col-12">Не найдено результатов по вашему запросу.</div>
</div>
</div>
<div data-ng-show="cut.activeSheetSelectStep == 2">
<div class="row">
<div class="col-12">
<div class="alert alert-info">
Внимание! Цвет на мониторе может отличаться от реального цвета ДСП. <br/>
Выберите цвет и размер кромки №1 или нажмите "Пропустить", если в поклейке кромок нет необходимости.
</div>
</div>
</div>
<div class="row">
<div id="selectBorder1Id{{border.id}}" class="select-border-data col-12" data-ng-repeat="border in filteredBorders1 = (config.borders | filterData:cut.searchFilters[2])">
<div data-ng-click="cut.selectBorderId(1, border)" data-ng-class="{'selected' : (cut.selectSheetConfigModalData.border1Id == border.id)}">
<div class="row">
<div class="col-8">
<div class="select-border-image" data-ng-if="cut.isSelectSheetConfigModalTriggered && border.image">
<img data-src="{{utilities.getBorderImageById(border.id)}}" class="img-thumbnail img-lazy">
</div>
<div class="">
<span><b>{{border.label}} {{cut.checkIfBorderInSheetColor(cut.selectSheetConfigModalData.sheetId, border.id)}}</b></span>
</div>
</div>
<div class="col-4">
<div class="form-group" data-ng-if="cut.selectSheetConfigModalData.border1Id == border.id">
<label for="border1HeightSelect">Толщина кромки:</label>
<select class="custom-select" id="border1HeightSelect" data-ng-model="cut.selectSheetConfigModalData.border1Height" data-ng-options="v.cut_border_dimension.id as v.cut_border_dimension.label for v in border.available_dimensions" data-ng-disabled="!cut.selectSheetConfigModalData.border1Id">
<option value="">Выберите толщину</option>
</select>
</div>
</div>
</div>
</div>
<a tabindex="-1" href="{{utilities.getBorderImageById(border.id)}}" data-lightbox="border1{{border.id}}LightBox" class="select-border-preview" data-ng-if="border.image">
<img src="/img/lightbox/zoom-in.png">
</a>
</div>
<div data-ng-if="!filteredBorders1.length" class="col-12">Не найдено результатов по вашему запросу.</div>
</div>
</div>
<div data-ng-show="cut.activeSheetSelectStep == 3">
<div class="row">
<div class="col-12">
<div class="alert alert-info">
Внимание! Цвет на мониторе может отличаться от реального цвета ДСП. <br/>
Выберите цвет и размер кромки №2 или нажмите "Пропустить", если в поклейке кромки №2 нет необходимости.
</div>
</div>
</div>
<div class="row">
<div id="selectBorder2Id{{border.id}}" class="select-border-data col-12" data-ng-repeat="border in filteredBorders2 = (config.borders | filterData:cut.searchFilters[3])">
<div data-ng-click="cut.selectBorderId(2, border)" data-ng-class="{'selected' : (cut.selectSheetConfigModalData.border2Id == border.id)}">
<div class="row">
<div class="col-8">
<div class="select-border-image" data-ng-if="cut.isSelectSheetConfigModalTriggered && border.image">
<img data-src="{{utilities.getBorderImageById(border.id)}}" class="img-thumbnail img-lazy">
</div>
<div class="">
<span><b>{{border.label}} {{cut.checkIfBorderInSheetColor(cut.selectSheetConfigModalData.sheetId, border.id)}}</b></span>
</div>
</div>
<div class="col-4">
<div class="form-group" data-ng-if="cut.selectSheetConfigModalData.border2Id == border.id">
<label for="border2HeightSelect">Толщина кромки:</label>
<select class="custom-select" id="border2HeightSelect" data-ng-model="cut.selectSheetConfigModalData.border2Height" data-ng-options="v.cut_border_dimension.id as v.cut_border_dimension.label for v in border.available_dimensions" data-ng-disabled="!cut.selectSheetConfigModalData.border2Id">
<option value="">Выберите толщину</option>
</select>
</div>
</div>
</div>
</div>
<a tabindex="-1" href="{{utilities.getBorderImageById(border.id)}}" data-lightbox="border2{{border.id}}LightBox" class="select-border-preview" data-ng-if="border.image">
<img src="/img/lightbox/zoom-in.png">
</a>
</div>
<div data-ng-if="!filteredBorders2.length" class="col-12">Не найдено результатов по вашему запросу.</div>
</div>
</div>
<div data-ng-show="cut.activeSheetSelectStep == 4">
<div class="row">
<div class="col-12">
<div class="alert alert-info">
Внимание! Цвет на мониторе может отличаться от реального цвета ДСП. <br/>
Проверьте выбранную фактуру листа и конфигурацию кромок и нажмите "Сохранить".
</div>
</div>
</div>
<div class="row">
<label class="col-form-label col-3">Фактура:</label>
<div class="col-9">
<label class="col-form-label">{{cut.getSelectedSheetLabel(cut.selectSheetConfigModalData.sheetId)}}</label>
</div>
</div>
<div class="row">
<label class="col-form-label col-3">Размеры листа:</label>
<div class="col-9">
<label class="col-form-label">{{cut.getSheetFullDimensionsBySheetId(cut.selectSheetConfigModalData.sheetId)}}</label>
</div>
</div>
<div class="row">
<label class="col-form-label col-3">Кромка 1:</label>
<div class="col-9">
<label class="col-form-label">
<span data-ng-bind-html="cut.getBorderLabel(cut.selectSheetConfigModalData.border1Id, cut.selectSheetConfigModalData.border1Height) | trustAs"></span>
</label>
</div>
</div>
<div class="row">
<label class="col-form-label col-3">Кромка 2:</label>
<div class="col-9">
<label class="col-form-label">
<span data-ng-bind-html="cut.getBorderLabel(cut.selectSheetConfigModalData.border2Id, cut.selectSheetConfigModalData.border2Height) | trustAs"></span>
</label>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-sm btn-secondary"
data-ng-if="cut.activeSheetSelectStep == 1"
data-ng-disabled="!cut.selectSheetConfigModalData.sheetId"
data-ng-click="cut.discardBordersSelectFromStep1()">
Пропустить выбор кромок
</button>
<button class="btn btn-sm btn-success"
data-ng-if="cut.activeSheetSelectStep == 1"
data-ng-disabled="!cut.selectSheetConfigModalData.sheetId"
data-ng-click="cut.setActiveSheetSelectStep(2)">
Перейти к выбору кромок
</button>
<button class="btn btn-sm btn-secondary"
data-ng-if="cut.activeSheetSelectStep == 2"
data-ng-click="cut.setActiveSheetSelectStep(1)">
Вернуться к выбору фактуры
</button>
<button class="btn btn-sm btn-warning"
data-ng-if="cut.activeSheetSelectStep == 2"
data-ng-click="cut.discardBordersSelectFromStep2()">
Пропустить
</button>
<button class="btn btn-sm btn-success"
data-ng-if="cut.activeSheetSelectStep == 2"
data-ng-disabled="!cut.selectSheetConfigModalData.border1Id || !cut.selectSheetConfigModalData.border1Height"
data-ng-click="cut.setActiveSheetSelectStep(3)">
Перейти к выбору кромки №2
</button>
<button class="btn btn-sm btn-secondary"
data-ng-if="cut.activeSheetSelectStep == 3"
data-ng-click="cut.setActiveSheetSelectStep(2)">
Вернуться к выбору кромки №1
</button>
<button class="btn btn-sm btn-warning"
data-ng-if="cut.activeSheetSelectStep == 3"
data-ng-click="cut.discardBorder2SelectFromStep3()">
Пропустить
</button>
<button class="btn btn-sm btn-success"
data-ng-if="cut.activeSheetSelectStep == 3"
data-ng-disabled="!cut.selectSheetConfigModalData.border2Id || !cut.selectSheetConfigModalData.border2Height"
data-ng-click="cut.setActiveSheetSelectStep(4)">
Завершить настройку
</button>
<button class="btn btn-sm btn-secondary"
data-ng-if="cut.activeSheetSelectStep == 4"
data-ng-click="cut.setActiveSheetSelectStep(1)">
Вернуться к выбору фактуры
</button>
<button class="btn btn-sm btn-success"
data-ng-if="cut.activeSheetSelectStep == 4"
data-ng-disabled="!cut.selectSheetConfigModalData.sheetId"
data-ng-click="cut.confirmSheetSelection()">
Сохранить
</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="detailCornersConfigModal" data-backdrop="static">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
Конфигурация детали №{{cut.detailCornersConfigModalData.uiOrderNum}}
</h5>
<div>
<button type="button" class="btn"
data-ng-class="{true:'btn-info', false:'btn-light'}[cut.detailCornersConfigModalData.activeTab == cornersConfigButton.id]"
data-ng-repeat="cornersConfigButton in config.cornersConfigButtons"
data-ng-click="cut.setDetailCornersConfigActiveTab(cornersConfigButton.id)">
{{cornersConfigButton.label}}
</button>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
</div>
<div class="modal-body">
<div class="row" data-ng-if="cut.detailCornersConfigModalData.activeTab == 'corners'">
<div class="detailCornersCenter">
<div class="detailCornersBox" data-ng-class="cut.getDetailCornersConfigClass()">
<div class="detailCornersHorizontalDivider"></div>
<div class="detailCornersVerticalDivider"></div>
<div data-ng-repeat="cornerPosition in config.cornerPositions" class="cornerConfigRadiusLabel cornerConfigRadiusLabel-{{cornerPosition.id}}">
<span>R={{cut.detailCornersConfigModalData.corners[cornerPosition.id].radius}}</span>
</div>
<div data-ng-repeat="cornerPosition in config.cornerPositions" class="cornerConfigCutLabel cornerConfigCutLabel-{{cornerPosition.id}}">
<span class="cornerConfigCutXLabel">x={{cut.detailCornersConfigModalData.corners[cornerPosition.id].x}}</span>
<span class="cornerConfigCutYLabel">y={{cut.detailCornersConfigModalData.corners[cornerPosition.id].y}}</span>
</div>
<div data-ng-repeat="cornerPosition in config.cornerPositions" class="cornerConfigCutLabel cornerConfigCutEdge-{{cornerPosition.id}}"></div>
</div>
<div class="detailCornersWidth">{{cut.detailCornersConfigModalData.detail.width}} мм</div>
<div class="detailCornersHeight">{{cut.detailCornersConfigModalData.detail.height}} мм</div>
</div>
<div data-ng-repeat="cornerPosition in config.cornerPositions" class="detailCorner detailCorner-{{cornerPosition.id}}">
<label>{{cornerPosition.label}}</label>
<div class="">
<button type="button" class="btn"
data-ng-class="{true:'active btn-primary', false:'btn-light'}[cut.detailCornersConfigModalData.corners[cornerPosition.id].value == cornerValue.id]"
data-ng-repeat="cornerValue in config.cornerValues"
data-ng-disabled="cut.isDetailCornerConfigValueSelectDisabled(cornerPosition.id)"
data-ng-click="cut.setDetailCornerConfigValue(cornerPosition.id, cornerValue.id)">
{{cornerValue.label}}
</button>
</div>
<div class="detailCorner-config col-12">
<div class="row" data-ng-if="cut.detailCornersConfigModalData.corners[cornerPosition.id].value == 'none'"></div>
<div class="row" data-ng-if="cut.detailCornersConfigModalData.corners[cornerPosition.id].value == 'radius'">
<div class="input-group col-sm-12 col-lg-9">
<div class="input-group-prepend">
<label class="input-group-text" for="detailCornersConfigModalData{{cornerPosition.id}}Radius">Радиус, мм</label>
</div>
<input id="detailCornersConfigModalData{{cornerPosition.id}}Radius"
class="form-control form-control-sm" type="number"
data-ng-model="cut.detailCornersConfigModalData.corners[cornerPosition.id].radius"
data-digits-only pattern="[0-9]*">
</div>
</div>
<div class="row" data-ng-if="cut.detailCornersConfigModalData.corners[cornerPosition.id].value == 'cut'">
<div class="input-group col-sm-12 col-lg-6 corners-config-left-group">
<div class="input-group-prepend">
<label class="input-group-text" for="detailCornersConfigModalData{{cornerPosition.id}}X">x, мм</label>
</div>
<input id="detailCornersConfigModalData{{cornerPosition.id}}X"
class="form-control form-control-sm" type="number"
data-ng-model="cut.detailCornersConfigModalData.corners[cornerPosition.id].x"
data-ng-disabled="cut.isDetailCornerConfigInputDisabled(cornerPosition.id)"
data-digits-only pattern="[0-9]*">
</div>
<div class="input-group col-sm-12 col-lg-6 corners-config-right-group">
<div class="input-group-prepend">
<label class="input-group-text" for="detailCornersConfigModalData{{cornerPosition.id}}Y">y, мм</label>
</div>
<input id="detailCornersConfigModalData{{cornerPosition.id}}X" class="form-control form-control-sm" type="number"
data-ng-model="cut.detailCornersConfigModalData.corners[cornerPosition.id].y"
data-ng-disabled="cut.isDetailCornerConfigInputDisabled(cornerPosition.id)"
data-digits-only pattern="[0-9]*" >
</div>
<div class="input-group col-sm-12 col-lg-12"
data-ng-class="{'disabled': cut.isDetailCornerConfigDiagonalCheckboxDisabled(cornerPosition.id)}"
data-ng-click="cut.onDetailCornersConfigDiagonalCutClick(cornerPosition.id)">
<div class="input-group-prepend">
<div class="input-group-text with-checkbox">
<i class="fa fa-square-o" data-ng-if="!cut.detailCornersConfigModalData.corners[cornerPosition.id].diagonalCut"></i>
<i class="fa fa-check-square-o" data-ng-if="cut.detailCornersConfigModalData.corners[cornerPosition.id].diagonalCut"></i>
</div>
</div>
<label class="col-form-label form-control">Диагональный срез</label>
</div>
</div>
</div>
</div>
</div>
<div class="row" data-ng-if="cut.detailCornersConfigModalData.activeTab == 'grooves'" data-ng-class="cut.getDetailGroovesConfigClass()">
<div class="detailGroovesCenter">
<div class="detailGroovesBox" data-ng-class="cut.getDetailGroovesConfigClass()">
<div data-ng-repeat="groovePosition in config.groovePositions" class="grooveConfigLabel grooveConfigLabel-{{groovePosition.id}}">
</div>
</div>
<div class="detailGroovesWidth">{{cut.detailCornersConfigModalData.detail.width}} мм</div>
<div class="detailGroovesHeight">{{cut.detailCornersConfigModalData.detail.height}} мм</div>
</div>
<div data-ng-repeat="groovePosition in config.groovePositions" class="detailGroove detailGroove-{{groovePosition.id}}">
<div class="detailGroove-config col-12">
<div class="row">
<div class="input-group"
data-ng-class="[{true:'col-12', false:'offset-4 col-4'}[(groovePosition.id == 'left') || (groovePosition.id == 'right')], {'disabled': cut.isDetailGrooveConfigCheckboxDisabled(groovePosition.id)}]"
data-ng-click="cut.onDetailGroovesConfigGrooveEnabledClick(groovePosition.id)">
<div class="input-group-prepend">
<div class="input-group-text input-group-checkbox-text with-checkbox">
<i class="fa fa-square-o" data-ng-if="!cut.detailCornersConfigModalData.grooves[groovePosition.id].enabled"></i>
<i class="fa fa-check-square-o" data-ng-if="cut.detailCornersConfigModalData.grooves[groovePosition.id].enabled"></i>
</div>
</div>
<label class="col-form-label form-control">{{groovePosition.label2}}</label>
</div>
</div>
<div class="row row-col-4 first-row-col-4" data-ng-if="cut.detailCornersConfigModalData.grooves[groovePosition.id].enabled">
<div class="input-group col-12">
<div class="input-group-prepend">
<label class="input-group-text" for="detailGroovesConfigModalData{{groovePosition.id}}Offset">Отступ от края, мм</label>
</div>
<input id="detailGroovesConfigModalData{{groovePosition.id}}Offset"
class="form-control form-control-sm" type="number"
data-ng-model="cut.detailCornersConfigModalData.grooves[groovePosition.id].offset"
data-digits-only pattern="[0-9]*">
</div>
</div>
<div class="row row-col-4 second-row-col-4" data-ng-if="cut.detailCornersConfigModalData.grooves[groovePosition.id].enabled">
<div class="input-group col-12">
<div class="input-group-prepend">
<label class="input-group-text" for="detailGroovesConfigModalData{{groovePosition.id}}Depth">Глубина пропила, мм</label>
</div>
<input id="detailGroovesConfigModalData{{groovePosition.id}}Depth"
class="form-control form-control-sm" type="number"
data-ng-model="cut.detailCornersConfigModalData.grooves[groovePosition.id].depth"
data-digits-only pattern="[0-9]*">
</div>
</div>
<div class="row row-col-4 third-row-col-4" data-ng-if="cut.detailCornersConfigModalData.grooves[groovePosition.id].enabled">
<div class="input-group col-12">
<div class="input-group-prepend">
<label class="input-group-text" for="detailGroovesConfigModalData{{groovePosition.id}}Width">Ширина пропила, мм</label>
</div>
<input id="detailGroovesConfigModalData{{groovePosition.id}}Width"
class="form-control form-control-sm" type="number"
data-ng-model="cut.detailCornersConfigModalData.grooves[groovePosition.id].width"
data-digits-only pattern="[0-9]*">
</div>
</div>
</div>
</div>
</div>
<br/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Закрыть</button>
<button class="btn btn-sm btn-success" data-ng-click="cut.saveDetailCornersConfig()">Сохранить</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="detailLabelConfigModal" data-backdrop="static">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
Наименование детали №{{cut.detailLabelConfigModalData.uiOrderNum}}
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12">
<input class="form-control" type="text" data-ng-model="cut.detailLabelConfigModalData.label" maxlength="20">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Закрыть</button>
<button class="btn btn-sm btn-success" data-ng-click="cut.saveDetailLabelConfig()">Сохранить</button>
</div>
</div>
</div>
</div>
</div>
<?php $this->Assign('title', $siteTitle);?>
View File
Whitespace-only changes.
+10
View File
@@ -0,0 +1,10 @@
<?php
$class = 'message';
if (!empty($params['class'])) {
$class .= ' ' . $params['class'];
}
if (!isset($params['escape']) || $params['escape'] !== false) {
$message = h($message);
}
?>
<div class="<?= h($class) ?>" onclick="this.classList.add('hidden');"><?= $message ?></div>
+4
View File
@@ -0,0 +1,4 @@
<div class="alert alert-danger alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
<p><?= $message ?></p>
</div>
+6
View File
@@ -0,0 +1,6 @@
<?php
if (!isset($params['escape']) || $params['escape'] !== false) {
$message = h($message);
}
?>
<div class="message success" onclick="this.classList.add('hidden')"><?= $message ?></div>
+7
View File
@@ -0,0 +1,7 @@
<?php use Cake\Core\Configure;?>
<script type="text/javascript">
supersamApp.factory('config', function() {
return <?php echo json_encode(isset($jsConfig) ? $jsConfig : []); ?>;
});
</script>
@@ -0,0 +1,7 @@
<?php use Cake\Core\Configure;?>
<script type="text/javascript">
supersamApp.factory('config', function() {
return <?php echo json_encode(isset($jsConfig) ? $jsConfig : []); ?>;
});
</script>
+38
View File
@@ -0,0 +1,38 @@
<script type="text/javascript">
function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length == 2) return parts.pop().split(";").shift();
else return null;
}
function setCookie(name,value,days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days*24*60*60*1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + (value || "") + expires + "; path=/";
}
(function($, document) {
$(document).ready(function () {
var cookieConsent = getCookie("cookie_consent");
if (cookieConsent == null) {
$("<div class=\"cookie-consent-container-wrap\"><div class=\"cookie-consent-container\"></div>"+
"<div class=\"cookie-consent-content\">"+
"<div><span>Мы используем файлы cookie, чтобы обеспечить максимальное удобство пользователям. Продолжая использовать этот сайт, вы даете согласие на использование файлов cookies.</span> <a href=\"https://supersamsev.ru/cookie-policy\" class=\"cookie-consent-more\" target=\"_blank\">Подробнее</a></div>"+
"<a class=\"cookie-consent-ok\">Согласен</a>"+
"</div>"+
"</div>").appendTo('body');
$(".cookie-consent-ok").on('click', function () {
setCookie('cookie_consent', true, 1095);
$(".cookie-consent-container-wrap").css({'opacity': '0', 'visibility': 'hidden'});
});
}
})
})(jQuery, document);
</script>
@@ -0,0 +1,264 @@
<div id="result"></div>
<div id="map"></div>
<a tabindex="-1" href="/services" title="{{::config.primarySiteTitle}}" target="_blank" class="delivery-logo-row">
<img class="apps-logo" src="/img/logo-<?=$jsConfig['siteDomain'];?>.png" alt="{{::config.primarySiteTitle}}">
</a>
<script type="text/javascript">
function resultsClose() {
$('#result').removeClass('active');
}
function init() {
var myMap = new ymaps.Map('map', {
center: [<?=$delivery['location'];?>],
zoom: 17,
type: 'yandex#map',
controls: []
}),
searchFinishPoint = new ymaps.control.SearchControl({
options: {
useMapBounds: true,
noCentering: true,
noPopup: true,
noPlacemark: true,
placeholderContent: 'Адрес доставки',
size: 'large',
float: 'none',
position: { left: 10, top: 44 }
}
}),
calculator = new DeliveryCalculator(myMap, myMap.getCenter()) ;
searchFinishPoint.events.add('load', function (event) { $('#result').removeClass('active'); });
searchFinishPoint.events.add('clear', function (event) { $('#result').removeClass('active'); });
searchFinishPoint.events.add('submit', function (event) { $('#result').removeClass('active'); });
myMap.controls.add(searchFinishPoint);
calculator.setStartPoint([<?=$delivery['location'];?>]);
searchFinishPoint.events.add('resultselect', function (e) {
var results = searchFinishPoint.getResultsArray(),
selected = e.get('index'),
point = results[selected].geometry.getCoordinates();
calculator.setFinishPoint(point);
})
.add('load', function (event) {
if (!event.get('skip') && searchFinishPoint.getResultsCount()) {
searchFinishPoint.showResult(0);
}
});
}
function DeliveryCalculator(map, finish) {
this._map = map;
this._start = null;
this._route = null;
this._startBalloon;
this._finishBalloon;
map.events.add('click', this._onClick, this);
}
var ptp = DeliveryCalculator.prototype;
ptp._onClick= function (e) {
if (this._start) {
this.setFinishPoint(e.get('coords'));
} else {
this.setStartPoint(e.get('coords'));
}
};
ptp._onStartDragEnd = function (e) {
var coords = this._start.geometry.getCoordinates();
this.geocode("start", coords);
};
ptp._onFinishDragEnd = function (e) {
var coords = this._finish.geometry.getCoordinates();
this.geocode("finish", coords);
};
ptp.getDirection = function () {
if(this._route) {
this._map.geoObjects.remove(this._route);
}
if (this._start && this._finish) {
var self = this,
start = this._start.geometry.getCoordinates(),
finish = this._finish.geometry.getCoordinates(),
startBalloon = this._startBalloon,
finishBalloon = this._finishBalloon;
ymaps.route([
finish,
start,
], {multiRoute: true })
.then(function (router) {
var routes = router.getRoutes(), distanceWithTolls = 0, distanceWithoutTolls = 0, distance = 0,
selectedRoute, selectedRouteWithTolls, selectedRouteWithoutTolls;
for (var i = 0, l = routes.getLength(); i < l; i++) {
var route = routes.get(i);
if (!route.properties.get('blocked')) {
distance = Math.ceil(route.properties.get('distance').value / 1000)
if (route.properties.get('hasTolls')) {
if ((distanceWithTolls == 0) || (distance < distanceWithTolls)) {
selectedRouteWithTolls = route;
distanceWithTolls = distance;
}
} else {
if ((distanceWithoutTolls == 0) || (distance < distanceWithoutTolls)) {
selectedRouteWithoutTolls = route;
distanceWithoutTolls = distance;
}
}
}
}
if (distanceWithoutTolls > 0) { //route without tolls exists
selectedRoute = selectedRouteWithoutTolls;
distance = distanceWithoutTolls;
self.drawSelectedRoute(router, selectedRoute, distance, startBalloon, finishBalloon, self);
self._map.setBounds(self._map.geoObjects.getBounds())
} else {
var distanceWithTolls2 = 0, distanceWithoutTolls2 = 0, distance2 = 0;
if (distanceWithTolls > 6) {
//возможно едем через паром, попытка построить роут в объезд
ymaps.route([
finish,
{ type: 'wayPoint', point: 'Сапунгорская ул.' },
start,
], {multiRoute: true })
.then(function (router2) {
var routes2 = router2.getRoutes();
for (var i = 0, l = routes2.getLength(); i < l; i++) {
var route2 = routes2.get(i);
if (!route2.properties.get('blocked')) {
distance2 = Math.ceil(route2.properties.get('distance').value / 1000);
if (route2.properties.get('hasTolls')) {
if ((distanceWithTolls2 == 0) || (distance2 < distanceWithTolls2)) {
selectedRouteWithTolls2 = route2;
distanceWithTolls2 = distance2;
}
} else {
if ((distanceWithoutTolls2 == 0) || (distance2 < distanceWithoutTolls2)) {
selectedRouteWithoutTolls2 = route2;
distanceWithoutTolls2 = distance2;
}
}
}
}
if (distanceWithoutTolls2 > 0) { //route without tolls exists
selectedRoute2 = selectedRouteWithoutTolls2;
distance2 = distanceWithoutTolls2;
} else {
selectedRoute2 = selectedRouteWithTolls;
distance2 = distanceWithTolls;
}
self.drawSelectedRoute(router2, selectedRoute2, distance2, startBalloon, finishBalloon, self);
self._map.setBounds(self._map.geoObjects.getBounds())
})
} else {
if (distanceWithoutTolls > 0) { //route without tolls exists
selectedRoute = selectedRouteWithoutTolls;
distance = distanceWithoutTolls;
} else {
selectedRoute = selectedRouteWithTolls;
distance = distanceWithTolls;
}
self.drawSelectedRoute(router, selectedRoute, distance2, startBalloon, finishBalloon, self);
self._map.setBounds(self._map.geoObjects.getBounds())
}
}
});
}
};
ptp.drawSelectedRoute = function(router, selectedRoute, distance, startBalloon, finishBalloon, self) {
router.setActiveRoute(selectedRoute);
var /*distance = Math.round(router.getLength() / 1000),*/
distanceLabel = "",
message = '<div class="resultInner"><span>Расстояние: ' + (distance < 1 ? 1 : distance) + 'км.</span><br/>' +
'<span style="font-weight: bold; font-style: italic; ">Стоимость доставки: %sр.</span><span id="resultsClose" onClick="javascript:resultsClose();">&times;</span><div>'
distanceLabel = self.calculate(distance);
self._route = selectedRoute.getPaths();
self._route.options.set({ strokeWidth: 5, strokeColor: '0000ffff', opacity: 0.5 });
self._map.geoObjects.add(self._route);
self._start.properties.set('balloonContentBody', startBalloon + message.replace('%s', distanceLabel));
self._finish.properties.set('balloonContentBody', finishBalloon + message.replace('%s', distanceLabel));
$('#result').html(finishBalloon + message.replace('%s', distanceLabel)).addClass('active');
};
ptp.setStartPoint = function (position) {
if(this._start) {
this._start.geometry.setCoordinates(position);
}
else {
this._start = new ymaps.Placemark(position, { iconContent: 'А', }, { draggable: false });
this._start.events.add('dragend', this._onStartDragEnd, this);
this._map.geoObjects.add(this._start);
}
this.geocode("start", position);
};
ptp.setFinishPoint = function (position) {
if(this._finish) {
this._finish.geometry.setCoordinates(position);
}
else {
this._finish = new ymaps.Placemark(position, { iconContent: 'Б' }, { draggable: true });
this._finish.events.add('dragend', this._onFinishDragEnd, this);
this._map.geoObjects.add(this._finish);
}
if (this._start) {
this.geocode("finish", position);
}
};
ptp.geocode = function (str, point) {
ymaps.geocode(point).then(function(geocode) {
if (str == "start") {
this._startBalloon = "<h3><?=$delivery['address'];?></h3><p>" + geocode.geoObjects.get(0).properties.get('description') + "</p>";
this._start.properties.set('balloonContentBody', this._startBalloon);
} else {
this._finishBalloon = geocode.geoObjects.get(0) &&
geocode.geoObjects.get(0).properties.get('balloonContentBody') || '';
}
this.getDirection();
}, this);
}
ptp.calculate = function (len) {
// Константы.
var DELIVERY_TARIF = <?=$delivery['costPerKm'];?>,
MINIMUM_COST = <?=$delivery['minimalCost'];?>;
if (len == 1) return (MINIMUM_COST);
return Math.max((len - 1) * DELIVERY_TARIF + MINIMUM_COST, MINIMUM_COST);
};
ymaps.ready(init);
</script>
<?php $this->Assign('title', $siteTitle);?>
+208
View File
@@ -0,0 +1,208 @@
<div id="result"></div>
<div id="map"></div>
<a tabindex="-1" href="/services" title="{{::config.primarySiteTitle}}" target="_blank" class="delivery-logo-row">
<img class="apps-logo" src="/img/logo-<?=$jsConfig['siteDomain'];?>.png" alt="{{::config.primarySiteTitle}}">
</a>
<script type="text/javascript">
function resultsClose() {
$('#result').removeClass('active');
}
function init() {
var myMap = new ymaps.Map('map', {
center: [<?=$delivery['location'];?>],
zoom: 17,
type: 'yandex#map',
controls: []
}),
searchFinishPoint = new ymaps.control.SearchControl({
options: {
useMapBounds: true,
noCentering: true,
noPopup: true,
noPlacemark: true,
placeholderContent: 'Адрес доставки',
size: 'large',
float: 'none',
position: { left: 10, top: 44 }
}
}),
calculator = new DeliveryCalculator(myMap, myMap.getCenter()) ;
searchFinishPoint.events.add('load', function (event) { $('#result').removeClass('active'); });
searchFinishPoint.events.add('clear', function (event) { $('#result').removeClass('active'); });
searchFinishPoint.events.add('submit', function (event) { $('#result').removeClass('active'); });
myMap.controls.add(searchFinishPoint);
calculator.setStartPoint([<?=$delivery['location'];?>]);
searchFinishPoint.events.add('resultselect', function (e) {
var results = searchFinishPoint.getResultsArray(),
selected = e.get('index'),
point = results[selected].geometry.getCoordinates();
calculator.setFinishPoint(point);
})
.add('load', function (event) {
if (!event.get('skip') && searchFinishPoint.getResultsCount()) {
searchFinishPoint.showResult(0);
}
});
}
function DeliveryCalculator(map, finish) {
this._map = map;
this._start = null;
this._route = null;
this._startBalloon;
this._finishBalloon;
map.events.add('click', this._onClick, this);
}
var ptp = DeliveryCalculator.prototype;
ptp._onClick= function (e) {
if (this._start) {
this.setFinishPoint(e.get('coords'));
} else {
this.setStartPoint(e.get('coords'));
}
};
ptp._onStartDragEnd = function (e) {
var coords = this._start.geometry.getCoordinates();
this.geocode("start", coords);
};
ptp._onFinishDragEnd = function (e) {
var coords = this._finish.geometry.getCoordinates();
this.geocode("finish", coords);
};
ptp.getDirection = function () {
if(this._route) {
this._map.geoObjects.remove(this._route);
}
if (this._start && this._finish) {
var self = this,
start = this._start.geometry.getCoordinates(),
finish = this._finish.geometry.getCoordinates(),
startBalloon = this._startBalloon,
finishBalloon = this._finishBalloon;
ymaps.route([
finish,
start,
], {multiRoute: true})
.then(function (router) {
//self._map.geoObjects.add(router);
var routes = router.getRoutes(), distanceWithTolls = 0, distanceWithoutTolls = 0, distance = 0,
selectedRoute, selectedRouteWithTolls, selectedRouteWithoutTolls;
for (var i = 0, l = routes.getLength(); i < l; i++) {
var route = routes.get(i);
if (!route.properties.get('blocked')) {
distance = Math.ceil(route.properties.get('distance').value / 1000)
if (route.properties.get('hasTolls')) {
if ((distanceWithTolls == 0) || (distance < distanceWithTolls)) {
selectedRouteWithTolls = route;
distanceWithTolls = distance;
}
} else {
if ((distanceWithoutTolls == 0) || (distance < distanceWithoutTolls)) {
selectedRouteWithoutTolls = route;
distanceWithoutTolls = distance;
}
}
}
}
if (distanceWithoutTolls > 0) { //route without tolls exists
selectedRoute = selectedRouteWithoutTolls;
distance = distanceWithoutTolls;
} else {
selectedRoute = selectedRouteWithTolls;
distance = distanceWithTolls;
}
router.setActiveRoute(selectedRoute);
var /*distance = Math.round(router.getLength() / 1000),*/
distanceLabel = "",
message = '<div class="resultInner"><span>Расстояние: ' + (distance < 1 ? 1 : distance) + 'км.</span><br/>' +
'<span style="font-weight: bold; font-style: italic; ">Стоимость доставки: %sр.</span><span id="resultsClose" onClick="javascript:resultsClose();">&times;</span><div>'
distanceLabel = self.calculate(distance);
self._route = selectedRoute.getPaths();
self._route.options.set({ strokeWidth: 5, strokeColor: '0000ffff', opacity: 0.5 });
self._map.geoObjects.add(self._route);
self._start.properties.set('balloonContentBody', startBalloon + message.replace('%s', distanceLabel));
self._finish.properties.set('balloonContentBody', finishBalloon + message.replace('%s', distanceLabel));
$('#result').html(finishBalloon + message.replace('%s', distanceLabel)).addClass('active');
});
self._map.setBounds(self._map.geoObjects.getBounds())
}
};
ptp.setStartPoint = function (position) {
if(this._start) {
this._start.geometry.setCoordinates(position);
}
else {
this._start = new ymaps.Placemark(position, { iconContent: 'А', }, { draggable: false });
this._start.events.add('dragend', this._onStartDragEnd, this);
this._map.geoObjects.add(this._start);
}
this.geocode("start", position);
};
ptp.setFinishPoint = function (position) {
if(this._finish) {
this._finish.geometry.setCoordinates(position);
}
else {
this._finish = new ymaps.Placemark(position, { iconContent: 'Б' }, { draggable: true });
this._finish.events.add('dragend', this._onFinishDragEnd, this);
this._map.geoObjects.add(this._finish);
}
if (this._start) {
this.geocode("finish", position);
}
};
ptp.geocode = function (str, point) {
ymaps.geocode(point).then(function(geocode) {
if (str == "start") {
this._startBalloon = "<h3><?=$delivery['address'];?></h3><p>" + geocode.geoObjects.get(0).properties.get('description') + "</p>";
this._start.properties.set('balloonContentBody', this._startBalloon);
} else {
this._finishBalloon = geocode.geoObjects.get(0) &&
geocode.geoObjects.get(0).properties.get('balloonContentBody') || '';
}
this.getDirection();
}, this);
}
ptp.calculate = function (len) {
// Константы.
var DELIVERY_TARIF = <?=$delivery['costPerKm'];?>,
MINIMUM_COST = <?=$delivery['minimalCost'];?>;
if (len == 1) return (MINIMUM_COST);
return Math.max((len - 1) * DELIVERY_TARIF + MINIMUM_COST, MINIMUM_COST);
};
ymaps.ready(init);
</script>
<?php $this->Assign('title', $siteTitle);?>
+8
View File
@@ -0,0 +1,8 @@
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon/favicon_32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon/favicon_16.png">
<link rel="shortcut icon" type="image/png" href="/img/favicon/favicon_16.png" />
<?php foreach(['57', '60', '76', '114', '120', '128', '144', '152', '167', '180', '192'] as $s) {
echo '<link rel="apple-touch-icon-precomposed" sizes="' . $s . 'x' . $s . '" href="/img/favicon/apple-touch-icon-' . $s . '-precomposed.png" />' . "\n";
} ?>
<link rel="apple-touch-icon-precomposed" href="/img/favicon/apple-touch-icon-57-precomposed.png" />
+2
View File
@@ -0,0 +1,2 @@
<?php
echo json_encode($result);
+149
View File
@@ -0,0 +1,149 @@
<?php if (!empty($isAuthorized)) { ?>
<nav class="navbar sticky-top navbar-expand-lg navbar-light bg-light" data-ng-controller="NavController" >
<div class="container">
<ul class="navbar-nav">
<li class="dropdown navbar-current-app">
<?php
foreach($jsConfig['appsList'] as $app) {
if (!empty($currentApp) && ($currentApp == $app['id']) && !empty($authUserAvailableApps) && in_array($app['id'], $authUserAvailableApps)) {
echo '<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown">'.$app['label'].'</a>';
}
}
?>
<div class="dropdown-menu">
<?php
foreach($jsConfig['appsList'] as $app) {
if (!empty($authUserAvailableApps) && in_array($app['id'], $authUserAvailableApps)) {
echo '<a href="' . $app['adminUrl'] . '" class="dropdown-item">' . $app['label'] . '</a>';
}
}
?>
</div>
</li>
</ul>
<button class="navbar-toggler pull-right" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-between" id="navbarNavDropdown">
<ul class="navbar-nav">
<?php if (!empty($currentApp)) {
if ($currentApp == "cut") { ?>
<li class="<?php echo($this->request->getParam('controller') == "CutOrders" ? "active" : "") ?>">
<?php echo $this->Html->link("Заказы", ['controller' => 'CutOrders', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>
<li class="<?php echo($this->request->getParam('controller') == "CutSettings" ? "active" : "") ?>">
<?php echo $this->Html->link("Настройки", ['controller' => 'CutSettings', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>
<li class="<?php echo($this->request->getParam('controller') == "CutStatistics" ? "active" : "") ?>">
<?php echo $this->Html->link("Статистика", ['controller' => 'CutStatistics', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>
<?php
}
if ($currentApp == "calcDoors") { ?>
<li class="<?php echo($this->request->getParam('controller') == "CalcDoorsOrders" ? "active" : "") ?>">
<?php echo $this->Html->link("Заказы", ['controller' => 'CalcDoorsOrders', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>
<li class="<?php echo($this->request->getParam('controller') == "CalcDoorsSettings" ? "active" : "") ?>">
<?php echo $this->Html->link("Настройки", ['controller' => 'CalcDoorsSettings', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>
<li class="<?php echo($this->request->getParam('controller') == "CalcDoorsStatistics" ? "active" : "") ?>">
<?php echo $this->Html->link("Статистика", ['controller' => 'CalcDoorsStatistics', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>
<?php }
if ($currentApp == "fasad") { ?>
<li class="<?php echo($this->request->getParam('controller') == "FasadOrders" ? "active" : "") ?>">
<?php echo $this->Html->link("Заказы", ['controller' => 'FasadOrders', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>
<li class="<?php echo($this->request->getParam('controller') == "FasadSettings" ? "active" : "") ?>">
<?php echo $this->Html->link("Настройки", ['controller' => 'FasadSettings', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>
<li class="<?php echo($this->request->getParam('controller') == "FasadStatistics" ? "active" : "") ?>">
<?php echo $this->Html->link("Статистика", ['controller' => 'FasadStatistics', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>
<?php
}
if ($currentApp == "smsSubscribes") { ?>
<li class="<?php echo($this->request->getParam('controller') == "SmsSubscribes" ? "active" : "") ?>">
<?php echo $this->Html->link("Список рассылок", ['controller' => 'SmsSubscribes', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>
<?php }
if ($currentApp == "smsOrdersReady") { ?>
<li class="<?php echo($this->request->getParam('controller') == "SmsOrdersReady" ? "active" : "") ?>">
<?php echo $this->Html->link("Поиск", ['controller' => 'SmsOrdersReady', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>
<?php }
if ($currentApp == "emailSubscribes") { ?>
<li class="<?php echo($this->request->getParam('controller') == "EmailSubscribes" ? "active" : "") ?>">
<?php echo $this->Html->link("Список рассылок", ['controller' => 'EmailSubscribes', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>
<?php }
if ($currentApp == "deliveryJournal") { ?>
<li class="<?php echo($this->request->getParam('controller') == "DeliveryJournal" ? "active" : "") ?>">
<?php echo $this->Html->link("Журнал", ['controller' => 'DeliveryJournal', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>
<?php }
if ($currentApp == "ordersInfo") { ?>
<li class="<?php echo($this->request->getParam('controller') == "OrdersInfo" ? "active" : "") ?>">
<?php echo $this->Html->link("Счета", ['controller' => 'OrdersInfo', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>
<!--<li class="<?php echo($this->request->getParam('controller') == "OrdersInfoSettings" ? "active" : "") ?>">
<?php echo $this->Html->link("Настройки", ['controller' => 'OrdersInfoSettings', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "nav-link"), null); ?>
</li>-->
<?php }
}?>
</ul>
<ul class="navbar-nav">
<li class="dropdown">
<?php echo $this->Html->link('<i class="fa fa-user"></i> ' . $authUserName, '#', array("escape" => false, "class" => "nav-link dropdown-toggle", "data-toggle" => "dropdown"), null); ?>
<div class="dropdown-menu dropdown-menu-right">
<?php
if (in_array("cut", $authUserAvailableApps) && ($currentApp == "cut")) {
echo '<span class="dropdown-item select-file-button cursor-pointer" ngf-select data-ng-model="nav.convertAstraToBazisFile" name="convertAstraToBazisFile" ngf-accept="\'.xml\'" ngf-max-size="20MB" data-ng-change="nav.convertAstraToBazis()">Астра<i class="fa fa-arrow-right"></i>Базис конвертер</span>';
}
if (in_array("cut", $authUserAvailableApps) || in_array("calcDoors", $authUserAvailableApps)) {
echo $this->Html->link("Настройки", ['controller' => 'Settings', 'action' => 'index', 'prefix' => 'manager'], array("escape" => false, "class" => "dropdown-item"), null);
}
if ($authUserRole == "admin") {
echo $this->Html->link("Пользователи", ['controller' => 'Users', 'action' => 'users_list' , 'prefix' => 'manager'], array("escape" => false, "class" => "dropdown-item"), null);
}
if ($authUserRole != "kassir") {
echo '<div class="dropdown-divider"></div>';
}
echo $this->Html->link("Выход", ['controller' => 'Users', 'action' => 'logout' , 'prefix' => 'manager' ], array("escape" => false, "class" => "dropdown-item", 'confirm' => "Вы уверены?"));
?>
</div>
</li>
<li>
<span class="current-time nav-link"></span>
</li>
</ul>
</div>
</div>
</nav>
<?php } ?>
+15
View File
@@ -0,0 +1,15 @@
<div class="page-404">
<div class="container">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-md-12 text-center">
<a href="<?=$jsConfig['primarySiteUrl'];?>" title="<?=$jsConfig['primarySiteTitle'];?>"><img class="apps-logo" src="/img/logo-<?=$jsConfig['siteDomain'];?>.png"></a><br/><br/>
<?= $this->element("services");?>
</div>
</div>
</div>
</div>
</div>
</div>
+36
View File
@@ -0,0 +1,36 @@
<div>
<a href="https://supersam24.ru" class="services-link btn btn-primary">Магазин фурнитуры</a>
<a href="https://supersamsev.ru" class="services-link btn btn-primary">Магазин ДСП</a>
<a href="https://prometeysev.ru" class="services-link btn btn-primary">Все для кухни, ремонта и дома</a>
<div class="services-social-links">
<a class="vk" href="https://vk.com/supersam_aristo" target="_blank">
<img src="/img/social/vk-h.png">
</a>
</div>
<br/><br/></div>
<b>Также вы можете воспользоваться нашими сервисами:</b><br/>
<div>
<?php
foreach ($jsConfig["appsList"] as $app) {
if (!empty($app['url'])) {
echo '<a href="' . $app['url'] . '" class="services-link btn btn-info">' . (!empty($app['site_label']) ? $app['site_label'] : $app['label_long']) . '</a>';
}
}
?>
</div>
<br/>
<div>
<?php
foreach ($jsConfig["delivery"] as $delivery) {
if (empty($delivery['is_deleted'])) {
echo '<a href="/delivery/' . $delivery['slug'] . '" class="services-link btn btn-info">' . $delivery['label404'] . '</a>';
}
}
?>
</div>
+20
View File
@@ -0,0 +1,20 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
$content = explode("\n", $content);
foreach ($content as $line) :
echo '<p> ' . $line . "</p>\n";
endforeach;
+16
View File
@@ -0,0 +1,16 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
echo $content;
+34
View File
@@ -0,0 +1,34 @@
<?php
use Cake\Core\Configure;
use Cake\Error\Debugger;
$this->layout = 'error';
if (Configure::read('debug')) :
$this->layout = 'dev_error';
$this->assign('title', $message);
$this->assign('templateName', 'error400.ctp');
$this->start('file');
?>
<?php if (!empty($error->queryString)) : ?>
<p class="notice">
<strong>SQL Query: </strong>
<?= h($error->queryString) ?>
</p>
<?php endif; ?>
<?php if (!empty($error->params)) : ?>
<strong>SQL Query Params: </strong>
<?php Debugger::dump($error->params) ?>
<?php endif; ?>
<?= $this->element('auto_table_warning') ?>
<?php
if (extension_loaded('xdebug')) :
xdebug_print_function_stack();
endif;
$this->end();
endif;
?>
<?= $this->element("page404");?>
+39
View File
@@ -0,0 +1,39 @@
<?php
use Cake\Core\Configure;
use Cake\Error\Debugger;
$this->layout = 'error';
if (Configure::read('debug')) :
$this->layout = 'dev_error';
$this->assign('title', $message);
$this->assign('templateName', 'error500.ctp');
$this->start('file');
?>
<?php if (!empty($error->queryString)) : ?>
<p class="notice">
<strong>SQL Query: </strong>
<?= h($error->queryString) ?>
</p>
<?php endif; ?>
<?php if (!empty($error->params)) : ?>
<strong>SQL Query Params: </strong>
<?php Debugger::dump($error->params) ?>
<?php endif; ?>
<?php if ($error instanceof Error) : ?>
<strong>Error in: </strong>
<?= sprintf('%s, line %s', str_replace(ROOT, 'ROOT', $error->getFile()), $error->getLine()) ?>
<?php endif; ?>
<?php
echo $this->element('auto_table_warning');
if (extension_loaded('xdebug')) :
xdebug_print_function_stack();
endif;
$this->end();
endif;
?>
<?= $this->element("page404");?>
+934
View File
@@ -0,0 +1,934 @@
<div data-ng-controller="FasadController" class="hidden-on-load">
<div class="container apps-container">
<form name="fasadConfigForm">
<div class="card">
<div class="card-body">
<div class="sheet-config-row">
<div class="row">
<div class="col-6 form-horizontal">
<div class="form-group no-bottom-margin">
<h3 class="siteTitle">
<span>{{config.common.siteTitle.value}}</span>
<button tabindex="-1" type="button" class="btn btn-sm btn-warning openSelectSheetConfigModal" data-ng-click="fasad.openSelectSheetConfigModal()">Выбрать фактуру листа и кромки</button>
</h3>
</div>
<div class="form-group row no-bottom-margin">
{{selectedSheetImage = utilities.getFasadSheetImageById(fasad.selectedSheetData.sheetId, 'image');""}}
<label for="selectedSheetDimension" class="col-4 col-form-label">Фактура листа:<a tabindex="-1" data-ng-if="fasad.selectedSheetData.sheetId && selectedSheetImage" href="{{selectedSheetImage}}" data-lightbox="sheetIdLightBox"><i class="fa fa-question-circle"></i></a>
</label>
<div class="col-8">
<label class="col-form-label selected-sheet-dimension">
{{fasad.getSelectedSheetLabel(fasad.selectedSheetData.sheetId)}}
</label>
</div>
</div>
<div class="form-group row no-bottom-margin">
<label for="selectedSheetDimension" class="col-4 col-form-label">Размер листа: </label>
<div class="col-8">
<label class="col-form-label selected-sheet-dimension">{{fasad.getSelectedSheetFullDimensions()}}</label>
</div>
</div>
<div class="form-group row no-bottom-margin">
<label class="col-4 col-form-label">Размер распила:<a class="popover-button cut-size-desc" tabindex="-1" role="button" data-ng-show="fasad.selectedSheetData.sheetId" data-container="body" data-toggle="popover" data-trigger="focus" data-placement="right" data-content="{{fasad.faceDescriptionWithPrice | trustAs}}" data-html="true"><i class="fa fa-question-circle"></i></a> </label>
<div class="col-8">
<label class="col-form-label selected-sheet-dimension">{{fasad.getSelectedSheetDimensions()}}</label>
</div>
</div>
</div>
<div class="col-6">
<div class="row logo-row text-left">
<div class="col-5">
<a tabindex="-1" href="/services" title="{{::config.primarySiteTitle}}" target="_blank">
<img src="/img/logo.png" alt="{{::config.primarySiteTitle}}">
</a>
</div>
<div class="col-7">
<h5 class="addressDelivery">
<span>Наш адрес:</span>
<span class="addressDelivery" data-ng-bind-html="::utilities.getConfigLabel('addressDelivery') | trustAs"></span>
<span class="addressDelivery" data-ng-bind-html="::utilities.getConfigLabel('addressDeliveryAdd') | trustAs"></span>
</h5>
</div>
</div>
<h5 class="detailWithBordersWarning text-danger">{{::utilities.getConfigLabel('detailWithBordersWarning')}}</h5>
<div class="headerInfoLabel">{{::utilities.getConfigLabel('headerInfoLabel') | trustAs}}</div>
</div>
</div>
<div class="row form-horizontal checkbox-group no-bottom-margin">
<div class="col-6">
<div class="form-group row no-bottom-margin">
<label class="col-8 col-form-label" data-ng-click="fasad.mergeSimilarDetailsToggle()">Объединение одинаковых деталей: </label>
<div class="col-4" id="mergeSimilarDetails">
<i class="fa fa-square-o" data-ng-if="!fasad.mergeSimilarDetails" data-ng-click="fasad.mergeSimilarDetailsToggle()"></i>
<i class="fa fa-check-square-o" data-ng-if="fasad.mergeSimilarDetails" data-ng-click="fasad.mergeSimilarDetailsToggle()"></i>
</div>
</div>
</div>
<div class="col-6">
<div class="form-group row no-bottom-margin">
<label class="col-6 col-form-label" data-ng-click="fasad.packCuttingsToOrderToggle()">Вкладывать остатки в заказ: </label>
<div class="col-4">
<i class="fa fa-square-o" data-ng-if="!fasad.packCuttingsToOrder" data-ng-click="fasad.packCuttingsToOrderToggle()"></i>
<i class="fa fa-check-square-o" data-ng-if="fasad.packCuttingsToOrder" data-ng-click="fasad.packCuttingsToOrderToggle()"></i>
</div>
</div>
</div>
</div>
<div class="row form-horizontal">
<div class="col-6">
<div class="form-group row no-bottom-margin">
{{selectedBorder1Image = utilities.getFasadBorderImageById(fasad.selectedSheetData.border1Id);""}}
<label class="col-3 col-form-label">
Кромка №1:<a tabindex="-1" class="preview-button" data-ng-if="fasad.selectedSheetData.border1Id && selectedBorder1Image" href="{{selectedBorder1Image}}" data-lightbox="border1IdLightBox"><i class="fa fa-question-circle"></i></a>
</label>
<div class="col-9">
<label class="col-form-label selected-sheet-dimension">
<span data-ng-bind-html="fasad.getBorderLabel(fasad.selectedSheetData.border1Id, fasad.selectedSheetData.border1Height) | trustAs"></span>
</label>
</div>
</div>
</div>
<div class="col-6">
<div class="form-group row no-bottom-margin">
{{selectedBorder2Image = utilities.getFasadBorderImageById(fasad.selectedSheetData.border2Id);""}}
<label class="col-3 col-form-label">
Кромка №2:<a tabindex="-1" class="preview-button" data-ng-if="fasad.selectedSheetData.border2Id && selectedBorder2Image" href="{{utilities.getFasadBorderImageById(fasad.selectedSheetData.border2Id)}}" data-lightbox="border2IdLightBox"><i class="fa fa-question-circle"></i></a>
</label>
<div class="col-9">
<label class="col-form-label selected-sheet-dimension">
<span data-ng-bind-html="fasad.getBorderLabel(fasad.selectedSheetData.border2Id, fasad.selectedSheetData.border2Height) | trustAs"></span>
</label>
</div>
</div>
</div>
</div>
<div class="row form-horizontal">
<div class="col-6">
<div class="form-group row margin-top-3 no-bottom-margin">
<label class="col-3 col-form-label" for="orderName">ФИО: <a class="popover-button" tabindex="-1" role="button" data-container="body" data-toggle="popover" data-trigger="focus" data-placement="right" data-content="{{utilities.getConfigLabel('fioIsRequired', true) | trustAs}}" data-html="true"><i class="fa fa-question-circle"></i></a></label>
<div class="col-9" data-ng-class="fasadConfigForm.orderName.$invalid && fasadConfigForm.orderName.$dirty ? 'has-error' : ''">
<input type="text" class="form-control form-control-sm" name="orderName" id="orderName" data-ng-model="fasad.orderName" required data-ng-enter="fasad.onFieldEnter('orderPhone')">
</div>
</div>
<div class="form-group row margin-top-3 no-bottom-margin">
<label class="col-3 col-form-label" for="orderPhone">Телефон: <a class="popover-button" tabindex="-1" role="button" data-container="body" data-toggle="popover" data-trigger="focus" data-placement="right" data-content="{{utilities.getConfigLabel('phoneIsRequired', true) | trustAs}}" data-html="true"><i class="fa fa-question-circle"></i></a></label>
<div class="col-9" data-ng-class="fasadConfigForm.orderPhone.$invalid && fasadConfigForm.orderPhone.$dirty ? 'has-error' : ''">
<input type="text" class="form-control form-control-sm" name="orderPhone" id="orderPhone" data-ng-model="fasad.orderPhone" data-model-view-value="true" data-digits-only placeholder=" " required data-ng-enter="fasad.onFieldEnter('orderEmail')">
</div>
</div>
<div class="form-group row margin-top-3 no-bottom-margin">
<label class="col-3 col-form-label" for="orderEmail">Email: <a class="popover-button" tabindex="-1" role="button" data-container="body" data-toggle="popover" data-trigger="focus" data-placement="right" data-content="{{utilities.getConfigLabel('emailIsRequired', true) | trustAs}}" data-html="true"><i class="fa fa-question-circle"></i></a></label>
<div class="col-9" data-ng-class="fasadConfigForm.orderEmail.$invalid && fasadConfigForm.orderEmail.$dirty ? 'has-error' : ''">
<input type="email" class="form-control form-control-sm" name="orderEmail" id="orderEmail" data-ng-model="fasad.orderEmail" required data-ng-enter="fasad.onFieldEnter('orderAddress')">
</div>
</div>
<div class="form-group row margin-top-3 no-bottom-margin">
<label class="col-3 col-form-label" for="orderAddress">Адрес доставки: </label>
<div class="col-9" data-ng-class="fasadConfigForm.orderAddress.$invalid && fasadConfigForm.orderAddress.$dirty ? 'has-error' : ''">
<input type="text" class="form-control form-control-sm" name="orderAddress" id="orderAddress" data-ng-model="fasad.orderAddress" data-ng-enter="fasad.onFieldEnter('orderComment')">
</div>
</div>
</div>
<div class="col-6">
<div class="form-group row margin-top-3 no-bottom-margin">
<div class="col-12">
<textarea class="form-control form-control-sm margin-top-3" name="orderComment" id="orderComment" data-ng-model="fasad.orderComment" placeholder="Комментарий к заказу"></textarea>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card details-config">
<div class="card-body">
<div class="row">
<div class="col-4 cut-results">
<div class="print-left">
<div data-ng-if="fasad.orderNum && (fasad.orderNum.length > 0)">Заказ: {{fasad.orderNum}}</div>
<div>Листов в распиле: {{fasad.sheets.length}}</div>
{{selectedSheetsInStock = utilities.getSelectedSheetsInStock(fasad.selectedSheetData.sheetId);""}}
<div data-ng-if="(selectedSheetsInStock != null) && (selectedSheetsInStock < fasad.sheets.length)" class="text-warning">Листов на складе: {{selectedSheetsInStock}}</div>
<div>Всего деталей: {{fasad.getDetailsCount()}}</div>
<div>Площадь деталей: {{(fasad.getDetailsArea()/1000000).toFixed(2).replace(".00", "")}}м<sup>2</sup></div>
<div>Длина пропила: {{fasad.notchCount}}м.</div>
<div data-ng-if="fasad.grooveCount.length > 0">Длина пропила пазов: {{fasad.grooveCount}}м.</div>
<div data-ng-if="fasad.selectedSheetData.border1Id || fasad.selectedSheetData.border2Id">Длина кромок:
<ul>
<li data-ng-repeat="borderId in [1, 2]" data-ng-if="fasad.selectedSheetData['border'+borderId+'Id'] > 0">
кромка №{{borderId}} - {{((fasad.selectedSheetData["border" + borderId + "Count"] ? fasad.selectedSheetData["border" + borderId + "Count"] : 0)/1000).toFixed(2).replace(".00", "")}}м.
</li>
</ul>
</div>
<div><span class="text-strong-danger">Ориентировочная</span> <span class="no-wrap">стоимость: {{fasad.price}} руб.</span></div>
<br/>
<div class="order-buttons">
<div class="" data-ng-if="config.createdForManagerId">
Если вы знаете менеджера,<br/> выберите его в поле ниже<br/>
<div class="btn-group order-available-managers">
<button tabindex="-1" data-ng-disabled="!fasad.selectedSheetData.sheetId || fasadConfigForm.$invalid || fasad.isSendOrderEmailSubmitting" type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ utilities.getAvailableToOrderManagerNameById(fasad.createdForManagerId) }} <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li data-ng-repeat="manager in config.availableToOrderManagers">
<a tabindex="-1" data-ng-click="fasad.setCreatedForManagerId(manager.id)" class="dropdown-item">{{manager.name}}</a>
</li>
</ul>
</div>
</div>
<br/>
<div class="btn-group print-order-button" data-ng-if="config.additionalActionsAllowed">
<button tabindex="-1" data-ng-disabled="!fasad.selectedSheetData.sheetId || fasadConfigForm.$invalid || fasad.isSendOrderEmailSubmitting" type="button" class="btn btn-sm btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-print"></i>&nbsp;Печать <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a tabindex="-1" data-ng-click="fasad.printOrder('Spec')" class="dropdown-item">Печать спецификации</a></li>
<li><a tabindex="-1" data-ng-click="fasad.printOrder('Fasad')" class="dropdown-item">Печать карт распила</a></li>
</ul>
</div>
<button tabindex="-1" data-ng-if="!config.savedOrderData && (config.branches.length == 1)" data-ng-disabled="!fasad.selectedSheetData.sheetId || fasadConfigForm.$invalid || fasad.isSendOrderEmailSubmitting" type="button" class="btn btn-sm btn-primary send-order-button" data-ng-click="fasad.sendOrderEmailNotify(config.branches[0].id)">Отправить менеджеру</button>
<div class="btn-group send-order-button" data-ng-if="!config.savedOrderData && (config.branches.length > 1)">
<button tabindex="-1" data-ng-disabled="!fasad.selectedSheetData.sheetId || fasadConfigForm.$invalid || fasad.isSendOrderEmailSubmitting" type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Отправить менеджеру <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li data-ng-repeat="branch in config.branches"><a tabindex="-1" data-ng-click="fasad.sendOrderEmailNotify(branch.id)" class="dropdown-item">{{branch.label}}</a></li>
</ul>
</div>
<button tabindex="-1" data-ng-if="config.savedOrderData" data-ng-disabled="!fasad.selectedSheetData.sheetId || fasadConfigForm.$invalid || fasad.isSendOrderEmailSubmitting" type="button" class="btn btn-sm btn-primary send-order-button" data-ng-click="fasad.sendOrderEmailNotify(false)">Сохранить заказ</button>
</div>
</div>
</div>
<div class="col-8 details-list">
<h5 class="justify-content-between">
<span>Параметры деталей, мм</span>
</h5>
<div>
<table class="table table-striped">
<tbody>
<tr data-ng-repeat="detail in fasad.details">
<td colspan="7">
<div>
<table class="table table-details-list table-no-borders">
<thead>
<tr>
<th class="detail-number-label">№</th>
<th>Длина</th>
<th>Ширина</th>
<th>Кол-во, шт</th>
<th class="detail-borders"><span data-ng-if="fasad.selectedSheetData.border1Id > 0">Кромка</span></th>
<th class="detailCorners detailGrooves text-center">Доп.</th>
<th class=""></th>
</tr>
</thead>
<tbody>
<tr>
<td class="detail-number-label">
<span>{{$index+1}}</span>
&nbsp;
<a tabindex="-1" href="#" data-ng-click="fasad.openDetailLabelConfigModal(detail.id)" data-click-prevent="true" title="Наименование детали">
<i class="fa fa-pencil" data-ng-class="{'text-success' : detail.label && (detail.label.length > 0) }"></i>
</a>
</td>
<td>
<input id="detail{{detail.id}}width" class="form-control form-control-sm" type="text" data-ng-model="detail.width" data-digits-only data-ng-enter="fasad.onDetailEnter(detail, 'width')" pattern="[0-9]*">
</td>
<td>
<input id="detail{{detail.id}}height" class="form-control form-control-sm" type="text" data-ng-model="detail.height" data-digits-only data-ng-enter="fasad.onDetailEnter(detail, 'height')" pattern="[0-9]*">
</td>
<td>
<input id="detail{{detail.id}}count" class="form-control form-control-sm" type="text" data-ng-model="detail.count" data-digits-only data-ng-enter="fasad.onDetailEnter(detail, 'count')" pattern="[0-9]*">
</td>
<td class="detail-borders">
<span class="detail-color-box" style="background-color: {{detail.color}}" data-ng-if="!fasad.selectedSheetData.border1Id"></span>
<div data-ng-if="fasad.selectedSheetData.border1Id > 0" data-ng-class="{'single-border-config' : !fasad.selectedSheetData.border2Id}">
<div class="left-detail-border" >
<div class="detail-border detail-border-{{borderId}} {{fasad.detailsBorders[detail.id].left == borderId ? 'active' : ''}}" data-ng-repeat="borderId in [1, 2]" data-ng-click="fasad.toggleDetailBorder(detail, 'left', borderId)" title="{{fasad.detailsBorders[detail.id].left == borderId ? 'Удалить кромку' : 'Добавить кромку'}}" data-ng-if="fasad.selectedSheetData['border'+borderId+'Id'] > 0">
{{borderId}}
</div>
</div>
<div class="detail-border-center">
<div class="top-detail-border">
<div class="detail-border detail-border-{{borderId}} {{fasad.detailsBorders[detail.id].top == borderId ? 'active' : ''}}" data-ng-repeat="borderId in [1, 2]" data-ng-click="fasad.toggleDetailBorder(detail, 'top', borderId)" title="{{fasad.detailsBorders[detail.id].top == borderId ? 'Удалить кромку' : 'Добавить кромку'}}" data-ng-if="fasad.selectedSheetData['border'+borderId+'Id'] > 0">
{{borderId}}
</div>
</div>
<div class="detail-borders {{fasad.getDetailBordersClass(detail)}}" style="background-color: {{detail.color}}; border-color: {{detail.color}};"></div>
<div class="bottom-detail-border">
<div class="detail-border detail-border-{{borderId}} {{fasad.detailsBorders[detail.id].bottom == borderId ? 'active' : ''}}" data-ng-repeat="borderId in [1, 2]" data-ng-click="fasad.toggleDetailBorder(detail, 'bottom', borderId)" title="{{fasad.detailsBorders[detail.id].bottom == borderId ? 'Удалить кромку' : 'Добавить кромку'}}" data-ng-if="fasad.selectedSheetData['border'+borderId+'Id'] > 0">
{{borderId}}
</div>
</div>
</div>
<div class="right-detail-border">
<div class="detail-border detail-border-{{borderId}} {{fasad.detailsBorders[detail.id].right == borderId ? 'active' : ''}}" data-ng-repeat="borderId in [1, 2]" data-ng-click="fasad.toggleDetailBorder(detail, 'right', borderId)" title="{{fasad.detailsBorders[detail.id].right == borderId ? 'Удалить кромку' : 'Добавить кромку'}}" data-ng-if="fasad.selectedSheetData['border'+borderId+'Id'] > 0">
{{borderId}}
</div>
</div>
</div>
</td>
<td class="config-detailCorners-button">
<a tabindex="-1" href="#" data-ng-click="fasad.openDetailCornersConfigModal(detail, $index+1)" data-click-prevent="true" title="Скругление/срез/паз" data-ng-disabled="!fasad.isDetailDimensionsFilled(detail)">
<img src="/img/corners.png" alt="{{::config.siteTitle}}">
</a>
</td>
<td class="remove-detail-button">
<a tabindex="-1" href="#" class="text-danger remove-detail" data-ng-if="fasad.details.length > 1" data-ng-click="fasad.removeDetail(detail)" data-click-prevent="true">x</a>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<div data-ng-bind-html="fasad.getDetailCornersText(detail) | trustAs"></div>
</div>
<div>
<div data-ng-bind-html="fasad.getDetailGroovesText(detail) | trustAs"></div>
</div>
</td>
</tr>
</tbody>
</table>
<div class="d-flex justify-content-between">
<button tabindex="-1" class="btn btn-sm btn-success" data-ng-click="fasad.addDetail()" data-click-prevent="true">
Добавить деталь
</button>
<button tabindex="-1" class="btn btn-danger btn-sm clear-button" data-ng-click="fasad.confirmCleanup()" data-click-prevent="true">
Очистить
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
<div class="sheets-wrap" data-ng-if="(fasad.selectedSheetData.sheetId) && fasad.selectedSheetDimension">
<div class="card" data-ng-repeat="sheet in fasad.sheets">
<div class="card-body">
<div class="sheet" data-ng-class="fasad.sheetClass" data-ng-style="fasad.sheetStyle">
<div class="info sheet-title">Лист {{sheet.id}}</div>
<div class="info sheet-width">
<span class="sheet-width-left">|&lsaquo;</span>
<span class="sheet-width-right">&rsaquo;|</span>
{{fasad.selectedSheetDimension.width}} мм
</div>
<div class="info sheet-height">
<span class="sheet-height-top">&rsaquo;|</span>
<span class="sheet-height-bottom">|&lsaquo;</span>
<span data-ng-style="fasad.sheetHeightStyle">{{fasad.selectedSheetDimension.height}} мм</span>
</div>
<div data-ng-repeat="detail in sheet.details" data-ng-style="fasad.getDetailStyle(detail)" class="detail detail-with-entire-borders" data-ng-class="fasad.getDetailClass(detail)" data-ng-click="fasad.rotateDetail(detail)">
<div class="detail-height">{{detail.height}}</div>
<div class="detail-width">{{detail.width}}</div>
<div class=""></div>
<a class="detail-label-edit" tabindex="-1" href="#" data-ng-click="fasad.openDetailLabelConfigModal(detail.detailId)" data-click-prevent="true" title="{{fasad.getDetailLabel(detail.detailId)}}" data-ng-if="fasad.getDetailLabel(detail.detailId).length > 0">
<i class="fa fa-edit"></i>
</a>
<div data-ng-repeat="borderPosition in config.borderPositions" class="entire-border {{borderPosition}}-border" data-ng-if="fasad.detailsBorders[detail.detailId][borderPosition]"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="notifyModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<div>{{fasad.notifyMessage}}</div>
</div>
<div class="modal-footer">
<button class="btn btn-sm btn-info" data-dismiss="modal" href="#">OK</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="notifySendOrderEmailModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<div data-ng-if="config.savedOrderData" data-ng-bind-html="::utilities.getConfigLabel('notifySaveOrderDataMessage', true) | trustAs"></div>
<div data-ng-if="!config.savedOrderData" data-ng-bind-html="::utilities.getConfigLabel('notifySendOrderEmailMessage') | trustAs"></div>
</div>
<div class="modal-footer">
<button class="btn btn-sm btn-info" data-dismiss="modal" href="#">OK</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="confirmCleanupModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<div class="alert alert-warning" data-ng-bind-html="::utilities.getConfigLabel('confirmCleanupMessage') | trustAs">
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="fullOrderCleanupCheckbox" data-ng-model="fasad.fullOrderCleanup">
<label class="form-check-label" for="fullOrderCleanupCheckbox">
Очистить всё
</label>
</div>
</div>
<div class="modal-footer justify-content-between">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Отмена</button>
<button type="button" class="btn btn-primary" data-ng-click="fasad.cleanup(true)">Продолжить</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="selectSheetConfigModal" data-backdrop="static">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<span data-ng-show="fasad.activeSheetSelectStep == 1">Шаг 1/4: Выбор фактуры и размера листа</span>
<span data-ng-show="fasad.activeSheetSelectStep == 2">Шаг 2/4: Выбор цвета и толщины кромки №1</span>
<span data-ng-show="fasad.activeSheetSelectStep == 3">Шаг 3/4: Выбор цвета и толщины кромки №2</span>
<span data-ng-show="fasad.activeSheetSelectStep == 4">Шаг 4/4: Подтверждение выбранной конфигурации</span>
</h5>
<div class="input-group input-group-sm search-input-group" data-ng-show="(fasad.activeSheetSelectStep != 4)">
<div class="input-group-prepend">
<label class="input-group-text" for="searchFilter{{fasad.activeSheetSelectStep}}" >
<i class="fa fa-search"></i>
</label>
</div>
<input placeholder="Поиск.." id="searchFilter{{fasad.activeSheetSelectStep}}" class="form-control " type="text" data-ng-model="fasad.searchFilters[fasad.activeSheetSelectStep]">
</div>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div data-ng-show="fasad.activeSheetSelectStep == 1">
<div class="row">
<div class="col-12">
<div class="alert alert-info">
Внимание! Цвет на мониторе может отличаться от реального цвета ДСП.
</div>
</div>
</div>
<div class="row">
<div id="selectSheetId{{sheet.id}}" class="select-sheet-data col-6 col-xl-4 col-lg-4 col-md-4 col-sm-6" data-ng-repeat="sheet in filteredSheets = (config.sheets | filterData:fasad.searchFilters[1])" data-ng-if="sheet.is_active || config.savedOrderData && config.savedOrderData.sheet_data && (config.savedOrderData.sheet_data.sheetId == sheet.id)">
<div data-ng-click="fasad.selectSheetId(sheet)" data-ng-class="{'selected' : (fasad.selectSheetConfigModalData.sheetId == sheet.id)}">
<div class="select-sheet-image img-thumbnail" data-ng-if="fasad.isSelectSheetConfigModalTriggered && sheet.thumb">
<img data-ng-src="{{utilities.getFasadSheetImageById(sheet.id, 'thumb')}}" class="img-lazy">
</div>
<div class="select-sheet-label">
<span><b>{{sheet.label}}</b></span>
<span>Размеры:</> {{fasad.getSheetFullDimensions(sheet.sheet_dimension_id)}}</span>
<span>Цена: {{sheet.price}} руб. <span data-ng-if="sheet.sheetIdFor1C && (sheet.quantityIn1C != null)" class="sheet-1c-quantity" data-ng-class="{'text-danger': (sheet.quantityIn1C < 1)}">Ост. {{sheet.quantityIn1C > 0 ? sheet.quantityIn1C : 0}}л</span></span>
</div>
</div>
<a tabindex="-1" href="{{utilities.getFasadSheetImageById(sheet.id, 'image')}}" data-lightbox="sheet{{sheet.id}}LightBox" class="select-sheet-preview" data-ng-if="sheet.image" data-ng-class="{'no-sheet-thumb' : !sheet.thumb}">
<img src="/img/lightbox/zoom-in.png">
</a>
</div>
<div data-ng-if="!filteredSheets.length" class="col-12">Не найдено результатов по вашему запросу.</div>
</div>
</div>
<div data-ng-show="fasad.activeSheetSelectStep == 2">
<div class="row">
<div class="col-12">
<div class="alert alert-info">
Внимание! Цвет на мониторе может отличаться от реального цвета ДСП. <br/>
Выберите цвет и размер кромки №1 или нажмите "Пропустить", если в поклейке кромок нет необходимости.
</div>
</div>
</div>
<div class="row">
<div id="selectBorder1Id{{border.id}}" class="select-border-data col-12" data-ng-repeat="border in filteredBorders1 = (config.borders | filterData:fasad.searchFilters[2])">
<div data-ng-click="fasad.selectBorderId(1, border)" data-ng-class="{'selected' : (fasad.selectSheetConfigModalData.border1Id == border.id)}">
<div class="row">
<div class="col-8">
<div class="select-border-image" data-ng-if="fasad.isSelectSheetConfigModalTriggered && border.image">
<img data-src="{{utilities.getFasadBorderImageById(border.id)}}" class="img-thumbnail img-lazy">
</div>
<div class="">
<span><b>{{border.label}} {{fasad.checkIfBorderInSheetColor(fasad.selectSheetConfigModalData.sheetId, border.id)}}</b></span>
</div>
</div>
<div class="col-4">
<div class="form-group" data-ng-if="fasad.selectSheetConfigModalData.border1Id == border.id">
<label for="border1HeightSelect">Толщина кромки:</label>
<select class="custom-select" id="border1HeightSelect" data-ng-model="fasad.selectSheetConfigModalData.border1Height" data-ng-options="v.fasad_border_dimension.id as v.fasad_border_dimension.label for v in border.available_dimensions" data-ng-disabled="!fasad.selectSheetConfigModalData.border1Id">
<option value="">Выберите толщину</option>
</select>
</div>
</div>
</div>
</div>
<a tabindex="-1" href="{{utilities.getFasadBorderImageById(border.id)}}" data-lightbox="border1{{border.id}}LightBox" class="select-border-preview" data-ng-if="border.image">
<img src="/img/lightbox/zoom-in.png">
</a>
</div>
<div data-ng-if="!filteredBorders1.length" class="col-12">Не найдено результатов по вашему запросу.</div>
</div>
</div>
<div data-ng-show="fasad.activeSheetSelectStep == 3">
<div class="row">
<div class="col-12">
<div class="alert alert-info">
Внимание! Цвет на мониторе может отличаться от реального цвета ДСП. <br/>
Выберите цвет и размер кромки №2 или нажмите "Пропустить", если в поклейке кромки №2 нет необходимости.
</div>
</div>
</div>
<div class="row">
<div id="selectBorder2Id{{border.id}}" class="select-border-data col-12" data-ng-repeat="border in filteredBorders2 = (config.borders | filterData:fasad.searchFilters[3])">
<div data-ng-click="fasad.selectBorderId(2, border)" data-ng-class="{'selected' : (fasad.selectSheetConfigModalData.border2Id == border.id)}">
<div class="row">
<div class="col-8">
<div class="select-border-image" data-ng-if="fasad.isSelectSheetConfigModalTriggered && border.image">
<img data-src="{{utilities.getFasadBorderImageById(border.id)}}" class="img-thumbnail img-lazy">
</div>
<div class="">
<span><b>{{border.label}} {{fasad.checkIfBorderInSheetColor(fasad.selectSheetConfigModalData.sheetId, border.id)}}</b></span>
</div>
</div>
<div class="col-4">
<div class="form-group" data-ng-if="fasad.selectSheetConfigModalData.border2Id == border.id">
<label for="border2HeightSelect">Толщина кромки:</label>
<select class="custom-select" id="border2HeightSelect" data-ng-model="fasad.selectSheetConfigModalData.border2Height" data-ng-options="v.fasad_border_dimension.id as v.fasad_border_dimension.label for v in border.available_dimensions" data-ng-disabled="!fasad.selectSheetConfigModalData.border2Id">
<option value="">Выберите толщину</option>
</select>
</div>
</div>
</div>
</div>
<a tabindex="-1" href="{{utilities.getFasadBorderImageById(border.id)}}" data-lightbox="border2{{border.id}}LightBox" class="select-border-preview" data-ng-if="border.image">
<img src="/img/lightbox/zoom-in.png">
</a>
</div>
<div data-ng-if="!filteredBorders2.length" class="col-12">Не найдено результатов по вашему запросу.</div>
</div>
</div>
<div data-ng-show="fasad.activeSheetSelectStep == 4">
<div class="row">
<div class="col-12">
<div class="alert alert-info">
Внимание! Цвет на мониторе может отличаться от реального цвета ДСП. <br/>
Проверьте выбранную фактуру листа и конфигурацию кромок и нажмите "Сохранить".
</div>
</div>
</div>
<div class="row">
<label class="col-form-label col-3">Фактура:</label>
<div class="col-9">
<label class="col-form-label">{{fasad.getSelectedSheetLabel(fasad.selectSheetConfigModalData.sheetId)}}</label>
</div>
</div>
<div class="row">
<label class="col-form-label col-3">Размеры листа:</label>
<div class="col-9">
<label class="col-form-label">{{fasad.getSheetFullDimensionsBySheetId(fasad.selectSheetConfigModalData.sheetId)}}</label>
</div>
</div>
<div class="row">
<label class="col-form-label col-3">Кромка 1:</label>
<div class="col-9">
<label class="col-form-label">
<span data-ng-bind-html="fasad.getBorderLabel(fasad.selectSheetConfigModalData.border1Id, fasad.selectSheetConfigModalData.border1Height) | trustAs"></span>
</label>
</div>
</div>
<div class="row">
<label class="col-form-label col-3">Кромка 2:</label>
<div class="col-9">
<label class="col-form-label">
<span data-ng-bind-html="fasad.getBorderLabel(fasad.selectSheetConfigModalData.border2Id, fasad.selectSheetConfigModalData.border2Height) | trustAs"></span>
</label>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-sm btn-secondary"
data-ng-if="fasad.activeSheetSelectStep == 1"
data-ng-disabled="!fasad.selectSheetConfigModalData.sheetId"
data-ng-click="fasad.discardBordersSelectFromStep1()">
Пропустить выбор кромок
</button>
<button class="btn btn-sm btn-success"
data-ng-if="fasad.activeSheetSelectStep == 1"
data-ng-disabled="!fasad.selectSheetConfigModalData.sheetId"
data-ng-click="fasad.setActiveSheetSelectStep(2)">
Перейти к выбору кромок
</button>
<button class="btn btn-sm btn-secondary"
data-ng-if="fasad.activeSheetSelectStep == 2"
data-ng-click="fasad.setActiveSheetSelectStep(1)">
Вернуться к выбору фактуры
</button>
<button class="btn btn-sm btn-warning"
data-ng-if="fasad.activeSheetSelectStep == 2"
data-ng-click="fasad.discardBordersSelectFromStep2()">
Пропустить
</button>
<button class="btn btn-sm btn-success"
data-ng-if="fasad.activeSheetSelectStep == 2"
data-ng-disabled="!fasad.selectSheetConfigModalData.border1Id || !fasad.selectSheetConfigModalData.border1Height"
data-ng-click="fasad.setActiveSheetSelectStep(3)">
Перейти к выбору кромки №2
</button>
<button class="btn btn-sm btn-secondary"
data-ng-if="fasad.activeSheetSelectStep == 3"
data-ng-click="fasad.setActiveSheetSelectStep(2)">
Вернуться к выбору кромки №1
</button>
<button class="btn btn-sm btn-warning"
data-ng-if="fasad.activeSheetSelectStep == 3"
data-ng-click="fasad.discardBorder2SelectFromStep3()">
Пропустить
</button>
<button class="btn btn-sm btn-success"
data-ng-if="fasad.activeSheetSelectStep == 3"
data-ng-disabled="!fasad.selectSheetConfigModalData.border2Id || !fasad.selectSheetConfigModalData.border2Height"
data-ng-click="fasad.setActiveSheetSelectStep(4)">
Завершить настройку
</button>
<button class="btn btn-sm btn-secondary"
data-ng-if="fasad.activeSheetSelectStep == 4"
data-ng-click="fasad.setActiveSheetSelectStep(1)">
Вернуться к выбору фактуры
</button>
<button class="btn btn-sm btn-success"
data-ng-if="fasad.activeSheetSelectStep == 4"
data-ng-disabled="!fasad.selectSheetConfigModalData.sheetId"
data-ng-click="fasad.confirmSheetSelection()">
Сохранить
</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="detailCornersConfigModal" data-backdrop="static">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
Конфигурация детали №{{fasad.detailCornersConfigModalData.uiOrderNum}}
</h5>
<div>
<button type="button" class="btn"
data-ng-class="{true:'btn-info', false:'btn-light'}[fasad.detailCornersConfigModalData.activeTab == cornersConfigButton.id]"
data-ng-repeat="cornersConfigButton in config.cornersConfigButtons"
data-ng-click="fasad.setDetailCornersConfigActiveTab(cornersConfigButton.id)">
{{cornersConfigButton.label}}
</button>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
</div>
<div class="modal-body">
<div class="row" data-ng-if="fasad.detailCornersConfigModalData.activeTab == 'corners'">
<div class="detailCornersCenter">
<div class="detailCornersBox" data-ng-class="fasad.getDetailCornersConfigClass()">
<div class="detailCornersHorizontalDivider"></div>
<div class="detailCornersVerticalDivider"></div>
<div data-ng-repeat="cornerPosition in config.cornerPositions" class="cornerConfigRadiusLabel cornerConfigRadiusLabel-{{cornerPosition.id}}">
<span>R={{fasad.detailCornersConfigModalData.corners[cornerPosition.id].radius}}</span>
</div>
<div data-ng-repeat="cornerPosition in config.cornerPositions" class="cornerConfigCutLabel cornerConfigCutLabel-{{cornerPosition.id}}">
<span class="cornerConfigCutXLabel">x={{fasad.detailCornersConfigModalData.corners[cornerPosition.id].x}}</span>
<span class="cornerConfigCutYLabel">y={{fasad.detailCornersConfigModalData.corners[cornerPosition.id].y}}</span>
</div>
<div data-ng-repeat="cornerPosition in config.cornerPositions" class="cornerConfigCutLabel cornerConfigCutEdge-{{cornerPosition.id}}"></div>
</div>
<div class="detailCornersWidth">{{fasad.detailCornersConfigModalData.detail.width}} мм</div>
<div class="detailCornersHeight">{{fasad.detailCornersConfigModalData.detail.height}} мм</div>
</div>
<div data-ng-repeat="cornerPosition in config.cornerPositions" class="detailCorner detailCorner-{{cornerPosition.id}}">
<label>{{cornerPosition.label}}</label>
<div class="">
<button type="button" class="btn"
data-ng-class="{true:'active btn-primary', false:'btn-light'}[fasad.detailCornersConfigModalData.corners[cornerPosition.id].value == cornerValue.id]"
data-ng-repeat="cornerValue in config.cornerValues"
data-ng-disabled="fasad.isDetailCornerConfigValueSelectDisabled(cornerPosition.id)"
data-ng-click="fasad.setDetailCornerConfigValue(cornerPosition.id, cornerValue.id)">
{{cornerValue.label}}
</button>
</div>
<div class="detailCorner-config col-12">
<div class="row" data-ng-if="fasad.detailCornersConfigModalData.corners[cornerPosition.id].value == 'none'"></div>
<div class="row" data-ng-if="fasad.detailCornersConfigModalData.corners[cornerPosition.id].value == 'radius'">
<div class="input-group col-sm-12 col-lg-9">
<div class="input-group-prepend">
<label class="input-group-text" for="detailCornersConfigModalData{{cornerPosition.id}}Radius">Радиус, мм</label>
</div>
<input id="detailCornersConfigModalData{{cornerPosition.id}}Radius"
class="form-control form-control-sm" type="number"
data-ng-model="fasad.detailCornersConfigModalData.corners[cornerPosition.id].radius"
data-digits-only pattern="[0-9]*">
</div>
</div>
<div class="row" data-ng-if="fasad.detailCornersConfigModalData.corners[cornerPosition.id].value == 'cut'">
<div class="input-group col-sm-12 col-lg-6 corners-config-left-group">
<div class="input-group-prepend">
<label class="input-group-text" for="detailCornersConfigModalData{{cornerPosition.id}}X">x, мм</label>
</div>
<input id="detailCornersConfigModalData{{cornerPosition.id}}X"
class="form-control form-control-sm" type="number"
data-ng-model="fasad.detailCornersConfigModalData.corners[cornerPosition.id].x"
data-ng-disabled="fasad.isDetailCornerConfigInputDisabled(cornerPosition.id)"
data-digits-only pattern="[0-9]*">
</div>
<div class="input-group col-sm-12 col-lg-6 corners-config-right-group">
<div class="input-group-prepend">
<label class="input-group-text" for="detailCornersConfigModalData{{cornerPosition.id}}Y">y, мм</label>
</div>
<input id="detailCornersConfigModalData{{cornerPosition.id}}X" class="form-control form-control-sm" type="number"
data-ng-model="fasad.detailCornersConfigModalData.corners[cornerPosition.id].y"
data-ng-disabled="fasad.isDetailCornerConfigInputDisabled(cornerPosition.id)"
data-digits-only pattern="[0-9]*" >
</div>
<div class="input-group col-sm-12 col-lg-12"
data-ng-class="{'disabled': fasad.isDetailCornerConfigDiagonalCheckboxDisabled(cornerPosition.id)}"
data-ng-click="fasad.onDetailCornersConfigDiagonalCutClick(cornerPosition.id)">
<div class="input-group-prepend">
<div class="input-group-text with-checkbox">
<i class="fa fa-square-o" data-ng-if="!fasad.detailCornersConfigModalData.corners[cornerPosition.id].diagonalCut"></i>
<i class="fa fa-check-square-o" data-ng-if="fasad.detailCornersConfigModalData.corners[cornerPosition.id].diagonalCut"></i>
</div>
</div>
<label class="col-form-label form-control">Диагональный срез</label>
</div>
</div>
</div>
</div>
</div>
<div class="row" data-ng-if="fasad.detailCornersConfigModalData.activeTab == 'grooves'" data-ng-class="fasad.getDetailGroovesConfigClass()">
<div class="detailGroovesCenter">
<div class="detailGroovesBox" data-ng-class="fasad.getDetailGroovesConfigClass()">
<div data-ng-repeat="groovePosition in config.groovePositions" class="grooveConfigLabel grooveConfigLabel-{{groovePosition.id}}">
</div>
</div>
<div class="detailGroovesWidth">{{fasad.detailCornersConfigModalData.detail.width}} мм</div>
<div class="detailGroovesHeight">{{fasad.detailCornersConfigModalData.detail.height}} мм</div>
</div>
<div data-ng-repeat="groovePosition in config.groovePositions" class="detailGroove detailGroove-{{groovePosition.id}}">
<div class="detailGroove-config col-12">
<div class="row">
<div class="input-group"
data-ng-class="[{true:'col-12', false:'offset-4 col-4'}[(groovePosition.id == 'left') || (groovePosition.id == 'right')], {'disabled': fasad.isDetailGrooveConfigCheckboxDisabled(groovePosition.id)}]"
data-ng-click="fasad.onDetailGroovesConfigGrooveEnabledClick(groovePosition.id)">
<div class="input-group-prepend">
<div class="input-group-text input-group-checkbox-text with-checkbox">
<i class="fa fa-square-o" data-ng-if="!fasad.detailCornersConfigModalData.grooves[groovePosition.id].enabled"></i>
<i class="fa fa-check-square-o" data-ng-if="fasad.detailCornersConfigModalData.grooves[groovePosition.id].enabled"></i>
</div>
</div>
<label class="col-form-label form-control">{{groovePosition.label2}}</label>
</div>
</div>
<div class="row row-col-4 first-row-col-4" data-ng-if="fasad.detailCornersConfigModalData.grooves[groovePosition.id].enabled">
<div class="input-group col-12">
<div class="input-group-prepend">
<label class="input-group-text" for="detailGroovesConfigModalData{{groovePosition.id}}Offset">Отступ от края, мм</label>
</div>
<input id="detailGroovesConfigModalData{{groovePosition.id}}Offset"
class="form-control form-control-sm" type="number"
data-ng-model="fasad.detailCornersConfigModalData.grooves[groovePosition.id].offset"
data-digits-only pattern="[0-9]*">
</div>
</div>
<div class="row row-col-4 second-row-col-4" data-ng-if="fasad.detailCornersConfigModalData.grooves[groovePosition.id].enabled">
<div class="input-group col-12">
<div class="input-group-prepend">
<label class="input-group-text" for="detailGroovesConfigModalData{{groovePosition.id}}Depth">Глубина пропила, мм</label>
</div>
<input id="detailGroovesConfigModalData{{groovePosition.id}}Depth"
class="form-control form-control-sm" type="number"
data-ng-model="fasad.detailCornersConfigModalData.grooves[groovePosition.id].depth"
data-digits-only pattern="[0-9]*">
</div>
</div>
<div class="row row-col-4 third-row-col-4" data-ng-if="fasad.detailCornersConfigModalData.grooves[groovePosition.id].enabled">
<div class="input-group col-12">
<div class="input-group-prepend">
<label class="input-group-text" for="detailGroovesConfigModalData{{groovePosition.id}}Width">Ширина пропила, мм</label>
</div>
<input id="detailGroovesConfigModalData{{groovePosition.id}}Width"
class="form-control form-control-sm" type="number"
data-ng-model="fasad.detailCornersConfigModalData.grooves[groovePosition.id].width"
data-digits-only pattern="[0-9]*">
</div>
</div>
</div>
</div>
</div>
<br/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Закрыть</button>
<button class="btn btn-sm btn-success" data-ng-click="fasad.saveDetailCornersConfig()">Сохранить</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="detailLabelConfigModal" data-backdrop="static">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
Наименование детали №{{fasad.detailLabelConfigModalData.uiOrderNum}}
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12">
<input class="form-control" type="text" data-ng-model="fasad.detailLabelConfigModalData.label" maxlength="20">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Закрыть</button>
<button class="btn btn-sm btn-success" data-ng-click="fasad.saveDetailLabelConfig()">Сохранить</button>
</div>
</div>
</div>
</div>
</div>
<?php $this->Assign('title', $siteTitle);?>
@@ -0,0 +1,24 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title><?= $this->fetch('title') ?></title>
</head>
<body>
<?= $this->fetch('content') ?>
</body>
</html>
@@ -0,0 +1,16 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
echo $this->fetch('content');
+16
View File
@@ -0,0 +1,16 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
echo $this->fetch('content');
+72
View File
@@ -0,0 +1,72 @@
<?php use Cake\Core\Configure; ?>
<!DOCTYPE html>
<html class="horizontal-only">
<head>
<?= $this->Html->charset() ?>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title><?= $this->fetch('title');?></title>
<?php echo $this->element("favicon"); ?>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<?php if (empty($isRejectedBrowser)) {
$release = "?v".Configure::read('Release');
$cssList = [
"bootstrap.min.css",
"lightbox.min.css",
"fontawesome.custom.css",
"styles.css",
];
foreach ($cssList as $key => $css) {
$cssList[$key] = $css.$release;
}
echo $this->Html->css($cssList);
} else {
echo $this->Html->css('jquery.reject');
} ?>
<?= $this->fetch('meta'); ?>
<?= $this->fetch('css'); ?>
<?= $this->fetch('script'); ?>
<base href="/">
</head>
<body data-ng-app="supersamApp">
<?php if (empty($isRejectedBrowser)) {
echo $this->fetch('content');
$scripts = [
"jquery.min.js",
"popper.min.js",
"bootstrap.min.js",
"angular.min.js",
"angular-local-storage.min.js",
"angular-mask.min.js",
"jquery.scrollTo.min.js",
"lightbox.min.js",
"lozad.min.js",
"custom.js",
"config.js",
"directives.js",
"calcDoorsController.js"
];
foreach ($scripts as $key => $script) {
$scripts[$key] = $script.$release;
}
echo $this->Html->script($scripts);
echo $this->element('common_data');
echo $this->element('cookie_consent');
} else {
echo $this->Html->script("jquery-1.min.js");
echo $this->Html->script("jquery.reject.js");
echo $this->Html->script("jquery.reject-config.js");
}
?>
</body>
</html>
+71
View File
@@ -0,0 +1,71 @@
<?php use Cake\Core\Configure; ?>
<!DOCTYPE html>
<html class="horizontal-only">
<head>
<?= $this->Html->charset() ?>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title><?= $this->fetch('title');?></title>
<?php echo $this->element("favicon"); ?>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<?php if (empty($isRejectedBrowser)) {
$release = "?v".Configure::read('Release');
$cssList = [
"bootstrap.min.css",
"lightbox.min.css",
"fontawesome.custom.css",
"styles.css",
];
foreach ($cssList as $key => $css) {
$cssList[$key] = $css.$release;
}
echo $this->Html->css($cssList);
} else {
echo $this->Html->css('jquery.reject');
} ?>
<?= $this->fetch('meta'); ?>
<?= $this->fetch('css'); ?>
<?= $this->fetch('script'); ?>
<base href="/">
</head>
<body data-ng-app="supersamApp">
<?php if (empty($isRejectedBrowser)) {
echo $this->fetch('content');
$scripts = [
"jquery.min.js",
"popper.min.js",
"bootstrap.min.js",
"angular.min.js",
"angular-local-storage.min.js",
"angular-mask.js",
"jquery.scrollTo.min.js",
"lightbox.min.js",
"lozad.min.js",
"custom.js",
"config.js",
"directives.js",
"cutController.js"
];
foreach ($scripts as $key => $script) {
$scripts[$key] = $script.$release;
}
echo $this->Html->script($scripts);
echo $this->element('common_data');
echo $this->element('cookie_consent');
} else {
echo $this->Html->script("jquery-1.min.js");
echo $this->Html->script("jquery.reject.js");
echo $this->Html->script("jquery.reject-config.js");
} ?>
</body>
</html>
+57
View File
@@ -0,0 +1,57 @@
<?php use Cake\Core\Configure; ?>
<!DOCTYPE html>
<html>
<head>
<?= $this->Html->charset() ?>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title><?= $this->fetch('title');?></title>
<?php echo $this->element("favicon"); ?>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<?php if (empty($isRejectedBrowser)) {
$release = "?v".Configure::read('Release');
$cssList = [
"delivery.css",
];
foreach ($cssList as $key => $css) {
$cssList[$key] = $css.$release;
}
echo $this->Html->css($cssList);
} else {
echo $this->Html->css('jquery.reject');
} ?>
<?= $this->fetch('meta'); ?>
<?= $this->fetch('css'); ?>
<?= $this->fetch('script'); ?>
<base href="/">
</head>
<body>
<?php if (empty($isRejectedBrowser)) {
$scripts = [
"jquery.min.js",
];
foreach ($scripts as $key => $script) {
$scripts[$key] = $script.$release;
}
$scripts[] = "https://api-maps.yandex.ru/2.1/?apikey=".$apiKey."&lang=ru_RU";
echo $this->Html->script($scripts);
echo $this->element('delivery_'.$delivery['slug']);
echo $this->fetch('content');
echo $this->element('cookie_consent');
} else {
echo $this->Html->script("jquery-1.min.js");
echo $this->Html->script("jquery.reject.js");
echo $this->Html->script("jquery.reject-config.js");
} ?>
</body>
</html>
+54
View File
@@ -0,0 +1,54 @@
<?php use Cake\Core\Configure; ?>
<!DOCTYPE html>
<html>
<head>
<?= $this->Html->charset() ?>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>СуперСам</title>
<?php echo $this->element("favicon"); ?>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<?php if (empty($isRejectedBrowser)) {
$release = "?v".Configure::read('Release');
$cssList = [
"bootstrap.min.css",
"styles.css",
];
foreach ($cssList as $key => $css) {
$cssList[$key] = $css.$release;
}
echo $this->Html->css($cssList);
} else {
echo $this->Html->css('jquery.reject');
} ?>
<?= $this->fetch('meta'); ?>
<?= $this->fetch('css'); ?>
<?= $this->fetch('script'); ?>
<base href="/">
</head>
<body>
<?php if (empty($isRejectedBrowser)) {
$scripts = [
"jquery.min.js",
];
foreach ($scripts as $key => $script) {
$scripts[$key] = $script.$release;
}
echo $this->Html->script($scripts);
echo $this->fetch('content');
echo $this->element('cookie_consent');
} else {
echo $this->Html->script("jquery-1.min.js");
echo $this->Html->script("jquery.reject.js");
echo $this->Html->script("jquery.reject-config.js");
} ?>
</body>
</html>
Loaded 100 of 17284 files, more files were not shown because too many files have changed in this diff. Show more