<?php
namespace App\Service;
use App\Entity\NotificationsConfig;
use App\Entity\User;
use App\Entity\Company;
use App\Entity\License;
use App\Entity\LicenseCompany;
use App\Entity\Order;
use App\Entity\Part;
use App\Entity\Library;
use App\Entity\Equipment;
use App\Entity\Material;
use App\Entity\Notifications;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\Security\Core\Security;
use Endroid\QrCode\Builder\Builder;
use Endroid\QrCode\Encoding\Encoding;
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh;
use Endroid\QrCode\Label\Alignment\LabelAlignmentCenter;
use Endroid\QrCode\Label\Font\NotoSans;
use Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeMargin;
use Endroid\QrCode\Writer\PngWriter;
use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Path;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
class UtilService
{
private $em;
private $logger;
private $params;
private $user;
private $mailer;
private $emailApp;
private $rootDir;
private $env;
private $translator;
public function __construct(
EntityManagerInterface $em,
LoggerInterface $logger,
ParameterBagInterface $params,
TranslatorInterface $translator,
Security $security,
MailerInterface $mailer,
string $emailApp,
string $projectDir,
string $environment)
{
$this->em = $em;
$this->logger = $logger;
$this->params = $params;
$this->user = $security->getUser();
$this->mailer = $mailer;
$this->emailApp = $emailApp;
$this->rootDir = $projectDir;
$this->env = $environment;
$this->translator = $translator;
}
public function upload($uploadDir, $file, $filename)
{
try {
$file->move($uploadDir, $filename);
} catch (FileException $e){
$this->logger->error('failed to upload image: ' . $e->getMessage());
throw new FileException('Failed to upload file');
}
}
public function cutText(?string $txt, $limit=255,$dots=true){
if($txt && strlen($txt)>$limit){
$txt = substr($txt,0,$limit-4);
if($dots){
$txt.=' ...';
}
}
return $txt;
}
/**
* @param string $msg
* @param int $type [1:info,2:notice,3:debug,4:error,5:warning,6:alert,7:critical]
*/
public function setLog(string $msg,$type=1){
switch ($type){
case 1: $this->logger->info($msg); break;
case 2: $this->logger->notice($msg); break;
case 3: $this->logger->debug($msg); break;
case 4: $this->logger->error($msg); break;
case 5: $this->logger->warning($msg); break;
case 6: $this->logger->alert($msg); break;
case 7: $this->logger->critical($msg); break;
}
}
public function generateQrCode(String $data,String $label='', $size=300, $margin=10, $base64=true){
$builder = Builder::create()
->writer(new PngWriter())
->writerOptions([])
->data($data)
->labelText($label)
->encoding(new Encoding('UTF-8'))
->errorCorrectionLevel(new ErrorCorrectionLevelHigh())
->size($size)
->margin($margin)
->roundBlockSizeMode(new RoundBlockSizeModeMargin())
->labelFont(new NotoSans(20))
->labelAlignment(new LabelAlignmentCenter());
if($base64){
$file = $builder->build();
return "data:image/png;base64,".base64_encode($file->getString());
}
else{
return $builder->build();
}
}
public function getLicenseField(Company $company, string $field){
$value = null;
$fieldExplode = explode('_',$field);
$get = 'get';
foreach($fieldExplode as $part){
$get.= ucfirst($part);
}
$license =$this->em->getRepository(LicenseCompany::class)->findOneBy(['company'=>$company,'state'=>2]);
if(!empty($license)){
$value = $license->getLicense()->$get();
}
return $value;
}
public function getCompanyUsersAvailable(Company $company){
$usersAvailable = [
'admin'=>['total'=>0,'in_use'=>0,'available'=>0,'role'=>'ROLE_ADMIN','label'=>'Superadministrador'],
'chief'=>['total'=>0,'in_use'=>0,'available'=>0,'role'=>'ROLE_CHIEF','label'=>'Administrador'],
'user'=>['total'=>0,'in_use'=>0,'available'=>0,'role'=>'ROLE_USER','label'=>'General'],
'client'=>['total'=>0,'in_use'=>0,'available'=>0,'role'=>'ROLE_CLIENT','label'=>'Cliente'],
'supplier'=>['total'=>0,'in_use'=>0,'available'=>0,'role'=>'ROLE_SUPPLIER','label'=>'Proveedor'],
'supplier_chief'=>['total'=>0,'in_use'=>0,'available'=>0,'role'=>'ROLE_SUPPLIER_CHIEF','label'=>'Proveedor (Administrador)'],
'all'=>['total'=>0,'in_use'=>0,'available'=>0],
];
foreach($usersAvailable as $type=>$data){
if($type=='all')break;
$total = intval($this->getLicenseField($company,'user_'.$type));
$inUse = 0;
$available = 0;
if($total>0){
$users = $this->em->getRepository(User::class)->findBy(['company'=>$company,'role'=>$data['role']]);
$inUse = $users ? count($users) : 0;
$available=($total-$inUse)>0 ? $total-$inUse : 0;
}
$usersAvailable[$type]['total']=$total;
$usersAvailable[$type]['in_use']=$inUse;
$usersAvailable[$type]['available']=$available;
$usersAvailable['all']['total']+=$total;
$usersAvailable['all']['in_use']+=$inUse;
$usersAvailable['all']['available']+=$available;
}
return $usersAvailable;
}
public function getCompanySuscriptionAvailable(LicenseCompany $licenseActive){
$company = $licenseActive->getCompany();
$users = $this->getCompanyUsersAvailable($company);
$partTotal = $this->getLicenseField($company,'part_num');
$partInUse = count($this->em->getRepository(Part::class)->findBy(['company'=>$company]));
$libTotal = $this->getLicenseField($company,'part_library');
$libInUse = count($this->em->getRepository(Library::class)->findBy(['company'=>$company]));
$suscriptionData = [
'license'=>$licenseActive->getLicense(),
'users'=>$users,
'parts'=>['total'=>$partTotal,'in_use'=>$partInUse,'available'=>($partTotal-$partInUse>0?$partTotal-$partInUse:0)],
'libraries'=>['total'=>$libTotal,'in_use'=>$libInUse,'available'=>($libTotal-$libInUse>0?$libTotal-$libInUse:0)],
];
return $suscriptionData;
}
public function getCompanyFileMaxsizes(Company $company){
$fileMaxsizes = ['cad'=>0,'stl'=>0,'fab'=>0,'ver'=>0,'2d'=>0,'others'=>0,'budget'=>0,'report'=>0];
foreach($fileMaxsizes as $field=>$num){
if(in_array($field,['others','budget','report'])){
$fileMaxsizes[$field] = intval(round($this->getLicenseField($company,'maxsize_'.$field)/5));
}
else{
$fileMaxsizes[$field] = intval($this->getLicenseField($company,'maxsize_'.$field));
}
if($fileMaxsizes[$field]<1){
$fileMaxsizes[$field]=1;
}
}
return $fileMaxsizes;
}
public function getActiveLicense(Company $company){
$license = null;
$lc =$this->em->getRepository(LicenseCompany::class)->findOneBy(['company'=>$company,'state'=>2]);
if(!empty($lc)){
$license = $lc->getLicense();
}
return $license;
}
public function getLicenseData(LicenseCompany $licenseActive){
$trans = $this->translator;
$license = $licenseActive->getLicense();
$licenseData = [
'general' => [
'label' => $trans->trans('General'),
'fields' => [
'id'=>['label'=> $trans->trans('Id'),'data'=>$licenseActive->getId(),'type'=>'integer'],
'name'=>['label'=> $trans->trans('Nombre'),'data'=>$license->getName(),'type'=>'string'],
'added'=>['label'=> $trans->trans('Alta'),'data'=>$licenseActive->getDateAdded()->format('d-m-Y H:i'),'type'=>'string'],
'start'=>['label'=> $trans->trans('Inicio'),'data'=>$licenseActive->getDateStart()->format('d-m-Y'),'type'=>'string'],
'end'=>['label'=> $trans->trans('Fin'),'data'=>$licenseActive->getDateEnd()->format('d-m-Y'),'type'=>'string'],
]
],
'users' => [
'label' => $trans->trans('Usuarios'),
'fields' => [
'user_client'=>['label'=> $trans->trans('Clientes')],
'user_user'=>['label'=> $trans->trans('Generales')],
'user_chief'=>['label'=> $trans->trans('Administradores')],
'user_supplier'=>['label'=> $trans->trans('Proveedores')],
'user_supplier_chief'=>['label'=> $trans->trans('Proveedores (adm)')]
],
],
'inventory' => [
'label' => $trans->trans('Inventario digital'),
'fields' => [
'part_library_type'=>['label'=> $trans->trans('Tipo')],
'part_num'=>['label'=> $trans->trans('Piezas')],
'part_library'=>['label'=> $trans->trans('Bibliotecas')],
'part_version'=>['label'=> $trans->trans('Versionado')],
'part_locked'=>['label'=> $trans->trans('Bloqueado')],
'maxsize_cad'=>['label'=> $trans->trans('MS-CAD')],
'maxsize_stl'=>['label'=> $trans->trans('MS-STL')],
'maxsize_fab'=>['label'=> $trans->trans('MS-FAB')],
'maxsize_ver'=>['label'=> $trans->trans('MS-VERIF')],
'maxsize_2d'=>['label'=> $trans->trans('MS-2D')],
'maxsize_others'=>['label'=> $trans->trans('MS-OTHERS')]
],
],
'production' => [
'label' => $trans->trans('Gestión de producción'),
'fields' => [
'wo_external'=>['label'=> $trans->trans('Externas')],
'wo_internal'=>['label'=> $trans->trans('Internas')],
'marketplace'=>['label'=> $trans->trans('Marketplace')],
'wo_custom'=>['label'=> $trans->trans('Personalizable')],
'search_supplier_cif'=>['label'=> $trans->trans('Búsqueda CT por CIF')],
'search_supplier_marketplace'=>['label'=> $trans->trans('Búsqueda en MP')],
'maxsize_budget'=>['label'=> $trans->trans('MS-BUDGET')],
'maxsize_report'=>['label'=> $trans->trans('MS-REPORT')]
],
],
'utilities'=>[
'label' => $trans->trans('Utilidades'),
'fields' => [
'dashboard'=>['label'=> $trans->trans('Dashboard')],
'massive_upload'=>['label'=>'Carga masiva'],
'advanced_filter_search'=>['label'=>'Filtro y búsquedas avanzada'],
'configurable_listings'=>['label'=>'Listados configurables'],
'grid_display'=>['label'=>'Vista cuadrícula'],
'hosting'=>['label'=> $trans->trans('Hosting')],
'multi_select'=>['label'=> $trans->trans('Selección múltiple')],
'graphic_custom'=>['label'=> $trans->trans('Personalización gráfica')],
'part_comparison'=>['label'=> $trans->trans('Comparación piezas')],
],
]
];
$selects = [
'part_library_type' => [0=>'-',1=>$trans->trans('Simple'),2=>$trans->trans('Árbol')],
'wo_external' => [0=>'-',1=>$trans->trans('Recibir'),2=>$trans->trans('Enviar'),3=>$trans->trans('Recibir/enviar')],
'dashboard' => [0=>'-',1=>$trans->trans('Simple'),2=>$trans->trans('Personalizable')],
'hosting' => [0=>'-',1=>$trans->trans('Cloud'),2=>$trans->trans('Cloud/On-Premise')]
];
foreach($licenseData as $catName=>$catData){
if($catName!='general'){
foreach($catData['fields'] as $field=>$data){
$fieldExplode = explode('_',$field);
$get = 'get';
foreach($fieldExplode as $part){
$get.= ucfirst($part);
}
try {
$value = $license->$get();
$value = array_key_exists($field,$selects) && !empty($selects[$field][$value]) ? $selects[$field][$value] : $value;
} catch (\Throwable $e) {
$value='-';
}
$licenseData[$catName]['fields'][$field]['data']=$value;
$licenseData[$catName]['fields'][$field]['type']=gettype($value);
}
}
}
return $licenseData;
}
//Se elimina toda la actividad de la empresa.
//Solo quedan los datos de la empresa y las licencias contratadas
public function rmAccount(Company $company, $entities=['library','user']){
$em = $this->em;
$fs = new Filesystem();
$rootDir = $this->rootDir;
$ds = DIRECTORY_SEPARATOR;
$result = ['error'=>false,'msg'=>''];
try{
//Ordenes de trabajo
$orders = $em->getRepository(Order::class)->findBy(['company'=>$company]);
if(!empty($orders)){
$ordersDir = $rootDir.$ds.'public'.$ds.'orders'.$ds;
foreach($orders as $order){
$files = explode(',',$order->getFilesSupplier());
if(!empty($files)){
foreach($files as $file){
if($fs->exists($ordersDir.$file) && !empty($file)){
$fs->remove($ordersDir.$file);
}
}
}
$em->remove($order);
}
$em->flush();
}
//Piezas
$parts = $em->getRepository(Part::class)->findBy(['company'=>$company]);
if(!empty($parts)){
$partsDir = $rootDir.$ds.'public'.$ds.'parts'.$ds;
$excelDir = $partsDir.'excel'.$ds;
foreach($parts as $part){
$getters = ['getFilename','getFileCad','getFileStl','getFile2d','getFileFab','getFileVerif','getFile01','getFile02','getFile03','getFile04','getFile05','getBulkImportFile'];
$files = [];
$excelFile = '';
foreach($getters as $get){
if($part->$get()){
if($get=='getBulkImportFile'){
$excelFile=$part->$get();
}
else{
$files[]=$part->$get();
}
}
}
if(!empty($files)){
foreach($files as $file){
if($fs->exists($partsDir.$file) && !empty($file)){
$fs->remove($partsDir.$file);
}
}
}
if(!empty($excelFile)){
if($fs->exists($excelDir.$excelFile)){
$fs->remove($excelDir.$excelFile);
}
}
$em->remove($part);
}
$em->flush();
}
//Equipos
$equipments = $em->getRepository(Equipment::class)->findBy(['company'=>$company]);
if(!empty($equipments)){
$dir = $rootDir.$ds.'public'.$ds.'supplier';
$getters = ['getImages','getFiles'];
foreach($equipments as $entity){
$filenames = [];
foreach($getters as $getter){
if($entity->$getter()){
$filenames = array_merge($filenames, explode(',',$entity->$getter()));
}
}
foreach($filenames as $fileToRemove){
if($fs->exists($dir.$ds.$fileToRemove) && !empty($fileToRemove)){
$fs->remove($dir.$ds.$fileToRemove);
}
}
$em->remove($entity);
}
$em->flush();
}
//Materiales
$materials = $em->getRepository(Material::class)->findBy(['company'=>$company]);
if(!empty($materials)){
$dir = $rootDir.$ds.'public'.$ds.'supplier';
$getters = ['getImages','getTechnicalFiles','getOtherFiles'];
foreach($materials as $entity){
$filenames = [];
foreach($getters as $getter){
if($entity->$getter()){
$filenames = array_merge($filenames, explode(',',$entity->$getter()));
}
}
foreach($filenames as $fileToRemove){
if($fs->exists($dir.$ds.$fileToRemove) && !empty($fileToRemove)){
$fs->remove($dir.$ds.$fileToRemove);
}
}
$em->remove($entity);
}
$em->flush();
}
//Bibliotecas / usuarios / proveedores [user=delete:cascade]
if(in_array('library', $entities)){
$em->createQuery("DELETE App:Library e WHERE e.company = {$company->getId()}")->execute();
}
if(in_array('user', $entities)){
$em->createQuery("DELETE App:User e WHERE e.company = {$company->getId()}")->execute();
}
return $result;
}
catch (IOExceptionInterface $e) {
$msg = "An error occurred while creating your directory at ".$e->getPath();
$this->logger->error($msg);
return ['error'=>true,'msg'=>$msg];
}
catch (\Doctrine\DBAL\Exception $e){
$msg = "An error has occurred trying to remove company data - Error: {$e->getMessage()}. Line : {$e->getLine()}";
$this->logger->error($msg);
return ['error'=>true,'msg'=>$msg];
}
catch (\Exception $e){
$msg = "An error has occurred trying to remove company data - Error: {$e->getMessage()}. Line : {$e->getLine()}";
$this->logger->error($msg);
return ['error'=>true,'msg'=>$msg];
}
}
public function email($to, string $subject, array $context, string $template, $from=null, ?array $addTo=null){
$result = ['error'=>false,'msg'=>''];
$from = $from ? $from : $this->emailApp;
$email = (new TemplatedEmail())
->from(new Address($from, 'Addvance3D'))
->to($to)
->subject($subject)
->htmlTemplate($template)
->context($context);
if($addTo){
foreach($addTo as $to){
$email->addTo($to);
}
}
try {
$this->mailer->send($email);
return $result;
} catch (TransportExceptionInterface $e) {
return ['error'=>true,'msg'=>$e->getMessage()];
}
}
public function notifications(Company $company, int $group, array $data, ?User $author=null, ?int $idForeign=null){
$message = !empty($data['message']) ? $data['message'] : '';
$messageAux = !empty($data['message_aux']) ? $data['message_aux'] : '';
$name = !empty($data['name']) ? $data['name'] : '';
$reference = !empty($data['reference']) ? $data['reference'] : '';
$path_name = !empty($data['path_name']) ? $data['path_name'] : '';
$path_parameters = !empty($data['path_parameters']) ? $data['path_parameters'] : '';
$n = new Notifications;
$n->setCompany($company);
$n->setCreatedAt(new \DateTime());
$n->setGroup($group);
$n->setUser($author);
$n->setMessage($this->cutText($message,500));
$n->setMessageAux($this->cutText($messageAux));
$n->setName($name);
$n->setReference($reference);
$n->setPathName($path_name);
$n->setPathParameters($path_parameters);
$n->setIdForeign($idForeign);
$this->em->persist($n);
$this->em->flush();
//Notificaciones por email
$usersCompany = $this->em->getRepository(User::class)->findBy(['company'=>$company]);
switch($group){
case 1: $getGroup = 'getGroupUser'; break;
case 2: $getGroup = 'getGroupPart'; break;
case 3: $getGroup = 'getGroupOrder'; break;
case 4: $getGroup = 'getGroupCollabExt'; break;
case 5: $getGroup = 'getGroupLibrary'; break;
default : $getGroup = 'getGroupUser';
}
$addTo = [];
foreach($usersCompany as $user){
if($user->getNotificationsConfig() && $user->getNotificationsConfig()->$getGroup()==2){
$addTo[]=$user->getEmail();
}
}
if($addTo){
$to = array_shift($addTo);
if($messageAux){
$context = ['msg' =>'<p>'.$this->translator->trans($message,['{aux}'=>$messageAux]). '</p>'];
}
else{
$context = ['msg' =>'<p>'.$this->translator->trans($message). '</p>'];
}
$this->email($to, $this->translator->trans('Notificaciones'), $context, 'common/email.html.twig', null, $addTo);
}
}
public function getCssCorporateColor(Company $company){
$color = $company->getCorporateColor();
$css = <<<EOT
<style>
/*Top-menu*/
.topbar {
background: #color# !important;
}
/*Left-menu*/
.sidebar-nav > ul > li.active > a {
border-color: #color#;
}
.sidebar-nav ul li a.active i, .sidebar-nav ul li a:hover i {
color: #color#;
}
/*Buttons*/
.bg-primary {
background-color: #color# !important;
}
.btn-info, .btn-info:hover, .btn-primary, .btn-primary:hover, .dt-buttons .dt-button, .btn-success {
background: #color# !important;
background-color: #color# !important;
border: 1px solid #color#;
}
.btn-inverse {
border: 1px solid #color#;
}
.btn-inverse.active, .btn-inverse.focus, .btn-inverse:active, .btn-inverse:focus, .btn-inverse:hover, .btn-inverse:hover, .open>.dropdown-toggle.btn-inverse {
border: 1px solid #color#;
}
.btn-inverse.active, .btn-inverse.focus, .btn-inverse:active, .btn-inverse:focus, .btn-inverse:hover, .btn-inverse:hover, .open>.dropdown-toggle.btn-inverse {
background-color: #AAA;
}
.btn-success.disabled:hover, .btn-success:hover {
background: #color#;
border: 1px solid #color#;
}
/*Forms and tables*/
.paginate_button.current, .paginate_button:hover {
background-color: #color# !important;
border: 1px solid #color# !important;
color: white !important;
}
/*Index*/
#notifications_header .card-text {
color: #color# !important;
}
/*Others*/
.text-primary {
color: #color# !important;
}
.profile-tab li a.nav-link.active, .customtab li a.nav-link.active {
border-bottom: 2px solid #color#;
color: #color#;
}
.msg_txt_end {
border-radius: 15px;
padding: 5px 10px;
border: 1px solid #color# !important;
font-size: 14px;
}
.dropdown-item.active {
background: #color# !important;
color: white;
}
</style>
EOT;
if($color){
return str_replace('#color#',$company->getCorporateColor(),$css);
}
return '';
}
public function fromHexToRgb(string $hex):string
{
$corporateColorArray = str_split(str_replace('#','',$hex), 2);
$corporateColorRGB = [];
$rgb = '';
if(count($corporateColorArray)===3){
foreach($corporateColorArray as $cc){
$one = (int) str_replace(['A','B','C','D','E','F'],[10,11,12,13,14,15],substr(strtoupper($cc),0,1));
$two = (int) str_replace(['A','B','C','D','E','F'],[10,11,12,13,14,15],substr(strtoupper($cc),1,1));
$corporateColorRGB[]=$one*16 + $two;
}
$rgb = implode(',',$corporateColorRGB);
}
return $rgb;
}
/**
* $q Cantidad de iteraciones
* $t Tipo de dato [1:hexadecimal,2:rgb]
*/
public function createRgbVersions(string $color, int $q=2, $t=1):array
{
$versions = [];
$rgb = $t==1 ? $this->fromHexToRgb($color) : $color;
$rgbArray = explode(',',$rgb);
if(count($rgbArray)===3 && $q>0){
$ratio = min(0.3, 1/$q);
$max = max($rgbArray);
$factor = $max < 128 ? 1+$ratio : 1-$ratio;
$versions[]=$rgbArray;
for($i=2;$i<=$q;$i++){
$newVersion = [];
foreach(end($versions) as $v){
$newVersion[]=$factor*$v;
}
$versions[]=$newVersion;
}
}
foreach($versions as $k=>$v){
$versions[$k]=implode(',',$v);
}
return $versions;
}
public function getRole($role)
{
switch ($role){
case 'ROLE_USER': $name = 'General'; break;
case 'ROLE_CHIEF': $name = 'Administrador'; break;
case 'ROLE_SUPPLIER': $name = 'Proveedor'; break;
case 'ROLE_SUPPLIER_CHIEF': $name = 'Proveedor (Administrador)'; break;
case 'ROLE_ADMIN': $name = 'Superadministrador'; break;
case 'ROLE_CLIENT': $name = 'Cliente'; break;
default: $name = 'General';
}
return $name;
}
public function fileIsEncrypted($filename,$ext='.packaged'){
$len = strlen($ext);
$start = strlen($filename)-$len;
return substr($filename, $start,$len) == $ext;
}
}