From 3f1c0b95faf7d8ff00b10fa93acdbed4047d6b9a Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 10 Jul 2024 15:03:25 +0200
Subject: [PATCH 01/12] fix: proposal to avoid notify error

---
 src/pages/Customer/Card/CustomerUnpaid.vue               | 2 +-
 src/pages/Customer/Card/CustomerWebAccess.vue            | 2 +-
 src/pages/Customer/components/CustomerChangePassword.vue | 2 +-
 src/pages/Customer/components/CustomerSamplesCreate.vue  | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/pages/Customer/Card/CustomerUnpaid.vue b/src/pages/Customer/Card/CustomerUnpaid.vue
index a9d4a3d66..6c61b92a1 100644
--- a/src/pages/Customer/Card/CustomerUnpaid.vue
+++ b/src/pages/Customer/Card/CustomerUnpaid.vue
@@ -92,7 +92,7 @@ const onSubmit = async () => {
         notify('globals.dataSaved', 'positive');
         unpaidClient.value = true;
     } catch (error) {
-        notify('errors.create', 'negative');
+        notify('errors.writeRequest', 'negative');
     } finally {
         isLoading.value = false;
     }
diff --git a/src/pages/Customer/Card/CustomerWebAccess.vue b/src/pages/Customer/Card/CustomerWebAccess.vue
index 33659dd77..4468c52f0 100644
--- a/src/pages/Customer/Card/CustomerWebAccess.vue
+++ b/src/pages/Customer/Card/CustomerWebAccess.vue
@@ -70,7 +70,7 @@ const onSubmit = async () => {
         notify('globals.dataSaved', 'positive');
         if (usersPreviewRef.value) usersPreviewRef.value.fetch();
     } catch (error) {
-        notify('errors.create', 'negative');
+        notify('errors.writeRequest', 'negative');
     } finally {
         isLoading.value = false;
     }
