Compare commits
9 Commits
dev
...
8945-migra
Author | SHA1 | Date |
---|---|---|
|
b25d421131 | |
|
500f38cc21 | |
|
a2e8f332df | |
|
574bf7f771 | |
|
3cdfb42386 | |
|
d9f7047239 | |
|
f511832fe5 | |
|
ca0d90c07e | |
|
47bde21df5 |
|
@ -64,6 +64,5 @@ export default defineConfig({
|
|||
...timeouts,
|
||||
includeShadowDom: true,
|
||||
waitForAnimations: true,
|
||||
testIsolation: false,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@ import { QLayout } from 'quasar';
|
|||
import mainShortcutMixin from './mainShortcutMixin';
|
||||
import { useCau } from 'src/composables/useCau';
|
||||
|
||||
export default boot(({ app, router }) => {
|
||||
export default boot(({ app }) => {
|
||||
QForm.mixins = [qFormMixin];
|
||||
QLayout.mixins = [mainShortcutMixin];
|
||||
|
||||
|
@ -22,14 +22,6 @@ export default boot(({ app, router }) => {
|
|||
}
|
||||
|
||||
switch (response?.status) {
|
||||
case 401:
|
||||
if (!router.currentRoute.value.name.toLowerCase().includes('login')) {
|
||||
message = 'errors.sessionExpired';
|
||||
} else message = 'login.loginError';
|
||||
break;
|
||||
case 403:
|
||||
if (!message || message.toLowerCase() === 'access denied')
|
||||
message = 'errors.accessDenied';
|
||||
case 422:
|
||||
if (error.name == 'ValidationError')
|
||||
message += ` "${responseError.details.context}.${Object.keys(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { computed, ref, useAttrs, watch, nextTick } from 'vue';
|
||||
import { computed, ref, useAttrs, watch } from 'vue';
|
||||
import { useRouter, onBeforeRouteLeave } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
@ -42,15 +42,7 @@ const $props = defineProps({
|
|||
},
|
||||
dataRequired: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
dataDefault: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
insertOnLoad: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
default: () => {},
|
||||
},
|
||||
defaultSave: {
|
||||
type: Boolean,
|
||||
|
@ -95,7 +87,6 @@ const formData = ref();
|
|||
const saveButtonRef = ref(null);
|
||||
const watchChanges = ref();
|
||||
const formUrl = computed(() => $props.url);
|
||||
const rowsContainer = ref(null);
|
||||
|
||||
const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
|
||||
|
||||
|
@ -131,11 +122,9 @@ async function fetch(data) {
|
|||
const rows = keyData ? data[keyData] : data;
|
||||
resetData(rows);
|
||||
emit('onFetch', rows);
|
||||
$props.insertOnLoad && await insert();
|
||||
return rows;
|
||||
}
|
||||
|
||||
|
||||
function resetData(data) {
|
||||
if (!data) return;
|
||||
if (data && Array.isArray(data)) {
|
||||
|
@ -146,16 +135,9 @@ function resetData(data) {
|
|||
formData.value = JSON.parse(JSON.stringify(data));
|
||||
|
||||
if (watchChanges.value) watchChanges.value(); //destroy watcher
|
||||
watchChanges.value = watch(formData, (nVal) => {
|
||||
hasChanges.value = false;
|
||||
const filteredNewData = nVal.filter(row => !isRowEmpty(row) || row[$props.primaryKey]);
|
||||
const filteredOriginal = originalData.value.filter(row => row[$props.primaryKey]);
|
||||
|
||||
const changes = getDifferences(filteredOriginal, filteredNewData);
|
||||
hasChanges.value = !isEmpty(changes);
|
||||
|
||||
}, { deep: true });
|
||||
watchChanges.value = watch(formData, () => (hasChanges.value = true), { deep: true });
|
||||
}
|
||||
|
||||
async function reset() {
|
||||
await fetch(originalData.value);
|
||||
hasChanges.value = false;
|
||||
|
@ -183,9 +165,7 @@ async function onSubmit() {
|
|||
});
|
||||
}
|
||||
isLoading.value = true;
|
||||
|
||||
await saveChanges($props.saveFn ? formData.value : null);
|
||||
|
||||
}
|
||||
|
||||
async function onSubmitAndGo() {
|
||||
|
@ -194,10 +174,6 @@ async function onSubmitAndGo() {
|
|||
}
|
||||
|
||||
async function saveChanges(data) {
|
||||
formData.value = formData.value.filter(row =>
|
||||
row[$props.primaryKey] || !isRowEmpty(row)
|
||||
);
|
||||
|
||||
if ($props.saveFn) {
|
||||
$props.saveFn(data, getChanges);
|
||||
isLoading.value = false;
|
||||
|
@ -227,32 +203,14 @@ async function saveChanges(data) {
|
|||
});
|
||||
}
|
||||
|
||||
async function insert(pushData = { ...$props.dataRequired, ...$props.dataDefault }) {
|
||||
formData.value = formData.value.filter(row => !isRowEmpty(row));
|
||||
|
||||
const lastRow = formData.value.at(-1);
|
||||
const isLastRowEmpty = lastRow ? isRowEmpty(lastRow) : false;
|
||||
|
||||
if (formData.value.length && isLastRowEmpty) return;
|
||||
const $index = formData.value.length ? formData.value.at(-1).$index + 1 : 0;
|
||||
|
||||
const nRow = Object.assign({ $index }, pushData);
|
||||
formData.value.push(nRow);
|
||||
|
||||
const hasChange = Object.keys(nRow).some(key => !isChange(nRow, key));
|
||||
if (hasChange) hasChanges.value = true;
|
||||
async function insert(pushData = $props.dataRequired) {
|
||||
const $index = formData.value.length
|
||||
? formData.value[formData.value.length - 1].$index + 1
|
||||
: 0;
|
||||
formData.value.push(Object.assign({ $index }, pushData));
|
||||
hasChanges.value = true;
|
||||
}
|
||||
|
||||
function isRowEmpty(row) {
|
||||
return Object.keys(row).every(key => isChange(row, key));
|
||||
}
|
||||
|
||||
|
||||
function isChange(row,key){
|
||||
return !row[key] || key == '$index' || Object.hasOwn($props.dataRequired || {}, key);
|
||||
}
|
||||
|
||||
|
||||
async function remove(data) {
|
||||
if (!data.length)
|
||||
return quasar.notify({
|
||||
|
@ -269,8 +227,10 @@ async function remove(data) {
|
|||
newData = newData.filter(
|
||||
(form) => !preRemove.some((index) => index == form.$index),
|
||||
);
|
||||
formData.value = newData;
|
||||
hasChanges.value = JSON.stringify(removeIndexField(formData.value)) !== JSON.stringify(removeIndexField(originalData.value));
|
||||
const changes = getChanges();
|
||||
if (!changes.creates?.length && !changes.updates?.length)
|
||||
hasChanges.value = false;
|
||||
fetch(newData);
|
||||
}
|
||||
if (ids.length) {
|
||||
quasar
|
||||
|
@ -288,8 +248,9 @@ async function remove(data) {
|
|||
newData = newData.filter((form) => !ids.some((id) => id == form[pk]));
|
||||
fetch(newData);
|
||||
});
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
|
||||
emit('update:selected', []);
|
||||
}
|
||||
|
||||
|
@ -300,7 +261,7 @@ function getChanges() {
|
|||
const pk = $props.primaryKey;
|
||||
for (const [i, row] of formData.value.entries()) {
|
||||
if (!row[pk]) {
|
||||
creates.push(Object.assign(row, { ...$props.dataRequired }));
|
||||
creates.push(row);
|
||||
} else if (originalData.value[i]) {
|
||||
const data = getDifferences(originalData.value[i], row);
|
||||
if (!isEmpty(data)) {
|
||||
|
@ -326,33 +287,6 @@ function isEmpty(obj) {
|
|||
return !Object.keys(obj).length;
|
||||
}
|
||||
|
||||
function removeIndexField(data) {
|
||||
if (Array.isArray(data)) {
|
||||
return data.map(({ $index, ...rest }) => rest);
|
||||
} else if (typeof data === 'object' && data !== null) {
|
||||
const { $index, ...rest } = data;
|
||||
return rest;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleTab(event) {
|
||||
event.preventDefault();
|
||||
const { shiftKey, target } = event;
|
||||
const focusableSelector = `tbody tr td:not(:first-child) :is(a, button, input, textarea, select, details):not([disabled])`;
|
||||
const focusableElements = rowsContainer.value?.querySelectorAll(focusableSelector);
|
||||
const currentIndex = Array.prototype.indexOf.call(focusableElements, target);
|
||||
const index = shiftKey ? currentIndex - 1 : currentIndex + 1;
|
||||
const isLast = target === focusableElements[focusableElements.length - 1];
|
||||
const isFirst = currentIndex === 0;
|
||||
|
||||
if ((shiftKey && !isFirst) || (!shiftKey && !isLast))
|
||||
focusableElements[index]?.focus();
|
||||
else if (isLast) {
|
||||
await insert();
|
||||
await nextTick();
|
||||
}
|
||||
}
|
||||
|
||||
async function reload(params) {
|
||||
const data = await vnPaginateRef.value.fetch(params);
|
||||
fetch(data);
|
||||
|
@ -378,14 +312,12 @@ watch(formUrl, async () => {
|
|||
v-bind="$attrs"
|
||||
>
|
||||
<template #body v-if="formData">
|
||||
<div ref="rowsContainer" @keydown.tab="handleTab">
|
||||
<slot
|
||||
name="body"
|
||||
:rows="formData"
|
||||
:validate="validate"
|
||||
:filter="filter"
|
||||
></slot>
|
||||
</div>
|
||||
</template>
|
||||
</VnPaginate>
|
||||
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown() && hasSubToolbar">
|
||||
|
|
|
@ -181,10 +181,6 @@ const col = computed(() => {
|
|||
newColumn.component = 'checkbox';
|
||||
if ($props.default && !newColumn.component) newColumn.component = $props.default;
|
||||
|
||||
if (typeof newColumn.component !== 'string') {
|
||||
newColumn.attrs = { ...newColumn.component.attrs, autofocus: $props.autofocus };
|
||||
newColumn.event = { ...newColumn.component.event, ...$props?.eventHandlers };
|
||||
}
|
||||
return newColumn;
|
||||
});
|
||||
|
||||
|
|
|
@ -684,10 +684,8 @@ const handleHeaderSelection = (evt, data) => {
|
|||
ref="CrudModelRef"
|
||||
@on-fetch="
|
||||
(...args) => {
|
||||
if ($props.multiCheck.expand) {
|
||||
selectAll = false;
|
||||
selected = [];
|
||||
}
|
||||
emit('onFetch', ...args);
|
||||
}
|
||||
"
|
||||
|
|
|
@ -193,11 +193,11 @@ describe('CrudModel', () => {
|
|||
});
|
||||
|
||||
it('should set originalData and formatData with data and generate watchChanges', async () => {
|
||||
data = [{
|
||||
data = {
|
||||
name: 'Tony',
|
||||
lastName: 'Stark',
|
||||
age: 42,
|
||||
}];
|
||||
};
|
||||
|
||||
vm.resetData(data);
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ const getBankEntities = (data) => {
|
|||
:acls="[{ model: 'BankEntity', props: '*', accessType: 'WRITE' }]"
|
||||
:options="bankEntities"
|
||||
hide-selected
|
||||
option-label="bic"
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
v-model="bankEntityFk"
|
||||
@update:model-value="$emit('updateBic', { iban, bankEntityFk })"
|
||||
|
|
|
@ -30,7 +30,7 @@ const onClick = async () => {
|
|||
params: { filter: JSON.stringify(filter) },
|
||||
};
|
||||
try {
|
||||
const { data } = await axios.get(props.url, params);
|
||||
const { data } = axios.get(props.url, params);
|
||||
rows.value = data;
|
||||
} catch (error) {
|
||||
const response = error.response;
|
||||
|
@ -83,7 +83,7 @@ defineEmits(['update:selected', 'select:all']);
|
|||
/>
|
||||
<span
|
||||
v-else
|
||||
v-text="t('records', { rows: rows?.length ?? 0 })"
|
||||
v-text="t('records', { rows: rows.length ?? 0 })"
|
||||
/>
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
|
|
|
@ -264,7 +264,7 @@ function deleteDms(dmsFk) {
|
|||
rows.value.splice(index, 1);
|
||||
notify(t('globals.dataDeleted'), 'positive');
|
||||
} catch (e) {
|
||||
throw e;
|
||||
notify(t('errorDmsDelete'), 'negative');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -435,9 +435,11 @@ defineExpose({
|
|||
</style>
|
||||
<i18n>
|
||||
en:
|
||||
errorDmsDelete: Error deleting the dms
|
||||
contentTypesInfo: Allowed file types {allowedContentTypes}
|
||||
The documentation is available in paper form: The documentation is available in paper form
|
||||
es:
|
||||
errorDmsSave: Error al eliminar el dms
|
||||
contentTypesInfo: Tipos de archivo permitidos {allowedContentTypes}
|
||||
Generate identifier for original file: Generar identificador para archivo original
|
||||
Upload file: Subir fichero
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
<script setup>
|
||||
import { ref, useTemplateRef } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useAccountShortToStandard } from 'src/composables/useAccountShortToStandard';
|
||||
import VnSelectDialog from './VnSelectDialog.vue';
|
||||
import CreateNewExpenseForm from '../CreateNewExpenseForm.vue';
|
||||
import FetchData from '../FetchData.vue';
|
||||
|
||||
const model = defineModel({ type: [String, Number, Object] });
|
||||
const { t } = useI18n();
|
||||
const expenses = ref([]);
|
||||
const selectDialogRef = useTemplateRef('selectDialogRef');
|
||||
|
||||
async function autocompleteExpense(evt) {
|
||||
const val = evt.target.value;
|
||||
if (!val || isNaN(val)) return;
|
||||
const lookup = expenses.value.find(({ id }) => id == useAccountShortToStandard(val));
|
||||
if (selectDialogRef.value)
|
||||
selectDialogRef.value.vnSelectDialogRef.vnSelectRef.toggleOption(lookup);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<VnSelectDialog
|
||||
v-bind="$attrs"
|
||||
ref="selectDialogRef"
|
||||
v-model="model"
|
||||
:options="expenses"
|
||||
option-value="id"
|
||||
:option-label="(x) => `${x.id}: ${x.name}`"
|
||||
:filter-options="['id', 'name']"
|
||||
:tooltip="t('Create a new expense')"
|
||||
:acls="[{ model: 'Expense', props: '*', accessType: 'WRITE' }]"
|
||||
@keydown.tab.prevent="autocompleteExpense"
|
||||
>
|
||||
<template #form>
|
||||
<CreateNewExpenseForm @on-data-saved="$refs.expensesRef.fetch()" />
|
||||
</template>
|
||||
</VnSelectDialog>
|
||||
<FetchData
|
||||
ref="expensesRef"
|
||||
url="Expenses"
|
||||
auto-load
|
||||
@on-fetch="(data) => (expenses = data)"
|
||||
/>
|
||||
</template>
|
||||
<i18n>
|
||||
es:
|
||||
Create a new expense: Crear nuevo gasto
|
||||
</i18n>
|
|
@ -40,11 +40,4 @@ describe('VnBankDetail Component', () => {
|
|||
await vm.autofillBic('ES1234567891324567891234');
|
||||
expect(vm.bankEntityFk).toBe(null);
|
||||
});
|
||||
|
||||
it('should not update bankEntityFk if IBAN country is not ES', async () => {
|
||||
vm.bankEntities = bankEntities;
|
||||
|
||||
await vm.autofillBic('FR1420041010050500013M02606');
|
||||
expect(vm.bankEntityFk).toBe(null);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -35,7 +35,6 @@ const $props = defineProps({
|
|||
selectType: { type: Boolean, default: false },
|
||||
justInput: { type: Boolean, default: false },
|
||||
goTo: { type: String, default: '' },
|
||||
useUserRelation: { type: Boolean, default: true },
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
@ -55,26 +54,6 @@ const defaultObservationType = computed(
|
|||
let savedNote = false;
|
||||
let originalText;
|
||||
|
||||
onBeforeRouteLeave((to, from, next) => {
|
||||
if (
|
||||
(newNote.text && !$props.justInput) ||
|
||||
(newNote.text !== originalText && $props.justInput)
|
||||
)
|
||||
quasar.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('globals.unsavedPopup.title'),
|
||||
message: t('globals.unsavedPopup.subtitle'),
|
||||
promise: () => next(),
|
||||
},
|
||||
});
|
||||
else next();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => (componentIsRendered.value = true));
|
||||
});
|
||||
|
||||
function handleClick(e) {
|
||||
if (e.shiftKey && e.key === 'Enter') return;
|
||||
if ($props.justInput) confirmAndUpdate();
|
||||
|
@ -129,6 +108,22 @@ async function update() {
|
|||
);
|
||||
}
|
||||
|
||||
onBeforeRouteLeave((to, from, next) => {
|
||||
if (
|
||||
(newNote.text && !$props.justInput) ||
|
||||
(newNote.text !== originalText && $props.justInput)
|
||||
)
|
||||
quasar.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('globals.unsavedPopup.title'),
|
||||
message: t('globals.unsavedPopup.subtitle'),
|
||||
promise: () => next(),
|
||||
},
|
||||
});
|
||||
else next();
|
||||
});
|
||||
|
||||
function fetchData([data]) {
|
||||
newNote.text = data?.notes;
|
||||
originalText = data?.notes;
|
||||
|
@ -142,38 +137,16 @@ const handleObservationTypes = (data) => {
|
|||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => (componentIsRendered.value = true));
|
||||
});
|
||||
|
||||
async function saveAndGo() {
|
||||
savedNote = false;
|
||||
await insert();
|
||||
await savedNote;
|
||||
router.push({ path: $props.goTo });
|
||||
}
|
||||
|
||||
function getUserFilter() {
|
||||
const newUserFilter = $props.userFilter ?? {};
|
||||
const userInclude = {
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['id', 'nickname', 'name'],
|
||||
},
|
||||
};
|
||||
if (newUserFilter.include) {
|
||||
if (Array.isArray(newUserFilter.include)) {
|
||||
newUserFilter.include.push(userInclude);
|
||||
} else {
|
||||
newUserFilter.include = [userInclude, newUserFilter.include];
|
||||
}
|
||||
} else {
|
||||
newUserFilter.include = userInclude;
|
||||
}
|
||||
|
||||
if ($props.useUserRelation) {
|
||||
return {
|
||||
...newUserFilter,
|
||||
...$props.userFilter,
|
||||
};
|
||||
}
|
||||
return $props.filter;
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<Teleport
|
||||
|
@ -269,7 +242,7 @@ function getUserFilter() {
|
|||
:url="$props.url"
|
||||
order="created DESC"
|
||||
:limit="0"
|
||||
:user-filter="getUserFilter()"
|
||||
:user-filter="userFilter"
|
||||
:filter="filter"
|
||||
auto-load
|
||||
ref="vnPaginateRef"
|
||||
|
@ -288,15 +261,15 @@ function getUserFilter() {
|
|||
<QCardSection horizontal>
|
||||
<VnAvatar
|
||||
:descriptor="false"
|
||||
:worker-id="note.user?.id"
|
||||
:worker-id="note.workerFk"
|
||||
size="md"
|
||||
:title="note.user?.nickname"
|
||||
:title="note.worker?.user.nickname"
|
||||
/>
|
||||
<div class="full-width row justify-between q-pa-xs">
|
||||
<div>
|
||||
<VnUserLink
|
||||
:name="`${note.user?.name}`"
|
||||
:worker-id="note.user?.id"
|
||||
:name="`${note.worker.user.name}`"
|
||||
:worker-id="note.worker.id"
|
||||
/>
|
||||
<QBadge
|
||||
class="q-ml-xs"
|
||||
|
|
|
@ -169,6 +169,7 @@ globals:
|
|||
selectDocumentId: Select document id
|
||||
document: Document
|
||||
import: Import from existing
|
||||
group: Group
|
||||
pageTitles:
|
||||
logIn: Login
|
||||
addressEdit: Update address
|
||||
|
@ -351,7 +352,9 @@ globals:
|
|||
vehicleList: Vehicles
|
||||
vehicle: Vehicle
|
||||
entryPreAccount: Pre-account
|
||||
fixedAsset: Fixed assets
|
||||
management: Worker management
|
||||
assignedInvoices: Assigned Invoices
|
||||
unsavedPopup:
|
||||
title: Unsaved changes will be lost
|
||||
subtitle: Are you sure exit without saving?
|
||||
|
@ -400,8 +403,6 @@ errors:
|
|||
updateUserConfig: Error updating user config
|
||||
tokenConfig: Error fetching token config
|
||||
writeRequest: The requested operation could not be completed
|
||||
sessionExpired: Your session has expired. Please log in again
|
||||
accessDenied: Access denied
|
||||
claimBeginningQuantity: Cannot import a line with a claimed quantity of 0
|
||||
login:
|
||||
title: Login
|
||||
|
|
|
@ -173,6 +173,7 @@ globals:
|
|||
selectDocumentId: Seleccione el id de gestión documental
|
||||
document: Documento
|
||||
import: Importar desde existente
|
||||
group: Grupo
|
||||
pageTitles:
|
||||
logIn: Inicio de sesión
|
||||
addressEdit: Modificar consignatario
|
||||
|
@ -354,7 +355,9 @@ globals:
|
|||
vehicleList: Vehículos
|
||||
vehicle: Vehículo
|
||||
entryPreAccount: Precontabilizar
|
||||
fixedAsset: Inmovilizados
|
||||
management: Gestión de trabajadores
|
||||
assignedInvoices: Facturas vinculadas
|
||||
unsavedPopup:
|
||||
title: Los cambios que no haya guardado se perderán
|
||||
subtitle: ¿Seguro que quiere salir sin guardar?
|
||||
|
@ -396,8 +399,6 @@ errors:
|
|||
updateUserConfig: Error al actualizar la configuración de usuario
|
||||
tokenConfig: Error al obtener configuración de token
|
||||
writeRequest: No se pudo completar la operación solicitada
|
||||
sessionExpired: Tu sesión ha expirado, por favor vuelve a iniciar sesión
|
||||
accessDenied: Acceso denegado
|
||||
claimBeginningQuantity: No se puede importar una linea sin una cantidad reclamada
|
||||
login:
|
||||
title: Inicio de sesión
|
||||
|
|
|
@ -138,7 +138,6 @@ const columns = computed(() => [
|
|||
:filter="developmentsFilter"
|
||||
ref="claimDevelopmentForm"
|
||||
:data-required="{ claimFk: route.params.id }"
|
||||
:insert-on-load="true"
|
||||
v-model:selected="selected"
|
||||
@save-changes="$router.push(`/claim/${route.params.id}/action`)"
|
||||
:default-save="false"
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useState } from 'src/composables/useState';
|
||||
import VnNotes from 'src/components/ui/VnNotes.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const state = useState();
|
||||
const user = state.getUser();
|
||||
|
||||
const $props = defineProps({
|
||||
id: { type: [Number, String], default: null },
|
||||
|
@ -12,13 +15,24 @@ const $props = defineProps({
|
|||
const claimId = computed(() => $props.id || route.params.id);
|
||||
|
||||
const claimFilter = {
|
||||
fields: ['id', 'created', 'userFk', 'text'],
|
||||
fields: ['id', 'created', 'workerFk', 'text'],
|
||||
include: {
|
||||
relation: 'worker',
|
||||
scope: {
|
||||
fields: ['id', 'firstName', 'lastName'],
|
||||
include: {
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['id', 'nickname', 'name'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const body = {
|
||||
claimFk: claimId.value,
|
||||
workerFk: user.value.id,
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -29,9 +43,7 @@ const claimFilter = {
|
|||
:add-note="$props.addNote"
|
||||
:user-filter="claimFilter"
|
||||
:filter="{ where: { claimFk: claimId } }"
|
||||
:body="{
|
||||
claimFk: claimId,
|
||||
}"
|
||||
:body="body"
|
||||
v-bind="$attrs"
|
||||
style="overflow-y: auto"
|
||||
/>
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
<script setup>
|
||||
import VnNotes from 'src/components/ui/VnNotes.vue';
|
||||
import { useState } from 'src/composables/useState';
|
||||
const state = useState();
|
||||
const user = state.getUser();
|
||||
</script>
|
||||
<template>
|
||||
<VnNotes
|
||||
url="clientObservations"
|
||||
:add-note="true"
|
||||
:filter="{ where: { clientFk: $route.params.id } }"
|
||||
:body="{ clientFk: $route.params.id, userFk: user.id }"
|
||||
:body="{ clientFk: $route.params.id }"
|
||||
style="overflow-y: auto"
|
||||
:select-type="true"
|
||||
required
|
||||
|
|
|
@ -76,7 +76,7 @@ const columns = computed(() => [
|
|||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'userFk',
|
||||
name: 'workerFk',
|
||||
label: t('Author'),
|
||||
tooltip: t('Worker who made the last observation'),
|
||||
columnFilter: {
|
||||
|
@ -155,7 +155,7 @@ function exprBuilder(param, value) {
|
|||
return { [`c.${param}`]: value };
|
||||
case 'payMethod':
|
||||
return { [`c.payMethodFk`]: value };
|
||||
case 'userFk':
|
||||
case 'workerFk':
|
||||
return { [`co.${param}`]: value };
|
||||
case 'departmentFk':
|
||||
return { [`c.${param}`]: value };
|
||||
|
@ -229,10 +229,10 @@ function exprBuilder(param, value) {
|
|||
<DepartmentDescriptorProxy :id="row.departmentFk" />
|
||||
</span>
|
||||
</template>
|
||||
<template #column-userFk="{ row }">
|
||||
<template #column-workerFk="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.workerName }}
|
||||
<WorkerDescriptorProxy :id="row.userFk" />
|
||||
<WorkerDescriptorProxy :id="row.workerFk" />
|
||||
</span>
|
||||
</template>
|
||||
</VnTable>
|
||||
|
|
|
@ -73,7 +73,6 @@ const columns = computed(() => [
|
|||
optionLabel: 'code',
|
||||
options: companies.value,
|
||||
},
|
||||
orderBy: false,
|
||||
},
|
||||
{
|
||||
name: 'warehouse',
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
<script setup>
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ref } from 'vue';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnSelect from 'components/common/VnSelect.vue';
|
||||
import VnInput from 'components/common/VnInput.vue';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||
import VnAccountNumber from 'src/components/common/VnAccountNumber.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
import VnCheckbox from 'src/components/common/VnCheckbox.vue';
|
||||
import { toCurrency } from 'src/filters';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const isNew = Boolean(!route.params.id);
|
||||
const natureOptions = ref([]);
|
||||
const prueba = ref();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
url="Ppes/getNatures"
|
||||
@on-fetch="(data) => (natureOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<VnSubToolbar v-if="isNew" />
|
||||
<div class="q-pa-md">
|
||||
<FormModel :url-update="`Ppes/${route.params.id}`" model="FixedAsset" auto-load>
|
||||
<template #form="{ data }">
|
||||
<VnRow>
|
||||
<VnInput
|
||||
:label="t('globals.description')"
|
||||
v-model="data.description"
|
||||
fill-input
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
url="companies"
|
||||
:label="t('globals.company')"
|
||||
v-model="data.companyFk"
|
||||
option-value="id"
|
||||
option-label="code"
|
||||
hide-selected
|
||||
/>
|
||||
<VnSelect
|
||||
:label="t('fixedAsset.nature')"
|
||||
v-model="data.nature"
|
||||
:options="natureOptions"
|
||||
option-value="nature"
|
||||
option-label="nature"
|
||||
hide-selected
|
||||
/>
|
||||
<VnInputNumber
|
||||
:label="t('fixedAsset.value')"
|
||||
v-model="data.value"
|
||||
fill-input
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
url="Ppes/getSubAccounts"
|
||||
:label="t('fixedAsset.account')"
|
||||
v-model="data.account"
|
||||
option-value="code"
|
||||
option-label="code"
|
||||
hide-selected
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
`${scope.opt?.description}`
|
||||
}}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ `#${scope.opt?.code}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<VnSelect
|
||||
url="Ppes/getEndowments"
|
||||
:label="t('fixedAsset.endowment')"
|
||||
v-model="data.endowment"
|
||||
option-value="code"
|
||||
option-label="code"
|
||||
hide-selected
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
`${scope.opt?.description}`
|
||||
}}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ `#${scope.opt?.code}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<VnAccountNumber
|
||||
:label="t('fixedAsset.elementAccount')"
|
||||
v-model="data.elementAccount"
|
||||
fill-input
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
url="PpeLocations"
|
||||
:label="t('fixedAsset.location')"
|
||||
v-model="data.locationFk"
|
||||
option-value="code"
|
||||
:option-label="(value) => `${value.code} - ${value.description}`"
|
||||
hide-selected
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
`${scope.opt?.description}`
|
||||
}}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ `#${scope.opt?.code}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<VnSelect
|
||||
url="PpeGroups"
|
||||
:label="t('globals.group')"
|
||||
v-model="data.groupFk"
|
||||
option-value="id"
|
||||
option-label="description"
|
||||
hide-selected
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnInputDate
|
||||
:label="t('fixedAsset.firstAmortizated')"
|
||||
v-model="data.firstAmortizated"
|
||||
placeholder="dd-mm-aaaa"
|
||||
fill-input
|
||||
/>
|
||||
<VnInputDate
|
||||
:label="t('fixedAsset.lastAmortizated')"
|
||||
v-model="data.lastAmortizated"
|
||||
placeholder="dd-mm-aaaa"
|
||||
fill-input
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnInputDate
|
||||
:label="t('fixedAsset.finished')"
|
||||
v-model="data.finished"
|
||||
placeholder="dd-mm-aaaa"
|
||||
fill-input
|
||||
/>
|
||||
<VnInputNumber
|
||||
:label="t('fixedAsset.amortization')"
|
||||
v-model="data.amortization"
|
||||
fill-input
|
||||
/>
|
||||
<VnSelect
|
||||
url="PpePlans"
|
||||
:label="t('fixedAsset.plan')"
|
||||
v-model="data.planFk"
|
||||
:option-label="(value) => `${value.rate}% - ${value.days} days`"
|
||||
hide-selected
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
`${scope.opt?.rate}% - ${scope.opt?.days} days`
|
||||
}}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ `#${scope.opt?.id}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnInputDate
|
||||
:label="t('fixedAsset.discharged')"
|
||||
v-model="data.discharged"
|
||||
placeholder="dd-mm-aaaa"
|
||||
fill-input
|
||||
/>
|
||||
<VnInput
|
||||
:label="t('fixedAsset.cause')"
|
||||
v-model="data.cause"
|
||||
fill-input
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnCheckbox
|
||||
v-model="data.isInvestmentAsset"
|
||||
:label="t('fixedAsset.isInvestmentAsset')"
|
||||
/>
|
||||
<VnCheckbox :label="t('fixedAsset.isDone')" v-model="data.isDone" />
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,12 @@
|
|||
<script setup>
|
||||
import VnCard from 'components/common/VnCard.vue';
|
||||
import FixedAssetDescriptor from 'pages/FixedAsset/Card/FixedAssetDescriptor.vue';
|
||||
</script>
|
||||
<template>
|
||||
<VnCard
|
||||
data-key="FixedAsset"
|
||||
url="Ppes"
|
||||
:descriptor="FixedAssetDescriptor"
|
||||
:filter="{ where: { id: $route.params.id } }"
|
||||
/>
|
||||
</template>
|
|
@ -0,0 +1,107 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import FixedAssetCard from './FixedAssetCard.vue';
|
||||
import EditPictureForm from 'src/components/EditPictureForm.vue';
|
||||
import VnImg from 'src/components/ui/VnImg.vue';
|
||||
import FixedAssetDescriptorMenu from './FixedAssetDescriptorMenu.vue';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const entityId = computed(() => {
|
||||
return Number(props.id || route.params.id);
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
summary: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const image = ref(null);
|
||||
const showEditPhotoForm = ref(false);
|
||||
const toggleEditPictureForm = () => {
|
||||
showEditPhotoForm.value = !showEditPhotoForm.value;
|
||||
};
|
||||
|
||||
const handlePhotoUpdated = (evt = false) => {
|
||||
image.value.reload(evt);
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<CardDescriptor
|
||||
v-bind="$attrs"
|
||||
:id="entityId"
|
||||
:card="FixedAssetCard"
|
||||
title="description"
|
||||
module="FixedAsset"
|
||||
>
|
||||
<template #before>
|
||||
<div class="relative-position">
|
||||
<VnImg
|
||||
ref="image"
|
||||
:id="parseInt(entityId)"
|
||||
collection="fixedAsset"
|
||||
resolution="520x520"
|
||||
class="photo"
|
||||
>
|
||||
<template #error>
|
||||
<div
|
||||
class="absolute-full picture text-center q-pa-md flex flex-center"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="text-grey-5"
|
||||
style="opacity: 0.4; font-size: 5vh"
|
||||
>
|
||||
<QIcon name="vn:claims" />
|
||||
</div>
|
||||
<div class="text-grey-5" style="opacity: 0.4">
|
||||
{{ t('fixedAsset.imageNotFound') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template> </VnImg
|
||||
><QBtn
|
||||
color="primary"
|
||||
size="lg"
|
||||
round
|
||||
class="edit-photo-btn"
|
||||
@click="toggleEditPictureForm()"
|
||||
>
|
||||
<QIcon name="edit" size="sm" />
|
||||
<QDialog ref="editPhotoFormDialog" v-model="showEditPhotoForm">
|
||||
<EditPictureForm
|
||||
collection="fixedAsset"
|
||||
:id="entityId"
|
||||
@close-form="toggleEditPictureForm()"
|
||||
@on-photo-uploaded="handlePhotoUpdated"
|
||||
/>
|
||||
</QDialog>
|
||||
</QBtn>
|
||||
</div>
|
||||
</template>
|
||||
<template #body="{ entity }">
|
||||
<VnLv
|
||||
:label="$t('fixedAsset.elementAccount')"
|
||||
:value="entity.elementAccount"
|
||||
copy
|
||||
/>
|
||||
<VnLv
|
||||
:label="$t('fixedAsset.location')"
|
||||
:value="entity.location.description"
|
||||
/>
|
||||
</template>
|
||||
<template #menu="{ entity }">
|
||||
<FixedAssetDescriptorMenu :fixedAsset="entity" />
|
||||
</template>
|
||||
</CardDescriptor>
|
||||
</template>
|
|
@ -0,0 +1,47 @@
|
|||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
|
||||
const props = defineProps({
|
||||
fixedAsset: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
const router = useRouter();
|
||||
const quasar = useQuasar();
|
||||
const { t } = useI18n();
|
||||
const fixedAssetId = props.fixedAsset.id;
|
||||
|
||||
function confirmRemove() {
|
||||
quasar
|
||||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('fixedAsset.confirmDeletion'),
|
||||
message: t('fixedAsset.confirmDeletionMessage'),
|
||||
promise: remove,
|
||||
},
|
||||
})
|
||||
.onOk(async () => await router.push({ name: 'FixedAssetList' }));
|
||||
}
|
||||
|
||||
async function remove() {
|
||||
await axios.delete(`Ppes/${fixedAssetId}`);
|
||||
quasar.notify({
|
||||
message: t('globals.dataDeleted'),
|
||||
type: 'positive',
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<QItem @click="confirmRemove" v-ripple clickable>
|
||||
<QItemSection avatar>
|
||||
<QIcon name="delete" />
|
||||
</QItemSection>
|
||||
<QItemSection>{{ t('fixedAsset.delete') }}</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
|
@ -0,0 +1,18 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import VnDmsList from 'src/components/common/VnDmsList.vue';
|
||||
|
||||
const dmsListRef = ref(null);
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<VnDmsList
|
||||
ref="dmsListRef"
|
||||
model="PpeDms"
|
||||
update-model="PpeDms"
|
||||
delete-model="PpeDms"
|
||||
download-model="dms"
|
||||
default-dms-code="fixedAssets"
|
||||
filter="ppeFk"
|
||||
/>
|
||||
</template>
|
|
@ -0,0 +1,149 @@
|
|||
<script setup>
|
||||
import { toDate, toCurrency } from 'src/filters/index';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { ref, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
import axios from 'axios';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
import InvoiceInDescriptorProxy from 'pages/InvoiceIn/Card/InvoiceInDescriptorProxy.vue';
|
||||
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const { notify } = useNotify();
|
||||
const quasar = useQuasar();
|
||||
const tableRef = ref();
|
||||
const dataKey = 'fixedAssetInvoiceIn';
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
align: 'left',
|
||||
name: 'issued',
|
||||
label: t('invoiceIn.list.issued'),
|
||||
columnFilter: {
|
||||
component: 'date',
|
||||
},
|
||||
format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.issued)),
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'supplierFk',
|
||||
label: t('invoiceIn.list.supplier'),
|
||||
columnFilter: {
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Suppliers',
|
||||
fields: ['id', 'name'],
|
||||
},
|
||||
},
|
||||
format: ({ supplierName }) => supplierName,
|
||||
columnClass: 'expand',
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'supplierRef',
|
||||
label: t('invoiceIn.supplierRef'),
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'amount',
|
||||
label: t('invoiceIn.list.amount'),
|
||||
format: ({ amount }) => toCurrency(amount),
|
||||
columnFilter: false,
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
name: 'tableActions',
|
||||
actions: [
|
||||
{
|
||||
title: t('fixedAsset.invoice.unassignInvoice'),
|
||||
isPrimary: true,
|
||||
icon: 'delete',
|
||||
action: ({ id }) => confirmRemove(id),
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
function confirmRemove(id) {
|
||||
quasar.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('fixedAsset.invoice.unassignInvoice'),
|
||||
message: t('fixedAsset.invoice.unassignInvoiceConfirmation'),
|
||||
promise: () => unassignInvoice(id),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function unassignInvoice(id) {
|
||||
try {
|
||||
await axios.delete(`PpeComponents/${id}`);
|
||||
notify(t('fixedAsset.invoice.unassignedInvoice'), 'positive');
|
||||
tableRef.value.reload();
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VnTable
|
||||
ref="tableRef"
|
||||
:data-key="dataKey"
|
||||
:url="`ppes/${route.params.id}/getInvoices`"
|
||||
:columns="columns"
|
||||
search-url="fixedAssetInvoiceIns"
|
||||
:order="['issued DESC', 'supplierRef ASC']"
|
||||
:create="{
|
||||
urlCreate: 'ppeComponents',
|
||||
title: t('fixedAsset.invoice.assignInvoice'),
|
||||
formInitialData: {
|
||||
ppeFk: parseInt(route.params.id, 10),
|
||||
},
|
||||
onDataSaved: ({ id }) => tableRef.reload(),
|
||||
}"
|
||||
:disable-option="{ card: true }"
|
||||
auto-load
|
||||
>
|
||||
<template #column-supplierFk="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.supplierName }}
|
||||
<SupplierDescriptorProxy :id="row.supplierId" />
|
||||
</span>
|
||||
</template>
|
||||
<template #column-supplierRef="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.supplierRef }}
|
||||
<InvoiceInDescriptorProxy :id="row.invoiceInFk" />
|
||||
</span>
|
||||
</template>
|
||||
<template #more-create-dialog="{ data }">
|
||||
<VnSelect
|
||||
url="invoiceIns"
|
||||
:label="t('invoiceIn.supplierRef')"
|
||||
:fields="['id', 'supplierRef', 'supplierFk']"
|
||||
:filter-options="['id', 'supplierRef']"
|
||||
v-model="data.invoiceInFk"
|
||||
option-label="supplierRef"
|
||||
:required="true"
|
||||
>
|
||||
</VnSelect>
|
||||
<VnInputNumber
|
||||
:label="t('invoiceIn.list.amount')"
|
||||
v-model="data.amount"
|
||||
required
|
||||
/>
|
||||
</template>
|
||||
</VnTable>
|
||||
</template>
|
|
@ -0,0 +1,6 @@
|
|||
<script setup>
|
||||
import VnLog from 'src/components/common/VnLog.vue';
|
||||
</script>
|
||||
<template>
|
||||
<VnLog model="Ppe" url="/FixedAssetLogs" />
|
||||
</template>
|
|
@ -0,0 +1,348 @@
|
|||
<script setup>
|
||||
import { onMounted, ref, computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { dashIfEmpty, toDate, toCurrency } from 'src/filters';
|
||||
import { getTotal } from 'src/composables/getTotal';
|
||||
import CardSummary from 'components/ui/CardSummary.vue';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import { getUrl } from 'src/composables/getUrl';
|
||||
import VnTitle from 'src/components/common/VnTitle.vue';
|
||||
import VnToSummary from 'src/components/ui/VnToSummary.vue';
|
||||
import VnCheckbox from 'src/components/common/VnCheckbox.vue';
|
||||
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||
import InvoiceInDescriptorProxy from 'src/pages/InvoiceIn/Card/InvoiceInDescriptorProxy.vue';
|
||||
import FixedAssetDescriptorMenu from './FixedAssetDescriptorMenu.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const dmsColumns = ref([
|
||||
{
|
||||
align: 'left',
|
||||
label: t('globals.id'),
|
||||
name: 'id',
|
||||
field: ({ id }) => id,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
label: t('globals.type'),
|
||||
name: 'type',
|
||||
field: ({ type }) => type?.name,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
label: t('globals.order'),
|
||||
name: 'order',
|
||||
field: ({ hardCopyNumber }) => dashIfEmpty(hardCopyNumber),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
label: t('globals.reference'),
|
||||
name: 'reference',
|
||||
field: ({ reference }) => dashIfEmpty(reference),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
label: t('globals.description'),
|
||||
name: 'description',
|
||||
field: ({ description }) => dashIfEmpty(description),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
label: t('globals.original'),
|
||||
name: 'hasFile',
|
||||
toolTip: t('The documentation is available in paper form'),
|
||||
component: 'checkbox',
|
||||
field: ({ hasFile }) => hasFile,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
label: t('globals.worker'),
|
||||
name: 'worker',
|
||||
field: ({ worker }) => worker?.name,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
label: t('globals.created'),
|
||||
name: 'created',
|
||||
field: ({ created }) => toDate(created),
|
||||
},
|
||||
]);
|
||||
|
||||
const entityId = computed(() => $props.id || route.params.id);
|
||||
|
||||
const summary = ref();
|
||||
const fixedAssetUrl = ref();
|
||||
|
||||
onMounted(async () => {
|
||||
fixedAssetUrl.value = (await getUrl('fixed-asset/')) + entityId.value + '/';
|
||||
});
|
||||
|
||||
function toFixedAssetUrl(section) {
|
||||
return '#/fixed-asset/' + entityId.value + '/' + section;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CardSummary
|
||||
ref="summary"
|
||||
:url="`Ppes/${entityId}/summary`"
|
||||
data-key="FixedAssetSummary"
|
||||
v-bind="$attrs.width"
|
||||
>
|
||||
<template #header-left>
|
||||
<VnToSummary
|
||||
v-if="route?.name !== 'FixedAssetSummary'"
|
||||
:route-name="'FixedAssetSummary'"
|
||||
:entity-id="entityId"
|
||||
:url="FixedAssetUrl"
|
||||
/>
|
||||
</template>
|
||||
<template #header="{ entity }">
|
||||
<div>{{ entity.id }} - {{ entity.description }}</div>
|
||||
</template>
|
||||
<template #menu="{ entity }">
|
||||
<FixedAssetDescriptorMenu :fixedAsset="entity" />
|
||||
</template>
|
||||
<template #body="{ entity }">
|
||||
<QCard class="vn-two">
|
||||
<VnTitle
|
||||
:url="toFixedAssetUrl('basic-data')"
|
||||
:text="t('globals.summary.basicData')"
|
||||
data-cy="titleBasicDataBlock1"
|
||||
/>
|
||||
<div class="vn-card-group">
|
||||
<div class="vn-card-content">
|
||||
<VnLv
|
||||
:label="t('globals.description')"
|
||||
:value="dashIfEmpty(entity.description)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('globals.company')"
|
||||
:value="entity.company.code"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('fixedAsset.account')"
|
||||
:value="entity.account"
|
||||
copy
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('fixedAsset.endowment')"
|
||||
:value="entity.endowment"
|
||||
copy
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('fixedAsset.elementAccount')"
|
||||
:value="entity.elementAccount"
|
||||
copy
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('fixedAsset.nature')"
|
||||
:value="dashIfEmpty(entity.nature)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('fixedAsset.location')"
|
||||
:value="
|
||||
dashIfEmpty(
|
||||
`${entity.location.code} - ${entity.location.description}`,
|
||||
)
|
||||
"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('globals.group')"
|
||||
:value="dashIfEmpty(entity.group.description)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('fixedAsset.isInvestmentAsset')"
|
||||
:value="entity.isInvestmentAsset"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</QCard>
|
||||
<QCard class="vn-two">
|
||||
<VnTitle
|
||||
:url="toFixedAssetUrl('basic-data')"
|
||||
:text="t('globals.summary.basicData')"
|
||||
data-cy="titleBasicDataBlock2"
|
||||
/>
|
||||
<div class="vn-card-content">
|
||||
<VnLv
|
||||
:label="t('fixedAsset.value')"
|
||||
:value="dashIfEmpty(toCurrency(entity.value))"
|
||||
copy
|
||||
/>
|
||||
<VnLv
|
||||
:label="$t('fixedAsset.firstAmortizated')"
|
||||
:tooltip="$t('fixedAsset.firstAmortizatedTooltip')"
|
||||
:value="dashIfEmpty(toDate(entity.firstAmortizated))"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('fixedAsset.lastAmortizated')"
|
||||
:tooltip="t('fixedAsset.lastAmortizatedTooltip')"
|
||||
:value="dashIfEmpty(toDate(entity.lastAmortizated))"
|
||||
/>
|
||||
<VnLv
|
||||
:label="$t('fixedAsset.finished')"
|
||||
:tooltip="$t('fixedAsset.finishedTooltip')"
|
||||
:value="dashIfEmpty(toDate(entity.finished))"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('fixedAsset.amortization')"
|
||||
:value="dashIfEmpty(toCurrency(entity.amortization))"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('fixedAsset.plan')"
|
||||
:value="
|
||||
dashIfEmpty(`${entity.plan.rate}% - ${entity.plan.days}days`)
|
||||
"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('fixedAsset.discharged')"
|
||||
:value="dashIfEmpty(toDate(entity.discharged))"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('fixedAsset.cause')"
|
||||
:value="dashIfEmpty(entity.cause)"
|
||||
/>
|
||||
<VnLv :label="t('fixedAsset.isDone')" :value="entity.isDone" />
|
||||
</div>
|
||||
</QCard>
|
||||
<QCard v-if="entity?.ppeDms?.length > 0" class="vn-two">
|
||||
<VnTitle
|
||||
:url="toFixedAssetUrl('dms')"
|
||||
:text="t('globals.pageTitles.dms')"
|
||||
data-cy="titleDmsBlock"
|
||||
/>
|
||||
<QTable :columns="dmsColumns" :rows="entity?.ppeDms" flat>
|
||||
<template #header="props">
|
||||
<QTr :props="props">
|
||||
<QTh auto-width class="text-left">{{ t('globals.id') }}</QTh>
|
||||
<QTh auto-width class="text-left">{{
|
||||
t('globals.type')
|
||||
}}</QTh>
|
||||
<QTh auto-width class="text-left">{{
|
||||
t('globals.order')
|
||||
}}</QTh>
|
||||
<QTh auto-width class="text-left">{{
|
||||
t('globals.reference')
|
||||
}}</QTh>
|
||||
<QTh auto-width class="text-left">{{
|
||||
t('globals.description')
|
||||
}}</QTh>
|
||||
<QTh auto-width class="text-center">{{
|
||||
t('globals.original')
|
||||
}}</QTh>
|
||||
<QTh auto-width class="text-left">{{
|
||||
t('globals.worker')
|
||||
}}</QTh>
|
||||
<QTh auto-width class="text-center">{{
|
||||
t('globals.created')
|
||||
}}</QTh>
|
||||
</QTr>
|
||||
</template>
|
||||
<template #body="props">
|
||||
<QTr :props="props">
|
||||
<QTd class="text-left">{{ props.row.dms.id }}</QTd>
|
||||
<QTd class="text-left">{{ props.row.dms.dmsType.name }}</QTd>
|
||||
<QTd class="text-left">{{
|
||||
props.row.dms.hardCopyNumber
|
||||
}}</QTd>
|
||||
<QTd class="text-left">{{ props.row.dms.reference }}</QTd>
|
||||
<QTd class="text-left">{{ props.row.dms.description }}</QTd>
|
||||
<QTd class="text-center"
|
||||
><VnCheckbox
|
||||
:disable="true"
|
||||
v-model="props.row.dms.hasFile"
|
||||
/></QTd>
|
||||
<QTd class="text-left"
|
||||
><span class="link" @click.stop
|
||||
>{{ props.row.dms.worker.firstName
|
||||
}}<WorkerDescriptorProxy
|
||||
:id="props.row.dms.worker.id" /></span
|
||||
></QTd>
|
||||
<QTd class="text-center">{{
|
||||
toDate(props.row.dms.created)
|
||||
}}</QTd>
|
||||
</QTr>
|
||||
</template>
|
||||
</QTable>
|
||||
</QCard>
|
||||
<QCard v-if="entity.ppeComponents?.length > 0" class="vn-two">
|
||||
<VnTitle
|
||||
:url="toFixedAssetUrl('invoice')"
|
||||
:text="$t('globals.pageTitles.assignedInvoices')"
|
||||
data-cy="titleInvoiceBlock"
|
||||
/>
|
||||
<QTable :rows="entity.ppeComponents" style="text-align: center">
|
||||
<template #body-cell="{ value }">
|
||||
<QTd>{{ value }}</QTd>
|
||||
</template>
|
||||
<template #header="props">
|
||||
<QTr class="tr-header" :props="props">
|
||||
<QTh auto-width>{{ $t('invoiceIn.list.issued') }}</QTh>
|
||||
<QTh auto-width>{{ $t('invoiceIn.list.supplier') }}</QTh>
|
||||
<QTh auto-width>{{ $t('invoiceIn.supplierRef') }}</QTh>
|
||||
<QTh auto-width>{{ $t('invoiceIn.list.amount') }}</QTh>
|
||||
</QTr>
|
||||
</template>
|
||||
<template #body="props">
|
||||
<QTr :props="props">
|
||||
<QTd>{{ toDate(props.row.invoiceIn.issued) }}</QTd>
|
||||
<QTd>
|
||||
<span class="link" data-cy="supplierLink">
|
||||
{{ props.row.invoiceIn.supplier.name }}
|
||||
<SupplierDescriptorProxy
|
||||
:id="props.row.invoiceIn.supplierFk"
|
||||
/>
|
||||
</span>
|
||||
</QTd>
|
||||
<QTd>
|
||||
<span class="link" data-cy="invoiceLink">
|
||||
{{ props.row.invoiceInFk }}
|
||||
<InvoiceInDescriptorProxy
|
||||
:id="props.row.invoiceInFk"
|
||||
/>
|
||||
</span>
|
||||
</QTd>
|
||||
<QTd>{{ toCurrency(props.row.amount) }}</QTd>
|
||||
</QTr>
|
||||
</template>
|
||||
<template #bottom-row>
|
||||
<QTr class="bg">
|
||||
<QTd></QTd>
|
||||
<QTd></QTd>
|
||||
<QTd></QTd>
|
||||
<QTd>
|
||||
{{ toCurrency(getTotal(entity.ppeComponents, 'amount')) }}
|
||||
</QTd>
|
||||
</QTr>
|
||||
</template>
|
||||
</QTable>
|
||||
</QCard>
|
||||
</template>
|
||||
</CardSummary>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.q-card.q-card--dark.q-dark.vn-one {
|
||||
& > .bodyCard {
|
||||
padding: 1%;
|
||||
}
|
||||
}
|
||||
|
||||
.q-table {
|
||||
tr,
|
||||
th,
|
||||
.q-td {
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,207 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnCheckbox from 'src/components/common/VnCheckbox.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
dataKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<div class="q-gutter-x-xs">
|
||||
<strong>{{ t(`fixedAsset.params.${tag.label}`) }}: </strong>
|
||||
<span>{{ formatFn(tag.value) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #body="{ params, searchFn }">
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
v-model="params.id"
|
||||
:label="t('globals.id')"
|
||||
dense
|
||||
filled
|
||||
data-cy="idInput"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
v-model="params.description"
|
||||
:label="t('globals.description')"
|
||||
dense
|
||||
filled
|
||||
data-cy="nameInput"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
url="Companies"
|
||||
:label="t('globals.company')"
|
||||
v-model="params.companyFk"
|
||||
option-value="id"
|
||||
option-label="code"
|
||||
dense
|
||||
filled
|
||||
data-cy="fixedAssetGroupSelect"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
v-model="params.firstAmortizated"
|
||||
:label="t('fixedAsset.firstAmortizated')"
|
||||
dense
|
||||
filled
|
||||
data-cy="firstAmortizatedDateInput"
|
||||
/>
|
||||
</QItemSection>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
v-model="params.lastAmortizated"
|
||||
:label="t('fixedAsset.lastAmortizated')"
|
||||
dense
|
||||
filled
|
||||
data-cy="lastAmortizatedDateInput"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
v-model="params.finished"
|
||||
:label="t('fixedAsset.finished')"
|
||||
dense
|
||||
filled
|
||||
data-cy="finishedDateInput"
|
||||
/>
|
||||
</QItemSection>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
v-model="params.discharged"
|
||||
:label="t('fixedAsset.discharged')"
|
||||
dense
|
||||
filled
|
||||
data-cy="dischargedDateInput"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
url="Ppes/getNatures"
|
||||
:label="t('fixedAsset.nature')"
|
||||
v-model="params.nature"
|
||||
option-value="nature"
|
||||
option-label="nature"
|
||||
dense
|
||||
filled
|
||||
data-cy="natureSelect"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
url="PpePlans"
|
||||
:label="t('fixedAsset.plan')"
|
||||
v-model="params.planFk"
|
||||
:option-label="(value) => `${value.rate}% - ${value.days} days`"
|
||||
dense
|
||||
filled
|
||||
data-cy="planSelect"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
`${scope.opt?.rate}% - ${scope.opt?.days} days`
|
||||
}}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ `#${scope.opt?.id}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
url="PpeGroups"
|
||||
:label="t('globals.group')"
|
||||
v-model="params.groupFk"
|
||||
option-value="id"
|
||||
option-label="description"
|
||||
dense
|
||||
filled
|
||||
data-cy="groupSelect"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
url="PpeLocations"
|
||||
:label="t('fixedAsset.location')"
|
||||
v-model="params.locationFk"
|
||||
option-value="code"
|
||||
:option-label="(value) => `${value.code} - ${value.description}`"
|
||||
dense
|
||||
filled
|
||||
data-cy="locationSelect"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt?.description }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ scope.opt?.code }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnCheckbox
|
||||
v-model="params.isInvestmentAsset"
|
||||
:label="t('fixedAsset.isInvestmentAsset')"
|
||||
dense
|
||||
filled
|
||||
data-cy="isInvestmentAssetCheckbox"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnCheckbox
|
||||
v-model="params.isDone"
|
||||
:label="t('fixedAsset.isDone')"
|
||||
dense
|
||||
filled
|
||||
data-cy="isDoneCheckbox"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnFilterPanel>
|
||||
</template>
|
|
@ -0,0 +1,366 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useState } from 'src/composables/useState';
|
||||
import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||
import VnSection from 'src/components/common/VnSection.vue';
|
||||
import { dashIfEmpty, toCurrency, toDate } from 'src/filters';
|
||||
import FixedAssetFilter from './FixedAssetFilter.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnRow from 'src/components/ui/VnRow.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
import VnAccountNumber from 'src/components/common/VnAccountNumber.vue';
|
||||
|
||||
const user = useState().getUser();
|
||||
const { t } = useI18n();
|
||||
const tableRef = ref();
|
||||
|
||||
const dataKey = 'FixedAssetList';
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
align: 'right',
|
||||
name: 'id',
|
||||
label: t('globals.id'),
|
||||
width: '35px',
|
||||
chip: {
|
||||
condition: () => true,
|
||||
},
|
||||
isId: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'description',
|
||||
label: t('globals.description'),
|
||||
cardVisible: true,
|
||||
columnClass: 'expand',
|
||||
},
|
||||
{
|
||||
name: 'companyFk',
|
||||
label: t('globals.company'),
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'companies',
|
||||
optionValue: 'code',
|
||||
},
|
||||
format: ({ company }) => company?.code,
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
label: t('fixedAsset.value'),
|
||||
component: 'number',
|
||||
format: ({ value }) => toCurrency(value),
|
||||
cardVisible: true,
|
||||
columnFilter: false,
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
name: 'firstAmortizated',
|
||||
label: t('fixedAsset.firstAmortizated'),
|
||||
toolTip: t('fixedAsset.firstAmortizatedTooltip'),
|
||||
component: 'date',
|
||||
format: ({ firstAmortizated }) => dashIfEmpty(toDate(firstAmortizated)),
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
name: 'lastAmortizated',
|
||||
label: t('fixedAsset.lastAmortizated'),
|
||||
toolTip: t('fixedAsset.lastAmortizatedTooltip'),
|
||||
component: 'date',
|
||||
format: ({ lastAmortizated }) => dashIfEmpty(toDate(lastAmortizated)),
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
name: 'finished',
|
||||
label: t('fixedAsset.finished'),
|
||||
toolTip: t('fixedAsset.finishedTooltip'),
|
||||
component: 'date',
|
||||
format: ({ finished }) => dashIfEmpty(toDate(finished)),
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
name: 'discharged',
|
||||
label: t('fixedAsset.discharged'),
|
||||
toolTip: t('fixedAsset.dischargedTooltip'),
|
||||
component: 'date',
|
||||
format: ({ discharged }) => dashIfEmpty(toDate(discharged)),
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'amortization',
|
||||
label: t('fixedAsset.amortization'),
|
||||
component: 'number',
|
||||
format: ({ amortization }) => toCurrency(amortization),
|
||||
cardVisible: true,
|
||||
columnFilter: false,
|
||||
},
|
||||
{
|
||||
name: 'nature',
|
||||
label: t('fixedAsset.nature'),
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Ppes/getNatures',
|
||||
optionValue: 'nature',
|
||||
optionLabel: 'nature',
|
||||
},
|
||||
format: ({ nature }) => dashIfEmpty(nature),
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'plan',
|
||||
label: t('fixedAsset.plan'),
|
||||
toolTip: t('fixedAsset.planTooltip'),
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'PpePlans',
|
||||
optionValue: 'rate',
|
||||
optionLabel: 'days',
|
||||
},
|
||||
format: ({ plan }) => dashIfEmpty(`${plan?.rate * 100}% - ${plan?.days} days`),
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
label: t('globals.group'),
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'PpeGroups',
|
||||
optionValue: 'id',
|
||||
optionLabel: 'description',
|
||||
},
|
||||
format: ({ group }) => dashIfEmpty(group?.description),
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'locationFk',
|
||||
label: t('fixedAsset.location'),
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'PpeLocations',
|
||||
optionValue: 'code',
|
||||
optionLabel: 'description',
|
||||
},
|
||||
format: ({ location }) =>
|
||||
dashIfEmpty(`${location?.code} - ${location?.description}`),
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
labelAbbreviation: t('fixedAsset.isInvestmentAssetAbbr'),
|
||||
label: t('fixedAsset.isInvestmentAsset'),
|
||||
toolTip: t('fixedAsset.isInvestmentAsset'),
|
||||
name: 'isInvestmentAsset',
|
||||
component: 'checkbox',
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
labelAbbreviation: t('fixedAsset.isDoneAbbr'),
|
||||
label: t('fixedAsset.isDone'),
|
||||
toolTip: t('fixedAsset.isDone'),
|
||||
name: 'isDone',
|
||||
component: 'checkbox',
|
||||
cardVisible: true,
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VnSection
|
||||
:data-key="dataKey"
|
||||
:columns="columns"
|
||||
prefix="fixedAsset"
|
||||
:array-data-props="{
|
||||
url: 'ppes/filter',
|
||||
}"
|
||||
>
|
||||
<template #advanced-menu>
|
||||
<FixedAssetFilter :data-key="dataKey" />
|
||||
</template>
|
||||
<template #body>
|
||||
<VnTable
|
||||
ref="tableRef"
|
||||
:data-key="dataKey"
|
||||
:columns="columns"
|
||||
redirect="fixed-asset"
|
||||
:create="{
|
||||
urlCreate: 'Ppes/createFixedAsset',
|
||||
title: t('fixedAsset.createFixedAsset'),
|
||||
onDataSaved: ({ id }) => tableRef.redirect(id),
|
||||
formInitialData: {
|
||||
nature: 'INMOVILIZADO',
|
||||
companyFk: user.companyFk,
|
||||
},
|
||||
}"
|
||||
:disable-option="{ card: true }"
|
||||
:right-search="false"
|
||||
>
|
||||
<template #more-create-dialog="{ data }">
|
||||
<div class="col-span-2">
|
||||
<VnRow>
|
||||
<VnInput
|
||||
v-model="data.id"
|
||||
:label="$t('globals.id')"
|
||||
required
|
||||
/>
|
||||
<VnInputNumber
|
||||
v-model="data.value"
|
||||
:label="$t('fixedAsset.value')"
|
||||
required
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnInput
|
||||
v-model="data.description"
|
||||
:label="$t('globals.description')"
|
||||
required
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnInputDate
|
||||
v-model="data.firstAmortizated"
|
||||
:label="$t('fixedAsset.firstAmortizated')"
|
||||
placeholder="dd-mm-aaaa"
|
||||
/>
|
||||
<VnInputDate
|
||||
v-model="data.lastAmortizated"
|
||||
:label="$t('fixedAsset.lastAmortizated')"
|
||||
placeholder="dd-mm-aaaa"
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
url="Ppes/getSubaccounts"
|
||||
v-model="data.account"
|
||||
:label="$t('fixedAsset.account')"
|
||||
option-value="code"
|
||||
option-label="code"
|
||||
hide-selected
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
`${scope.opt?.description}`
|
||||
}}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ `#${scope.opt?.code}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<VnSelect
|
||||
url="Ppes/getEndowments"
|
||||
v-model="data.endowment"
|
||||
:label="$t('fixedAsset.endowment')"
|
||||
option-value="code"
|
||||
option-label="code"
|
||||
hide-selected
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
`${scope.opt?.description}`
|
||||
}}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ `#${scope.opt?.code}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnAccountNumber
|
||||
:label="$t('fixedAsset.elementAccount')"
|
||||
v-model="data.elementAccount"
|
||||
required
|
||||
/>
|
||||
<VnSelect
|
||||
url="PpePlans"
|
||||
v-model="data.planFk"
|
||||
:label="$t('fixedAsset.plan')"
|
||||
option-value="id"
|
||||
:option-label="
|
||||
(value) => `${value.rate * 100}% - ${value.days} days`
|
||||
"
|
||||
hide-selected
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
`${scope.opt?.rate * 100}% - ${scope.opt?.days} days`
|
||||
}}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ `#${scope.opt?.id}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
url="Ppes/getNatures"
|
||||
v-model="data.nature"
|
||||
:label="$t('fixedAsset.nature')"
|
||||
option-value="nature"
|
||||
option-label="nature"
|
||||
hide-selected
|
||||
/>
|
||||
<VnSelect
|
||||
url="PpeGroups"
|
||||
v-model="data.groupFk"
|
||||
:label="$t('globals.group')"
|
||||
option-value="id"
|
||||
option-label="description"
|
||||
hide-selected
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
url="Companies"
|
||||
:fields="['id', 'code']"
|
||||
v-model="data.companyFk"
|
||||
:label="$t('globals.company')"
|
||||
option-value="id"
|
||||
option-label="code"
|
||||
hide-selected
|
||||
/>
|
||||
<VnSelect
|
||||
url="PpeLocations"
|
||||
v-model="data.locationFk"
|
||||
:label="$t('fixedAsset.location')"
|
||||
option-value="code"
|
||||
:option-label="
|
||||
(value) => `${value.code} - ${value.description}`
|
||||
"
|
||||
hide-selected
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
`${scope.opt?.description}`
|
||||
}}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ `#${scope.opt?.code}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</VnRow>
|
||||
</div>
|
||||
</template>
|
||||
</VnTable>
|
||||
</template>
|
||||
</VnSection>
|
||||
</template>
|
|
@ -0,0 +1,50 @@
|
|||
fixedAsset:
|
||||
search: Search fixed assets
|
||||
searchInfo: Search fixed assets by id
|
||||
value: Value
|
||||
firstAmortizated: Amort. start
|
||||
firstAmortizatedTooltip: Amortization start date
|
||||
lastAmortizated: Amort. end
|
||||
lastAmortizatedTooltip: Amortization end date
|
||||
amortization: Amortization
|
||||
finished: Final date
|
||||
finishedTooltip: Final date
|
||||
discharged: Discharged
|
||||
dischargedTooltip: Discharged
|
||||
nature: Nature
|
||||
plan: Amort. plan
|
||||
planTooltip: Amortization plan
|
||||
cause: Cause of discharge
|
||||
location: Location
|
||||
elementAccount: Element account
|
||||
account: Subaccount
|
||||
endowment: Endowment
|
||||
isInvestmentAsset: Investment asset
|
||||
isInvestmentAssetAbbr: IA
|
||||
isDone: Completed
|
||||
isDoneAbbr: Co
|
||||
createFixedAsset: Create fixed asset
|
||||
confirmDeletion: Confirm deletion
|
||||
confirmDeletionMessage: Are you sure you want to delete this fixed asset?
|
||||
delete: Delete fixed asset
|
||||
params:
|
||||
id: Id
|
||||
description: Description
|
||||
companyFk: Company
|
||||
firstAmortizated: Amort. start
|
||||
lastAmortizated: Amort. end
|
||||
finished: Final date
|
||||
discharged: Discharged
|
||||
nature: Nature
|
||||
planFk: Amort. plan
|
||||
groupFk: Group
|
||||
locationFk: Location
|
||||
isInvestmentAsset: Investment asset
|
||||
isDone: Completed
|
||||
issued: Issued
|
||||
invoice:
|
||||
assignedInvoices: Assigned invoices
|
||||
assignInvoice: Assign invoice
|
||||
unassignedInvoice: Unassigned invoice
|
||||
unassignInvoice: Unassign invoice
|
||||
unassignInvoiceConfirmation: This invoice will be unlinked from this fixed asset, continue anyway?
|
|
@ -0,0 +1,51 @@
|
|||
fixedAsset:
|
||||
search: Buscar inmovilizados
|
||||
searchInfo: Buscar inmovilizados por id
|
||||
value: Valor
|
||||
firstAmortizated: F. ini. amort.
|
||||
firstAmortizatedTooltip: Fecha inicial de amortización.
|
||||
lastAmortizated: F. fin. amort.
|
||||
lastAmortizatedTooltip: Fecha final de amortización
|
||||
amortization: Amortización
|
||||
finished: F. final
|
||||
finishedTooltip: Fecha final
|
||||
discharged: F. baja
|
||||
dischargedTooltip: Fecha de baja
|
||||
nature: Naturaleza
|
||||
plan: Plan amort.
|
||||
planTooltip: Plan de amortización
|
||||
cause: Causa de la baja
|
||||
location: Emplazamiento
|
||||
elementAccount: Cuenta de elemento
|
||||
account: Subcuenta
|
||||
endowment: Dotación
|
||||
isInvestmentAsset: Bien de inversión
|
||||
isInvestmentAssetAbbr: BI
|
||||
isDone: Completado
|
||||
isDoneAbbr: Co
|
||||
createFixedAsset: Crear inmovilizado
|
||||
confirmDeletion: Confirmar eliminación
|
||||
confirmDeletionMessage: Seguro que quieres eliminar este inmovilizado?
|
||||
delete: Eliminar inmovilizado
|
||||
params:
|
||||
search: Búsqueda general
|
||||
id: Id
|
||||
description: Descripción
|
||||
companyFk: Empresa
|
||||
firstAmortizated: F. ini. amort.
|
||||
lastAmortizated: F. fin. amort.
|
||||
finished: F. final
|
||||
discharged: F. baja
|
||||
nature: Naturaleza
|
||||
planFk: Plan amort.
|
||||
groupFk: Grupo
|
||||
locationFk: Emplazamiento
|
||||
isInvestmentAsset: Bien de inversión
|
||||
isDone: Completado
|
||||
issued: F. emisión
|
||||
invoice:
|
||||
assignedInvoices: Facturas vinculadas
|
||||
assignInvoice: Vincular factura
|
||||
unassignedInvoice: Factura desvinculada
|
||||
unassignInvoice: Desvincular factura
|
||||
unassignInvoiceConfirmation: Esta factura se desvinculará de este inmovilizado, ¿Continuar de todas formas?
|
|
@ -90,7 +90,6 @@ const columns = computed(() => [
|
|||
auto-load
|
||||
:data-required="{ invoiceInFk: invoiceInId }"
|
||||
:filter="filter"
|
||||
:insert-on-load="true"
|
||||
v-model:selected="rowsSelected"
|
||||
@on-fetch="(data) => (invoceInIntrastat = data)"
|
||||
>
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
<script setup>
|
||||
import { ref, computed, markRaw } from 'vue';
|
||||
import { ref, computed, nextTick } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import { getTotal } from 'src/composables/getTotal';
|
||||
import { toCurrency } from 'src/filters';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import CrudModel from 'src/components/CrudModel.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
|
||||
import CreateNewExpenseForm from 'src/components/CreateNewExpenseForm.vue';
|
||||
import { getExchange } from 'src/composables/getExchange';
|
||||
import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||
import VnSelectExpense from 'src/components/common/VnSelectExpense.vue';
|
||||
import { useAccountShortToStandard } from 'src/composables/useAccountShortToStandard';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const arrayData = useArrayData();
|
||||
const route = useRoute();
|
||||
const invoiceIn = computed(() => arrayData.store.data);
|
||||
|
@ -19,142 +24,100 @@ const expenses = ref([]);
|
|||
const sageTaxTypes = ref([]);
|
||||
const sageTransactionTypes = ref([]);
|
||||
const rowsSelected = ref([]);
|
||||
const invoiceInVatTableRef = ref();
|
||||
const invoiceInFormRef = ref();
|
||||
|
||||
defineProps({ actionIcon: { type: String, default: 'add' } });
|
||||
|
||||
function taxRate(invoiceInTax) {
|
||||
const sageTaxTypeId = invoiceInTax.taxTypeSageFk;
|
||||
const taxRateSelection = sageTaxTypes.value.find(
|
||||
(transaction) => transaction.id == sageTaxTypeId,
|
||||
);
|
||||
const taxTypeSage = taxRateSelection?.rate ?? 0;
|
||||
const taxableBase = invoiceInTax?.taxableBase ?? 0;
|
||||
|
||||
return (taxTypeSage / 100) * taxableBase;
|
||||
}
|
||||
defineProps({
|
||||
actionIcon: {
|
||||
type: String,
|
||||
default: 'add',
|
||||
},
|
||||
});
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
name: 'expenseFk',
|
||||
name: 'expense',
|
||||
label: t('Expense'),
|
||||
component: markRaw(VnSelectExpense),
|
||||
format: (row) => {
|
||||
const expense = expenses.value.find((e) => e.id === row.expenseFk);
|
||||
return expense ? `${expense.id}: ${expense.name}` : row.expenseFk;
|
||||
},
|
||||
field: (row) => row.expenseFk,
|
||||
options: expenses.value,
|
||||
model: 'expenseFk',
|
||||
optionValue: 'id',
|
||||
optionLabel: (row) => `${row.id}: ${row.name}`,
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
isEditable: true,
|
||||
create: true,
|
||||
width: '250px',
|
||||
},
|
||||
{
|
||||
name: 'taxableBase',
|
||||
name: 'taxablebase',
|
||||
label: t('Taxable base'),
|
||||
component: 'number',
|
||||
attrs: {
|
||||
clearable: true,
|
||||
'clear-icon': 'close',
|
||||
},
|
||||
field: (row) => row.taxableBase,
|
||||
model: 'taxableBase',
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
isEditable: true,
|
||||
create: true,
|
||||
},
|
||||
{
|
||||
name: 'isDeductible',
|
||||
label: t('invoiceIn.isDeductible'),
|
||||
component: 'checkbox',
|
||||
field: (row) => row.isDeductible,
|
||||
model: 'isDeductible',
|
||||
align: 'center',
|
||||
isEditable: true,
|
||||
create: true,
|
||||
createAttrs: {
|
||||
defaultValue: true,
|
||||
},
|
||||
width: '100px',
|
||||
},
|
||||
{
|
||||
name: 'taxTypeSageFk',
|
||||
name: 'sageiva',
|
||||
label: t('Sage iva'),
|
||||
component: 'select',
|
||||
attrs: {
|
||||
field: (row) => row.taxTypeSageFk,
|
||||
options: sageTaxTypes.value,
|
||||
model: 'taxTypeSageFk',
|
||||
optionValue: 'id',
|
||||
optionLabel: (row) => `${row.id}: ${row.vat}`,
|
||||
filterOptions: ['id', 'vat'],
|
||||
'data-cy': 'vat-sageiva',
|
||||
},
|
||||
format: (row) => {
|
||||
const taxType = sageTaxTypes.value.find((t) => t.id === row.taxTypeSageFk);
|
||||
return taxType ? `${taxType.id}: ${taxType.vat}` : row.taxTypeSageFk;
|
||||
},
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
isEditable: true,
|
||||
create: true,
|
||||
},
|
||||
{
|
||||
name: 'transactionTypeSageFk',
|
||||
name: 'sagetransaction',
|
||||
label: t('Sage transaction'),
|
||||
component: 'select',
|
||||
attrs: {
|
||||
field: (row) => row.transactionTypeSageFk,
|
||||
options: sageTransactionTypes.value,
|
||||
model: 'transactionTypeSageFk',
|
||||
optionValue: 'id',
|
||||
optionLabel: (row) => `${row.id}: ${row.transaction}`,
|
||||
filterOptions: ['id', 'transaction'],
|
||||
},
|
||||
format: (row) => {
|
||||
const transType = sageTransactionTypes.value.find(
|
||||
(t) => t.id === row.transactionTypeSageFk,
|
||||
);
|
||||
return transType
|
||||
? `${transType.id}: ${transType.transaction}`
|
||||
: row.transactionTypeSageFk;
|
||||
},
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
isEditable: true,
|
||||
create: true,
|
||||
},
|
||||
{
|
||||
name: 'rate',
|
||||
label: t('Rate'),
|
||||
sortable: false,
|
||||
format: (row) => taxRate(row).toFixed(2),
|
||||
sortable: true,
|
||||
field: (row) => taxRate(row, row.taxTypeSageFk),
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'foreignValue',
|
||||
name: 'foreignvalue',
|
||||
label: t('Foreign value'),
|
||||
component: 'number',
|
||||
sortable: true,
|
||||
field: (row) => row.foreignValue,
|
||||
align: 'left',
|
||||
create: true,
|
||||
disable: !isNotEuro(currency.value),
|
||||
},
|
||||
{
|
||||
name: 'total',
|
||||
label: t('Total'),
|
||||
label: 'Total',
|
||||
align: 'left',
|
||||
format: (row) => (Number(row.taxableBase || 0) + Number(taxRate(row))).toFixed(2),
|
||||
},
|
||||
]);
|
||||
|
||||
const tableRows = computed(
|
||||
() => invoiceInVatTableRef.value?.CrudModelRef?.formData || [],
|
||||
);
|
||||
const taxableBaseTotal = computed(() => {
|
||||
return getTotal(tableRows.value, 'taxableBase');
|
||||
return getTotal(invoiceInFormRef.value.formData, 'taxableBase');
|
||||
});
|
||||
|
||||
const taxRateTotal = computed(() => {
|
||||
return tableRows.value.reduce((sum, row) => sum + Number(taxRate(row)), 0);
|
||||
return getTotal(invoiceInFormRef.value.formData, null, {
|
||||
cb: taxRate,
|
||||
});
|
||||
});
|
||||
|
||||
const combinedTotal = computed(() => {
|
||||
return +taxableBaseTotal.value + +taxRateTotal.value;
|
||||
});
|
||||
|
||||
const filter = computed(() => ({
|
||||
const filter = {
|
||||
fields: [
|
||||
'id',
|
||||
'invoiceInFk',
|
||||
|
@ -168,75 +131,388 @@ const filter = computed(() => ({
|
|||
where: {
|
||||
invoiceInFk: route.params.id,
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
const isNotEuro = (code) => code != 'EUR';
|
||||
|
||||
async function handleForeignValueUpdate(val, row) {
|
||||
if (!isNotEuro(currency.value)) return;
|
||||
row.taxableBase = await getExchange(
|
||||
val,
|
||||
invoiceIn.value?.currencyFk,
|
||||
invoiceIn.value?.issued,
|
||||
function taxRate(invoiceInTax) {
|
||||
const sageTaxTypeId = invoiceInTax.taxTypeSageFk;
|
||||
const taxRateSelection = sageTaxTypes.value.find(
|
||||
(transaction) => transaction.id == sageTaxTypeId,
|
||||
);
|
||||
const taxTypeSage = taxRateSelection?.rate ?? 0;
|
||||
const taxableBase = invoiceInTax?.taxableBase ?? 0;
|
||||
|
||||
return ((taxTypeSage / 100) * taxableBase).toFixed(2);
|
||||
}
|
||||
|
||||
function autocompleteExpense(evt, row, col, ref) {
|
||||
const val = evt.target.value;
|
||||
if (!val) return;
|
||||
|
||||
const param = isNaN(val) ? row[col.model] : val;
|
||||
const lookup = expenses.value.find(
|
||||
({ id }) => id == useAccountShortToStandard(param),
|
||||
);
|
||||
|
||||
ref.vnSelectDialogRef.vnSelectRef.toggleOption(lookup);
|
||||
}
|
||||
|
||||
function setCursor(ref) {
|
||||
nextTick(() => {
|
||||
const select = ref.vnSelectDialogRef
|
||||
? ref.vnSelectDialogRef.vnSelectRef
|
||||
: ref.vnSelectRef;
|
||||
select.$el.querySelector('input').setSelectionRange(0, 0);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<FetchData url="Expenses" auto-load @on-fetch="(data) => (expenses = data)" />
|
||||
<FetchData
|
||||
ref="expensesRef"
|
||||
url="Expenses"
|
||||
auto-load
|
||||
@on-fetch="(data) => (expenses = data)"
|
||||
/>
|
||||
<FetchData url="SageTaxTypes" auto-load @on-fetch="(data) => (sageTaxTypes = data)" />
|
||||
<FetchData
|
||||
url="sageTransactionTypes"
|
||||
auto-load
|
||||
@on-fetch="(data) => (sageTransactionTypes = data)"
|
||||
/>
|
||||
<VnTable
|
||||
<CrudModel
|
||||
ref="invoiceInFormRef"
|
||||
v-if="invoiceIn"
|
||||
ref="invoiceInVatTableRef"
|
||||
data-key="InvoiceInTaxes"
|
||||
url="InvoiceInTaxes"
|
||||
save-url="InvoiceInTaxes/crud"
|
||||
:filter="filter"
|
||||
:data-required="{ invoiceInFk: $route.params.id }"
|
||||
:insert-on-load="true"
|
||||
auto-load
|
||||
v-model:selected="rowsSelected"
|
||||
:columns="columns"
|
||||
:is-editable="true"
|
||||
:table="{ selection: 'multiple', 'row-key': '$index' }"
|
||||
footer
|
||||
:right-search="false"
|
||||
:column-search="false"
|
||||
:disable-option="{ card: true }"
|
||||
class="q-pa-none"
|
||||
:create="{
|
||||
urlCreate: 'InvoiceInTaxes',
|
||||
title: t('Add tax'),
|
||||
formInitialData: { invoiceInFk: $route.params.id, isDeductible: true },
|
||||
onDataSaved: () => invoiceInVatTableRef.reload(),
|
||||
}"
|
||||
:crud-model="{ goTo: `/invoice-in/${$route.params.id}/due-day` }"
|
||||
:go-to="`/invoice-in/${$route.params.id}/due-day`"
|
||||
>
|
||||
<template #column-footer-taxableBase>
|
||||
<template #body="{ rows }">
|
||||
<QTable
|
||||
v-model:selected="rowsSelected"
|
||||
selection="multiple"
|
||||
:columns="columns"
|
||||
:rows="rows"
|
||||
row-key="$index"
|
||||
:grid="$q.screen.lt.sm"
|
||||
>
|
||||
<template #body-cell-expense="{ row, col }">
|
||||
<QTd>
|
||||
<VnSelectDialog
|
||||
:ref="`expenseRef-${row.$index}`"
|
||||
v-model="row[col.model]"
|
||||
:options="col.options"
|
||||
:option-value="col.optionValue"
|
||||
:option-label="col.optionLabel"
|
||||
:filter-options="['id', 'name']"
|
||||
:tooltip="t('Create a new expense')"
|
||||
:acls="[
|
||||
{ model: 'Expense', props: '*', accessType: 'WRITE' },
|
||||
]"
|
||||
@keydown.tab.prevent="
|
||||
autocompleteExpense(
|
||||
$event,
|
||||
row,
|
||||
col,
|
||||
$refs[`expenseRef-${row.$index}`],
|
||||
)
|
||||
"
|
||||
@update:model-value="
|
||||
setCursor($refs[`expenseRef-${row.$index}`])
|
||||
"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
{{ `${scope.opt.id}: ${scope.opt.name}` }}
|
||||
</QItem>
|
||||
</template>
|
||||
<template #form>
|
||||
<CreateNewExpenseForm
|
||||
@on-data-saved="$refs.expensesRef.fetch()"
|
||||
/>
|
||||
</template>
|
||||
</VnSelectDialog>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-isDeductible="{ row }">
|
||||
<QTd align="center">
|
||||
<QCheckbox
|
||||
v-model="row.isDeductible"
|
||||
data-cy="isDeductible_checkbox"
|
||||
/>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-taxablebase="{ row }">
|
||||
<QTd shrink>
|
||||
<VnInputNumber
|
||||
clear-icon="close"
|
||||
v-model="row.taxableBase"
|
||||
clearable
|
||||
/>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-sageiva="{ row, col }">
|
||||
<QTd>
|
||||
<VnSelect
|
||||
:ref="`sageivaRef-${row.$index}`"
|
||||
v-model="row[col.model]"
|
||||
:options="col.options"
|
||||
:option-value="col.optionValue"
|
||||
:option-label="col.optionLabel"
|
||||
:filter-options="['id', 'vat']"
|
||||
data-cy="vat-sageiva"
|
||||
@update:model-value="
|
||||
setCursor($refs[`sageivaRef-${row.$index}`])
|
||||
"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt.vat }}</QItemLabel>
|
||||
<QItemLabel>
|
||||
{{ `#${scope.opt.id}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-sagetransaction="{ row, col }">
|
||||
<QTd>
|
||||
<VnSelect
|
||||
:ref="`sagetransactionRef-${row.$index}`"
|
||||
v-model="row[col.model]"
|
||||
:options="col.options"
|
||||
:option-value="col.optionValue"
|
||||
:option-label="col.optionLabel"
|
||||
:filter-options="['id', 'transaction']"
|
||||
@update:model-value="
|
||||
setCursor($refs[`sagetransactionRef-${row.$index}`])
|
||||
"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
scope.opt.transaction
|
||||
}}</QItemLabel>
|
||||
<QItemLabel>
|
||||
{{ `#${scope.opt.id}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-foreignvalue="{ row }">
|
||||
<QTd shrink>
|
||||
<VnInputNumber
|
||||
:class="{
|
||||
'no-pointer-events': !isNotEuro(currency),
|
||||
}"
|
||||
:disable="!isNotEuro(currency)"
|
||||
v-model="row.foreignValue"
|
||||
@update:model-value="
|
||||
async (val) => {
|
||||
if (!isNotEuro(currency)) return;
|
||||
row.taxableBase = await getExchange(
|
||||
val,
|
||||
row.currencyFk,
|
||||
invoiceIn.issued,
|
||||
);
|
||||
}
|
||||
"
|
||||
/>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #bottom-row>
|
||||
<QTr class="bg">
|
||||
<QTd />
|
||||
<QTd />
|
||||
<QTd>
|
||||
{{ toCurrency(taxableBaseTotal) }}
|
||||
</template>
|
||||
<template #column-footer-rate>
|
||||
</QTd>
|
||||
<QTd />
|
||||
<QTd />
|
||||
<QTd />
|
||||
<QTd>
|
||||
{{ toCurrency(taxRateTotal) }}
|
||||
</template>
|
||||
<template #column-footer-total>
|
||||
</QTd>
|
||||
<QTd />
|
||||
<QTd>
|
||||
{{ toCurrency(combinedTotal) }}
|
||||
</QTd>
|
||||
</QTr>
|
||||
</template>
|
||||
</VnTable>
|
||||
|
||||
<template #item="props">
|
||||
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
||||
<QCard bordered flat class="q-my-xs">
|
||||
<QCardSection>
|
||||
<QCheckbox v-model="props.selected" dense />
|
||||
</QCardSection>
|
||||
<QSeparator />
|
||||
<QList>
|
||||
<QItem>
|
||||
<VnSelectDialog
|
||||
:label="t('Expense')"
|
||||
class="full-width"
|
||||
v-model="props.row['expenseFk']"
|
||||
:options="expenses"
|
||||
option-value="id"
|
||||
:option-label="(row) => `${row.id}:${row.name}`"
|
||||
:filter-options="['id', 'name']"
|
||||
:tooltip="t('Create a new expense')"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
{{ `${scope.opt.id}: ${scope.opt.name}` }}
|
||||
</QItem>
|
||||
</template>
|
||||
<template #form>
|
||||
<CreateNewExpenseForm />
|
||||
</template>
|
||||
</VnSelectDialog>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<VnInputNumber
|
||||
:label="t('Taxable base')"
|
||||
:class="{
|
||||
'no-pointer-events': isNotEuro(currency),
|
||||
}"
|
||||
class="full-width"
|
||||
:disable="isNotEuro(currency)"
|
||||
clear-icon="close"
|
||||
v-model="props.row.taxableBase"
|
||||
clearable
|
||||
/>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<VnSelect
|
||||
:label="t('Sage iva')"
|
||||
class="full-width"
|
||||
v-model="props.row['taxTypeSageFk']"
|
||||
:options="sageTaxTypes"
|
||||
option-value="id"
|
||||
:option-label="(row) => `${row.id}:${row.vat}`"
|
||||
:filter-options="['id', 'vat']"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
scope.opt.vat
|
||||
}}</QItemLabel>
|
||||
<QItemLabel>
|
||||
{{ `#${scope.opt.id}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<VnSelect
|
||||
class="full-width"
|
||||
v-model="props.row['transactionTypeSageFk']"
|
||||
:options="sageTransactionTypes"
|
||||
option-value="id"
|
||||
:option-label="
|
||||
(row) => `${row.id}:${row.transaction}`
|
||||
"
|
||||
:filter-options="['id', 'transaction']"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
scope.opt.transaction
|
||||
}}</QItemLabel>
|
||||
<QItemLabel>
|
||||
{{ `#${scope.opt.id}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</QItem>
|
||||
<QItem>
|
||||
{{ toCurrency(taxRate(props.row), currency) }}
|
||||
</QItem>
|
||||
<QItem>
|
||||
<VnInputNumber
|
||||
:label="t('Foreign value')"
|
||||
class="full-width"
|
||||
:class="{
|
||||
'no-pointer-events': !isNotEuro(currency),
|
||||
}"
|
||||
:disable="!isNotEuro(currency)"
|
||||
v-model="props.row.foreignValue"
|
||||
/>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QCard>
|
||||
</div>
|
||||
</template>
|
||||
</QTable>
|
||||
</template>
|
||||
</CrudModel>
|
||||
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
||||
<QBtn
|
||||
color="primary"
|
||||
icon="add"
|
||||
size="lg"
|
||||
v-shortcut="'+'"
|
||||
round
|
||||
@click="invoiceInFormRef.insert()"
|
||||
>
|
||||
<QTooltip>{{ t('Add tax') }}</QTooltip>
|
||||
</QBtn>
|
||||
</QPageSticky>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bg {
|
||||
background-color: var(--vn-light-gray);
|
||||
}
|
||||
@media (max-width: $breakpoint-xs) {
|
||||
.q-dialog {
|
||||
.q-card {
|
||||
&__section:not(:first-child) {
|
||||
.q-item {
|
||||
flex-direction: column;
|
||||
|
||||
.q-checkbox {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.q-item {
|
||||
min-height: 0;
|
||||
}
|
||||
.default-icon {
|
||||
cursor: pointer;
|
||||
border-radius: 50px;
|
||||
background-color: $primary;
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
es:
|
||||
Expense: Gasto
|
||||
Create a new expense: Crear nuevo gasto
|
||||
Add tax: Añadir Gasto/IVA # Changed label slightly
|
||||
Add tax: Crear gasto
|
||||
Taxable base: Base imp.
|
||||
Sage iva: Sage iva # Kept original label
|
||||
Sage tax: Sage iva
|
||||
Sage transaction: Sage transacción
|
||||
Rate: Cuota # Changed label
|
||||
Rate: Tasa
|
||||
Foreign value: Divisa
|
||||
Total: Total
|
||||
invoiceIn.isDeductible: Deducible
|
||||
</i18n>
|
||||
|
|
|
@ -51,7 +51,6 @@ const submit = async (rows) => {
|
|||
<CrudModel
|
||||
:data-required="{ itemFk: route.params.id }"
|
||||
:default-remove="false"
|
||||
:insert-on-load="true"
|
||||
:filter="{
|
||||
fields: ['id', 'itemFk', 'code'],
|
||||
where: { itemFk: route.params.id },
|
||||
|
|
|
@ -76,22 +76,15 @@ const insertTag = (rows) => {
|
|||
model="ItemTags"
|
||||
url="ItemTags"
|
||||
:data-required="{
|
||||
$index: undefined,
|
||||
itemFk: route.params.id,
|
||||
priority: undefined,
|
||||
tag: {
|
||||
isFree: true,
|
||||
value: undefined,
|
||||
name: undefined,
|
||||
},
|
||||
|
||||
}"
|
||||
:data-default="{
|
||||
tag: {
|
||||
isFree: true,
|
||||
isFree: undefined,
|
||||
value: undefined,
|
||||
name: undefined,
|
||||
},
|
||||
tagFk: undefined,
|
||||
priority: undefined,
|
||||
}"
|
||||
:default-remove="false"
|
||||
:user-filter="{
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { Notify } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import VnInputPassword from 'src/components/common/VnInputPassword.vue';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import { useLogin } from 'src/composables/useLogin';
|
||||
import useNotify from 'src/composables/useNotify';
|
||||
|
||||
import VnLogo from 'components/ui/VnLogo.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import axios from 'axios';
|
||||
|
@ -14,14 +15,16 @@ const session = useSession();
|
|||
const loginCache = useLogin();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const { notify } = useNotify();
|
||||
|
||||
const username = ref('');
|
||||
const password = ref('');
|
||||
const keepLogin = ref(true);
|
||||
|
||||
async function onSubmit() {
|
||||
const params = { user: username.value, password: password.value };
|
||||
const params = {
|
||||
user: username.value,
|
||||
password: password.value,
|
||||
};
|
||||
try {
|
||||
const { data } = await axios.post('Accounts/login', params);
|
||||
if (!data) return;
|
||||
|
@ -30,7 +33,11 @@ async function onSubmit() {
|
|||
await session.setLogin(data);
|
||||
} catch (res) {
|
||||
if (res.response?.data?.error?.code === 'REQUIRES_2FA') {
|
||||
notify(t('login.twoFactorRequired'), 'warning', 'phoneLink_lock');
|
||||
Notify.create({
|
||||
message: t('login.twoFactorRequired'),
|
||||
icon: 'phoneLink_lock',
|
||||
type: 'warning',
|
||||
});
|
||||
params.keepLogin = keepLogin.value;
|
||||
loginCache.setUser(params);
|
||||
return router.push({
|
||||
|
@ -38,7 +45,10 @@ async function onSubmit() {
|
|||
query: router.currentRoute.value?.query,
|
||||
});
|
||||
}
|
||||
throw res;
|
||||
Notify.create({
|
||||
message: t('login.loginError'),
|
||||
type: 'negative',
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -18,7 +18,7 @@ const noteFilter = computed(() => {
|
|||
|
||||
const body = {
|
||||
vehicleFk: vehicleId.value,
|
||||
userFk: user.value.id,
|
||||
workerFk: user.value.id,
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -24,10 +24,10 @@ const crudModelFilter = reactive({
|
|||
where: { ticketFk: route.params.id },
|
||||
});
|
||||
|
||||
const crudModelDefaultData = computed(() => ({
|
||||
created: Date.vnNew(),
|
||||
const crudModelRequiredData = computed(() => ({
|
||||
packagingFk: null,
|
||||
quantity: 0,
|
||||
created: Date.vnNew(),
|
||||
ticketFk: route.params.id,
|
||||
}));
|
||||
|
||||
|
@ -63,7 +63,7 @@ watch(
|
|||
url="TicketPackagings"
|
||||
model="TicketPackagings"
|
||||
:filter="crudModelFilter"
|
||||
:data-default="crudModelDefaultData"
|
||||
:data-required="crudModelRequiredData"
|
||||
:default-remove="false"
|
||||
auto-load
|
||||
>
|
||||
|
|
|
@ -719,7 +719,6 @@ watch(
|
|||
:create-as-dialog="false"
|
||||
:crud-model="{
|
||||
disableInfiniteScroll: true,
|
||||
insertOnLoad: false,
|
||||
}"
|
||||
:default-remove="false"
|
||||
:default-reset="false"
|
||||
|
|
|
@ -181,7 +181,6 @@ const setUserParams = (params) => {
|
|||
:create="false"
|
||||
:crud-model="{
|
||||
disableInfiniteScroll: true,
|
||||
insertOnLoad: false,
|
||||
}"
|
||||
:table="{
|
||||
'row-key': 'itemFk',
|
||||
|
|
|
@ -547,7 +547,6 @@ watch(route, () => {
|
|||
},
|
||||
]"
|
||||
v-text="col.value"
|
||||
:data-cy="`extra-community-${col.name}`"
|
||||
/>
|
||||
<TravelDescriptorProxy
|
||||
v-if="col.name === 'id'"
|
||||
|
|
|
@ -99,10 +99,6 @@ const columns = computed(() => [
|
|||
workerFk: entityId,
|
||||
},
|
||||
}"
|
||||
:crud-model="{
|
||||
insertOnLoad: false,
|
||||
}"
|
||||
|
||||
order="paymentDate DESC"
|
||||
:columns="columns"
|
||||
auto-load
|
||||
|
|
|
@ -128,9 +128,6 @@ const columns = computed(() => [
|
|||
workerFk: entityId,
|
||||
},
|
||||
}"
|
||||
:crud-model="{
|
||||
insertOnLoad: false,
|
||||
}"
|
||||
order="id DESC"
|
||||
:columns="columns"
|
||||
auto-load
|
||||
|
|
|
@ -109,9 +109,6 @@ const columns = [
|
|||
workerFk: entityId,
|
||||
},
|
||||
}"
|
||||
:crud-model="{
|
||||
insertOnLoad: false,
|
||||
}"
|
||||
order="date DESC"
|
||||
:columns="columns"
|
||||
auto-load
|
||||
|
|
|
@ -1,26 +1,28 @@
|
|||
<script setup>
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useState } from 'src/composables/useState';
|
||||
|
||||
import VnNotes from 'src/components/ui/VnNotes.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const state = useState();
|
||||
const user = state.getUser();
|
||||
|
||||
const userFilter = {
|
||||
order: 'created DESC',
|
||||
|
||||
include: {
|
||||
relation: 'worker',
|
||||
scope: {
|
||||
fields: ['id', 'firstName', 'lastName'],
|
||||
include: {
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['id', 'nickname', 'name'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const body = {
|
||||
workerFk: route.params.id,
|
||||
userFk: user.value.id,
|
||||
};
|
||||
const body = { workerFk: route.params.id };
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -170,9 +170,6 @@ function isSigned(row) {
|
|||
'row-key': 'deviceProductionFk',
|
||||
selection: 'multiple',
|
||||
}"
|
||||
:crud-model="{
|
||||
insertOnLoad: false,
|
||||
}"
|
||||
:table-filter="{ hiddenTags: ['userFk'] }"
|
||||
>
|
||||
<template #moreBeforeActions>
|
||||
|
|
|
@ -66,8 +66,6 @@ const excludeType = computed({
|
|||
const arrayData = useArrayData('ZoneEvents');
|
||||
|
||||
const exclusionGeoCreate = async () => {
|
||||
if (await zoneHasTickets(route.params.id, dated.value)) return;
|
||||
|
||||
const params = {
|
||||
zoneFk: parseInt(route.params.id),
|
||||
date: dated.value,
|
||||
|
@ -89,8 +87,6 @@ const exclusionCreate = async () => {
|
|||
};
|
||||
const zoneIds = props.zoneIds?.length ? props.zoneIds : [route.params.id];
|
||||
for (const id of zoneIds) {
|
||||
if (await zoneHasTickets(id, dated.value)) return;
|
||||
|
||||
const url = `Zones/${id}/exclusions`;
|
||||
let today = moment(dated.value);
|
||||
let lastDay = today.clone().add(nMonths, 'months').endOf('month');
|
||||
|
@ -127,26 +123,6 @@ const exclusionCreate = async () => {
|
|||
await refetchEvents();
|
||||
};
|
||||
|
||||
const zoneHasTickets = async (zoneId, date) => {
|
||||
const filter = {
|
||||
where: {
|
||||
zoneFk: zoneId,
|
||||
shipped: date,
|
||||
},
|
||||
};
|
||||
const params = { filter: JSON.stringify(filter) };
|
||||
const { data } = await axios.get('Tickets', { params });
|
||||
if (data.length > 0) {
|
||||
quasar.notify({
|
||||
message: t('eventsExclusionForm.cantCloseZone'),
|
||||
type: 'negative',
|
||||
});
|
||||
await refetchEvents();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
if (excludeType.value === 'all') exclusionCreate();
|
||||
else exclusionGeoCreate();
|
||||
|
|
|
@ -80,7 +80,6 @@ eventsExclusionForm:
|
|||
all: All
|
||||
specificLocations: Specific locations
|
||||
rootTreeLabel: Locations where it is not distributed
|
||||
cantCloseZone: Can not close this zone because there are tickets programmed for that day
|
||||
eventsInclusionForm:
|
||||
addEvent: Add event
|
||||
editEvent: Edit event
|
||||
|
|
|
@ -81,7 +81,6 @@ eventsExclusionForm:
|
|||
all: Todo
|
||||
specificLocations: Localizaciones concretas
|
||||
rootTreeLabel: Localizaciones en las que no se reparte
|
||||
cantCloseZone: No se puede cerrar la zona porque hay tickets programados para ese día
|
||||
eventsInclusionForm:
|
||||
addEvent: Añadir evento
|
||||
editEvent: Editar evento
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
import { RouterView } from 'vue-router';
|
||||
|
||||
const fixedAssetCard = {
|
||||
name: 'FixedAssetCard',
|
||||
path: ':id',
|
||||
component: () => import('src/pages/FixedAsset/Card/FixedAssetCard.vue'),
|
||||
redirect: { name: 'FixedAssetSummary' },
|
||||
meta: {
|
||||
menu: ['FixedAssetBasicData', 'FixedAssetInvoice', 'FixedAssetDms', 'FixedAssetLog'],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'FixedAssetSummary',
|
||||
path: 'summary',
|
||||
meta: {
|
||||
title: 'summary',
|
||||
icon: 'view_list',
|
||||
},
|
||||
component: () => import('src/pages/FixedAsset/Card/FixedAssetSummary.vue'),
|
||||
},
|
||||
{
|
||||
name: 'FixedAssetBasicData',
|
||||
path: 'basic-data',
|
||||
meta: {
|
||||
title: 'basicData',
|
||||
icon: 'vn:settings',
|
||||
},
|
||||
component: () => import('src/pages/FixedAsset/Card/FixedAssetBasicData.vue'),
|
||||
},
|
||||
{
|
||||
name: 'FixedAssetInvoice',
|
||||
path: 'invoice',
|
||||
meta: {
|
||||
title: 'invoiceIns',
|
||||
icon: 'vn:invoice-in',
|
||||
},
|
||||
component: () => import('pages/FixedAsset/Card/FixedAssetInvoice.vue'),
|
||||
},
|
||||
{
|
||||
name: 'FixedAssetDms',
|
||||
path: 'dms',
|
||||
meta: {
|
||||
title: 'dms',
|
||||
icon: 'cloud_upload',
|
||||
},
|
||||
component: () => import('src/pages/FixedAsset/Card/FixedAssetDms.vue'),
|
||||
},
|
||||
{
|
||||
name: 'FixedAssetLog',
|
||||
path: 'history',
|
||||
meta: {
|
||||
title: 'history',
|
||||
icon: 'history',
|
||||
},
|
||||
component: () => import('src/pages/FixedAsset/Card/FixedAssetLog.vue'),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'FixedAsset',
|
||||
path: '/fixed-asset',
|
||||
meta: {
|
||||
title: 'fixedAsset',
|
||||
icon: 'inventory_2',
|
||||
moduleName: 'FixedAsset',
|
||||
menu: ['FixedAssetList'],
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'FixedAssetMain' },
|
||||
children: [
|
||||
{
|
||||
name: 'FixedAssetMain',
|
||||
path: '',
|
||||
component: () => import('src/components/common/VnModule.vue'),
|
||||
redirect: { name: 'FixedAssetIndexMain' },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'FixedAssetIndexMain',
|
||||
component: () => import('src/pages/FixedAsset/FixedAssetList.vue'),
|
||||
redirect: { name: 'FixedAssetList' },
|
||||
children: [
|
||||
{
|
||||
name: 'FixedAssetList',
|
||||
path: 'list',
|
||||
meta: {
|
||||
title: 'list',
|
||||
icon: 'view_list',
|
||||
},
|
||||
component: () => import('src/pages/FixedAsset/FixedAssetList.vue'),
|
||||
},
|
||||
fixedAssetCard,
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
|
@ -15,6 +15,7 @@ import Entry from './entry';
|
|||
import Zone from './zone';
|
||||
import Account from './account';
|
||||
import Monitor from './monitor';
|
||||
import FixedAsset from './fixedAsset';
|
||||
|
||||
export default [
|
||||
Item,
|
||||
|
@ -34,4 +35,5 @@ export default [
|
|||
Zone,
|
||||
Account,
|
||||
Monitor,
|
||||
FixedAsset,
|
||||
];
|
||||
|
|
|
@ -15,6 +15,7 @@ import entry from 'src/router/modules/entry';
|
|||
import zone from 'src/router/modules/zone';
|
||||
import account from './modules/account';
|
||||
import monitor from 'src/router/modules/monitor';
|
||||
import fixedAsset from 'src/router/modules/fixedAsset';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
|
@ -83,6 +84,7 @@ const routes = [
|
|||
entry,
|
||||
zone,
|
||||
account,
|
||||
fixedAsset,
|
||||
{
|
||||
path: '/:catchAll(.*)*',
|
||||
name: 'NotFound',
|
||||
|
|
|
@ -18,6 +18,7 @@ export const useNavigationStore = defineStore('navigationStore', () => {
|
|||
'monitor',
|
||||
'supplier',
|
||||
'claim',
|
||||
'fixedAsset',
|
||||
'route',
|
||||
'ticket',
|
||||
'worker',
|
||||
|
|
|
@ -24,8 +24,13 @@ export CI=true
|
|||
export TZ=Europe/Madrid
|
||||
|
||||
# IMAGES
|
||||
docker-compose -f test/cypress/docker-compose.yml --project-directory . pull db
|
||||
docker-compose -f test/cypress/docker-compose.yml --project-directory . pull back
|
||||
docker build -t registry.verdnatura.es/salix-back:dev -f "$salix_dir/back/Dockerfile" "$salix_dir"
|
||||
cd "$salix_dir" && npx myt run -t
|
||||
docker exec vn-database sh -c "rm -rf /mysql-template"
|
||||
docker exec vn-database sh -c "cp -a /var/lib/mysql /mysql-template"
|
||||
docker commit vn-database registry.verdnatura.es/salix-db:dev
|
||||
docker rm -f vn-database
|
||||
cd "$current_dir"
|
||||
docker build -f ./docs/Dockerfile.dev -t lilium-dev .
|
||||
# END IMAGES
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('Account descriptor', { testIsolation: true }, () => {
|
||||
describe('Account descriptor', () => {
|
||||
const descriptorOptions = '[data-cy="descriptor-more-opts-menu"] > .q-list';
|
||||
const url = '/#/account/1/summary';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('ClaimDevelopment', { testIsolation: true }, () => {
|
||||
describe('ClaimDevelopment', () => {
|
||||
const claimId = 1;
|
||||
const firstLineReason = 'tbody > :nth-child(1) > :nth-child(2)';
|
||||
const thirdRow = 'tbody > :nth-child(3)';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('Client fiscal data', { testIsolation: true }, () => {
|
||||
describe('Client fiscal data', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport(1280, 720);
|
||||
cy.login('developer');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('Client list', { testIsolation: true }, () => {
|
||||
describe('Client list', () => {
|
||||
beforeEach(() => {
|
||||
cy.login('developer');
|
||||
cy.visit('/#/customer/list', {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('Entry PreAccount Functionality', { testIsolation: true }, () => {
|
||||
describe('Entry PreAccount Functionality', () => {
|
||||
beforeEach(() => {
|
||||
cy.login('administrative');
|
||||
cy.visit('/#/entry/pre-account');
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
describe('FixedAssetBasicData', () => {
|
||||
const selectors = {
|
||||
resetBtn: '#st-actions > .q-btn-group > .q-btn[title="Reset"]',
|
||||
saveBtn: '#st-actions > .q-btn-group > .q-btn[title="Save"]',
|
||||
labelDescription: '[data-cy="Description_input"]',
|
||||
investmentCheckbox: 'vnCheckboxInvestment asset',
|
||||
completedCheckbox: 'vnCheckboxCompleted',
|
||||
};
|
||||
|
||||
const updateData = {
|
||||
Id: { val: '12' },
|
||||
Value: { val: '100000' },
|
||||
Description: { val: 'F.R.I.D.A.Y.' },
|
||||
Company: { val: 'CCs', type: 'select' },
|
||||
Subaccount: { val: '2800000000', type: 'select' },
|
||||
Endowment: { val: '6810000000', type: 'select' },
|
||||
'Element account': { val: '561.12' },
|
||||
'Amort. start': { val: '1.5', type: 'date' },
|
||||
'Amort. end': { val: '1.10.21', type: 'date' },
|
||||
'Final date': { val: '1.11.21', type: 'date' },
|
||||
Amortization: { val: '640' },
|
||||
'Amort. plan': { val: '2', type: 'select' },
|
||||
Group: { val: 'Chitauri', type: 'select' },
|
||||
Location: { val: 'Stark tower', type: 'select' },
|
||||
Discharged: { val: '1.4.25', type: 'date' },
|
||||
'Cause of discharge': { val: 'Venta' },
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.viewport(1280, 720);
|
||||
cy.login('administrative');
|
||||
cy.visit('#/fixed-asset/1/basic-data');
|
||||
});
|
||||
|
||||
it('Should reset fixed asset basic data', () => {
|
||||
cy.get(selectors.labelDescription)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.invoke('text')
|
||||
.then((name) => {
|
||||
name = name.trim();
|
||||
cy.get(selectors.labelDescription)
|
||||
.click()
|
||||
.type(`{selectall}{backspace}Tony Stark`);
|
||||
cy.get(selectors.resetBtn).click();
|
||||
cy.containContent(selectors.labelDescription, name);
|
||||
});
|
||||
});
|
||||
|
||||
it('Should edit fixed asset basic data', () => {
|
||||
cy.fillInForm(updateData);
|
||||
cy.dataCy(selectors.investmentCheckbox).click();
|
||||
cy.dataCy(selectors.completedCheckbox).click();
|
||||
cy.get(selectors.saveBtn).click();
|
||||
cy.checkNotification('Data saved');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
describe('FixedAssetDescriptor', () => {
|
||||
const selectors = {
|
||||
listItem: '[role="menu"] .q-list .q-item',
|
||||
deleteOpt: 'Delete fixed asset',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.viewport(1280, 720);
|
||||
cy.login('developer');
|
||||
cy.visit('#/fixed-asset/2/summary');
|
||||
});
|
||||
|
||||
it('should delete the fixed-asset', () => {
|
||||
cy.openActionsDescriptor();
|
||||
cy.contains(selectors.listItem, selectors.deleteOpt).click();
|
||||
cy.clickConfirm();
|
||||
cy.checkNotification('Data deleted');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,122 @@
|
|||
describe('FixedAssetDms', () => {
|
||||
const getBtnSelector = (trPosition, btnPosition) =>
|
||||
`tr:${trPosition}-child > .text-right > .no-wrap > :nth-child(${btnPosition}) > .q-btn > .q-btn__content > .q-icon`;
|
||||
|
||||
const selectors = {
|
||||
firstRowDownloadBtn: getBtnSelector('first', 1),
|
||||
firstRowEditBtn: getBtnSelector('first', 2),
|
||||
firstRowDeleteBtn: getBtnSelector('first', 3),
|
||||
firstRowReference:
|
||||
'tr:first-child > :nth-child(5) > .q-tr > :nth-child(1) > span',
|
||||
firstRowId: 'tr:first-child > :nth-child(2) > .q-tr > :nth-child(1) > span',
|
||||
descriptorTitle: '.descriptor .title',
|
||||
lastRowWorkerLink: 'tr:last-child > :nth-child(8) > .q-tr > .link',
|
||||
summaryGoToSummaryBtn: '.summaryHeader [data-cy="goToSummaryBtn"]',
|
||||
descriptorOpenSummaryBtn: '.q-menu > .descriptor [data-cy="openSummaryBtn"]',
|
||||
descriptorGoToSummaryBtn: '.q-menu .descriptor [data-cy="goToSummaryBtn"]',
|
||||
summaryTitle: '.summaryHeader',
|
||||
referenceInput: 'Reference_input',
|
||||
companySelect: 'Company_select',
|
||||
warehouseSelect: 'Warehouse_select',
|
||||
fileInput: 'VnDms_inputFile',
|
||||
importBtn: 'importBtn',
|
||||
addBtn: 'addButton',
|
||||
saveFormBtn: 'FormModelPopup_save',
|
||||
};
|
||||
|
||||
const data = {
|
||||
Reference: { val: 'FixedAsset:Peter Parker' },
|
||||
Company: { val: 'VNL', type: 'select' },
|
||||
Warehouse: { val: 'Warehouse One', type: 'select' },
|
||||
};
|
||||
|
||||
const updateData = {
|
||||
Reference: { val: 'FixedAsset:Peter Parker House' },
|
||||
Company: { val: 'CCs', type: 'select' },
|
||||
Warehouse: { val: 'Warehouse Two', type: 'select' },
|
||||
};
|
||||
|
||||
const workerSummaryUrlRegex = /worker\/\d+\/summary/;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.viewport(1920, 1080);
|
||||
cy.login('developer');
|
||||
cy.visit(`/#/fixed-asset/1/dms`);
|
||||
});
|
||||
|
||||
it('Should create new DMS', () => {
|
||||
cy.dataCy(selectors.addBtn).click();
|
||||
cy.fillInForm(data);
|
||||
cy.dataCy(selectors.fileInput).selectFile('test/cypress/fixtures/image.jpg', {
|
||||
force: true,
|
||||
});
|
||||
cy.dataCy(selectors.saveFormBtn).click();
|
||||
cy.checkNotification('Data saved');
|
||||
});
|
||||
|
||||
/*
|
||||
TODO: #8946 REDMINE
|
||||
*/
|
||||
it.skip('Should download DMS', () => {
|
||||
let fileName;
|
||||
cy.get(selectors.firstRowId)
|
||||
.invoke('text')
|
||||
.then((label) => {
|
||||
label = label.trim();
|
||||
fileName = `${label}.jpg`;
|
||||
});
|
||||
cy.intercept('GET', /\/api\/dms\/\d+\/downloadFile/).as('download');
|
||||
cy.get(selectors.firstRowDownloadBtn).click();
|
||||
|
||||
cy.wait('@download').then((interception) => {
|
||||
expect(interception.response.statusCode).to.equal(200);
|
||||
expect(interception.response.headers['content-disposition']).to.contain(
|
||||
fileName,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Should edit DMS', () => {
|
||||
cy.get(selectors.firstRowEditBtn).click();
|
||||
cy.fillInForm(updateData);
|
||||
cy.dataCy(selectors.saveFormBtn).click();
|
||||
cy.checkNotification('Data saved');
|
||||
cy.validateContent(selectors.firstRowReference, updateData.Reference.val);
|
||||
});
|
||||
|
||||
it('Should delete DMS', () => {
|
||||
cy.get(selectors.firstRowDeleteBtn).click();
|
||||
cy.clickConfirm();
|
||||
cy.checkNotification('Data deleted');
|
||||
cy.validateContent(selectors.firstRowReference, 'FixedAsset: Laser');
|
||||
});
|
||||
|
||||
describe('Worker pop-ups', () => {
|
||||
it('Should redirect to the worker summary from worker descriptor pop-up', () => {
|
||||
cy.get(selectors.lastRowWorkerLink)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.invoke('text')
|
||||
.then((workerName) => {
|
||||
workerName = workerName.trim();
|
||||
cy.get(selectors.descriptorGoToSummaryBtn).click();
|
||||
cy.location().should('match', workerSummaryUrlRegex);
|
||||
cy.containContent(selectors.descriptorTitle, workerName);
|
||||
});
|
||||
});
|
||||
|
||||
it('Should redirect to the worker summary from summary pop-up from the worker descriptor pop-up', () => {
|
||||
cy.get(selectors.lastRowWorkerLink)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.invoke('text')
|
||||
.then((workerName) => {
|
||||
workerName = workerName.trim();
|
||||
cy.get(selectors.descriptorOpenSummaryBtn).click();
|
||||
cy.get(selectors.summaryGoToSummaryBtn).click();
|
||||
cy.location().should('match', workerSummaryUrlRegex);
|
||||
cy.containContent(selectors.descriptorTitle, workerName);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,86 @@
|
|||
describe('FixedAssetInvoice', () => {
|
||||
const getLinkSelector = (colField) =>
|
||||
`tr:first-child > [data-col-field="${colField}"] > .no-padding > .link`;
|
||||
const selectors = {
|
||||
firstRowSupplier: getLinkSelector('supplierFk'),
|
||||
firstRowInvoice: getLinkSelector('supplierRef'),
|
||||
descriptorSupplierTitle: '[data-cy="vnDescriptor_description"]',
|
||||
descriptorInvoiceInTitle: '[data-cy="vnDescriptor_title"]',
|
||||
descriptorOpenSummaryBtn: '.q-menu > .descriptor [data-cy="openSummaryBtn"]',
|
||||
descriptorGoToSummaryBtn: '.q-menu > .descriptor [data-cy="goToSummaryBtn"]',
|
||||
summaryGoToSummaryBtn: '.summaryHeader [data-cy="goToSummaryBtn"]',
|
||||
unassignBtn: 'tableAction-0',
|
||||
};
|
||||
|
||||
const supplierSummaryUrlRegex = /supplier\/\d+\/summary/;
|
||||
const invoiceInSummaryUrlRegex = /invoice-in\/\d+\/summary/;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.viewport(1920, 1080);
|
||||
cy.login('administrative');
|
||||
cy.visit(`/#/fixed-asset/1/invoice`);
|
||||
});
|
||||
|
||||
it('Should assign a new invoice', () => {
|
||||
const data = {
|
||||
'Invoice nº': { val: '1243', type: 'select' },
|
||||
Amount: { val: '1000' },
|
||||
};
|
||||
cy.addBtnClick();
|
||||
cy.fillInForm(data);
|
||||
cy.dataCy('FormModelPopup_save').click();
|
||||
cy.checkNotification('Data created');
|
||||
});
|
||||
|
||||
it('Should unassign an invoice', () => {
|
||||
cy.dataCy(selectors.unassignBtn).last().click();
|
||||
cy.clickConfirm();
|
||||
cy.checkNotification('Unassigned invoice');
|
||||
});
|
||||
|
||||
describe('Supplier pop-ups', () => {
|
||||
it('Should redirect to the supplier summary from the supplier descriptor pop-up', () => {
|
||||
cy.checkRedirectionFromPopUp({
|
||||
selectorToClick: selectors.firstRowSupplier,
|
||||
steps: [selectors.descriptorGoToSummaryBtn],
|
||||
expectedUrlRegex: supplierSummaryUrlRegex,
|
||||
expectedTextSelector: selectors.descriptorSupplierTitle,
|
||||
});
|
||||
});
|
||||
|
||||
it('Should redirect to the supplier summary from summary pop-up from the supplier descriptor pop-up', () => {
|
||||
cy.checkRedirectionFromPopUp({
|
||||
selectorToClick: selectors.firstRowSupplier,
|
||||
steps: [
|
||||
selectors.descriptorOpenSummaryBtn,
|
||||
selectors.summaryGoToSummaryBtn,
|
||||
],
|
||||
expectedUrlRegex: supplierSummaryUrlRegex,
|
||||
expectedTextSelector: selectors.descriptorSupplierTitle,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invoice pop-ups', () => {
|
||||
it('Should redirect to the invoiceIn summary from the invoice descriptor pop-up', () => {
|
||||
cy.checkRedirectionFromPopUp({
|
||||
selectorToClick: selectors.firstRowInvoice,
|
||||
steps: [selectors.descriptorGoToSummaryBtn],
|
||||
expectedUrlRegex: invoiceInSummaryUrlRegex,
|
||||
expectedTextSelector: selectors.descriptorInvoiceInTitle,
|
||||
});
|
||||
});
|
||||
|
||||
it('Should redirect to the invoiceIn summary from summary pop-up from the invoice descriptor pop-up', () => {
|
||||
cy.checkRedirectionFromPopUp({
|
||||
selectorToClick: selectors.firstRowInvoice,
|
||||
steps: [
|
||||
selectors.descriptorOpenSummaryBtn,
|
||||
selectors.summaryGoToSummaryBtn,
|
||||
],
|
||||
expectedUrlRegex: invoiceInSummaryUrlRegex,
|
||||
expectedTextSelector: selectors.descriptorInvoiceInTitle,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
describe('FixedAssetList', () => {
|
||||
const selectors = {
|
||||
firstRow: 'tr:first-child > [data-col-field="description"]',
|
||||
descriptorTitle: '[data-cy="vnDescriptor_title"]',
|
||||
};
|
||||
|
||||
const summaryUrlRegex = /fixed-asset\/\d+\/summary/;
|
||||
|
||||
const data = {
|
||||
Id: { val: '123' },
|
||||
Value: { val: '100000' },
|
||||
Description: { val: 'F.R.I.D.A.Y.' },
|
||||
'Amort. start': { val: '1.5', type: 'date' },
|
||||
'Amort. end': { val: '1.10.21', type: 'date' },
|
||||
Subaccount: { val: '2800000000', type: 'select' },
|
||||
Endowment: { val: '6810000000', type: 'select' },
|
||||
'Element account': { val: '561.123' },
|
||||
'Amort. plan': { val: '1', type: 'select' },
|
||||
Group: { val: 'Avengers', type: 'select' },
|
||||
Location: { val: 'Stark tower', type: 'select' },
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.viewport(1920, 1080);
|
||||
cy.login('administrative');
|
||||
cy.visit('/#/fixed-asset/list');
|
||||
cy.typeSearchbar('{enter}');
|
||||
});
|
||||
|
||||
it('Should redirect to the fixed asset summary when clicking the row', () => {
|
||||
cy.get(selectors.firstRow)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.invoke('text')
|
||||
.then((name) => {
|
||||
name = name.trim();
|
||||
cy.location().should('match', summaryUrlRegex);
|
||||
cy.containContent(selectors.descriptorTitle, name);
|
||||
});
|
||||
});
|
||||
|
||||
it('Should create new fixed asset', () => {
|
||||
cy.addBtnClick();
|
||||
cy.fillInForm(data);
|
||||
cy.dataCy('FormModelPopup_save').should('be.visible').click();
|
||||
cy.checkNotification('Data created');
|
||||
cy.location().should('match', summaryUrlRegex);
|
||||
cy.containContent(selectors.descriptorTitle, data.Description.val);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,143 @@
|
|||
describe('FixedAsset summary', () => {
|
||||
const selectors = {
|
||||
summaryTitle: '.summaryHeader',
|
||||
descriptorTitle: '[data-cy="vnDescriptor_title"]',
|
||||
supplierDescriptorTitle: '[data-cy="vnDescriptor_description"]',
|
||||
descriptorOpenSummaryBtn: '.q-menu > .descriptor [data-cy="openSummaryBtn"]',
|
||||
descriptorGoToSummaryBtn: '.q-menu > .descriptor [data-cy="goToSummaryBtn"]',
|
||||
summaryGoToSummaryBtn: '.summaryHeader [data-cy="goToSummaryBtn"]',
|
||||
summaryBasicDataBlock1Link: 'a.link[data-cy="titleBasicDataBlock1"]',
|
||||
summaryBasicDataBlock2Link: 'a.link[data-cy="titleBasicDataBlock2"]',
|
||||
summaryInvoiceBlockLink: 'a.link[data-cy="titleInvoiceBlock"]',
|
||||
summaryDmsBlockLink: 'a.link[data-cy="titleDmsBlock"]',
|
||||
dmsFirstRowWorkerLink: '.summaryBody :nth-child(1) > :nth-child(7) .link',
|
||||
invoiceFirstRowSupplierLink:
|
||||
':nth-child(1) > :nth-child(2) > [data-cy="supplierLink"]',
|
||||
invoiceFirstRowSupplierRefLink:
|
||||
':nth-child(1) > :nth-child(3) > [data-cy="invoiceLink"]',
|
||||
basicDataIcon: 'FixedAssetBasicData-menu-item',
|
||||
invoiceIcon: 'FixedAssetInvoice-menu-item',
|
||||
dmsIcon: 'FixedAssetDms-menu-item',
|
||||
logIcon: 'FixedAssetLog-menu-item',
|
||||
};
|
||||
|
||||
const url = '/#/fixed-asset/1/summary';
|
||||
const basicDataUrlRegex = /fixed-asset\/1\/basic-data/;
|
||||
const invoiceUrlRegex = /fixed-asset\/1\/invoice/;
|
||||
const dmsUrlRegex = /fixed-asset\/1\/dms/;
|
||||
const logUrlRegex = /fixed-asset\/1\/history/;
|
||||
const workerSummaryUrlRegex = /worker\/\d+\/summary/;
|
||||
const supplierSummaryUrlRegex = /supplier\/\d+\/summary/;
|
||||
const invoiceSummaryUrlRegex = /invoice-in\/\d+\/summary/;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.viewport(1920, 1080);
|
||||
cy.login('administrative');
|
||||
cy.visit(url);
|
||||
});
|
||||
|
||||
it('Should redirect to the corresponding section when clicking on the icons in the left menu', () => {
|
||||
cy.dataCy(selectors.basicDataIcon).click();
|
||||
cy.location().should('match', basicDataUrlRegex);
|
||||
|
||||
cy.visit(url);
|
||||
cy.dataCy(selectors.invoiceIcon).click();
|
||||
cy.location().should('match', invoiceUrlRegex);
|
||||
|
||||
cy.visit(url);
|
||||
cy.dataCy(selectors.dmsIcon).click();
|
||||
cy.location().should('match', dmsUrlRegex);
|
||||
|
||||
cy.visit(url);
|
||||
cy.dataCy(selectors.logIcon).click();
|
||||
cy.location().should('match', logUrlRegex);
|
||||
});
|
||||
|
||||
it('Should redirect to fixed asset basic-data when clicking on basic-data title links', () => {
|
||||
cy.get(selectors.summaryBasicDataBlock1Link).click();
|
||||
cy.location().should('match', basicDataUrlRegex);
|
||||
cy.visit(url);
|
||||
cy.get(selectors.summaryBasicDataBlock2Link).click();
|
||||
cy.location().should('match', basicDataUrlRegex);
|
||||
cy.visit(url);
|
||||
});
|
||||
|
||||
it('Should redirect to fixed asset invoices when clicking on asigned invoices title', () => {
|
||||
cy.get(selectors.summaryInvoiceBlockLink).click();
|
||||
cy.location().should('match', invoiceUrlRegex);
|
||||
});
|
||||
|
||||
it('Should redirect to fixed asset DMS when clicking on file managment title', () => {
|
||||
cy.get(selectors.summaryDmsBlockLink).click();
|
||||
cy.location().should('match', dmsUrlRegex);
|
||||
});
|
||||
|
||||
describe('Worker pop-ups', () => {
|
||||
it('Should redirect to the worker summary from worker descriptor pop-up', () => {
|
||||
cy.checkRedirectionFromPopUp({
|
||||
selectorToClick: selectors.dmsFirstRowWorkerLink,
|
||||
steps: [selectors.descriptorGoToSummaryBtn],
|
||||
expectedUrlRegex: workerSummaryUrlRegex,
|
||||
expectedTextSelector: selectors.descriptorTitle,
|
||||
});
|
||||
});
|
||||
|
||||
it('Should redirect to the worker summary from summary pop-up from the worker descriptor pop-up', () => {
|
||||
cy.checkRedirectionFromPopUp({
|
||||
selectorToClick: selectors.dmsFirstRowWorkerLink,
|
||||
steps: [
|
||||
selectors.descriptorOpenSummaryBtn,
|
||||
selectors.summaryGoToSummaryBtn,
|
||||
],
|
||||
expectedUrlRegex: workerSummaryUrlRegex,
|
||||
expectedTextSelector: selectors.descriptorTitle,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Supplier pop-ups', () => {
|
||||
it('Should redirect to the Supplier summary from Supplier descriptor pop-up', () => {
|
||||
cy.checkRedirectionFromPopUp({
|
||||
selectorToClick: selectors.invoiceFirstRowSupplierLink,
|
||||
steps: [selectors.descriptorGoToSummaryBtn],
|
||||
expectedUrlRegex: supplierSummaryUrlRegex,
|
||||
expectedTextSelector: selectors.supplierDescriptorTitle,
|
||||
});
|
||||
});
|
||||
|
||||
it('Should redirect to the Supplier summary from summary pop-up from the Supplier descriptor pop-up', () => {
|
||||
cy.checkRedirectionFromPopUp({
|
||||
selectorToClick: selectors.invoiceFirstRowSupplierLink,
|
||||
steps: [
|
||||
selectors.descriptorOpenSummaryBtn,
|
||||
selectors.summaryGoToSummaryBtn,
|
||||
],
|
||||
expectedUrlRegex: supplierSummaryUrlRegex,
|
||||
expectedTextSelector: selectors.supplierDescriptorTitle,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invoice in pop-ups', () => {
|
||||
it('Should redirect to the invoice summary from invoice descriptor pop-up', () => {
|
||||
cy.checkRedirectionFromPopUp({
|
||||
selectorToClick: selectors.invoiceFirstRowSupplierRefLink,
|
||||
steps: [selectors.descriptorGoToSummaryBtn],
|
||||
expectedUrlRegex: invoiceSummaryUrlRegex,
|
||||
expectedTextSelector: selectors.descriptorTitle,
|
||||
});
|
||||
});
|
||||
|
||||
it('Should redirect to the invoice summary from summary pop-up from the invoice descriptor pop-up', () => {
|
||||
cy.checkRedirectionFromPopUp({
|
||||
selectorToClick: selectors.invoiceFirstRowSupplierRefLink,
|
||||
steps: [
|
||||
selectors.descriptorOpenSummaryBtn,
|
||||
selectors.summaryGoToSummaryBtn,
|
||||
],
|
||||
expectedUrlRegex: invoiceSummaryUrlRegex,
|
||||
expectedTextSelector: selectors.descriptorTitle,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
/// <reference types="cypress" />
|
||||
import moment from 'moment';
|
||||
describe('InvoiceInBasicData', { testIsolation: true }, () => {
|
||||
describe('InvoiceInBasicData', () => {
|
||||
const dialogInputs = '.q-dialog input';
|
||||
const getDocumentBtns = (opt) => `[data-cy="dms-buttons"] > :nth-child(${opt})`;
|
||||
const futureDate = moment().add(1, 'days').format('DD-MM-YYYY');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('invoiceInCorrective', { testIsolation: true }, () => {
|
||||
describe('invoiceInCorrective', () => {
|
||||
beforeEach(() => cy.login('administrative'));
|
||||
|
||||
it('should modify the invoice', () => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('InvoiceInDescriptor', { testIsolation: true }, () => {
|
||||
describe('InvoiceInDescriptor', () => {
|
||||
beforeEach(() => cy.login('administrative'));
|
||||
|
||||
describe('more options', () => {
|
||||
|
|
|
@ -24,7 +24,6 @@ describe('InvoiceOut list', () => {
|
|||
});
|
||||
|
||||
it.skip('should download all pdfs', () => {
|
||||
cy.get(columnCheckbox).click();
|
||||
cy.get(columnCheckbox).click();
|
||||
cy.dataCy('InvoiceOutDownloadPdfBtn').click();
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('InvoiceOut summary', { testIsolation: true }, () => {
|
||||
describe('InvoiceOut summary', () => {
|
||||
const transferInvoice = {
|
||||
Client: { val: 'employee', type: 'select' },
|
||||
Type: { val: 'Error in customer data', type: 'select' },
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('ItemBarcodes', { testIsolation: true }, () => {
|
||||
describe('ItemBarcodes', () => {
|
||||
beforeEach(() => {
|
||||
cy.login('developer');
|
||||
cy.visit(`/#/item/1/barcode`);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('Item summary', { testIsolation: true }, () => {
|
||||
describe('Item summary', () => {
|
||||
beforeEach(() => {
|
||||
cy.login('developer');
|
||||
cy.visit(`/#/item/1/summary`);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('Item tag', { testIsolation: true }, () => {
|
||||
describe('Item tag', () => {
|
||||
beforeEach(() => {
|
||||
cy.login('developer');
|
||||
cy.visit(`/#/item/1/tags`);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('Login', { testIsolation: true }, () => {
|
||||
describe('Login', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/#/login');
|
||||
cy.get('#switchLanguage').click();
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('Logout', { testIsolation: true }, () => {
|
||||
describe('Logout', () => {
|
||||
beforeEach(() => {
|
||||
cy.login('developer');
|
||||
cy.visit(`/#/dashboard`);
|
||||
cy.waitForElement('.q-page', 6000);
|
||||
});
|
||||
|
||||
describe('by user', () => {
|
||||
it('should logout', () => {
|
||||
cy.get('#user').click();
|
||||
cy.get('#logout').click();
|
||||
});
|
||||
|
||||
it('should throw session expired error if token has expired or is not valid during navigation', () => {
|
||||
});
|
||||
describe('not user', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('GET', '**StarredModules**', {
|
||||
statusCode: 401,
|
||||
body: {
|
||||
|
@ -24,9 +25,13 @@ describe('Logout', { testIsolation: true }, () => {
|
|||
},
|
||||
statusMessage: 'AUTHORIZATION_REQUIRED',
|
||||
}).as('badRequest');
|
||||
});
|
||||
|
||||
it('when token not exists', () => {
|
||||
cy.get('.q-list').should('be.visible').first().should('be.visible').click();
|
||||
cy.wait('@badRequest');
|
||||
|
||||
cy.checkNotification('Your session has expired. Please log in again');
|
||||
cy.checkNotification('Authorization Required');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('Monitor Tickets Table', { testIsolation: true }, () => {
|
||||
describe('Monitor Tickets Table', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport(1920, 1080);
|
||||
cy.login('salesPerson');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('OrderCatalog', { testIsolation: true }, () => {
|
||||
describe.skip('OrderCatalog', () => {
|
||||
beforeEach(() => {
|
||||
cy.login('developer');
|
||||
cy.viewport(1920, 1080);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('OrderList', { testIsolation: true }, () => {
|
||||
describe('OrderList', () => {
|
||||
const clientCreateSelect = '#formModel [data-cy="Client_select"]';
|
||||
const addressCreateSelect = '#formModel [data-cy="Address_select"]';
|
||||
const agencyCreateSelect = '#formModel [data-cy="Agency_select"]';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('Cmr list', { testIsolation: true }, () => {
|
||||
describe('Cmr list', () => {
|
||||
const getLinkSelector = (colField) =>
|
||||
`tr:first-child > [data-col-field="${colField}"] > .no-padding > .link`;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe.skip('RoadMap', () => {
|
||||
describe('RoadMap', () => {
|
||||
const getSelector = (colField) =>
|
||||
`tr:last-child > [data-col-field="${colField}"] > .no-padding`;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('RouteAutonomous', { testIsolation: true }, () => {
|
||||
describe('RouteAutonomous', () => {
|
||||
const getLinkSelector = (colField, link = true) =>
|
||||
`tr:first-child > [data-col-field="${colField}"] > .no-padding${
|
||||
link ? ' > .link' : ''
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('Route', { testIsolation: true }, () => {
|
||||
describe('Route', () => {
|
||||
const getSelector = (colField) =>
|
||||
`tr:last-child > [data-col-field="${colField}"] > .no-padding > .link`;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('Vehicle DMS', { testIsolation: true }, () => {
|
||||
describe('Vehicle DMS', () => {
|
||||
const getSelector = (btnPosition) =>
|
||||
`tr:last-child > .text-right > .no-wrap > :nth-child(${btnPosition}) > .q-btn > .q-btn__content > .q-icon`;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/// <reference types="cypress" />
|
||||
const firstRow = 'tbody > :nth-child(1)';
|
||||
|
||||
describe('TicketSale', { testIsolation: true }, () => {
|
||||
describe('TicketSale', () => {
|
||||
describe('Ticket #23', () => {
|
||||
beforeEach(() => {
|
||||
cy.login('claimManager');
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
describe('Travel Extracommunity', () => {
|
||||
it('Should show travels', () => {
|
||||
cy.login('logistic');
|
||||
cy.visit(`/#/travel/extra-community`);
|
||||
cy.get('.q-page').should('be.visible');
|
||||
});
|
||||
|
||||
it('Should show travels when user is supplier', () => {
|
||||
cy.login('petterparker');
|
||||
cy.visit(`/#/travel/extra-community`);
|
||||
cy.get('[data-cy="vnFilterPanelChip_continent"] > .q-chip__icon--remove').click();
|
||||
cy.dataCy('extra-community-cargoSupplierNickname').each(($el) => {
|
||||
cy.wrap($el).should('contain.text', 'The farmer');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('UserPanel', { testIsolation: true }, () => {
|
||||
describe('UserPanel', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport(1280, 720);
|
||||
cy.login('developer');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('VnBreadcrumbs', { testIsolation: true }, () => {
|
||||
describe('VnBreadcrumbs', () => {
|
||||
const lastBreadcrumb = '.q-breadcrumbs--last > .q-breadcrumbs__el';
|
||||
beforeEach(() => {
|
||||
cy.login('developer');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { randomNumber, randomString } = require('../../support');
|
||||
|
||||
describe('VnLocation', { testIsolation: true }, () => {
|
||||
describe('VnLocation', () => {
|
||||
const locationOptions = '[role="listbox"] > div.q-virtual-scroll__content > .q-item';
|
||||
const dialogInputs = '.q-dialog label input';
|
||||
const createLocationButton = '.q-form > .q-card > .vn-row:nth-child(6) .--add-icon';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('VnLog', { testIsolation: true }, () => {
|
||||
describe('VnLog', () => {
|
||||
beforeEach(() => {
|
||||
cy.login('developer');
|
||||
cy.visit(`/#/claim/${1}/log`);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('WorkerCreate', { testIsolation: true }, () => {
|
||||
describe('WorkerCreate', () => {
|
||||
const externalRadio = '.q-radio:nth-child(2)';
|
||||
const developerBossId = 120;
|
||||
const payMethodCross =
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('WorkerManagement', { testIsolation: true }, () => {
|
||||
describe('WorkerManagement', () => {
|
||||
const nif = '12091201A';
|
||||
const searchButton = '.q-scrollarea__content > .q-btn--standard > .q-btn__content';
|
||||
const url = '/#/worker/management';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('WorkerNotificationsManager', { testIsolation: true }, () => {
|
||||
describe('WorkerNotificationsManager', () => {
|
||||
const salesPersonId = 18;
|
||||
const developerId = 9;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('ZoneBasicData', { testIsolation: true }, () => {
|
||||
describe('ZoneBasicData', () => {
|
||||
const priceBasicData = '[data-cy="ZoneBasicDataPrice"]';
|
||||
const saveBtn = '.q-btn-group > .q-btn--standard';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('ZoneCalendar', { testIsolation: true }, () => {
|
||||
describe('ZoneCalendar', () => {
|
||||
const addEventBtn = '.q-page-sticky > div > .q-btn';
|
||||
const submitBtn = '.q-mt-lg > .q-btn--standard';
|
||||
const deleteBtn = 'ZoneEventsPanelDeleteBtn';
|
||||
|
@ -47,20 +47,4 @@ describe('ZoneCalendar', { testIsolation: true }, () => {
|
|||
cy.dataCy('ZoneEventExclusionDeleteBtn').click();
|
||||
cy.dataCy('VnConfirm_confirm').click();
|
||||
});
|
||||
|
||||
it(
|
||||
'should not exclude an event if there are tickets for that zone and day',
|
||||
{ testIsoaltion: true },
|
||||
() => {
|
||||
cy.visit(`/#/zone/3/events`);
|
||||
cy.get('.q-mb-sm > .q-radio__inner').click();
|
||||
cy.get(
|
||||
'.q-current-day > .q-calendar-month__day--content > [data-cy="ZoneCalendarDay"]',
|
||||
).click();
|
||||
cy.get('.q-mt-lg > .q-btn--standard').click();
|
||||
cy.checkNotification(
|
||||
'Can not close this zone because there are tickets programmed for that day',
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe('ZoneCreate', { testIsolation: true }, () => {
|
||||
describe('ZoneCreate', () => {
|
||||
const data = {
|
||||
Name: { val: 'Zone pickup D' },
|
||||
Price: { val: '3' },
|
||||
|
|
|
@ -26,8 +26,27 @@ describe('ZoneDeliveryDays', () => {
|
|||
});
|
||||
}).as('events');
|
||||
|
||||
cy.selectOption('[data-cy="ZoneDeliveryDaysPostcodeSelect"]', postcode);
|
||||
cy.selectOption('[data-cy="ZoneDeliveryDaysAgencySelect"]', agency);
|
||||
cy.dataCy('ZoneDeliveryDaysPostcodeSelect').type(postcode);
|
||||
cy.get('.q-menu .q-item').contains(postcode).click();
|
||||
cy.get('.q-menu').then(($menu) => {
|
||||
if ($menu.is(':visible')) {
|
||||
cy.get('[data-cy="ZoneDeliveryDaysPostcodeSelect"]')
|
||||
.as('focusedElement')
|
||||
.focus();
|
||||
cy.get('@focusedElement').blur();
|
||||
}
|
||||
});
|
||||
|
||||
cy.dataCy('ZoneDeliveryDaysAgencySelect').type(agency);
|
||||
cy.get('.q-menu .q-item').contains(agency).click();
|
||||
cy.get('.q-menu').then(($menu) => {
|
||||
if ($menu.is(':visible')) {
|
||||
cy.get('[data-cy="ZoneDeliveryDaysAgencySelect"]')
|
||||
.as('focusedElement')
|
||||
.focus();
|
||||
cy.get('@focusedElement').blur();
|
||||
}
|
||||
});
|
||||
|
||||
cy.get(submitForm).click();
|
||||
cy.wait('@events').then((interception) => {
|
||||
|
|
|
@ -392,7 +392,6 @@ Cypress.Commands.add('validateContent', (selector, expectedValue) => {
|
|||
|
||||
Cypress.Commands.add('containContent', (selector, expectedValue) => {
|
||||
cy.get(selector)
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.then((text) => {
|
||||
expect(text).to.include(expectedValue);
|
||||
|
|
Loading…
Reference in New Issue