-
+
- {{ $t('salesTicketsTable.noVerifiedData') }}
-
-
- {{ $t('salesTicketsTable.purchaseRequest') }}
-
-
- {{ $t('salesTicketsTable.notVisible') }}
-
-
- {{ $t('salesTicketsTable.clientFrozen') }}
-
-
-
+
{{ $t('salesTicketsTable.componentLack') }}
-
+
+
+ {{ $t('ticket.summary.hasItemDelay') }}
+
+
+
+
+ {{ $t('salesTicketsTable.hasItemLost') }}
+
+
+
+ {{ $t('salesTicketsTable.notVisible') }}
+
+
+
+ {{ $t('ticketList.rounding') }}
+
+
+
+ {{ $t('salesTicketsTable.purchaseRequest') }}
+
+
+ {{ $t('salesTicketsTable.noVerifiedData') }}
+
+
+ {{ $t('salesTicketsTable.clientFrozen') }}
+
+
{{ $t('salesTicketsTable.tooLittle') }}
diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 185d41ebb..04b7c0a46 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -629,7 +629,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
size="md"
round
flat
- shortcut="+"
+ v-shortcut="'+'"
:disabled="!disabledAttr"
/>
@@ -647,7 +647,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
color="primary"
fab
icon="add"
- shortcut="+"
+ v-shortcut="'+'"
data-cy="vnTableCreateBtn"
/>
diff --git a/src/components/__tests__/UserPanel.spec.js b/src/components/__tests__/UserPanel.spec.js
new file mode 100644
index 000000000..ac20f911e
--- /dev/null
+++ b/src/components/__tests__/UserPanel.spec.js
@@ -0,0 +1,61 @@
+import { vi, describe, expect, it, beforeEach, beforeAll, afterEach } from 'vitest';
+import { createWrapper } from 'app/test/vitest/helper';
+import UserPanel from 'src/components/UserPanel.vue';
+import axios from 'axios';
+import { useState } from 'src/composables/useState';
+
+describe('UserPanel', () => {
+ let wrapper;
+ let vm;
+ let state;
+
+ beforeEach(() => {
+ wrapper = createWrapper(UserPanel, {});
+ state = useState();
+ state.setUser({
+ id: 115,
+ name: 'itmanagement',
+ nickname: 'itManagementNick',
+ lang: 'en',
+ darkMode: false,
+ companyFk: 442,
+ warehouseFk: 1,
+ });
+ wrapper = wrapper.wrapper;
+ vm = wrapper.vm;
+ });
+
+ afterEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('should fetch warehouses data on mounted', async () => {
+ const fetchData = wrapper.findComponent({ name: 'FetchData' });
+ expect(fetchData.props('url')).toBe('Warehouses');
+ expect(fetchData.props('autoLoad')).toBe(true);
+ });
+
+ it('should toggle dark mode correctly and update preferences', async () => {
+ await vm.saveDarkMode(true);
+ expect(axios.patch).toHaveBeenCalledWith('/UserConfigs/115', { darkMode: true });
+ expect(vm.user.darkMode).toBe(true);
+ vm.updatePreferences();
+ expect(vm.darkMode).toBe(true);
+ });
+
+ it('should change user language and update preferences', async () => {
+ const userLanguage = 'es';
+ await vm.saveLanguage(userLanguage);
+ expect(axios.patch).toHaveBeenCalledWith('/VnUsers/115', { lang: userLanguage });
+ expect(vm.user.lang).toBe(userLanguage);
+ vm.updatePreferences();
+ expect(vm.locale).toBe(userLanguage);
+ });
+
+ it('should update user data', async () => {
+ const key = 'name';
+ const value = 'itboss';
+ await vm.saveUserData(key, value);
+ expect(axios.post).toHaveBeenCalledWith('UserConfigs/setUserConfig', { [key]: value });
+ });
+});
diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue
index 36c87bab0..424781a26 100644
--- a/src/components/common/VnDmsList.vue
+++ b/src/components/common/VnDmsList.vue
@@ -17,7 +17,7 @@ import { useSession } from 'src/composables/useSession';
const route = useRoute();
const quasar = useQuasar();
const { t } = useI18n();
-const rows = ref();
+const rows = ref([]);
const dmsRef = ref();
const formDialog = ref({});
const token = useSession().getTokenMultimedia();
@@ -389,6 +389,14 @@ defineExpose({
+
+
+ {{ t('No data to display') }}
+
+
@@ -405,7 +413,7 @@ defineExpose({
fab
color="primary"
icon="add"
- shortcut="+"
+ v-shortcut
@click="showFormDialog()"
class="fill-icon"
>
diff --git a/src/components/common/VnLog.vue b/src/components/common/VnLog.vue
index fdf2e52ee..d870e487f 100644
--- a/src/components/common/VnLog.vue
+++ b/src/components/common/VnLog.vue
@@ -268,7 +268,7 @@ async function applyFilter() {
filter.where.and.push(selectedFilters.value);
}
- paginate.value.fetch(filter);
+ paginate.value.fetch({ filter });
}
function setDate(type) {
@@ -404,7 +404,7 @@ watch(
ref="paginate"
:data-key="`${model}Log`"
:url="`${model}Logs`"
- :filter="filter"
+ :user-filter="filter"
:skeleton="false"
auto-load
@on-fetch="setLogTree"
diff --git a/src/components/common/__tests__/VnNotes.spec.js b/src/components/common/__tests__/VnNotes.spec.js
index 8f24a7f14..2603bf03c 100644
--- a/src/components/common/__tests__/VnNotes.spec.js
+++ b/src/components/common/__tests__/VnNotes.spec.js
@@ -1,51 +1,78 @@
-import { describe, it, expect, vi, beforeAll, afterEach, beforeEach } from 'vitest';
+import {
+ describe,
+ it,
+ expect,
+ vi,
+ beforeAll,
+ afterEach,
+ beforeEach,
+ afterAll,
+} from 'vitest';
import { createWrapper, axios } from 'app/test/vitest/helper';
import VnNotes from 'src/components/ui/VnNotes.vue';
+import vnDate from 'src/boot/vnDate';
describe('VnNotes', () => {
let vm;
let wrapper;
let spyFetch;
let postMock;
- let expectedBody;
- const mockData= {name: 'Tony', lastName: 'Stark', text: 'Test Note', observationTypeFk: 1};
-
- function generateExpectedBody() {
- expectedBody = {...vm.$props.body, ...{ text: vm.newNote.text, observationTypeFk: vm.newNote.observationTypeFk }};
- }
-
- async function setTestParams(text, observationType, type){
- vm.newNote.text = text;
- vm.newNote.observationTypeFk = observationType;
- wrapper.setProps({ selectType: type });
- }
-
- beforeAll(async () => {
- vi.spyOn(axios, 'get').mockReturnValue({ data: [] });
-
+ let patchMock;
+ let expectedInsertBody;
+ let expectedUpdateBody;
+ const defaultOptions = {
+ url: '/test',
+ body: { name: 'Tony', lastName: 'Stark' },
+ selectType: false,
+ saveUrl: null,
+ justInput: false,
+ };
+ function generateWrapper(
+ options = defaultOptions,
+ text = null,
+ observationType = null,
+ ) {
+ vi.spyOn(axios, 'get').mockResolvedValue({ data: [] });
wrapper = createWrapper(VnNotes, {
- propsData: {
- url: '/test',
- body: { name: 'Tony', lastName: 'Stark' },
- }
+ propsData: options,
});
wrapper = wrapper.wrapper;
vm = wrapper.vm;
- });
+ vm.newNote.text = text;
+ vm.newNote.observationTypeFk = observationType;
+ }
+
+ function createSpyFetch() {
+ spyFetch = vi.spyOn(vm.$refs.vnPaginateRef, 'fetch');
+ }
+
+ function generateExpectedBody() {
+ expectedInsertBody = {
+ ...vm.$props.body,
+ ...{ text: vm.newNote.text, observationTypeFk: vm.newNote.observationTypeFk },
+ };
+ expectedUpdateBody = { ...vm.$props.body, ...{ notes: vm.newNote.text } };
+ }
beforeEach(() => {
- postMock = vi.spyOn(axios, 'post').mockResolvedValue(mockData);
- spyFetch = vi.spyOn(vm.vnPaginateRef, 'fetch').mockImplementation(() => vi.fn());
+ postMock = vi.spyOn(axios, 'post');
+ patchMock = vi.spyOn(axios, 'patch');
});
afterEach(() => {
vi.clearAllMocks();
- expectedBody = {};
+ expectedInsertBody = {};
+ expectedUpdateBody = {};
+ });
+
+ afterAll(() => {
+ vi.restoreAllMocks();
});
describe('insert', () => {
- it('should not call axios.post and vnPaginateRef.fetch if newNote.text is null', async () => {
- await setTestParams( null, null, true );
+ it('should not call axios.post and vnPaginateRef.fetch when newNote.text is null', async () => {
+ generateWrapper({ selectType: true });
+ createSpyFetch();
await vm.insert();
@@ -53,8 +80,9 @@ describe('VnNotes', () => {
expect(spyFetch).not.toHaveBeenCalled();
});
- it('should not call axios.post and vnPaginateRef.fetch if newNote.text is empty', async () => {
- await setTestParams( "", null, false );
+ it('should not call axios.post and vnPaginateRef.fetch when newNote.text is empty', async () => {
+ generateWrapper(null, '');
+ createSpyFetch();
await vm.insert();
@@ -62,8 +90,9 @@ describe('VnNotes', () => {
expect(spyFetch).not.toHaveBeenCalled();
});
- it('should not call axios.post and vnPaginateRef.fetch if observationTypeFk is missing and selectType is true', async () => {
- await setTestParams( "Test Note", null, true );
+ it('should not call axios.post and vnPaginateRef.fetch when observationTypeFk is null and selectType is true', async () => {
+ generateWrapper({ selectType: true }, 'Test Note');
+ createSpyFetch();
await vm.insert();
@@ -71,37 +100,57 @@ describe('VnNotes', () => {
expect(spyFetch).not.toHaveBeenCalled();
});
- it('should call axios.post and vnPaginateRef.fetch if observationTypeFk is missing and selectType is false', async () => {
- await setTestParams( "Test Note", null, false );
-
+ it('should call axios.post and vnPaginateRef.fetch when observationTypeFk is missing and selectType is false', async () => {
+ generateWrapper(null, 'Test Note');
+ createSpyFetch();
generateExpectedBody();
await vm.insert();
- expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody);
- expect(spyFetch).toHaveBeenCalled();
- });
-
- it('should call axios.post and vnPaginateRef.fetch if observationTypeFk is setted and selectType is false', async () => {
- await setTestParams( "Test Note", 1, false );
-
- generateExpectedBody();
-
- await vm.insert();
-
- expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody);
+ expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedInsertBody);
expect(spyFetch).toHaveBeenCalled();
});
it('should call axios.post and vnPaginateRef.fetch when newNote is valid', async () => {
- await setTestParams( "Test Note", 1, true );
-
+ generateWrapper({ selectType: true }, 'Test Note', 1);
+ createSpyFetch();
generateExpectedBody();
-
+
await vm.insert();
- expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody);
+ expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedInsertBody);
expect(spyFetch).toHaveBeenCalled();
});
});
-});
\ No newline at end of file
+
+ describe('update', () => {
+ it('should call axios.patch with saveUrl when saveUrl is set and justInput is true', async () => {
+ generateWrapper({
+ url: '/business',
+ justInput: true,
+ saveUrl: '/saveUrlTest',
+ });
+ generateExpectedBody();
+
+ await vm.update();
+
+ expect(patchMock).toHaveBeenCalledWith(vm.$props.saveUrl, expectedUpdateBody);
+ });
+
+ it('should call axios.patch with url when saveUrl is not set and justInput is true', async () => {
+ generateWrapper({
+ url: '/business',
+ body: { workerFk: 1110 },
+ justInput: true,
+ });
+ generateExpectedBody();
+
+ await vm.update();
+
+ expect(patchMock).toHaveBeenCalledWith(
+ `${vm.$props.url}/${vm.$props.body.workerFk}`,
+ expectedUpdateBody,
+ );
+ });
+ });
+});
diff --git a/src/components/ui/CardSummary.vue b/src/components/ui/CardSummary.vue
index 2d7da8729..9d108c1f8 100644
--- a/src/components/ui/CardSummary.vue
+++ b/src/components/ui/CardSummary.vue
@@ -15,6 +15,10 @@ const props = defineProps({
type: Object,
default: null,
},
+ userFilter: {
+ type: Object,
+ default: null,
+ },
entityId: {
type: [Number, String],
default: null,
@@ -34,6 +38,7 @@ const isSummary = ref();
const arrayData = useArrayData(props.dataKey, {
url: props.url,
filter: props.filter,
+ userFilter: props.userFilter,
skip: 0,
});
const { store } = arrayData;
diff --git a/src/components/ui/VnNotes.vue b/src/components/ui/VnNotes.vue
index 1690a94ba..5b1d6e726 100644
--- a/src/components/ui/VnNotes.vue
+++ b/src/components/ui/VnNotes.vue
@@ -1,6 +1,6 @@
{
auto-load
@on-fetch="(data) => (observationTypes = data)"
/>
-
-
+
+
+
{{ t('New note') }}
@@ -75,19 +138,19 @@ onBeforeRouteLeave((to, from, next) => {
v-model="newNote.observationTypeFk"
option-label="description"
style="flex: 0.15"
- :required="true"
+ :required="isRequired"
@keyup.enter.stop="insert"
/>
{
icon="save"
color="primary"
flat
- @click="insert"
+ @click="handleClick"
class="q-mb-xs"
dense
data-cy="saveNote"
@@ -106,6 +169,7 @@ onBeforeRouteLeave((to, from, next) => {
{
}
}
}
+.just-input {
+ padding-right: 18px;
+ margin-bottom: 2px;
+ box-shadow: none;
+}
es:
@@ -205,4 +274,6 @@ onBeforeRouteLeave((to, from, next) => {
New note: Nueva nota
Save (Enter): Guardar (Intro)
Observation type: Tipo de observación
+ New note is empty: La nueva nota esta vacia
+ Are you sure remove this note?: Estas seguro de quitar esta nota?
diff --git a/src/components/ui/VnSubToolbar.vue b/src/components/ui/VnSubToolbar.vue
index 5ded4be00..8d4126d1d 100644
--- a/src/components/ui/VnSubToolbar.vue
+++ b/src/components/ui/VnSubToolbar.vue
@@ -19,23 +19,26 @@ onMounted(() => {
const observer = new MutationObserver(
() =>
(hasContent.value =
- actions.value?.childNodes?.length + data.value?.childNodes?.length)
+ actions.value?.childNodes?.length + data.value?.childNodes?.length),
);
if (actions.value) observer.observe(actions.value, opts);
if (data.value) observer.observe(data.value, opts);
});
-onBeforeUnmount(() => stateStore.toggleSubToolbar());
+const actionsChildCount = () => !!actions.value?.childNodes?.length;
+
+onBeforeUnmount(() => stateStore.toggleSubToolbar() && hasSubToolbar);
-
+
+
diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
index 3268939de..bd3cecf08 100644
--- a/src/composables/useArrayData.js
+++ b/src/composables/useArrayData.js
@@ -93,6 +93,9 @@ export function useArrayData(key, userOptions) {
if (params.filter.where || exprFilter)
params.filter.where = { ...params.filter.where, ...exprFilter };
+
+ if (!params?.filter?.order?.length) delete params?.filter?.order;
+
params.filter = JSON.stringify(params.filter);
store.isLoading = true;
diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue
index 2a84e5aa1..3ad1c79bc 100644
--- a/src/layouts/MainLayout.vue
+++ b/src/layouts/MainLayout.vue
@@ -2,7 +2,7 @@
import Navbar from 'src/components/NavBar.vue';
-
+
diff --git a/src/pages/Account/Card/AccountMailAlias.vue b/src/pages/Account/Card/AccountMailAlias.vue
index efd2b481b..54798d6ab 100644
--- a/src/pages/Account/Card/AccountMailAlias.vue
+++ b/src/pages/Account/Card/AccountMailAlias.vue
@@ -86,7 +86,7 @@ watch(
() => route.params.id,
() => {
getAccountData();
- }
+ },
);
onMounted(async () => await getAccountData(false));
@@ -130,7 +130,8 @@ onMounted(async () => await getAccountData(false));
openConfirmationModal(
t('User will be removed from alias'),
t('¿Seguro que quieres continuar?'),
- () => deleteMailAlias(row, rows, rowIndex)
+ () =>
+ deleteMailAlias(row, rows, rowIndex),
)
"
>
@@ -157,7 +158,7 @@ onMounted(async () => await getAccountData(false));
icon="add"
color="primary"
@click="openCreateMailAliasForm()"
- shortcut="+"
+ v-shortcut="'+'"
>
{{ t('warehouses.add') }}
diff --git a/src/pages/Account/Role/Card/SubRoles.vue b/src/pages/Account/Role/Card/SubRoles.vue
index 6cac94667..d1a146375 100644
--- a/src/pages/Account/Role/Card/SubRoles.vue
+++ b/src/pages/Account/Role/Card/SubRoles.vue
@@ -63,7 +63,7 @@ watch(
store.url = urlPath.value;
store.filter = filter.value;
fetchSubRoles();
- }
+ },
);
const fetchSubRoles = () => paginateRef.value.fetch();
@@ -109,7 +109,7 @@ const redirectToRoleSummary = (id) =>
openConfirmationModal(
t('El rol va a ser eliminado'),
t('¿Seguro que quieres continuar?'),
- () => deleteSubRole(row, rows, rowIndex)
+ () => deleteSubRole(row, rows, rowIndex),
)
"
>
@@ -131,7 +131,7 @@ const redirectToRoleSummary = (id) =>