|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "salix-front",
|
||||
"version": "24.36.0",
|
||||
"version": "24.40.0",
|
||||
"description": "Salix frontend",
|
||||
"productName": "Salix",
|
||||
"author": "Verdnatura",
|
||||
|
@ -62,4 +62,4 @@
|
|||
"vite": "^5.1.4",
|
||||
"vitest": "^0.31.1"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,16 +9,16 @@ export default {
|
|||
const keyBindingMap = routes
|
||||
.filter((route) => route.meta.keyBinding)
|
||||
.reduce((map, route) => {
|
||||
map[route.meta.keyBinding.toLowerCase()] = route.path;
|
||||
map['Key' + route.meta.keyBinding.toUpperCase()] = route.path;
|
||||
return map;
|
||||
}, {});
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
const { ctrlKey, altKey, key } = event;
|
||||
const { ctrlKey, altKey, code } = event;
|
||||
|
||||
if (ctrlKey && altKey && keyBindingMap[key] && !isNotified) {
|
||||
if (ctrlKey && altKey && keyBindingMap[code] && !isNotified) {
|
||||
event.preventDefault();
|
||||
router.push(keyBindingMap[key]);
|
||||
router.push(keyBindingMap[code]);
|
||||
isNotified = true;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import FormModelPopup from './FormModelPopup.vue';
|
||||
|
||||
const emit = defineEmits(['onDataSaved']);
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<FormModelPopup
|
||||
url-create="Expenses"
|
||||
model="Expense"
|
||||
:title="t('New expense')"
|
||||
:form-initial-data="{ id: null, isWithheld: false, name: null }"
|
||||
@on-data-saved="emit('onDataSaved', $event)"
|
||||
>
|
||||
<template #form-inputs="{ data, validate }">
|
||||
<VnRow>
|
||||
<VnInput
|
||||
:label="`${t('globals.code')}`"
|
||||
v-model="data.id"
|
||||
:required="true"
|
||||
:rules="validate('expense.code')"
|
||||
/>
|
||||
<QCheckbox
|
||||
dense
|
||||
size="sm"
|
||||
:label="`${t('It\'s a withholding')}`"
|
||||
v-model="data.isWithheld"
|
||||
:rules="validate('expense.isWithheld')"
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnInput
|
||||
:label="`${t('globals.description')}`"
|
||||
v-model="data.name"
|
||||
:required="true"
|
||||
:rules="validate('expense.description')"
|
||||
/>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModelPopup>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
New expense: Nuevo gasto
|
||||
It's a withholding: Es una retención
|
||||
</i18n>
|
|
@ -105,7 +105,7 @@ async function setProvince(id, data) {
|
|||
option-label="name"
|
||||
option-value="id"
|
||||
:rules="validate('postcode.city')"
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
|
||||
:emit-value="false"
|
||||
clearable
|
||||
>
|
||||
|
|
|
@ -189,11 +189,11 @@ async function saveChanges(data) {
|
|||
});
|
||||
}
|
||||
|
||||
async function insert() {
|
||||
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 }, $props.dataRequired));
|
||||
formData.value.push(Object.assign({ $index }, pushData));
|
||||
hasChanges.value = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -159,8 +159,8 @@ const removeTag = (index, params, search) => {
|
|||
/>
|
||||
<VnFilterPanel
|
||||
:data-key="props.dataKey"
|
||||
:expr-builder="exprBuilder"
|
||||
:custom-tags="customTags"
|
||||
:expr-builder="props.exprBuilder"
|
||||
:custom-tags="props.customTags"
|
||||
>
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<strong v-if="tag.label === 'categoryFk'">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { onMounted, ref, reactive } from 'vue';
|
||||
import { onMounted, watch, ref, reactive } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { QSeparator, useQuasar } from 'quasar';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
@ -29,6 +29,15 @@ onMounted(async () => {
|
|||
getRoutes();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.matched,
|
||||
() => {
|
||||
items.value = [];
|
||||
getRoutes();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
function findMatches(search, item) {
|
||||
const matches = [];
|
||||
function findRoute(search, item) {
|
||||
|
|
|
@ -33,7 +33,12 @@ const itemComputed = computed(() => {
|
|||
<QItemSection avatar v-if="!itemComputed.icon">
|
||||
<QIcon name="disabled_by_default" />
|
||||
</QItemSection>
|
||||
<QItemSection>{{ t(itemComputed.title) }}</QItemSection>
|
||||
<QItemSection>
|
||||
{{ t(itemComputed.title) }}
|
||||
<QTooltip>
|
||||
{{ 'Ctrl + Alt + ' + item?.keyBinding?.toUpperCase() }}
|
||||
</QTooltip>
|
||||
</QItemSection>
|
||||
<QItemSection side>
|
||||
<slot name="side" :item="itemComputed" />
|
||||
</QItemSection>
|
||||
|
|
|
@ -33,6 +33,7 @@ const invoiceCorrectionTypesOptions = ref([]);
|
|||
const refund = async () => {
|
||||
const params = {
|
||||
id: invoiceParams.id,
|
||||
withWarehouse: invoiceParams.inheritWarehouse,
|
||||
cplusRectificationTypeFk: invoiceParams.cplusRectificationTypeFk,
|
||||
siiTypeInvoiceOutFk: invoiceParams.siiTypeInvoiceOutFk,
|
||||
invoiceCorrectionTypeFk: invoiceParams.invoiceCorrectionTypeFk,
|
||||
|
|
|
@ -38,7 +38,7 @@ async function onProvinceCreated(_, data) {
|
|||
hide-selected
|
||||
v-model="provinceFk"
|
||||
:rules="validate && validate('postcode.provinceFk')"
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
:acls="[{ model: 'Province', props: '*', accessType: 'WRITE' }]"
|
||||
>
|
||||
<template #option="{ itemProps, opt }">
|
||||
<QItem v-bind="itemProps">
|
||||
|
|
|
@ -49,6 +49,10 @@ const $props = defineProps({
|
|||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
createAsDialog: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
cardClass: {
|
||||
type: String,
|
||||
default: 'flex-one',
|
||||
|
@ -85,6 +89,10 @@ const $props = defineProps({
|
|||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
crudModel: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
tableHeight: {
|
||||
type: String,
|
||||
default: '90vh',
|
||||
|
@ -284,10 +292,17 @@ function parseOrder(urlOrders) {
|
|||
|
||||
const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
|
||||
defineExpose({
|
||||
create: createForm,
|
||||
reload,
|
||||
redirect: redirectFn,
|
||||
selected,
|
||||
CrudModelRef,
|
||||
});
|
||||
|
||||
function handleOnDataSaved(_, res) {
|
||||
if (_.onDataSaved) _.onDataSaved(this);
|
||||
else $props.create.onDataSaved(_);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<QDrawer
|
||||
|
@ -374,6 +389,7 @@ defineExpose({
|
|||
@virtual-scroll="
|
||||
(event) =>
|
||||
event.index > rows.length - 2 &&
|
||||
($props.crudModel?.paginate ?? true) &&
|
||||
CrudModelRef.vnPaginateRef.paginate()
|
||||
"
|
||||
@row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
|
||||
|
@ -482,6 +498,7 @@ defineExpose({
|
|||
>
|
||||
<QBtn
|
||||
v-for="(btn, index) of col.actions"
|
||||
v-show="btn.show ? btn.show(row) : true"
|
||||
:key="index"
|
||||
:title="btn.title"
|
||||
:icon="btn.icon"
|
||||
|
@ -616,8 +633,17 @@ defineExpose({
|
|||
</QTable>
|
||||
</template>
|
||||
</CrudModel>
|
||||
<QPageSticky v-if="create" :offset="[20, 20]" style="z-index: 2">
|
||||
<QBtn @click="showForm = !showForm" color="primary" fab icon="add" shortcut="+" />
|
||||
<QPageSticky :offset="[20, 20]" style="z-index: 2">
|
||||
<QBtn
|
||||
@click="
|
||||
() =>
|
||||
createAsDialog ? (showForm = !showForm) : handleOnDataSaved(create)
|
||||
"
|
||||
color="primary"
|
||||
fab
|
||||
icon="add"
|
||||
shortcut="+"
|
||||
/>
|
||||
<QTooltip>
|
||||
{{ createForm.title }}
|
||||
</QTooltip>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import { onBeforeMount, computed } from 'vue';
|
||||
import { useRoute, onBeforeRouteUpdate } from 'vue-router';
|
||||
import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import useCardSize from 'src/composables/useCardSize';
|
||||
|
@ -17,10 +17,12 @@ const props = defineProps({
|
|||
filterPanel: { type: Object, default: undefined },
|
||||
searchDataKey: { type: String, default: undefined },
|
||||
searchbarProps: { type: Object, default: undefined },
|
||||
redirectOnError: { type: Boolean, default: false },
|
||||
});
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const url = computed(() => {
|
||||
if (props.baseUrl) return `${props.baseUrl}/${route.params.id}`;
|
||||
return props.customUrl;
|
||||
|
@ -35,8 +37,12 @@ const arrayData = useArrayData(props.dataKey, {
|
|||
});
|
||||
|
||||
onBeforeMount(async () => {
|
||||
if (!props.baseUrl) arrayData.store.filter.where = { id: route.params.id };
|
||||
await arrayData.fetch({ append: false, updateRouter: false });
|
||||
try {
|
||||
if (!props.baseUrl) arrayData.store.filter.where = { id: route.params.id };
|
||||
await arrayData.fetch({ append: false, updateRouter: false });
|
||||
} catch (e) {
|
||||
router.push({ name: 'WorkerList' });
|
||||
}
|
||||
});
|
||||
|
||||
if (props.baseUrl) {
|
||||
|
|
|
@ -135,6 +135,7 @@ const columns = computed(() => [
|
|||
field: 'hasFile',
|
||||
label: t('globals.original'),
|
||||
name: 'hasFile',
|
||||
toolTip: t('The documentation is available in paper form'),
|
||||
component: QCheckbox,
|
||||
props: (prop) => ({
|
||||
disable: true,
|
||||
|
@ -297,6 +298,14 @@ defineExpose({
|
|||
row-key="clientFk"
|
||||
:grid="$q.screen.lt.sm"
|
||||
>
|
||||
<template #header="props">
|
||||
<QTr :props="props" class="bg">
|
||||
<QTh v-for="col in props.cols" :key="col.name" :props="props">
|
||||
<QTooltip v-if="col.toolTip">{{ col.toolTip }}</QTooltip
|
||||
>{{ col.label }}
|
||||
</QTh>
|
||||
</QTr>
|
||||
</template>
|
||||
<template #body-cell="props">
|
||||
<QTd :props="props">
|
||||
<QTr :props="props">
|
||||
|
@ -397,8 +406,10 @@ defineExpose({
|
|||
<i18n>
|
||||
en:
|
||||
contentTypesInfo: Allowed file types {allowedContentTypes}
|
||||
The documentation is available in paper form: The documentation is available in paper form
|
||||
es:
|
||||
contentTypesInfo: Tipos de archivo permitidos {allowedContentTypes}
|
||||
Generate identifier for original file: Generar identificador para archivo original
|
||||
Upload file: Subir fichero
|
||||
the documentation is available in paper form: Se tiene la documentación en papel
|
||||
</i18n>
|
||||
|
|
|
@ -92,7 +92,6 @@ const mixinRules = [
|
|||
<slot name="prepend" />
|
||||
</template>
|
||||
<template #append>
|
||||
<slot name="append" v-if="$slots.append && !$attrs.disabled" />
|
||||
<QIcon
|
||||
name="close"
|
||||
size="xs"
|
||||
|
@ -104,6 +103,7 @@ const mixinRules = [
|
|||
}
|
||||
"
|
||||
></QIcon>
|
||||
<slot name="append" v-if="$slots.append && !$attrs.disabled" />
|
||||
<QIcon v-if="info" name="info">
|
||||
<QTooltip max-width="350px">
|
||||
{{ info }}
|
||||
|
@ -119,3 +119,8 @@ const mixinRules = [
|
|||
es:
|
||||
inputMin: Debe ser mayor a {value}
|
||||
</i18n>
|
||||
<style lang="scss">
|
||||
.q-field__append {
|
||||
padding-inline: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -9,6 +9,10 @@ const $props = defineProps({
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showEvent: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
@ -94,6 +98,7 @@ watch(
|
|||
:class="{ required: $attrs.required }"
|
||||
:rules="$attrs.required ? [requiredFieldRule] : null"
|
||||
:clearable="false"
|
||||
@click="isPopupOpen = true"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon
|
||||
|
@ -111,6 +116,7 @@ watch(
|
|||
"
|
||||
/>
|
||||
<QIcon
|
||||
v-if="showEvent"
|
||||
name="event"
|
||||
class="cursor-pointer"
|
||||
@click="isPopupOpen = !isPopupOpen"
|
||||
|
@ -130,6 +136,7 @@ watch(
|
|||
v-model="popupDate"
|
||||
:landscape="true"
|
||||
:today-btn="true"
|
||||
:options="$attrs.options"
|
||||
@update:model-value="
|
||||
(date) => {
|
||||
formattedDate = date;
|
||||
|
|
|
@ -14,6 +14,7 @@ import VnJsonValue from '../common/VnJsonValue.vue';
|
|||
import FetchData from '../FetchData.vue';
|
||||
import VnSelect from './VnSelect.vue';
|
||||
import VnUserLink from '../ui/VnUserLink.vue';
|
||||
import VnPaginate from '../ui/VnPaginate.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const validationsStore = useValidator();
|
||||
|
@ -66,9 +67,10 @@ const filter = {
|
|||
},
|
||||
},
|
||||
],
|
||||
where: { and: [{ originFk: route.params.id }] },
|
||||
};
|
||||
|
||||
const workers = ref();
|
||||
const paginate = ref();
|
||||
const actions = ref();
|
||||
const changeInput = ref();
|
||||
const searchInput = ref();
|
||||
|
@ -235,9 +237,7 @@ async function openPointRecord(id, modelLog) {
|
|||
const locale = validations[modelLog.model]?.locale || {};
|
||||
pointRecord.value = parseProps(propNames, locale, data);
|
||||
}
|
||||
async function setLogTree() {
|
||||
filter.where = { and: [{ originFk: route.params.id }] };
|
||||
const { data } = await getLogs(filter);
|
||||
async function setLogTree(data) {
|
||||
logTree.value = getLogTree(data);
|
||||
}
|
||||
|
||||
|
@ -266,15 +266,7 @@ async function applyFilter() {
|
|||
filter.where.and.push(selectedFilters.value);
|
||||
}
|
||||
|
||||
const { data } = await getLogs(filter);
|
||||
|
||||
logTree.value = getLogTree(data);
|
||||
}
|
||||
|
||||
async function getLogs(filter) {
|
||||
return axios.get(props.url ?? `${props.model}Logs`, {
|
||||
params: { filter: JSON.stringify(filter) },
|
||||
});
|
||||
paginate.value.fetch(filter);
|
||||
}
|
||||
|
||||
function setDate(type) {
|
||||
|
@ -377,8 +369,6 @@ async function clearFilter() {
|
|||
await applyFilter();
|
||||
}
|
||||
|
||||
setLogTree();
|
||||
|
||||
onUnmounted(() => {
|
||||
stateStore.rightDrawer = false;
|
||||
});
|
||||
|
@ -391,16 +381,6 @@ watch(
|
|||
);
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
:url="`${props.model}Logs/${route.params.id}/editors`"
|
||||
:filter="{
|
||||
fields: ['id', 'nickname', 'name', 'image'],
|
||||
order: 'nickname',
|
||||
limit: 30,
|
||||
}"
|
||||
@on-fetch="(data) => (workers = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
:url="`${props.model}Logs/${route.params.id}/models`"
|
||||
:filter="{ order: ['changedModel'] }"
|
||||
|
@ -418,231 +398,283 @@ watch(
|
|||
"
|
||||
auto-load
|
||||
/>
|
||||
<div
|
||||
class="column items-center logs origin-log q-mt-md"
|
||||
v-for="(originLog, originLogIndex) in logTree"
|
||||
:key="originLogIndex"
|
||||
<VnPaginate
|
||||
ref="paginate"
|
||||
:data-key="`${model}Log`"
|
||||
:url="`${model}Logs`"
|
||||
:filter="filter"
|
||||
:skeleton="false"
|
||||
auto-load
|
||||
@on-fetch="setLogTree"
|
||||
>
|
||||
<QItem class="origin-info items-center q-my-md" v-if="logTree.length > 1">
|
||||
<h6 class="origin-id text-grey">
|
||||
{{ useCapitalize(validations[props.model].locale.name) }}
|
||||
#{{ originLog.originFk }}
|
||||
</h6>
|
||||
<div class="line bg-grey"></div>
|
||||
</QItem>
|
||||
<div
|
||||
class="user-log q-mb-sm"
|
||||
v-for="(userLog, userIndex) in originLog.logs"
|
||||
:key="userIndex"
|
||||
>
|
||||
<div class="timeline">
|
||||
<div class="user-avatar">
|
||||
<VnUserLink :worker-id="userLog?.user?.id">
|
||||
<template #link>
|
||||
<VnAvatar
|
||||
:class="{ 'cursor-pointer': userLog?.user?.id }"
|
||||
:worker-id="userLog?.user?.id"
|
||||
:title="userLog?.user?.nickname"
|
||||
:show-letter="!userLog?.user"
|
||||
size="lg"
|
||||
/>
|
||||
</template>
|
||||
</VnUserLink>
|
||||
</div>
|
||||
<div class="arrow bg-panel" v-if="byRecord"></div>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<QList class="user-changes" v-if="userLog">
|
||||
<QItem
|
||||
class="model-log column q-px-none q-py-xs"
|
||||
v-for="(modelLog, modelLogIndex) in userLog.logs"
|
||||
:key="modelLogIndex"
|
||||
<template #body>
|
||||
<div
|
||||
class="column items-center logs origin-log q-mt-md"
|
||||
v-for="(originLog, originLogIndex) in logTree"
|
||||
:key="originLogIndex"
|
||||
>
|
||||
<QItem class="origin-info items-center q-my-md" v-if="logTree.length > 1">
|
||||
<h6 class="origin-id text-grey">
|
||||
{{ useCapitalize(validations[props.model].locale.name) }}
|
||||
#{{ originLog.originFk }}
|
||||
</h6>
|
||||
<div class="line bg-grey"></div>
|
||||
</QItem>
|
||||
<div
|
||||
class="user-log q-mb-sm"
|
||||
v-for="(userLog, userIndex) in originLog.logs"
|
||||
:key="userIndex"
|
||||
>
|
||||
<QItemSection>
|
||||
<QItemLabel class="model-info q-mb-xs" v-if="!byRecord">
|
||||
<QChip
|
||||
dense
|
||||
size="md"
|
||||
class="model-name q-mr-xs text-white"
|
||||
v-if="
|
||||
!(modelLog.changedModel && modelLog.changedModelId) &&
|
||||
modelLog.model
|
||||
"
|
||||
:style="{
|
||||
backgroundColor: useColor(modelLog.model),
|
||||
}"
|
||||
:title="`${modelLog.model} #${modelLog.id}`"
|
||||
>
|
||||
{{ t(modelLog.modelI18n) }}
|
||||
</QChip>
|
||||
|
||||
<span
|
||||
class="model-id q-mr-xs"
|
||||
v-if="modelLog.summaryId"
|
||||
v-text="`#${modelLog.summaryId}`"
|
||||
/>
|
||||
<span
|
||||
class="model-value"
|
||||
:title="modelLog.showValue"
|
||||
v-text="modelLog.showValue"
|
||||
/>
|
||||
<QBtn
|
||||
flat
|
||||
round
|
||||
color="grey"
|
||||
class="q-mr-xs q-ml-auto"
|
||||
size="sm"
|
||||
icon="filter_alt"
|
||||
:title="t('recordChanges')"
|
||||
@click.stop="filterByRecord(modelLog)"
|
||||
/>
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
<QItemSection>
|
||||
<QCard
|
||||
class="changes-log q-py-none q-mb-xs"
|
||||
v-for="(log, logIndex) in modelLog.logs"
|
||||
:key="logIndex"
|
||||
<div class="timeline">
|
||||
<div class="user-avatar">
|
||||
<VnUserLink :worker-id="userLog?.user?.id">
|
||||
<template #link>
|
||||
<VnAvatar
|
||||
:class="{ 'cursor-pointer': userLog?.user?.id }"
|
||||
:worker-id="userLog?.user?.id"
|
||||
:title="userLog?.user?.nickname"
|
||||
:show-letter="!userLog?.user"
|
||||
size="lg"
|
||||
/>
|
||||
</template>
|
||||
</VnUserLink>
|
||||
</div>
|
||||
<div class="arrow bg-panel" v-if="byRecord"></div>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<QList class="user-changes" v-if="userLog">
|
||||
<QItem
|
||||
class="model-log column q-px-none q-py-xs"
|
||||
v-for="(modelLog, modelLogIndex) in userLog.logs"
|
||||
:key="modelLogIndex"
|
||||
>
|
||||
<QCardSection class="change-info q-pa-none">
|
||||
<QItem
|
||||
class="q-px-sm q-py-xs justify-between items-center"
|
||||
>
|
||||
<div
|
||||
class="date text-grey text-caption q-mr-sm"
|
||||
:title="
|
||||
date.formatDate(
|
||||
log.creationDate,
|
||||
'DD/MM/YYYY hh:mm:ss'
|
||||
) ?? `date:'dd/MM/yyyy HH:mm:ss'`
|
||||
<QItemSection>
|
||||
<QItemLabel class="model-info q-mb-xs" v-if="!byRecord">
|
||||
<QChip
|
||||
dense
|
||||
size="md"
|
||||
class="model-name q-mr-xs text-white"
|
||||
v-if="
|
||||
!(
|
||||
modelLog.changedModel &&
|
||||
modelLog.changedModelId
|
||||
) && modelLog.model
|
||||
"
|
||||
:style="{
|
||||
backgroundColor: useColor(modelLog.model),
|
||||
}"
|
||||
:title="`${modelLog.model} #${modelLog.id}`"
|
||||
>
|
||||
{{ toRelativeDate(log.creationDate) }}
|
||||
</div>
|
||||
<div>
|
||||
<QBtn
|
||||
color="grey"
|
||||
class="pit"
|
||||
icon="preview"
|
||||
flat
|
||||
round
|
||||
:title="t('pointRecord')"
|
||||
padding="none"
|
||||
v-if="log.action != 'insert'"
|
||||
@click.stop="
|
||||
openPointRecord(log.id, modelLog)
|
||||
"
|
||||
>
|
||||
<QPopupProxy>
|
||||
<QCard v-if="pointRecord">
|
||||
<div
|
||||
class="header q-px-sm q-py-xs q-ma-none text-white text-bold bg-primary"
|
||||
>
|
||||
{{ modelLog.modelI18n }}
|
||||
<span v-if="modelLog.id"
|
||||
>#{{ modelLog.id }}</span
|
||||
>
|
||||
</div>
|
||||
<QCardSection
|
||||
class="change-detail q-pa-sm"
|
||||
>
|
||||
<QItem
|
||||
v-for="(
|
||||
value, index
|
||||
) in pointRecord"
|
||||
:key="index"
|
||||
class="q-pa-none"
|
||||
>
|
||||
<span
|
||||
class="json-field q-mr-xs text-grey"
|
||||
:title="value.name"
|
||||
>
|
||||
{{ value.nameI18n }}:
|
||||
</span>
|
||||
<VnJsonValue
|
||||
:value="value.val.val"
|
||||
/>
|
||||
</QItem>
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
</QPopupProxy>
|
||||
</QBtn>
|
||||
<QIcon
|
||||
class="action q-ml-xs"
|
||||
:class="actionsClass[log.action]"
|
||||
:name="actionsIcon[log.action]"
|
||||
:title="
|
||||
t(`actions.${actionsText[log.action]}`)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</QItem>
|
||||
</QCardSection>
|
||||
<QCardSection
|
||||
class="change-detail q-px-sm q-py-xs"
|
||||
:class="{ expanded: log.expand }"
|
||||
v-if="log.props.length || log.description"
|
||||
>
|
||||
<QIcon
|
||||
class="cursor-pointer q-mr-md"
|
||||
color="grey"
|
||||
name="expand_more"
|
||||
:title="t('globals.details')"
|
||||
size="sm"
|
||||
@click="log.expand = !log.expand"
|
||||
/>
|
||||
<span v-if="log.props.length" class="attributes">
|
||||
<span v-if="!log.expand" class="q-pa-none text-grey">
|
||||
<span
|
||||
v-for="(prop, propIndex) in log.props"
|
||||
:key="propIndex"
|
||||
class="basic-json"
|
||||
>
|
||||
<span class="json-field" :title="prop.name">
|
||||
{{ prop.nameI18n }}:
|
||||
</span>
|
||||
<VnJsonValue :value="prop.val.val" />
|
||||
<span v-if="propIndex < log.props.length - 1"
|
||||
>,
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
{{ t(modelLog.modelI18n) }}
|
||||
</QChip>
|
||||
|
||||
<span
|
||||
v-if="log.expand"
|
||||
class="expanded-json column q-pa-none"
|
||||
>
|
||||
<div
|
||||
v-for="(prop, prop2Index) in log.props"
|
||||
:key="prop2Index"
|
||||
class="q-pa-none text-grey"
|
||||
class="model-id q-mr-xs"
|
||||
v-if="modelLog.summaryId"
|
||||
v-text="`#${modelLog.summaryId}`"
|
||||
/>
|
||||
<span
|
||||
class="model-value"
|
||||
:title="modelLog.showValue"
|
||||
v-text="modelLog.showValue"
|
||||
/>
|
||||
<QBtn
|
||||
flat
|
||||
round
|
||||
color="grey"
|
||||
class="q-mr-xs q-ml-auto"
|
||||
size="sm"
|
||||
icon="filter_alt"
|
||||
:title="t('recordChanges')"
|
||||
@click.stop="filterByRecord(modelLog)"
|
||||
/>
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
<QItemSection>
|
||||
<QCard
|
||||
class="changes-log q-py-none q-mb-xs"
|
||||
v-for="(log, logIndex) in modelLog.logs"
|
||||
:key="logIndex"
|
||||
>
|
||||
<QCardSection class="change-info q-pa-none">
|
||||
<QItem
|
||||
class="q-px-sm q-py-xs justify-between items-center"
|
||||
>
|
||||
<span class="json-field" :title="prop.name">
|
||||
{{ prop.nameI18n }}:
|
||||
</span>
|
||||
<VnJsonValue :value="prop.val.val" />
|
||||
<span v-if="prop.val.id" class="id-value">
|
||||
#{{ prop.val.id }}
|
||||
</span>
|
||||
<span v-if="log.action == 'update'">
|
||||
←
|
||||
<VnJsonValue :value="prop.old.val" />
|
||||
<span v-if="prop.old.id" class="id-value">
|
||||
#{{ prop.old.id }}
|
||||
<div
|
||||
class="date text-grey text-caption q-mr-sm"
|
||||
:title="
|
||||
date.formatDate(
|
||||
log.creationDate,
|
||||
'DD/MM/YYYY hh:mm:ss'
|
||||
) ?? `date:'dd/MM/yyyy HH:mm:ss'`
|
||||
"
|
||||
>
|
||||
{{ toRelativeDate(log.creationDate) }}
|
||||
</div>
|
||||
<div>
|
||||
<QBtn
|
||||
color="grey"
|
||||
class="pit"
|
||||
icon="preview"
|
||||
flat
|
||||
round
|
||||
:title="t('pointRecord')"
|
||||
padding="none"
|
||||
v-if="log.action != 'insert'"
|
||||
@click.stop="
|
||||
openPointRecord(log.id, modelLog)
|
||||
"
|
||||
>
|
||||
<QPopupProxy>
|
||||
<QCard v-if="pointRecord">
|
||||
<div
|
||||
class="header q-px-sm q-py-xs q-ma-none text-white text-bold bg-primary"
|
||||
>
|
||||
{{ modelLog.modelI18n }}
|
||||
<span v-if="modelLog.id"
|
||||
>#{{
|
||||
modelLog.id
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
<QCardSection
|
||||
class="change-detail q-pa-sm"
|
||||
>
|
||||
<QItem
|
||||
v-for="(
|
||||
value, index
|
||||
) in pointRecord"
|
||||
:key="index"
|
||||
class="q-pa-none"
|
||||
>
|
||||
<span
|
||||
class="json-field q-mr-xs text-grey"
|
||||
:title="
|
||||
value.name
|
||||
"
|
||||
>
|
||||
{{
|
||||
value.nameI18n
|
||||
}}:
|
||||
</span>
|
||||
<VnJsonValue
|
||||
:value="
|
||||
value.val.val
|
||||
"
|
||||
/>
|
||||
</QItem>
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
</QPopupProxy>
|
||||
</QBtn>
|
||||
<QIcon
|
||||
class="action q-ml-xs"
|
||||
:class="actionsClass[log.action]"
|
||||
:name="actionsIcon[log.action]"
|
||||
:title="
|
||||
t(
|
||||
`actions.${
|
||||
actionsText[log.action]
|
||||
}`
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</QItem>
|
||||
</QCardSection>
|
||||
<QCardSection
|
||||
class="change-detail q-px-sm q-py-xs"
|
||||
:class="{ expanded: log.expand }"
|
||||
v-if="log.props.length || log.description"
|
||||
>
|
||||
<QIcon
|
||||
class="cursor-pointer q-mr-md"
|
||||
color="grey"
|
||||
name="expand_more"
|
||||
:title="t('globals.details')"
|
||||
size="sm"
|
||||
@click="log.expand = !log.expand"
|
||||
/>
|
||||
<span v-if="log.props.length" class="attributes">
|
||||
<span
|
||||
v-if="!log.expand"
|
||||
class="q-pa-none text-grey"
|
||||
>
|
||||
<span
|
||||
v-for="(prop, propIndex) in log.props"
|
||||
:key="propIndex"
|
||||
class="basic-json"
|
||||
>
|
||||
<span
|
||||
class="json-field"
|
||||
:title="prop.name"
|
||||
>
|
||||
{{ prop.nameI18n }}:
|
||||
</span>
|
||||
<VnJsonValue :value="prop.val.val" />
|
||||
<span
|
||||
v-if="
|
||||
propIndex <
|
||||
log.props.length - 1
|
||||
"
|
||||
>,
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
<span v-if="!log.props.length" class="description">
|
||||
{{ log.description }}
|
||||
</span>
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
v-if="log.expand"
|
||||
class="expanded-json column q-pa-none"
|
||||
>
|
||||
<div
|
||||
v-for="(
|
||||
prop, prop2Index
|
||||
) in log.props"
|
||||
:key="prop2Index"
|
||||
class="q-pa-none text-grey"
|
||||
>
|
||||
<span
|
||||
class="json-field"
|
||||
:title="prop.name"
|
||||
>
|
||||
{{ prop.nameI18n }}:
|
||||
</span>
|
||||
<VnJsonValue :value="prop.val.val" />
|
||||
<span
|
||||
v-if="prop.val.id"
|
||||
class="id-value"
|
||||
>
|
||||
#{{ prop.val.id }}
|
||||
</span>
|
||||
<span v-if="log.action == 'update'">
|
||||
←
|
||||
<VnJsonValue
|
||||
:value="prop.old.val"
|
||||
/>
|
||||
<span
|
||||
v-if="prop.old.id"
|
||||
class="id-value"
|
||||
>
|
||||
#{{ prop.old.id }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
v-if="!log.props.length"
|
||||
class="description"
|
||||
>
|
||||
{{ log.description }}
|
||||
</span>
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</VnPaginate>
|
||||
<Teleport to="#right-panel" v-if="stateStore.isHeaderMounted()">
|
||||
<QList dense>
|
||||
<QSeparator />
|
||||
|
@ -691,17 +723,16 @@ watch(
|
|||
</QOptionGroup>
|
||||
</QItem>
|
||||
<QItem class="q-mt-sm">
|
||||
<QItemSection v-if="!workers">
|
||||
<QSkeleton type="QInput" class="full-width" />
|
||||
</QItemSection>
|
||||
<QItemSection v-if="workers && userRadio !== null">
|
||||
<QItemSection v-if="userRadio !== null">
|
||||
<VnSelect
|
||||
class="full-width"
|
||||
:label="t('globals.user')"
|
||||
v-model="userSelect"
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
:options="workers"
|
||||
:url="`${model}Logs/${$route.params.id}/editors`"
|
||||
:fields="['id', 'nickname', 'name', 'image']"
|
||||
sort-by="nickname"
|
||||
@update:model-value="selectFilter('userSelect')"
|
||||
hide-selected
|
||||
>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useRole } from 'src/composables/useRole';
|
||||
import { useAcl } from 'src/composables/useAcl';
|
||||
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
@ -11,6 +12,10 @@ const $props = defineProps({
|
|||
type: Array,
|
||||
default: () => ['developer'],
|
||||
},
|
||||
acls: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
actionIcon: {
|
||||
type: String,
|
||||
default: 'add',
|
||||
|
@ -22,15 +27,13 @@ const $props = defineProps({
|
|||
});
|
||||
|
||||
const role = useRole();
|
||||
const acl = useAcl();
|
||||
const showForm = ref(false);
|
||||
|
||||
const isAllowedToCreate = computed(() => {
|
||||
if ($props.acls.length) return acl.hasAny($props.acls);
|
||||
return role.hasAny($props.rolesAllowedToCreate);
|
||||
});
|
||||
|
||||
const toggleForm = () => {
|
||||
showForm.value = !showForm.value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -41,7 +44,7 @@ const toggleForm = () => {
|
|||
>
|
||||
<template v-if="isAllowedToCreate" #append>
|
||||
<QIcon
|
||||
@click.stop.prevent="toggleForm()"
|
||||
@click.stop.prevent="$refs.dialog.show()"
|
||||
:name="actionIcon"
|
||||
:size="actionIcon === 'add' ? 'xs' : 'sm'"
|
||||
:class="['default-icon', { '--add-icon': actionIcon === 'add' }]"
|
||||
|
@ -51,7 +54,7 @@ const toggleForm = () => {
|
|||
>
|
||||
<QTooltip v-if="tooltip">{{ tooltip }}</QTooltip>
|
||||
</QIcon>
|
||||
<QDialog v-model="showForm" transition-show="scale" transition-hide="scale">
|
||||
<QDialog ref="dialog" transition-show="scale" transition-hide="scale">
|
||||
<slot name="form" />
|
||||
</QDialog>
|
||||
</template>
|
||||
|
|
|
@ -31,6 +31,7 @@ const props = defineProps({
|
|||
});
|
||||
|
||||
defineEmits(['confirm', ...useDialogPluginComponent.emits]);
|
||||
defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.hide() });
|
||||
|
||||
const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
||||
|
||||
|
@ -68,8 +69,10 @@ async function confirm() {
|
|||
<QSpace />
|
||||
<QBtn icon="close" :disable="isLoading" flat round dense v-close-popup />
|
||||
</QCardSection>
|
||||
<QCardSection class="row items-center">
|
||||
<QCardSection class="q-pb-none">
|
||||
<span v-if="message !== false" v-html="message" />
|
||||
</QCardSection>
|
||||
<QCardSection class="row items-center q-pt-none">
|
||||
<slot name="customHTML"></slot>
|
||||
</QCardSection>
|
||||
<QCardActions align="right">
|
||||
|
|
|
@ -132,17 +132,6 @@ async function search(evt) {
|
|||
}
|
||||
}
|
||||
|
||||
async function reload() {
|
||||
isLoading.value = true;
|
||||
const params = Object.values(userParams.value).filter((param) => param);
|
||||
store.skip = 0;
|
||||
store.page = 1;
|
||||
await arrayData.fetch({ append: false });
|
||||
if (!$props.showAll && !params.length) store.data = [];
|
||||
isLoading.value = false;
|
||||
emit('refresh');
|
||||
}
|
||||
|
||||
async function clearFilters() {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
|
@ -231,32 +220,18 @@ function sanitizer(params) {
|
|||
</QItemLabel>
|
||||
</QItemSection>
|
||||
<QItemSection top side>
|
||||
<div class="q-gutter-xs">
|
||||
<QBtn
|
||||
@click="clearFilters"
|
||||
color="primary"
|
||||
dense
|
||||
flat
|
||||
icon="filter_list_off"
|
||||
padding="none"
|
||||
round
|
||||
size="sm"
|
||||
>
|
||||
<QTooltip>{{ t('Remove filters') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
@click="reload"
|
||||
color="primary"
|
||||
dense
|
||||
flat
|
||||
icon="refresh"
|
||||
padding="none"
|
||||
round
|
||||
size="sm"
|
||||
>
|
||||
<QTooltip>{{ t('Refresh') }}</QTooltip>
|
||||
</QBtn>
|
||||
</div>
|
||||
<QBtn
|
||||
@click="clearFilters"
|
||||
color="primary"
|
||||
dense
|
||||
flat
|
||||
icon="filter_list_off"
|
||||
padding="none"
|
||||
round
|
||||
size="sm"
|
||||
>
|
||||
<QTooltip>{{ t('Remove filters') }}</QTooltip>
|
||||
</QBtn>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem class="q-mb-sm">
|
||||
|
|
|
@ -119,8 +119,8 @@ watch(
|
|||
);
|
||||
|
||||
watch(
|
||||
() => [props.url, props.filter],
|
||||
([url, filter]) => mounted.value && fetch({ url, filter })
|
||||
() => [props.url, props.filter, props.userParams],
|
||||
([url, filter, userParams]) => mounted.value && fetch({ url, filter, userParams })
|
||||
);
|
||||
|
||||
const addFilter = async (filter, params) => {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
<script setup>
|
||||
defineProps({ wrap: { type: Boolean, default: false } });
|
||||
</script>
|
||||
<template>
|
||||
<div class="vn-row q-gutter-md q-mb-md">
|
||||
<slot />
|
||||
|
@ -15,7 +18,9 @@
|
|||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
.vn-row {
|
||||
flex-direction: column;
|
||||
&:not(.wrap) {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import { toCurrency } from 'src/filters';
|
||||
|
||||
export function getTotal(rows, key, opts = {}) {
|
||||
const { currency, cb } = opts;
|
||||
const total = rows.reduce((acc, row) => acc + +(cb ? cb(row) : row[key] || 0), 0);
|
||||
|
||||
return currency
|
||||
? toCurrency(total, currency == 'default' ? undefined : currency)
|
||||
: total;
|
||||
}
|
|
@ -16,13 +16,18 @@ export function useAcl() {
|
|||
state.setAcls(acls);
|
||||
}
|
||||
|
||||
function hasAny(model, prop, accessType) {
|
||||
const acls = state.getAcls().value[model];
|
||||
if (acls)
|
||||
return ['*', prop].some((key) => {
|
||||
const acl = acls[key];
|
||||
return acl && (acl['*'] || acl[accessType]);
|
||||
});
|
||||
function hasAny(acls) {
|
||||
for (const acl of acls) {
|
||||
let { model, props, accessType } = acl;
|
||||
const modelAcls = state.getAcls().value[model];
|
||||
Array.isArray(props) || (props = [props]);
|
||||
if (modelAcls)
|
||||
return ['*', ...props].some((key) => {
|
||||
const acl = modelAcls[key];
|
||||
return acl && (acl['*'] || acl[accessType]);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -37,6 +37,10 @@ a {
|
|||
.link {
|
||||
color: $color-link;
|
||||
cursor: pointer;
|
||||
|
||||
&--white {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.tx-color-link {
|
||||
|
|
|
@ -40,6 +40,8 @@ globals:
|
|||
noChanges: No changes to save
|
||||
changesToSave: You have changes pending to save
|
||||
confirmRemove: You are about to delete this row. Are you sure?
|
||||
rowWillBeRemoved: This row will be removed
|
||||
sureToContinue: Are you sure you want to continue?
|
||||
rowAdded: Row added
|
||||
rowRemoved: Row removed
|
||||
pleaseWait: Please wait...
|
||||
|
@ -84,7 +86,7 @@ globals:
|
|||
description: Description
|
||||
id: Id
|
||||
order: Order
|
||||
original: Original
|
||||
original: Phys. Doc
|
||||
file: File
|
||||
selectFile: Select a file
|
||||
copyClipboard: Copy on clipboard
|
||||
|
@ -96,6 +98,7 @@ globals:
|
|||
to: To
|
||||
notes: Notes
|
||||
refresh: Refresh
|
||||
weight: Weight
|
||||
pageTitles:
|
||||
logIn: Login
|
||||
summary: Summary
|
||||
|
@ -261,6 +264,7 @@ globals:
|
|||
clientsActionsMonitor: Clients and actions
|
||||
serial: Serial
|
||||
medical: Mutual
|
||||
supplier: Supplier
|
||||
created: Created
|
||||
worker: Worker
|
||||
now: Now
|
||||
|
@ -753,56 +757,6 @@ parking:
|
|||
searchBar:
|
||||
info: You can search by parking code
|
||||
label: Search parking...
|
||||
invoiceIn:
|
||||
list:
|
||||
ref: Reference
|
||||
supplier: Supplier
|
||||
supplierRef: Supplier ref.
|
||||
serialNumber: Serial number
|
||||
serial: Serial
|
||||
file: File
|
||||
issued: Issued
|
||||
isBooked: Is booked
|
||||
awb: AWB
|
||||
amount: Amount
|
||||
card:
|
||||
issued: Issued
|
||||
amount: Amount
|
||||
client: Client
|
||||
company: Company
|
||||
customerCard: Customer card
|
||||
ticketList: Ticket List
|
||||
vat: Vat
|
||||
dueDay: Due day
|
||||
intrastat: Intrastat
|
||||
summary:
|
||||
supplier: Supplier
|
||||
supplierRef: Supplier ref.
|
||||
currency: Currency
|
||||
docNumber: Doc number
|
||||
issued: Expedition date
|
||||
operated: Operation date
|
||||
bookEntried: Entry date
|
||||
bookedDate: Booked date
|
||||
sage: Sage withholding
|
||||
vat: Undeductible VAT
|
||||
company: Company
|
||||
booked: Booked
|
||||
expense: Expense
|
||||
taxableBase: Taxable base
|
||||
rate: Rate
|
||||
sageVat: Sage vat
|
||||
sageTransaction: Sage transaction
|
||||
dueDay: Date
|
||||
bank: Bank
|
||||
amount: Amount
|
||||
foreignValue: Foreign value
|
||||
dueTotal: Due day
|
||||
noMatch: Do not match
|
||||
code: Code
|
||||
net: Net
|
||||
stems: Stems
|
||||
country: Country
|
||||
order:
|
||||
field:
|
||||
salesPersonFk: Sales Person
|
||||
|
@ -1318,6 +1272,7 @@ components:
|
|||
active: Is active
|
||||
visible: Is visible
|
||||
floramondo: Is floramondo
|
||||
showBadDates: Show future items
|
||||
userPanel:
|
||||
copyToken: Token copied to clipboard
|
||||
settings: Settings
|
||||
|
|
|
@ -39,6 +39,8 @@ globals:
|
|||
noChanges: Sin cambios que guardar
|
||||
changesToSave: Tienes cambios pendientes de guardar
|
||||
confirmRemove: Vas a eliminar este registro. ¿Continuar?
|
||||
rowWillBeRemoved: Esta linea se eliminará
|
||||
sureToContinue: ¿Seguro que quieres continuar?
|
||||
rowAdded: Fila añadida
|
||||
rowRemoved: Fila eliminada
|
||||
pleaseWait: Por favor espera...
|
||||
|
@ -86,7 +88,7 @@ globals:
|
|||
description: Descripción
|
||||
id: Id
|
||||
order: Orden
|
||||
original: Original
|
||||
original: Doc. física
|
||||
file: Fichero
|
||||
selectFile: Seleccione un fichero
|
||||
copyClipboard: Copiar en portapapeles
|
||||
|
@ -98,6 +100,7 @@ globals:
|
|||
to: Hasta
|
||||
notes: Notas
|
||||
refresh: Actualizar
|
||||
weight: Peso
|
||||
pageTitles:
|
||||
logIn: Inicio de sesión
|
||||
summary: Resumen
|
||||
|
@ -265,6 +268,7 @@ globals:
|
|||
clientsActionsMonitor: Clientes y acciones
|
||||
serial: Facturas por serie
|
||||
medical: Mutua
|
||||
supplier: Proveedor
|
||||
created: Fecha creación
|
||||
worker: Trabajador
|
||||
now: Ahora
|
||||
|
@ -799,54 +803,6 @@ parking:
|
|||
searchBar:
|
||||
info: Puedes buscar por código de parking
|
||||
label: Buscar parking...
|
||||
invoiceIn:
|
||||
list:
|
||||
ref: Referencia
|
||||
supplier: Proveedor
|
||||
supplierRef: Ref. proveedor
|
||||
serialNumber: Num. serie
|
||||
shortIssued: F. emisión
|
||||
serial: Serie
|
||||
file: Fichero
|
||||
issued: Fecha emisión
|
||||
isBooked: Conciliada
|
||||
awb: AWB
|
||||
amount: Importe
|
||||
card:
|
||||
issued: Fecha emisión
|
||||
amount: Importe
|
||||
client: Cliente
|
||||
company: Empresa
|
||||
customerCard: Ficha del cliente
|
||||
ticketList: Listado de tickets
|
||||
vat: Iva
|
||||
dueDay: Fecha de vencimiento
|
||||
summary:
|
||||
supplier: Proveedor
|
||||
supplierRef: Ref. proveedor
|
||||
currency: Divisa
|
||||
docNumber: Número documento
|
||||
issued: Fecha de expedición
|
||||
operated: Fecha operación
|
||||
bookEntried: Fecha asiento
|
||||
bookedDate: Fecha contable
|
||||
sage: Retención sage
|
||||
vat: Iva no deducible
|
||||
company: Empresa
|
||||
booked: Contabilizada
|
||||
expense: Gasto
|
||||
taxableBase: Base imp.
|
||||
rate: Tasa
|
||||
sageTransaction: Sage transación
|
||||
dueDay: Fecha
|
||||
bank: Caja
|
||||
amount: Importe
|
||||
foreignValue: Divisa
|
||||
dueTotal: Vencimiento
|
||||
code: Código
|
||||
net: Neto
|
||||
stems: Tallos
|
||||
country: País
|
||||
department:
|
||||
pageTitles:
|
||||
basicData: Basic data
|
||||
|
@ -1298,6 +1254,7 @@ components:
|
|||
active: Activo
|
||||
visible: Visible
|
||||
floramondo: Floramondo
|
||||
showBadDates: Ver items a futuro
|
||||
userPanel:
|
||||
copyToken: Token copiado al portapapeles
|
||||
settings: Configuración
|
||||
|
|
|
@ -27,15 +27,15 @@ const filter = {
|
|||
order: 'created DESC',
|
||||
};
|
||||
|
||||
const urlPath = 'AccessTokens';
|
||||
const urlPath = 'VnTokens';
|
||||
|
||||
const refresh = () => paginateRef.value.fetch();
|
||||
|
||||
const navigate = (id) => router.push({ name: 'AccountSummary', params: { id } });
|
||||
|
||||
const killSession = async (id) => {
|
||||
const killSession = async ({ userId, created }) => {
|
||||
try {
|
||||
await axios.delete(`${urlPath}/${id}`);
|
||||
await axios.post(`${urlPath}/killSession`, { userId, created });
|
||||
paginateRef.value.fetch();
|
||||
notify(t('Session killed'), 'positive');
|
||||
} catch (error) {
|
||||
|
@ -84,7 +84,7 @@ const killSession = async (id) => {
|
|||
openConfirmationModal(
|
||||
t('Session will be killed'),
|
||||
t('Are you sure you want to continue?'),
|
||||
() => killSession(row.id)
|
||||
() => killSession(row)
|
||||
)
|
||||
"
|
||||
outline
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { computed, onBeforeMount, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useRole } from 'src/composables/useRole';
|
||||
import { useAcl } from 'src/composables/useAcl';
|
||||
import axios from 'axios';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
|
@ -24,7 +24,7 @@ import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescr
|
|||
const { openConfirmationModal } = useVnConfirm();
|
||||
const { sendEmail } = usePrintService();
|
||||
const { t } = useI18n();
|
||||
const { hasAny } = useRole();
|
||||
const { hasAny } = useAcl();
|
||||
|
||||
const session = useSession();
|
||||
const tokenMultimedia = session.getTokenMultimedia();
|
||||
|
@ -284,7 +284,9 @@ const showBalancePdf = ({ id }) => {
|
|||
>
|
||||
<VnInput
|
||||
v-model="scope.value"
|
||||
:disable="!hasAny(['administrative'])"
|
||||
:disable="
|
||||
!hasAny([{ model: 'Receipt', props: '*', accessType: 'WRITE' }])
|
||||
"
|
||||
@keypress.enter="scope.set"
|
||||
autofocus
|
||||
/>
|
||||
|
|
|
@ -70,7 +70,7 @@ const getBankEntities = (data, formData) => {
|
|||
<VnSelectDialog
|
||||
:label="t('Swift / BIC')"
|
||||
:options="bankEntitiesOptions"
|
||||
:roles-allowed-to-create="['salesAssistant', 'hr']"
|
||||
:acls="[{ model: 'BankEntity', props: '*', accessType: 'WRITE' }]"
|
||||
:rules="validate('Worker.bankEntity')"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
|
|
|
@ -93,7 +93,7 @@ function handleLocation(data, location) {
|
|||
<VnRow>
|
||||
<VnLocation
|
||||
:rules="validate('Worker.postcode')"
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
|
||||
v-model="data.postcode"
|
||||
@update:model-value="(location) => handleLocation(data, location)"
|
||||
/>
|
||||
|
|
|
@ -86,7 +86,7 @@ function handleLocation(data, location) {
|
|||
<VnRow>
|
||||
<VnLocation
|
||||
:rules="validate('Worker.postcode')"
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
|
||||
v-model="data.location"
|
||||
@update:model-value="(location) => handleLocation(data, location)"
|
||||
>
|
||||
|
|
|
@ -412,7 +412,7 @@ function handleLocation(data, location) {
|
|||
>
|
||||
<template #more-create-dialog="{ data }">
|
||||
<VnLocation
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
:acls="[{ model: 'Province', props: '*', accessType: 'WRITE' }]"
|
||||
v-model="data.location"
|
||||
@update:model-value="(location) => handleLocation(data, location)"
|
||||
/>
|
||||
|
|
|
@ -92,7 +92,7 @@ function handleLocation(data, location) {
|
|||
|
||||
<VnLocation
|
||||
:rules="validate('Worker.postcode')"
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
|
||||
v-model="data.location"
|
||||
@update:model-value="(location) => handleLocation(data, location)"
|
||||
/>
|
||||
|
|
|
@ -176,7 +176,7 @@ function handleLocation(data, location) {
|
|||
<div class="col">
|
||||
<VnLocation
|
||||
:rules="validate('Worker.postcode')"
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
|
||||
v-model="data.postalCode"
|
||||
@update:model-value="(location) => handleLocation(data, location)"
|
||||
></VnLocation>
|
||||
|
|
|
@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import axios from 'axios';
|
||||
import { usePrintService } from 'composables/usePrintService';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
import { useState } from 'src/composables/useState';
|
||||
|
@ -27,7 +28,7 @@ const router = useRouter();
|
|||
const state = useState();
|
||||
const user = state.getUser();
|
||||
const stateStore = useStateStore();
|
||||
|
||||
const { sendEmail } = usePrintService();
|
||||
const client = ref({});
|
||||
const hasChanged = ref(false);
|
||||
const isLoading = ref(false);
|
||||
|
@ -156,22 +157,33 @@ const onSubmit = async () => {
|
|||
}
|
||||
};
|
||||
|
||||
const onDataSaved = async ({
|
||||
addressId,
|
||||
companyFk,
|
||||
companyId,
|
||||
from,
|
||||
recipient,
|
||||
replyTo,
|
||||
}) => {
|
||||
await axios.post(`Clients/${route.params.id}/incoterms-authorization-email`, {
|
||||
addressId,
|
||||
companyFk,
|
||||
companyId,
|
||||
from,
|
||||
recipient,
|
||||
replyTo,
|
||||
});
|
||||
const getSamples = async () => {
|
||||
try {
|
||||
const filter = { where: { id: initialData.typeFk } };
|
||||
let { data } = await axios.get('Samples', {
|
||||
params: { filter: JSON.stringify(filter) },
|
||||
});
|
||||
return data[0];
|
||||
} catch (error) {
|
||||
notify('errors.create', 'negative');
|
||||
}
|
||||
};
|
||||
|
||||
getSamples();
|
||||
const onDataSaved = async () => {
|
||||
try {
|
||||
const params = {
|
||||
recipientId: initialData.recipientId,
|
||||
recipient: initialData.recipient,
|
||||
replyTo: initialData.replyTo,
|
||||
};
|
||||
setParams(params);
|
||||
const samplesData = await getSamples();
|
||||
const path = `${samplesData.model}/${route.params.id}/${samplesData.code}-email`;
|
||||
await sendEmail(path, params);
|
||||
} catch (error) {
|
||||
notify('errors.create', 'negative');
|
||||
}
|
||||
toCustomerSamples();
|
||||
};
|
||||
|
||||
|
|
|
@ -55,6 +55,15 @@ const pinnedModules = computed(() => navigation.getPinnedModules());
|
|||
>
|
||||
<div class="text-center text-primary button-text">
|
||||
{{ t(item.title) }}
|
||||
<div v-if="item.keyBinding">
|
||||
{{ '(' + item.keyBinding + ')' }}
|
||||
<QTooltip>
|
||||
{{
|
||||
'Ctrl + Alt + ' +
|
||||
item.keyBinding.toUpperCase()
|
||||
}}
|
||||
</QTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</QBtn>
|
||||
</div>
|
||||
|
|
|
@ -26,7 +26,6 @@ const { notify } = useNotify();
|
|||
|
||||
const rowsSelected = ref([]);
|
||||
const entryBuysPaginateRef = ref(null);
|
||||
const packagingsOptions = ref(null);
|
||||
const originalRowDataCopy = ref(null);
|
||||
|
||||
const getInputEvents = (colField, props) => {
|
||||
|
@ -66,7 +65,10 @@ const tableColumnComponents = computed(() => ({
|
|||
'map-options': true,
|
||||
'use-input': true,
|
||||
'hide-selected': true,
|
||||
options: packagingsOptions.value,
|
||||
url: 'Packagings',
|
||||
fields: ['id'],
|
||||
where: { freightItemFk: true },
|
||||
'sort-by': 'id ASC',
|
||||
dense: true,
|
||||
},
|
||||
event: getInputEvents,
|
||||
|
@ -304,13 +306,6 @@ const lockIconType = (groupingMode, mode) => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
ref="expensesRef"
|
||||
url="Packagings"
|
||||
:filter="{ fields: ['id'], where: { freightItemFk: true }, order: 'id ASC' }"
|
||||
auto-load
|
||||
@on-fetch="(data) => (packagingsOptions = data)"
|
||||
/>
|
||||
<VnSubToolbar>
|
||||
<template #st-actions>
|
||||
<QBtnGroup push style="column-gap: 10px">
|
||||
|
|
|
@ -223,6 +223,10 @@ async function onSubmit() {
|
|||
autofocus
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnInputDate :label="t('Entry date')" v-model="data.bookEntried" />
|
||||
<VnInputDate :label="t('Accounted date')" v-model="data.booked" />
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
:label="t('Undeductible VAT')"
|
||||
|
@ -285,10 +289,6 @@ async function onSubmit() {
|
|||
</template>
|
||||
</VnInput>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnInputDate :label="t('Entry date')" v-model="data.bookEntried" />
|
||||
<VnInputDate :label="t('Accounted date')" v-model="data.booked" />
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
:label="t('Currency')"
|
||||
|
|
|
@ -3,6 +3,8 @@ import VnCard from 'components/common/VnCard.vue';
|
|||
import InvoiceInDescriptor from './InvoiceInDescriptor.vue';
|
||||
import InvoiceInFilter from '../InvoiceInFilter.vue';
|
||||
import InvoiceInSearchbar from '../InvoiceInSearchbar.vue';
|
||||
import { onBeforeRouteUpdate } from 'vue-router';
|
||||
import { setRectificative } from '../composables/setRectificative';
|
||||
|
||||
const filter = {
|
||||
include: [
|
||||
|
@ -20,6 +22,8 @@ const filter = {
|
|||
{ relation: 'currency' },
|
||||
],
|
||||
};
|
||||
|
||||
onBeforeRouteUpdate(async (to) => await setRectificative(to));
|
||||
</script>
|
||||
<template>
|
||||
<VnCard
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useQuasar } from 'quasar';
|
||||
import axios from 'axios';
|
||||
import { toCurrency, toDate } from 'src/filters';
|
||||
import { useRole } from 'src/composables/useRole';
|
||||
import { useAcl } from 'src/composables/useAcl';
|
||||
import { downloadFile } from 'src/composables/downloadFile';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import { usePrintService } from 'composables/usePrintService';
|
||||
|
@ -24,7 +24,7 @@ const $props = defineProps({ id: { type: Number, default: null } });
|
|||
const { push, currentRoute } = useRouter();
|
||||
|
||||
const quasar = useQuasar();
|
||||
const { hasAny } = useRole();
|
||||
const { hasAny } = useAcl();
|
||||
const { t } = useI18n();
|
||||
const { openReport, sendEmail } = usePrintService();
|
||||
const arrayData = useArrayData();
|
||||
|
@ -195,7 +195,8 @@ async function cloneInvoice() {
|
|||
push({ path: `/invoice-in/${data.id}/summary` });
|
||||
}
|
||||
|
||||
const isAdministrative = () => hasAny(['administrative']);
|
||||
const canEditProp = (props) =>
|
||||
hasAny([{ model: 'InvoiceIn', props, accessType: 'WRITE' }]);
|
||||
|
||||
const isAgricultural = () => {
|
||||
if (!config.value) return false;
|
||||
|
@ -283,7 +284,7 @@ const createInvoiceInCorrection = async () => {
|
|||
<InvoiceInToBook>
|
||||
<template #content="{ book }">
|
||||
<QItem
|
||||
v-if="!entity?.isBooked && isAdministrative()"
|
||||
v-if="!entity?.isBooked && canEditProp('toBook')"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="book(entityId)"
|
||||
|
@ -293,7 +294,7 @@ const createInvoiceInCorrection = async () => {
|
|||
</template>
|
||||
</InvoiceInToBook>
|
||||
<QItem
|
||||
v-if="entity?.isBooked && isAdministrative()"
|
||||
v-if="entity?.isBooked && canEditProp('toUnbook')"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="triggerMenu('unbook')"
|
||||
|
@ -303,7 +304,7 @@ const createInvoiceInCorrection = async () => {
|
|||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-if="isAdministrative()"
|
||||
v-if="canEditProp('deleteById')"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="triggerMenu('delete')"
|
||||
|
@ -311,7 +312,7 @@ const createInvoiceInCorrection = async () => {
|
|||
<QItemSection>{{ t('Delete invoice') }}</QItemSection>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-if="isAdministrative()"
|
||||
v-if="canEditProp('clone')"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="triggerMenu('clone')"
|
||||
|
@ -356,10 +357,7 @@ const createInvoiceInCorrection = async () => {
|
|||
<template #body="{ entity }">
|
||||
<VnLv :label="t('invoiceIn.card.issued')" :value="toDate(entity.issued)" />
|
||||
<VnLv :label="t('invoiceIn.summary.booked')" :value="toDate(entity.booked)" />
|
||||
<VnLv
|
||||
:label="t('invoiceIn.card.amount')"
|
||||
:value="toCurrency(totalAmount, entity.currency?.code)"
|
||||
/>
|
||||
<VnLv :label="t('invoiceIn.card.amount')" :value="toCurrency(totalAmount)" />
|
||||
<VnLv :label="t('invoiceIn.summary.supplier')">
|
||||
<template #value>
|
||||
<span class="link">
|
||||
|
|
|
@ -5,10 +5,10 @@ import { useI18n } from 'vue-i18n';
|
|||
import axios from 'axios';
|
||||
import { toDate } from 'src/filters';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import { getTotal } from 'src/composables/getTotal';
|
||||
import CrudModel from 'src/components/CrudModel.vue';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import { toCurrency } from 'src/filters';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
|
@ -72,7 +72,6 @@ async function insert() {
|
|||
await invoiceInFormRef.value.reload();
|
||||
notify(t('globals.dataSaved'), 'positive');
|
||||
}
|
||||
const getTotalAmount = (rows) => rows.reduce((acc, { amount }) => acc + +amount, 0);
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
|
@ -155,9 +154,17 @@ const getTotalAmount = (rows) => rows.reduce((acc, { amount }) => acc + +amount,
|
|||
<QTd />
|
||||
<QTd />
|
||||
<QTd>
|
||||
{{ toCurrency(getTotalAmount(rows), currency) }}
|
||||
{{ getTotal(rows, 'amount', { currency: 'default' }) }}
|
||||
</QTd>
|
||||
<QTd>
|
||||
<template v-if="isNotEuro(invoiceIn.currency.code)">
|
||||
{{
|
||||
getTotal(rows, 'foreignValue', {
|
||||
currency: invoiceIn.currency.code,
|
||||
})
|
||||
}}
|
||||
</template>
|
||||
</QTd>
|
||||
<QTd />
|
||||
</QTr>
|
||||
</template>
|
||||
<template #item="props">
|
||||
|
|
|
@ -2,18 +2,15 @@
|
|||
import { computed, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { toCurrency } from 'src/filters';
|
||||
import { getTotal } from 'src/composables/getTotal';
|
||||
import CrudModel from 'src/components/CrudModel.vue';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const route = useRoute();
|
||||
const arrayData = useArrayData();
|
||||
const currency = computed(() => arrayData.store.data?.currency?.code);
|
||||
const invoceInIntrastat = ref([]);
|
||||
const rowsSelected = ref([]);
|
||||
const countries = ref([]);
|
||||
|
@ -72,9 +69,6 @@ const columns = computed(() => [
|
|||
},
|
||||
]);
|
||||
|
||||
const getTotal = (data, key) =>
|
||||
data.reduce((acc, cur) => acc + +String(cur[key] || 0).replace(',', '.'), 0);
|
||||
|
||||
const formatOpt = (row, { model, options }, prop) => {
|
||||
const obj = row[model];
|
||||
const option = options.find(({ id }) => id == obj);
|
||||
|
@ -154,7 +148,7 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
<QTd />
|
||||
<QTd />
|
||||
<QTd>
|
||||
{{ toCurrency(getTotal(rows, 'amount'), currency) }}
|
||||
{{ getTotal(rows, 'amount', { currency: 'default' }) }}
|
||||
</QTd>
|
||||
<QTd>
|
||||
{{ getTotal(rows, 'net') }}
|
||||
|
|
|
@ -35,7 +35,7 @@ const vatColumns = ref([
|
|||
name: 'landed',
|
||||
label: 'invoiceIn.summary.taxableBase',
|
||||
field: (row) => row.taxableBase,
|
||||
format: (value) => toCurrency(value, currency.value),
|
||||
format: (value) => toCurrency(value),
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
|
@ -64,7 +64,7 @@ const vatColumns = ref([
|
|||
name: 'rate',
|
||||
label: 'invoiceIn.summary.rate',
|
||||
field: (row) => taxRate(row.taxableBase, row.taxTypeSage?.rate),
|
||||
format: (value) => toCurrency(value, currency.value),
|
||||
format: (value) => toCurrency(value),
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
|
@ -72,7 +72,7 @@ const vatColumns = ref([
|
|||
name: 'currency',
|
||||
label: 'invoiceIn.summary.currency',
|
||||
field: (row) => row.foreignValue,
|
||||
format: (value) => value,
|
||||
format: (val) => val && toCurrency(val, currency.value),
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
|
@ -97,7 +97,7 @@ const dueDayColumns = ref([
|
|||
name: 'amount',
|
||||
label: 'invoiceIn.summary.amount',
|
||||
field: (row) => row.amount,
|
||||
format: (value) => toCurrency(value, currency.value),
|
||||
format: (value) => toCurrency(value),
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
|
@ -105,7 +105,7 @@ const dueDayColumns = ref([
|
|||
name: 'landed',
|
||||
label: 'invoiceIn.summary.foreignValue',
|
||||
field: (row) => row.foreignValue,
|
||||
format: (value) => value,
|
||||
format: (val) => val && toCurrency(val, currency.value),
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
|
@ -124,7 +124,7 @@ const intrastatColumns = ref([
|
|||
{
|
||||
name: 'amount',
|
||||
label: 'invoiceIn.summary.amount',
|
||||
field: (row) => toCurrency(row.amount, currency.value),
|
||||
field: (row) => toCurrency(row.amount),
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
},
|
||||
|
@ -179,7 +179,6 @@ const getTotalTax = (tax) =>
|
|||
|
||||
const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CardSummary
|
||||
data-key="InvoiceInSummary"
|
||||
|
@ -229,10 +228,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
:label="t('invoiceIn.summary.currency')"
|
||||
:value="entity.currency?.code"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.docNumber')"
|
||||
:value="`${entity.serial}/${entity.serialNumber}`"
|
||||
/>
|
||||
<VnLv :label="t('invoiceIn.serial')" :value="`${entity.serial}`" />
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<QCardSection class="q-pa-none">
|
||||
|
@ -293,12 +289,9 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
<QCardSection class="q-pa-none">
|
||||
<VnLv
|
||||
:label="t('invoiceIn.summary.taxableBase')"
|
||||
:value="toCurrency(entity.totals.totalTaxableBase, currency)"
|
||||
/>
|
||||
<VnLv
|
||||
label="Total"
|
||||
:value="toCurrency(entity.totals.totalVat, currency)"
|
||||
:value="toCurrency(entity.totals.totalTaxableBase)"
|
||||
/>
|
||||
<VnLv label="Total" :value="toCurrency(entity.totals.totalVat)" />
|
||||
<VnLv :label="t('invoiceIn.summary.dueTotal')">
|
||||
<template #value>
|
||||
<QChip
|
||||
|
@ -311,7 +304,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
: t('invoiceIn.summary.dueTotal')
|
||||
"
|
||||
>
|
||||
{{ toCurrency(entity.totals.totalDueDay, currency) }}
|
||||
{{ toCurrency(entity.totals.totalDueDay) }}
|
||||
</QChip>
|
||||
</template>
|
||||
</VnLv>
|
||||
|
@ -350,15 +343,17 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
<template #bottom-row>
|
||||
<QTr class="bg">
|
||||
<QTd></QTd>
|
||||
<QTd>{{ toCurrency(entity.totals.totalTaxableBase) }}</QTd>
|
||||
<QTd></QTd>
|
||||
<QTd></QTd>
|
||||
<QTd>{{ toCurrency(getTotalTax(entity.invoiceInTax)) }}</QTd>
|
||||
<QTd>{{
|
||||
toCurrency(entity.totals.totalTaxableBase, currency)
|
||||
entity.totals.totalTaxableBaseForeignValue &&
|
||||
toCurrency(
|
||||
entity.totals.totalTaxableBaseForeignValue,
|
||||
currency
|
||||
)
|
||||
}}</QTd>
|
||||
<QTd></QTd>
|
||||
<QTd></QTd>
|
||||
<QTd>{{
|
||||
toCurrency(getTotalTax(entity.invoiceInTax, currency))
|
||||
}}</QTd>
|
||||
<QTd></QTd>
|
||||
</QTr>
|
||||
</template>
|
||||
</QTable>
|
||||
|
@ -384,9 +379,17 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
<QTd></QTd>
|
||||
<QTd></QTd>
|
||||
<QTd>
|
||||
{{ toCurrency(entity.totals.totalDueDay, currency) }}
|
||||
{{ toCurrency(entity.totals.totalDueDay) }}
|
||||
</QTd>
|
||||
<QTd>
|
||||
{{
|
||||
entity.totals.totalDueDayForeignValue &&
|
||||
toCurrency(
|
||||
entity.totals.totalDueDayForeignValue,
|
||||
currency
|
||||
)
|
||||
}}
|
||||
</QTd>
|
||||
<QTd></QTd>
|
||||
</QTr>
|
||||
</template>
|
||||
</QTable>
|
||||
|
@ -421,7 +424,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
<template #bottom-row>
|
||||
<QTr class="bg">
|
||||
<QTd></QTd>
|
||||
<QTd>{{ toCurrency(intrastatTotals.amount, currency) }}</QTd>
|
||||
<QTd>{{ toCurrency(intrastatTotals.amount) }}</QTd>
|
||||
<QTd>{{ intrastatTotals.net }}</QTd>
|
||||
<QTd>{{ intrastatTotals.stems }}</QTd>
|
||||
<QTd></QTd>
|
||||
|
|
|
@ -2,18 +2,17 @@
|
|||
import { ref, computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
import axios from 'axios';
|
||||
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 VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
|
||||
import CreateNewExpenseForm from 'src/components/CreateNewExpenseForm.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const quasar = useQuasar();
|
||||
|
||||
const arrayData = useArrayData();
|
||||
const invoiceIn = computed(() => arrayData.store.data);
|
||||
|
@ -23,15 +22,7 @@ const expenses = ref([]);
|
|||
const sageTaxTypes = ref([]);
|
||||
const sageTransactionTypes = ref([]);
|
||||
const rowsSelected = ref([]);
|
||||
const newExpense = ref({
|
||||
code: undefined,
|
||||
isWithheld: false,
|
||||
description: undefined,
|
||||
});
|
||||
|
||||
const invoiceInFormRef = ref();
|
||||
const expensesRef = ref();
|
||||
const newExpenseRef = ref();
|
||||
|
||||
defineProps({
|
||||
actionIcon: {
|
||||
|
@ -56,7 +47,7 @@ const columns = computed(() => [
|
|||
{
|
||||
name: 'taxablebase',
|
||||
label: t('Taxable base'),
|
||||
field: (row) => toCurrency(row.taxableBase, currency.value),
|
||||
field: (row) => row.taxableBase,
|
||||
model: 'taxableBase',
|
||||
sortable: true,
|
||||
tabIndex: 2,
|
||||
|
@ -91,7 +82,7 @@ const columns = computed(() => [
|
|||
label: t('Rate'),
|
||||
sortable: true,
|
||||
tabIndex: 5,
|
||||
field: (row) => toCurrency(taxRate(row, row.taxTypeSageFk), currency.value),
|
||||
field: (row) => taxRate(row, row.taxTypeSageFk),
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
|
@ -132,40 +123,6 @@ function taxRate(invoiceInTax) {
|
|||
return (taxTypeSage / 100) * taxableBase;
|
||||
}
|
||||
|
||||
async function addExpense() {
|
||||
try {
|
||||
if (!newExpense.value.code) throw new Error(t(`The code can't be empty`));
|
||||
if (isNaN(newExpense.value.code))
|
||||
throw new Error(t(`The code have to be a number`));
|
||||
if (!newExpense.value.description)
|
||||
throw new Error(t(`The description can't be empty`));
|
||||
|
||||
const data = [
|
||||
{
|
||||
id: newExpense.value.code,
|
||||
isWithheld: newExpense.value.isWithheld,
|
||||
name: newExpense.value.description,
|
||||
},
|
||||
];
|
||||
|
||||
await axios.post(`Expenses`, data);
|
||||
await expensesRef.value.fetch();
|
||||
quasar.notify({
|
||||
type: 'positive',
|
||||
message: t('globals.dataSaved'),
|
||||
});
|
||||
newExpenseRef.value.hide();
|
||||
} catch (error) {
|
||||
quasar.notify({
|
||||
type: 'negative',
|
||||
message: t(`${error.message}`),
|
||||
});
|
||||
}
|
||||
}
|
||||
const getTotalTaxableBase = (rows) =>
|
||||
rows.reduce((acc, { taxableBase }) => acc + +(taxableBase || 0), 0);
|
||||
const getTotalRate = (rows) => rows.reduce((acc, cur) => acc + +taxRate(cur), 0);
|
||||
|
||||
const formatOpt = (row, { model, options }, prop) => {
|
||||
const obj = row[model];
|
||||
const option = options.find(({ id }) => id == obj);
|
||||
|
@ -207,37 +164,25 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
>
|
||||
<template #body-cell-expense="{ row, col }">
|
||||
<QTd>
|
||||
<VnSelect
|
||||
<VnSelectDialog
|
||||
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')"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
{{ `${scope.opt.id}: ${scope.opt.name}` }}
|
||||
</QItem>
|
||||
</template>
|
||||
<template #append>
|
||||
<QIcon
|
||||
name="close"
|
||||
@click.stop="value = null"
|
||||
class="cursor-pointer"
|
||||
size="xs"
|
||||
<template #form>
|
||||
<CreateNewExpenseForm
|
||||
@on-data-saved="$refs.expensesRef.fetch()"
|
||||
/>
|
||||
<QIcon
|
||||
@click.stop.prevent="newExpenseRef.show()"
|
||||
:name="actionIcon"
|
||||
size="xs"
|
||||
class="default-icon"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Create expense') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</VnSelectDialog>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-taxablebase="{ row }">
|
||||
|
@ -325,12 +270,24 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
<QTd />
|
||||
<QTd />
|
||||
<QTd>
|
||||
{{ toCurrency(getTotalTaxableBase(rows), currency) }}
|
||||
{{ getTotal(rows, 'taxableBase', { currency: 'default' }) }}
|
||||
</QTd>
|
||||
<QTd />
|
||||
<QTd />
|
||||
<QTd> {{ toCurrency(getTotalRate(rows), currency) }}</QTd>
|
||||
<QTd />
|
||||
<QTd>
|
||||
{{
|
||||
getTotal(rows, null, { cb: taxRate, currency: 'default' })
|
||||
}}</QTd
|
||||
>
|
||||
<QTd>
|
||||
<template v-if="isNotEuro(invoiceIn.currency.code)">
|
||||
{{
|
||||
getTotal(rows, 'foreignValue', {
|
||||
currency: invoiceIn.currency.code,
|
||||
})
|
||||
}}
|
||||
</template>
|
||||
</QTd>
|
||||
</QTr>
|
||||
</template>
|
||||
<template #item="props">
|
||||
|
@ -342,7 +299,7 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
<QSeparator />
|
||||
<QList>
|
||||
<QItem>
|
||||
<VnSelect
|
||||
<VnSelectDialog
|
||||
:label="t('Expense')"
|
||||
class="full-width"
|
||||
v-model="props.row['expenseFk']"
|
||||
|
@ -350,13 +307,17 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
option-value="id"
|
||||
option-label="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>
|
||||
</VnSelect>
|
||||
<template #form>
|
||||
<CreateNewExpenseForm />
|
||||
</template>
|
||||
</VnSelectDialog>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<VnInputNumber
|
||||
|
@ -439,44 +400,6 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
</QTable>
|
||||
</template>
|
||||
</CrudModel>
|
||||
<QDialog ref="newExpenseRef">
|
||||
<QCard>
|
||||
<QCardSection class="q-pb-none">
|
||||
<QItem class="q-pa-none">
|
||||
<span class="text-primary text-h6 full-width">
|
||||
<QIcon name="edit" class="q-mr-xs" />
|
||||
{{ t('New expense') }}
|
||||
</span>
|
||||
<QBtn icon="close" flat round dense v-close-popup />
|
||||
</QItem>
|
||||
</QCardSection>
|
||||
<QCardSection class="q-pt-none">
|
||||
<QItem>
|
||||
<VnInput
|
||||
:label="`${t('Code')}*`"
|
||||
v-model="newExpense.code"
|
||||
:required="true"
|
||||
/>
|
||||
<QCheckbox
|
||||
dense
|
||||
size="sm"
|
||||
:label="`${t('It\'s a withholding')}`"
|
||||
v-model="newExpense.isWithheld"
|
||||
/>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<VnInput
|
||||
:label="`${t('Descripction')}*`"
|
||||
v-model="newExpense.description"
|
||||
/>
|
||||
</QItem>
|
||||
</QCardSection>
|
||||
<QCardActions class="justify-end">
|
||||
<QBtn flat :label="t('globals.close')" color="primary" v-close-popup />
|
||||
<QBtn :label="t('globals.save')" color="primary" @click="addExpense" />
|
||||
</QCardActions>
|
||||
</QCard>
|
||||
</QDialog>
|
||||
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
||||
<QBtn
|
||||
color="primary"
|
||||
|
@ -484,7 +407,9 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
size="lg"
|
||||
round
|
||||
@click="invoiceInFormRef.insert()"
|
||||
/>
|
||||
>
|
||||
<QTooltip>{{ t('Add tax') }}</QTooltip>
|
||||
</QBtn>
|
||||
</QPageSticky>
|
||||
</template>
|
||||
|
||||
|
@ -524,18 +449,11 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
<i18n>
|
||||
es:
|
||||
Expense: Gasto
|
||||
Create expense: Crear gasto
|
||||
Create a new expense: Crear nuevo gasto
|
||||
Add tax: Crear gasto
|
||||
Taxable base: Base imp.
|
||||
Sage tax: Sage iva
|
||||
Sage transaction: Sage transacción
|
||||
Rate: Tasa
|
||||
Foreign value: Divisa
|
||||
New expense: Nuevo gasto
|
||||
Code: Código
|
||||
It's a withholding: Es una retención
|
||||
Descripction: Descripción
|
||||
The code can't be empty: El código no puede estar vacío
|
||||
The description can't be empty: La descripción no puede estar vacía
|
||||
The code have to be a number: El código debe ser un número.
|
||||
</i18n>
|
||||
|
|
|
@ -28,6 +28,16 @@ const activities = ref([]);
|
|||
</div>
|
||||
</template>
|
||||
<template #body="{ params, searchFn }">
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate :label="t('From')" v-model="params.from" is-outlined />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate :label="t('To')" v-model="params.to" is-outlined />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
|
@ -64,16 +74,6 @@ const activities = ref([]);
|
|||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
:label="t('params.serialNumber')"
|
||||
v-model="params.serialNumber"
|
||||
is-outlined
|
||||
lazy-rules
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
|
@ -84,15 +84,6 @@ const activities = ref([]);
|
|||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
:label="t('Issued')"
|
||||
v-model="params.issued"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
|
@ -140,22 +131,6 @@ const activities = ref([]);
|
|||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QExpansionItem :label="t('More options')" expand-separator>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
:label="t('From')"
|
||||
v-model="params.from"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate :label="t('To')" v-model="params.to" is-outlined />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QExpansionItem>
|
||||
</template>
|
||||
</VnFilterPanel>
|
||||
</template>
|
||||
|
@ -179,6 +154,7 @@ en:
|
|||
correctedFk: Rectified
|
||||
issued: Issued
|
||||
to: To
|
||||
from: From
|
||||
awbCode: AWB
|
||||
correctingFk: Rectificative
|
||||
supplierActivityFk: Supplier activity
|
||||
|
@ -201,6 +177,8 @@ es:
|
|||
correctedFk: Rectificada
|
||||
correctingFk: Rectificativa
|
||||
supplierActivityFk: Actividad proveedor
|
||||
from: Desde
|
||||
to: Hasta
|
||||
From: Desde
|
||||
To: Hasta
|
||||
Amount: Importe
|
||||
|
|
|
@ -47,12 +47,6 @@ const cols = computed(() => [
|
|||
name: 'supplierRef',
|
||||
label: t('invoiceIn.list.supplierRef'),
|
||||
},
|
||||
|
||||
{
|
||||
align: 'left',
|
||||
name: 'serialNumber',
|
||||
label: t('invoiceIn.list.serialNumber'),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'serial',
|
||||
|
@ -141,7 +135,7 @@ const cols = computed(() => [
|
|||
v-model="data.supplierFk"
|
||||
url="Suppliers"
|
||||
:fields="['id', 'nickname']"
|
||||
:label="t('Supplier')"
|
||||
:label="t('globals.supplier')"
|
||||
option-value="id"
|
||||
option-label="nickname"
|
||||
:filter-options="['id', 'name']"
|
||||
|
@ -162,7 +156,7 @@ const cols = computed(() => [
|
|||
/>
|
||||
<VnSelect
|
||||
url="Companies"
|
||||
:label="t('Company')"
|
||||
:label="t('globals.company')"
|
||||
:fields="['id', 'code']"
|
||||
v-model="data.companyFk"
|
||||
option-value="id"
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import axios from 'axios';
|
||||
|
||||
export async function setRectificative(route) {
|
||||
const card = route.matched.find((route) => route.name === 'InvoiceInCard');
|
||||
const corrective = card.children.find(
|
||||
(route) => route.name === 'InvoiceInCorrective'
|
||||
);
|
||||
|
||||
corrective.meta.hidden = !(
|
||||
await axios.get('InvoiceInCorrections', {
|
||||
params: { filter: { where: { correctingFk: route.params.id } } },
|
||||
})
|
||||
).data.length;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
invoiceIn:
|
||||
serial: Serial
|
||||
list:
|
||||
ref: Reference
|
||||
supplier: Supplier
|
||||
supplierRef: Supplier ref.
|
||||
serial: Serial
|
||||
file: File
|
||||
issued: Issued
|
||||
isBooked: Is booked
|
||||
awb: AWB
|
||||
amount: Amount
|
||||
card:
|
||||
issued: Issued
|
||||
amount: Amount
|
||||
client: Client
|
||||
company: Company
|
||||
customerCard: Customer card
|
||||
ticketList: Ticket List
|
||||
vat: Vat
|
||||
dueDay: Due day
|
||||
intrastat: Intrastat
|
||||
summary:
|
||||
supplier: Supplier
|
||||
supplierRef: Supplier ref.
|
||||
currency: Currency
|
||||
issued: Expedition date
|
||||
operated: Operation date
|
||||
bookEntried: Entry date
|
||||
bookedDate: Booked date
|
||||
sage: Sage withholding
|
||||
vat: Undeductible VAT
|
||||
company: Company
|
||||
booked: Booked
|
||||
expense: Expense
|
||||
taxableBase: Taxable base
|
||||
rate: Rate
|
||||
sageVat: Sage vat
|
||||
sageTransaction: Sage transaction
|
||||
dueDay: Date
|
||||
bank: Bank
|
||||
amount: Amount
|
||||
foreignValue: Foreign value
|
||||
dueTotal: Due day
|
||||
noMatch: Do not match
|
||||
code: Code
|
||||
net: Net
|
||||
stems: Stems
|
||||
country: Country
|
|
@ -0,0 +1,47 @@
|
|||
invoiceIn:
|
||||
serial: Serie
|
||||
list:
|
||||
ref: Referencia
|
||||
supplier: Proveedor
|
||||
supplierRef: Ref. proveedor
|
||||
shortIssued: F. emisión
|
||||
file: Fichero
|
||||
issued: Fecha emisión
|
||||
isBooked: Conciliada
|
||||
awb: AWB
|
||||
amount: Importe
|
||||
card:
|
||||
issued: Fecha emisión
|
||||
amount: Importe
|
||||
client: Cliente
|
||||
company: Empresa
|
||||
customerCard: Ficha del cliente
|
||||
ticketList: Listado de tickets
|
||||
vat: Iva
|
||||
dueDay: Fecha de vencimiento
|
||||
summary:
|
||||
supplier: Proveedor
|
||||
supplierRef: Ref. proveedor
|
||||
currency: Divisa
|
||||
docNumber: Número documento
|
||||
issued: Fecha de expedición
|
||||
operated: Fecha operación
|
||||
bookEntried: Fecha asiento
|
||||
bookedDate: Fecha contable
|
||||
sage: Retención sage
|
||||
vat: Iva no deducible
|
||||
company: Empresa
|
||||
booked: Contabilizada
|
||||
expense: Gasto
|
||||
taxableBase: Base imp.
|
||||
rate: Tasa
|
||||
sageTransaction: Sage transación
|
||||
dueDay: Fecha
|
||||
bank: Caja
|
||||
amount: Importe
|
||||
foreignValue: Divisa
|
||||
dueTotal: Vencimiento
|
||||
code: Código
|
||||
net: Neto
|
||||
stems: Tallos
|
||||
country: País
|
|
@ -10,8 +10,6 @@ import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.v
|
|||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
import RegularizeStockForm from 'components/RegularizeStockForm.vue';
|
||||
import ItemDescriptorImage from 'src/pages/Item/Card/ItemDescriptorImage.vue';
|
||||
|
||||
import { useState } from 'src/composables/useState';
|
||||
import useCardDescription from 'src/composables/useCardDescription';
|
||||
import { getUrl } from 'src/composables/getUrl';
|
||||
import axios from 'axios';
|
||||
|
@ -35,58 +33,69 @@ const $props = defineProps({
|
|||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
warehouseFk: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const quasar = useQuasar();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const state = useState();
|
||||
const user = state.getUser();
|
||||
|
||||
const warehouseConfig = ref(null);
|
||||
const entityId = computed(() => {
|
||||
return $props.id || route.params.id;
|
||||
});
|
||||
|
||||
const regularizeStockFormDialog = ref(null);
|
||||
const available = ref(null);
|
||||
const visible = ref(null);
|
||||
const _warehouseFk = ref(null);
|
||||
const salixUrl = ref();
|
||||
const warehouseFk = computed({
|
||||
get() {
|
||||
return _warehouseFk.value;
|
||||
},
|
||||
set(val) {
|
||||
_warehouseFk.value = val;
|
||||
if (val) updateStock();
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
warehouseFk.value = user.value.warehouseFk;
|
||||
salixUrl.value = await getUrl('');
|
||||
await getItemConfigs();
|
||||
await updateStock();
|
||||
});
|
||||
|
||||
carlossa marked this conversation as resolved
Outdated
|
||||
const data = ref(useCardDescription());
|
||||
const setData = (entity) => {
|
||||
if (!entity) return;
|
||||
data.value = useCardDescription(entity.name, entity.id);
|
||||
const setData = async (entity) => {
|
||||
try {
|
||||
if (!entity) return;
|
||||
data.value = useCardDescription(entity.name, entity.id);
|
||||
await updateStock();
|
||||
} catch (err) {
|
||||
console.error('Error item');
|
||||
}
|
||||
};
|
||||
|
||||
const getItemConfigs = async () => {
|
||||
try {
|
||||
const { data } = await axios.get('ItemConfigs/findOne');
|
||||
if (!data) return;
|
||||
return (warehouseConfig.value = data.warehouseFk);
|
||||
} catch (err) {
|
||||
console.error('Error item');
|
||||
}
|
||||
};
|
||||
const updateStock = async () => {
|
||||
carlossa marked this conversation as resolved
jsegarra
commented
Deberia aparecer el almacen seleccionado al abrir el modal segun el valor del usaurio Deberia aparecer el almacen seleccionado al abrir el modal segun el valor del usaurio
|
||||
try {
|
||||
available.value = null;
|
||||
visible.value = null;
|
||||
|
||||
const params = {
|
||||
warehouseFk: warehouseFk.value,
|
||||
warehouseFk: $props.warehouseFk,
|
||||
dated: $props.dated,
|
||||
};
|
||||
|
||||
await getItemConfigs();
|
||||
if (!params.warehouseFk) {
|
||||
params.warehouseFk = warehouseConfig.value;
|
||||
}
|
||||
const { data } = await axios.get(`Items/${entityId.value}/getVisibleAvailable`, {
|
||||
params,
|
||||
});
|
||||
|
||||
available.value = data.available;
|
||||
visible.value = data.visible;
|
||||
} catch (err) {
|
||||
|
|
|
@ -47,8 +47,11 @@ const getWarehouseName = async (warehouseFk) => {
|
|||
const filter = {
|
||||
where: { id: warehouseFk },
|
||||
};
|
||||
|
||||
const { data } = await axios.get('Warehouses/findOne', { filter });
|
||||
const { data } = await axios.get('Warehouses/findOne', {
|
||||
params: {
|
||||
filter: JSON.stringify(filter),
|
||||
},
|
||||
});
|
||||
if (!data) return;
|
||||
warehouseName.value = data.name;
|
||||
};
|
||||
|
|
|
@ -15,6 +15,10 @@ const $props = defineProps({
|
|||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
warehouseFk: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -26,6 +30,7 @@ const $props = defineProps({
|
|||
:summary="ItemSummary"
|
||||
:dated="dated"
|
||||
:sale-fk="saleFk"
|
||||
:warehouse-fk="warehouseFk"
|
||||
/>
|
||||
</QPopupProxy>
|
||||
</template>
|
||||
|
|
|
@ -7,8 +7,7 @@ import CardSummary from 'components/ui/CardSummary.vue';
|
|||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import ItemDescriptorImage from 'src/pages/Item/Card/ItemDescriptorImage.vue';
|
||||
import VnUserLink from 'src/components/ui/VnUserLink.vue';
|
||||
|
||||
import { useRole } from 'src/composables/useRole';
|
||||
import VnTitle from 'src/components/common/VnTitle.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
|
@ -19,23 +18,10 @@ const $props = defineProps({
|
|||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const roleState = useRole();
|
||||
|
||||
const entityId = computed(() => $props.id || route.params.id);
|
||||
|
||||
const isBuyer = computed(() => {
|
||||
return roleState.hasAny(['buyer']);
|
||||
});
|
||||
|
||||
const isReplenisher = computed(() => {
|
||||
return roleState.hasAny(['replenisher']);
|
||||
});
|
||||
|
||||
const isAdministrative = computed(() => {
|
||||
return roleState.hasAny(['administrative']);
|
||||
});
|
||||
const getUrl = (id, param) => `#/Item/${id}/${param}`;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CardSummary
|
||||
ref="summary"
|
||||
|
@ -44,13 +30,15 @@ const isAdministrative = computed(() => {
|
|||
data-key="ItemSummary"
|
||||
>
|
||||
<template #header-left>
|
||||
<router-link
|
||||
v-if="route.name !== 'ItemSummary'"
|
||||
<QBtn
|
||||
v-if="$route.name !== 'ItemSummary'"
|
||||
:to="{ name: 'ItemSummary', params: { id: entityId } }"
|
||||
class="header link"
|
||||
>
|
||||
<QIcon name="open_in_new" color="white" size="sm" />
|
||||
</router-link>
|
||||
class="header link--white"
|
||||
icon="open_in_new"
|
||||
flat
|
||||
dense
|
||||
round
|
||||
/>
|
||||
</template>
|
||||
<template #header="{ entity: { item } }">
|
||||
{{ item.id }} - {{ item.name }}
|
||||
|
@ -65,15 +53,10 @@ const isAdministrative = computed(() => {
|
|||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<component
|
||||
:is="isBuyer ? 'router-link' : 'span'"
|
||||
:to="{ name: 'ItemBasicData', params: { id: entityId } }"
|
||||
class="header"
|
||||
:class="{ 'header-link': isBuyer }"
|
||||
>
|
||||
{{ t('item.summary.basicData') }}
|
||||
<QIcon v-if="isBuyer" name="open_in_new" />
|
||||
</component>
|
||||
<VnTitle
|
||||
:url="getUrl(entityId, 'basic-data')"
|
||||
:text="t('item.summary.basicData')"
|
||||
/>
|
||||
<VnLv :label="t('item.summary.name')" :value="item.name" />
|
||||
<VnLv :label="t('item.summary.completeName')" :value="item.longName" />
|
||||
<VnLv :label="t('item.summary.family')" :value="item.itemType.name" />
|
||||
|
@ -104,15 +87,10 @@ const isAdministrative = computed(() => {
|
|||
</VnLv>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<component
|
||||
:is="isBuyer ? 'router-link' : 'span'"
|
||||
:to="{ name: 'ItemBasicData', params: { id: entityId } }"
|
||||
class="header"
|
||||
:class="{ 'header-link': isBuyer }"
|
||||
>
|
||||
{{ t('item.summary.otherData') }}
|
||||
<QIcon v-if="isBuyer" name="open_in_new" />
|
||||
</component>
|
||||
<VnTitle
|
||||
:url="getUrl(entityId, 'basic-data')"
|
||||
:text="t('item.summary.otherData')"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('item.summary.intrastatCode')"
|
||||
:value="item.intrastat.id"
|
||||
|
@ -137,15 +115,7 @@ const isAdministrative = computed(() => {
|
|||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<component
|
||||
:is="isBuyer || isReplenisher ? 'router-link' : 'span'"
|
||||
:to="{ name: 'ItemTags', params: { id: entityId } }"
|
||||
class="header"
|
||||
:class="{ 'header-link': isBuyer || isReplenisher }"
|
||||
>
|
||||
{{ t('item.summary.tags') }}
|
||||
<QIcon v-if="isBuyer || isReplenisher" name="open_in_new" />
|
||||
</component>
|
||||
<VnTitle :url="getUrl(entityId, 'tags')" :text="t('item.summary.tags')" />
|
||||
<VnLv
|
||||
v-for="(tag, index) in tags"
|
||||
:key="index"
|
||||
|
@ -154,29 +124,14 @@ const isAdministrative = computed(() => {
|
|||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one" v-if="item.description">
|
||||
<component
|
||||
:is="isBuyer ? 'router-link' : 'span'"
|
||||
:to="{ name: 'ItemBasicData', params: { id: entityId } }"
|
||||
class="header"
|
||||
:class="{ 'header-link': isBuyer }"
|
||||
>
|
||||
{{ t('item.summary.description') }}
|
||||
<QIcon v-if="isBuyer" name="open_in_new" />
|
||||
</component>
|
||||
<p>
|
||||
{{ item.description }}
|
||||
</p>
|
||||
<VnTitle
|
||||
:url="getUrl(entityId, 'basic-data')"
|
||||
:text="t('item.summary.description')"
|
||||
/>
|
||||
<p v-text="item.description" />
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<component
|
||||
:is="isBuyer || isAdministrative ? 'router-link' : 'span'"
|
||||
:to="{ name: 'ItemTax', params: { id: entityId } }"
|
||||
class="header"
|
||||
:class="{ 'header-link': isBuyer || isAdministrative }"
|
||||
>
|
||||
{{ t('item.summary.tax') }}
|
||||
<QIcon v-if="isBuyer || isAdministrative" name="open_in_new" />
|
||||
</component>
|
||||
<VnTitle :url="getUrl(entityId, 'tax')" :text="t('item.summary.tax')" />
|
||||
<VnLv
|
||||
v-for="(tax, index) in item.taxes"
|
||||
:key="index"
|
||||
|
@ -185,15 +140,10 @@ const isAdministrative = computed(() => {
|
|||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<component
|
||||
:is="isBuyer ? 'router-link' : 'span'"
|
||||
:to="{ name: 'ItemBotanical', params: { id: entityId } }"
|
||||
class="header"
|
||||
:class="{ 'header-link': isBuyer }"
|
||||
>
|
||||
{{ t('item.summary.botanical') }}
|
||||
<QIcon v-if="isBuyer" name="open_in_new" />
|
||||
</component>
|
||||
<VnTitle
|
||||
:url="getUrl(entityId, 'botanical')"
|
||||
:text="t('item.summary.botanical')"
|
||||
/>
|
||||
<VnLv :label="t('item.summary.genus')" :value="botanical?.genus?.name" />
|
||||
<VnLv
|
||||
:label="t('item.summary.specie')"
|
||||
|
@ -201,23 +151,19 @@ const isAdministrative = computed(() => {
|
|||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<component
|
||||
:is="isBuyer || isReplenisher ? 'router-link' : 'span'"
|
||||
:to="{ name: 'ItemBarcode', params: { id: entityId } }"
|
||||
class="header"
|
||||
:class="{ 'header-link': isBuyer || isReplenisher }"
|
||||
>
|
||||
{{ t('item.summary.barcode') }}
|
||||
<QIcon v-if="isBuyer || isReplenisher" name="open_in_new" />
|
||||
</component>
|
||||
<p v-for="(barcode, index) in item.itemBarcode" :key="index">
|
||||
{{ barcode.code }}
|
||||
</p>
|
||||
<VnTitle
|
||||
:url="getUrl(entityId, 'barcode')"
|
||||
:text="t('item.summary.barcode')"
|
||||
/>
|
||||
<p
|
||||
v-for="(barcode, index) in item.itemBarcode"
|
||||
:key="index"
|
||||
v-text="barcode.code"
|
||||
/>
|
||||
</QCard>
|
||||
</template>
|
||||
</CardSummary>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
en:
|
||||
Este artículo necesita una foto: Este artículo necesita una foto
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -9,18 +9,29 @@ import ItemsFilterPanel from 'src/components/ItemsFilterPanel.vue';
|
|||
|
||||
const { t } = useI18n();
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
dataKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
warehousesOptions: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const itemTypeWorkersOptions = ref([]);
|
||||
const exprBuilder = (param, value) => {
|
||||
switch (param) {
|
||||
case 'name':
|
||||
return { 'i.name': { like: `%${value}%` } };
|
||||
case 'itemFk':
|
||||
case 'warehouseFk':
|
||||
case 'rate2':
|
||||
case 'rate3':
|
||||
param = `fp.${param}`;
|
||||
return { [param]: value };
|
||||
case 'minPrice':
|
||||
param = `i.${param}`;
|
||||
return { [param]: value };
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -31,7 +42,7 @@ const itemTypeWorkersOptions = ref([]);
|
|||
:filter="{ fields: ['id', 'nickname'], order: 'nickname ASC', limit: 30 }"
|
||||
@on-fetch="(data) => (itemTypeWorkersOptions = data)"
|
||||
/>
|
||||
<ItemsFilterPanel :data-key="dataKey" :custom-tags="['tags']">
|
||||
<ItemsFilterPanel :data-key="props.dataKey" :custom-tags="['tags']">
|
||||
<template #body="{ params, searchFn }">
|
||||
<QItem class="q-my-md">
|
||||
<QItemSection>
|
||||
|
@ -52,9 +63,11 @@ const itemTypeWorkersOptions = ref([]);
|
|||
<QItem class="q-my-md">
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
url="Warehouses"
|
||||
auto-load
|
||||
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }"
|
||||
:label="t('components.itemsFilterPanel.warehouseFk')"
|
||||
v-model="params.warehouseFk"
|
||||
:options="warehousesOptions"
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
dense
|
||||
|
@ -93,8 +106,15 @@ const itemTypeWorkersOptions = ref([]);
|
|||
toggle-indeterminate
|
||||
@update:model-value="searchFn()"
|
||||
/>
|
||||
</QItemSection>
|
||||
<QItemSection>
|
||||
|
||||
<QCheckbox
|
||||
v-model="params.showBadDates"
|
||||
:label="t(`components.itemsFilterPanel.showBadDates`)"
|
||||
toggle-indeterminate
|
||||
@update:model-value="searchFn()"
|
||||
>
|
||||
</QCheckbox>
|
||||
|
||||
<QCheckbox
|
||||
:label="t('components.itemsFilterPanel.hasMinPrice')"
|
||||
v-model="params.hasMinPrice"
|
||||
|
|
|
@ -23,7 +23,7 @@ function confirmRemove() {
|
|||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('confirmDeletion'),
|
||||
title: t('globals.confirmDeletion'),
|
||||
message: t('confirmDeletionMessage'),
|
||||
promise: remove,
|
||||
},
|
||||
|
@ -52,12 +52,10 @@ async function remove() {
|
|||
<i18n>
|
||||
en:
|
||||
deleteOrder: Delete order
|
||||
confirmDeletion: Confirm deletion
|
||||
confirmDeletionMessage: Are you sure you want to delete this order?
|
||||
|
||||
es:
|
||||
deleteOrder: Eliminar pedido
|
||||
confirmDeletion: Confirmar eliminación
|
||||
confirmDeletionMessage: Seguro que quieres eliminar este pedido?
|
||||
|
||||
</i18n>
|
||||
|
|
|
@ -83,7 +83,7 @@ function handleLocation(data, location) {
|
|||
<VnRow>
|
||||
<VnLocation
|
||||
:rules="validate('Worker.postcode')"
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
|
||||
v-model="data.location"
|
||||
@update:model-value="(location) => handleLocation(data, location)"
|
||||
>
|
||||
|
|
|
@ -129,7 +129,7 @@ function handleLocation(data, location) {
|
|||
<VnRow>
|
||||
<VnLocation
|
||||
:rules="validate('Worker.postcode')"
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
|
||||
v-model="data.postCode"
|
||||
@update:model-value="(location) => handleLocation(data, location)"
|
||||
>
|
||||
|
|
|
@ -4,13 +4,11 @@ import { useRoute } from 'vue-router';
|
|||
import { useI18n } from 'vue-i18n';
|
||||
import CardSummary from 'components/ui/CardSummary.vue';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import { useRole } from 'src/composables/useRole';
|
||||
import { dashIfEmpty } from 'src/filters';
|
||||
import VnUserLink from 'src/components/ui/VnUserLink.vue';
|
||||
import VnTitle from 'src/components/common/VnTitle.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const roleState = useRole();
|
||||
const { t } = useI18n();
|
||||
|
||||
const $props = defineProps({
|
||||
|
@ -32,13 +30,7 @@ async function setData(data) {
|
|||
}
|
||||
}
|
||||
|
||||
const isAdministrative = computed(() => {
|
||||
return roleState.hasAny(['administrative']);
|
||||
});
|
||||
|
||||
function getUrl(section) {
|
||||
return isAdministrative.value && `#/supplier/${entityId.value}/${section}`;
|
||||
}
|
||||
const getUrl = (section) => `#/supplier/${entityId.value}/${section}`;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -9,6 +9,8 @@ import SendEmailDialog from 'components/common/SendEmailDialog.vue';
|
|||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
import VnSmsDialog from 'components/common/VnSmsDialog.vue';
|
||||
import toDate from 'filters/toDate';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
|
||||
const props = defineProps({
|
||||
ticket: {
|
||||
|
@ -21,9 +23,10 @@ const { push, currentRoute } = useRouter();
|
|||
const { dialog, notify } = useQuasar();
|
||||
const { t } = useI18n();
|
||||
const { openReport, sendEmail } = usePrintService();
|
||||
|
||||
const ticketSummary = useArrayData('TicketSummary');
|
||||
const ticket = ref(props.ticket);
|
||||
const ticketId = currentRoute.value.params.id;
|
||||
const weight = ref();
|
||||
const actions = {
|
||||
clone: async () => {
|
||||
const opts = { message: t('Ticket cloned'), type: 'positive' };
|
||||
|
@ -46,6 +49,25 @@ const actions = {
|
|||
push({ name: 'TicketSummary', params: { id: clonedTicketId } });
|
||||
}
|
||||
},
|
||||
setWeight: async () => {
|
||||
try {
|
||||
const invoiceIds = (
|
||||
await axios.post(`Tickets/${ticketId}/setWeight`, {
|
||||
weight: weight.value,
|
||||
})
|
||||
).data;
|
||||
|
||||
notify({ message: t('Weight set'), type: 'positive' });
|
||||
if (invoiceIds.length)
|
||||
notify({
|
||||
message: t('invoiceIds', { invoiceIds: invoiceIds.join() }),
|
||||
type: 'positive',
|
||||
});
|
||||
await ticketSummary.fetch({ updateRouter: false });
|
||||
} catch (e) {
|
||||
notify({ message: e.message, type: 'negative' });
|
||||
}
|
||||
},
|
||||
remove: async () => {
|
||||
try {
|
||||
await axios.post(`Tickets/${ticketId}/setDeleted`);
|
||||
|
@ -255,6 +277,12 @@ function openConfirmDialog(callback) {
|
|||
</QItemSection>
|
||||
<QItemSection>{{ t('To clone ticket') }}</QItemSection>
|
||||
</QItem>
|
||||
<QItem @click="$refs.weightDialog.show()" v-ripple clickable>
|
||||
<QItemSection avatar>
|
||||
<QIcon name="weight" />
|
||||
</QItemSection>
|
||||
<QItemSection>{{ t('Set weight') }}</QItemSection>
|
||||
</QItem>
|
||||
<template v-if="!ticket.isDeleted">
|
||||
<QSeparator />
|
||||
<QItem @click="openConfirmDialog('remove')" v-ripple clickable>
|
||||
|
@ -264,9 +292,25 @@ function openConfirmDialog(callback) {
|
|||
<QItemSection>{{ t('Delete ticket') }}</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
<VnConfirm
|
||||
ref="weightDialog"
|
||||
:title="t('Set weight')"
|
||||
:message="t('This ticket may be invoiced, do you want to continue?')"
|
||||
:promise="actions.setWeight"
|
||||
>
|
||||
<template #customHTML>
|
||||
<VnInputNumber
|
||||
:label="t('globals.weight')"
|
||||
v-model="weight"
|
||||
class="full-width"
|
||||
/>
|
||||
</template>
|
||||
</VnConfirm>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
en:
|
||||
invoiceIds: "Invoices have been generated with the following ids: {invoiceIds}"
|
||||
|
||||
es:
|
||||
Open Delivery Note...: Abrir albarán...
|
||||
Send Delivery Note...: Enviar albarán...
|
||||
|
@ -284,4 +328,8 @@ es:
|
|||
To clone ticket: Clonar ticket
|
||||
Ticket cloned: Ticked clonado
|
||||
It was not able to clone the ticket: No se pudo clonar el ticket
|
||||
Set weight: Establecer peso
|
||||
Weight set: Peso establecido
|
||||
This ticket may be invoiced, do you want to continue?: Es posible que se facture este ticket, desea continuar?
|
||||
invoiceIds: "Se han generado las facturas con los siguientes ids: {invoiceIds}"
|
||||
</i18n>
|
||||
|
|
|
@ -31,8 +31,7 @@ const $props = defineProps({
|
|||
const entityId = computed(() => $props.id || route.params.id);
|
||||
|
||||
const summaryRef = ref();
|
||||
const ticket = ref();
|
||||
const salesLines = ref(null);
|
||||
const ticket = computed(() => summaryRef.value?.entity);
|
||||
const editableStates = ref([]);
|
||||
const ticketUrl = ref();
|
||||
const grafanaUrl = 'https://grafana.verdnatura.es';
|
||||
|
@ -40,12 +39,6 @@ const grafanaUrl = 'https://grafana.verdnatura.es';
|
|||
onMounted(async () => {
|
||||
ticketUrl.value = (await getUrl('ticket/')) + entityId.value + '/';
|
||||
});
|
||||
async function setData(data) {
|
||||
if (data) {
|
||||
ticket.value = data;
|
||||
salesLines.value = data.sales;
|
||||
}
|
||||
}
|
||||
|
||||
function formattedAddress() {
|
||||
if (!ticket.value) return '';
|
||||
|
@ -89,7 +82,6 @@ async function changeState(value) {
|
|||
<CardSummary
|
||||
ref="summaryRef"
|
||||
:url="`Tickets/${entityId}/summary`"
|
||||
@on-fetch="(data) => setData(data)"
|
||||
data-key="TicketSummary"
|
||||
>
|
||||
<template #header="{ entity }">
|
||||
|
@ -131,7 +123,7 @@ async function changeState(value) {
|
|||
</QList>
|
||||
</QBtnDropdown>
|
||||
</template>
|
||||
<template #body>
|
||||
<template #body="{ entity }">
|
||||
<QCard class="vn-one">
|
||||
<VnTitle
|
||||
:url="ticketUrl + 'basic-data/step-one'"
|
||||
|
@ -139,57 +131,57 @@ async function changeState(value) {
|
|||
/>
|
||||
<VnLv :label="t('ticket.summary.state')">
|
||||
<template #value>
|
||||
<QChip :color="ticket.ticketState?.state?.classColor ?? 'dark'">
|
||||
{{ ticket.ticketState?.state?.name }}
|
||||
<QChip :color="entity.ticketState?.state?.classColor ?? 'dark'">
|
||||
{{ entity.ticketState?.state?.name }}
|
||||
</QChip>
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv :label="t('ticket.summary.salesPerson')">
|
||||
<template #value>
|
||||
<VnUserLink
|
||||
:name="ticket.client?.salesPersonUser?.name"
|
||||
:worker-id="ticket.client?.salesPersonFk"
|
||||
:name="entity.client?.salesPersonUser?.name"
|
||||
:worker-id="entity.client?.salesPersonFk"
|
||||
/>
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv
|
||||
:label="t('ticket.summary.agency')"
|
||||
:value="ticket.agencyMode?.name"
|
||||
:value="entity.agencyMode?.name"
|
||||
/>
|
||||
<VnLv :label="t('ticket.summary.zone')" :value="ticket?.zone?.name" />
|
||||
<VnLv :label="t('ticket.summary.zone')" :value="entity?.zone?.name" />
|
||||
<VnLv
|
||||
:label="t('ticket.summary.warehouse')"
|
||||
:value="ticket.warehouse?.name"
|
||||
:value="entity.warehouse?.name"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('ticket.summary.collection')"
|
||||
:value="ticket.ticketCollections[0]?.collectionFk"
|
||||
:value="entity.ticketCollections[0]?.collectionFk"
|
||||
>
|
||||
<template #value>
|
||||
<a
|
||||
:href="`${grafanaUrl}/d/d552ab74-85b4-4e7f-a279-fab7cd9c6124/control-de-expediciones?orgId=1&var-collectionFk=${ticket.ticketCollections[0]?.collectionFk}`"
|
||||
:href="`${grafanaUrl}/d/d552ab74-85b4-4e7f-a279-fab7cd9c6124/control-de-expediciones?orgId=1&var-collectionFk=${entity.ticketCollections[0]?.collectionFk}`"
|
||||
target="_blank"
|
||||
class="grafana"
|
||||
>
|
||||
{{ ticket.ticketCollections[0]?.collectionFk }}
|
||||
{{ entity.ticketCollections[0]?.collectionFk }}
|
||||
</a>
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv :label="t('ticket.summary.route')" :value="ticket.routeFk" />
|
||||
<VnLv :label="t('ticket.summary.route')" :value="entity.routeFk" />
|
||||
<VnLv :label="t('ticket.summary.invoice')">
|
||||
<template #value>
|
||||
<span :class="{ link: ticket.refFk }">
|
||||
{{ dashIfEmpty(ticket.refFk) }}
|
||||
<span :class="{ link: entity.refFk }">
|
||||
{{ dashIfEmpty(entity.refFk) }}
|
||||
<InvoiceOutDescriptorProxy
|
||||
:id="ticket.invoiceOut.id"
|
||||
v-if="ticket.refFk"
|
||||
:id="entity.invoiceOut.id"
|
||||
v-if="entity.refFk"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv
|
||||
:label="t('ticket.summary.weight')"
|
||||
:value="dashIfEmpty(ticket.weight)"
|
||||
:value="dashIfEmpty(entity.weight)"
|
||||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
|
@ -199,35 +191,35 @@ async function changeState(value) {
|
|||
/>
|
||||
<VnLv
|
||||
:label="t('ticket.summary.shipped')"
|
||||
:value="toDate(ticket.shipped)"
|
||||
:value="toDate(entity.shipped)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('ticket.summary.landed')"
|
||||
:value="toDate(ticket.landed)"
|
||||
:value="toDate(entity.landed)"
|
||||
/>
|
||||
<VnLv :label="t('globals.packages')" :value="ticket.packages" />
|
||||
<VnLv :value="ticket.address.phone">
|
||||
<VnLv :label="t('globals.packages')" :value="entity.packages" />
|
||||
<VnLv :value="entity.address.phone">
|
||||
<template #label>
|
||||
{{ t('ticket.summary.consigneePhone') }}
|
||||
<VnLinkPhone :phone-number="ticket.address.phone" />
|
||||
<VnLinkPhone :phone-number="entity.address.phone" />
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv :value="ticket.address.mobile">
|
||||
<VnLv :value="entity.address.mobile">
|
||||
<template #label>
|
||||
{{ t('ticket.summary.consigneeMobile') }}
|
||||
<VnLinkPhone :phone-number="ticket.address.mobile" />
|
||||
<VnLinkPhone :phone-number="entity.address.mobile" />
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv :value="ticket.client.phone">
|
||||
<VnLv :value="entity.client.phone">
|
||||
<template #label>
|
||||
{{ t('ticket.summary.clientPhone') }}
|
||||
<VnLinkPhone :phone-number="ticket.client.phone" />
|
||||
<VnLinkPhone :phone-number="entity.client.phone" />
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv :value="ticket.client.mobile">
|
||||
<VnLv :value="entity.client.mobile">
|
||||
<template #label>
|
||||
{{ t('ticket.summary.clientMobile') }}
|
||||
<VnLinkPhone :phone-number="ticket.client.mobile" />
|
||||
<VnLinkPhone :phone-number="entity.client.mobile" />
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv
|
||||
|
@ -235,13 +227,13 @@ async function changeState(value) {
|
|||
:value="formattedAddress()"
|
||||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one" v-if="ticket.notes.length">
|
||||
<QCard class="vn-one" v-if="entity.notes.length">
|
||||
<VnTitle
|
||||
:url="ticketUrl + 'observation'"
|
||||
:text="t('ticket.pageTitles.notes')"
|
||||
/>
|
||||
<VnLv
|
||||
v-for="note in ticket.notes"
|
||||
v-for="note in entity.notes"
|
||||
:key="note.id"
|
||||
:label="note.observationType.description"
|
||||
:value="note.description"
|
||||
|
@ -262,15 +254,15 @@ async function changeState(value) {
|
|||
<div class="bodyCard">
|
||||
<VnLv
|
||||
:label="t('ticket.summary.subtotal')"
|
||||
:value="toCurrency(ticket.totalWithoutVat)"
|
||||
:value="toCurrency(entity.totalWithoutVat)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('ticket.summary.vat')"
|
||||
:value="toCurrency(ticket.totalWithVat - ticket.totalWithoutVat)"
|
||||
:value="toCurrency(entity.totalWithVat - entity.totalWithoutVat)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('ticket.summary.total')"
|
||||
:value="toCurrency(ticket.totalWithVat)"
|
||||
:value="toCurrency(entity.totalWithVat)"
|
||||
/>
|
||||
</div>
|
||||
</QCard>
|
||||
|
@ -279,7 +271,7 @@ async function changeState(value) {
|
|||
:url="ticketUrl + 'sale'"
|
||||
:text="t('ticket.summary.saleLines')"
|
||||
/>
|
||||
<QTable :rows="ticket.sales" style="text-align: center">
|
||||
<QTable :rows="entity.sales" style="text-align: center">
|
||||
<template #body-cell="{ value }">
|
||||
<QTd>{{ value }}</QTd>
|
||||
</template>
|
||||
|
@ -383,7 +375,11 @@ async function changeState(value) {
|
|||
<QTd>
|
||||
<QBtn class="link" flat>
|
||||
{{ props.row.itemFk }}
|
||||
<ItemDescriptorProxy :id="props.row.itemFk" />
|
||||
<ItemDescriptorProxy
|
||||
:id="props.row.itemFk"
|
||||
:sale-fk="props.row.id"
|
||||
:warehouse-fk="ticket.warehouseFk"
|
||||
/>
|
||||
</QBtn>
|
||||
</QTd>
|
||||
<QTd>{{ props.row.visible }}</QTd>
|
||||
|
@ -419,10 +415,10 @@ async function changeState(value) {
|
|||
</QCard>
|
||||
<QCard
|
||||
class="vn-max"
|
||||
v-if="ticket.packagings.length > 0 || ticket.services.length > 0"
|
||||
v-if="entity.packagings.length || entity.services.length"
|
||||
>
|
||||
<VnTitle :url="ticketUrl + 'package'" :text="t('globals.packages')" />
|
||||
<QTable :rows="ticket.packagings" flat>
|
||||
<QTable :rows="entity.packagings" flat>
|
||||
<template #header="props">
|
||||
<QTr :props="props">
|
||||
<QTh auto-width>{{ t('ticket.summary.created') }}</QTh>
|
||||
|
@ -442,7 +438,7 @@ async function changeState(value) {
|
|||
:url="ticketUrl + 'service'"
|
||||
:text="t('ticket.summary.service')"
|
||||
/>
|
||||
<QTable :rows="ticket.services" flat>
|
||||
<QTable :rows="entity.services" flat>
|
||||
<template #header="props">
|
||||
<QTr :props="props">
|
||||
<QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh>
|
||||
|
|
|
@ -133,6 +133,14 @@ const ticketColumns = computed(() => [
|
|||
sortable: true,
|
||||
columnFilter: null,
|
||||
},
|
||||
{
|
||||
label: t('advanceTickets.preparation'),
|
||||
name: 'preparation',
|
||||
field: 'preparation',
|
||||
align: 'left',
|
||||
sortable: true,
|
||||
columnFilter: null,
|
||||
},
|
||||
{
|
||||
label: t('advanceTickets.liters'),
|
||||
name: 'liters',
|
||||
|
@ -624,6 +632,7 @@ onMounted(async () => {
|
|||
</QIcon>
|
||||
</QTd>
|
||||
</template>
|
||||
|
||||
<template #body-cell-ticketId="{ row }">
|
||||
<QTd>
|
||||
<QBtn flat class="link">
|
||||
|
@ -635,6 +644,7 @@ onMounted(async () => {
|
|||
<template #body-cell-state="{ row }">
|
||||
<QTd>
|
||||
<QBadge
|
||||
v-if="row.state"
|
||||
text-color="black"
|
||||
:color="row.classColor"
|
||||
class="q-ma-none"
|
||||
|
@ -642,6 +652,7 @@ onMounted(async () => {
|
|||
>
|
||||
{{ row.state }}
|
||||
</QBadge>
|
||||
<span v-else> {{ dashIfEmpty(row.state) }}</span>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-import="{ row }">
|
||||
|
|
|
@ -55,7 +55,7 @@ onMounted(async () => await getItemPackingTypes());
|
|||
:data-key="props.dataKey"
|
||||
:search-button="true"
|
||||
:hidden-tags="['search']"
|
||||
:un-removable-params="['warehouseFk', 'dateFuture', 'dateToAdvance']"
|
||||
:unremovable-params="['warehouseFk', 'dateFuture', 'dateToAdvance']"
|
||||
>
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<div class="q-gutter-x-xs">
|
||||
|
@ -119,10 +119,9 @@ onMounted(async () => await getItemPackingTypes());
|
|||
<QItem>
|
||||
<QItemSection>
|
||||
<QCheckbox
|
||||
:label="t('params.itemPackingTypes')"
|
||||
v-model="params.itemPackingTypes"
|
||||
:label="t('params.isFullMovable')"
|
||||
v-model="params.isFullMovable"
|
||||
toggle-indeterminate
|
||||
:false-value="null"
|
||||
@update:model-value="searchFn()"
|
||||
/>
|
||||
</QItemSection>
|
||||
|
@ -155,7 +154,7 @@ en:
|
|||
dateToAdvance: Destination date
|
||||
futureIpt: Origin IPT
|
||||
ipt: Destination IPT
|
||||
itemPackingTypes: 100% movable
|
||||
isFullMovable: 100% movable
|
||||
warehouseFk: Warehouse
|
||||
es:
|
||||
Horizontal: Horizontal
|
||||
|
@ -166,6 +165,6 @@ es:
|
|||
dateToAdvance: Fecha destino
|
||||
futureIpt: IPT Origen
|
||||
ipt: IPT destino
|
||||
itemPackingTypes: 100% movible
|
||||
isFullMovable: 100% movible
|
||||
warehouseFk: Almacén
|
||||
</i18n>
|
||||
|
|
|
@ -49,8 +49,8 @@ const exprBuilder = (param, value) => {
|
|||
};
|
||||
|
||||
const userParams = reactive({
|
||||
futureDated: Date.vnNew().toISOString(),
|
||||
originDated: Date.vnNew().toISOString(),
|
||||
futureScopeDays: Date.vnNew().toISOString(),
|
||||
originScopeDays: Date.vnNew().toISOString(),
|
||||
warehouseFk: user.value.warehouseFk,
|
||||
});
|
||||
|
||||
|
@ -62,8 +62,8 @@ const arrayData = useArrayData('FutureTickets', {
|
|||
const { store } = arrayData;
|
||||
|
||||
const params = reactive({
|
||||
futureDated: Date.vnNew(),
|
||||
originDated: Date.vnNew(),
|
||||
futureScopeDays: Date.vnNew(),
|
||||
originScopeDays: Date.vnNew(),
|
||||
warehouseFk: user.value.warehouseFk,
|
||||
});
|
||||
|
||||
|
@ -172,7 +172,7 @@ const ticketColumns = computed(() => [
|
|||
label: t('futureTickets.availableLines'),
|
||||
name: 'lines',
|
||||
field: 'lines',
|
||||
align: 'left',
|
||||
align: 'center',
|
||||
sortable: true,
|
||||
columnFilter: {
|
||||
component: VnInput,
|
||||
|
@ -234,7 +234,7 @@ const ticketColumns = computed(() => [
|
|||
{
|
||||
label: t('futureTickets.futureState'),
|
||||
name: 'futureState',
|
||||
align: 'left',
|
||||
align: 'right',
|
||||
sortable: true,
|
||||
columnFilter: null,
|
||||
format: (val) => dashIfEmpty(val),
|
||||
|
@ -458,7 +458,7 @@ onMounted(async () => {
|
|||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-shipped="{ row }">
|
||||
<QTd>
|
||||
<QTd class="shipped">
|
||||
<QBadge
|
||||
text-color="black"
|
||||
:color="getDateQBadgeColor(row.shipped)"
|
||||
|
@ -505,7 +505,7 @@ onMounted(async () => {
|
|||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-futureShipped="{ row }">
|
||||
<QTd>
|
||||
<QTd class="shipped">
|
||||
<QBadge
|
||||
text-color="black"
|
||||
:color="getDateQBadgeColor(row.futureShipped)"
|
||||
|
@ -532,6 +532,9 @@ onMounted(async () => {
|
|||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.shipped {
|
||||
min-width: 132px;
|
||||
}
|
||||
.vertical-separator {
|
||||
border-left: 4px solid white !important;
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ onMounted(async () => {
|
|||
<VnFilterPanel
|
||||
:data-key="props.dataKey"
|
||||
:hidden-tags="['search']"
|
||||
:un-removable-params="['warehouseFk', 'originDated', 'futureDated']"
|
||||
:un-removable-params="['warehouseFk', 'originScopeDays ', 'futureScopeDays']"
|
||||
>
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<div class="q-gutter-x-xs">
|
||||
|
@ -80,8 +80,8 @@ onMounted(async () => {
|
|||
<QItem class="q-my-sm">
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
v-model="params.originDated"
|
||||
:label="t('params.originDated')"
|
||||
v-model="params.originScopeDays"
|
||||
:label="t('params.originScopeDays')"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
|
@ -89,8 +89,8 @@ onMounted(async () => {
|
|||
<QItem class="q-my-sm">
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
v-model="params.futureDated"
|
||||
:label="t('params.futureDated')"
|
||||
v-model="params.futureScopeDays"
|
||||
:label="t('params.futureScopeDays')"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
|
@ -214,8 +214,8 @@ onMounted(async () => {
|
|||
en:
|
||||
iptInfo: IPT
|
||||
params:
|
||||
originDated: Origin date
|
||||
futureDated: Destination date
|
||||
originScopeDays: Origin date
|
||||
futureScopeDays: Destination date
|
||||
futureIpt: Destination IPT
|
||||
ipt: Origin IPT
|
||||
warehouseFk: Warehouse
|
||||
|
@ -229,8 +229,8 @@ es:
|
|||
Vertical: Vertical
|
||||
iptInfo: Encajado
|
||||
params:
|
||||
originDated: Fecha origen
|
||||
futureDated: Fecha destino
|
||||
originScopeDays: Fecha origen
|
||||
futureScopeDays: Fecha destino
|
||||
futureIpt: IPT destino
|
||||
ipt: IPT Origen
|
||||
warehouseFk: Almacén
|
||||
|
|
|
@ -105,7 +105,7 @@ advanceTickets:
|
|||
futureLines: Líneas
|
||||
futureImport: Importe
|
||||
advanceTickets: Adelantar tickets con negativos
|
||||
advanceTicketTitle: Advance tickets
|
||||
advanceTicketTitle: Adelantar tickets
|
||||
advanceTitleSubtitle: '¿Desea adelantar {selectedTickets} tickets?'
|
||||
noDeliveryZone: No hay una zona de reparto disponible para la fecha de envío seleccionada
|
||||
moveTicketSuccess: 'Tickets movidos correctamente {ticketsNumber}'
|
||||
|
|
|
@ -8,7 +8,7 @@ import VnConfirm from 'components/ui/VnConfirm.vue';
|
|||
|
||||
import axios from 'axios';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import { useRole } from 'src/composables/useRole';
|
||||
import { useAcl } from 'src/composables/useAcl';
|
||||
|
||||
const $props = defineProps({
|
||||
travel: {
|
||||
|
@ -21,7 +21,6 @@ const { t } = useI18n();
|
|||
const router = useRouter();
|
||||
const quasar = useQuasar();
|
||||
const { notify } = useNotify();
|
||||
const role = useRole();
|
||||
|
||||
const redirectToCreateView = (queryParams) => {
|
||||
router.push({ name: 'TravelCreate', query: { travelData: queryParams } });
|
||||
|
@ -42,9 +41,7 @@ const cloneTravelWithEntries = async () => {
|
|||
}
|
||||
};
|
||||
|
||||
const isBuyer = computed(() => {
|
||||
return role.hasAny(['buyer']);
|
||||
});
|
||||
const canDelete = computed(() => useAcl().hasAny('Travel','*','WRITE'));
|
||||
|
||||
const openDeleteEntryDialog = (id) => {
|
||||
quasar
|
||||
|
@ -81,7 +78,7 @@ const deleteTravel = async (id) => {
|
|||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-if="isBuyer && travel.totalEntries === 0"
|
||||
v-if="canDelete && travel.totalEntries === 0"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="openDeleteEntryDialog(travel.id)"
|
||||
|
|
|
@ -15,5 +15,6 @@ import WorkerFilter from '../WorkerFilter.vue';
|
|||
label: 'Search worker',
|
||||
info: 'You can search by worker id or name',
|
||||
}"
|
||||
:redirect-on-error="true"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
|
||||
import WorkerChangePasswordForm from 'src/pages/Worker/Card/WorkerChangePasswordForm.vue';
|
||||
import useCardDescription from 'src/composables/useCardDescription';
|
||||
import { useState } from 'src/composables/useState';
|
||||
import axios from 'axios';
|
||||
import VnImg from 'src/components/ui/VnImg.vue';
|
||||
|
@ -38,49 +37,6 @@ const entityId = computed(() => {
|
|||
|
||||
const worker = ref();
|
||||
const workerExcluded = ref(false);
|
||||
const filter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['email', 'name', 'nickname'],
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'department',
|
||||
scope: {
|
||||
include: [
|
||||
{
|
||||
relation: 'department',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'sip',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const sip = ref(null);
|
||||
watch(
|
||||
() => [worker.value?.sip?.extension, state.get('extension')],
|
||||
([newWorkerSip, newStateExtension], [oldWorkerSip, oldStateExtension]) => {
|
||||
if (newStateExtension !== oldStateExtension || sip.value === oldStateExtension) {
|
||||
sip.value = newStateExtension;
|
||||
} else if (newWorkerSip !== oldWorkerSip && sip.value !== newStateExtension) {
|
||||
sip.value = newWorkerSip;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const data = ref(useCardDescription());
|
||||
const setData = (entity) => {
|
||||
if (!entity) return;
|
||||
data.value = useCardDescription(entity.user?.nickname, entity.id);
|
||||
};
|
||||
|
||||
const openChangePasswordForm = () => changePasswordFormDialog.value.show();
|
||||
|
||||
const getIsExcluded = async () => {
|
||||
try {
|
||||
|
@ -115,14 +71,12 @@ const refetch = async () => await cardDescriptorRef.value.getData();
|
|||
ref="cardDescriptorRef"
|
||||
module="Worker"
|
||||
data-key="workerData"
|
||||
:url="`Workers/${entityId}`"
|
||||
:filter="filter"
|
||||
:title="data.title"
|
||||
:subtitle="data.subtitle"
|
||||
url="Workers/descriptor"
|
||||
:filter="{ where: { id: entityId } }"
|
||||
title="user.nickname"
|
||||
@on-fetch="
|
||||
(data) => {
|
||||
worker = data;
|
||||
setData(data);
|
||||
getIsExcluded();
|
||||
}
|
||||
"
|
||||
|
@ -141,7 +95,7 @@ const refetch = async () => await cardDescriptorRef.value.getData();
|
|||
v-if="!worker.user.emailVerified && user.id != worker.id"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="openChangePasswordForm()"
|
||||
@click="$refs.changePasswordFormDialog.show()"
|
||||
>
|
||||
<QItemSection>
|
||||
{{ t('Change password') }}
|
||||
|
@ -209,10 +163,10 @@ const refetch = async () => await cardDescriptorRef.value.getData();
|
|||
<VnLinkPhone :phone-number="entity.phone" />
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv :value="sip">
|
||||
<VnLv :value="worker?.sip?.extension">
|
||||
<template #label>
|
||||
{{ t('worker.summary.sipExtension') }}
|
||||
<VnLinkPhone v-if="sip" :phone-number="sip" />
|
||||
<VnLinkPhone :phone-number="worker?.sip?.extension" />
|
||||
</template>
|
||||
</VnLv>
|
||||
</template>
|
||||
|
|
|
@ -3,13 +3,13 @@ import { ref, computed } from 'vue';
|
|||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import axios from 'axios';
|
||||
import { useRole } from 'src/composables/useRole';
|
||||
import { useAcl } from 'src/composables/useAcl';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
|
||||
const { hasAny } = useRole();
|
||||
const { hasAny } = useAcl();
|
||||
const { t } = useI18n();
|
||||
const fetchData = ref();
|
||||
const originaLockerId = ref();
|
||||
|
@ -57,7 +57,11 @@ const init = async (data) => {
|
|||
option-label="code"
|
||||
option-value="id"
|
||||
hide-selected
|
||||
:readonly="!hasAny(['productionBoss', 'hr'])"
|
||||
:readonly="
|
||||
!hasAny([
|
||||
{ model: 'Worker', props: '__get__locker', accessType: 'READ' },
|
||||
])
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
</FormModel>
|
||||
|
|
|
@ -34,13 +34,22 @@ const filter = {
|
|||
{
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['email', 'name', 'nickname', 'roleFk'],
|
||||
include: {
|
||||
relation: 'role',
|
||||
scope: {
|
||||
fields: ['name'],
|
||||
fields: ['name', 'nickname', 'roleFk'],
|
||||
|
||||
include: [
|
||||
{
|
||||
relation: 'role',
|
||||
scope: {
|
||||
fields: ['name'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'emailUser',
|
||||
scope: {
|
||||
fields: ['email'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -153,14 +162,18 @@ const filter = {
|
|||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<VnTitle :text="t('worker.summary.userData')" />
|
||||
<VnLv :label="t('worker.summary.userId')" :value="worker.user.id" />
|
||||
<VnLv :label="t('worker.card.name')" :value="worker.user.nickname" />
|
||||
<VnLv :label="t('worker.list.email')" :value="worker.user.email" copy />
|
||||
<VnLv :label="t('worker.summary.userId')" :value="worker?.user?.id" />
|
||||
<VnLv :label="t('worker.card.name')" :value="worker?.user?.nickname" />
|
||||
<VnLv
|
||||
:label="t('worker.list.email')"
|
||||
:value="worker.user?.emailUser?.email"
|
||||
copy
|
||||
/>
|
||||
<VnLv :label="t('worker.summary.role')">
|
||||
<template #value>
|
||||
<span class="link">
|
||||
{{ worker.user.role.name }}
|
||||
<RoleDescriptorProxy :id="worker.user.role.id" />
|
||||
{{ worker?.user?.role?.name }}
|
||||
<RoleDescriptorProxy :id="worker?.user?.role?.id" />
|
||||
</span>
|
||||
</template>
|
||||
</VnLv>
|
||||
|
|
|
@ -13,6 +13,7 @@ import WorkerTimeControlCalendar from 'pages/Worker/Card/WorkerTimeControlCalend
|
|||
import useNotify from 'src/composables/useNotify.js';
|
||||
import axios from 'axios';
|
||||
import { useRole } from 'src/composables/useRole';
|
||||
import { useAcl } from 'src/composables/useAcl';
|
||||
import { useWeekdayStore } from 'src/stores/useWeekdayStore';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { useState } from 'src/composables/useState';
|
||||
|
@ -26,7 +27,6 @@ import { date } from 'quasar';
|
|||
const route = useRoute();
|
||||
const { t, locale } = useI18n();
|
||||
const { notify } = useNotify();
|
||||
const { hasAny } = useRole();
|
||||
const _state = useState();
|
||||
const user = _state.getUser();
|
||||
const stateStore = useStateStore();
|
||||
|
@ -66,9 +66,11 @@ const arrayData = useArrayData('workerData');
|
|||
|
||||
const worker = computed(() => arrayData.store?.data);
|
||||
|
||||
const isHr = computed(() => hasAny(['hr']));
|
||||
const isHr = computed(() => useRole().hasAny(['hr']));
|
||||
|
||||
const isHimSelf = computed(() => user.value.id === Number(route.params.id));
|
||||
const canSend = computed(() => useAcl().hasAny('WorkerTimeControl', 'sendMail', 'WRITE'));
|
||||
|
||||
const isHimself = computed(() => user.value.id === Number(route.params.id));
|
||||
|
||||
const columns = computed(() => {
|
||||
return weekdayStore.getLocales?.map((day, index) => {
|
||||
|
@ -447,7 +449,7 @@ onMounted(async () => {
|
|||
<div>
|
||||
<QBtnGroup push class="q-gutter-x-sm" flat>
|
||||
<QBtn
|
||||
v-if="isHimSelf && state"
|
||||
v-if="isHimself && state"
|
||||
:label="t('Satisfied')"
|
||||
color="primary"
|
||||
type="submit"
|
||||
|
@ -455,7 +457,7 @@ onMounted(async () => {
|
|||
@click="isSatisfied()"
|
||||
/>
|
||||
<QBtn
|
||||
v-if="isHimSelf && state"
|
||||
v-if="isHimself && state"
|
||||
:label="t('Not satisfied')"
|
||||
color="primary"
|
||||
type="submit"
|
||||
|
@ -466,14 +468,14 @@ onMounted(async () => {
|
|||
</QBtnGroup>
|
||||
<QBtnGroup push class="q-gutter-x-sm q-ml-none" flat>
|
||||
<QBtn
|
||||
v-if="reason && state && (isHimSelf || isHr)"
|
||||
v-if="reason && state && (isHimself || isHr)"
|
||||
:label="t('Reason')"
|
||||
color="primary"
|
||||
type="submit"
|
||||
@click="showReasonForm()"
|
||||
/>
|
||||
<QBtn
|
||||
v-if="isHr && state !== 'CONFIRMED' && canResend"
|
||||
v-if="canSend && state !== 'CONFIRMED' && canResend"
|
||||
:label="state ? t('Resend') : t('globals.send')"
|
||||
color="primary"
|
||||
type="submit"
|
||||
|
@ -603,7 +605,7 @@ onMounted(async () => {
|
|||
<WorkerTimeReasonForm
|
||||
@on-submit="isUnsatisfied($event)"
|
||||
:reason="reason"
|
||||
:is-him-self="isHimSelf"
|
||||
:is-himself="isHimself"
|
||||
/>
|
||||
</QDialog>
|
||||
</QPage>
|
||||
|
@ -629,6 +631,9 @@ onMounted(async () => {
|
|||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
:deep(.q-td) {
|
||||
min-width: 170px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
|
|
|
@ -9,7 +9,7 @@ const $props = defineProps({
|
|||
type: String,
|
||||
default: '',
|
||||
},
|
||||
isHimSelf: {
|
||||
isHimself: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
@ -40,7 +40,7 @@ const closeForm = () => {
|
|||
v-model="reasonFormData"
|
||||
type="textarea"
|
||||
autogrow
|
||||
:disable="!isHimSelf"
|
||||
:disable="!isHimself"
|
||||
/>
|
||||
</template>
|
||||
</FormPopup>
|
||||
|
|
|
@ -262,7 +262,7 @@ async function autofillBic(worker) {
|
|||
</VnRow>
|
||||
<VnRow>
|
||||
<VnLocation
|
||||
:roles-allowed-to-create="['deliveryAssistant']"
|
||||
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
|
||||
:options="postcodesOptions"
|
||||
v-model="data.location"
|
||||
@update:model-value="(location) => handleLocation(data, location)"
|
||||
|
@ -311,7 +311,7 @@ async function autofillBic(worker) {
|
|||
option-label="name"
|
||||
option-value="id"
|
||||
hide-selected
|
||||
:roles-allowed-to-create="['salesAssistant', 'hr']"
|
||||
:acls="[{ model: 'BankEntity', props: '*', accessType: 'WRITE' }]"
|
||||
:disable="data.isFreelance"
|
||||
@update:model-value="autofillBic(data)"
|
||||
:filter-options="['bic', 'name']"
|
||||
|
|
|
@ -60,15 +60,12 @@ export default route(function (/* { store, ssrContext } */) {
|
|||
await useTokenConfig().fetch();
|
||||
}
|
||||
const matches = to.matched;
|
||||
const hasRequiredRoles = matches.every((route) => {
|
||||
const hasRequiredAcls = matches.every((route) => {
|
||||
const meta = route.meta;
|
||||
if (meta && meta.roles) return useRole().hasAny(meta.roles);
|
||||
return true;
|
||||
if (!meta?.acls) return true;
|
||||
return useAcl().hasAny(meta.acls);
|
||||
});
|
||||
|
||||
if (!hasRequiredRoles) {
|
||||
return next({ path: '/' });
|
||||
}
|
||||
if (!hasRequiredAcls) return next({ path: '/' });
|
||||
}
|
||||
|
||||
next();
|
||||
|
|
|
@ -7,6 +7,7 @@ export default {
|
|||
title: 'suppliers',
|
||||
icon: 'vn:supplier',
|
||||
moduleName: 'Supplier',
|
||||
keyBinding: 'p',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'SupplierMain' },
|
||||
|
|
|
@ -7,6 +7,7 @@ export default {
|
|||
title: 'users',
|
||||
icon: 'face',
|
||||
moduleName: 'Account',
|
||||
keyBinding: 'u',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'AccountMain' },
|
||||
|
@ -79,7 +80,7 @@ export default {
|
|||
meta: {
|
||||
title: 'accounts',
|
||||
icon: 'accessibility',
|
||||
roles: ['itManagement'],
|
||||
acls: [{ model: 'Account', props: '*', accessType: '*' }],
|
||||
},
|
||||
component: () => import('src/pages/Account/AccountAccounts.vue'),
|
||||
},
|
||||
|
@ -89,7 +90,7 @@ export default {
|
|||
meta: {
|
||||
title: 'ldap',
|
||||
icon: 'account_tree',
|
||||
roles: ['itManagement'],
|
||||
acls: [{ model: 'LdapConfig', props: '*', accessType: '*' }],
|
||||
},
|
||||
component: () => import('src/pages/Account/AccountLdap.vue'),
|
||||
},
|
||||
|
@ -99,7 +100,7 @@ export default {
|
|||
meta: {
|
||||
title: 'samba',
|
||||
icon: 'preview',
|
||||
roles: ['itManagement'],
|
||||
acls: [{ model: 'SambaConfig', props: '*', accessType: '*' }],
|
||||
},
|
||||
component: () => import('src/pages/Account/AccountSamba.vue'),
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@ export default {
|
|||
title: 'claims',
|
||||
icon: 'vn:claims',
|
||||
moduleName: 'Claim',
|
||||
keyBinding: 'r',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'ClaimMain' },
|
||||
|
@ -61,7 +62,7 @@ export default {
|
|||
meta: {
|
||||
title: 'basicData',
|
||||
icon: 'vn:settings',
|
||||
roles: ['salesPerson'],
|
||||
acls: [{ model: 'Claim', props: 'findById', accessType: 'READ' }],
|
||||
},
|
||||
component: () => import('src/pages/Claim/Card/ClaimBasicData.vue'),
|
||||
},
|
||||
|
@ -98,7 +99,13 @@ export default {
|
|||
meta: {
|
||||
title: 'development',
|
||||
icon: 'vn:traceability',
|
||||
roles: ['claimManager'],
|
||||
acls: [
|
||||
{
|
||||
model: 'ClaimDevelopment',
|
||||
props: '*',
|
||||
accessType: 'WRITE',
|
||||
},
|
||||
],
|
||||
},
|
||||
component: () => import('src/pages/Claim/Card/ClaimDevelopment.vue'),
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@ export default {
|
|||
title: 'customers',
|
||||
icon: 'vn:client',
|
||||
moduleName: 'Customer',
|
||||
keyBinding: 'c',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'CustomerMain' },
|
||||
|
|
|
@ -7,6 +7,7 @@ export default {
|
|||
title: 'entries',
|
||||
icon: 'vn:entry',
|
||||
moduleName: 'Entry',
|
||||
keyBinding: 'e',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'EntryMain' },
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { RouterView } from 'vue-router';
|
||||
|
||||
import { setRectificative } from 'src/pages/InvoiceIn/composables/setRectificative';
|
||||
export default {
|
||||
path: '/invoice-in',
|
||||
name: 'InvoiceIn',
|
||||
|
@ -63,6 +63,10 @@ export default {
|
|||
path: ':id',
|
||||
component: () => import('src/pages/InvoiceIn/Card/InvoiceInCard.vue'),
|
||||
redirect: { name: 'InvoiceInSummary' },
|
||||
beforeEnter: async (to, from, next) => {
|
||||
await setRectificative(to);
|
||||
next();
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'InvoiceInSummary',
|
||||
|
@ -80,7 +84,6 @@ export default {
|
|||
meta: {
|
||||
title: 'basicData',
|
||||
icon: 'vn:settings',
|
||||
roles: ['salesPerson'],
|
||||
},
|
||||
component: () =>
|
||||
import('src/pages/InvoiceIn/Card/InvoiceInBasicData.vue'),
|
||||
|
|
|
@ -7,6 +7,7 @@ export default {
|
|||
title: 'items',
|
||||
icon: 'vn:item',
|
||||
moduleName: 'Item',
|
||||
keyBinding: 'a',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'ItemMain' },
|
||||
|
|
|
@ -7,6 +7,7 @@ export default {
|
|||
title: 'monitors',
|
||||
icon: 'grid_view',
|
||||
moduleName: 'Monitor',
|
||||
keyBinding: 'm',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'MonitorMain' },
|
||||
|
|
|
@ -7,6 +7,7 @@ export default {
|
|||
title: 'order',
|
||||
icon: 'vn:basket',
|
||||
moduleName: 'Order',
|
||||
keyBinding: 'o',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'OrderMain' },
|
||||
|
|
|
@ -7,7 +7,6 @@ export default {
|
|||
title: 'routes',
|
||||
icon: 'vn:delivery',
|
||||
moduleName: 'Route',
|
||||
keyBinding: 'r',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'RouteMain' },
|
||||
|
|
|
@ -76,7 +76,6 @@ export default {
|
|||
meta: {
|
||||
title: 'basicData',
|
||||
icon: 'vn:settings',
|
||||
roles: ['salesPerson'],
|
||||
},
|
||||
component: () => import('pages/Shelving/Card/ShelvingForm.vue'),
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@ export default {
|
|||
title: 'tickets',
|
||||
icon: 'vn:ticket',
|
||||
moduleName: 'Ticket',
|
||||
keyBinding: 't',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'TicketMain' },
|
||||
|
@ -53,7 +54,6 @@ export default {
|
|||
meta: {
|
||||
title: 'createTicket',
|
||||
icon: 'vn:ticketAdd',
|
||||
roles: ['developer'],
|
||||
},
|
||||
component: () => import('src/pages/Ticket/TicketCreate.vue'),
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@ export default {
|
|||
title: 'workers',
|
||||
icon: 'vn:worker',
|
||||
moduleName: 'Worker',
|
||||
keyBinding: 'w',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'WorkerMain' },
|
||||
|
|
|
@ -7,6 +7,7 @@ export default {
|
|||
title: 'zones',
|
||||
icon: 'vn:zone',
|
||||
moduleName: 'Zone',
|
||||
keyBinding: 'z',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'ZoneMain' },
|
||||
|
|
|
@ -2,7 +2,7 @@ import axios from 'axios';
|
|||
import { ref } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { toLowerCamel } from 'src/filters';
|
||||
import { useRole } from 'src/composables/useRole';
|
||||
import { useAcl } from 'src/composables/useAcl';
|
||||
import routes from 'src/router/modules';
|
||||
|
||||
export const useNavigationStore = defineStore('navigationStore', () => {
|
||||
|
@ -26,7 +26,7 @@ export const useNavigationStore = defineStore('navigationStore', () => {
|
|||
'zone',
|
||||
];
|
||||
const pinnedModules = ref([]);
|
||||
const role = useRole();
|
||||
const acl = useAcl();
|
||||
|
||||
function getModules() {
|
||||
const modulesRoutes = ref([]);
|
||||
|
@ -56,6 +56,7 @@ export const useNavigationStore = defineStore('navigationStore', () => {
|
|||
function addMenuItem(module, route, parent) {
|
||||
const { meta } = route;
|
||||
let { menuChildren = null } = meta;
|
||||
if (meta.hidden) return;
|
||||
if (menuChildren)
|
||||
menuChildren = menuChildren.map(({ name, title, icon }) => ({
|
||||
name,
|
||||
|
@ -63,7 +64,7 @@ export const useNavigationStore = defineStore('navigationStore', () => {
|
|||
title: `globals.pageTitles.${title}`,
|
||||
}));
|
||||
|
||||
if (meta && meta.roles && role.hasAny(meta.roles) === false) return;
|
||||
if (meta && meta.acls && acl.hasAny(meta.acls) === false) return;
|
||||
|
||||
const item = {
|
||||
name: route.name,
|
||||
|
@ -72,6 +73,7 @@ export const useNavigationStore = defineStore('navigationStore', () => {
|
|||
if (meta) {
|
||||
item.title = `globals.pageTitles.${meta.title}`;
|
||||
item.icon = meta.icon;
|
||||
item.keyBinding = meta.keyBinding;
|
||||
}
|
||||
|
||||
parent.push(item);
|
||||
|
|
|
@ -36,8 +36,7 @@ describe('InvoiceInBasicData', () => {
|
|||
});
|
||||
|
||||
it('should throw an error creating a new dms if a file is not attached', () => {
|
||||
cy.get(formInputs).eq(5).click();
|
||||
cy.get(formInputs).eq(5).type('{selectall}{backspace}');
|
||||
cy.get(formInputs).eq(7).type('{selectall}{backspace}');
|
||||
cy.get(documentBtns).eq(0).click();
|
||||
cy.get(dialogActionBtns).eq(1).click();
|
||||
cy.get('.q-notification__message').should(
|
||||
|
|
|
@ -3,13 +3,14 @@ describe('InvoiceInVat', () => {
|
|||
const thirdRow = 'tbody > :nth-child(3)';
|
||||
const firstLineVat = 'tbody > :nth-child(1) > :nth-child(4)';
|
||||
const dialogInputs = '.q-dialog label input';
|
||||
const dialogBtns = '.q-dialog button';
|
||||
const acrossInput = 'tbody tr:nth-child(1) td:nth-child(2) .default-icon';
|
||||
const addBtn = 'tbody tr:nth-child(1) td:nth-child(2) .--add-icon';
|
||||
const randomInt = Math.floor(Math.random() * 100);
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login('developer');
|
||||
cy.visit(`/#/invoice-in/1/vat`);
|
||||
cy.intercept('GET', '/api/InvoiceIns/1/getTotals').as('lastCall');
|
||||
cy.wait('@lastCall');
|
||||
});
|
||||
|
||||
it('should edit the sage iva', () => {
|
||||
|
@ -26,22 +27,15 @@ describe('InvoiceInVat', () => {
|
|||
});
|
||||
|
||||
it('should remove the first line', () => {
|
||||
cy.removeRow(2);
|
||||
});
|
||||
|
||||
it('should throw an error if there are fields undefined', () => {
|
||||
cy.get(acrossInput).click();
|
||||
cy.get(dialogBtns).eq(2).click();
|
||||
cy.get('.q-notification__message').should('have.text', "The code can't be empty");
|
||||
cy.removeRow(1);
|
||||
});
|
||||
|
||||
it('should correctly handle expense addition', () => {
|
||||
cy.get(acrossInput).click();
|
||||
cy.get(addBtn).click();
|
||||
cy.get(dialogInputs).eq(0).type(randomInt);
|
||||
cy.get(dialogInputs).eq(1).click();
|
||||
cy.get(dialogInputs).eq(1).type('This is a dummy expense');
|
||||
|
||||
cy.get(dialogBtns).eq(2).click();
|
||||
cy.get('.q-notification__message').should('have.text', 'Data saved');
|
||||
cy.get('button[type="submit"]').click();
|
||||
cy.get('.q-notification__message').should('have.text', 'Data created');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -52,9 +52,9 @@ describe('Login', () => {
|
|||
cy.url().should('contain', '/login');
|
||||
});
|
||||
|
||||
it(`should get redirected to dashboard since employee can't create tickets`, () => {
|
||||
cy.visit('/#/ticket/create', { failOnStatusCode: false });
|
||||
cy.url().should('contain', '/#/login?redirect=/ticket/create');
|
||||
it(`should be redirected to dashboard since the employee is not enabled to see ldap`, () => {
|
||||
cy.visit('/#/account/ldap', { failOnStatusCode: false });
|
||||
cy.url().should('contain', '/#/login?redirect=/account/ldap');
|
||||
cy.get('input[aria-label="Username"]').type('employee');
|
||||
cy.get('input[aria-label="Password"]').type('nightmare');
|
||||
cy.get('button[type="submit"]').click();
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('Ticket descriptor', () => {
|
||||
const toCloneOpt = '[role="menu"] .q-list > :nth-child(5)';
|
||||
const setWeightOpt = '[role="menu"] .q-list > :nth-child(6)';
|
||||
const warehouseValue = ':nth-child(1) > :nth-child(6) > .value > span';
|
||||
const summaryHeader = '.summaryHeader > div';
|
||||
|
||||
const weight = 25;
|
||||
const weightValue = ':nth-child(10) > .value > span';
|
||||
beforeEach(() => {
|
||||
const ticketId = 1;
|
||||
|
||||
cy.login('developer');
|
||||
cy.visit(`/#/ticket/${ticketId}/summary`);
|
||||
cy.viewport(1920, 1080);
|
||||
});
|
||||
|
||||
it('should clone the ticket without warehouse', () => {
|
||||
cy.openLeftMenu();
|
||||
cy.visit('/#/ticket/1/summary');
|
||||
cy.openActionsDescriptor();
|
||||
cy.get(toCloneOpt).click();
|
||||
cy.clickConfirm();
|
||||
|
@ -24,4 +24,15 @@ describe('Ticket descriptor', () => {
|
|||
cy.wrap(owner.trim()).should('eq', 'Bruce Wayne (1101)');
|
||||
});
|
||||
});
|
||||
|
||||
it('should set the weight of the ticket', () => {
|
||||
cy.visit('/#/ticket/10/summary');
|
||||
cy.openActionsDescriptor();
|
||||
cy.get(setWeightOpt).click();
|
||||
cy.intercept('POST', /\/api\/Tickets\/\d+\/setWeight/).as('weight');
|
||||
cy.get('.q-dialog input').type(weight);
|
||||
cy.clickConfirm();
|
||||
cy.wait('@weight');
|
||||
cy.get(weightValue).contains(weight);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
describe('VnShortcuts', () => {
|
||||
const modules = {
|
||||
item: 'a',
|
||||
customer: 'c',
|
||||
ticket: 't',
|
||||
claim: 'r',
|
||||
worker: 'w',
|
||||
monitor: 'm',
|
||||
order: 'o',
|
||||
supplier: 'p',
|
||||
entry: 'e',
|
||||
zone: 'z',
|
||||
account: 'u',
|
||||
};
|
||||
beforeEach(() => {
|
||||
cy.login('developer');
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
for (const [module, shortcut] of Object.entries(modules)) {
|
||||
it(`should visit ${module} module`, () => {
|
||||
cy.get('body').trigger('keydown', {
|
||||
ctrlKey: true,
|
||||
altKey: true,
|
||||
code: `Key${shortcut.toUpperCase()}`,
|
||||
});
|
||||
|
||||
cy.url().should('include', module);
|
||||
});
|
||||
}
|
||||
});
|
|
@ -3,6 +3,7 @@ describe('WagonTypeCreate', () => {
|
|||
cy.viewport(1920, 1080);
|
||||
cy.login('developer');
|
||||
cy.visit('/#/wagon/type/create');
|
||||
cy.waitForElement('.q-page', 6000);
|
||||
});
|
||||
|
||||
function chooseColor(color) {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
describe('WorkerLocker', () => {
|
||||
const workerId = 1109;
|
||||
const productionId = 49;
|
||||
const lockerCode = '2F';
|
||||
const input = '.q-card input';
|
||||
const thirdOpt = '[role="listbox"] .q-item:nth-child(1)';
|
||||
beforeEach(() => {
|
||||
cy.viewport(1280, 720);
|
||||
cy.login('productionBoss');
|
||||
cy.visit(`/#/worker/${workerId}/locker`);
|
||||
cy.visit(`/#/worker/${productionId}/locker`);
|
||||
});
|
||||
|
||||
it('should allocates a locker', () => {
|
||||
|
|
|
@ -48,40 +48,62 @@ describe('useAcl', () => {
|
|||
|
||||
describe('hasAny', () => {
|
||||
it('should return false if no roles matched', async () => {
|
||||
expect(acl.hasAny('Worker', 'updateAttributes', 'WRITE')).toBeFalsy();
|
||||
expect(
|
||||
acl.hasAny([
|
||||
{ model: 'Worker', props: 'updateAttributes', accessType: 'WRITE' },
|
||||
])
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return false if no roles matched', async () => {
|
||||
expect(acl.hasAny('Worker', 'holidays', 'READ')).toBeTruthy();
|
||||
expect(
|
||||
acl.hasAny([{ model: 'Worker', props: 'holidays', accessType: 'READ' }])
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('*', () => {
|
||||
it('should return true if an acl matched', async () => {
|
||||
expect(acl.hasAny('Address', '*', 'WRITE')).toBeTruthy();
|
||||
expect(
|
||||
acl.hasAny([{ model: 'Address', props: '*', accessType: 'WRITE' }])
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return false if no acls matched', async () => {
|
||||
expect(acl.hasAny('Worker', '*', 'READ')).toBeFalsy();
|
||||
expect(
|
||||
acl.hasAny([{ model: 'Worker', props: '*', accessType: 'READ' }])
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('$authenticated', () => {
|
||||
it('should return false if no acls matched', async () => {
|
||||
expect(acl.hasAny('Url', 'getByUser', '*')).toBeFalsy();
|
||||
expect(
|
||||
acl.hasAny([{ model: 'Url', props: 'getByUser', accessType: '*' }])
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return true if an acl matched', async () => {
|
||||
expect(acl.hasAny('Url', 'getByUser', 'READ')).toBeTruthy();
|
||||
expect(
|
||||
acl.hasAny([{ model: 'Url', props: 'getByUser', accessType: 'READ' }])
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('$everyone', () => {
|
||||
it('should return false if no acls matched', async () => {
|
||||
expect(acl.hasAny('TpvTransaction', 'start', 'READ')).toBeFalsy();
|
||||
expect(
|
||||
acl.hasAny([
|
||||
{ model: 'TpvTransaction', props: 'start', accessType: 'READ' },
|
||||
])
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return false if an acl matched', async () => {
|
||||
expect(acl.hasAny('TpvTransaction', 'start', 'WRITE')).toBeTruthy();
|
||||
expect(
|
||||
acl.hasAny([
|
||||
{ model: 'TpvTransaction', props: 'start', accessType: 'WRITE' },
|
||||
])
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
?