diff --git a/src/pages/Customer/components/CustomerChangePassword.vue b/src/pages/Customer/components/CustomerChangePassword.vue
index 1bfc5e103..632b11dc9 100644
--- a/src/pages/Customer/components/CustomerChangePassword.vue
+++ b/src/pages/Customer/components/CustomerChangePassword.vue
@@ -48,7 +48,7 @@ const onSubmit = async () => {
         await axios.patch(`Clients/${$props.id}/setPassword`, payload);
         await $props.promise();
     } catch (error) {
-        notify('errors.create', 'negative');
+        notify('errors.writeRequest', 'negative');
     } finally {
         isLoading.value = false;
         if (closeButton.value) closeButton.value.click();
diff --git a/src/pages/Customer/components/CustomerSamplesCreate.vue b/src/pages/Customer/components/CustomerSamplesCreate.vue
index be614aa0b..283b8fa97 100644
--- a/src/pages/Customer/components/CustomerSamplesCreate.vue
+++ b/src/pages/Customer/components/CustomerSamplesCreate.vue
@@ -150,7 +150,7 @@ const onSubmit = async () => {
         notify('globals.dataSaved', 'positive');
         onDataSaved(data);
     } catch (error) {
-        notify('errors.create', 'negative');
+        notify('errors.writeRequest', 'negative');
     } finally {
         isLoading.value = false;
     }

From e90b78c4c5867f2afd48b9029c9b443d5291c385 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Fri, 13 Sep 2024 12:27:37 +0200
Subject: [PATCH 02/12] fix: refs #7702 rollback

---
 src/pages/Customer/Card/CustomerWebAccess.vue | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/src/pages/Customer/Card/CustomerWebAccess.vue b/src/pages/Customer/Card/CustomerWebAccess.vue
index 3dc025d9c..f990e720c 100644
--- a/src/pages/Customer/Card/CustomerWebAccess.vue
+++ b/src/pages/Customer/Card/CustomerWebAccess.vue
@@ -4,7 +4,6 @@ import { useI18n } from 'vue-i18n';
 import { useRoute } from 'vue-router';
 import axios from 'axios';
 import { useQuasar } from 'quasar';
-import useNotify from 'src/composables/useNotify';
 import VnInput from 'src/components/common/VnInput.vue';
 import CustomerChangePassword from 'src/pages/Customer/components/CustomerChangePassword.vue';
 import FormModel from 'components/FormModel.vue';
@@ -13,7 +12,6 @@ const { t } = useI18n();
 const quasar = useQuasar();
 const route = useRoute();
 const canChangePassword = ref(0);
-const { notify } = useNotify();
 
 const filter = computed(() => {
     return {
@@ -32,13 +30,8 @@ const showChangePasswordDialog = () => {
 };
 
 async function hasCustomerRole() {
-    try {
-        canChangePassword.value = (
-            await axios(`Clients/${route.params.id}/hasCustomerRole`)
-        ).data;
-    } catch (e) {
-        notify('errors.writeRequest', 'negative');
-    }
+    const { data } = await axios(`Clients/${route.params.id}/hasCustomerRole`);
+    canChangePassword.value = data;
 }
 </script>
 

From 4bf8e1224d1d8cd0082174fe7830baeeddc45b0c Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Fri, 13 Sep 2024 12:28:07 +0200
Subject: [PATCH 03/12] chore: refs #7702 rollback

---
 src/pages/Customer/Card/CustomerWebAccess.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/pages/Customer/Card/CustomerWebAccess.vue b/src/pages/Customer/Card/CustomerWebAccess.vue
index f990e720c..8d025a365 100644
--- a/src/pages/Customer/Card/CustomerWebAccess.vue
+++ b/src/pages/Customer/Card/CustomerWebAccess.vue
@@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n';
 import { useRoute } from 'vue-router';
 import axios from 'axios';
 import { useQuasar } from 'quasar';
+
 import VnInput from 'src/components/common/VnInput.vue';
 import CustomerChangePassword from 'src/pages/Customer/components/CustomerChangePassword.vue';
 import FormModel from 'components/FormModel.vue';

From 98cdeabe9f91791faa4a623ed9e67237180b0666 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Fri, 13 Sep 2024 18:15:28 +0200
Subject: [PATCH 04/12] feat: refs #7702 vnChangePassword

---
 src/components/common/VnChangePassword.vue    | 112 ++++++++++++++++++
 src/pages/Customer/Card/CustomerWebAccess.vue |  28 ++---
 2 files changed, 124 insertions(+), 16 deletions(-)
 create mode 100644 src/components/common/VnChangePassword.vue

diff --git a/src/components/common/VnChangePassword.vue b/src/components/common/VnChangePassword.vue
new file mode 100644
index 000000000..d7c9ad02f
--- /dev/null
+++ b/src/components/common/VnChangePassword.vue
@@ -0,0 +1,112 @@
+<script setup>
+import { ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import VnRow from '../ui/VnRow.vue';
+import VnInput from './VnInput.vue';
+import FetchData from '../FetchData.vue';
+import useNotify from 'src/composables/useNotify';
+
+const props = defineProps({ submitFn: { type: Function, default: () => {} } });
+
+const { t } = useI18n();
+const { notify } = useNotify();
+
+const form = ref();
+const changePassDialog = ref();
+const passwords = ref({ newPassword: null, repeatPassword: null });
+const requirements = ref([]);
+
+const validate = async () => {
+    const { newPassword, repeatPassword } = passwords.value;
+    if (newPassword !== repeatPassword) {
+        notify(t("Passwords don't match"), 'negative');
+        return;
+    }
+    try {
+        await props.submitFn(newPassword);
+    } catch (e) {
+        notify('errors.writeRequest', 'negative');
+    } finally {
+        changePassDialog.value.hide();
+    }
+};
+
+defineExpose({ show: () => changePassDialog.value.show() });
+</script>
+<template>
+    <FetchData
+        url="UserPasswords/findOne"
+        auto-load
+        @on-fetch="(data) => (requirements = data)"
+    />
+    <QDialog ref="changePassDialog">
+        <QCard style="width: 350px">
+            <QCardSection>
+                <slot name="header">
+                    <VnRow class="items-center" style="flex-direction: row">
+                        <span class="text-h6" v-text="t('Change password')" />
+                        <QIcon
+                            class="cursor-pointer"
+                            name="close"
+                            size="xs"
+                            style="flex: 0"
+                            v-close-popup
+                        />
+                    </VnRow>
+                </slot>
+            </QCardSection>
+            <QForm ref="form">
+                <QCardSection>
+                    <VnInput
+                        :label="t('New password')"
+                        v-model="passwords.newPassword"
+                        type="password"
+                        :required="true"
+                        :info="
+                            t('passwordRequirements', {
+                                length: requirements.length,
+                                nAlpha: requirements.nAlpha,
+                                nUpper: requirements.nUpper,
+                                nDigits: requirements.nDigits,
+                                nPunct: requirements.nPunct,
+                            })
+                        "
+                        autofocus
+                    />
+
+                    <VnInput
+                        :label="t('Repeat password')"
+                        v-model="passwords.repeatPassword"
+                        type="password"
+                    />
+                </QCardSection>
+            </QForm>
+            <QCardActions>
+                <slot name="actions">
+                    <QBtn
+                        :label="t('globals.cancel')"
+                        class="q-ml-sm"
+                        color="primary"
+                        flat
+                        type="reset"
+                        v-close-popup
+                    />
+                    <QBtn
+                        :label="t('globals.confirm')"
+                        color="primary"
+                        @click="validate"
+                    />
+                </slot>
+            </QCardActions>
+        </QCard>
+    </QDialog>
+</template>
+
+<i18n>
+es:
+    Change password: Cambiar contraseña
+    New password: Nueva contraseña
+    Repeat password: Repetir contraseña
+    You must enter a new password: Debes introducir la nueva contraseña
+    Passwords don't match: Las contraseñas no coinciden
+</i18n>
diff --git a/src/pages/Customer/Card/CustomerWebAccess.vue b/src/pages/Customer/Card/CustomerWebAccess.vue
index 8d025a365..ba906a144 100644
--- a/src/pages/Customer/Card/CustomerWebAccess.vue
+++ b/src/pages/Customer/Card/CustomerWebAccess.vue
@@ -3,14 +3,11 @@ import { computed, ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRoute } from 'vue-router';
 import axios from 'axios';
-import { useQuasar } from 'quasar';
-
 import VnInput from 'src/components/common/VnInput.vue';
-import CustomerChangePassword from 'src/pages/Customer/components/CustomerChangePassword.vue';
 import FormModel from 'components/FormModel.vue';
+import VnChangePassword from 'src/components/common/VnChangePassword.vue';
 
 const { t } = useI18n();
-const quasar = useQuasar();
 const route = useRoute();
 const canChangePassword = ref(0);
 
@@ -21,21 +18,11 @@ const filter = computed(() => {
     };
 });
 
-const showChangePasswordDialog = () => {
-    quasar.dialog({
-        component: CustomerChangePassword,
-        componentProps: {
-            id: route.params.id,
-        },
-    });
-};
-
 async function hasCustomerRole() {
     const { data } = await axios(`Clients/${route.params.id}/hasCustomerRole`);
     canChangePassword.value = data;
 }
 </script>
-
 <template>
     <FormModel
         url="VnUsers/preview"
@@ -73,12 +60,21 @@ async function hasCustomerRole() {
                 color="primary"
                 icon="edit"
                 :disable="!canChangePassword"
-                @click="showChangePasswordDialog()"
+                @click="$refs.changePassRef.show"
             />
         </template>
     </FormModel>
+    <VnChangePassword
+        ref="changePassRef"
+        :submit-fn="
+            async (newPass) => {
+                await axios.patch(`Clients/${$route.params.id}/setPassword`, {
+                    newPassword: newPass,
+                });
+            }
+        "
+    />
 </template>
-
 <i18n>
 es:
     Enable web access: Habilitar acceso web

From 9ec2fb4c77edede9e5b80a34a80263a33ac80a1c Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Mon, 16 Sep 2024 10:36:56 +0200
Subject: [PATCH 05/12] feat: refs #7702 fine tunning

---
 src/components/common/VnChangePassword.vue | 14 +++++++-
 src/pages/Worker/Card/WorkerDescriptor.vue | 40 ++++++++++------------
 2 files changed, 31 insertions(+), 23 deletions(-)

diff --git a/src/components/common/VnChangePassword.vue b/src/components/common/VnChangePassword.vue
index d7c9ad02f..a36c6bcff 100644
--- a/src/components/common/VnChangePassword.vue
+++ b/src/components/common/VnChangePassword.vue
@@ -7,7 +7,7 @@ import FetchData from '../FetchData.vue';
 import useNotify from 'src/composables/useNotify';
 
 const props = defineProps({ submitFn: { type: Function, default: () => {} } });
-
+const emit = defineEmits(['onSubmit']);
 const { t } = useI18n();
 const { notify } = useNotify();
 
@@ -15,19 +15,27 @@ const form = ref();
 const changePassDialog = ref();
 const passwords = ref({ newPassword: null, repeatPassword: null });
 const requirements = ref([]);
+const isLoading = ref(false);
 
 const validate = async () => {
     const { newPassword, repeatPassword } = passwords.value;
+    if (!newPassword) {
+        notify(t('You must enter a new password'), 'negative');
+        return;
+    }
     if (newPassword !== repeatPassword) {
         notify(t("Passwords don't match"), 'negative');
         return;
     }
     try {
+        isLoading.value = true;
         await props.submitFn(newPassword);
+        emit('onSubmit');
     } catch (e) {
         notify('errors.writeRequest', 'negative');
     } finally {
         changePassDialog.value.hide();
+        isLoading.value = false;
     }
 };
 
@@ -84,6 +92,8 @@ defineExpose({ show: () => changePassDialog.value.show() });
             <QCardActions>
                 <slot name="actions">
                     <QBtn
+                        :disabled="isLoading"
+                        :loading="isLoading"
                         :label="t('globals.cancel')"
                         class="q-ml-sm"
                         color="primary"
@@ -92,6 +102,8 @@ defineExpose({ show: () => changePassDialog.value.show() });
                         v-close-popup
                     />
                     <QBtn
+                        :disabled="isLoading"
+                        :loading="isLoading"
                         :label="t('globals.confirm')"
                         color="primary"
                         @click="validate"
diff --git a/src/pages/Worker/Card/WorkerDescriptor.vue b/src/pages/Worker/Card/WorkerDescriptor.vue
index be30537df..b48859e9b 100644
--- a/src/pages/Worker/Card/WorkerDescriptor.vue
+++ b/src/pages/Worker/Card/WorkerDescriptor.vue
@@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n';
 import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
-import WorkerChangePasswordForm from 'src/pages/Worker/Card/WorkerChangePasswordForm.vue';
+import VnChangePassword from 'src/components/common/VnChangePassword.vue';
 import { useState } from 'src/composables/useState';
 import axios from 'axios';
 import VnImg from 'src/components/ui/VnImg.vue';
@@ -24,18 +24,13 @@ const route = useRoute();
 const { t } = useI18n();
 const state = useState();
 const user = state.getUser();
-const changePasswordFormDialog = ref(null);
-const cardDescriptorRef = ref(null);
 const showEditPhotoForm = ref(false);
 const toggleEditPictureForm = () => {
     showEditPhotoForm.value = !showEditPhotoForm.value;
 };
-
 const entityId = computed(() => {
     return $props.id || route.params.id;
 });
-
-const worker = ref();
 const workerExcluded = ref(false);
 
 const getIsExcluded = async () => {
@@ -61,10 +56,10 @@ const handleExcluded = async () => {
 
     workerExcluded.value = !workerExcluded.value;
 };
+
 const handlePhotoUpdated = (evt = false) => {
     image.value.reload(evt);
 };
-const refetch = async () => await cardDescriptorRef.value.getData();
 </script>
 <template>
     <CardDescriptor
@@ -74,15 +69,10 @@ const refetch = async () => await cardDescriptorRef.value.getData();
         url="Workers/descriptor"
         :filter="{ where: { id: entityId } }"
         title="user.nickname"
-        @on-fetch="
-            (data) => {
-                worker = data;
-                getIsExcluded();
-            }
-        "
+        @on-fetch="getIsExcluded"
     >
-        <template #menu="{}">
-            <QItem v-ripple clickable @click="handleExcluded()">
+        <template #menu="{ entity }">
+            <QItem v-ripple clickable @click="handleExcluded">
                 <QItemSection>
                     {{
                         workerExcluded
@@ -92,16 +82,13 @@ const refetch = async () => await cardDescriptorRef.value.getData();
                 </QItemSection>
             </QItem>
             <QItem
-                v-if="!worker.user.emailVerified && user.id != worker.id"
+                v-if="!entity.user.emailVerified && user.id != entity.id"
                 v-ripple
                 clickable
-                @click="$refs.changePasswordFormDialog.show()"
+                @click="$refs.changePassRef.show"
             >
                 <QItemSection>
                     {{ t('Change password') }}
-                    <QDialog ref="changePasswordFormDialog">
-                        <WorkerChangePasswordForm @on-submit="refetch()" :id="entityId" />
-                    </QDialog>
                 </QItemSection>
             </QItem>
         </template>
@@ -163,10 +150,10 @@ const refetch = async () => await cardDescriptorRef.value.getData();
                     <VnLinkPhone :phone-number="entity.phone" />
                 </template>
             </VnLv>
-            <VnLv :value="worker?.sip?.extension">
+            <VnLv :value="entity?.sip?.extension">
                 <template #label>
                     {{ t('worker.summary.sipExtension') }}
-                    <VnLinkPhone :phone-number="worker?.sip?.extension" />
+                    <VnLinkPhone :phone-number="entity?.sip?.extension" />
                 </template>
             </VnLv>
         </template>
@@ -197,6 +184,15 @@ const refetch = async () => await cardDescriptorRef.value.getData();
             </QCardActions>
         </template>
     </CardDescriptor>
+    <VnChangePassword
+        ref="changePassRef"
+        :submit-fn="
+            async (newPass) => {
+                await axios.patch(`Workers/${$route.params.id}/setPassword`, { newPass });
+            }
+        "
+        @on-submit="$refs.cardDescriptorRef?.getData"
+    />
 </template>
 
 <style lang="scss" scoped>

From 91dccd10d370263d0e9c7087efd0b9e8156b2b8d Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Mon, 16 Sep 2024 10:39:39 +0200
Subject: [PATCH 06/12] feat: refs #7702 drop old components

---
 .../components/CustomerChangePassword.vue     | 139 ------------------
 .../Worker/Card/WorkerChangePasswordForm.vue  |  99 -------------
 2 files changed, 238 deletions(-)
 delete mode 100644 src/pages/Customer/components/CustomerChangePassword.vue
 delete mode 100644 src/pages/Worker/Card/WorkerChangePasswordForm.vue

diff --git a/src/pages/Customer/components/CustomerChangePassword.vue b/src/pages/Customer/components/CustomerChangePassword.vue
deleted file mode 100644
index 632b11dc9..000000000
--- a/src/pages/Customer/components/CustomerChangePassword.vue
+++ /dev/null
@@ -1,139 +0,0 @@
-<script setup>
-import { ref } from 'vue';
-import { useI18n } from 'vue-i18n';
-
-import axios from 'axios';
-import { useDialogPluginComponent } from 'quasar';
-
-import useNotify from 'src/composables/useNotify';
-
-import VnRow from 'components/ui/VnRow.vue';
-import VnInput from 'src/components/common/VnInput.vue';
-import FetchData from 'src/components/FetchData.vue';
-
-const { dialogRef } = useDialogPluginComponent();
-const { notify } = useNotify();
-const { t } = useI18n();
-
-const $props = defineProps({
-    id: {
-        type: String,
-        required: true,
-    },
-    promise: {
-        type: Function,
-        required: true,
-    },
-});
-const userPasswords = ref({});
-
-const closeButton = ref(null);
-const isLoading = ref(false);
-const newPassword = ref('');
-const requestPassword = ref('');
-
-const onSubmit = async () => {
-    isLoading.value = true;
-
-    if (newPassword.value !== requestPassword.value) {
-        notify(t("Passwords don't match"), 'negative');
-        isLoading.value = false;
-        return;
-    }
-
-    const payload = {
-        newPassword: newPassword.value,
-    };
-    try {
-        await axios.patch(`Clients/${$props.id}/setPassword`, payload);
-        await $props.promise();
-    } catch (error) {
-        notify('errors.writeRequest', 'negative');
-    } finally {
-        isLoading.value = false;
-        if (closeButton.value) closeButton.value.click();
-    }
-};
-</script>
-
-<template>
-    <QDialog ref="dialogRef">
-        <FetchData
-            @on-fetch="(data) => (userPasswords = data[0])"
-            auto-load
-            url="UserPasswords"
-        />
-        <QCard class="q-pa-lg">
-            <QCardSection>
-                <QForm @submit.prevent="onSubmit">
-                    <span
-                        ref="closeButton"
-                        class="row justify-end close-icon"
-                        v-close-popup
-                    >
-                        <QIcon name="close" size="sm" />
-                    </span>
-
-                    <VnRow class="row q-gutter-md q-mb-md" style="flex-direction: column">
-                        <div class="col">
-                            <VnInput
-                                :label="t('New password')"
-                                clearable
-                                v-model="newPassword"
-                                type="password"
-                            >
-                                <template #append>
-                                    <QIcon name="info" class="cursor-info">
-                                        <QTooltip>
-                                            {{
-                                                t('customer.card.passwordRequirements', {
-                                                    ...userPasswords,
-                                                })
-                                            }}
-                                        </QTooltip>
-                                    </QIcon>
-                                </template>
-                            </VnInput>
-                        </div>
-                        <div class="col">
-                            <VnInput
-                                :label="t('Request password')"
-                                clearable
-                                v-model="requestPassword"
-                                type="password"
-                            />
-                        </div>
-                    </VnRow>
-
-                    <div class="q-mt-lg row justify-end">
-                        <QBtn
-                            :disabled="isLoading"
-                            :label="t('globals.cancel')"
-                            :loading="isLoading"
-                            class="q-ml-sm"
-                            color="primary"
-                            flat
-                            type="reset"
-                            v-close-popup
-                        />
-                        <QBtn
-                            :disabled="isLoading"
-                            :label="t('Change password')"
-                            :loading="isLoading"
-                            color="primary"
-                            type="submit"
-                        />
-                    </div>
-                </QForm>
-            </QCardSection>
-        </QCard>
-    </QDialog>
-</template>
-
-<i18n>
-es:
-    New password: Nueva contraseña
-    Request password: Repetir contraseña
-    Change password: Cambiar contraseña
-    Passwords don't match: Las contraseñas no coinciden
-</i18n>
diff --git a/src/pages/Worker/Card/WorkerChangePasswordForm.vue b/src/pages/Worker/Card/WorkerChangePasswordForm.vue
deleted file mode 100644
index 20132f21e..000000000
--- a/src/pages/Worker/Card/WorkerChangePasswordForm.vue
+++ /dev/null
@@ -1,99 +0,0 @@
-<script setup>
-import { ref, reactive, onMounted } from 'vue';
-import { useI18n } from 'vue-i18n';
-
-import VnRow from 'components/ui/VnRow.vue';
-import FormPopup from 'src/components/FormPopup.vue';
-import VnInput from 'src/components/common/VnInput.vue';
-
-import axios from 'axios';
-import useNotify from 'src/composables/useNotify.js';
-
-const $props = defineProps({
-    id: {
-        type: Object,
-        default: () => {},
-    },
-});
-
-const emit = defineEmits(['onSubmit']);
-
-const { t } = useI18n();
-const { notify } = useNotify();
-
-const formData = reactive({
-    newPassword: null,
-    repeatPassword: null,
-});
-
-const passRequirements = ref([]);
-
-const setPassword = async () => {
-    try {
-        if (!formData.newPassword) {
-            notify(t('You must enter a new password'), 'negative');
-            return;
-        }
-
-        if (formData.newPassword != formData.repeatPassword) {
-            notify(t(`Passwords don't match`), 'negative');
-            return;
-        }
-
-        await axios.patch(`Workers/${$props.id}/setPassword`, {
-            newPass: formData.newPassword,
-        });
-        notify(t('Password changed!'), 'positive');
-        emit('onSubmit');
-    } catch (err) {
-        console.error('Error setting password', err);
-    }
-};
-
-const getPassRequirements = async () => {
-    const { data } = await axios.get('UserPasswords/findOne');
-    passRequirements.value = data;
-};
-
-onMounted(async () => await getPassRequirements());
-</script>
-
-<template>
-    <FormPopup :title="t('Reset password')" @on-submit="setPassword()">
-        <template #form-inputs>
-            <VnRow>
-                <VnInput
-                    :label="t('New password')"
-                    v-model="formData.newPassword"
-                    type="password"
-                    :required="true"
-                    :info="
-                        t('passwordRequirements', {
-                            length: passRequirements.length,
-                            nAlpha: passRequirements.nAlpha,
-                            nUpper: passRequirements.nUpper,
-                            nDigits: passRequirements.nDigits,
-                            nPunct: passRequirements.nPunct,
-                        })
-                    "
-                />
-            </VnRow>
-            <VnRow>
-                <VnInput
-                    :label="t('Repeat password')"
-                    v-model="formData.repeatPassword"
-                    type="password"
-                />
-            </VnRow>
-        </template>
-    </FormPopup>
-</template>
-
-<i18n>
-es:
-    Reset password: Restablecer contraseña
-    New password: Nueva contraseña
-    Repeat password: Repetir contraseña
-    You must enter a new password: Debes introducir la nueva contraseña
-    Passwords don't match: Las contraseñas no coinciden
-</i18n>

From b015397822a7c0212751672d5fa7d7a5761bb4ce Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Mon, 16 Sep 2024 10:59:32 +0200
Subject: [PATCH 07/12] feat: refs #7702 test wip

---
 .../common/VnChangePassword.spec.js           | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 test/vitest/__tests__/components/common/VnChangePassword.spec.js

diff --git a/test/vitest/__tests__/components/common/VnChangePassword.spec.js b/test/vitest/__tests__/components/common/VnChangePassword.spec.js
new file mode 100644
index 000000000..a481efa81
--- /dev/null
+++ b/test/vitest/__tests__/components/common/VnChangePassword.spec.js
@@ -0,0 +1,47 @@
+import { createWrapper } from 'app/test/vitest/helper';
+import VnChangePassword from 'src/components/common/VnChangePassword.vue';
+import { vi, afterEach, beforeAll, describe, expect, it } from 'vitest';
+
+describe('VnSmsDialog', () => {
+    let vm;
+
+    beforeAll(() => {
+        vm = createWrapper(VnChangePassword, {
+            propsData: {
+                submitFn: vi.fn(),
+            },
+        }).vm;
+    });
+
+    afterEach(() => {
+        vi.clearAllMocks();
+    });
+
+    it('should notify when new password is empty', async () => {
+        vm.passwords.newPassword = '';
+        vm.passwords.repeatPassword = 'password';
+        vi.spyOn(vm, 'notify');
+        await vm.validate();
+        expect(vm.notify).toHaveBeenCalledWith(
+            expect.arguments({
+                message: 'You must enter a new password',
+                type: 'negative',
+            })
+        );
+    });
+
+    it("should notify when passwords don't match", async () => {
+        vm.passwords.newPassword = 'password1';
+        vm.passwords.repeatPassword = 'password2';
+        vi.spyOn(vm, 'notify');
+        await vm.validate();
+        expect(vm.notify).toHaveBeenCalledWith("Passwords don't match", 'negative');
+    });
+
+    it('should call submitFn and emit onSubmit when passwords match', async () => {
+        vm.passwords.newPassword = 'password';
+        vm.passwords.repeatPassword = 'password';
+        await vm.validate();
+        expect(vm.props.submitFn).toHaveBeenCalledWith('password');
+    });
+});

From 6cc8ca6731e0b691d0862dad0953366a8bf32d08 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Fri, 20 Sep 2024 17:38:02 +0200
Subject: [PATCH 08/12] chore: refs #7702 add tests

---
 src/components/common/VnChangePassword.vue    |  6 +++++
 .../common/VnChangePassword.spec.js           | 26 ++++++++++++++-----
 2 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/src/components/common/VnChangePassword.vue b/src/components/common/VnChangePassword.vue
index a36c6bcff..18f538fc3 100644
--- a/src/components/common/VnChangePassword.vue
+++ b/src/components/common/VnChangePassword.vue
@@ -19,6 +19,8 @@ const isLoading = ref(false);
 
 const validate = async () => {
     const { newPassword, repeatPassword } = passwords.value;
+    console.log('Validating passwords:', newPassword, repeatPassword);
+
     if (!newPassword) {
         notify(t('You must enter a new password'), 'negative');
         return;
@@ -29,11 +31,15 @@ const validate = async () => {
     }
     try {
         isLoading.value = true;
+        console.log('Calling submitFn with:', newPassword);
+
         await props.submitFn(newPassword);
         emit('onSubmit');
     } catch (e) {
+        console.error('submitFn failed:', e);
         notify('errors.writeRequest', 'negative');
     } finally {
+        console.log('Entering finally block');
         changePassDialog.value.hide();
         isLoading.value = false;
     }
diff --git a/test/vitest/__tests__/components/common/VnChangePassword.spec.js b/test/vitest/__tests__/components/common/VnChangePassword.spec.js
index a481efa81..e7a3bdcf7 100644
--- a/test/vitest/__tests__/components/common/VnChangePassword.spec.js
+++ b/test/vitest/__tests__/components/common/VnChangePassword.spec.js
@@ -1,11 +1,15 @@
-import { createWrapper } from 'app/test/vitest/helper';
+import { createWrapper, axios } from 'app/test/vitest/helper';
 import VnChangePassword from 'src/components/common/VnChangePassword.vue';
-import { vi, afterEach, beforeAll, describe, expect, it } from 'vitest';
+import { vi, beforeEach, afterEach, beforeAll, describe, expect, it } from 'vitest';
+import { Notify } from 'quasar';
 
 describe('VnSmsDialog', () => {
     let vm;
 
     beforeAll(() => {
+        vi.spyOn(axios, 'get').mockResolvedValue({
+            data: [],
+        });
         vm = createWrapper(VnChangePassword, {
             propsData: {
                 submitFn: vi.fn(),
@@ -13,6 +17,10 @@ describe('VnSmsDialog', () => {
         }).vm;
     });
 
+    beforeEach(() => {
+        Notify.create = vi.fn();
+    });
+
     afterEach(() => {
         vi.clearAllMocks();
     });
@@ -20,10 +28,10 @@ describe('VnSmsDialog', () => {
     it('should notify when new password is empty', async () => {
         vm.passwords.newPassword = '';
         vm.passwords.repeatPassword = 'password';
-        vi.spyOn(vm, 'notify');
+
         await vm.validate();
-        expect(vm.notify).toHaveBeenCalledWith(
-            expect.arguments({
+        expect(Notify.create).toHaveBeenCalledWith(
+            expect.objectContaining({
                 message: 'You must enter a new password',
                 type: 'negative',
             })
@@ -33,9 +41,13 @@ describe('VnSmsDialog', () => {
     it("should notify when passwords don't match", async () => {
         vm.passwords.newPassword = 'password1';
         vm.passwords.repeatPassword = 'password2';
-        vi.spyOn(vm, 'notify');
         await vm.validate();
-        expect(vm.notify).toHaveBeenCalledWith("Passwords don't match", 'negative');
+        expect(Notify.create).toHaveBeenCalledWith(
+            expect.objectContaining({
+                message: `Passwords don't match`,
+                type: 'negative',
+            })
+        );
     });
 
     it('should call submitFn and emit onSubmit when passwords match', async () => {

From 05a1d0e8fbb0a70d8630e72cf6c5952c3ecd3327 Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Thu, 3 Oct 2024 10:52:16 +0200
Subject: [PATCH 09/12] fix: refs #7129 translates from globals to module
 locals

---
 src/i18n/locale/en.yml        | 30 +-----------------------------
 src/i18n/locale/es.yml        | 15 ---------------
 src/pages/Route/locale/en.yml | 14 ++++++++++++++
 src/pages/Route/locale/es.yml | 14 ++++++++++++++
 4 files changed, 29 insertions(+), 44 deletions(-)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index 20a612a82..4c559dfdf 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -870,35 +870,7 @@ wagon:
         minHeightBetweenTrays: 'The minimum height between trays is '
         maxWagonHeight: 'The maximum height of the wagon is '
         uncompleteTrays: There are incomplete trays
-route:
-    pageTitles:
-        agency: Agency List
-        routes: Routes
-        cmrsList: CMRs list
-        RouteList: List
-        routeCreate: New route
-        basicData: Basic Data
-        summary: Summary
-        RouteRoadmap: Roadmaps
-        RouteRoadmapCreate: Create roadmap
-        tickets: Tickets
-        log: Log
-        autonomous: Autonomous
-        RouteExtendedList: Router
-    cmr:
-        list:
-            results: results
-            cmrFk: CMR id
-            hasCmrDms: Attached in gestdoc
-            'true': 'Yes'
-            'false': 'No'
-            ticketFk: Ticketd id
-            routeFk: Route id
-            country: Country
-            clientFk: Client id
-            shipped: Preparation date
-            viewCmr: View CMR
-            downloadCmrs: Download CMRs
+
 supplier:
     list:
         payMethod: Pay method
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index ae0274415..7cb17aad3 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -868,21 +868,6 @@ wagon:
         minHeightBetweenTrays: 'La distancia mínima entre bandejas es '
         maxWagonHeight: 'La altura máxima del vagón es '
         uncompleteTrays: Hay bandejas sin completar
-route:
-    cmr:
-        list:
-            results: resultados
-            cmrFk: Id CMR
-            hasCmrDms: Gestdoc
-            'true': Sí
-            'false': 'No'
-            ticketFk: Id ticket
-            routeFk: Id ruta
-            country: País
-            clientFk: Id cliente
-            shipped: Fecha preparación
-            viewCmr: Ver CMR
-            downloadCmrs: Descargar CMRs
 supplier:
     list:
         payMethod: Método de pago
diff --git a/src/pages/Route/locale/en.yml b/src/pages/Route/locale/en.yml
index 617d704d2..d113fda67 100644
--- a/src/pages/Route/locale/en.yml
+++ b/src/pages/Route/locale/en.yml
@@ -23,3 +23,17 @@ route:
     Summary: Summary
     Route is closed: Route is closed
     Route is not served: Route is not served
+    cmr:
+        list:
+            results: results
+            cmrFk: CMR id
+            hasCmrDms: Attached in gestdoc
+            'true': 'Yes'
+            'false': 'No'
+            ticketFk: Ticketd id
+            routeFk: Route id
+            country: Country
+            clientFk: Client id
+            shipped: Preparation date
+            viewCmr: View CMR
+            downloadCmrs: Download CMRs
diff --git a/src/pages/Route/locale/es.yml b/src/pages/Route/locale/es.yml
index ed96ad915..a6ba4f370 100644
--- a/src/pages/Route/locale/es.yml
+++ b/src/pages/Route/locale/es.yml
@@ -23,3 +23,17 @@ route:
     Summary: Resumen
     Route is closed: La ruta está cerrada
     Route is not served: La ruta no está servida
+    cmr:
+        list:
+            results: resultados
+            cmrFk: Id CMR
+            hasCmrDms: Gestdoc
+            'true': Sí
+            'false': 'No'
+            ticketFk: Id ticket
+            routeFk: Id ruta
+            country: País
+            clientFk: Id cliente
+            shipped: Fecha preparación
+            viewCmr: Ver CMR
+            downloadCmrs: Descargar CMRs

From c04dad38f12a36aa22d85a7c8e459cbbf01bf495 Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Thu, 3 Oct 2024 15:02:14 +0200
Subject: [PATCH 10/12] feat: refs #7404 change travel name and remove buyer
 filter

---
 src/pages/Entry/EntryStockBought.vue | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/pages/Entry/EntryStockBought.vue b/src/pages/Entry/EntryStockBought.vue
index 0b31dde17..2b5ec53f5 100644
--- a/src/pages/Entry/EntryStockBought.vue
+++ b/src/pages/Entry/EntryStockBought.vue
@@ -45,6 +45,7 @@ const columns = [
             optionValue: 'id',
             useLike: false,
         },
+        columnFilter: false,
     },
     {
         align: 'center',
@@ -157,7 +158,7 @@ function round(value) {
                 @on-fetch="
                     (data) => {
                         travel = data.find(
-                            (data) => data.warehouseIn.code.toLowerCase() === 'vnh'
+                            (data) => data.warehouseIn?.code.toLowerCase() === 'vnh'
                         );
                     }
                 "
@@ -165,7 +166,7 @@ function round(value) {
             <VnRow class="travel">
                 <div v-if="travel">
                     <span style="color: var(--vn-label-color)">
-                        {{ t('Booked trucks') }}:
+                        {{ t('Purchase Spaces') }}:
                     </span>
                     <span>
                         {{ travel?.m3 }}
@@ -236,6 +237,7 @@ function round(value) {
                 :footer="true"
                 table-height="80vh"
                 auto-load
+                :column-search="false"
             >
                 <template #column-workerFk="{ row }">
                     <span class="link" @click.stop>
@@ -288,7 +290,7 @@ function round(value) {
     es:
         Edit travel: Editar envío
         Travel: Envíos
-        Booked trucks: Camiones reservados
+        Purchase Spaces: Espacios de compra
         Buyer: Comprador
         Reserve: Reservado
         Bought: Comprado

From 67c2e284ba52d4d5df4338d50b05385bf65916c7 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Fri, 4 Oct 2024 11:45:10 +0200
Subject: [PATCH 11/12] feat: refs #7702 ask old pass

---
 src/components/common/VnChangePassword.vue    | 26 ++++++++++-------
 src/i18n/locale/en.yml                        |  1 +
 src/i18n/locale/es.yml                        |  1 +
 .../Account/Card/AccountDescriptorMenu.vue    | 28 ++++++++++++++++++-
 src/pages/Customer/Card/CustomerWebAccess.vue |  3 +-
 src/pages/Worker/Card/WorkerDescriptor.vue    |  3 +-
 6 files changed, 47 insertions(+), 15 deletions(-)

diff --git a/src/components/common/VnChangePassword.vue b/src/components/common/VnChangePassword.vue
index 18f538fc3..79784f3c5 100644
--- a/src/components/common/VnChangePassword.vue
+++ b/src/components/common/VnChangePassword.vue
@@ -6,7 +6,10 @@ import VnInput from './VnInput.vue';
 import FetchData from '../FetchData.vue';
 import useNotify from 'src/composables/useNotify';
 
-const props = defineProps({ submitFn: { type: Function, default: () => {} } });
+const props = defineProps({
+    submitFn: { type: Function, default: () => {} },
+    askOldPass: { type: Boolean, default: false },
+});
 const emit = defineEmits(['onSubmit']);
 const { t } = useI18n();
 const { notify } = useNotify();
@@ -18,8 +21,7 @@ const requirements = ref([]);
 const isLoading = ref(false);
 
 const validate = async () => {
-    const { newPassword, repeatPassword } = passwords.value;
-    console.log('Validating passwords:', newPassword, repeatPassword);
+    const { newPassword, repeatPassword, oldPassword } = passwords.value;
 
     if (!newPassword) {
         notify(t('You must enter a new password'), 'negative');
@@ -29,17 +31,14 @@ const validate = async () => {
         notify(t("Passwords don't match"), 'negative');
         return;
     }
+
     try {
         isLoading.value = true;
-        console.log('Calling submitFn with:', newPassword);
-
-        await props.submitFn(newPassword);
+        await props.submitFn(newPassword, oldPassword);
         emit('onSubmit');
     } catch (e) {
-        console.error('submitFn failed:', e);
         notify('errors.writeRequest', 'negative');
     } finally {
-        console.log('Entering finally block');
         changePassDialog.value.hide();
         isLoading.value = false;
     }
@@ -58,7 +57,7 @@ defineExpose({ show: () => changePassDialog.value.show() });
             <QCardSection>
                 <slot name="header">
                     <VnRow class="items-center" style="flex-direction: row">
-                        <span class="text-h6" v-text="t('Change password')" />
+                        <span class="text-h6" v-text="t('globals.changePass')" />
                         <QIcon
                             class="cursor-pointer"
                             name="close"
@@ -71,6 +70,14 @@ defineExpose({ show: () => changePassDialog.value.show() });
             </QCardSection>
             <QForm ref="form">
                 <QCardSection>
+                    <VnInput
+                        v-if="props.askOldPass"
+                        :label="t('Old password')"
+                        v-model="passwords.oldPassword"
+                        type="password"
+                        :required="true"
+                        autofocus
+                    />
                     <VnInput
                         :label="t('New password')"
                         v-model="passwords.newPassword"
@@ -122,7 +129,6 @@ defineExpose({ show: () => changePassDialog.value.show() });
 
 <i18n>
 es:
-    Change password: Cambiar contraseña
     New password: Nueva contraseña
     Repeat password: Repetir contraseña
     You must enter a new password: Debes introducir la nueva contraseña
diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index 4c559dfdf..b76c147cd 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -300,6 +300,7 @@ globals:
         from: From
         To: To
         stateFk: State
+    changePass: Change password
 errors:
     statusUnauthorized: Access denied
     statusInternalServerError: An internal server error has ocurred
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index 7cb17aad3..d0d5bdc0d 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -304,6 +304,7 @@ globals:
         from: Desde
         To: Hasta
         stateFk: Estado
+    changePass: Cambiar contraseña
 errors:
     statusUnauthorized: Acceso denegado
     statusInternalServerError: Ha ocurrido un error interno del servidor
diff --git a/src/pages/Account/Card/AccountDescriptorMenu.vue b/src/pages/Account/Card/AccountDescriptorMenu.vue
index 0e35d25f3..6f1d2ca1f 100644
--- a/src/pages/Account/Card/AccountDescriptorMenu.vue
+++ b/src/pages/Account/Card/AccountDescriptorMenu.vue
@@ -4,9 +4,12 @@ import { computed, ref, toRefs } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useVnConfirm } from 'composables/useVnConfirm';
 import { useRoute } from 'vue-router';
+import { useAcl } from 'src/composables/useAcl';
 import { useArrayData } from 'src/composables/useArrayData';
 import VnConfirm from 'src/components/ui/VnConfirm.vue';
+import VnChangePassword from 'src/components/common/VnChangePassword.vue';
 import useNotify from 'src/composables/useNotify.js';
+
 const $props = defineProps({
     hasAccount: {
         type: Boolean,
@@ -62,6 +65,19 @@ async function sync() {
 }
 </script>
 <template>
+    <VnChangePassword
+        ref="changePassRef"
+        :ask-old-pass="true"
+        :submit-fn="
+            async (newPassword, oldPassword) => {
+                await axios.patch(`Accounts/change-password`, {
+                    userId: entityId,
+                    newPassword,
+                    oldPassword,
+                });
+            }
+        "
+    />
     <VnConfirm
         v-model="showSyncDialog"
         :message="t('account.card.actions.sync.message')"
@@ -92,6 +108,17 @@ async function sync() {
             />
         </template>
     </VnConfirm>
+    <QItem
+        v-if="
+            entityId == account.id &&
+            useAcl().hasAny([{ model: 'Account', props: '*', accessType: 'WRITE' }])
+        "
+        v-ripple
+        clickable
+        @click="$refs.changePassRef.show()"
+    >
+        <QItemSection>{{ t('globals.changePass') }}</QItemSection>
+    </QItem>
     <QItem
         v-if="account.hasAccount"
         v-ripple
@@ -138,6 +165,5 @@ async function sync() {
     <QItem v-ripple clickable @click="showSyncDialog = true">
         <QItemSection>{{ t('account.card.actions.sync.name') }}</QItemSection>
     </QItem>
-
     <QSeparator />
 </template>
diff --git a/src/pages/Customer/Card/CustomerWebAccess.vue b/src/pages/Customer/Card/CustomerWebAccess.vue
index ba906a144..1db32c752 100644
--- a/src/pages/Customer/Card/CustomerWebAccess.vue
+++ b/src/pages/Customer/Card/CustomerWebAccess.vue
@@ -56,7 +56,7 @@ async function hasCustomerRole() {
         </template>
         <template #moreActions>
             <QBtn
-                :label="t('Change password')"
+                :label="t('globals.changePass')"
                 color="primary"
                 icon="edit"
                 :disable="!canChangePassword"
@@ -81,5 +81,4 @@ es:
     User: Usuario
     Recovery email: Correo de recuperacion
     This email is used for user to regain access their account: Este correo electrónico se usa para que el usuario recupere el acceso a su cuenta
-    Change password: Cambiar contraseña
 </i18n>
diff --git a/src/pages/Worker/Card/WorkerDescriptor.vue b/src/pages/Worker/Card/WorkerDescriptor.vue
index 6a70e46f5..1cb42bbfb 100644
--- a/src/pages/Worker/Card/WorkerDescriptor.vue
+++ b/src/pages/Worker/Card/WorkerDescriptor.vue
@@ -88,7 +88,7 @@ const handlePhotoUpdated = (evt = false) => {
                 @click="$refs.changePassRef.show"
             >
                 <QItemSection>
-                    {{ t('Change password') }}
+                    {{ t('globals.changePass') }}
                 </QItemSection>
             </QItem>
         </template>
@@ -209,5 +209,4 @@ const handlePhotoUpdated = (evt = false) => {
 es:
     Click to allow the user to be disabled: Marcar para deshabilitar
     Click to exclude the user from getting disabled: Marcar para no deshabilitar
-    Change password: Cambiar contraseña
 </i18n>

From ae8e4ba4b975b1c6381fabec08de18cbc190f978 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Fri, 4 Oct 2024 11:54:40 +0200
Subject: [PATCH 12/12] chore: refs #7702 fix tests

---
 .../common/VnChangePassword.spec.js           | 21 ++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/test/vitest/__tests__/components/common/VnChangePassword.spec.js b/test/vitest/__tests__/components/common/VnChangePassword.spec.js
index e7a3bdcf7..f5a967bb5 100644
--- a/test/vitest/__tests__/components/common/VnChangePassword.spec.js
+++ b/test/vitest/__tests__/components/common/VnChangePassword.spec.js
@@ -50,10 +50,21 @@ describe('VnSmsDialog', () => {
         );
     });
 
-    it('should call submitFn and emit onSubmit when passwords match', async () => {
-        vm.passwords.newPassword = 'password';
-        vm.passwords.repeatPassword = 'password';
-        await vm.validate();
-        expect(vm.props.submitFn).toHaveBeenCalledWith('password');
+    describe('if passwords match', () => {
+        it('should call submitFn and emit password', async () => {
+            vm.passwords.newPassword = 'password';
+            vm.passwords.repeatPassword = 'password';
+            await vm.validate();
+            expect(vm.props.submitFn).toHaveBeenCalledWith('password', undefined);
+        });
+
+        it('should call submitFn and emit password and old password', async () => {
+            vm.passwords.newPassword = 'password';
+            vm.passwords.repeatPassword = 'password';
+            vm.passwords.oldPassword = 'oldPassword';
+
+            await vm.validate();
+            expect(vm.props.submitFn).toHaveBeenCalledWith('password', 'oldPassword');
+        });
     });
 });