case - contact many-to-many

This commit is contained in:
yuri
2015-08-13 14:54:31 +03:00
parent 94c345c386
commit 3eb4cf27f2
12 changed files with 355 additions and 27 deletions
@@ -0,0 +1,69 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
namespace Espo\Modules\Crm\Repositories;
use Espo\ORM\Entity;
class CaseObj extends \Espo\Core\ORM\Repositories\RDB
{
public function afterSave(Entity $entity, array $options)
{
$result = parent::afterSave($entity, $options);
$this->handleAfterSaveContacts($entity, $options);
return $result;
}
protected function handleAfterSaveContacts(Entity $entity, array $options)
{
$contactIdChanged = $entity->has('contactId') && $entity->get('contactId') != $entity->getFetched('contactId');
if ($contactIdChanged) {
$contactId = $entity->get('contactId');
if (empty($contactId)) {
$this->unrelate($entity, 'contacts', $entity->getFetched('contactId'));
return;
}
}
if ($contactIdChanged) {
$pdo = $this->getEntityManager()->getPDO();
$sql = "
SELECT id FROM case_contact
WHERE
contact_id = ".$pdo->quote($contactId)." AND
case_id = ".$pdo->quote($entity->id)." AND
deleted = 0
";
$sth = $pdo->prepare($sql);
$sth->execute();
if (!$sth->fetch()) {
if ($contactIdChanged) {
$this->relate($entity, 'accounts');
}
}
}
}
}
@@ -43,7 +43,17 @@ class Contact extends \Espo\Core\ORM\Repositories\RDB
public function afterSave(Entity $entity, array $options)
{
$result = parent::afterSave($entity, $options);
$this->handleAfterSaveAccounts($entity, $options);
if ($entity->has('targetListId') && $entity->isNew()) {
$this->relate($entity, 'targetLists', $entity->get('targetListId'));
}
return $result;
}
protected function handleAfterSaveAccounts(Entity $entity, array $options)
{
$accountIdChanged = $entity->has('accountId') && $entity->get('accountId') != $entity->getFetched('accountId');
$titleChanged = $entity->has('title') && $entity->get('title') != $entity->getFetched('title');
@@ -51,7 +61,7 @@ class Contact extends \Espo\Core\ORM\Repositories\RDB
$accountId = $entity->get('accountId');
if (empty($accountId)) {
$this->unrelate($entity, 'accounts', $entity->getFetched('accountId'));
return $result;
return;
}
}
@@ -59,7 +69,7 @@ class Contact extends \Espo\Core\ORM\Repositories\RDB
if (empty($accountId)) {
$accountId = $entity->getFetched('accountId');
if (empty($accountId)) {
return $result;
return;
}
}
}
@@ -91,12 +101,6 @@ class Contact extends \Espo\Core\ORM\Repositories\RDB
}
}
}
if ($entity->has('targetListId') && $entity->isNew()) {
$this->relate($entity, 'targetLists', $entity->get('targetListId'));
}
return $result;
}
}
@@ -5,13 +5,21 @@
"status": "Status",
"account": "Account",
"contact": "Contact",
"contacts": "Contacts",
"priority": "Priority",
"type": "Type",
"description": "Description",
"inboundEmail": "Inbound Email"
},
"links": {
"inboundEmail": "Inbound Email"
"inboundEmail": "Inbound Email",
"account": "Account",
"contact": "Contact (Primary)",
"Contacts": "Contacts",
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks",
"emails": "Emails"
},
"options": {
"status": {
@@ -21,7 +21,10 @@
"cases": "Cases",
"targetLists": "Target Lists",
"campaignLogRecords": "Campaign Log",
"campaign": "Campaign"
"campaign": "Campaign",
"account": "Account (Primary)",
"accounts": "Accounts",
"casesPrimary": "Cases (Primary)"
},
"labels": {
"Create Contact": "Create Contact"
@@ -56,7 +56,7 @@
"Log Call": "Log Call",
"Archive Email": "Archive Email",
"Create Task": "Create Task",
"Tasks": "Tasks"
"Tasks": "Tasks"
},
"fields": {
"billingAddressCity": "City",
@@ -68,7 +68,7 @@
"addressStreet": "Street",
"addressCountry": "Country",
"addressState": "State",
"addressPostalCode": "Postal Code",
"addressPostalCode": "Postal Code",
"shippingAddressCity": "City (Shipping)",
"shippingAddressStreet": "Street (Shipping)",
"shippingAddressCountry": "Country (Shipping)",
@@ -78,7 +78,7 @@
"links": {
"contacts": "Contacts",
"opportunities": "Opportunities",
"leads": "Leads",
"leads": "Leads",
"meetings": "Meetings",
"calls": "Calls",
"tasks": "Tasks",
@@ -87,7 +87,7 @@
"cases": "Cases",
"documents": "Documents",
"account": "Account",
"opportunity": "Opportunity",
"opportunity": "Opportunity",
"contact": "Contact",
"parent": "Parent"
},
@@ -4,7 +4,7 @@
"rows":[
[{"name":"name"},{"name":"number"}],
[{"name":"status"},{"name":"account"}],
[{"name":"priority"},{"name":"contact"}],
[{"name":"priority"},{"name":"contacts"}],
[{"name":"type"},false],
[{"name":"description", "fullWidth": true}]
]
@@ -41,6 +41,10 @@
"type": "link",
"view": "Crm:Case.Fields.Contact"
},
"contacts": {
"type": "linkMultiple",
"view": "crm:views/case/fields/contacts"
},
"inboundEmail": {
"type": "link",
"readOnly": true
@@ -100,7 +104,13 @@
"contact": {
"type": "belongsTo",
"entity": "Contact",
"foreign": "cases"
"foreign": "casesPrimary"
},
"contacts": {
"type": "hasMany",
"entity": "Contact",
"foreign": "cases",
"layoutRelationshipsDisabled": true
},
"meetings": {
"type": "hasChildren",
@@ -183,10 +183,16 @@
"entity": "Opportunity",
"foreign": "contacts"
},
"casesPrimary": {
"type": "hasMany",
"entity": "Case",
"foreign": "contact",
"layoutRelationshipsDisabled": true
},
"cases": {
"type": "hasMany",
"entity": "Case",
"foreign": "contact"
"foreign": "contacts"
},
"meetings": {
"type": "hasMany",
@@ -0,0 +1,43 @@
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
Espo.define('crm:views/case/fields/contacts', 'views/fields/link-multiple-with-primary', function (Dep) {
return Dep.extend({
primaryLink: 'contact',
getSelectFilters: function () {
if (this.model.get('accountId')) {
return {
'account': {
type: 'equals',
field: 'accountId',
value: this.model.get('accountId'),
valueName: this.model.get('accountName')
}
};
}
}
});
});
@@ -116,7 +116,7 @@ Espo.define('Crm:Views.Contact.Fields.Accounts', 'Views.Fields.LinkMultipleWithR
return Dep.prototype.addLinkHtml.call(this, id, name);
}
$el = Dep.prototype.addLinkHtml.call(this, id, name);
var $el = Dep.prototype.addLinkHtml.call(this, id, name);
var isPrimary = (id == this.primaryId);
@@ -0,0 +1,191 @@
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: http://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
************************************************************************/
Espo.define('views/fields/link-multiple-with-primary', 'views/fields/link-multiple', function (Dep) {
return Dep.extend({
primaryLink: null,
events: {
'click [data-action="switchPrimary"]': function (e) {
$target = $(e.currentTarget);
var id = $target.data('id');
if (!$target.hasClass('active')) {
this.$el.find('button[data-action="switchPrimary"]').removeClass('active').children().addClass('text-muted');
$target.addClass('active').children().removeClass('text-muted');
this.setPrimaryId(id);
}
}
},
getAttributeList: function () {
var list = Dep.prototype.getAttributeList.call(this);
list.push(this.primaryIdFieldName);
list.push(this.primaryNameFieldName);
return list;
},
setup: function () {
this.primaryLink = this.options.primaryLink || this.primaryLink;
this.primaryIdFieldName = this.primaryLink + 'Id';
this.primaryNameFieldName = this.primaryLink + 'Name';
Dep.prototype.setup.call(this);
this.primaryId = this.model.get(this.primaryIdFieldName);
this.primaryName = this.model.get(this.primaryNameFieldName);
this.listenTo(this.model, 'change:' + this.primaryIdFieldName, function () {
this.primaryId = this.model.get(this.primaryIdFieldName);
this.primaryName = this.model.get(this.primaryNameFieldName);
}.bind(this));
},
setPrimaryId: function (id) {
this.primaryId = id;
if (id) {
this.primaryName = this.nameHash[id];
} else {
this.primaryName = null;
}
this.trigger('change');
},
renderLinks: function () {
if (this.primaryId) {
this.addLinkHtml(this.primaryId, this.primaryName);
}
this.ids.forEach(function (id) {
if (id != this.primaryId) {
this.addLinkHtml(id, this.nameHash[id]);
}
}, this);
},
getValueForDisplay: function () {
if (this.mode == 'detail' || this.mode == 'list') {
var names = [];
if (this.primaryId) {
names.push(this.getDetailLinkHtml(this.primaryId, this.primaryName));
}
if (!this.ids.length) {
return;
}
this.ids.forEach(function (id) {
if (id != this.primaryId) {
names.push(this.getDetailLinkHtml(id));
}
}, this);
return '<div>' + names.join('</div><div>') + '</div>';
}
},
deleteLink: function (id) {
if (id == this.primaryId) {
this.setPrimaryId(null);
}
Dep.prototype.deleteLink.call(this, id);
},
deleteLinkHtml: function (id) {
Dep.prototype.deleteLinkHtml.call(this, id);
this.managePrimaryButton();
},
addLinkHtml: function (id, name) {
if (this.mode == 'search') {
return Dep.prototype.addLinkHtml.call(this, id, name);
}
var $container = this.$el.find('.link-container');
var $el = $('<div class="form-inline list-group-item link-with-role">').addClass('link-' + id);
var nameHtml = '<div>' + name + '&nbsp;' + '</div>';
var removeHtml = '<a href="javascript:" class="pull-right" data-id="' + id + '" data-action="clearLink"><span class="glyphicon glyphicon-remove"></a>';
$left = $('<div class="pull-left">').css({
'width': '92%',
'display': 'inline-block'
});
$left.append(nameHtml);
$el.append($left);
$right = $('<div>').css({
'width': '8%',
'display': 'inline-block',
'vertical-align': 'top'
});
$right.append(removeHtml);
$el.append($right);
$el.append('<br style="clear: both;" />');
var isPrimary = (id == this.primaryId);
var iconHtml = '<span class="glyphicon glyphicon-star ' + (!isPrimary ? 'text-muted' : '') + '"></span>';
var title = this.translate('Primary');
var $primary = $('<button type="button" class="btn btn-link btn-sm pull-right hidden" title="'+title+'" data-action="switchPrimary" data-id="'+id+'">'+iconHtml+'</button>');
$primary.insertBefore($el.children().first().children().first());
$container.append($el);
this.managePrimaryButton();
return $el;
},
afterRender: function () {
Dep.prototype.afterRender.call(this);
},
managePrimaryButton: function () {
var $primary = this.$el.find('button[data-action="switchPrimary"]');
if ($primary.size() > 1) {
$primary.removeClass('hidden');
} else {
$primary.addClass('hidden');
}
if ($primary.filter('.active').size() == 0) {
var $first = $primary.first();
if ($first.size()) {
$first.addClass('active').children().removeClass('text-muted');
this.setPrimaryId($first.data('id'));
}
}
},
fetch: function () {
var data = Dep.prototype.fetch.call(this);
data[this.primaryIdFieldName] = this.primaryId;
data[this.primaryNameFieldName] = this.primaryName;
return data;
},
});
});
@@ -126,7 +126,7 @@ Espo.define('Views.Fields.LinkMultipleWithRole', 'Views.Fields.LinkMultiple', fu
if (this.mode == 'search') {
return Dep.prototype.addLinkHtml.call(this, id, name);
}
var $conteiner = this.$el.find('.link-container');
var $container = this.$el.find('.link-container');
var $el = $('<div class="form-inline list-group-item link-with-role">').addClass('link-' + id);
var nameHtml = '<div>' + name + '&nbsp;' + '</div>';
@@ -148,11 +148,8 @@ Espo.define('Views.Fields.LinkMultipleWithRole', 'Views.Fields.LinkMultiple', fu
'width': '92%',
'display': 'inline-block'
});
$left.append($role);
$left.append(nameHtml);
$el.append($left);
$right = $('<div>').css({
@@ -160,14 +157,11 @@ Espo.define('Views.Fields.LinkMultipleWithRole', 'Views.Fields.LinkMultiple', fu
'display': 'inline-block',
'vertical-align': 'top'
});
$right.append(removeHtml);
$el.append($right)
$el.append($right);
$el.append('<br style="clear: both;" />');
$conteiner.append($el);
$container.append($el);
if (this.mode == 'edit') {
var fetch = function ($target) {