|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "salix-front",
|
||||
"version": "24.36.0",
|
||||
"version": "24.40.0",
|
||||
"description": "Salix frontend",
|
||||
"productName": "Salix",
|
||||
"author": "Verdnatura",
|
||||
|
|
|
@ -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 () => {
|
||||
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,6 +398,16 @@ watch(
|
|||
"
|
||||
auto-load
|
||||
/>
|
||||
<VnPaginate
|
||||
ref="paginate"
|
||||
:data-key="`${model}Log`"
|
||||
:url="`${model}Logs`"
|
||||
:filter="filter"
|
||||
:skeleton="false"
|
||||
auto-load
|
||||
@on-fetch="setLogTree"
|
||||
>
|
||||
<template #body>
|
||||
<div
|
||||
class="column items-center logs origin-log q-mt-md"
|
||||
v-for="(originLog, originLogIndex) in logTree"
|
||||
|
@ -465,8 +455,10 @@ watch(
|
|||
size="md"
|
||||
class="model-name q-mr-xs text-white"
|
||||
v-if="
|
||||
!(modelLog.changedModel && modelLog.changedModelId) &&
|
||||
modelLog.model
|
||||
!(
|
||||
modelLog.changedModel &&
|
||||
modelLog.changedModelId
|
||||
) && modelLog.model
|
||||
"
|
||||
:style="{
|
||||
backgroundColor: useColor(modelLog.model),
|
||||
|
@ -540,7 +532,9 @@ watch(
|
|||
>
|
||||
{{ modelLog.modelI18n }}
|
||||
<span v-if="modelLog.id"
|
||||
>#{{ modelLog.id }}</span
|
||||
>#{{
|
||||
modelLog.id
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
<QCardSection
|
||||
|
@ -555,12 +549,18 @@ watch(
|
|||
>
|
||||
<span
|
||||
class="json-field q-mr-xs text-grey"
|
||||
:title="value.name"
|
||||
:title="
|
||||
value.name
|
||||
"
|
||||
>
|
||||
{{ value.nameI18n }}:
|
||||
{{
|
||||
value.nameI18n
|
||||
}}:
|
||||
</span>
|
||||
<VnJsonValue
|
||||
:value="value.val.val"
|
||||
:value="
|
||||
value.val.val
|
||||
"
|
||||
/>
|
||||
</QItem>
|
||||
</QCardSection>
|
||||
|
@ -572,7 +572,11 @@ watch(
|
|||
:class="actionsClass[log.action]"
|
||||
:name="actionsIcon[log.action]"
|
||||
:title="
|
||||
t(`actions.${actionsText[log.action]}`)
|
||||
t(
|
||||
`actions.${
|
||||
actionsText[log.action]
|
||||
}`
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
@ -592,17 +596,27 @@ watch(
|
|||
@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-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">
|
||||
<span
|
||||
class="json-field"
|
||||
:title="prop.name"
|
||||
>
|
||||
{{ prop.nameI18n }}:
|
||||
</span>
|
||||
<VnJsonValue :value="prop.val.val" />
|
||||
<span v-if="propIndex < log.props.length - 1"
|
||||
<span
|
||||
v-if="
|
||||
propIndex <
|
||||
log.props.length - 1
|
||||
"
|
||||
>,
|
||||
</span>
|
||||
</span>
|
||||
|
@ -612,28 +626,44 @@ watch(
|
|||
class="expanded-json column q-pa-none"
|
||||
>
|
||||
<div
|
||||
v-for="(prop, prop2Index) in log.props"
|
||||
v-for="(
|
||||
prop, prop2Index
|
||||
) in log.props"
|
||||
:key="prop2Index"
|
||||
class="q-pa-none text-grey"
|
||||
>
|
||||
<span class="json-field" :title="prop.name">
|
||||
<span
|
||||
class="json-field"
|
||||
:title="prop.name"
|
||||
>
|
||||
{{ prop.nameI18n }}:
|
||||
</span>
|
||||
<VnJsonValue :value="prop.val.val" />
|
||||
<span v-if="prop.val.id" class="id-value">
|
||||
<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">
|
||||
<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">
|
||||
<span
|
||||
v-if="!log.props.length"
|
||||
class="description"
|
||||
>
|
||||
{{ log.description }}
|
||||
</span>
|
||||
</QCardSection>
|
||||
|
@ -643,6 +673,8 @@ watch(
|
|||
</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,7 +220,6 @@ function sanitizer(params) {
|
|||
</QItemLabel>
|
||||
</QItemSection>
|
||||
<QItemSection top side>
|
||||
<div class="q-gutter-xs">
|
||||
<QBtn
|
||||
@click="clearFilters"
|
||||
color="primary"
|
||||
|
@ -244,19 +232,6 @@ function sanitizer(params) {
|
|||
>
|
||||
<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>
|
||||
</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 {
|
||||
&: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,14 +16,19 @@ 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];
|
||||
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 {
|
||||
fetch,
|
||||
|
|
|
@ -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) => {
|
||||
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
|
||||
|
|
|
@ -1,175 +1,230 @@
|
|||
<script setup>
|
||||
import { onMounted, ref, reactive, onUnmounted, nextTick, computed } from 'vue';
|
||||
import { ref, reactive, computed, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { toDate } from 'src/filters';
|
||||
import { useQuasar } from 'quasar';
|
||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||
import FetchedTags from 'components/ui/FetchedTags.vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import VnTable from 'components/VnTable/VnTable.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
import EditTableCellValueForm from 'src/components/EditTableCellValueForm.vue';
|
||||
import ItemFixedPriceFilter from './ItemFixedPriceFilter.vue';
|
||||
import { useQuasar } from 'quasar';
|
||||
import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue';
|
||||
import { tMobile } from 'src/composables/tMobile';
|
||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { toDate } from 'src/filters';
|
||||
import { useVnConfirm } from 'composables/useVnConfirm';
|
||||
import { useState } from 'src/composables/useState';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import axios from 'axios';
|
||||
import { useArrayData } from 'composables/useArrayData';
|
||||
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
import { isLower, isBigger } from 'src/filters/date.js';
|
||||
import RightMenu from 'src/components/common/RightMenu.vue';
|
||||
import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||
import { QCheckbox } from 'quasar';
|
||||
|
||||
const quasar = useQuasar();
|
||||
const stateStore = useStateStore();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const { openConfirmationModal } = useVnConfirm();
|
||||
const state = useState();
|
||||
const { notify } = useNotify();
|
||||
const tableRef = ref();
|
||||
const editTableCellDialogRef = ref(null);
|
||||
const tableRef = ref();
|
||||
const quasar = useQuasar();
|
||||
jsegarra marked this conversation as resolved
Outdated
|
||||
const user = state.getUser();
|
||||
const fixedPrices = ref([]);
|
||||
const fixedPricesOriginalData = ref([]);
|
||||
const warehousesOptions = ref([]);
|
||||
const rowsSelected = ref([]);
|
||||
|
||||
// function localUserData() {
|
||||
// state.setUser(user.value);
|
||||
// }
|
||||
|
||||
// const userLocal = localUserData();
|
||||
// console.log('localUserData', userLocal);
|
||||
|
||||
console.log('user', user.value.warehouseFk);
|
||||
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 };
|
||||
}
|
||||
};
|
||||
const itemFixedPriceFilterRef = ref();
|
||||
|
||||
const params = reactive({});
|
||||
const arrayData = useArrayData('ItemFixedPrices', {
|
||||
url: 'FixedPrices/filter',
|
||||
userParams: params,
|
||||
order: ['name ASC', 'itemFk'],
|
||||
exprBuilder: exprBuilder,
|
||||
const defaultColumnAttrs = {
|
||||
align: 'left',
|
||||
sortable: true,
|
||||
};
|
||||
onMounted(async () => {
|
||||
stateStore.rightDrawer = true;
|
||||
params.warehouseFk = user.value.warehouseFk;
|
||||
});
|
||||
const store = arrayData.store;
|
||||
|
||||
const fetchFixedPrices = async () => {
|
||||
await arrayData.fetch({ append: false });
|
||||
};
|
||||
|
||||
const onFixedPricesFetched = (data) => {
|
||||
fixedPrices.value = data;
|
||||
// el objetivo de guardar una copia de las rows es evitar guardar cambios si la data no cambió al disparar los eventos
|
||||
fixedPricesOriginalData.value = JSON.parse(JSON.stringify(data));
|
||||
};
|
||||
|
||||
watch(
|
||||
() => store.data,
|
||||
(data) => onFixedPricesFetched(data)
|
||||
);
|
||||
|
||||
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||
const columns = computed(() => [
|
||||
{
|
||||
label: t('item.fixedPrice.itemId'),
|
||||
name: 'itemFk',
|
||||
align: 'left',
|
||||
name: 'itemId',
|
||||
...defaultColumnAttrs,
|
||||
isId: true,
|
||||
chip: {
|
||||
condition: () => true,
|
||||
cardVisible: true,
|
||||
columnField: {
|
||||
component: 'input',
|
||||
type: 'number',
|
||||
},
|
||||
columnClass: 'shrink',
|
||||
},
|
||||
{
|
||||
label: t('globals.description'),
|
||||
label: t('globals.name'),
|
||||
field: 'name',
|
||||
name: 'description',
|
||||
align: 'left',
|
||||
...defaultColumnAttrs,
|
||||
create: true,
|
||||
columnCreate: {
|
||||
component: 'select',
|
||||
url: 'Items',
|
||||
},
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
label: t('item.fixedPrice.groupingPrice'),
|
||||
field: 'rate2',
|
||||
name: 'rate2',
|
||||
...defaultColumnAttrs,
|
||||
cardVisible: true,
|
||||
columnField: {
|
||||
class: 'expand',
|
||||
component: 'input',
|
||||
align: 'left',
|
||||
create: true,
|
||||
type: 'number',
|
||||
},
|
||||
columnFilter: {
|
||||
class: 'expand',
|
||||
component: 'input',
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('item.fixedPrice.packingPrice'),
|
||||
field: 'rate3',
|
||||
name: 'rate3',
|
||||
...defaultColumnAttrs,
|
||||
cardVisible: true,
|
||||
columnField: {
|
||||
class: 'expand',
|
||||
component: 'input',
|
||||
align: 'left',
|
||||
create: true,
|
||||
type: 'number',
|
||||
},
|
||||
columnFilter: {
|
||||
class: 'expand',
|
||||
component: 'input',
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
label: t('item.fixedPrice.minPrice'),
|
||||
field: 'minPrice',
|
||||
columnClass: 'shrink',
|
||||
name: 'minPrice',
|
||||
...defaultColumnAttrs,
|
||||
cardVisible: true,
|
||||
columnField: {
|
||||
class: 'expand',
|
||||
component: 'input',
|
||||
align: 'left',
|
||||
create: true,
|
||||
type: 'number',
|
||||
},
|
||||
columnFilter: {
|
||||
class: 'expand',
|
||||
component: 'input',
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('item.fixedPrice.started'),
|
||||
name: 'started',
|
||||
align: 'left',
|
||||
create: true,
|
||||
format: (row) => toDate(row.started),
|
||||
columnCreate: {
|
||||
format: ({ started }) => toDate(started),
|
||||
cardVisible: true,
|
||||
...defaultColumnAttrs,
|
||||
columnField: {
|
||||
component: 'date',
|
||||
class: 'shrink',
|
||||
},
|
||||
columnFilter: {
|
||||
component: 'date',
|
||||
},
|
||||
columnClass: 'expand',
|
||||
},
|
||||
{
|
||||
label: t('item.fixedPrice.ended'),
|
||||
name: 'ended',
|
||||
align: 'left',
|
||||
create: true,
|
||||
format: (row) => toDate(row.ended),
|
||||
columnCreate: {
|
||||
...defaultColumnAttrs,
|
||||
cardVisible: true,
|
||||
columnField: {
|
||||
component: 'date',
|
||||
class: 'shrink',
|
||||
},
|
||||
columnFilter: {
|
||||
component: 'date',
|
||||
},
|
||||
columnClass: 'expand',
|
||||
format: (row) => toDate(row.ended),
|
||||
},
|
||||
{
|
||||
label: t('item.fixedPrice.warehouse'),
|
||||
field: 'warehouseFk',
|
||||
name: 'warehouseFk',
|
||||
...defaultColumnAttrs,
|
||||
columnClass: 'shrink',
|
||||
columnFilter: {
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Warehouses',
|
||||
optionValue: 'id',
|
||||
optionLabel: 'name',
|
||||
},
|
||||
},
|
||||
columnField: {
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Warehouses',
|
||||
fields: ['id', 'name'],
|
||||
class: 'expand',
|
||||
},
|
||||
attrs: {
|
||||
options: warehousesOptions,
|
||||
},
|
||||
disable: false,
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
label: '',
|
||||
name: 'tableActions',
|
||||
actions: [
|
||||
{
|
||||
title: t('Delete'),
|
||||
title: t('delete'),
|
||||
icon: 'delete',
|
||||
action: (row) => confirmRemove(row),
|
||||
isPrimary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const editTableFieldsOptions = [
|
||||
{
|
||||
field: 'rate2',
|
||||
label: t('item.fixedPrice.groupingPrice'),
|
||||
component: 'input',
|
||||
attrs: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'rate3',
|
||||
label: t('item.fixedPrice.packingPrice'),
|
||||
component: 'input',
|
||||
attrs: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'minPrice',
|
||||
label: t('item.fixedPrice.minPrice'),
|
||||
component: 'input',
|
||||
attrs: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'started',
|
||||
label: t('item.fixedPrice.started'),
|
||||
component: 'date',
|
||||
},
|
||||
{
|
||||
field: 'ended',
|
||||
label: t('item.fixedPrice.ended'),
|
||||
component: 'date',
|
||||
},
|
||||
];
|
||||
|
||||
function confirmRemove(row) {
|
||||
quasar.dialog({
|
||||
component: VnConfirm,
|
||||
|
@ -178,182 +233,270 @@ function confirmRemove(row) {
|
|||
message: t('confirmDeletionMessage'),
|
||||
promise: () => remove(row),
|
||||
},
|
||||
});
|
||||
}),
|
||||
};
|
||||
const getRowUpdateInputEvents = (props, resetMinPrice, inputType = 'text') => {
|
||||
return inputType === 'text'
|
||||
? {
|
||||
'keyup.enter': () => upsertPrice(props, resetMinPrice),
|
||||
blur: () => upsertPrice(props, resetMinPrice),
|
||||
}
|
||||
|
||||
async function remove(item) {
|
||||
await axios.post('FixedPrices/filter', {
|
||||
rows: [item.id],
|
||||
});
|
||||
quasar.notify({
|
||||
message: t('globals.dataDeleted'),
|
||||
type: 'positive',
|
||||
});
|
||||
tableRef.value.reload();
|
||||
}
|
||||
// const getRowUpdateInputEvents = (props, resetMinPrice, inputType = 'text') => {
|
||||
// return inputType === 'text'
|
||||
// ? {
|
||||
// 'keyup.enter': () => upsertPrice(props, resetMinPrice),
|
||||
// blur: () => upsertPrice(props, resetMinPrice),
|
||||
// }
|
||||
// : { 'update:modelValue': () => upsertPrice(props, resetMinPrice) };
|
||||
// };
|
||||
|
||||
const validations = (row, rowIndex, col) => {
|
||||
const isNew = !row.id;
|
||||
// Si la row no tiene id significa que fue agregada con addRow y no se ha guardado en la base de datos
|
||||
// Si isNew es falso no se checkea si el valor es igual a la original
|
||||
if (!isNew)
|
||||
if (fixedPricesOriginalData.value[rowIndex][col.field] == row[col.field])
|
||||
return false;
|
||||
|
||||
const requiredFields = ['itemFk', 'started', 'ended', 'rate2', 'rate3'];
|
||||
return requiredFields.every(
|
||||
(field) => row[field] !== null && row[field] !== undefined
|
||||
);
|
||||
: { 'update:modelValue': () => upsertPrice(props, resetMinPrice) };
|
||||
};
|
||||
|
||||
const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => {
|
||||
if (!validations(row, rowIndex, col)) return;
|
||||
const updateMinPrice = async (value, props) => {
|
||||
// El checkbox hasMinPrice se encuentra en la misma columna que el input hasMinPrice
|
||||
// Por lo tanto le mandamos otro objeto con las mismas propiedades pero con el campo 'field' cambiado
|
||||
props.row.hasMinPrice = value;
|
||||
await upsertPrice({
|
||||
row: props.row,
|
||||
col: { field: 'hasMinPrice' },
|
||||
rowIndex: props.rowIndex,
|
||||
});
|
||||
};
|
||||
|
||||
const upsertPrice = async (props, resetMinPrice = false) => {
|
||||
try {
|
||||
const { row } = props;
|
||||
if (tableRef.value.CrudModelRef.getChanges().updates.length > 0) {
|
||||
if (resetMinPrice) row.hasMinPrice = 0;
|
||||
|
||||
const { data } = await axios.patch('FixedPrices/upsertFixedPrice', row);
|
||||
row = data;
|
||||
fixedPricesOriginalData.value[rowIndex][col.field] = row[col.field];
|
||||
await upsertFixedPrice(row);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error editing price', err);
|
||||
}
|
||||
};
|
||||
|
||||
// const addRow = () => {
|
||||
// if (!fixedPrices.value || fixedPrices.value.length === 0) {
|
||||
// fixedPrices.value = [];
|
||||
async function upsertFixedPrice(row) {
|
||||
try {
|
||||
const { data } = await axios.patch('FixedPrices/upsertFixedPrice', row);
|
||||
return data;
|
||||
} catch (err) {
|
||||
console.error('Error editing price', err);
|
||||
}
|
||||
}
|
||||
|
||||
// const today = Date.vnNew();
|
||||
// const millisecsInDay = 86400000;
|
||||
// const daysInWeek = 7;
|
||||
// const nextWeek = new Date(today.getTime() + daysInWeek * millisecsInDay);
|
||||
async function saveOnRowChange(row) {
|
||||
if (rowsSelected.value.length > 1) return;
|
||||
if (rowsSelected.value[0]?.id === row.id) return;
|
||||
else if (rowsSelected.value.length === 1) await upsertPrice(rowsSelected.value[0]);
|
||||
rowsSelected.value = [row];
|
||||
}
|
||||
|
||||
// const newPrice = {
|
||||
// started: today,
|
||||
// ended: nextWeek,
|
||||
// hasMinPrice: 0,
|
||||
// };
|
||||
function checkLastVisibleRow() {
|
||||
let lastVisibleRow = null;
|
||||
|
||||
// fixedPricesOriginalData.value.push({ ...newPrice });
|
||||
// fixedPrices.value.push({ ...newPrice });
|
||||
// return;
|
||||
// }
|
||||
getTableRows().forEach((row, index) => {
|
||||
const rect = row.getBoundingClientRect();
|
||||
if (rect.top >= 0 && rect.bottom <= window.innerHeight) {
|
||||
lastVisibleRow = index;
|
||||
}
|
||||
});
|
||||
|
||||
// const lastItemCopy = JSON.parse(
|
||||
// JSON.stringify(fixedPrices.value[fixedPrices.value.length - 1])
|
||||
// );
|
||||
// delete lastItemCopy.id;
|
||||
// fixedPricesOriginalData.value.push(lastItemCopy);
|
||||
// fixedPrices.value.push(lastItemCopy);
|
||||
// };
|
||||
return lastVisibleRow;
|
||||
}
|
||||
|
||||
// const onWarehousesFetched = (data) => {
|
||||
// warehousesOptions.value = data;
|
||||
// // Actualiza las 'options' del elemento con field 'warehouseFk' en 'editTableFieldsOptions'.
|
||||
// const warehouseField = editTableFieldsOptions.find(
|
||||
// (field) => field.field === 'warehouseFk'
|
||||
// );
|
||||
// warehouseField.attrs.options = data;
|
||||
// };
|
||||
const addRow = (original = null) => {
|
||||
let copy = null;
|
||||
if (!original) {
|
||||
const today = Date.vnNew();
|
||||
const millisecsInDay = 86400000;
|
||||
const daysInWeek = 7;
|
||||
const nextWeek = new Date(today.getTime() + daysInWeek * millisecsInDay);
|
||||
|
||||
// const removePrice = async (id, rowIndex) => {
|
||||
// try {
|
||||
// await axios.delete(`FixedPrices/${id}`);
|
||||
// fixedPrices.value.splice(rowIndex, 1);
|
||||
// fixedPricesOriginalData.value.splice(rowIndex, 1);
|
||||
// notify(t('globals.dataSaved'), 'positive');
|
||||
// } catch (err) {
|
||||
// console.error('Error removing price', err);
|
||||
// }
|
||||
// };
|
||||
copy = {
|
||||
id: 0,
|
||||
started: today,
|
||||
ended: nextWeek,
|
||||
hasMinPrice: 0,
|
||||
$index: 0,
|
||||
};
|
||||
} else
|
||||
copy = {
|
||||
$index: original.$index - 1,
|
||||
itemFk: original.itemFk,
|
||||
name: original.name,
|
||||
subName: original.subName,
|
||||
value5: original.value5,
|
||||
value6: original.value6,
|
||||
value7: original.value7,
|
||||
value8: original.value8,
|
||||
value9: original.value9,
|
||||
value10: original.value10,
|
||||
warehouseFk: original.warehouseFk,
|
||||
rate2: original.rate2,
|
||||
rate3: original.rate3,
|
||||
hasMinPrice: original.hasMinPrice,
|
||||
minPrice: original.minPrice,
|
||||
started: Date.vnNew(),
|
||||
ended: Date.vnNew(),
|
||||
};
|
||||
return { original, copy };
|
||||
};
|
||||
|
||||
// const updateMinPrice = async (value, props) => {
|
||||
// // El checkbox hasMinPrice se encuentra en la misma columna que el input hasMinPrice
|
||||
// // Por lo tanto le mandamos otro objeto con las mismas propiedades pero con el campo 'field' cambiado
|
||||
// props.row.hasMinPrice = value;
|
||||
// await upsertPrice({
|
||||
// row: props.row,
|
||||
// col: { field: 'hasMinPrice' },
|
||||
// rowIndex: props.rowIndex,
|
||||
// });
|
||||
// };
|
||||
const getTableRows = () =>
|
||||
document.getElementsByClassName('q-table')[0].querySelectorAll('tr.cursor-pointer');
|
||||
|
||||
function highlightNewRow({ $index: index }) {
|
||||
const row = getTableRows()[index];
|
||||
if (row) {
|
||||
row.classList.add('highlight');
|
||||
setTimeout(() => {
|
||||
row.classList.remove('highlight');
|
||||
}, 3000); // Duración de la animación en milisegundos
|
||||
}
|
||||
}
|
||||
const openEditTableCellDialog = () => {
|
||||
editTableCellDialogRef.value.show();
|
||||
};
|
||||
|
||||
const onEditCellDataSaved = async () => {
|
||||
rowsSelected.value = [];
|
||||
tableRef.value.reload();
|
||||
};
|
||||
|
||||
const removeFuturePrice = async () => {
|
||||
try {
|
||||
rowsSelected.value.forEach(({ id }) => {
|
||||
const rowIndex = fixedPrices.value.findIndex(({ id }) => id === id);
|
||||
removePrice(id, rowIndex);
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Error removing price', err);
|
||||
}
|
||||
};
|
||||
|
||||
function confirmRemove(item, isFuture) {
|
||||
const promise = async () =>
|
||||
isFuture ? removeFuturePrice(item.id) : removePrice(item.id);
|
||||
quasar.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('globals.rowWillBeRemoved'),
|
||||
message: t('globals.confirmDeletion'),
|
||||
promise,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const removePrice = async (id) => {
|
||||
try {
|
||||
await axios.delete(`FixedPrices/${id}`);
|
||||
notify(t('globals.dataSaved'), 'positive');
|
||||
tableRef.value.reload({});
|
||||
} catch (err) {
|
||||
console.error('Error removing price', err);
|
||||
}
|
||||
};
|
||||
const dateStyle = (date) =>
|
||||
date
|
||||
? {
|
||||
'bg-color': 'warning',
|
||||
'is-outlined': true,
|
||||
}
|
||||
: {};
|
||||
|
||||
function handleOnDataSave({ CrudModelRef }) {
|
||||
const { original, copy } = addRow(CrudModelRef.formData[checkLastVisibleRow()]);
|
||||
if (original) {
|
||||
CrudModelRef.formData.splice(original?.$index ?? 0, 0, copy);
|
||||
} else {
|
||||
CrudModelRef.insert(copy);
|
||||
}
|
||||
nextTick(() => {
|
||||
highlightNewRow(original ?? { $index: 0 });
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VnTable
|
||||
ref="tableRef"
|
||||
data-key="ItemFixedPrices"
|
||||
url="FixedPrices/filter"
|
||||
save-url="FixedPrices/crud"
|
||||
:filter="{ where: { warehouseFk: user.warehouseFk } }"
|
||||
:create="{
|
||||
urlCreate: 'PriceFixed',
|
||||
title: 'Create Item',
|
||||
onDataSaved: () => tableRef.redirect(),
|
||||
formInitialData: {},
|
||||
}"
|
||||
order="id ASC"
|
||||
:columns="columns"
|
||||
<FetchData
|
||||
@on-fetch="(data) => (warehousesOptions = data)"
|
||||
auto-load
|
||||
:is-editable="true"
|
||||
:use-model="true"
|
||||
>
|
||||
<template #column-description="{ row }">
|
||||
<div class="row column full-width justify-between items-start">
|
||||
<span class="link" @click.stop>
|
||||
{{ row?.name }}
|
||||
<ItemDescriptorProxy class="link" :id="row.itemFk" />
|
||||
</span>
|
||||
<div v-if="row?.subName" class="subName">
|
||||
{{ row?.subName.toUpperCase() }}
|
||||
</div>
|
||||
</div>
|
||||
<FetchedTags :item="row" :max-length="6" />
|
||||
url="Warehouses"
|
||||
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }"
|
||||
/>
|
||||
<RightMenu>
|
||||
<template #right-panel>
|
||||
<ItemFixedPriceFilter
|
||||
data-key="ItemFixedPrices"
|
||||
ref="itemFixedPriceFilterRef"
|
||||
/>
|
||||
</template>
|
||||
</RightMenu>
|
||||
<QPage class="column items-center q-pa-md">
|
||||
<QTable
|
||||
:rows="fixedPrices"
|
||||
:columns="columns"
|
||||
row-key="id"
|
||||
selection="multiple"
|
||||
v-model:selected="rowsSelected"
|
||||
:pagination="{ rowsPerPage: 0 }"
|
||||
class="full-width q-mt-md"
|
||||
:no-data-label="t('globals.noResults')"
|
||||
<VnSubToolbar>
|
||||
<template #st-data>
|
||||
<QBtn
|
||||
v-if="rowsSelected.length"
|
||||
@click="openEditTableCellDialog()"
|
||||
color="primary"
|
||||
icon="edit"
|
||||
>
|
||||
<template #top-row="{ cols }">
|
||||
<QTr>
|
||||
<QTd />
|
||||
<QTd
|
||||
v-for="(col, index) in cols"
|
||||
:key="index"
|
||||
style="max-width: 100px"
|
||||
>
|
||||
<component
|
||||
:is="col.columnFilter.component"
|
||||
v-if="col.columnFilter"
|
||||
v-model="col.columnFilter.filterValue"
|
||||
v-bind="col.columnFilter.attrs"
|
||||
v-on="col.columnFilter.event(col)"
|
||||
dense
|
||||
<QTooltip>
|
||||
{{ t('Edit fixed price(s)') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
:label="tMobile('globals.remove')"
|
||||
color="primary"
|
||||
icon="delete"
|
||||
flat
|
||||
@click="(row) => confirmRemove(row, true)"
|
||||
:title="t('globals.remove')"
|
||||
v-if="rowsSelected.length"
|
||||
/>
|
||||
</QTd>
|
||||
</QTr>
|
||||
</template>
|
||||
</VnSubToolbar>
|
||||
<QPage>
|
||||
<VnTable
|
||||
@on-fetch="
|
||||
(data) =>
|
||||
data.forEach((item) => {
|
||||
item.hasMinPrice = `${item.hasMinPrice !== 0}`;
|
||||
})
|
||||
"
|
||||
:default-remove="false"
|
||||
:default-reset="false"
|
||||
:default-save="false"
|
||||
data-key="ItemFixedPrices"
|
||||
url="FixedPrices/filter"
|
||||
:order="['itemFk ASC']"
|
||||
save-url="FixedPrices/crud"
|
||||
:user-params="{ warehouseFk: user.warehouseFk }"
|
||||
ref="tableRef"
|
||||
dense
|
||||
:columns="columns"
|
||||
default-mode="table"
|
||||
auto-load
|
||||
:is-editable="true"
|
||||
:right-search="false"
|
||||
:table="{
|
||||
'row-key': 'id',
|
||||
selection: 'multiple',
|
||||
}"
|
||||
:crud-model="{
|
||||
paginate: false,
|
||||
}"
|
||||
v-model:selected="rowsSelected"
|
||||
:row-click="saveOnRowChange"
|
||||
:create-as-dialog="false"
|
||||
:create="{
|
||||
onDataSaved: handleOnDataSave,
|
||||
}"
|
||||
:use-model="true"
|
||||
:disable-option="{ card: true }"
|
||||
>
|
||||
<template #header-selection="scope">
|
||||
<QCheckbox v-model="scope.selected" />
|
||||
</template>
|
||||
<template #body-selection="scope">
|
||||
{{ scope }}
|
||||
<QCheckbox flat v-model="scope.selected" />
|
||||
</template>
|
||||
|
||||
<template #body-cell-itemId="props">
|
||||
<QTd>
|
||||
<template #column-itemId="props">
|
||||
<VnSelect
|
||||
style="max-width: 100px"
|
||||
url="Items/withName"
|
||||
hide-selected
|
||||
option-label="id"
|
||||
|
@ -370,87 +513,74 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => {
|
|||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-description="{ row }">
|
||||
<QTd class="col">
|
||||
<template #column-description="{ row }">
|
||||
<span class="link">
|
||||
{{ row.name }}
|
||||
</span>
|
||||
<span class="subName">{{ row.subName }}</span>
|
||||
<ItemDescriptorProxy :id="row.itemFk" />
|
||||
<FetchedTags :item="row" />
|
||||
</QTd>
|
||||
<FetchedTags style="width: max-content; max-width: 220px" :item="row" />
|
||||
</template>
|
||||
<template #body-cell-groupingPrice="props">
|
||||
<QTd class="col">
|
||||
<template #column-rate2="props">
|
||||
<VnInput
|
||||
mask="###.##"
|
||||
v-model.number="props.row.rate2"
|
||||
v-on="getRowUpdateInputEvents(props)"
|
||||
>
|
||||
<template #append>€</template>
|
||||
</VnInput>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-packingPrice="props">
|
||||
<QTd class="col">
|
||||
<template #column-rate3="props">
|
||||
<VnInput
|
||||
mask="###.##"
|
||||
v-model.number="props.row.rate3"
|
||||
v-on="getRowUpdateInputEvents(props)"
|
||||
>
|
||||
<template #append>€</template>
|
||||
</VnInput>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-minPrice="props">
|
||||
<template #column-minPrice="props">
|
||||
<QTd class="col">
|
||||
<div class="row">
|
||||
<div class="row" style="width: 115px">
|
||||
<QCheckbox
|
||||
class="col"
|
||||
:model-value="props.row.hasMinPrice"
|
||||
@update:model-value="updateMinPrice($event, props)"
|
||||
:false-value="0"
|
||||
:true-value="1"
|
||||
:toggle-indeterminate="false"
|
||||
:false-value="'false'"
|
||||
:true-value="'true'"
|
||||
/>
|
||||
<VnInput
|
||||
class="col"
|
||||
:disable="!props.row.hasMinPrice"
|
||||
:disable="props.row.hasMinPrice === 1"
|
||||
v-model.number="props.row.minPrice"
|
||||
v-on="getRowUpdateInputEvents(props)"
|
||||
type="number"
|
||||
/>
|
||||
>
|
||||
<template #append>€</template>
|
||||
</VnInput>
|
||||
</div>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-started="props">
|
||||
<QTd class="col" style="min-width: 160px">
|
||||
<template #column-started="props">
|
||||
<VnInputDate
|
||||
class="vnInputDate"
|
||||
:show-event="true"
|
||||
v-model="props.row.started"
|
||||
v-on="getRowUpdateInputEvents(props, false, 'date')"
|
||||
v-bind="
|
||||
isBigger(props.row.started)
|
||||
? { 'bg-color': 'warning', 'is-outlined': true }
|
||||
: {}
|
||||
"
|
||||
v-bind="dateStyle(isBigger(props.row.started))"
|
||||
/>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-ended="props">
|
||||
<QTd class="col" style="min-width: 150px">
|
||||
<template #column-ended="props">
|
||||
<VnInputDate
|
||||
class="vnInputDate"
|
||||
:show-event="true"
|
||||
v-model="props.row.ended"
|
||||
v-on="getRowUpdateInputEvents(props, false, 'date')"
|
||||
v-bind="
|
||||
isLower(props.row.ended)
|
||||
? { 'bg-color': 'warning', 'is-outlined': true }
|
||||
: {}
|
||||
"
|
||||
v-bind="dateStyle(isLower(props.row.ended))"
|
||||
/>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-warehouse="props">
|
||||
<QTd class="col">
|
||||
<template #column-warehouseFk="props">
|
||||
<VnSelect
|
||||
style="max-width: 150px"
|
||||
:options="warehousesOptions"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
|
@ -458,10 +588,8 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => {
|
|||
v-model="props.row.warehouseFk"
|
||||
v-on="getRowUpdateInputEvents(props, false, 'select')"
|
||||
/>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-deleteAction="{ row, rowIndex }">
|
||||
<QTd class="col">
|
||||
<template #column-deleteAction="{ row, rowIndex }">
|
||||
<QIcon
|
||||
name="delete"
|
||||
size="sm"
|
||||
|
@ -469,40 +597,19 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => {
|
|||
color="primary"
|
||||
@click.stop="
|
||||
openConfirmationModal(
|
||||
t('This row will be removed'),
|
||||
t('globals.rowWillBeRemoved'),
|
||||
t('Do you want to clone this item?'),
|
||||
() => removePrice(row.id, rowIndex)
|
||||
)
|
||||
"
|
||||
>
|
||||
<QTooltip class="text-no-wrap">
|
||||
{{ t('Delete') }}
|
||||
{{ t('globals.delete') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #bottom-row>
|
||||
<QTd align="center">
|
||||
<QIcon
|
||||
@click.stop="addRow()"
|
||||
class="fill-icon-on-hover"
|
||||
color="primary"
|
||||
name="add_circle"
|
||||
size="sm"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Add fixed price') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</QTd>
|
||||
</template>
|
||||
</QTable>
|
||||
<QPageSticky v-if="rowsSelected.length" :offset="[20, 20]">
|
||||
<QBtn @click="openEditTableCellDialog()" color="primary" fab icon="edit" />
|
||||
<QTooltip>
|
||||
{{ t('Edit fixed price(s)') }}
|
||||
</QTooltip>
|
||||
</QPageSticky>
|
||||
</VnTable>
|
||||
|
||||
<QDialog ref="editTableCellDialogRef">
|
||||
<EditTableCellValueForm
|
||||
edit-url="FixedPrices/editFixedPrice"
|
||||
|
@ -513,8 +620,50 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => {
|
|||
</QDialog>
|
||||
</QPage>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
<style lang="scss">
|
||||
.q-table th,
|
||||
.q-table td {
|
||||
padding-inline: 5px !important;
|
||||
// text-align: -webkit-right;
|
||||
}
|
||||
.q-table tbody td {
|
||||
max-width: none;
|
||||
|
||||
.q-td.col {
|
||||
& .vnInputDate {
|
||||
min-width: 90px;
|
||||
}
|
||||
& div.row {
|
||||
& .q-checkbox {
|
||||
& .q-checkbox__inner {
|
||||
position: relative !important;
|
||||
&.q-checkbox__inner--truthy {
|
||||
color: var(--q-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.q-field__after,
|
||||
.q-field__append {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
tbody tr.highlight .q-td {
|
||||
animation: highlight-animation 4s ease-in-out;
|
||||
}
|
||||
@keyframes highlight-animation {
|
||||
0% {
|
||||
background-color: $primary-light;
|
||||
}
|
||||
100% {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
.subName {
|
||||
margin-left: 5%;
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
color: var(--vn-label-color);
|
||||
}
|
||||
|
@ -523,7 +672,4 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => {
|
|||
es:
|
||||
Add fixed price: Añadir precio fijado
|
||||
Edit fixed price(s): Editar precio(s) fijado(s)
|
||||
This row will be removed: Esta linea se eliminará
|
||||
Are you sure you want to continue?: ¿Seguro que quieres continuar?
|
||||
Delete: Eliminar
|
||||
</i18n>
|
||||
|
|
|
@ -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: {
|
||||
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
?