calendar date scope support

This commit is contained in:
Yuri Kuznetsov
2025-08-07 16:15:04 +03:00
parent 2fa6162920
commit cb87133fae
6 changed files with 123 additions and 28 deletions

View File

@@ -5,7 +5,6 @@
"Task": "#70c173"
},
"scopeList": ["Meeting", "Call", "Task"],
"allDayScopeList": ["Task"],
"modeList": ["month", "agendaWeek", "timeline", "agendaDay"],
"sharedViewModeList": ["basicWeek", "month", "basicDay"],
"additionalColorList": ["#AB78AD", "#CC9B45"],

View File

@@ -14,6 +14,7 @@
"importable": true,
"notifications": true,
"calendar": true,
"calendarOneDay": true,
"object": true,
"statusField": "status",
"stream": true,

View File

@@ -61,6 +61,7 @@ use Espo\ORM\Query\Part\Condition as Cond;
use Espo\ORM\Query\Part\Expression as Expr;
use Espo\ORM\Query\Part\Where\OrGroup;
use Espo\ORM\Query\Select;
use Espo\ORM\Type\AttributeType;
use Espo\ORM\Type\RelationType;
use Espo\Tools\WorkingTime\Calendar as WorkingCalendar;
use Espo\Tools\WorkingTime\CalendarFactory as WorkingCalendarFactory;
@@ -274,15 +275,30 @@ class Service
$seed = $this->entityManager->getNewEntity($scope);
if ($seed->getAttributeType('dateStart') === AttributeType::DATE) {
$dateStartSelect = ['null', 'dateStart'];
$dateEndSelect = ['null', 'dateEnd'];
$dateStartDateSelect = ['dateStart', 'dateStartDate'];
$dateEndDateSelect = ['dateEnd', 'dateEndDate'];
} else {
$dateStartSelect = ['dateStart', 'dateStart'];
$dateEndSelect = ['dateEnd', 'dateEnd'];
$dateStartDateSelect = ($seed->hasAttribute('dateStartDate') ?
['dateStartDate', 'dateStartDate'] : ['null', 'dateStartDate']);
$dateEndDateSelect = ($seed->hasAttribute('dateEndDate') ?
['dateEndDate', 'dateEndDate'] : ['null', 'dateEndDate']);
}
$select = [
['"' . $scope . '"', 'scope'],
'id',
'name',
['dateStart', 'dateStart'],
['dateEnd', 'dateEnd'],
$dateStartSelect,
$dateEndSelect,
($seed->hasAttribute('status') ? ['status', 'status'] : ['null', 'status']),
($seed->hasAttribute('dateStartDate') ? ['dateStartDate', 'dateStartDate'] : ['null', 'dateStartDate']),
($seed->hasAttribute('dateEndDate') ? ['dateEndDate', 'dateEndDate'] : ['null', 'dateEndDate']),
$dateStartDateSelect,
$dateEndDateSelect,
($seed->hasAttribute('parentType') ? ['parentType', 'parentType'] : ['null', 'parentType']),
($seed->hasAttribute('parentId') ? ['parentId', 'parentId'] : ['null', 'parentId']),
Field::CREATED_AT,

View File

@@ -51,8 +51,31 @@ class CalendarView extends View {
eventAttributes = []
colors = {}
allDayScopeList = ['Task']
/**
* @private
* @type {string[]}
*/
allDayScopeList
/**
* @private
* @type {string[]}
*/
scopeList = ['Meeting', 'Call', 'Task']
/**
* @private
* @type {string[]}
*/
onlyDateScopeList
/**
* @private
* @type {string[]}
*/
enabledScopeList
header = true
modeList = []
fullCalendarModeList = [
@@ -204,11 +227,19 @@ class CalendarView extends View {
this.modeList = this.getMetadata()
.get('clientDefs.Calendar.modeList') || this.modeList;
this.scopeList = this.getConfig()
.get('calendarEntityList') || Espo.Utils.clone(this.scopeList);
this.scopeList = this.getConfig().get('calendarEntityList') || Espo.Utils.clone(this.scopeList);
this.allDayScopeList = this.getMetadata()
.get('clientDefs.Calendar.allDayScopeList') || this.allDayScopeList;
this.allDayScopeList = this.getMetadata().get('clientDefs.Calendar.allDayScopeList') ?? [];
this.scopeList.forEach(scope => {
if (this.getMetadata().get(`scopes.${scope}.calendarOneDay`) && !this.allDayScopeList.includes(scope)) {
this.allDayScopeList.push(scope);
}
});
this.onlyDateScopeList = this.scopeList.filter(scope => {
return this.getMetadata().get(`entityDefs.${scope}.fields.dateStart.type`) === 'date';
});
this.slotDuration = this.options.slotDuration ||
this.getPreferences().get('calendarSlotDuration') ||
@@ -553,13 +584,13 @@ class CalendarView extends View {
let start;
let end;
if (o.dateStart) {
if (o.dateStart || o.dateStartDate) {
start = !o.dateStartDate ?
this.getDateTime().toMoment(o.dateStart) :
this.dateToMoment(o.dateStartDate);
}
if (o.dateEnd) {
if (o.dateEnd || o.dateEndDate) {
end = !o.dateEndDate ?
this.getDateTime().toMoment(o.dateEnd) :
this.dateToMoment(o.dateEndDate);
@@ -737,7 +768,7 @@ class CalendarView extends View {
event.allDay = true;
event.allDayCopy = event.allDay;
if (!afterDrop) {
if (!afterDrop && end) {
end.add(1, 'days')
}
@@ -989,6 +1020,12 @@ class CalendarView extends View {
const scope = event.extendedProps.scope;
if (this.onlyDateScopeList.includes(scope)) {
info.revert();
return;
}
if (!event.allDay && event.extendedProps.allDayCopy) {
info.revert();
@@ -1249,11 +1286,13 @@ class CalendarView extends View {
attributes.assignedUserName = this.options.userName || this.options.userId;
}
const scopeList = this.enabledScopeList.filter(it => !this.onlyDateScopeList.includes(it));
Espo.Ui.notifyWait();
const view = await this.createView('dialog', 'crm:views/calendar/modals/edit', {
attributes: attributes,
enabledScopeList: this.enabledScopeList,
enabledScopeList: scopeList,
scopeList: this.scopeList,
allDay: values.allDay,
dateStartDate: values.dateStartDate,

View File

@@ -42,8 +42,33 @@ class TimelineView extends View {
eventAttributes = []
colors = {}
scopeList = []
/**
* @private
* @type {string[]}
*/
allDayScopeList
/**
* @private
* @type {string[]}
*/
scopeList = ['Meeting', 'Call', 'Task']
/**
* @private
* @type {string[]}
*/
enabledScopeList
/**
* @private
* @type {string[]}
*/
onlyDateScopeList
header = true
modeList = []
defaultMode = 'timeline'
maxRange = 120
@@ -166,14 +191,19 @@ class TimelineView extends View {
this.$container = this.options.$container;
this.colors = Espo.Utils
.clone(this.getMetadata().get('clientDefs.Calendar.colors') || this.colors || {});
this.modeList = this.getMetadata()
.get('clientDefs.Calendar.modeList') || this.modeList || [];
this.scopeList = this.getConfig()
.get('calendarEntityList') || Espo.Utils.clone(this.scopeList) || [];
this.allDayScopeList = this.getMetadata()
.get('clientDefs.Calendar.allDayScopeList') || this.allDayScopeList || [];
this.colors = Espo.Utils.clone(this.getMetadata().get('clientDefs.Calendar.colors') || this.colors || {});
this.modeList = this.getMetadata().get('clientDefs.Calendar.modeList') || this.modeList || [];
this.scopeList = this.getConfig().get('calendarEntityList') || Espo.Utils.clone(this.scopeList);
this.allDayScopeList = this.getMetadata().get('clientDefs.Calendar.allDayScopeList') ?? [];
this.scopeList.forEach(scope => {
if (this.getMetadata().get(`scopes.${scope}.calendarOneDay`) && !this.allDayScopeList.includes(scope)) {
this.allDayScopeList.push(scope);
}
});
this.colors = {...this.colors, ...this.getHelper().themeManager.getParam('calendarColors')};
@@ -211,6 +241,10 @@ class TimelineView extends View {
}
});
this.onlyDateScopeList = this.scopeList.filter(scope => {
return this.getMetadata().get(`entityDefs.${scope}.fields.dateStart.type`) === 'date';
});
if (this.options.calendarType) {
this.calendarType = this.options.calendarType;
} else {
@@ -403,7 +437,7 @@ class TimelineView extends View {
event[attr] = o[attr];
});
if (o.dateStart) {
if (o.dateStart || o.dateStartDate) {
if (!o.dateStartDate) {
event.start = this.getDateTime().toMoment(o.dateStart);
} else {
@@ -411,7 +445,7 @@ class TimelineView extends View {
}
}
if (o.dateEnd) {
if (o.dateEnd || o.dateEndDate) {
if (!o.dateEndDate) {
event.end = this.getDateTime().toMoment(o.dateEnd);
} else {
@@ -419,7 +453,7 @@ class TimelineView extends View {
}
}
if (o.dateStartDate && !~this.allDayScopeList.indexOf(o.scope)) {
if (o.dateStartDate && !this.allDayScopeList.includes(o.scope) && event.end) {
event.end = event.end.clone().add(1, 'days');
}
@@ -427,7 +461,7 @@ class TimelineView extends View {
return event;
}
if (~this.allDayScopeList.indexOf(o.scope)) {
if (this.allDayScopeList.includes(o.scope)) {
event.type = 'box';
if (event.end) {
@@ -697,11 +731,13 @@ class TimelineView extends View {
attributes.assignedUserName = userName || userId;
}
const scopeList = this.enabledScopeList.filter(it => !this.onlyDateScopeList.includes(it));
Espo.Ui.notifyWait();
const view = await this.createView('dialog', 'crm:views/calendar/modals/edit', {
attributes: attributes,
enabledScopeList: this.enabledScopeList,
enabledScopeList: scopeList,
scopeList: this.scopeList
});

View File

@@ -249,6 +249,10 @@
"description": "Whether the entity can be displayed on the Calendar.",
"type": "boolean"
},
"calendarOneDay": {
"description": "Display as one-day events in the calendar.",
"type": "boolean"
},
"activity": {
"description": "Whether the entity can be displayed on the Activities panel.",
"type": "boolean"