event status treatment editable

This commit is contained in:
Yuri Kuznetsov
2023-05-01 14:21:27 +03:00
parent 7960eb87cc
commit c681c3ba54
21 changed files with 253 additions and 141 deletions
@@ -30,15 +30,20 @@
namespace Espo\Classes\Select\Event\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Core\Templates\Entities\Event;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Query\SelectBuilder;
class Held implements Filter
{
public function __construct(
private string $entityType,
private Metadata $metadata
) {}
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'status' => Event::STATUS_HELD,
]);
$statusList = $this->metadata->get(['scopes', $this->entityType, 'completedStatusList']) ?? [];
$queryBuilder->where(['status' => $statusList]);
}
}
@@ -30,15 +30,20 @@
namespace Espo\Classes\Select\Event\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Core\Templates\Entities\Event;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Query\SelectBuilder;
class Planned implements Filter
{
public function __construct(
private string $entityType,
private Metadata $metadata
) {}
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'status' => Event::STATUS_PLANNED,
]);
$statusList = $this->metadata->get(['scopes', $this->entityType, 'activityStatusList']) ?? [];
$queryBuilder->where(['status' => $statusList]);
}
}
@@ -12,6 +12,8 @@
"notifications": true,
"activityStatusList": ["Planned"],
"historyStatusList": ["Held", "Not Held"],
"completedStatusList": ["Held"],
"canceledStatusList": ["Not Held"],
"statusField": "status",
"statusFieldLocked": true
}
@@ -30,15 +30,20 @@
namespace Espo\Modules\Crm\Classes\Select\Meeting\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Modules\Crm\Entities\Meeting;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Query\SelectBuilder;
class Held implements Filter
{
public function __construct(
private string $entityType,
private Metadata $metadata
) {}
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'status' => Meeting::STATUS_HELD,
]);
$statusList = $this->metadata->get(['scopes', $this->entityType, 'completedStatusList']) ?? [];
$queryBuilder->where(['status' => $statusList]);
}
}
@@ -30,15 +30,20 @@
namespace Espo\Modules\Crm\Classes\Select\Meeting\PrimaryFilters;
use Espo\Core\Select\Primary\Filter;
use Espo\Modules\Crm\Entities\Meeting;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Query\SelectBuilder;
class Planned implements Filter
{
public function __construct(
private string $entityType,
private Metadata $metadata
) {}
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where([
'status' => Meeting::STATUS_PLANNED,
]);
$statusList = $this->metadata->get(['scopes', $this->entityType, 'activityStatusList']) ?? [];
$queryBuilder->where(['status' => $statusList]);
}
}
@@ -33,36 +33,21 @@ use Espo\Entities\User;
use Espo\ORM\Query\SelectBuilder;
use Espo\ORM\Query\Part\Condition as Cond;
use Espo\Core\Select\Primary\Filter;
use Espo\Core\Select\Helpers\UserTimeZoneProvider;
use Espo\Core\Select\Where\Item;
use Espo\Core\Select\Where\ConverterFactory;
use Espo\Core\Utils\Metadata;
use Espo\Modules\Crm\Entities\Task;
class ActualStartingNotInFuture implements Filter
{
private $user;
private $userTimeZoneProvider;
private $metadata;
private $converterFactory;
public function __construct(
User $user,
UserTimeZoneProvider $userTimeZoneProvider,
Metadata $metadata,
ConverterFactory $converterFactory
) {
$this->user = $user;
$this->userTimeZoneProvider = $userTimeZoneProvider;
$this->metadata = $metadata;
$this->converterFactory = $converterFactory;
}
private User $user,
private UserTimeZoneProvider $userTimeZoneProvider,
private Metadata $metadata,
private ConverterFactory $converterFactory
) {}
public function apply(SelectBuilder $queryBuilder): void
{
@@ -91,7 +76,7 @@ class ActualStartingNotInFuture implements Filter
$converter->convert(
$queryBuilder,
Item::fromRaw([
'type' => 'past',
'type' => Item\Type::PAST,
'attribute' => 'dateStart',
'timeZone' => $this->userTimeZoneProvider->get(),
'dateTime' => true,
@@ -100,7 +85,7 @@ class ActualStartingNotInFuture implements Filter
$converter->convert(
$queryBuilder,
Item::fromRaw([
'type' => 'today',
'type' => Item\Type::TODAY,
'attribute' => 'dateStart',
'timeZone' => $this->userTimeZoneProvider->get(),
'dateTime' => true,
@@ -29,20 +29,21 @@
namespace Espo\Modules\Crm\Classes\Select\Task\PrimaryFilters;
use Espo\Core\Utils\Metadata;
use Espo\Modules\Crm\Entities\Task;
use Espo\ORM\Query\SelectBuilder;
use Espo\ORM\Query\Part\Condition as Cond;
use Espo\Core\Select\Primary\Filter;
class Completed implements Filter
{
public function __construct(
private Metadata $metadata
) {}
public function apply(SelectBuilder $queryBuilder): void
{
$queryBuilder->where(
Cond::equal(
Cond::column('status'),
'Completed'
)
);
$statusList = $this->metadata->get(['scopes', Task::ENTITY_TYPE, 'completedStatusList']) ?? [];
$queryBuilder->where(['status' => $statusList]);
}
}
@@ -29,9 +29,9 @@
namespace Espo\Modules\Crm\Classes\Select\Task\PrimaryFilters;
use Espo\Modules\Crm\Entities\Task;
use Espo\ORM\Query\SelectBuilder;
use Espo\ORM\Query\Part\Condition as Cond;
use Espo\Core\Select\Primary\Filter;
class Deferred implements Filter
@@ -41,7 +41,7 @@ class Deferred implements Filter
$queryBuilder->where(
Cond::equal(
Cond::column('status'),
'Deferred'
Task::STATUS_DEFERRED
)
);
}
@@ -30,52 +30,33 @@
namespace Espo\Modules\Crm\Classes\Select\Task\PrimaryFilters;
use Espo\Entities\User;
use Espo\ORM\Query\SelectBuilder;
use Espo\ORM\Query\Part\Condition as Cond;
use Espo\Core\Select\Primary\Filter;
use Espo\Core\Select\Helpers\UserTimeZoneProvider;
use Espo\Core\Select\Where\Item;
use Espo\Core\Select\Where\ConverterFactory;
use Espo\Core\Utils\Metadata;
use Espo\Modules\Crm\Entities\Task;
class Overdue implements Filter
{
private $user;
private $userTimeZoneProvider;
private $metadata;
private $converterFactory;
public function __construct(
User $user,
UserTimeZoneProvider $userTimeZoneProvider,
Metadata $metadata,
ConverterFactory $converterFactory
) {
$this->user = $user;
$this->userTimeZoneProvider = $userTimeZoneProvider;
$this->metadata = $metadata;
$this->converterFactory = $converterFactory;
}
private User $user,
private UserTimeZoneProvider $userTimeZoneProvider,
private Metadata $metadata,
private ConverterFactory $converterFactory
) {}
public function apply(SelectBuilder $queryBuilder): void
{
$notActualStatusList =
array_filter(
$notActualStatusList = array_filter(
$this->metadata->get(['entityDefs', 'Task', 'fields', 'status', 'notActualOptions']) ?? [],
function (string $item) {
return $item !== 'Deferred';
}
fn(string $item) => $item !== Task::STATUS_DEFERRED
);
$pastItem = Item::fromRaw([
'type' => 'past',
'type' => Item\Type::PAST,
'attribute' => 'dateEnd',
'timeZone' => $this->userTimeZoneProvider->get(),
'dateTime' => true,
@@ -1,8 +1,14 @@
{
"fields": {
"historyStatusList": "History Statuses"
"activityStatusList": "Activity Statuses",
"historyStatusList": "History Statuses",
"completedStatusList": "Completed Statues",
"canceledStatusList": "Canceled Statues"
},
"tooltips": {
"historyStatusList": "Status values determining that a record should be displayed in the History panel."
"activityStatusList": "Status values determining that an activity record should be displayed in the Activity panel and considered as actual.",
"historyStatusList": "Status values determining that an activity record should be displayed in the History panel.",
"completedStatusList": "Status values determining that an activity is completed.",
"canceledStatusList": "Status values determining that an activity is canceled and won't be taken into account in free/busy ranges."
}
}
@@ -1,7 +1,5 @@
{
"additionalAttributeList": [
"color"
],
"canceledStatusList": ["Not Held", "Canceled"],
"completedStatusList": ["Held", "Completed"]
]
}
@@ -1,34 +1,135 @@
{
"Meeting": {
"activityStatusList": {
"location": "scopes",
"fieldDefs": {
"type": "multiEnum",
"required": true,
"tooltip": true,
"view": "crm:views/admin/entity-manager/fields/status-list"
}
},
"historyStatusList": {
"location": "scopes",
"fieldDefs": {
"type": "multiEnum",
"required": true,
"tooltip": true,
"view": "crm:views/admin/entity-manager/fields/history-status-list"
"view": "crm:views/admin/entity-manager/fields/status-list"
}
},
"completedStatusList": {
"location": "scopes",
"fieldDefs": {
"type": "multiEnum",
"required": true,
"tooltip": true,
"view": "crm:views/admin/entity-manager/fields/status-list"
}
},
"canceledStatusList": {
"location": "scopes",
"fieldDefs": {
"type": "multiEnum",
"required": true,
"tooltip": true,
"view": "crm:views/admin/entity-manager/fields/status-list"
}
}
},
"Call": {
"activityStatusList": {
"location": "scopes",
"fieldDefs": {
"type": "multiEnum",
"required": true,
"tooltip": true,
"view": "crm:views/admin/entity-manager/fields/status-list"
}
},
"historyStatusList": {
"location": "scopes",
"fieldDefs": {
"type": "multiEnum",
"required": true,
"tooltip": true,
"view": "crm:views/admin/entity-manager/fields/history-status-list"
"view": "crm:views/admin/entity-manager/fields/status-list"
}
},
"completedStatusList": {
"location": "scopes",
"fieldDefs": {
"type": "multiEnum",
"required": true,
"tooltip": true,
"view": "crm:views/admin/entity-manager/fields/status-list"
}
},
"canceledStatusList": {
"location": "scopes",
"fieldDefs": {
"type": "multiEnum",
"required": true,
"tooltip": true,
"view": "crm:views/admin/entity-manager/fields/status-list"
}
}
},
"Task": {
"completedStatusList": {
"location": "scopes",
"fieldDefs": {
"type": "multiEnum",
"required": true,
"tooltip": true,
"view": "crm:views/admin/entity-manager/fields/status-list"
}
},
"canceledStatusList": {
"location": "scopes",
"fieldDefs": {
"type": "multiEnum",
"required": true,
"tooltip": true,
"view": "crm:views/admin/entity-manager/fields/status-list"
}
}
},
"@Event": {
"activityStatusList": {
"location": "scopes",
"fieldDefs": {
"type": "multiEnum",
"required": true,
"tooltip": true,
"view": "crm:views/admin/entity-manager/fields/status-list"
}
},
"historyStatusList": {
"location": "scopes",
"fieldDefs": {
"type": "multiEnum",
"required": true,
"tooltip": true,
"view": "crm:views/admin/entity-manager/fields/history-status-list"
"view": "crm:views/admin/entity-manager/fields/status-list"
}
},
"completedStatusList": {
"location": "scopes",
"fieldDefs": {
"type": "multiEnum",
"required": true,
"tooltip": true,
"view": "crm:views/admin/entity-manager/fields/status-list"
}
},
"canceledStatusList": {
"location": "scopes",
"fieldDefs": {
"type": "multiEnum",
"required": true,
"tooltip": true,
"view": "crm:views/admin/entity-manager/fields/status-list"
}
}
}
@@ -13,6 +13,8 @@
"object": true,
"activityStatusList": ["Planned"],
"historyStatusList": ["Held", "Not Held"],
"completedStatusList": ["Held"],
"canceledStatusList": ["Not Held"],
"statusField": "status",
"statusFieldLocked": true
}
@@ -14,6 +14,8 @@
"object": true,
"activityStatusList": ["Planned"],
"historyStatusList": ["Held", "Not Held"],
"completedStatusList": ["Held"],
"canceledStatusList": ["Not Held"],
"statusField": "status",
"statusFieldLocked": true
}
@@ -7,6 +7,8 @@
"aclPortalLevelList": ["all", "account", "contact", "own", "no"],
"activityStatusList": ["Not Started", "Started"],
"historyStatusList": ["Completed"],
"completedStatusList": ["Completed"],
"canceledStatusList": ["Canceled"],
"module": "Crm",
"customizable": true,
"importable": true,
@@ -62,6 +62,8 @@ use RuntimeException;
class Service
{
private const BUSY_RANGES_MAX_RANGE_DAYS = 10;
/** @var array<string, string[]> */
private array $entityTypeCanceledStatusListCacheMap = [];
public function __construct(
private EntityManager $entityManager,
@@ -757,8 +759,6 @@ class Service
}
}
$canceledStatusList = $this->metadata->get(['app', 'calendar', 'canceledStatusList']) ?? [];
foreach ($eventList as $event) {
if (!$event instanceof Event) {
continue;
@@ -773,7 +773,7 @@ class Service
continue;
}
if (in_array($status, $canceledStatusList)) {
if (in_array($status, $this->getEntityTypeCanceledStatusList($event->getEntityType()))) {
continue;
}
@@ -850,7 +850,18 @@ class Service
}
/**
* @return array<int,WorkingRange|NonWorkingRange>
* @return string[]
*/
private function getEntityTypeCanceledStatusList(string $entityType): array
{
$this->entityTypeCanceledStatusListCacheMap[$entityType] ??=
$this->metadata->get(['scopes', $entityType, 'canceledStatusList']) ?? [];
return $this->entityTypeCanceledStatusListCacheMap[$entityType];
}
/**
* @return array<int, WorkingRange|NonWorkingRange>
*/
private function getWorkingRangeList(WorkingCalendar $calendar, FetchParams $fetchParams): array
{
@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('crm:views/admin/entity-manager/fields/history-status-list', ['views/fields/multi-enum'], function (Dep) {
define('crm:views/admin/entity-manager/fields/status-list', ['views/fields/multi-enum'], function (Dep) {
return Dep.extend({
@@ -36,8 +36,6 @@ define('crm:views/admin/entity-manager/fields/history-status-list', ['views/fiel
this.params.options = Espo.Utils.clone(
this.getMetadata().get(['entityDefs', entityType, 'fields', 'status', 'options'])) || [];
this.params.options = this.params.options.filter(item => item !== 'Planned');
this.params.translation = `${entityType}.options.status`;
},
});
@@ -33,21 +33,11 @@ define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], function (D
template: 'crm:calendar/calendar',
eventAttributes: [],
colors: {},
allDayScopeList: ['Task'],
scopeList: ['Meeting', 'Call', 'Task'],
canceledStatusList: [],
completedStatusList: [],
header: true,
modeList: [],
fullCalendarModeList: [
'month',
'agendaWeek',
@@ -56,11 +46,8 @@ define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], function (D
'basicDay',
'listWeek',
],
defaultMode: 'agendaWeek',
slotDuration: 30,
titleFormat: {
month: 'MMMM YYYY',
week: 'MMMM YYYY',
@@ -133,10 +120,6 @@ define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], function (D
.clone(this.getMetadata().get('clientDefs.Calendar.colors') || this.colors);
this.modeList = this.getMetadata()
.get('clientDefs.Calendar.modeList') || this.modeList;
this.canceledStatusList = this.getMetadata()
.get('app.calendar.canceledStatusList') || this.canceledStatusList;
this.completedStatusList = this.getMetadata()
.get('app.calendar.completedStatusList') || this.completedStatusList;
this.scopeList = this.getConfig()
.get('calendarEntityList') || Espo.Utils.clone(this.scopeList);
this.allDayScopeList = this.getMetadata()
@@ -424,6 +407,22 @@ define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], function (D
return event;
},
/**
* @param {string} scope
* @return {string[]}
*/
getEventTypeCompletedStatusList: function (scope) {
return this.getMetadata().get(['scopes', scope, 'completedStatusList']) || [];
},
/**
* @param {string} scope
* @return {string[]}
*/
getEventTypeCanceledStatusList: function (scope) {
return this.getMetadata().get(['scopes', scope, 'canceledStatusList']) || [];
},
fillColor: function (event) {
let color = this.colors[event.scope];
@@ -437,7 +436,10 @@ define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], function (D
if (
color &&
~this.completedStatusList.indexOf(event.status) || ~this.canceledStatusList.indexOf(event.status)
(
this.getEventTypeCompletedStatusList(event.scope).includes(event.status) ||
this.getEventTypeCanceledStatusList(event.scope).includes(event.status)
)
) {
color = this.shadeColor(color, 0.4);
}
@@ -446,7 +448,7 @@ define('crm:views/calendar/calendar', ['view', 'lib!full-calendar'], function (D
},
handleStatus: function (event) {
if (~this.canceledStatusList.indexOf(event.status)) {
if (this.getEventTypeCanceledStatusList(event.scope).includes(event.status)) {
event.className = ['event-canceled'];
} else {
event.className = [];
@@ -33,33 +33,17 @@ define('crm:views/calendar/timeline', ['view', 'lib!vis'], function (Dep, Vis) {
template: 'crm:calendar/timeline',
eventAttributes: [],
colors: {},
scopeList: [],
canceledStatusList: [],
completedStatusList: [],
header: true,
modeList: [],
defaultMode: 'timeline',
maxRange: 120,
rangeMarginThreshold: 12 * 3600,
leftMargin: 24 * 3600,
rightMargin: 48 * 3600,
calendarType: 'single',
calendarTypeList: ['single', 'shared'],
zoomPercentage: 1,
data: function () {
@@ -150,10 +134,6 @@ define('crm:views/calendar/timeline', ['view', 'lib!vis'], function (Dep, Vis) {
.clone(this.getMetadata().get('clientDefs.Calendar.colors') || this.colors || {});
this.modeList = this.getMetadata()
.get('clientDefs.Calendar.modeList') || this.modeList || [];
this.canceledStatusList = this.getMetadata()
.get('app.calendar.canceledStatusList') || this.canceledStatusList || [];
this.completedStatusList = this.getMetadata()
.get('app.calendar.completedStatusList') || this.completedStatusList || [];
this.scopeList = this.getConfig()
.get('calendarEntityList') || Espo.Utils.clone(this.scopeList) || [];
this.allDayScopeList = this.getMetadata()
@@ -509,6 +489,22 @@ define('crm:views/calendar/timeline', ['view', 'lib!vis'], function (Dep, Vis) {
return event;
},
/**
* @param {string} scope
* @return {string[]}
*/
getEventTypeCompletedStatusList: function (scope) {
return this.getMetadata().get(['scopes', scope, 'completedStatusList']) || [];
},
/**
* @param {string} scope
* @return {string[]}
*/
getEventTypeCanceledStatusList: function (scope) {
return this.getMetadata().get(['scopes', scope, 'canceledStatusList']) || [];
},
fillColor: function (event) {
let key = event.scope;
@@ -529,8 +525,8 @@ define('crm:views/calendar/timeline', ['view', 'lib!vis'], function (Dep, Vis) {
if (
event.status &&
(
~this.completedStatusList.indexOf(event.status) ||
~this.canceledStatusList.indexOf(event.status)
this.getEventTypeCompletedStatusList(event.scope).includes(event.status) ||
this.getEventTypeCanceledStatusList(event.scope).includes(event.status)
)
) {
color = this.shadeColor(color, 0.4);
@@ -542,7 +538,7 @@ define('crm:views/calendar/timeline', ['view', 'lib!vis'], function (Dep, Vis) {
},
handleStatus: function (event) {
if (~this.canceledStatusList.indexOf(event.status)) {
if (this.getEventTypeCanceledStatusList(event.scope).includes(event.status)) {
event.className += ' event-canceled';
}
},
@@ -33,13 +33,13 @@ define('views/event/fields/name-for-history', ['views/fields/varchar'], function
listLinkTemplate: 'event/fields/name-for-history/list-link',
data: function () {
var data = Dep.prototype.data.call(this);
let data = Dep.prototype.data.call(this);
var status = this.model.get('status');
let status = this.model.get('status');
var canceledStatusList = this.getMetadata().get('app.calendar.canceledStatusList') || [];
let canceledStatusList = this.getMetadata().get(['scopes', this.model.entityType, 'canceledStatusList']);
data.strikethrough = !!~canceledStatusList.indexOf(status);
data.strikethrough = canceledStatusList.includes(status);
return data;
},
+8 -3
View File
@@ -65,11 +65,16 @@ class AfterUpgrade
$toSave = true;
$data = $metadata->getCustom('selectDefs', $entityType);
$data1 = $metadata->getCustom('selectDefs', $entityType);
$data1->primaryFilterClassNameMap = $primaryFilterClassNameMap;
$data->primaryFilterClassNameMap = $primaryFilterClassNameMap;
$metadata->saveCustom('selectDefs', $entityType, $data1);
$metadata->saveCustom('selectDefs', $entityType, $data);
$data2 = $metadata->getCustom('scopes', $entityType);
$data2->completedStatusList = ['Held'];
$data2->canceledStatusList = ['Not Held'];
$metadata->saveCustom('scopes', $entityType, $data2);
}
if ($toSave) {