Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| adfec3441b | |||
| 9068302357 | |||
| 1bb0439f19 | |||
| f28585e245 | |||
| 99cfeb25ee | |||
| 6b7da9a39e | |||
| 18bba7baec | |||
| ec8ffbc090 | |||
| 60eb1ca236 | |||
| 5e3756313c | |||
| 938415aa79 | |||
| 2fd77e4034 | |||
| 4f004b8402 | |||
| 1adb19f427 | |||
| fce4a4b1d9 | |||
| 9da8ab9bdb | |||
| 8d1724df84 | |||
| cd88d59937 | |||
| cab4f5ce64 | |||
| 62ab1f8d00 | |||
| f0e1439c79 | |||
| 5060918087 | |||
| bf6b058152 | |||
| 40b607a73f | |||
| 2bdbba0eff | |||
| 058d821a7c | |||
| 4575ff89ad | |||
| 608454fcf2 | |||
| 58c1bec2da | |||
| e1ce16c399 | |||
| 2d3a748286 | |||
| 06834dd8bf | |||
| e4c0f19b1c | |||
| 84faf7a782 | |||
| 61db675ab1 | |||
| 59e72796e3 | |||
| 76e61a6358 |
@@ -87,6 +87,20 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
}
|
||||
}
|
||||
|
||||
public function filterFieldList($entityType, $fieldList, $exportAllFields)
|
||||
{
|
||||
if ($exportAllFields) {
|
||||
foreach ($fieldList as $i => $field) {
|
||||
$type = $this->getMetadata()->get(['entityDefs', $entityType, 'fields', $field, 'type']);
|
||||
if (in_array($type, ['linkMultiple', 'attachmentMultiple'])) {
|
||||
unset($fieldList[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($fieldList);
|
||||
}
|
||||
|
||||
public function addAdditionalAttributes($entityType, &$attributeList, $fieldList)
|
||||
{
|
||||
$linkList = [];
|
||||
@@ -420,6 +434,36 @@ class Xlsx extends \Espo\Core\Injectable
|
||||
$value .= $row[$name.'Country'];
|
||||
}
|
||||
$sheet->setCellValue("$col$rowNumber", $value);
|
||||
} else if ($type == 'duration') {
|
||||
if (!empty($row[$name])) {
|
||||
$seconds = intval($row[$name]);
|
||||
|
||||
$days = intval(floor($seconds / 86400));
|
||||
$seconds = $seconds - $days * 86400;
|
||||
$hours = intval(floor($seconds / 3600));
|
||||
$seconds = $seconds - $hours * 3600;
|
||||
$minutes = intval(floor($seconds / 60));
|
||||
|
||||
$value = '';
|
||||
if ($days) {
|
||||
$value .= (string) $days . $this->getInjection('language')->translate('d', 'durationUnits');
|
||||
if ($minutes || $hours) {
|
||||
$value .= ' ';
|
||||
}
|
||||
}
|
||||
if ($hours) {
|
||||
$value .= (string) $hours . $this->getInjection('language')->translate('h', 'durationUnits');
|
||||
if ($minutes) {
|
||||
$value .= ' ';
|
||||
}
|
||||
}
|
||||
if ($minutes) {
|
||||
$value .= (string) $minutes . $this->getInjection('language')->translate('m', 'durationUnits');
|
||||
}
|
||||
|
||||
$sheet->setCellValue("$col$rowNumber", $value);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (array_key_exists($name, $row)) {
|
||||
$sheet->setCellValue("$col$rowNumber", $row[$name]);
|
||||
|
||||
@@ -351,7 +351,7 @@ class Importer
|
||||
if ($email->get('messageId')) {
|
||||
$duplicate = $this->getEntityManager()->getRepository('Email')->select(['id'])->where(array(
|
||||
'messageId' => $email->get('messageId')
|
||||
))->findOne();
|
||||
))->findOne(['skipAdditionalSelectParams' => true]);
|
||||
if ($duplicate) {
|
||||
return $duplicate;
|
||||
}
|
||||
|
||||
@@ -368,14 +368,14 @@ class RDB extends \Espo\ORM\Repositories\RDB implements Injectable
|
||||
protected function processEmailAddressSave(Entity $entity)
|
||||
{
|
||||
if ($entity->hasRelation('emailAddresses') && $entity->hasAttribute('emailAddress')) {
|
||||
$emailAddressRepository = $this->getEntityManager()->getRepository('EmailAddress')->storeEntityEmailAddress($entity);
|
||||
$this->getEntityManager()->getRepository('EmailAddress')->storeEntityEmailAddress($entity);
|
||||
}
|
||||
}
|
||||
|
||||
protected function processPhoneNumberSave(Entity $entity)
|
||||
{
|
||||
if ($entity->hasRelation('phoneNumbers') && $entity->hasAttribute('phoneNumber')) {
|
||||
$emailAddressRepository = $this->getEntityManager()->getRepository('PhoneNumber')->storeEntityPhoneNumber($entity);
|
||||
$this->getEntityManager()->getRepository('PhoneNumber')->storeEntityPhoneNumber($entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
namespace Espo\Core\Utils;
|
||||
|
||||
use \Espo\Core\Exceptions\Error;
|
||||
use \Espo\Core\Exceptions\BadRequest;
|
||||
use \Espo\Core\Exceptions\Forbidden;
|
||||
use \Espo\Core\Exceptions\Conflict;
|
||||
use \Espo\Core\Utils\Json;
|
||||
@@ -122,17 +123,36 @@ class EntityManager
|
||||
return false;
|
||||
}
|
||||
|
||||
public function create($name, $type, $params = array())
|
||||
public function create($name, $type, $params = [], $replaceData = [])
|
||||
{
|
||||
$name = ucfirst($name);
|
||||
$name = trim($name);
|
||||
|
||||
if (empty($name) || empty($type)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (strlen($name) > 100) {
|
||||
throw new Error('Entity name should not be longer than 100.');
|
||||
}
|
||||
|
||||
if (is_numeric($name[0])) {
|
||||
throw new Error('Bad entity name.');
|
||||
}
|
||||
|
||||
if (!in_array($type, $this->getMetadata()->get(['app', 'entityTemplateList'], []))) {
|
||||
throw new Error('Type \''.$type.'\' does not exist.');
|
||||
}
|
||||
|
||||
$templateDefs = $this->getMetadata()->get(['app', 'entityTemplates', $type], []);
|
||||
|
||||
if (!empty($templateDefs['isNotCreatable']) && empty($params['forceCreate'])) {
|
||||
throw new Error('Type \''.$type.'\' is not creatable.');
|
||||
}
|
||||
|
||||
if ($this->getMetadata()->get('scopes.' . $name)) {
|
||||
throw new Conflict('Entity \''.$name.'\' already exists.');
|
||||
}
|
||||
if (empty($name) || empty($type)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
if ($this->checkControllerExists($name)) {
|
||||
throw new Conflict('Entity name \''.$name.'\' is not allowed.');
|
||||
@@ -149,9 +169,20 @@ class EntityManager
|
||||
|
||||
$normalizedName = Util::normilizeClassName($name);
|
||||
|
||||
$templateNamespace = "\Espo\Core\Templates";
|
||||
$templatePath = "application/Espo/Core/Templates";
|
||||
|
||||
$templateModuleName = null;
|
||||
if (!empty($templateDefs['module'])) {
|
||||
$templateModuleName = $templateDefs['module'];
|
||||
$normalizedTemplateModuleName = Util::normilizeClassName($templateModuleName);
|
||||
$templateNamespace = "\Espo\Modules\\{$normalizedTemplateModuleName}\Core\Templates";
|
||||
$templatePath = "application/Espo/Modules/".$normalizedTemplateModuleName."/Core/Templates";
|
||||
}
|
||||
|
||||
$contents = "<" . "?" . "php\n\n".
|
||||
"namespace Espo\Custom\Entities;\n\n".
|
||||
"class {$normalizedName} extends \Espo\Core\Templates\Entities\\{$type}\n".
|
||||
"class {$normalizedName} extends {$templateNamespace}\Entities\\{$type}\n".
|
||||
"{\n".
|
||||
" protected \$entityType = \"$name\";\n".
|
||||
"}\n";
|
||||
@@ -161,7 +192,7 @@ class EntityManager
|
||||
|
||||
$contents = "<" . "?" . "php\n\n".
|
||||
"namespace Espo\Custom\Controllers;\n\n".
|
||||
"class {$normalizedName} extends \Espo\Core\Templates\Controllers\\{$type}\n".
|
||||
"class {$normalizedName} extends {$templateNamespace}\Controllers\\{$type}\n".
|
||||
"{\n".
|
||||
"}\n";
|
||||
$filePath = "custom/Espo/Custom/Controllers/{$normalizedName}.php";
|
||||
@@ -169,7 +200,7 @@ class EntityManager
|
||||
|
||||
$contents = "<" . "?" . "php\n\n".
|
||||
"namespace Espo\Custom\Services;\n\n".
|
||||
"class {$normalizedName} extends \Espo\Core\Templates\Services\\{$type}\n".
|
||||
"class {$normalizedName} extends {$templateNamespace}\Services\\{$type}\n".
|
||||
"{\n".
|
||||
"}\n";
|
||||
$filePath = "custom/Espo/Custom/Services/{$normalizedName}.php";
|
||||
@@ -177,17 +208,17 @@ class EntityManager
|
||||
|
||||
$contents = "<" . "?" . "php\n\n".
|
||||
"namespace Espo\Custom\Repositories;\n\n".
|
||||
"class {$normalizedName} extends \Espo\Core\Templates\Repositories\\{$type}\n".
|
||||
"class {$normalizedName} extends {$templateNamespace}\Repositories\\{$type}\n".
|
||||
"{\n".
|
||||
"}\n";
|
||||
|
||||
$filePath = "custom/Espo/Custom/Repositories/{$normalizedName}.php";
|
||||
$this->getFileManager()->putContents($filePath, $contents);
|
||||
|
||||
if (file_exists('application/Espo/Core/Templates/SelectManagers/' . $type . '.php')) {
|
||||
if (file_exists($templatePath . '/SelectManagers/' . $type . '.php')) {
|
||||
$contents = "<" . "?" . "php\n\n".
|
||||
"namespace Espo\Custom\SelectManagers;\n\n".
|
||||
"class {$normalizedName} extends \Espo\Core\Templates\SelectManagers\\{$type}\n".
|
||||
"class {$normalizedName} extends {$templateNamespace}\SelectManagers\\{$type}\n".
|
||||
"{\n".
|
||||
"}\n";
|
||||
|
||||
@@ -214,19 +245,26 @@ class EntityManager
|
||||
|
||||
$languageList = $this->getConfig()->get('languageList', []);
|
||||
foreach ($languageList as $language) {
|
||||
$filePath = 'application/Espo/Core/Templates/i18n/' . $language . '/' . $type . '.json';
|
||||
$filePath = $templatePath . '/i18n/' . $language . '/' . $type . '.json';
|
||||
if (!file_exists($filePath)) continue;
|
||||
$languageContents = $this->getFileManager()->getContents($filePath);
|
||||
$languageContents = str_replace('{entityType}', $name, $languageContents);
|
||||
$languageContents = str_replace('{entityTypeTranslated}', $labelSingular, $languageContents);
|
||||
foreach ($replaceData as $key => $value) {
|
||||
$languageContents = str_replace('{'.$key.'}', $value, $languageContents);
|
||||
}
|
||||
|
||||
$destinationFilePath = 'custom/Espo/Custom/Resources/i18n/' . $language . '/' . $name . '.json';
|
||||
$this->getFileManager()->putContents($destinationFilePath, $languageContents);
|
||||
}
|
||||
|
||||
$filePath = "application/Espo/Core/Templates/Metadata/{$type}/scopes.json";
|
||||
$filePath = $templatePath . "/Metadata/{$type}/scopes.json";
|
||||
$scopesDataContents = $this->getFileManager()->getContents($filePath);
|
||||
$scopesDataContents = str_replace('{entityType}', $name, $scopesDataContents);
|
||||
foreach ($replaceData as $key => $value) {
|
||||
$scopesDataContents = str_replace('{'.$key.'}', $value, $scopesDataContents);
|
||||
}
|
||||
|
||||
$scopesData = Json::decode($scopesDataContents, true);
|
||||
|
||||
$scopesData['stream'] = $stream;
|
||||
@@ -236,18 +274,28 @@ class EntityManager
|
||||
$scopesData['object'] = true;
|
||||
$scopesData['isCustom'] = true;
|
||||
|
||||
if (!empty($templateDefs['isNotRemovable']) ||!empty($params['isNotRemovable'])) {
|
||||
$scopesData['isNotRemovable'] = true;
|
||||
}
|
||||
|
||||
$this->getMetadata()->set('scopes', $name, $scopesData);
|
||||
|
||||
$filePath = "application/Espo/Core/Templates/Metadata/{$type}/entityDefs.json";
|
||||
$filePath = $templatePath . "/Metadata/{$type}/entityDefs.json";
|
||||
$entityDefsDataContents = $this->getFileManager()->getContents($filePath);
|
||||
$entityDefsDataContents = str_replace('{entityType}', $name, $entityDefsDataContents);
|
||||
$entityDefsDataContents = str_replace('{tableName}', $this->getEntityManager()->getQuery()->toDb($name), $entityDefsDataContents);
|
||||
foreach ($replaceData as $key => $value) {
|
||||
$entityDefsDataContents = str_replace('{'.$key.'}', $value, $entityDefsDataContents);
|
||||
}
|
||||
$entityDefsData = Json::decode($entityDefsDataContents, true);
|
||||
$this->getMetadata()->set('entityDefs', $name, $entityDefsData);
|
||||
|
||||
$filePath = "application/Espo/Core/Templates/Metadata/{$type}/clientDefs.json";
|
||||
$filePath = $templatePath . "/Metadata/{$type}/clientDefs.json";
|
||||
$clientDefsContents = $this->getFileManager()->getContents($filePath);
|
||||
$clientDefsContents = str_replace('{entityType}', $name, $clientDefsContents);
|
||||
foreach ($replaceData as $key => $value) {
|
||||
$clientDefsContents = str_replace('{'.$key.'}', $value, $clientDefsContents);
|
||||
}
|
||||
$clientDefsData = Json::decode($clientDefsContents, true);
|
||||
$this->getMetadata()->set('clientDefs', $name, $clientDefsData);
|
||||
|
||||
@@ -257,7 +305,7 @@ class EntityManager
|
||||
$this->getMetadata()->save();
|
||||
$this->getLanguage()->save();
|
||||
|
||||
$layoutsPath = "application/Espo/Core/Templates/Layouts/{$type}";
|
||||
$layoutsPath = $templatePath . "/Layouts/{$type}";
|
||||
if ($this->getFileManager()->isDir($layoutsPath)) {
|
||||
$this->getFileManager()->copy($layoutsPath, 'custom/Espo/Custom/Resources/layouts/' . $name);
|
||||
}
|
||||
@@ -326,7 +374,7 @@ class EntityManager
|
||||
return true;
|
||||
}
|
||||
|
||||
public function delete($name)
|
||||
public function delete($name, $params = [])
|
||||
{
|
||||
if (!$this->isCustom($name)) {
|
||||
throw new Forbidden;
|
||||
@@ -336,6 +384,19 @@ class EntityManager
|
||||
|
||||
$type = $this->getMetadata()->get(['scopes', $name, 'type']);
|
||||
|
||||
$isNotRemovable = $this->getMetadata()->get(['scopes', $name, 'isNotRemovable']);
|
||||
|
||||
$templateDefs = $this->getMetadata()->get(['app', 'entityTemplates', $type], []);
|
||||
|
||||
$templateModuleName = null;
|
||||
if (!empty($templateDefs['module'])) {
|
||||
$templateModuleName = $templateDefs['module'];
|
||||
}
|
||||
|
||||
if ((!empty($templateDefs['isNotRemovable']) || $isNotRemovable) && empty($params['forceRemove'])) {
|
||||
throw new Error('Type \''.$type.'\' is not removable.');
|
||||
}
|
||||
|
||||
$unsets = array(
|
||||
'entityDefs',
|
||||
'clientDefs',
|
||||
@@ -414,6 +475,18 @@ class EntityManager
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($link) || empty($linkForeign)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (strlen($link) > 255 || strlen($linkForeign) > 255) {
|
||||
throw new Error('Link name should not be longer than 255.');
|
||||
}
|
||||
|
||||
if (is_numeric($link[0]) || is_numeric($linkForeign[0])) {
|
||||
throw new Error('Bad link name.');
|
||||
}
|
||||
|
||||
$linkMultipleField = false;
|
||||
if (!empty($params['linkMultipleField'])) {
|
||||
$linkMultipleField = true;
|
||||
@@ -786,8 +859,20 @@ class EntityManager
|
||||
|
||||
protected function getHook($type)
|
||||
{
|
||||
$templateDefs = $this->getMetadata()->get(['app', 'entityTemplates', $type], []);
|
||||
|
||||
$className = '\\Espo\\Core\\Utils\\EntityManager\\Hooks\\' . $type . 'Type';
|
||||
$className = $this->getMetadata()->get(['entityTemplates', $type, 'hookClassName'], $className);
|
||||
|
||||
$templateModuleName = null;
|
||||
if (!empty($templateDefs['module'])) {
|
||||
$templateModuleName = $templateDefs['module'];
|
||||
$normalizedTemplateModuleName = Util::normilizeClassName($templateModuleName);
|
||||
$className = '\\Espo\\Modules\\'.$normalizedTemplateModuleName.'\\Core\\Utils\\EntityManager\\Hooks\\' . $type . 'Type';
|
||||
}
|
||||
|
||||
|
||||
|
||||
$className = $this->getMetadata()->get(['app', 'entityTemplates', $type, 'hookClassName'], $className);
|
||||
|
||||
if (class_exists($className)) {
|
||||
$hook = new $className();
|
||||
|
||||
@@ -95,6 +95,18 @@ class FieldManager
|
||||
|
||||
public function create($scope, $name, $fieldDefs)
|
||||
{
|
||||
if (empty($name)) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
if (strlen($name) > 255) {
|
||||
throw new Error('Field name should not be longer than 255.');
|
||||
}
|
||||
|
||||
if (is_numeric($name[0])) {
|
||||
throw new Error('Bad field name.');
|
||||
}
|
||||
|
||||
$existingField = $this->getFieldDefs($scope, $name);
|
||||
if (isset($existingField)) {
|
||||
throw new Conflict('Field ['.$name.'] exists in '.$scope);
|
||||
|
||||
@@ -37,7 +37,7 @@ class Opportunity extends \Espo\Core\Controllers\Record
|
||||
public function actionReportByLeadSource($params, $data, $request)
|
||||
{
|
||||
$level = $this->getAcl()->getLevel('Opportunity', 'read');
|
||||
if (!$level || $level == 'own' || $level == 'no') {
|
||||
if (!$level || $level == 'no') {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class Opportunity extends \Espo\Core\Controllers\Record
|
||||
public function actionReportByStage($params, $data, $request)
|
||||
{
|
||||
$level = $this->getAcl()->getLevel('Opportunity', 'read');
|
||||
if (!$level || $level == 'own' || $level == 'no') {
|
||||
if (!$level || $level == 'no') {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class Opportunity extends \Espo\Core\Controllers\Record
|
||||
public function actionReportSalesByMonth($params, $data, $request)
|
||||
{
|
||||
$level = $this->getAcl()->getLevel('Opportunity', 'read');
|
||||
if (!$level || $level == 'own' || $level == 'no') {
|
||||
if (!$level || $level == 'no') {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class Opportunity extends \Espo\Core\Controllers\Record
|
||||
public function actionReportSalesPipeline($params, $data, $request)
|
||||
{
|
||||
$level = $this->getAcl()->getLevel('Opportunity', 'read');
|
||||
if (!$level || $level == 'own' || $level == 'no') {
|
||||
if (!$level || $level == 'no') {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -90,4 +90,3 @@ class Opportunity extends \Espo\Core\Controllers\Record
|
||||
return $this->getService('Opportunity')->reportSalesPipeline($dateFilter, $dateFrom, $dateTo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,19 +19,6 @@
|
||||
"documents": "Dokumenty",
|
||||
"campaign": "Kampaň"
|
||||
},
|
||||
"options": {
|
||||
"stage": {
|
||||
"Request": "Poptávka",
|
||||
"Quote": "Nabídka",
|
||||
"Order": "Objednávka",
|
||||
"ToProduction": "Čeká na výrobu",
|
||||
"InProduction": "Vyrábí se...",
|
||||
"Dispatch": "Expedice",
|
||||
"Delivered": "Dodáno",
|
||||
"Closed Won": "VYFAKTUROVÁNO",
|
||||
"Closed Lost": "ODMÍTNUTO"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"Create Opportunity": "Vytvořit případ"
|
||||
},
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Undersøges",
|
||||
"Qualification": "Kvalifikation",
|
||||
"Proposal": "Tilbud",
|
||||
"Negotiation": "Forhandler",
|
||||
"Needs Analysis": "Behovsanalyse",
|
||||
"Value Proposition": "Vejledende Tilbud",
|
||||
"Id. Decision Makers": "Identifikation af Beslutningstagere",
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Identifikation",
|
||||
"Qualification": "Qualifikation",
|
||||
"Proposal": "Angebot",
|
||||
"Negotiation": "Verhandlung",
|
||||
"Needs Analysis": "Bedarfserhebung",
|
||||
"Value Proposition": "Richtangebot",
|
||||
"Id. Decision Makers": "Identifikation der Entscheider",
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospecting",
|
||||
"Qualification": "Qualification",
|
||||
"Proposal": "Proposal",
|
||||
"Negotiation": "Negotiation",
|
||||
"Needs Analysis": "Needs Analysis",
|
||||
"Value Proposition": "Value Proposition",
|
||||
"Id. Decision Makers": "Id. Decision Makers",
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospección",
|
||||
"Qualification": "Calificación",
|
||||
"Proposal": "Propuesta",
|
||||
"Negotiation": "Negociación",
|
||||
"Needs Analysis": "Análisis de necesidades",
|
||||
"Value Proposition": "Propuesta de valor",
|
||||
"Id. Decision Makers": "Identificar tomador de decisiones",
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospección",
|
||||
"Qualification": "Calificación",
|
||||
"Proposal": "Cotización con Propuesta",
|
||||
"Negotiation": "Negociación",
|
||||
"Needs Analysis": "Análisis de Necesidades",
|
||||
"Value Proposition": "Propuesta de Valor",
|
||||
"Id. Decision Makers": "Id. Tomadores de Decisiones",
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospection",
|
||||
"Qualification": "Qualification",
|
||||
"Proposal": "Proposition",
|
||||
"Negotiation": "Négociation",
|
||||
"Needs Analysis": "Analyse des besoins",
|
||||
"Value Proposition": "Valeur de la proposition",
|
||||
"Id. Decision Makers": "Id. Decisionnaire",
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
"Prospecting": "Pencarian",
|
||||
"Qualification": "Kualifikasi",
|
||||
"Needs Analysis": "Butuh analisa",
|
||||
"Proposal": "Proposal",
|
||||
"Negotiation": "Negosiasi",
|
||||
"Value Proposition": "Proposisi nilai",
|
||||
"Id. Decision Makers": "Id. Pembuat keputusan",
|
||||
"Perception Analysis": "Analisis persepsi",
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospecting",
|
||||
"Qualification": "Qualifica",
|
||||
"Proposal": "Proposta",
|
||||
"Negotiation": "Negoziazione",
|
||||
"Needs Analysis": "Necessita di analisi",
|
||||
"Value Proposition": "Proposta di Valore",
|
||||
"Id. Decision Makers": "Id. Responsabile",
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Potencialių klientų paieška",
|
||||
"Qualification": "Kvalifikacija",
|
||||
"Proposal": "Pasiūlymas",
|
||||
"Negotiation": "Derybos",
|
||||
"Needs Analysis": "Reikalinga analizė",
|
||||
"Value Proposition": "Pasiūlymo vertė",
|
||||
"Id. Decision Makers": "Id. Sprendimus priimantys žmonės",
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Undersøkes",
|
||||
"Qualification": "Kvalitetskontrolleres",
|
||||
"Proposal": "Prisforslag",
|
||||
"Negotiation": "Forhandler",
|
||||
"Needs Analysis": "Trenger analyse",
|
||||
"Value Proposition": "Verdivurdering",
|
||||
"Id. Decision Makers": "Ser etter beslutningstagere",
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospectie",
|
||||
"Qualification": "Kwalificatie",
|
||||
"Proposal": "Voorstel",
|
||||
"Negotiation": "Onderhandeling",
|
||||
"Needs Analysis": "Analyse behoefte",
|
||||
"Value Proposition": "Waarde voorstel",
|
||||
"Id. Decision Makers": "Id. Beslissers",
|
||||
|
||||
@@ -23,12 +23,14 @@
|
||||
"stage": {
|
||||
"Prospecting": "I Spotkanie",
|
||||
"Qualification": "Kwalifikacja",
|
||||
"Proposal": "Oferta Cenowa",
|
||||
"Negotiation": "Negocjacje",
|
||||
"Needs Analysis": "Dodatkowa Analiza",
|
||||
"Value Proposition": "Oferta Produktowa",
|
||||
"Id. Decision Makers": "Oferta przekazna do osoby decyzyjnej",
|
||||
"Perception Analysis": "Perception Analysis",
|
||||
"Proposal/Price Quote": "Oferta Cenowa",
|
||||
"Negotiation/Review": "Negocjacje ",
|
||||
"Negotiation/Review": "Negocjacje",
|
||||
"Closed Won": "Zakończone Wygrane",
|
||||
"Closed Lost": "Zakończone Przegrane"
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospectando",
|
||||
"Qualification": "Qualificação",
|
||||
"Proposal": "Proposta",
|
||||
"Negotiation": "Negociação",
|
||||
"Needs Analysis": "Necessita de Análise",
|
||||
"Value Proposition": "Proposta de Valor",
|
||||
"Id. Decision Makers": "Identificação dos Decisores",
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Prospectare",
|
||||
"Qualification": "Calificari",
|
||||
"Proposal": "Propunere",
|
||||
"Negotiation": "Negociere",
|
||||
"Needs Analysis": "Necesita Analiza",
|
||||
"Value Proposition": "Valoare Propunere",
|
||||
"Id. Decision Makers": "Id. Factori de Decizie",
|
||||
@@ -25,7 +27,7 @@
|
||||
"Proposal/Price Quote": "Propunere/Oferta Pret",
|
||||
"Negotiation/Review": "Negociere/Revizuire",
|
||||
"Closed Won": "Inchide ca Castigat",
|
||||
"Closed Lost": "Inchide ca Pierdut"
|
||||
"Closed Lost": "Inchide ca Pierdut"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Привлечение клиента",
|
||||
"Qualification": "Оценка возможности",
|
||||
"Proposal": "Предложение",
|
||||
"Negotiation": "Согласование",
|
||||
"Needs Analysis": "Требует анализа",
|
||||
"Value Proposition": "Выбор предложения/оферты",
|
||||
"Id. Decision Makers": "Определение ответственного лица",
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Izviđanje",
|
||||
"Qualification": "Kvalifikacija",
|
||||
"Proposal": "Predlog",
|
||||
"Negotiation": "Pregovori",
|
||||
"Needs Analysis": "Analiza potrebna",
|
||||
"Value Proposition": "Dobitak",
|
||||
"Id. Decision Makers": "Id. Donosioci odluka",
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "Araştırma",
|
||||
"Qualification": "Yeterlilik",
|
||||
"Proposal": "Proposal",
|
||||
"Negotiation": "Görüşme",
|
||||
"Needs Analysis": "Analiz Yapılmalı",
|
||||
"Value Proposition": "Değer Önerisi",
|
||||
"Id. Decision Makers": "Id. Karar Vericiler",
|
||||
|
||||
@@ -26,12 +26,14 @@
|
||||
"stage": {
|
||||
"Prospecting": "Проспектінг",
|
||||
"Qualification": "Кваліфікація",
|
||||
"Proposal": "Пропозиція",
|
||||
"Negotiation": "Переговори",
|
||||
"Needs Analysis": "Вимагає аналізу",
|
||||
"Value Proposition": "Цінова пропозиція",
|
||||
"Id. Decision Makers": "Визначення осіб, що приймають рішення",
|
||||
"Perception Analysis": "Аналізу сприйняття",
|
||||
"Proposal/Price Quote": "Квота пропозицій/цін",
|
||||
"Negotiation/Review": "Заперечення/перегляд",
|
||||
"Negotiation/Review": "Переговори/перегляд",
|
||||
"Closed Won": "Закрито переможно",
|
||||
"Closed Lost": "Закрито провалом"
|
||||
}
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
"Perception Analysis": "Phân tích khả năng",
|
||||
"Proposal/Price Quote": "Giá đặt ra",
|
||||
"Negotiation/Review": "Đàm phán / đánh giá",
|
||||
"Proposal": "Giá đặt ra",
|
||||
"Negotiation": "Đàm phán",
|
||||
"Closed Won": "Thắng thầu",
|
||||
"Closed Lost": "Thua thầu"
|
||||
"Closed Lost": "Thua thầu"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"stage": {
|
||||
"Prospecting": "预期",
|
||||
"Qualification": "评价",
|
||||
"Proposal": "提案",
|
||||
"Negotiation": "协商",
|
||||
"Needs Analysis": "需求分析",
|
||||
"Value Proposition": "价值主张",
|
||||
"Id. Decision Makers": "Id决策者",
|
||||
|
||||
@@ -186,6 +186,7 @@
|
||||
"layoutListDisabled": true,
|
||||
"layoutMassUpdateDisabled": true,
|
||||
"importDisabled": true,
|
||||
"exportDisabled": true,
|
||||
"noLoad": true
|
||||
},
|
||||
"targetList": {
|
||||
|
||||
@@ -42,19 +42,15 @@
|
||||
},
|
||||
"stage": {
|
||||
"type": "enum",
|
||||
"options": ["Prospecting", "Qualification", "Needs Analysis", "Value Proposition", "Id. Decision Makers", "Perception Analysis", "Proposal/Price Quote", "Negotiation/Review", "Closed Won", "Closed Lost"],
|
||||
"options": ["Prospecting", "Qualification", "Proposal", "Negotiation", "Closed Won", "Closed Lost"],
|
||||
"view": "crm:views/opportunity/fields/stage",
|
||||
"default": "Prospecting",
|
||||
"audited": true,
|
||||
"probabilityMap": {
|
||||
"Prospecting": 10,
|
||||
"Qualification": 10,
|
||||
"Needs Analysis": 20,
|
||||
"Value Proposition": 50,
|
||||
"Id. Decision Makers": 60,
|
||||
"Perception Analysis": 70,
|
||||
"Proposal/Price Quote": 75,
|
||||
"Negotiation/Review": 90,
|
||||
"Qualification": 20,
|
||||
"Proposal": 50,
|
||||
"Negotiation": 80,
|
||||
"Closed Won": 100,
|
||||
"Closed Lost": 0
|
||||
},
|
||||
|
||||
@@ -38,34 +38,41 @@ class Opportunity extends \Espo\Services\Record
|
||||
{
|
||||
public function reportSalesPipeline($dateFilter, $dateFrom = null, $dateTo = null)
|
||||
{
|
||||
if (in_array('amount', $this->getAcl()->getScopeForbiddenAttributeList('Opportunity'))) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
if ($dateFilter !== 'between' && $dateFilter !== 'ever') {
|
||||
list($dateFrom, $dateTo) = $this->getDateRangeByFilter($dateFilter);
|
||||
}
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
$options = $this->getMetadata()->get('entityDefs.Opportunity.fields.stage.options');
|
||||
$options = $this->getMetadata()->get('entityDefs.Opportunity.fields.stage.options', []);
|
||||
|
||||
$sql = "
|
||||
SELECT opportunity.stage AS `stage`, SUM(opportunity.amount * currency.rate) as `amount`
|
||||
FROM opportunity
|
||||
JOIN currency ON currency.id = opportunity.amount_currency
|
||||
WHERE
|
||||
opportunity.deleted = 0 AND
|
||||
";
|
||||
$selectManager = $this->getSelectManagerFactory()->create('Opportunity');
|
||||
|
||||
$selectParams = [
|
||||
'select' => ['stage', ['SUM:amountConverted', 'amount']],
|
||||
'whereClause' => [
|
||||
'stage!=' => 'Closed Lost'
|
||||
],
|
||||
'orderBy' => 'LIST:stage:' . implode(',', $options),
|
||||
'groupBy' => ['stage']
|
||||
];
|
||||
|
||||
if ($dateFilter !== 'ever') {
|
||||
$sql .= "
|
||||
opportunity.close_date >= ".$pdo->quote($dateFrom)." AND
|
||||
opportunity.close_date < ".$pdo->quote($dateTo)." AND
|
||||
";
|
||||
$selectParams['whereClause'][] = [
|
||||
'closeDate>=' => $dateFrom,
|
||||
'closeDate<' => $dateTo
|
||||
];
|
||||
}
|
||||
|
||||
$sql .= "
|
||||
opportunity.stage <> 'Closed Lost'
|
||||
GROUP BY opportunity.stage
|
||||
ORDER BY FIELD(opportunity.stage, '".implode("','", $options)."')
|
||||
";
|
||||
$selectManager->applyAccess($selectParams);
|
||||
|
||||
$this->getEntityManager()->getRepository('Opportunity')->handleSelectParams($selectParams);
|
||||
|
||||
$sql = $this->getEntityManager()->getQuery()->createSelectQuery('Opportunity', $selectParams);
|
||||
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
@@ -82,31 +89,46 @@ class Opportunity extends \Espo\Services\Record
|
||||
|
||||
public function reportByLeadSource($dateFilter, $dateFrom = null, $dateTo = null)
|
||||
{
|
||||
if (in_array('amount', $this->getAcl()->getScopeForbiddenAttributeList('Opportunity'))) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
if (in_array('leadSource', $this->getAcl()->getScopeForbiddenAttributeList('Opportunity'))) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
if ($dateFilter !== 'between' && $dateFilter !== 'ever') {
|
||||
list($dateFrom, $dateTo) = $this->getDateRangeByFilter($dateFilter);
|
||||
}
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
$sql = "
|
||||
SELECT opportunity.lead_source AS `leadSource`, SUM(opportunity.amount * currency.rate * opportunity.probability / 100) as `amount`
|
||||
FROM opportunity
|
||||
JOIN currency ON currency.id = opportunity.amount_currency
|
||||
WHERE
|
||||
opportunity.deleted = 0 AND
|
||||
";
|
||||
$options = $this->getMetadata()->get('entityDefs.Lead.fields.source.options', []);
|
||||
|
||||
$selectManager = $this->getSelectManagerFactory()->create('Opportunity');
|
||||
|
||||
$selectParams = [
|
||||
'select' => ['leadSource', ['SUM:amountWeightedConverted', 'amount']],
|
||||
'whereClause' => [
|
||||
'stage!=' => 'Closed Lost',
|
||||
['leadSource!=' => ''],
|
||||
['leadSource!=' => null]
|
||||
],
|
||||
'orderBy' => 'LIST:leadSource:' . implode(',', $options),
|
||||
'groupBy' => ['leadSource']
|
||||
];
|
||||
|
||||
if ($dateFilter !== 'ever') {
|
||||
$sql .= "
|
||||
opportunity.close_date >= ".$pdo->quote($dateFrom)." AND
|
||||
opportunity.close_date < ".$pdo->quote($dateTo)." AND
|
||||
";
|
||||
$selectParams['whereClause'][] = [
|
||||
'closeDate>=' => $dateFrom,
|
||||
'closeDate<' => $dateTo
|
||||
];
|
||||
}
|
||||
|
||||
$sql .= "
|
||||
opportunity.stage <> 'Closed Lost' AND
|
||||
opportunity.lead_source <> ''
|
||||
GROUP BY opportunity.lead_source
|
||||
";
|
||||
$selectManager->applyAccess($selectParams);
|
||||
|
||||
$this->getEntityManager()->getRepository('Opportunity')->handleSelectParams($selectParams);
|
||||
|
||||
$sql = $this->getEntityManager()->getQuery()->createSelectQuery('Opportunity', $selectParams);
|
||||
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
@@ -123,35 +145,42 @@ class Opportunity extends \Espo\Services\Record
|
||||
|
||||
public function reportByStage($dateFilter, $dateFrom = null, $dateTo = null)
|
||||
{
|
||||
if (in_array('amount', $this->getAcl()->getScopeForbiddenAttributeList('Opportunity'))) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
if ($dateFilter !== 'between' && $dateFilter !== 'ever') {
|
||||
list($dateFrom, $dateTo) = $this->getDateRangeByFilter($dateFilter);
|
||||
}
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
$options = $this->getMetadata()->get('entityDefs.Opportunity.fields.stage.options');
|
||||
$options = $this->getMetadata()->get('entityDefs.Opportunity.fields.stage.options', []);
|
||||
|
||||
$sql = "
|
||||
SELECT opportunity.stage AS `stage`, SUM(opportunity.amount * currency.rate) as `amount`
|
||||
FROM opportunity
|
||||
JOIN currency ON currency.id = opportunity.amount_currency
|
||||
WHERE
|
||||
opportunity.deleted = 0 AND
|
||||
";
|
||||
$selectManager = $this->getSelectManagerFactory()->create('Opportunity');
|
||||
|
||||
$selectParams = [
|
||||
'select' => ['stage', ['SUM:amountConverted', 'amount']],
|
||||
'whereClause' => [
|
||||
'stage!=' => 'Closed Lost',
|
||||
'stage!=' => 'Closed Won'
|
||||
],
|
||||
'orderBy' => 'LIST:stage:' . implode(',', $options),
|
||||
'groupBy' => ['stage']
|
||||
];
|
||||
|
||||
if ($dateFilter !== 'ever') {
|
||||
$sql .= "
|
||||
opportunity.close_date >= ".$pdo->quote($dateFrom)." AND
|
||||
opportunity.close_date < ".$pdo->quote($dateTo)." AND
|
||||
";
|
||||
$selectParams['whereClause'][] = [
|
||||
'closeDate>=' => $dateFrom,
|
||||
'closeDate<' => $dateTo
|
||||
];
|
||||
}
|
||||
|
||||
$sql .= "
|
||||
opportunity.stage <> 'Closed Lost' AND
|
||||
opportunity.stage <> 'Closed Won'
|
||||
GROUP BY opportunity.stage
|
||||
ORDER BY FIELD(opportunity.stage, '".implode("','", $options)."')
|
||||
";
|
||||
$selectManager->applyAccess($selectParams);
|
||||
|
||||
$this->getEntityManager()->getRepository('Opportunity')->handleSelectParams($selectParams);
|
||||
|
||||
$sql = $this->getEntityManager()->getQuery()->createSelectQuery('Opportunity', $selectParams);
|
||||
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
@@ -168,32 +197,40 @@ class Opportunity extends \Espo\Services\Record
|
||||
|
||||
public function reportSalesByMonth($dateFilter, $dateFrom = null, $dateTo = null)
|
||||
{
|
||||
if (in_array('amount', $this->getAcl()->getScopeForbiddenAttributeList('Opportunity'))) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
if ($dateFilter !== 'between' && $dateFilter !== 'ever') {
|
||||
list($dateFrom, $dateTo) = $this->getDateRangeByFilter($dateFilter);
|
||||
}
|
||||
|
||||
$pdo = $this->getEntityManager()->getPDO();
|
||||
|
||||
$sql = "
|
||||
SELECT DATE_FORMAT(opportunity.close_date, '%Y-%m') AS `month`, SUM(opportunity.amount * currency.rate) as `amount`
|
||||
FROM opportunity
|
||||
JOIN currency ON currency.id = opportunity.amount_currency
|
||||
WHERE
|
||||
opportunity.deleted = 0 AND
|
||||
";
|
||||
$selectManager = $this->getSelectManagerFactory()->create('Opportunity');
|
||||
|
||||
$selectParams = [
|
||||
'select' => [['MONTH:closeDate', 'month'], ['SUM:amountConverted', 'amount']],
|
||||
'whereClause' => [
|
||||
'stage' => 'Closed Won'
|
||||
],
|
||||
'orderBy' => 1,
|
||||
'groupBy' => ['MONTH:closeDate']
|
||||
];
|
||||
|
||||
if ($dateFilter !== 'ever') {
|
||||
$sql .= "
|
||||
opportunity.close_date >= ".$pdo->quote($dateFrom)." AND
|
||||
opportunity.close_date < ".$pdo->quote($dateTo)." AND
|
||||
";
|
||||
$selectParams['whereClause'][] = [
|
||||
'closeDate>=' => $dateFrom,
|
||||
'closeDate<' => $dateTo
|
||||
];
|
||||
}
|
||||
|
||||
$sql .= "
|
||||
opportunity.stage = 'Closed Won'
|
||||
GROUP BY DATE_FORMAT(opportunity.close_date, '%Y-%m')
|
||||
ORDER BY `month`
|
||||
";
|
||||
$selectManager->applyAccess($selectParams);
|
||||
|
||||
$this->getEntityManager()->getRepository('Opportunity')->handleSelectParams($selectParams);
|
||||
|
||||
$sql = $this->getEntityManager()->getQuery()->createSelectQuery('Opportunity', $selectParams);
|
||||
|
||||
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute();
|
||||
@@ -207,7 +244,14 @@ class Opportunity extends \Espo\Services\Record
|
||||
|
||||
$dt = new \DateTime($dateFrom);
|
||||
$dtTo = new \DateTime($dateTo);
|
||||
$dtTo->setDate($dtTo->format('Y'), $dtTo->format('m'), 1);
|
||||
|
||||
if (intval($dtTo->format('d')) !== 1) {
|
||||
$dtTo->setDate($dtTo->format('Y'), $dtTo->format('m'), 1);
|
||||
$dtTo->modify('+ 1 month');
|
||||
} else {
|
||||
$dtTo->setDate($dtTo->format('Y'), $dtTo->format('m'), 1);
|
||||
}
|
||||
|
||||
if ($dt && $dtTo) {
|
||||
$interval = new \DateInterval('P1M');
|
||||
while ($dt->getTimestamp() <= $dtTo->getTimestamp()) {
|
||||
@@ -219,7 +263,6 @@ class Opportunity extends \Espo\Services\Record
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$keyList = array_keys($result);
|
||||
sort($keyList);
|
||||
|
||||
|
||||
@@ -187,7 +187,9 @@ class RDB extends \Espo\ORM\Repository
|
||||
|
||||
public function find(array $params = array())
|
||||
{
|
||||
$this->handleSelectParams($params);
|
||||
if (empty($params['skipAdditionalSelectParams'])) {
|
||||
$this->handleSelectParams($params);
|
||||
}
|
||||
$params = $this->getSelectParams($params);
|
||||
|
||||
$dataArr = $this->getMapper()->select($this->seed, $params);
|
||||
@@ -230,7 +232,9 @@ class RDB extends \Espo\ORM\Repository
|
||||
}
|
||||
|
||||
if ($entityType) {
|
||||
$this->getEntityManager()->getRepository($entityType)->handleSelectParams($params);
|
||||
if (empty($params['skipAdditionalSelectParams'])) {
|
||||
$this->getEntityManager()->getRepository($entityType)->handleSelectParams($params);
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->getMapper()->selectRelated($entity, $relationName, $params);
|
||||
@@ -250,7 +254,9 @@ class RDB extends \Espo\ORM\Repository
|
||||
return;
|
||||
}
|
||||
$entityType = $entity->relations[$relationName]['entity'];
|
||||
$this->getEntityManager()->getRepository($entityType)->handleSelectParams($params);
|
||||
if (empty($params['skipAdditionalSelectParams'])) {
|
||||
$this->getEntityManager()->getRepository($entityType)->handleSelectParams($params);
|
||||
}
|
||||
|
||||
return intval($this->getMapper()->countRelated($entity, $relationName, $params));
|
||||
}
|
||||
@@ -423,7 +429,9 @@ class RDB extends \Espo\ORM\Repository
|
||||
|
||||
public function count(array $params = array())
|
||||
{
|
||||
$this->handleSelectParams($params);
|
||||
if (empty($params['skipAdditionalSelectParams'])) {
|
||||
$this->handleSelectParams($params);
|
||||
}
|
||||
|
||||
$params = $this->getSelectParams($params);
|
||||
$count = $this->getMapper()->count($this->seed, $params);
|
||||
|
||||
@@ -437,6 +437,12 @@
|
||||
"dayNamesShort": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
||||
"dayNamesMin": ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
|
||||
},
|
||||
"durationUnits": {
|
||||
"d": "d",
|
||||
"h": "h",
|
||||
"m": "m",
|
||||
"s": "s"
|
||||
},
|
||||
"options": {
|
||||
"salutationName": {
|
||||
"Mr.": "Mr.",
|
||||
|
||||
@@ -124,7 +124,6 @@
|
||||
},
|
||||
"statusStyles": {
|
||||
"Lead": {
|
||||
"New" : "primary",
|
||||
"Assigned" : "primary",
|
||||
"In Process" : "primary",
|
||||
"Converted" : "success",
|
||||
@@ -140,14 +139,8 @@
|
||||
"Duplicate" : "danger"
|
||||
},
|
||||
"Opportunity": {
|
||||
"Prospecting": "primary",
|
||||
"Qualification": "primary",
|
||||
"Needs Analysis": "primary",
|
||||
"Value Proposition": "primary",
|
||||
"Id. Decision Makers": "primary",
|
||||
"Perception Analysis": "primary",
|
||||
"Proposal/Price Quote": "primary",
|
||||
"Negotiation/Review": "primary",
|
||||
"Proposal": "primary",
|
||||
"Negotiation": "primary",
|
||||
"Closed Won" : "success",
|
||||
"Closed Lost" : "danger"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"entity": true,
|
||||
"layouts": false,
|
||||
"tab": false,
|
||||
"tab": true,
|
||||
"acl": "recordAllTeamNo",
|
||||
"customizable": true,
|
||||
"disabled": false
|
||||
|
||||
@@ -44,6 +44,7 @@ class App extends \Espo\Core\Services\Base
|
||||
$this->addDependency('container');
|
||||
$this->addDependency('entityManager');
|
||||
$this->addDependency('metadata');
|
||||
$this->addDependency('selectManagerFactory');
|
||||
}
|
||||
|
||||
protected function getPreferences()
|
||||
@@ -112,7 +113,8 @@ class App extends \Espo\Core\Services\Base
|
||||
'settings' => $settings,
|
||||
'language' => $language,
|
||||
'appParams' => [
|
||||
'maxUploadSize' => $this->getMaxUploadSize() / 1024.0 / 1024.0
|
||||
'maxUploadSize' => $this->getMaxUploadSize() / 1024.0 / 1024.0,
|
||||
'templateEntityTypeList' => $this->getTemplateEntityTypeList()
|
||||
]
|
||||
];
|
||||
}
|
||||
@@ -210,6 +212,31 @@ class App extends \Espo\Core\Services\Base
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function getTemplateEntityTypeList()
|
||||
{
|
||||
if (!$this->getAcl()->checkScope('Template')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$list = [];
|
||||
|
||||
$selectManager = $this->getInjection('selectManagerFactory')->create('Template');
|
||||
|
||||
$selectParams = $selectManager->getEmptySelectParams();
|
||||
$selectManager->applyAccess($selectParams);
|
||||
|
||||
$templateList = $this->getEntityManager()->getRepository('Template')
|
||||
->select(['entityType'])
|
||||
->groupBy(['entityType'])
|
||||
->find($selectParams);
|
||||
|
||||
foreach ($templateList as $template) {
|
||||
$list[] = $template->get('entityType');
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function jobClearCache()
|
||||
{
|
||||
$this->getInjection('container')->get('dataManager')->clearCache();
|
||||
@@ -219,4 +246,5 @@ class App extends \Espo\Core\Services\Base
|
||||
{
|
||||
$this->getInjection('container')->get('dataManager')->rebuild();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1424,10 +1424,12 @@ class Record extends \Espo\Core\Services\Base
|
||||
}
|
||||
|
||||
if (!array_key_exists('fieldList', $params)) {
|
||||
$exportAllFields = true;
|
||||
$fieldDefs = $this->getMetadata()->get(['entityDefs', $this->entityType, 'fields'], []);
|
||||
$fieldList = array_keys($fieldDefs);
|
||||
array_unshift($fieldList, 'id');
|
||||
} else {
|
||||
$exportAllFields = false;
|
||||
$fieldList = $params['fieldList'];
|
||||
}
|
||||
|
||||
@@ -1438,6 +1440,10 @@ class Record extends \Espo\Core\Services\Base
|
||||
}
|
||||
$fieldList = array_values($fieldList);
|
||||
|
||||
if (method_exists($exportObj, 'filterFieldList')) {
|
||||
$fieldList = $exportObj->filterFieldList($this->entityType, $fieldList, $exportAllFields);
|
||||
}
|
||||
|
||||
if (is_null($attributeList)) {
|
||||
$attributeList = [];
|
||||
$seed = $this->getEntityManager()->getEntity($this->entityType);
|
||||
|
||||
@@ -158,10 +158,6 @@ Espo.define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], functi
|
||||
if (Object.prototype.toString.call(this.enabledScopeList) !== '[object Array]') {
|
||||
this.enabledScopeList = [];
|
||||
}
|
||||
|
||||
this.once('remove', function () {
|
||||
this.isRemoved = true;
|
||||
}, this);
|
||||
},
|
||||
|
||||
toggleScopeFilter: function (name) {
|
||||
@@ -380,7 +376,7 @@ Espo.define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], functi
|
||||
},
|
||||
|
||||
adjustSize: function () {
|
||||
if (this.isRemoved) return;
|
||||
if (this.isRemoved()) return;
|
||||
var height = this.getCalculatedHeight();
|
||||
this.$calendar.fullCalendar('option', 'contentHeight', height);
|
||||
},
|
||||
|
||||
@@ -48,7 +48,7 @@ Espo.define('crm:views/dashlets/abstract/chart', ['views/dashlets/abstract/base'
|
||||
|
||||
hoverColor: '#FF3F19',
|
||||
|
||||
legendColumnWidth: 90,
|
||||
legendColumnWidth: 110,
|
||||
|
||||
legendColumnNumber: 6,
|
||||
|
||||
@@ -85,6 +85,7 @@ Espo.define('crm:views/dashlets/abstract/chart', ['views/dashlets/abstract/base'
|
||||
|
||||
this.once('after:render', function () {
|
||||
$(window).on('resize.chart' + this.name, function () {
|
||||
this.adjustContainer();
|
||||
this.draw();
|
||||
}.bind(this));
|
||||
}, this);
|
||||
@@ -141,38 +142,73 @@ Espo.define('crm:views/dashlets/abstract/chart', ['views/dashlets/abstract/base'
|
||||
return '';
|
||||
},
|
||||
|
||||
getLegentColumnNumber: function () {
|
||||
getLegendColumnNumber: function () {
|
||||
var width = this.$el.closest('.panel-body').width();
|
||||
var legendColumnNumber = Math.floor(width / this.legendColumnWidth);
|
||||
return legendColumnNumber || this.legendColumnNumber;
|
||||
},
|
||||
|
||||
getLegentHeight: function () {
|
||||
var lineNumber = Math.ceil(this.chartData.length / this.getLegentColumnNumber());
|
||||
getLegendHeight: function () {
|
||||
var lineNumber = Math.ceil(this.chartData.length / this.getLegendColumnNumber());
|
||||
var legendHeight = 0;
|
||||
|
||||
var lineHeight = this.getThemeManager().getParam('dashletChartLegentRowHeight') || 22;
|
||||
var lineHeight = this.getThemeManager().getParam('dashletChartLegendRowHeight') || 19;
|
||||
var paddingTopHeight = this.getThemeManager().getParam('dashletChartLegendPaddingTopHeight') || 7;
|
||||
|
||||
if (lineNumber > 0) {
|
||||
legendHeight = lineHeight * lineNumber;
|
||||
legendHeight = lineHeight * lineNumber + paddingTopHeight;
|
||||
}
|
||||
return legendHeight;
|
||||
},
|
||||
|
||||
adjustContainer: function () {
|
||||
var legendHeight = this.getLegendHeight();
|
||||
var heightCss = 'calc(100% - ' + legendHeight.toString() + 'px)';
|
||||
this.$container.css('height', heightCss);
|
||||
},
|
||||
|
||||
adjustLegend: function () {
|
||||
var number = this.getLegendColumnNumber();
|
||||
if (!number) return;
|
||||
|
||||
var dashletChartLegendBoxWidth = this.getThemeManager().getParam('dashletChartLegendBoxWidth') || 21;
|
||||
|
||||
var containerWidth = this.$legendContainer.width();
|
||||
|
||||
var width = Math.floor((containerWidth - dashletChartLegendBoxWidth * number) / number);
|
||||
|
||||
var columnNumber = this.$legendContainer.find('> table tr:first-child > td').size() / 2;
|
||||
|
||||
var tableWidth = (width + dashletChartLegendBoxWidth) * columnNumber;
|
||||
|
||||
this.$legendContainer.find('> table')
|
||||
.css('table-layout', 'fixed')
|
||||
.attr('width', tableWidth);
|
||||
|
||||
this.$legendContainer.find('td.flotr-legend-label').attr('width', width);
|
||||
this.$legendContainer.find('td.flotr-legend-color-box').attr('width', dashletChartLegendBoxWidth);
|
||||
|
||||
this.$legendContainer.find('td.flotr-legend-label > span').each(function(i, span) {
|
||||
span.setAttribute('title', span.textContent);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
this.$el.closest('.panel-body').css({
|
||||
'overflow-y': 'visible',
|
||||
'overflow-x': 'visible'
|
||||
});
|
||||
|
||||
this.$legendContainer = this.$el.find('.legend-container');
|
||||
this.$container = this.$el.find('.chart-container');
|
||||
|
||||
this.fetch(function (data) {
|
||||
this.chartData = this.prepareData(data);
|
||||
|
||||
var $container = this.$container = this.$el.find('.chart-container');
|
||||
var legendHeight = this.getLegentHeight();
|
||||
var heightCss = 'calc(100% - '+legendHeight.toString()+'px)';
|
||||
$container.css('height', heightCss);
|
||||
this.adjustContainer();
|
||||
|
||||
setTimeout(function () {
|
||||
if (!$container.size() || !$container.is(":visible")) return;
|
||||
if (!this.$container.size() || !this.$container.is(":visible")) return;
|
||||
this.draw();
|
||||
}.bind(this), 1);
|
||||
});
|
||||
|
||||
@@ -112,7 +112,7 @@ Espo.define('crm:views/dashlets/opportunities-by-lead-source', 'crm:views/dashle
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
noColumns: this.getLegentColumnNumber(),
|
||||
noColumns: this.getLegendColumnNumber(),
|
||||
container: this.$el.find('.legend-container'),
|
||||
labelBoxMargin: 0,
|
||||
labelFormatter: self.labelFormatter.bind(self),
|
||||
@@ -120,6 +120,8 @@ Espo.define('crm:views/dashlets/opportunities-by-lead-source', 'crm:views/dashle
|
||||
backgroundOpacity: 0
|
||||
}
|
||||
});
|
||||
|
||||
this.adjustLegend();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -73,6 +73,16 @@ Espo.define('crm:views/dashlets/opportunities-by-stage', 'crm:views/dashlets/abs
|
||||
i++;
|
||||
}, this);
|
||||
|
||||
var max = 0;
|
||||
if (d.length) {
|
||||
d.forEach(function (item) {
|
||||
if ( item.value && item.value > max) {
|
||||
max = item.value;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
this.max = max;
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
@@ -108,11 +118,15 @@ Espo.define('crm:views/dashlets/opportunities-by-stage', 'crm:views/dashlets/abs
|
||||
xaxis: {
|
||||
min: 0,
|
||||
color: this.textColor,
|
||||
max: this.max + 0.08 * this.max,
|
||||
tickFormatter: function (value) {
|
||||
if (value == 0) {
|
||||
return '';
|
||||
}
|
||||
if (value % 1 == 0) {
|
||||
if (value > self.max + 0.05 * this.max) {
|
||||
return '';
|
||||
}
|
||||
return self.currencySymbol + self.formatNumber(Math.floor(value)).toString();
|
||||
}
|
||||
return '';
|
||||
@@ -131,7 +145,7 @@ Espo.define('crm:views/dashlets/opportunities-by-stage', 'crm:views/dashlets/abs
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
noColumns: this.getLegentColumnNumber(),
|
||||
noColumns: this.getLegendColumnNumber(),
|
||||
container: this.$el.find('.legend-container'),
|
||||
labelBoxMargin: 0,
|
||||
labelFormatter: self.labelFormatter.bind(self),
|
||||
@@ -139,6 +153,8 @@ Espo.define('crm:views/dashlets/opportunities-by-stage', 'crm:views/dashlets/abs
|
||||
backgroundOpacity: 0
|
||||
}
|
||||
});
|
||||
|
||||
this.adjustLegend();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -46,7 +46,7 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch
|
||||
return url;
|
||||
},
|
||||
|
||||
getLegentHeight: function () {
|
||||
getLegendHeight: function () {
|
||||
return 0;
|
||||
},
|
||||
|
||||
@@ -70,13 +70,20 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch
|
||||
|
||||
var data = [];
|
||||
|
||||
var max = 0;
|
||||
|
||||
values.forEach(function (value, i) {
|
||||
if (value && value > max) {
|
||||
max = value;
|
||||
}
|
||||
data.push({
|
||||
data: [[i, value]],
|
||||
color: (value >= mid) ? this.successColor : this.colorBad
|
||||
});
|
||||
}, this);
|
||||
|
||||
this.max = max;
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
@@ -110,6 +117,7 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch
|
||||
min: 0,
|
||||
showLabels: true,
|
||||
color: this.textColor,
|
||||
max: this.max + 0.08 * this.max,
|
||||
tickFormatter: function (value) {
|
||||
if (value == 0) {
|
||||
return '';
|
||||
@@ -127,6 +135,9 @@ Espo.define('crm:views/dashlets/sales-by-month', 'crm:views/dashlets/abstract/ch
|
||||
if (value % 1 == 0) {
|
||||
var i = parseInt(value);
|
||||
if (i in self.monthList) {
|
||||
if (self.monthList.length > 12 && i === self.monthList.length - 1) {
|
||||
return '';
|
||||
}
|
||||
return moment(self.monthList[i] + '-01').format('MMM YYYY');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,16 +69,15 @@ Espo.define('crm:views/dashlets/sales-pipeline', 'crm:views/dashlets/abstract/ch
|
||||
});
|
||||
}
|
||||
|
||||
this.maxY = 1000;
|
||||
var max = 0;
|
||||
if (d.length) {
|
||||
for (var i = 0; i < d.length; i++) {
|
||||
var y = d[i].value + (d[i].value / 20);
|
||||
if (y > this.maxY) {
|
||||
this.maxY = y;
|
||||
d.forEach(function (item) {
|
||||
if ( item.value && item.value > max) {
|
||||
max = item.value;
|
||||
}
|
||||
}
|
||||
|
||||
}, this);
|
||||
}
|
||||
this.max = max;
|
||||
|
||||
return data;
|
||||
},
|
||||
@@ -124,7 +123,7 @@ Espo.define('crm:views/dashlets/sales-pipeline', 'crm:views/dashlets/abstract/ch
|
||||
},
|
||||
yaxis: {
|
||||
min: 0,
|
||||
max: this.maxY,
|
||||
max: this.max + 0.08 * this.max,
|
||||
showLabels: false
|
||||
},
|
||||
xaxis: {
|
||||
@@ -147,7 +146,7 @@ Espo.define('crm:views/dashlets/sales-pipeline', 'crm:views/dashlets/abstract/ch
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
noColumns: this.getLegentColumnNumber(),
|
||||
noColumns: this.getLegendColumnNumber(),
|
||||
container: this.$el.find('.legend-container'),
|
||||
labelBoxMargin: 0,
|
||||
labelFormatter: self.labelFormatter.bind(self),
|
||||
@@ -155,6 +154,8 @@ Espo.define('crm:views/dashlets/sales-pipeline', 'crm:views/dashlets/abstract/ch
|
||||
backgroundOpacity: 0
|
||||
}
|
||||
});
|
||||
|
||||
this.adjustLegend();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
</button>
|
||||
<ul class="dropdown-menu pull-right">
|
||||
<li><a href="javascript:" data-action="editFormula" data-scope="{{name}}">{{translate 'Formula' scope='EntityManager'}}</a></li>
|
||||
{{#if isCustom}}
|
||||
{{#if isRemovable}}
|
||||
<li><a href="javascript:" data-action="removeEntity" data-scope="{{name}}">{{translate 'Remove'}}</a></li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
{{#unless fields}}
|
||||
{{translate 'No fields available for Mass Update'}}
|
||||
{{else}}
|
||||
{{else}}
|
||||
<div class="button-container">
|
||||
<button class="btn btn-default pull-right hidden" data-action="reset">{{translate 'Reset'}}</button>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" tabindex="-1">{{translate 'Select Field'}} <span class="caret"></span></button>
|
||||
<button class="btn btn-default dropdown-toggle select-field" data-toggle="dropdown" tabindex="-1">{{translate 'Select Field'}} <span class="caret"></span></button>
|
||||
<ul class="dropdown-menu pull-left filter-list">
|
||||
{{#each ../fields}}
|
||||
<li><a href="javascript:" data-name="{{./this}}" data-action="add-field">{{translate this scope=../../scope category='fields'}}</a></li>
|
||||
<li data-name="{{./this}}"><a href="javascript:" data-name="{{./this}}" data-action="add-field">{{translate this scope=../../scope category='fields'}}</a></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -39,6 +39,8 @@ Espo.define('controllers/base', 'controller', function (Dep) {
|
||||
},
|
||||
|
||||
logout: function () {
|
||||
var title = this.getConfig().get('applicationName') || 'EspoCRM';
|
||||
$('head title').text(title);
|
||||
this.trigger('logout');
|
||||
},
|
||||
|
||||
|
||||
@@ -90,9 +90,15 @@ Espo.define('views/admin/entity-manager/index', 'view', function (Dep) {
|
||||
scopeList.forEach(function (scope) {
|
||||
var d = this.getMetadata().get('scopes.' + scope);
|
||||
|
||||
var isRemovable = !!d.isCustom;
|
||||
if (d.isNotRemovable) {
|
||||
isRemovable = false;
|
||||
}
|
||||
|
||||
this.scopeDataList.push({
|
||||
name: scope,
|
||||
isCustom: d.isCustom,
|
||||
isRemovable: isRemovable,
|
||||
customizable: d.customizable,
|
||||
type: d.type,
|
||||
label: this.getLanguage().translate(scope, 'scopeNames'),
|
||||
|
||||
@@ -117,7 +117,7 @@ Espo.define('views/admin/field-manager/edit', ['view', 'model'], function (Dep,
|
||||
}.bind(this))
|
||||
]).then(function () {
|
||||
this.paramList = [];
|
||||
var paramList = this.getFieldManager().getParams(this.type) || [];
|
||||
var paramList = Espo.Utils.clone(this.getFieldManager().getParams(this.type) || []);
|
||||
|
||||
if (!this.isNew) {
|
||||
(this.getMetadata().get(['entityDefs', this.scope, 'fields', this.field, 'fieldManagerAdditionalParamList']) || []).forEach(function (item) {
|
||||
|
||||
@@ -112,13 +112,13 @@ Espo.define('views/fields/duration', 'views/fields/enum', function (Dep) {
|
||||
|
||||
var parts = [];
|
||||
if (days) {
|
||||
parts.push(days + '' + this.getLanguage().translate('d'));
|
||||
parts.push(days + '' + this.getLanguage().translate('d', 'durationUnits'));
|
||||
}
|
||||
if (hours) {
|
||||
parts.push(hours + '' + this.getLanguage().translate('h'));
|
||||
parts.push(hours + '' + this.getLanguage().translate('h', 'durationUnits'));
|
||||
}
|
||||
if (minutes) {
|
||||
parts.push(minutes + '' + this.getLanguage().translate('m'));
|
||||
parts.push(minutes + '' + this.getLanguage().translate('m', 'durationUnits'));
|
||||
}
|
||||
return parts.join(' ');
|
||||
},
|
||||
|
||||
@@ -49,13 +49,11 @@ Espo.define('views/modals/mass-update', 'views/modal', function (Dep) {
|
||||
},
|
||||
'click a[data-action="add-field"]': function (e) {
|
||||
var field = $(e.currentTarget).data('name');
|
||||
var $ul = $(e.currentTarget).closest('ul');
|
||||
$(e.currentTarget).parent().remove();
|
||||
if ($ul.children().size() == 0) {
|
||||
$ul.parent().find('button').addClass('disabled');
|
||||
}
|
||||
this.addField(field);
|
||||
},
|
||||
'click button[data-action="reset"]': function (e) {
|
||||
this.reset();
|
||||
}
|
||||
},
|
||||
|
||||
setup: function () {
|
||||
@@ -96,17 +94,23 @@ Espo.define('views/modals/mass-update', 'views/modal', function (Dep) {
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
this.fieldsToUpdate = [];
|
||||
this.fieldList = [];
|
||||
},
|
||||
|
||||
|
||||
|
||||
addField: function (name) {
|
||||
this.enableButton('update');
|
||||
|
||||
this.$el.find('[data-action="reset"]').removeClass('hidden');
|
||||
|
||||
this.$el.find('ul.filter-list li[data-name="'+name+'"]').addClass('hidden');
|
||||
|
||||
if (this.$el.find('ul.filter-list li:not(.hidden)').size() == 0) {
|
||||
this.$el.find('button.select-field').addClass('disabled').attr('disabled', 'disabled');
|
||||
}
|
||||
|
||||
this.notify('Loading...');
|
||||
var label = this.translate(name, 'fields', this.scope);
|
||||
var html = '<div class="cell form-group col-sm-6"><label class="control-label">'+label+'</label><div class="field" data-name="'+name+'" /></div>';
|
||||
var html = '<div class="cell form-group col-sm-6" data-name="'+name+'"><label class="control-label">'+label+'</label><div class="field" data-name="'+name+'" /></div>';
|
||||
this.$el.find('.fields-container').append(html);
|
||||
|
||||
var type = this.model.getFieldType(name);
|
||||
@@ -121,7 +125,7 @@ Espo.define('views/modals/mass-update', 'views/modal', function (Dep) {
|
||||
},
|
||||
mode: 'edit'
|
||||
}, function (view) {
|
||||
this.fieldsToUpdate.push(name);
|
||||
this.fieldList.push(name);
|
||||
view.render();
|
||||
view.notify(false);
|
||||
}.bind(this));
|
||||
@@ -131,7 +135,7 @@ Espo.define('views/modals/mass-update', 'views/modal', function (Dep) {
|
||||
var self = this;
|
||||
|
||||
var attributes = {};
|
||||
this.fieldsToUpdate.forEach(function (field) {
|
||||
this.fieldList.forEach(function (field) {
|
||||
var view = self.getView(field);
|
||||
_.extend(attributes, view.fetch());
|
||||
});
|
||||
@@ -139,7 +143,7 @@ Espo.define('views/modals/mass-update', 'views/modal', function (Dep) {
|
||||
this.model.set(attributes);
|
||||
|
||||
var notValid = false;
|
||||
this.fieldsToUpdate.forEach(function (field) {
|
||||
this.fieldList.forEach(function (field) {
|
||||
var view = self.getView(field);
|
||||
notValid = view.validate() || notValid;
|
||||
});
|
||||
@@ -164,12 +168,29 @@ Espo.define('views/modals/mass-update', 'views/modal', function (Dep) {
|
||||
},
|
||||
error: function () {
|
||||
self.notify('Error occurred', 'error');
|
||||
},
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.notify('Not valid', 'error');
|
||||
}
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
this.fieldList.forEach(function (field) {
|
||||
this.clearView(field);
|
||||
this.$el.find('.cell[data-name="'+field+'"]').remove();
|
||||
}, this);
|
||||
|
||||
this.fieldList = [];
|
||||
|
||||
this.model.clear();
|
||||
|
||||
this.$el.find('[data-action="reset"]').addClass('hidden');
|
||||
|
||||
this.$el.find('button.select-field').removeClass('disabled').removeAttr('disabled');
|
||||
this.$el.find('ul.filter-list').find('li').removeClass('hidden');
|
||||
|
||||
this.disableButton('update');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -253,9 +253,9 @@ Espo.define('views/record/base', ['view', 'view-record-helper', 'dynamic-logic']
|
||||
|
||||
this.recordHelper = new ViewRecordHelper();
|
||||
|
||||
this.on('remove', function () {
|
||||
this.once('remove', function () {
|
||||
if (this.isChanged) {
|
||||
this.model.set(this.attributes);
|
||||
this.resetModelChanges();
|
||||
}
|
||||
this.setIsNotChanged();
|
||||
}, this);
|
||||
@@ -302,6 +302,17 @@ Espo.define('views/record/base', ['view', 'view-record-helper', 'dynamic-logic']
|
||||
return !_.isEqual(this.attributes[name], this.model.get(name));
|
||||
},
|
||||
|
||||
resetModelChanges: function () {
|
||||
var attributes = this.model.attributes;
|
||||
for (var attr in attributes) {
|
||||
if (!(attr in this.attributes)) {
|
||||
this.model.unset(attr);
|
||||
}
|
||||
}
|
||||
|
||||
this.model.set(this.attributes);
|
||||
},
|
||||
|
||||
initDynamicLogic: function () {
|
||||
if (!Object.keys(this.dynamicLogicDefs || {}).length) return;
|
||||
|
||||
@@ -474,7 +485,7 @@ Espo.define('views/record/base', ['view', 'view-record-helper', 'dynamic-logic']
|
||||
|
||||
if (xhr.status == 400) {
|
||||
if (!this.isNew) {
|
||||
this.model.set(this.attributes);
|
||||
this.resetModelChanges();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -223,11 +223,25 @@ Espo.define('views/record/detail', ['views/record/base', 'view-record-helper'],
|
||||
}
|
||||
}
|
||||
|
||||
if (this.printPdfAction) {
|
||||
this.dropdownItemList.push({
|
||||
'label': 'Print to PDF',
|
||||
'name': 'printPdf'
|
||||
});
|
||||
if (this.type === 'detail') {
|
||||
var printPdfAction = this.printPdfAction;
|
||||
|
||||
if (!printPdfAction) {
|
||||
if (~(this.getHelper().getAppParam('templateEntityTypeList') || []).indexOf(this.entityType)) {
|
||||
printPdfAction = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (printPdfAction && !this.getAcl().check('Template', 'read')) {
|
||||
printPdfAction = false
|
||||
}
|
||||
|
||||
if (printPdfAction) {
|
||||
this.dropdownItemList.push({
|
||||
'label': 'Print to PDF',
|
||||
'name': 'printPdf'
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -459,11 +473,23 @@ Espo.define('views/record/detail', ['views/record/base', 'view-record-helper'],
|
||||
},
|
||||
|
||||
cancelEdit: function () {
|
||||
this.model.set(this.attributes);
|
||||
this.resetModelChanges();
|
||||
|
||||
this.setDetailMode();
|
||||
this.setIsNotChanged();
|
||||
},
|
||||
|
||||
resetModelChanges: function () {
|
||||
var attributes = this.model.attributes;
|
||||
for (var attr in attributes) {
|
||||
if (!(attr in this.attributes)) {
|
||||
this.model.unset(attr);
|
||||
}
|
||||
}
|
||||
|
||||
this.model.set(this.attributes);
|
||||
},
|
||||
|
||||
delete: function () {
|
||||
this.confirm({
|
||||
message: this.translate('removeRecordConfirmation', 'messages'),
|
||||
@@ -649,9 +675,9 @@ Espo.define('views/record/detail', ['views/record/base', 'view-record-helper'],
|
||||
}
|
||||
}
|
||||
|
||||
this.on('remove', function () {
|
||||
this.once('remove', function () {
|
||||
if (this.isChanged) {
|
||||
this.model.set(this.attributes);
|
||||
this.resetModelChanges();
|
||||
}
|
||||
this.setIsNotChanged();
|
||||
$(window).off('scroll.detail-' + this.numId);
|
||||
|
||||
@@ -57,9 +57,21 @@ Espo.define('views/template/record/edit', 'views/record/edit', function (Dep) {
|
||||
return;
|
||||
}
|
||||
|
||||
var header = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', entityType, 'header']);
|
||||
var body = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', entityType, 'body']);
|
||||
var footer = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', entityType, 'footer']);
|
||||
var header, body, footer;
|
||||
if (this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', entityType])) {
|
||||
header = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', entityType, 'header']);
|
||||
body = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', entityType, 'body']);
|
||||
footer = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', entityType, 'footer']);
|
||||
} else {
|
||||
var scopeType = this.getMetadata().get(['scopes', entityType, 'type']);
|
||||
if (scopeType) {
|
||||
if (this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', scopeType])) {
|
||||
header = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', scopeType, 'header']);
|
||||
body = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', scopeType, 'body']);
|
||||
footer = this.getMetadata().get(['entityDefs', 'Template', 'defaultTemplates', scopeType, 'footer']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (header) {
|
||||
this.model.set('header', header);
|
||||
|
||||
@@ -1280,9 +1280,10 @@ stream-head-text-container .span {
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
|
||||
.dashlet-body .legend-container {
|
||||
.legend-container {
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
padding-top: 7px;
|
||||
}
|
||||
|
||||
.legend-container table td {
|
||||
@@ -1301,12 +1302,20 @@ stream-head-text-container .span {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dashlet-body .legend-container td.flotr-legend-label > span {
|
||||
max-height: 2em;
|
||||
display: inline-block;
|
||||
.legend-container td.flotr-legend-label {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.legend-container td.flotr-legend-label > span {
|
||||
display: block;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
line-height: 1.3em;
|
||||
max-height: 15px;
|
||||
}
|
||||
|
||||
.legend-container .flotr-legend-color-box > div {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
@@ -1341,6 +1350,7 @@ table.table td.cell .complex-text {
|
||||
p:first-child,
|
||||
ul:first-child,
|
||||
ol:first-child,
|
||||
pre:first-child,
|
||||
blockquote:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
@@ -1829,4 +1839,13 @@ pre > code {
|
||||
.panel-body > div:first-child > .list:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.list > .table {
|
||||
.dropdown-menu {
|
||||
position: static !important;
|
||||
}
|
||||
.pull-right.open > .btn {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ table.table {
|
||||
}
|
||||
|
||||
.record .middle > .panel:first-child {
|
||||
border-top-width: 1px;
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
.record .middle > .panel {
|
||||
@@ -152,3 +152,7 @@ table.table {
|
||||
.admin-panel-iframe-container {
|
||||
background-color: @white-color;
|
||||
}
|
||||
|
||||
.panel.dashlet > .panel-heading > .btn-group {
|
||||
margin-top: -6px;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "5.0.4",
|
||||
"version": "5.0.5",
|
||||
"description": "",
|
||||
"main": "index.php",
|
||||
"repository": {
|
||||
|
||||
@@ -182,7 +182,23 @@ PO.prototype.fixString = function (savedString) {
|
||||
return savedString;
|
||||
}
|
||||
|
||||
var po = new PO(espoPath, language);
|
||||
|
||||
po.run();
|
||||
if (language === '--all') {
|
||||
var pathToLanguage = espoPath + '/application/Espo/Resources/i18n/';
|
||||
|
||||
var languageList = [];
|
||||
fs.readdirSync(pathToLanguage).forEach(function (dir) {
|
||||
if (dir.indexOf('_') == 2) {
|
||||
languageList.push(dir);
|
||||
}
|
||||
});
|
||||
|
||||
languageList.forEach(function (language) {
|
||||
var po = new PO(espoPath, language);
|
||||
po.run();
|
||||
});
|
||||
|
||||
} else {
|
||||
var po = new PO(espoPath, language);
|
||||
po.run();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user