MT#61777 Introduce B-number set in CF

*Users can now add/select a B-number
 set when creating a call forward.
 Before this commit, this was only
 possible from the admin panel.
 Note, users cannot delete or amend
 B-numbers created by someone else
 (e.g. if a PBX admin user creates
 a B-number for a Seat,the Seat-User
 won't be able to edit it).
 *Fix the way we handle sourceset
 deletion ensuring data is reloaded.

Change-Id: I58cb639156fb9ac0386d6bf0f3ec26daa4ebf857
mr13.3
Debora Crescenzo 3 months ago committed by Crescenzo Debora
parent 47d4627980
commit 1697c9e483

@ -17,6 +17,14 @@ export async function cfLoadMappings (subscriberId) {
})
}
export async function cfLoadBNumberSets (subscriberId) {
return getList({
resource: 'cfbnumbersets',
all: true,
params: (subscriberId) ? { subscriber_id: subscriberId } : {}
})
}
export async function cfLoadDestinationSets (subscriberId) {
return getList({
resource: 'cfdestinationsets',
@ -46,7 +54,8 @@ export async function cfLoadMappingsFull (subscriberId) {
cfLoadMappings(subscriberId),
cfLoadDestinationSets(),
cfLoadSourceSets(),
cfLoadTimeSets()
cfLoadTimeSets(),
cfLoadBNumberSets()
])
}
@ -64,6 +73,60 @@ export async function cfDeleteDestinationSet (id) {
})
}
export async function cfCreateBNumberSet (id, payload) {
const bNumbers = []
payload.numbers.forEach((number) => {
bNumbers.push({
bnumber: number
})
})
try {
const res = await post({
resource: 'cfbnumbersets',
body: {
name: payload.name,
subscriber_id: id,
is_regex: true,
bnumbers: bNumbers,
mode: payload.mode
}
})
if (!_.isString(res)) {
return `${res.id}`
}
return res
} catch (e) {
showGlobalError(e)
}
}
export async function cfDeleteBNumberSet (id) {
return del({
resource: 'cfbnumbersets',
resourceId: id
})
}
export async function cfUpdateBNumberSet (id, payload) {
const bnumbers = []
payload.numbers.forEach((number) => {
bnumbers.push({
bnumber: number
})
})
return putMinimal({
resource: 'cfbnumbersets',
resourceId: payload.id,
body: {
name: payload.name,
subscriber_id: id,
is_regex: true,
bnumbers,
mode: payload.mode
}
})
}
export async function cfCreateSourceSet (id, payload) {
const sources = []
payload.numbers.forEach((number) => {

@ -0,0 +1,71 @@
<template>
<q-select
:value="value"
:options="filteredOptions"
emit-value
use-input
map-options
input-debounce="300"
v-bind="$attrs"
@filter="filter"
/>
</template>
<script>
import { mapActions, mapState } from 'vuex'
export default {
name: 'CscCfBNumberSetSelection',
props: {
value: {
type: [String, Number],
default: undefined
},
mode: {
type: String,
required: true
}
},
data () {
return {
options: []
}
},
computed: {
...mapState('callForwarding', [
'bNumberSets'
]),
allOptions () {
return this.bNumberSets
.filter((bNumberSet) => bNumberSet.mode === this.mode)
.map((bNumberSet) => ({
value: bNumberSet.id,
label: bNumberSet.name
}))
},
filteredOptions () {
return this.options.length ? this.options : this.allOptions
}
},
async created () {
await this.loadBNumberSets()
this.options = this.allOptions
},
methods: {
...mapActions('callForwarding', [
'loadBNumberSets'
]),
async filter (value, update) {
await this.loadBNumberSets()
update(() => {
if (!value) {
this.options = this.allOptions
} else {
const lowerCaseValue = value.toLowerCase()
this.options = this.allOptions.filter((option) =>
option.label.toLowerCase().startsWith(lowerCaseValue)
)
}
})
}
}
}
</script>

@ -5,12 +5,67 @@
<csc-cf-group-condition-menu
v-if="internalStep === 'menu'"
:mapping="mapping"
:b-number-set="bNumberSet"
:destination-set="destinationSet"
:source-set="sourceSet"
:time-set="timeSet"
@step="internalStep=$event"
@close="closePopup"
/>
<csc-cf-group-condition-b-number-set-create
v-if="internalStep === 'call-to'"
mode="whitelist"
:title="$t('call to ...')"
icon="person_add"
:back-button="true"
:mapping="mapping"
:b-number-set="bNumberSet"
:subscriber-id="subscriberId"
@back="internalStep='menu'"
@select="internalStep='call-to-select'"
@close="closePopup"
/>
<csc-cf-group-condition-b-number-set-select
v-if="internalStep === 'call-to-select'"
mode="whitelist"
:title="$t('call to ...')"
icon="person_add"
:create-label="$t('Create List')"
:back-button="true"
:mapping="mapping"
:b-number-set="bNumberSet"
:subscriber-id="subscriberId"
@back="internalStep='call-to'"
@create="internalStep='call-to'"
@close="closePopup"
/>
<csc-cf-group-condition-b-number-set-create
v-if="internalStep === 'call-not-to'"
mode="blacklist"
:title="$t('call not to ...')"
icon="person_add_disabled"
:back-button="true"
:mapping="mapping"
:b-number-set="bNumberSet"
:subscriber-id="subscriberId"
@back="internalStep='menu'"
@select="internalStep='call-not-to-select'"
@close="closePopup"
/>
<csc-cf-group-condition-b-number-set-select
v-if="internalStep === 'call-not-to-select'"
mode="blacklist"
:title="$t('call not to ...')"
icon="person_add_disabled"
:create-label="$t('Create List')"
:back-button="true"
:mapping="mapping"
:b-number-set="bNumberSet"
:subscriber-id="subscriberId"
@back="internalStep='call-not-to'"
@create="internalStep='call-not-to'"
@close="closePopup"
/>
<csc-cf-group-condition-source-set-create
v-if="internalStep === 'call-from'"
mode="whitelist"
@ -123,6 +178,8 @@
<script>
import CscCfConditionPopup from 'components/call-forwarding/CscCfConditionPopup'
import CscCfGroupConditionBNumberSetCreate from 'components/call-forwarding/CscCfGroupConditionBNumberSetCreate'
import CscCfGroupConditionBNumberSetSelect from 'components/call-forwarding/CscCfGroupConditionBNumberSetSelect'
import CscCfGroupConditionDate from 'components/call-forwarding/CscCfGroupConditionDate'
import CscCfGroupConditionDateRange from 'components/call-forwarding/CscCfGroupConditionDateRange'
import CscCfGroupConditionMenu from 'components/call-forwarding/CscCfGroupConditionMenu'
@ -138,6 +195,8 @@ export default {
CscCfGroupConditionWeekdays,
CscCfGroupConditionDateRange,
CscCfGroupConditionDate,
CscCfGroupConditionBNumberSetSelect,
CscCfGroupConditionBNumberSetCreate,
CscCfGroupConditionSourceSetSelect,
CscCfGroupConditionSourceSetCreate,
CscCfGroupConditionMenu
@ -155,6 +214,10 @@ export default {
type: Object,
required: true
},
bNumberSet: {
type: Object,
default: undefined
},
sourceSet: {
type: Object,
default: undefined

@ -0,0 +1,73 @@
<template>
<csc-cf-condition-popup
ref="popup"
>
<csc-cf-group-condition-b-number-set-create
v-if="internalStep === 'call-not-to'"
mode="blacklist"
:title="$t('call not to ...')"
icon="person_add_disabled"
:back-button="false"
:delete-button="true"
:unassign-button="true"
:mapping="mapping"
:b-number-set="bNumberSet"
@select="internalStep='call-not-to-select'"
@close="closePopup"
/>
<csc-cf-group-condition-b-number-set-select
v-if="internalStep === 'call-not-to-select'"
mode="blacklist"
:title="$t('call not to ...')"
icon="person_add_disabled"
:create-label="$t('Edit List')"
:back-button="true"
:mapping="mapping"
:b-number-set="bNumberSet"
@back="internalStep='call-not-to'"
@create="internalStep='call-not-to'"
@close="closePopup"
/>
</csc-cf-condition-popup>
</template>
<script>
import CscCfConditionPopup from 'components/call-forwarding/CscCfConditionPopup'
import CscCfGroupConditionBNumberSetCreate from 'components/call-forwarding/CscCfGroupConditionBNumberSetCreate'
import CscCfGroupConditionBNumberSetSelect from 'components/call-forwarding/CscCfGroupConditionBNumberSetSelect'
export default {
name: 'CscCfConditionPopupCallNotTo',
components: {
CscCfConditionPopup,
CscCfGroupConditionBNumberSetSelect,
CscCfGroupConditionBNumberSetCreate
},
props: {
mapping: {
type: Object,
required: true
},
bNumberSet: {
type: Object,
default: undefined
}
},
data () {
return {
internalStep: 'call-not-to',
selectedBNumberSet: null
}
},
watch: {
internalStep () {
this.$refs.popup.reOpen()
}
},
methods: {
closePopup () {
this.internalStep = 'call-not-to'
this.$refs.popup.close()
}
}
}
</script>

@ -0,0 +1,79 @@
<template>
<csc-cf-condition-popup
ref="popup"
>
<csc-cf-group-condition-b-number-set-create
v-if="internalStep === 'call-to'"
mode="whitelist"
:title="$t('call to ...')"
icon="person_add"
:back-button="false"
:delete-button="true"
:unassign-button="true"
:mapping="mapping"
:b-number-set="bNumberSet"
:subscriber-id="subscriberId"
@select="internalStep='call-to-select'"
@close="closePopup"
/>
<csc-cf-group-condition-b-number-set-select
v-if="internalStep === 'call-to-select'"
mode="whitelist"
:title="$t('call to ...')"
icon="person_add"
:create-label="$t('Edit List')"
:back-button="true"
:mapping="mapping"
:b-number-set="bNumberSet"
:subscriber-id="subscriberId"
@back="internalStep='call-to'"
@create="internalStep='call-to'"
@close="closePopup"
/>
</csc-cf-condition-popup>
</template>
<script>
import CscCfConditionPopup from 'components/call-forwarding/CscCfConditionPopup'
import CscCfGroupConditionBNumberSetCreate from 'components/call-forwarding/CscCfGroupConditionBNumberSetCreate'
import CscCfGroupConditionBNumberSetSelect from 'components/call-forwarding/CscCfGroupConditionBNumberSetSelect'
export default {
name: 'CscCfConditionPopupCallTo',
components: {
CscCfConditionPopup,
CscCfGroupConditionBNumberSetSelect,
CscCfGroupConditionBNumberSetCreate
},
props: {
mapping: {
type: Object,
required: true
},
bNumberSet: {
type: Object,
default: undefined
},
subscriberId: {
type: String,
default: ''
}
},
data () {
return {
internalStep: 'call-to',
selectedBNumberSet: null
}
},
watch: {
internalStep () {
this.$refs.popup.reOpen()
}
},
methods: {
closePopup () {
this.internalStep = 'call-to'
this.$refs.popup.close()
}
}
}
</script>

@ -9,6 +9,7 @@
:loading="loading"
:mapping="mapping"
:destination-set="destinationSet"
:b-number-set="bNumberSet"
:source-set="sourceSet"
:time-set="timeSet"
:subscriber-id="subscriberId"
@ -31,6 +32,7 @@
:destination-index="destinationIndex"
:mapping="mapping"
:destination-set="destinationSet"
:b-number-set="bNumberSet"
:source-set="sourceSet"
:time-set="timeSet"
:subscriber-id="subscriberId"
@ -70,6 +72,10 @@ export default {
type: Object,
required: true
},
bNumberSet: {
type: Object,
default: undefined
},
sourceSet: {
type: Object,
default: undefined

@ -0,0 +1,235 @@
<template>
<csc-cf-group-condition
:title="title"
:loading="$wait.is('csc-cf-b-number-set-create')"
v-bind="$attrs"
@close="$emit('close')"
>
<q-list
class="no-margin q-pa-md"
dense
>
<q-item
class="no-margin no-padding"
>
<q-item-section>
<csc-input
v-model="bNumberSetNameInternal"
dense
clearable
:label="$t('Number list name')"
data-cy="csc-call-select-number-list"
/>
</q-item-section>
</q-item>
<q-item
v-for="(number, index) in bNumberSetNumbersInternal"
:key="index"
class="no-margin no-padding"
>
<q-item-section>
<csc-input
v-model="bNumberSetNumbersInternal[index]"
dense
clearable
:label="$t('Number')"
data-cy="csc-call-select-number"
>
<template
v-if="index > 0"
>
<q-btn
flat
dense
color="negative"
icon="delete"
data-cy="csc-call-select-number-delete"
@click="deleteNumber(index)"
/>
</template>
</csc-input>
</q-item-section>
</q-item>
<q-item
class="no-margin no-padding"
>
<q-item-section>
<q-btn
:label="$t('Add number')"
data-cy="csc-call-select-number-add"
flat
color="primary"
icon="add"
@click="bNumberSetNumbersInternal.push('')"
/>
</q-item-section>
</q-item>
</q-list>
<template
#actions
>
<q-btn
v-if="deleteButton"
:label="$t('Delete')"
data-cy="csc-call-select-delete"
flat
color="negative"
icon="delete"
@click="deleteBNumberSetEvent"
/>
<q-btn
v-if="unassignButton"
:label="$t('Unassign')"
data-cy="csc-call-select-unassign"
flat
color="primary"
icon="undo"
@click="unassignSourceSetEvent"
/>
<q-btn
:label="$t('Select')"
data-cy="csc-call-select-select"
flat
color="primary"
icon="source"
@click="$emit('select')"
/>
<q-btn
:label="$t('Save')"
data-cy="csc-call-select-save"
flat
color="primary"
icon="check"
@click="createBNumberSetEvent"
/>
</template>
</csc-cf-group-condition>
</template>
<script>
import CscCfGroupCondition from 'components/call-forwarding/CscCfGroupCondition'
import CscInput from 'components/form/CscInput'
import { mapActions } from 'vuex'
export default {
name: 'CscCfGroupConditionBNumberSetCreate',
components: {
CscCfGroupCondition,
CscInput
},
props: {
title: {
type: String,
required: true
},
mode: {
type: String,
required: true,
validator (value) {
return ['whitelist', 'blacklist'].includes(value.toLowerCase())
}
},
mapping: {
type: Object,
required: true
},
bNumberSet: {
type: Object,
default: undefined
},
deleteButton: {
type: Boolean,
default: false
},
unassignButton: {
type: Boolean,
default: false
},
subscriberId: {
type: String,
default: null
}
},
emits: ['close', 'select'],
data () {
return {
bNumberSetNameInternal: null,
bNumberSetNumbersInternal: null
}
},
computed: {
bNumberSetNumbers () {
const bnumbers = []
if (this.bNumberSet && this.bNumberSet.bnumbers) {
this.bNumberSet.bnumbers.forEach((item) => {
bnumbers.push(item.bnumber)
})
} else {
bnumbers.push('')
}
return bnumbers
},
bNumberSetName () {
let name = this.$t('MyNumberList')
if (this.bNumberSet) {
name = this.bNumberSet.name
}
return name
}
},
mounted () {
this.bNumberSetNameInternal = this.bNumberSetName
this.bNumberSetNumbersInternal = this.bNumberSetNumbers
},
methods: {
...mapActions('callForwarding', [
'createBNumberSet',
'updateBNumberSet',
'deleteBNumberSet',
'unassignBNumberSet'
]),
deleteNumber (index) {
this.bNumberSetNumbersInternal = this.bNumberSetNumbersInternal.filter((number, numberIndex) => {
return numberIndex !== index
})
},
async createBNumberSetEvent () {
if (this.bNumberSet) {
await this.updateBNumberSet({
mapping: this.mapping,
id: this.bNumberSet.id,
name: this.bNumberSetNameInternal,
numbers: this.bNumberSetNumbersInternal,
mode: this.mode,
subscriberId: this.subscriberId
})
} else {
await this.createBNumberSet({
mapping: this.mapping,
name: this.bNumberSetNameInternal,
numbers: this.bNumberSetNumbersInternal,
mode: this.mode,
subscriberId: this.subscriberId
})
}
this.$emit('close')
},
async deleteBNumberSetEvent () {
if (this.bNumberSet) {
await this.deleteBNumberSet({
mapping: this.mapping,
id: this.bNumberSet.id
})
}
},
async unassignSourceSetEvent () {
if (this.bNumberSet) {
await this.unassignBNumberSet({
mapping: this.mapping,
id: this.bNumberSet.id,
subscriberId: this.subscriberId
})
}
}
}
}
</script>

@ -0,0 +1,102 @@
<template>
<csc-cf-group-condition
:title="title"
:loading="$wait.is('csc-cf-b-number-set-create')"
v-bind="$attrs"
@close="$emit('close')"
>
<div
class="no-margin q-pa-md"
>
<csc-cf-b-number-set-selection
v-model="selectedBNumberSet"
:mode="mode"
dense
:label="$t('Number list')"
data-cy="csc-call-select-number-list"
/>
</div>
<template
#actions
>
<q-btn
:label="createLabel"
flat
color="primary"
icon="source"
data-cy="csc-call-select-edit-list"
@click="$emit('create')"
/>
<q-btn
:label="$t('Save')"
data-cy="csc-call-selection-save"
flat
color="primary"
icon="check"
:disable="!selectedBNumberSet"
@click="selectBNumberSetEvent"
/>
</template>
</csc-cf-group-condition>
</template>
<script>
import CscCfBNumberSetSelection from 'components/call-forwarding/CscCfBNumberSetSelection'
import CscCfGroupCondition from 'components/call-forwarding/CscCfGroupCondition'
import { mapActions } from 'vuex'
export default {
name: 'CscCfGroupConditionBNumberSetSelect',
components: {
CscCfBNumberSetSelection,
CscCfGroupCondition
},
props: {
title: {
type: String,
required: true
},
mode: {
type: String,
required: true,
validator (value) {
return ['whitelist', 'blacklist'].includes(value.toLowerCase())
}
},
mapping: {
type: Object,
required: true
},
bNumberSet: {
type: Object,
default: undefined
},
createLabel: {
type: String,
required: true
},
subscriberId: {
type: String,
default: ''
}
},
emits: ['close', 'create'],
data () {
return {
selectedBNumberSet: null
}
},
methods: {
...mapActions('callForwarding', [
'assignBNumberSet'
]),
async selectBNumberSetEvent () {
await this.assignBNumberSet({
mapping: this.mapping,
id: this.selectedBNumberSet,
subscriberId: this.subscriberId
})
this.$emit('close')
}
}
}
</script>

@ -5,6 +5,22 @@
<q-list
dense
>
<csc-popup-menu-item
icon="person_add"
:label="$t('call to ...')"
data-cy="csc-condition-call-to"
:close-popup="false"
:disable="!!bNumberSet"
@click="$emit('step', 'call-to')"
/>
<csc-popup-menu-item
icon="person_add_disabled"
:label="$t('call not to ...')"
data-cy="csc-condition-call-to"
:close-popup="false"
:disable="!!bNumberSet"
@click="$emit('step', 'call-not-to')"
/>
<csc-popup-menu-item
icon="person_add"
:label="$t('call from ...')"
@ -72,6 +88,10 @@ export default {
type: Object,
required: true
},
bNumberSet: {
type: Object,
default: undefined
},
sourceSet: {
type: Object,
default: undefined

@ -181,14 +181,6 @@ export default {
type: Object,
required: true
},
sourceSet: {
type: Object,
default: undefined
},
timeSet: {
type: Object,
default: undefined
},
loading: {
type: Boolean,
default: false

@ -23,9 +23,7 @@
>
{{ $t('If busy') }}
</template>
<template
v-if="sourceSet"
>
<template v-if="sourceSet">
<template
v-if="sourceSet.mode === 'whitelist'"
>
@ -69,6 +67,49 @@
/>
</span>
</template>
<template v-if="bNumberSet">
<span v-if="sourceSet">{{ ' ' + $t('or') + ' ' }} </span>
<span v-else>{{ ' ' + $t('and') + ' ' }} </span>
<template
v-if="bNumberSet.mode === 'whitelist'"
>
{{ ' ' + $t('call to') + ' ' }}
</template>
<template
v-else
>
{{ ' ' + $t('call not to') + ' ' }}
</template>
<span
:class="clickableClasses"
style="white-space: nowrap"
>
<q-icon
v-if="bNumberSet.mode === 'whitelist'"
name="person_add"
/>
<q-icon
v-else
name="person_add_disabled"
/>
{{ bNumberSet.name }}
<csc-cf-condition-popup-call-to
v-if="bNumberSet.mode === 'whitelist'"
data-cy="csc-condition-call-to"
:mapping="mapping"
:b-number-set="bNumberSet"
:subscriber-id="subscriberId"
/>
<csc-cf-condition-popup-call-not-to
v-else
data-cy="csc-condition-call-not-to"
:mapping="mapping"
:b-number-set="bNumberSet"
:subscriber-id="subscriberId"
/>
</span>
</template>
<template
v-if="timeSet"
>
@ -188,6 +229,7 @@
step="menu"
:mapping="mapping"
:destination-set="destinationSet"
:b-number-set="bNumberSet"
:source-set="sourceSet"
:time-set="timeSet"
:subscriber-id="subscriberId"
@ -364,6 +406,8 @@ import CscPopupMenuItemDelete from 'components/CscPopupMenuItemDelete'
import CscCfConditionPopupAll from 'components/call-forwarding/CscCfConditionPopupAll'
import CscCfConditionPopupCallFrom from 'components/call-forwarding/CscCfConditionPopupCallFrom'
import CscCfConditionPopupCallNotFrom from 'components/call-forwarding/CscCfConditionPopupCallNotFrom'
import CscCfConditionPopupCallNotTo from 'components/call-forwarding/CscCfConditionPopupCallNotTo'
import CscCfConditionPopupCallTo from 'components/call-forwarding/CscCfConditionPopupCallTo'
import CscCfConditionPopupCustom from 'components/call-forwarding/CscCfConditionPopupCustom'
import CscCfConditionPopupDate from 'components/call-forwarding/CscCfConditionPopupDate'
import CscCfConditionPopupDateRange from 'components/call-forwarding/CscCfConditionPopupDateRange'
@ -383,6 +427,8 @@ export default {
CscCfConditionPopupDateRange,
CscCfConditionPopupCallNotFrom,
CscCfConditionPopupCallFrom,
CscCfConditionPopupCallNotTo,
CscCfConditionPopupCallTo,
CscCfConditionPopupDate,
CscCfConditionPopupAll,
CscPopupMenuItem,
@ -399,6 +445,10 @@ export default {
type: Object,
required: true
},
bNumberSet: {
type: Object,
default: undefined
},
sourceSet: {
type: Object,
default: undefined

@ -1,7 +1,7 @@
<template>
<q-select
:value="value"
:options="options"
:options="filteredOptions"
emit-value
use-input
map-options
@ -11,11 +11,7 @@
/>
</template>
<script>
import _ from 'lodash'
import {
mapActions,
mapState
} from 'vuex'
import { mapActions, mapState } from 'vuex'
export default {
name: 'CscCfSourceSetSelection',
props: {
@ -38,36 +34,37 @@ export default {
'sourceSets'
]),
allOptions () {
const options = []
if (this.sourceSets) {
this.sourceSets.forEach((sourceSet) => {
if (sourceSet.mode === this.mode) {
options.push({
value: sourceSet.id,
label: sourceSet.name
})
}
})
}
return options
return this.sourceSets
.filter((sourceSet) => sourceSet.mode === this.mode)
.map((sourceSet) => ({
value: sourceSet.id,
label: sourceSet.name
}))
},
filteredOptions () {
return this.options.length ? this.options : this.allOptions
}
},
async created () {
await this.loadSourceSets()
this.options = this.allOptions
},
methods: {
...mapActions('callForwarding', [
'loadSourceSets'
]),
async filter (value, update) {
await this.loadSourceSets()
if (value === '' || value === null || value === undefined) {
update(() => {
update(() => {
if (!value) {
this.options = this.allOptions
})
} else {
update(() => {
this.options = this.allOptions.filter((sourceSet) =>
_.startsWith(_.lowerCase(sourceSet.label), _.lowerCase(value)))
})
}
} else {
const lowerCaseValue = value.toLowerCase()
this.options = this.allOptions.filter((option) =>
option.label.toLowerCase().startsWith(lowerCaseValue)
)
}
})
}
}
}

@ -75,6 +75,7 @@
class="q-mb-lg"
:loading="$wait.is('csc-cf-mappings-full')"
:mapping="group"
:b-number-set="bNumberSetMap[group.bnumberset_id]"
:destination-set="destinationSetMap[group.destinationset_id]"
:source-set="sourceSetMap[group.sourceset_id]"
:time-set="timeSetMap[group.timeset_id]"
@ -129,6 +130,7 @@ export default {
'groups'
]),
...mapState('callForwarding', [
'bNumberSetMap',
'destinationSetMap',
'sourceSetMap',
'timeSetMap'

@ -638,6 +638,10 @@
"busy": "besetzt",
"call from ...": "Anruf von ...",
"call not from ...": "Anruf nicht von ...",
"call not to": "Anruf nicht an",
"call not to ...": "Anruf nicht an ...",
"call to": "Anruf an",
"call to ...": "Anruf an ...",
"cli of the seat": "cli der Nebenstelle",
"condition": "Bedingung",
"data error": "Datenfehler",
@ -656,6 +660,7 @@
"name": "Name",
"office hours are": "Bürozeiten sind",
"office hours are ...": "Bürozeiten sind ...",
"or": "or",
"page": "Seite",
"pages": "Seiten",
"parent": "Eltern",

@ -613,9 +613,15 @@
"and": "and",
"and call from": "and call from",
"and call not from": "and call not from",
"and call not to": "and call not to",
"and call to": "and call to",
"busy": "busy",
"call from ...": "call from ...",
"call not from ...": "call not from ...",
"call not to": "call not to",
"call not to ...": "call not to ...",
"call to": "call to",
"call to ...": "call to ...",
"cli of the seat": "cli of the seat",
"condition": "condition",
"data error": "data error",
@ -634,6 +640,7 @@
"name": "name",
"office hours are": "office hours are",
"office hours are ...": "office hours are ...",
"or": "or",
"page": "page",
"pages": "pages",
"parent": "parent",

@ -211,18 +211,18 @@
"Forgot password?": "¿Ha olvidado su contraseña?",
"Format": "Formato",
"Forward": "Forward",
"Forward to Auto Attendant": "Forward to Auto Attendant",
"Forward to Call Through": "Forward to Call Through",
"Forward to Calling Card": "Forward to Calling Card",
"Forward to Auto Attendant": "Reenviar al Asistente Automático",
"Forward to Call Through": "Reenviar a Call Through",
"Forward to Calling Card": "Reenviar a Calling Card",
"Forward to Conference": "Reenviar a Conferencia",
"Forward to Custom Announcement": "Forward to Custom Announcement",
"Forward to Custom Announcement": "Reenviar a Anuncio Personalizado",
"Forward to Fax2Mail": "Reenviar a Fax2Mail",
"Forward to Local Subscriber": "Forward to Local Subscriber",
"Forward to Manager Secretary": "Forward to Manager Secretary",
"Forward to Local Subscriber": "Reenviar a Abonado Local",
"Forward to Manager Secretary": "Reenviar a Manager Secretary",
"Forward to ManagerSecretary": "Reenviar a Jefe-Asistente",
"Forward to Number": "Reenviar al número",
"Forward to Office Hours Announcement": "Forward to Office Hours Announcement",
"Forward to Voicebox": "Reenviar al buzón de voz",
"Forward to Office Hours Announcement": "Reenviar a Anuncio de Horarios de Oficina",
"Forward to Voicebox": "Reenviar al Buzón de Voz",
"Forwarded to": "Reenviado a",
"Forwarding": "Reenvío",
"Fr": "Fr",
@ -283,7 +283,7 @@
"Leave conference": "Salir de conferencia",
"Leave current conference now!": "¡Salir de la conferencia actual ahora!",
"List of registered devices for the subscriber": "Lista de dispositivos registrados para el abonado",
"Local Subscriber": "Local Subscriber",
"Local Subscriber": "Abonado Local",
"Login": "Iniciar sesión",
"Logout": "Cerrar sesión",
"Loop": "Bucle",
@ -355,7 +355,7 @@
"Number list name": "Nombre de la lista de números",
"Numbers": "Números",
"October": "Octubre",
"Office Hours Announcement": "Office Hours Announcement",
"Office Hours Announcement": "Anuncio de Horarios de Oficina",
"On weekdays": "En días de la semana",
"Only incoming calls from listed numbers are allowed": "Permitir solo los números listados",
"Only none decimal numbers are allowed": "Sólo se permiten números no decimales",
@ -633,18 +633,22 @@
"Your number is visible to the callee": "Su número es visible para la persona que llama",
"Your number is visible to the callee within own PBX": "Su número es visible para el receptor de la llamada en su propia centralita.",
"Your password has been changed successfully": "Su contraseña ha sido cambiada exitosamente",
"Your password has expired": "Your password has expired",
"Your password has expired": "Su contraseña ha caducado",
"ago": "atrás",
"and": "y",
"and call from": "y llamar desde",
"and call not from": "y no llamar desde",
"and call from": "y la llamada viene de",
"and call not from": "y la llamada no proviene de",
"busy": "ocupado",
"call from ...": "llamada de ...",
"call not from ...": "no llamar desde ...",
"call from ...": "la llamada viene de ...",
"call not from ...": "la llamada no proviene de ...",
"call not to": "el destinatario de la llamada no es",
"call not to ...": "el destinatario de la llamada no es ...",
"call to": "el destinatario de la llamada es",
"call to ...": "el destinatario de la llamada es ...",
"cli of the seat": "cli of the seat",
"condition": "condición",
"data error": "Error de datos",
"date is": "La fecha es",
"date is": "la fecha es",
"date is ...": "la fecha es ...",
"date range is": "el rango de fechas es",
"date range is ...": "el rango de fechas es ...",
@ -659,6 +663,7 @@
"name": "nombre",
"office hours are": "los horarios de oficina son",
"office hours are ...": "los horarios de oficina son...",
"or": "o",
"page": "página",
"pages": "páginas",
"parent": "parent",

@ -632,11 +632,15 @@
"Your password has expired": "Your password has expired",
"ago": ".",
"and": "et",
"and call from": "et appeler de",
"and call not from": "et de ne pas appeler de",
"and call from": "et lappel provient de",
"and call not from": "Lappel ne provient pas",
"busy": "occupé",
"call from ...": "appel de ...",
"call not from ...": "ne pas appeler de...",
"call from ...": "et lappel provient de ...",
"call not from ...": "lappel ne provient pas ...",
"call not to": "lappel est non destiné à",
"call not to ...": "lappel est non destiné à ...",
"call to": "lappel est destiné à",
"call to ...": "lappel est destiné à ...",
"cli of the seat": "cli of the seat",
"condition": "condition",
"data error": "erreur de données",
@ -655,6 +659,7 @@
"name": "nom",
"office hours are": "les heures de bureau sont",
"office hours are ...": "les heures de bureau sont ...",
"or": "ou",
"page": "page",
"pages": "pages",
"parent": "parent",

@ -130,9 +130,9 @@
"Created device {device} successfully": "Dispositivo {device} creato correttamente",
"Created manager secretary config for {msConfig} successfully": "Configurazione segreteria per {msConfig} creata correttamente",
"Created sound set {soundSet} successfully": "Set di messaggi audio {soundSet} creato correttamente",
"Current Password": "Current Password",
"Custom Announcement": "Custom Announcement",
"Custom Announcements": "Custom Announcements",
"Current Password": "Password Corrente",
"Custom Announcement": "Annuncio Personalizzato",
"Custom Announcements": "Annunci Personalizzati",
"Custom sound": "Audio personalizzato",
"Custom time set": "Intervallo di tempo personalizzato",
"Customer Details": "Dettagli cliente",
@ -208,18 +208,18 @@
"Forgot password?": "Password dimenticata?",
"Format": "Format",
"Forward": "Forward",
"Forward to Auto Attendant": "Forward to Auto Attendant",
"Forward to Call Through": "Forward to Call Through",
"Forward to Calling Card": "Forward to Calling Card",
"Forward to Conference": "Forward to Conference",
"Forward to Custom Announcement": "Forward to Custom Announcement",
"Forward to Fax2Mail": "Forward to Fax2Mail",
"Forward to Local Subscriber": "Forward to Local Subscriber",
"Forward to Manager Secretary": "Forward to Manager Secretary",
"Forward to Number": "Forward to Number",
"Forward to Office Hours Announcement": "Forward to Office Hours Announcement",
"Forward to Voicebox": "Forward to Voicebox",
"Forwarded to": "Forwarded to",
"Forward to Auto Attendant": "Inoltra all'Operatore Automatico",
"Forward to Call Through": "Inoltra al Call Through",
"Forward to Calling Card": "Inoltra al Calling Card",
"Forward to Conference": "Inoltra alla conferenza",
"Forward to Custom Announcement": "Inoltra all'Annuncio Personalizzato",
"Forward to Fax2Mail": "Inoltra a Fax2Mail",
"Forward to Local Subscriber": "Inoltra ad un Abbonato Locale",
"Forward to Manager Secretary": "Inoltra al Direttore/Segretaria",
"Forward to Number": "Inoltra al Numero",
"Forward to Office Hours Announcement": "Inoltra all'Annuncio degli Orari di Ufficio",
"Forward to Voicebox": "Inoltra alla Casella Vocale",
"Forwarded to": "Inoltrato a",
"Forwarding": "Forwarding",
"Fr": "Fr",
"Free": "Libero",
@ -242,9 +242,9 @@
"Hunt Timeout": "Timeout ricerca",
"Hunt timeout": "Timeout ricerca",
"Id": "Id",
"If available": "If available",
"If busy": "If busy",
"If not available": "If not available",
"If available": "Se disponibile",
"If busy": "Se occupato",
"If not available": "Se non disponibile",
"Ignore Members Call Forwards when Hunting": "Ignore Members Call Forwards when Hunting",
"In": "In",
"In call with": "Chiamata in corso con",
@ -348,7 +348,7 @@
"Number list name": "Number list name",
"Numbers": "Numeri",
"October": "Ottobre",
"Office Hours Announcement": "Office Hours Announcement",
"Office Hours Announcement": "Annuncio degli Orari di Ufficio",
"On weekdays": "Nei giorni feriali",
"Only incoming calls from listed numbers are allowed": "Solo ammesse solo chiamate dai numeri in elenco",
"Only none decimal numbers are allowed": "Sono ammessi solo numeri non decimali",
@ -620,29 +620,34 @@
"Your password has expired": "Your password has expired",
"ago": "fa",
"and": "e",
"and call from": "and call from",
"and call not from": "and call not from",
"and call from": "e la chiamata proviene da",
"and call not from": "e la chiamata non proviene da",
"busy": "occupato",
"call from ...": "call from ...",
"call not from ...": "call not from ...",
"call from ...": "e la chiamata proviene da ...",
"call not from ...": "e la chiamata non proviene da ...",
"call not to": "il destinatario della chiamata non é",
"call not to ...": "il destinatario della chiamata non é ...",
"call to": "il destinatario della chiamata é",
"call to ...": "il destinatario della chiamata é ...",
"cli of the seat": "cli of the seat",
"condition": "condizione",
"data error": "data error",
"date is": "date is",
"date is ...": "date is ...",
"date range is": "date range is",
"date range is ...": "date range is ...",
"date is": "la data é",
"date is ...": "la data é ...",
"date range is": "l'intervallo di date é",
"date range is ...": "l'intervallo di date é ...",
"default option": "opzione predefinita",
"description": "descrizione",
"empty": "empty",
"forwarded to": "forwarded to",
"forwarded to": "inoltrato a",
"from": "da",
"greet": "greet",
"minutes": "minuti",
"music on hold of the seat": "music on hold of the seat",
"name": "nome",
"office hours are": "office hours are",
"office hours are ...": "office hours are ...",
"office hours are": "gli orari di ufficio sono",
"office hours are ...": "gli orari di ufficio sono ...",
"or": "o",
"page": "pagina",
"pages": "pagine",
"parent": "parent",
@ -660,8 +665,8 @@
"to": "a",
"unavailable": "non disponibile",
"validators.": "validators.",
"weekdays are": "weekdays are",
"weekdays are ...": "weekdays are ...",
"weekdays are": "i giorni della settimana sono",
"weekdays are ...": "i giorni della settimana sono ...",
"{fieldOne} or {fieldTwo} is required": "Necessario compilare il campo {fieldOne} o il campo {fieldTwo}",
"{field} is required": "Il campo {field} è richiesto",
"{field} must be at least {minValue} second": "Il valore minimo del campo {field} è {minValue} secondi",

@ -79,6 +79,7 @@
class="q-mb-lg"
:loading="$wait.is('csc-cf-mappings-full')"
:mapping="group"
:b-number-set="bNumberSetMap[group.bnumberset_id]"
:destination-set="destinationSetMap[group.destinationset_id]"
:source-set="sourceSetMap[group.sourceset_id]"
:time-set="timeSetMap[group.timeset_id]"
@ -109,6 +110,7 @@ export default {
computed: {
...mapState('callForwarding', [
'mappings',
'bNumberSetMap',
'destinationSetMap',
'sourceSetMap',
'timeSetMap'

@ -1,17 +1,22 @@
import _ from 'lodash'
import {
cfCreateBNumberSet,
cfCreateOfficeHours,
cfCreateSourceSet,
cfCreateTimeSetDate,
cfCreateTimeSetDateRange,
cfCreateTimeSetWeekdays,
cfDeleteBNumberSet,
cfDeleteDestinationSet,
cfDeleteSourceSet,
cfDeleteTimeSet,
cfLoadBNumberSets,
cfLoadDestinationSets,
cfLoadMappingsFull,
cfLoadSourceSets,
cfLoadTimeSets, cfUpdateOfficeHours,
cfLoadTimeSets,
cfUpdateBNumberSet,
cfUpdateOfficeHours,
cfUpdateSourceSet,
cfUpdateTimeSetDate,
cfUpdateTimeSetDateRange,
@ -54,7 +59,8 @@ export async function loadMappingsFull ({ dispatch, commit, rootGetters }, id) {
mappings: mappingData[0],
destinationSets: mappingData[1].items,
sourceSets: mappingData[2].items,
timeSets: mappingData[3].items
timeSets: mappingData[3].items,
bNumberSets: mappingData[4].items
})
dispatch('wait/end', WAIT_IDENTIFIER, { root: true })
}
@ -120,8 +126,8 @@ export async function deleteMapping ({ dispatch, commit, state, rootGetters }, p
await cfDeleteDestinationSet(payload.destinationset_id)
} catch (e) {
if (e.code === 404 && e.message === 'Entity \'cfdestinationset\' not found.') {
// This happens when CF was set by Admin therefore current
// csc user doesn't have rights to delete the entity
// This happens when entity was set by Admin therefore current
// csc user doesn't have rights to delete the entity from DB.
showGlobalWarning(i18n.global.tc('Entity belongs to admin'))
} else {
showGlobalError(e.message)
@ -254,6 +260,135 @@ export async function loadSourceSets ({ dispatch, commit }) {
dispatch('wait/end', 'csc-cf-sourcesets', { root: true })
}
export async function createBNumberSet ({ dispatch, commit, rootGetters, state }, payload) {
try {
dispatch('wait/start', 'csc-cf-b-number-set-create', { root: true })
const bNumberSetId = await cfCreateBNumberSet(rootGetters['user/getSubscriberId'], payload)
const updatedMapping = _.cloneDeep(state.mappings[payload.mapping.type])
updatedMapping[payload.mapping.index].bnumberset_id = bNumberSetId
const updatedMappings = await patchReplaceFull({
resource: 'cfmappings',
resourceId: (payload.subscriberId) ? payload.subscriberId : rootGetters['user/getSubscriberId'],
fieldPath: payload.mapping.type,
value: updatedMapping
})
const bNumberSets = await cfLoadBNumberSets()
commit('dataSucceeded', {
mappings: updatedMappings,
bNumberSets: bNumberSets.items
})
} finally {
dispatch('wait/end', 'csc-cf-b-number-set-create', { root: true })
}
}
export async function loadBNumberSets ({ dispatch, commit }) {
dispatch('wait/start', 'csc-cf-b-number-set', { root: true })
const bNumberSets = await cfLoadBNumberSets()
commit('dataSucceeded', {
bNumberSets: bNumberSets.items
})
dispatch('wait/end', 'csc-cf-b-number-set', { root: true })
}
export async function updateBNumberSet ({ dispatch, commit, rootGetters }, payload) {
try {
dispatch('wait/start', 'csc-cf-b-number-set-create', { root: true })
await cfUpdateBNumberSet(rootGetters['user/getSubscriberId'], payload)
const bNumberSets = await cfLoadBNumberSets()
commit('dataSucceeded', {
bNumberSets: bNumberSets.items
})
} catch (e) {
if (e.code === 404 && e.message === 'Entity \'bnumberset\' not found.') {
// This happens when entity was set by Admin therefore current
// csc user doesn't have rights to edit the entity.
showGlobalWarning(i18n.global.tc('Entity belongs to admin'))
} else {
showGlobalError(e.message)
}
} finally {
dispatch('wait/end', 'csc-cf-b-number-set-create', { root: true })
}
}
export async function deleteBNumberSet ({ dispatch, commit, rootGetters, state }, payload) {
try {
dispatch('wait/start', 'csc-cf-b-number-set-create', { root: true })
const updatedMapping = _.cloneDeep(state.mappings[payload.mapping.type])
updatedMapping[payload.mapping.index].bnumberset_id = null
updatedMapping[payload.mapping.index].bnumberset = null
const updatedMappings = await patchReplaceFull({
resource: 'cfmappings',
resourceId: (payload.subscriberId) ? payload.subscriberId : rootGetters['user/getSubscriberId'],
fieldPath: payload.mapping.type,
value: updatedMapping
})
try {
await cfDeleteBNumberSet(payload.id)
} catch (e) {
if (e.code === 404 && e.message === 'Entity \'bnumberset\' not found.') {
// This happens when entity was set by Admin therefore current
// csc user doesn't have rights to delete the entity from DB.
// In this scenario the b-number is only removed from the mappings.
showGlobalWarning(i18n.global.tc('Entity belongs to admin'))
} else {
showGlobalError(e.message)
}
}
const bNumberSets = await cfLoadBNumberSets()
commit('dataSucceeded', {
mappings: updatedMappings,
bNumberSets: bNumberSets.items
})
} catch (e) {
showGlobalError(e.message)
} finally {
dispatch('wait/end', 'csc-cf-b-number-set-create', { root: true })
}
}
export async function assignBNumberSet ({ dispatch, commit, rootGetters, state }, payload) {
try {
dispatch('wait/start', 'csc-cf-b-number-set-create', { root: true })
const updatedMapping = _.cloneDeep(state.mappings[payload.mapping.type])
updatedMapping[payload.mapping.index].bnumberset_id = payload.id
const updatedMappings = await patchReplaceFull({
resource: 'cfmappings',
resourceId: (payload.subscriberId) ? payload.subscriberId : rootGetters['user/getSubscriberId'],
fieldPath: payload.mapping.type,
value: updatedMapping
})
commit('dataSucceeded', {
mappings: updatedMappings
})
} finally {
dispatch('wait/end', 'csc-cf-b-number-set-create', { root: true })
}
}
export async function unassignBNumberSet ({ dispatch, commit, rootGetters, state }, payload) {
try {
dispatch('wait/start', 'csc-cf-b-number-set-create', { root: true })
const updatedMapping = _.cloneDeep(state.mappings[payload.mapping.type])
updatedMapping[payload.mapping.index].bnumberset_id = null
updatedMapping[payload.mapping.index].bnumberset = null
const updatedMappings = await patchReplaceFull({
resource: 'cfmappings',
resourceId: (payload.subscriberId) ? payload.subscriberId : rootGetters['user/getSubscriberId'],
fieldPath: payload.mapping.type,
value: updatedMapping
})
commit('dataSucceeded', {
mappings: updatedMappings
})
} finally {
dispatch('wait/end', 'csc-cf-b-number-set-create', { root: true })
}
}
export async function createSourceSet ({ dispatch, commit, rootGetters, state }, payload) {
try {
dispatch('wait/start', 'csc-cf-source-set-create', { root: true })
@ -286,8 +421,8 @@ export async function updateSourceSet ({ dispatch, commit, rootGetters }, payloa
})
} catch (e) {
if (e.code === 404 && e.message === 'Entity \'sourceset\' not found.') {
// This happens when CF was set by Admin therefore current
// csc user doesn't have rights to delete the entity
// This happens when entity was set by Admin therefore current
// csc user doesn't have rights to edit the entity.
showGlobalWarning(i18n.global.tc('Entity belongs to admin'))
} else {
showGlobalError(e.message)
@ -309,7 +444,21 @@ export async function deleteSourceSet ({ dispatch, commit, rootGetters, state },
fieldPath: payload.mapping.type,
value: updatedMapping
})
await cfDeleteSourceSet(payload.id)
try {
await cfDeleteSourceSet(payload.id)
} catch (e) {
if (e.code === 404 && e.message === 'Entity \'sourceset\' not found.') {
// This happens when entity was set by Admin therefore current
// csc user doesn't have rights to delete the entity from DB.
// In this scenario the sources is only removed from the mappings.
showGlobalWarning(i18n.global.tc('Entity belongs to admin'))
// Force reload of SourceSets
} else {
throw e
}
}
const sourceSets = await cfLoadSourceSets()
commit('dataSucceeded', {
mappings: updatedMappings,
@ -410,8 +559,9 @@ export async function deleteTimeSet ({ dispatch, commit, rootGetters, state }, p
await cfDeleteTimeSet(payload.id)
} catch (e) {
if (e.code === 404 && e.message === 'Entity \'cftimeset\' not found.') {
// This happens when CF was set by Admin therefore current
// csc user doesn't have rights to delete the entity
// This happens when entity was set by Admin therefore current
// csc user doesn't have rights to delete the entity from DB.
// In this case entity is only removed from the mappings.
showGlobalWarning(i18n.global.tc('Entity belongs to admin'))
} else {
showGlobalError(e.message)
@ -456,8 +606,6 @@ export async function doNotRingPrimaryNumber ({ commit, rootGetters, state }, pa
}
export async function updateRingTimeout ({ commit, rootGetters, state }, payload) {
// eslint-disable-next-line no-console
console.debug('aaa')
const updatedMappings = await patchReplaceFull({
resource: 'cfmappings',
resourceId: (payload.subscriberId) ? payload.subscriberId : rootGetters['user/getSubscriberId'],
@ -494,8 +642,8 @@ export async function updateTimeSetDateRange ({ dispatch, commit }, payload) {
await cfUpdateTimeSetDateRange(payload.id, payload.date)
} catch (e) {
if (e.code === 404 && e.message === 'Entity \'timeset\' not found.') {
// This happens when CF was set by Admin therefore current
// csc user doesn't have rights to delete the entity
// This happens when entity was set by Admin therefore current
// csc user doesn't have rights to edit the entity
showGlobalWarning(i18n.global.tc('Entity belongs to admin'))
} else {
showGlobalError(e.message)
@ -534,8 +682,8 @@ export async function updateTimeSetWeekdays ({ dispatch, commit }, payload) {
await cfUpdateTimeSetWeekdays(payload.id, payload.weekdays)
} catch (e) {
if (e.code === 404 && e.message === 'Entity \'timeset\' not found.') {
// This happens when CF was set by Admin therefore current
// csc user doesn't have rights to delete the entity
// This happens when entity was set by Admin therefore current
// csc user doesn't have rights to edit the entity
showGlobalWarning(i18n.global.tc('Entity belongs to admin'))
} else {
showGlobalError(e.message)

@ -1,4 +1,12 @@
export function dataSucceeded (state, res) {
if (res.bNumberSets) {
const bNumberSetMap = {}
res.bNumberSets.forEach((bNumberSet) => {
bNumberSetMap[bNumberSet.id] = bNumberSet
})
state.bNumberSetMap = bNumberSetMap
state.bNumberSets = res.bNumberSets
}
if (res.destinationSets) {
const destinationSetMap = {}
res.destinationSets.forEach((destinationSet) => {

@ -10,6 +10,8 @@ export default function () {
cft_ringtimeout: null,
cfu: []
},
bNumberSets: null,
bNumberSetMap: {},
destinationSets: null,
destinationSetMap: {},
sourceSets: null,

Loading…
Cancel
Save