phone number only numeric search param
This commit is contained in:
@@ -33,6 +33,7 @@ use Espo\Core\Duplicate\WhereBuilder;
|
||||
use Espo\Core\Field\EmailAddressGroup;
|
||||
use Espo\Core\Field\PhoneNumberGroup;
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Entity;
|
||||
@@ -49,7 +50,8 @@ class General implements WhereBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Metadata $metadata,
|
||||
private Defs $ormDefs
|
||||
private Defs $ormDefs,
|
||||
private Config $config
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -177,6 +179,12 @@ class General implements WhereBuilder
|
||||
|
||||
$toCheck = false;
|
||||
|
||||
$isNumeric = $this->config->get('phoneNumberNumericSearch');
|
||||
|
||||
$column = $isNumeric ?
|
||||
$field . 'Numeric' :
|
||||
$field;
|
||||
|
||||
if (
|
||||
($entity->get($field) || $entity->get($field . 'Data')) &&
|
||||
(
|
||||
@@ -185,11 +193,15 @@ class General implements WhereBuilder
|
||||
$entity->isAttributeChanged($field . 'Data')
|
||||
)
|
||||
) {
|
||||
foreach ($this->getPhoneNumberList($entity) as $phoneNumber) {
|
||||
foreach ($this->getPhoneNumberList($entity) as $number) {
|
||||
if ($isNumeric) {
|
||||
$number = preg_replace('/[^0-9]/', '', $number);
|
||||
}
|
||||
|
||||
$orBuilder->add(
|
||||
Cond::equal(
|
||||
Cond::column($field),
|
||||
$phoneNumber
|
||||
Cond::column($column),
|
||||
$number
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -48,4 +48,9 @@ class ConfigProvider
|
||||
{
|
||||
return $this->config->get('textFilterUseContainsForVarchar') ?? false;
|
||||
}
|
||||
|
||||
public function usePhoneNumberNumericSearch(): bool
|
||||
{
|
||||
return $this->config->get('phoneNumberNumericSearch') ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +112,25 @@ class DefaultFilter implements Filter
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!str_contains($attribute, '.') &&
|
||||
$this->metadataProvider->getFieldType($this->entityType, $attribute) === 'phone'
|
||||
) {
|
||||
if (str_contains($filter, ' ')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->config->usePhoneNumberNumericSearch()) {
|
||||
$attribute = $attribute . 'Numeric';
|
||||
|
||||
$filter = preg_replace('/[^0-9]/', '', $filter);
|
||||
}
|
||||
|
||||
if (!$filter) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$expression = $filter;
|
||||
|
||||
if (!$skipWildcards) {
|
||||
|
||||
@@ -279,5 +279,6 @@ return [
|
||||
'oidcScopes' => ['profile', 'email', 'phone'],
|
||||
'listViewSettingsDisabled' => false,
|
||||
'cleanupDeletedRecords' => true,
|
||||
'phoneNumberNumericSearch' => false,
|
||||
'isInstalled' => false,
|
||||
];
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"adminNotificationsNewVersion": "Show notification when new EspoCRM version is available",
|
||||
"adminNotificationsNewExtensionVersion": "Show notification when new versions of extensions are available",
|
||||
"textFilterUseContainsForVarchar": "Use 'contains' operator when filtering varchar fields",
|
||||
"phoneNumberNumericSearch": "Numeric phone number search",
|
||||
"authTokenPreventConcurrent": "Only one auth token per user",
|
||||
"scopeColorsDisabled": "Disable scope colors",
|
||||
"tabColorsDisabled": "Disable tab colors",
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
{
|
||||
"label": "Search",
|
||||
"rows": [
|
||||
[{"name": "textFilterUseContainsForVarchar"}, {"name": "globalSearchEntityList"}]
|
||||
[{"name": "textFilterUseContainsForVarchar"}, {"name": "globalSearchEntityList"}],
|
||||
[{"name": "phoneNumberNumericSearch"}, false]
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -688,6 +688,9 @@
|
||||
"type": "bool",
|
||||
"tooltip": true
|
||||
},
|
||||
"phoneNumberNumericSearch": {
|
||||
"type": "bool"
|
||||
},
|
||||
"scopeColorsDisabled": {
|
||||
"type": "bool"
|
||||
},
|
||||
|
||||
@@ -42,9 +42,9 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
events = {
|
||||
/** @this PhoneFieldView */
|
||||
'click [data-action="switchPhoneProperty"]': function (e) {
|
||||
let $target = $(e.currentTarget);
|
||||
let $block = $(e.currentTarget).closest('div.phone-number-block');
|
||||
let property = $target.data('property-type');
|
||||
const $target = $(e.currentTarget);
|
||||
const $block = $(e.currentTarget).closest('div.phone-number-block');
|
||||
const property = $target.data('property-type');
|
||||
|
||||
if (property === 'primary') {
|
||||
if (!$target.hasClass('active')) {
|
||||
@@ -68,13 +68,13 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
},
|
||||
/** @this PhoneFieldView */
|
||||
'click [data-action="removePhoneNumber"]': function (e) {
|
||||
let $block = $(e.currentTarget).closest('div.phone-number-block');
|
||||
const $block = $(e.currentTarget).closest('div.phone-number-block');
|
||||
|
||||
this.removePhoneNumber($block);
|
||||
|
||||
this.trigger('change');
|
||||
|
||||
let $last = this.$el.find('.phone-number').last();
|
||||
const $last = this.$el.find('.phone-number').last();
|
||||
|
||||
if ($last.length) {
|
||||
$last[0].focus({preventScroll: true});
|
||||
@@ -82,8 +82,8 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
},
|
||||
/** @this PhoneFieldView */
|
||||
'change input.phone-number': function (e) {
|
||||
let $input = $(e.currentTarget);
|
||||
let $block = $input.closest('div.phone-number-block');
|
||||
const $input = $(e.currentTarget);
|
||||
const $block = $input.closest('div.phone-number-block');
|
||||
|
||||
if (this._itemJustRemoved) {
|
||||
return;
|
||||
@@ -112,9 +112,9 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
},
|
||||
/** @this PhoneFieldView */
|
||||
'keydown input.phone-number': function (e) {
|
||||
let key = Espo.Utils.getKeyFromKeyEvent(e);
|
||||
const key = Espo.Utils.getKeyFromKeyEvent(e);
|
||||
|
||||
let $target = $(e.currentTarget);
|
||||
const $target = $(e.currentTarget);
|
||||
|
||||
if (key === 'Enter') {
|
||||
if (!this.$el.find('[data-action="addPhoneNumber"]').hasClass('disabled')) {
|
||||
@@ -127,7 +127,7 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
}
|
||||
|
||||
if (key === 'Backspace' && $target.val() === '') {
|
||||
let $block = $target.closest('div.phone-number-block');
|
||||
const $block = $target.closest('div.phone-number-block');
|
||||
|
||||
this._itemJustRemoved = true;
|
||||
setTimeout(() => this._itemJustRemoved = false, 100);
|
||||
@@ -147,7 +147,7 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
}
|
||||
|
||||
if (!this.model.get(this.name)) {
|
||||
let msg = this.translate('fieldIsRequired', 'messages')
|
||||
const msg = this.translate('fieldIsRequired', 'messages')
|
||||
.replace('{field}', this.getLabelText());
|
||||
|
||||
this.showValidationMessage(msg, 'div.phone-number-block:nth-child(1) input.phone-number');
|
||||
@@ -158,37 +158,37 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
validatePhoneData() {
|
||||
let data = this.model.get(this.dataFieldName);
|
||||
const data = this.model.get(this.dataFieldName);
|
||||
|
||||
if (!data || !data.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var {string} */
|
||||
let pattern = '^' + this.getMetadata().get(['app', 'regExpPatterns', 'phoneNumberLoose', 'pattern']) + '$';
|
||||
let regExp = new RegExp(pattern);
|
||||
const pattern = '^' + this.getMetadata().get(['app', 'regExpPatterns', 'phoneNumberLoose', 'pattern']) + '$';
|
||||
const regExp = new RegExp(pattern);
|
||||
|
||||
let numberList = [];
|
||||
const numberList = [];
|
||||
let notValid = false;
|
||||
|
||||
data.forEach((row, i) => {
|
||||
let number = row.phoneNumber;
|
||||
const msg = this.translate('fieldValueDuplicate', 'messages')
|
||||
.replace('{field}', this.getLabelText());
|
||||
const number = row.phoneNumber;
|
||||
|
||||
if (!regExp.test(number)) {
|
||||
notValid = true;
|
||||
|
||||
let msg = this.translate('fieldPhoneInvalidCharacters', 'messages')
|
||||
const msg = this.translate('fieldPhoneInvalidCharacters', 'messages')
|
||||
.replace('{field}', this.getLabelText());
|
||||
|
||||
this.showValidationMessage(msg, 'div.phone-number-block:nth-child(' + (i + 1)
|
||||
.toString() + ') input.phone-number');
|
||||
}
|
||||
|
||||
let numberClean = String(number).replace(/[\s+]/g, '');
|
||||
const numberClean = String(number).replace(/[\s+]/g, '');
|
||||
|
||||
if (~numberList.indexOf(numberClean)) {
|
||||
let msg = this.translate('fieldValueDuplicate', 'messages')
|
||||
.replace('{field}', this.getLabelText());
|
||||
|
||||
this.showValidationMessage(msg, 'div.phone-number-block:nth-child(' + (i + 1)
|
||||
.toString() + ') input.phone-number');
|
||||
@@ -207,6 +207,7 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
}
|
||||
|
||||
data() {
|
||||
const number = this.model.get(this.name);
|
||||
let phoneNumberData;
|
||||
|
||||
if (this.mode === this.MODE_EDIT) {
|
||||
@@ -239,7 +240,7 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
phoneNumberData = Espo.Utils.cloneDeep(phoneNumberData);
|
||||
|
||||
phoneNumberData.forEach((item) => {
|
||||
let number = item.phoneNumber || '';
|
||||
const number = item.phoneNumber || '';
|
||||
|
||||
item.erased = number.indexOf(this.erasedPlaceholder) === 0;
|
||||
|
||||
@@ -252,9 +253,8 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
}
|
||||
|
||||
if ((!phoneNumberData || phoneNumberData.length === 0) && this.model.get(this.name)) {
|
||||
let number = this.model.get(this.name);
|
||||
|
||||
let o = {
|
||||
const o = {
|
||||
phoneNumber: number,
|
||||
primary: true,
|
||||
valueForLink: number.replace(/ /g, ''),
|
||||
@@ -267,7 +267,7 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
phoneNumberData = [o];
|
||||
}
|
||||
|
||||
let data = {
|
||||
const data = {
|
||||
...super.data(),
|
||||
phoneNumberData: phoneNumberData,
|
||||
doNotCall: this.model.get('doNotCall'),
|
||||
@@ -295,7 +295,7 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
}
|
||||
|
||||
focusOnLast(cursorAtEnd) {
|
||||
let $item = this.$el.find('input.form-control').last();
|
||||
const $item = this.$el.find('input.form-control').last();
|
||||
|
||||
$item.focus();
|
||||
|
||||
@@ -315,9 +315,9 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
}
|
||||
|
||||
addPhoneNumber() {
|
||||
let data = Espo.Utils.cloneDeep(this.fetchPhoneNumberData());
|
||||
const data = Espo.Utils.cloneDeep(this.fetchPhoneNumberData());
|
||||
|
||||
let o = {
|
||||
const o = {
|
||||
phoneNumber: '',
|
||||
primary: !data.length,
|
||||
type: false,
|
||||
@@ -346,6 +346,12 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
}
|
||||
}
|
||||
|
||||
afterRenderSearch() {
|
||||
super.afterRenderSearch();
|
||||
|
||||
// @todo If numeric search enabled, allow only digits to enter.
|
||||
}
|
||||
|
||||
removePhoneNumberBlock($block) {
|
||||
let changePrimary = false;
|
||||
|
||||
@@ -368,7 +374,7 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
}
|
||||
|
||||
manageAddButton() {
|
||||
let $input = this.$el.find('input.phone-number');
|
||||
const $input = this.$el.find('input.phone-number');
|
||||
let c = 0;
|
||||
|
||||
$input.each((i, input) => {
|
||||
@@ -391,9 +397,9 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
}
|
||||
|
||||
manageButtonsVisibility() {
|
||||
let $primary = this.$el.find('button[data-property-type="primary"]');
|
||||
let $remove = this.$el.find('button[data-action="removePhoneNumber"]');
|
||||
let $container = this.$el.find('.phone-number-block-container');
|
||||
const $primary = this.$el.find('button[data-property-type="primary"]');
|
||||
const $remove = this.$el.find('button[data-action="removePhoneNumber"]');
|
||||
const $container = this.$el.find('.phone-number-block-container');
|
||||
|
||||
if ($primary.length > 1) {
|
||||
$primary.removeClass('hidden');
|
||||
@@ -440,14 +446,14 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
}
|
||||
|
||||
fetchPhoneNumberData() {
|
||||
let data = [];
|
||||
const data = [];
|
||||
|
||||
let $list = this.$el.find('div.phone-number-block');
|
||||
const $list = this.$el.find('div.phone-number-block');
|
||||
|
||||
if ($list.length) {
|
||||
$list.each((i, d) => {
|
||||
let row = {};
|
||||
let $d = $(d);
|
||||
const row = {};
|
||||
const $d = $(d);
|
||||
|
||||
row.phoneNumber = $d.find('input.phone-number').val().trim();
|
||||
|
||||
@@ -468,9 +474,9 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
}
|
||||
|
||||
fetch() {
|
||||
let data = {};
|
||||
const data = {};
|
||||
|
||||
let addressData = this.fetchPhoneNumberData() || [];
|
||||
const addressData = this.fetchPhoneNumberData() || [];
|
||||
|
||||
data[this.dataFieldName] = addressData;
|
||||
data[this.name] = null;
|
||||
@@ -494,7 +500,7 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
});
|
||||
|
||||
if (addressData.length && primaryIndex > 0) {
|
||||
let t = addressData[0];
|
||||
const t = addressData[0];
|
||||
|
||||
addressData[0] = addressData[primaryIndex];
|
||||
addressData[primaryIndex] = t;
|
||||
@@ -509,6 +515,59 @@ class PhoneFieldView extends VarcharFieldView {
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
fetchSearch() {
|
||||
const type = this.fetchSearchType() || 'startsWith';
|
||||
|
||||
const isNumeric = this.getConfig().get('phoneNumberNumericSearch');
|
||||
|
||||
const name = isNumeric ?
|
||||
this.name + 'Numeric' :
|
||||
this.name;
|
||||
|
||||
if (~['isEmpty', 'isNotEmpty'].indexOf(type)) {
|
||||
if (type === 'isEmpty') {
|
||||
return {
|
||||
type: 'isNull',
|
||||
attribute: name,
|
||||
data: {
|
||||
type: type,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'isNotNull',
|
||||
attribute: name,
|
||||
data: {
|
||||
type: type,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** @type {string} */
|
||||
let value = this.$element.val()
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
if (isNumeric && value) {
|
||||
value = value.replace(/[^0-9]/g, '');
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
type: type,
|
||||
value: value,
|
||||
attribute: name,
|
||||
data: {
|
||||
type: type,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default PhoneFieldView;
|
||||
|
||||
Reference in New Issue
Block a user