case - contact many-to-many
This commit is contained in:
@@ -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 + ' ' + '</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 + ' ' + '</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) {
|
||||
|
||||
Reference in New Issue
Block a user