diff --git a/application/Espo/Modules/Crm/Repositories/Case.php b/application/Espo/Modules/Crm/Repositories/Case.php new file mode 100644 index 0000000000..be7936bd92 --- /dev/null +++ b/application/Espo/Modules/Crm/Repositories/Case.php @@ -0,0 +1,69 @@ +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'); + } + } + } + } +} + diff --git a/application/Espo/Modules/Crm/Repositories/Contact.php b/application/Espo/Modules/Crm/Repositories/Contact.php index dd3eeb7e17..95055d2061 100644 --- a/application/Espo/Modules/Crm/Repositories/Contact.php +++ b/application/Espo/Modules/Crm/Repositories/Contact.php @@ -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; } } diff --git a/application/Espo/Modules/Crm/Resources/i18n/en_US/Case.json b/application/Espo/Modules/Crm/Resources/i18n/en_US/Case.json index c95a61bf1a..032253c6c8 100644 --- a/application/Espo/Modules/Crm/Resources/i18n/en_US/Case.json +++ b/application/Espo/Modules/Crm/Resources/i18n/en_US/Case.json @@ -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": { diff --git a/application/Espo/Modules/Crm/Resources/i18n/en_US/Contact.json b/application/Espo/Modules/Crm/Resources/i18n/en_US/Contact.json index 333af87725..3c7e51c7fa 100644 --- a/application/Espo/Modules/Crm/Resources/i18n/en_US/Contact.json +++ b/application/Espo/Modules/Crm/Resources/i18n/en_US/Contact.json @@ -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" diff --git a/application/Espo/Modules/Crm/Resources/i18n/en_US/Global.json b/application/Espo/Modules/Crm/Resources/i18n/en_US/Global.json index 71400eed64..8840ebf1a4 100644 --- a/application/Espo/Modules/Crm/Resources/i18n/en_US/Global.json +++ b/application/Espo/Modules/Crm/Resources/i18n/en_US/Global.json @@ -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" }, diff --git a/application/Espo/Modules/Crm/Resources/layouts/Case/detail.json b/application/Espo/Modules/Crm/Resources/layouts/Case/detail.json index d7eee59570..c5ffa81738 100644 --- a/application/Espo/Modules/Crm/Resources/layouts/Case/detail.json +++ b/application/Espo/Modules/Crm/Resources/layouts/Case/detail.json @@ -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}] ] diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Case.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Case.json index 2998e9ac0f..bda57da3e9 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Case.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Case.json @@ -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", diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Contact.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Contact.json index f520e150a5..3e115c5bdd 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Contact.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Contact.json @@ -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", diff --git a/frontend/client/modules/crm/src/views/case/fields/contacts.js b/frontend/client/modules/crm/src/views/case/fields/contacts.js new file mode 100644 index 0000000000..22e421276e --- /dev/null +++ b/frontend/client/modules/crm/src/views/case/fields/contacts.js @@ -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') + } + }; + } + } + + }); + +}); diff --git a/frontend/client/modules/crm/src/views/contact/fields/accounts.js b/frontend/client/modules/crm/src/views/contact/fields/accounts.js index 4493308476..20f789ca7f 100644 --- a/frontend/client/modules/crm/src/views/contact/fields/accounts.js +++ b/frontend/client/modules/crm/src/views/contact/fields/accounts.js @@ -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); diff --git a/frontend/client/src/views/fields/link-multiple-with-primary.js b/frontend/client/src/views/fields/link-multiple-with-primary.js new file mode 100644 index 0000000000..9b72a75e20 --- /dev/null +++ b/frontend/client/src/views/fields/link-multiple-with-primary.js @@ -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 '