Merge branch 'dev' into feature/WorkerDescriptorMenu
gitea/salix-front/pipeline/pr-dev This commit looks good
Details
gitea/salix-front/pipeline/pr-dev This commit looks good
Details
This commit is contained in:
commit
adbc9dee2c
|
@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- (Item) => Se añade la opción de añadir un comentario del motivo de hacer una foto
|
||||
- (Worker) => Se añade la opción de crear un trabajador ajeno a la empresa
|
||||
- (Route) => Ahora se muestran todos los cmrs
|
||||
|
||||
## [2418.01]
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import axios from 'axios';
|
||||
import { useState } from './useState';
|
||||
|
||||
export function useAcl() {
|
||||
const state = useState();
|
||||
|
||||
async function fetch() {
|
||||
const { data } = await axios.get('VnUsers/acls');
|
||||
const acls = {};
|
||||
data.forEach((acl) => {
|
||||
acls[acl.model] = acls[acl.model] || {};
|
||||
acls[acl.model][acl.property] = acls[acl.model][acl.property] || {};
|
||||
acls[acl.model][acl.property][acl.accessType] = true;
|
||||
});
|
||||
|
||||
state.setAcls(acls);
|
||||
}
|
||||
|
||||
function hasAny(model, prop, accessType) {
|
||||
const acls = state.getAcls().value[model];
|
||||
if (acls)
|
||||
return ['*', prop].some((key) => {
|
||||
const acl = acls[key];
|
||||
return acl && (acl['*'] || acl[accessType]);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
fetch,
|
||||
hasAny,
|
||||
state,
|
||||
};
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import { useState } from './useState';
|
||||
import { useRole } from './useRole';
|
||||
import { useAcl } from './useAcl';
|
||||
import { useUserConfig } from './useUserConfig';
|
||||
import axios from 'axios';
|
||||
import useNotify from './useNotify';
|
||||
|
@ -88,6 +89,7 @@ export function useSession() {
|
|||
setSession(data);
|
||||
|
||||
await useRole().fetch();
|
||||
await useAcl().fetch();
|
||||
await useUserConfig().fetch();
|
||||
await useTokenConfig().fetch();
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ if (sessionStorage.getItem('user'))
|
|||
user.value = JSON.parse(sessionStorage.getItem('user'));
|
||||
|
||||
const roles = ref([]);
|
||||
const acls = ref([]);
|
||||
const tokenConfig = ref({});
|
||||
const drawer = ref(true);
|
||||
const headerMounted = ref(false);
|
||||
|
@ -42,6 +43,14 @@ export function useState() {
|
|||
function setRoles(data) {
|
||||
roles.value = data;
|
||||
}
|
||||
|
||||
function getAcls() {
|
||||
return computed(() => acls.value);
|
||||
}
|
||||
|
||||
function setAcls(data) {
|
||||
acls.value = data;
|
||||
}
|
||||
function getTokenConfig() {
|
||||
return computed(() => {
|
||||
return tokenConfig.value;
|
||||
|
@ -69,6 +78,8 @@ export function useState() {
|
|||
setUser,
|
||||
getRoles,
|
||||
setRoles,
|
||||
getAcls,
|
||||
setAcls,
|
||||
getTokenConfig,
|
||||
setTokenConfig,
|
||||
set,
|
||||
|
|
|
@ -962,7 +962,7 @@ roadmap:
|
|||
route:
|
||||
pageTitles:
|
||||
routes: Routes
|
||||
cmrsList: External CMRs list
|
||||
cmrsList: CMRs list
|
||||
RouteList: List
|
||||
routeCreate: New route
|
||||
basicData: Basic Data
|
||||
|
|
|
@ -950,7 +950,7 @@ roadmap:
|
|||
route:
|
||||
pageTitles:
|
||||
routes: Rutas
|
||||
cmrsList: Listado de CMRs externos
|
||||
cmrsList: Listado de CMRs
|
||||
RouteList: Listado
|
||||
routeCreate: Nueva ruta
|
||||
basicData: Datos básicos
|
||||
|
|
|
@ -234,7 +234,7 @@ const showBalancePdf = (balance) => {
|
|||
<template #body-cell-employee="{ row }">
|
||||
<QTd auto-width @click.stop>
|
||||
<QBtn color="blue" flat no-caps>{{ row.userName }}</QBtn>
|
||||
<WorkerDescriptorProxy :id="row.clientFk" />
|
||||
<WorkerDescriptorProxy :id="row.workerFk" />
|
||||
</QTd>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
|
@ -16,10 +16,11 @@ const props = defineProps({
|
|||
});
|
||||
|
||||
const countries = ref();
|
||||
const warehouses = ref();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData url="Countries" @on-fetch="(data) => (countries = data)" auto-load />
|
||||
<FetchData url="Warehouses" @on-fetch="(data) => (warehouses = data)" auto-load />
|
||||
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<div class="q-gutter-x-xs">
|
||||
|
@ -93,13 +94,13 @@ const countries = ref();
|
|||
<QItemSection v-if="!countries">
|
||||
<QSkeleton type="QInput" class="full-width" />
|
||||
</QItemSection>
|
||||
<QItemSection v-if="countries" class="q-mb-sm">
|
||||
<QSelect
|
||||
<QItemSection v-if="countries">
|
||||
<VnSelect
|
||||
:label="t('route.cmr.list.country')"
|
||||
v-model="params.country"
|
||||
:options="countries"
|
||||
option-value="country"
|
||||
option-label="country"
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
emit-value
|
||||
|
@ -111,9 +112,23 @@ const countries = ref();
|
|||
<template #prepend>
|
||||
<QIcon name="flag" size="sm"></QIcon>
|
||||
</template>
|
||||
</QSelect>
|
||||
</VnSelect>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<VnSelect
|
||||
:label="t('globals.warehouse')"
|
||||
:options="warehouses"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
v-model="params.warehouseFk"
|
||||
rounded
|
||||
dense
|
||||
outlined
|
||||
>
|
||||
</VnSelect>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
|
@ -126,7 +141,6 @@ const countries = ref();
|
|||
</template>
|
||||
</VnFilterPanel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
en:
|
||||
params:
|
||||
|
|
|
@ -14,6 +14,7 @@ const { t } = useI18n();
|
|||
const { getTokenMultimedia } = useSession();
|
||||
const token = getTokenMultimedia();
|
||||
const selected = ref([]);
|
||||
const warehouses = ref([]);
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
|
@ -63,6 +64,13 @@ const columns = computed(() => [
|
|||
sortable: true,
|
||||
headerStyle: 'padding-left: 33px',
|
||||
},
|
||||
{
|
||||
name: 'warehouseFk',
|
||||
label: t('globals.warehouse'),
|
||||
field: ({ warehouseFk }) => warehouseFk,
|
||||
align: 'center',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: 'icons',
|
||||
align: 'center',
|
||||
|
@ -99,7 +107,7 @@ function downloadPdfs() {
|
|||
<div class="list">
|
||||
<VnPaginate
|
||||
data-key="CmrList"
|
||||
:url="`Routes/getExternalCmrs`"
|
||||
:url="`Routes/cmrs`"
|
||||
order="cmrFk DESC"
|
||||
limit="null"
|
||||
auto-load
|
||||
|
@ -147,6 +155,11 @@ function downloadPdfs() {
|
|||
<CustomerDescriptorProxy :id="value" />
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-warehouseFk="{ value }">
|
||||
<QTd align="center">
|
||||
{{ warehouses.find(({ id }) => id === value)?.name }}
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-icons="{ value }">
|
||||
<QTd align="center">
|
||||
<a :href="getCmrUrl(value)" target="_blank">
|
||||
|
|
|
@ -86,7 +86,7 @@ onBeforeMount(async () => {
|
|||
url-create="Workers/new"
|
||||
model="worker"
|
||||
:form-initial-data="formData"
|
||||
@on-data-saved="({ id }) => $router.push({ path: `/worker/${id}` })"
|
||||
@on-data-saved="(__, { id }) => $router.push({ path: `/worker/${id}` })"
|
||||
>
|
||||
<template #form="{ data, validate }">
|
||||
<VnRow>
|
||||
|
|
|
@ -13,6 +13,7 @@ import { useRole } from 'src/composables/useRole';
|
|||
import { useUserConfig } from 'src/composables/useUserConfig';
|
||||
import { toLowerCamel } from 'src/filters';
|
||||
import { useTokenConfig } from 'src/composables/useTokenConfig';
|
||||
import { useAcl } from 'src/composables/useAcl';
|
||||
|
||||
const state = useState();
|
||||
const session = useSession();
|
||||
|
@ -55,6 +56,7 @@ export default route(function (/* { store, ssrContext } */) {
|
|||
const stateRoles = state.getRoles().value;
|
||||
if (stateRoles.length === 0) {
|
||||
await useRole().fetch();
|
||||
await useAcl().fetch();
|
||||
await useUserConfig().fetch();
|
||||
await useTokenConfig().fetch();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
import { vi, describe, expect, it, beforeAll, afterAll } from 'vitest';
|
||||
import { axios, flushPromises } from 'app/test/vitest/helper';
|
||||
import { useAcl } from 'src/composables/useAcl';
|
||||
|
||||
describe('useAcl', () => {
|
||||
const acl = useAcl();
|
||||
const mockAcls = [
|
||||
{
|
||||
model: 'Address',
|
||||
property: '*',
|
||||
accessType: '*',
|
||||
permission: 'ALLOW',
|
||||
principalType: 'ROLE',
|
||||
principalId: 'employee',
|
||||
},
|
||||
{
|
||||
model: 'Worker',
|
||||
property: 'holidays',
|
||||
accessType: 'READ',
|
||||
permission: 'ALLOW',
|
||||
principalType: 'ROLE',
|
||||
principalId: 'employee',
|
||||
},
|
||||
{
|
||||
model: 'Url',
|
||||
property: 'getByUser',
|
||||
accessType: 'READ',
|
||||
permission: 'ALLOW',
|
||||
principalType: 'ROLE',
|
||||
principalId: '$everyone',
|
||||
},
|
||||
{
|
||||
model: 'TpvTransaction',
|
||||
property: 'start',
|
||||
accessType: 'WRITE',
|
||||
permission: 'ALLOW',
|
||||
principalType: 'ROLE',
|
||||
principalId: '$authenticated',
|
||||
},
|
||||
];
|
||||
|
||||
beforeAll(async () => {
|
||||
vi.spyOn(axios, 'get').mockResolvedValue({ data: mockAcls });
|
||||
await acl.fetch();
|
||||
});
|
||||
|
||||
afterAll(async () => await flushPromises());
|
||||
|
||||
describe('hasAny', () => {
|
||||
it('should return false if no roles matched', async () => {
|
||||
expect(acl.hasAny('Worker', 'updateAttributes', 'WRITE')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return false if no roles matched', async () => {
|
||||
expect(acl.hasAny('Worker', 'holidays', 'READ')).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('*', () => {
|
||||
it('should return true if an acl matched', async () => {
|
||||
expect(acl.hasAny('Address', '*', 'WRITE')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return false if no acls matched', async () => {
|
||||
expect(acl.hasAny('Worker', '*', 'READ')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('$authenticated', () => {
|
||||
it('should return false if no acls matched', async () => {
|
||||
expect(acl.hasAny('Url', 'getByUser', '*')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return true if an acl matched', async () => {
|
||||
expect(acl.hasAny('Url', 'getByUser', 'READ')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('$everyone', () => {
|
||||
it('should return false if no acls matched', async () => {
|
||||
expect(acl.hasAny('TpvTransaction', 'start', 'READ')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return false if an acl matched', async () => {
|
||||
expect(acl.hasAny('TpvTransaction', 'start', 'WRITE')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
import { vi, describe, expect, it, beforeAll, beforeEach } from 'vitest';
|
||||
import { axios, flushPromises } from 'app/test/vitest/helper';
|
||||
import { axios } from 'app/test/vitest/helper';
|
||||
import { useSession } from 'composables/useSession';
|
||||
import { useState } from 'composables/useState';
|
||||
|
||||
|
@ -87,13 +87,17 @@ describe('session', () => {
|
|||
},
|
||||
},
|
||||
];
|
||||
beforeEach(() => {
|
||||
vi.spyOn(axios, 'get').mockImplementation((url) => {
|
||||
if (url === 'VnUsers/acls') return Promise.resolve({ data: [] });
|
||||
return Promise.resolve({
|
||||
data: { roles: rolesData, user: expectedUser },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch the user roles and then set token in the sessionStorage', async () => {
|
||||
const expectedRoles = ['salesPerson', 'admin'];
|
||||
vi.spyOn(axios, 'get').mockResolvedValue({
|
||||
data: { roles: rolesData, user: expectedUser },
|
||||
});
|
||||
|
||||
const expectedToken = 'mySessionToken';
|
||||
const expectedTokenMultimedia = 'mySessionTokenMultimedia';
|
||||
const keepLogin = false;
|
||||
|
@ -117,10 +121,6 @@ describe('session', () => {
|
|||
|
||||
it('should fetch the user roles and then set token in the localStorage', async () => {
|
||||
const expectedRoles = ['salesPerson', 'admin'];
|
||||
vi.spyOn(axios, 'get').mockResolvedValue({
|
||||
data: { roles: rolesData, user: expectedUser },
|
||||
});
|
||||
|
||||
const expectedToken = 'myLocalToken';
|
||||
const expectedTokenMultimedia = 'myLocalTokenMultimedia';
|
||||
const keepLogin = true;
|
||||
|
|
|
@ -23,8 +23,9 @@ describe('Login', () => {
|
|||
},
|
||||
};
|
||||
vi.spyOn(axios, 'post').mockResolvedValueOnce({ data: { token: 'token' } });
|
||||
vi.spyOn(axios, 'get').mockResolvedValue({
|
||||
data: { roles: [], user: expectedUser , multimediaToken: {id:'multimediaToken' }},
|
||||
vi.spyOn(axios, 'get').mockImplementation((url) => {
|
||||
if (url === 'VnUsers/acls') return Promise.resolve({ data: [] });
|
||||
return Promise.resolve({data: { roles: [], user: expectedUser , multimediaToken: {id:'multimediaToken' }}});
|
||||
});
|
||||
vi.spyOn(vm.quasar, 'notify');
|
||||
|
||||
|
|
Loading…
Reference in New Issue