Merge branch 'feature/SuppliersCorrections' of https://gitea.verdnatura.es/hyervoni/salix-front-mindshore into dev

This commit is contained in:
Alex Moreno 2024-02-02 07:30:57 +01:00
commit 0ffff5ea1c
81 changed files with 4104 additions and 2913 deletions

6
package-lock.json generated
View File

@ -12,6 +12,7 @@
"@quasar/extras": "^1.16.4",
"axios": "^1.4.0",
"chromium": "^3.0.3",
"croppie": "^2.6.5",
"pinia": "^2.1.3",
"quasar": "^2.12.0",
"validator": "^13.9.0",
@ -3169,6 +3170,11 @@
"node": ">= 10"
}
},
"node_modules/croppie": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/croppie/-/croppie-2.6.5.tgz",
"integrity": "sha512-IlChnVUGG5T3w2gRZIaQgBtlvyuYnlUWs2YZIXXR3H9KrlO1PtBT3j+ykxvy9eZIWhk+V5SpBmhCQz5UXKrEKQ=="
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",

View File

@ -19,6 +19,7 @@
"@quasar/extras": "^1.16.4",
"axios": "^1.4.0",
"chromium": "^3.0.3",
"croppie": "^2.6.5",
"pinia": "^2.1.3",
"quasar": "^2.12.0",
"validator": "^13.9.0",

View File

@ -0,0 +1,363 @@
<script setup>
import { reactive, computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
import Croppie from 'croppie/croppie';
import 'croppie/croppie.css';
import useNotify from 'src/composables/useNotify.js';
import axios from 'axios';
const emit = defineEmits(['closeForm', 'onPhotoUploaded']);
const props = defineProps({
id: {
type: String,
default: '',
},
collection: {
type: String,
default: '',
},
});
const { t } = useI18n();
const { notify } = useNotify();
const uploadMethodsOptions = [
{ label: t('Select from computer'), value: 'computer' },
{ label: t('Import from external URL'), value: 'URL' },
];
const viewportTypes = [
{
code: 'normal',
description: t('Normal'),
viewport: {
width: 400,
height: 400,
},
output: {
width: 1200,
height: 1200,
},
},
{
code: 'panoramic',
description: t('Panoramic'),
viewport: {
width: 675,
height: 450,
},
output: {
width: 1350,
height: 900,
},
},
{
code: 'vertical',
description: t('Vertical'),
viewport: {
width: 306.66,
height: 533.33,
},
output: {
width: 460,
height: 800,
},
},
];
const uploadMethodSelected = ref('computer');
const viewPortTypeSelected = ref(viewportTypes[0]);
const inputFileRef = ref(null);
const allowedContentTypes = ref('');
const photoContainerRef = ref(null);
const editor = ref(null);
const newPhoto = reactive({
id: props.id,
collection: props.collection,
file: null,
url: null,
blob: null,
});
const openInputFile = () => {
inputFileRef.value.pickFiles();
};
const displayEditor = () => {
const viewportType = viewPortTypeSelected.value;
const viewport = viewportType.viewport;
const boundaryWidth = viewport.width + 200;
const boundaryHeight = viewport.height + 200;
if (editor.value) editor.value.destroy();
editor.value = new Croppie(photoContainerRef.value, {
viewport: { width: viewport.width, height: viewport.height },
boundary: { width: boundaryWidth, height: boundaryHeight },
enableOrientation: true,
showZoomer: true,
});
};
const viewportSelection = computed({
get() {
return viewPortTypeSelected.value;
},
set(val) {
viewPortTypeSelected.value = val;
const hasFile = newPhoto.files || newPhoto.url;
if (!val || !hasFile) return;
let file;
if (uploadMethodSelected.value == 'computer') file = newPhoto.files;
else if (uploadMethodSelected.value == 'URL') file = newPhoto.url;
updatePhotoPreview(file);
},
});
const updatePhotoPreview = (value) => {
if (value) {
displayEditor();
if (uploadMethodSelected.value == 'computer') {
newPhoto.files = value;
const reader = new FileReader();
reader.onload = (e) => editor.value.bind({ url: e.target.result });
reader.readAsDataURL(value);
} else if (uploadMethodSelected.value == 'URL') {
newPhoto.url = value;
const img = new Image();
img.crossOrigin = 'Anonymous';
img.src = value;
img.onload = () => editor.value.bind({ url: value });
img.onerror = () => {
notify(
t("This photo provider doesn't allow remote downloads"),
'negative'
);
};
}
}
};
const rotateLeft = () => {
editor.value.rotate(90);
};
const rotateRight = () => {
editor.value.rotate(-90);
};
const onUploadAccept = () => {
try {
if (!newPhoto.files && !newPhoto.url) {
notify(t('Select an image'), 'negative');
return;
}
const options = {
type: 'blob',
};
editor.value
.result(options)
.then((result) => {
const file = new File([result], newPhoto.files?.name || '');
newPhoto.blob = file;
})
.then(() => makeRequest());
} catch (err) {
console.error('Error uploading image');
}
};
const makeRequest = async () => {
const formData = new FormData();
const now = Date.vnNew();
const timestamp = now.getTime();
const fileName = `${newPhoto.files?.name}_${timestamp}`;
formData.append('blob', newPhoto.blob, fileName);
await axios.post('Images/upload', formData, {
params: newPhoto,
headers: {
'Content-Type': 'multipart/form-data',
},
});
emit('closeForm');
emit('onPhotoUploaded');
notify(t('globals.dataSaved'), 'positive');
};
</script>
<template>
<FetchData
ref="allowTypesRef"
url="ImageContainers/allowedContentTypes"
@on-fetch="(data) => (allowedContentTypes = data.join(', '))"
auto-load
/>
<QForm @submit="onUploadAccept()" class="all-pointer-events">
<QCard class="q-pa-lg">
<span ref="closeButton" class="close-icon" v-close-popup>
<QIcon name="close" size="sm" />
</span>
<h1 class="title">{{ t('Edit photo') }}</h1>
<div class="row q-gutter-lg">
<div
v-show="newPhoto.files || newPhoto.url"
class="row q-gutter-lg items-center"
>
<QIcon
name="rotate_left"
size="sm"
color="primary"
class="cursor-pointer"
@click="rotateLeft()"
>
<!-- <QTooltip class="no-pointer-events">
{{ t('Rotate left') }}
</QTooltip> -->
</QIcon>
<div>
<div ref="photoContainerRef" />
</div>
<QIcon
name="rotate_right"
size="sm"
color="primary"
class="cursor-pointer"
@click="rotateRight()"
>
<!-- <QTooltip class="no-pointer-events">
{{ t('Rotate right') }}
</QTooltip> -->
</QIcon>
</div>
<div class="column">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QOptionGroup
:options="uploadMethodsOptions"
type="radio"
v-model="uploadMethodSelected"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QFile
v-if="uploadMethodSelected === 'computer'"
ref="inputFileRef"
:label="t('File')"
:multiple="false"
v-model="newPhoto.files"
@update:model-value="updatePhotoPreview($event)"
:accept="allowedContentTypes"
class="required cursor-pointer"
>
<template #append>
<QIcon
name="vn:attach"
class="cursor-pointer q-mr-sm"
@click="openInputFile()"
>
<!-- <QTooltip>{{ t('Select a file') }}</QTooltip> -->
</QIcon>
<QIcon name="info" class="cursor-pointer">
<QTooltip>{{
t(
'components.editPictureForm.allowedFilesText',
{
allowedContentTypes:
allowedContentTypes,
}
)
}}</QTooltip>
</QIcon>
</template>
</QFile>
<VnInput
v-if="uploadMethodSelected === 'URL'"
v-model="newPhoto.url"
@update:model-value="updatePhotoPreview($event)"
placeholder="https://"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('Orientation')"
:options="viewportTypes"
hide-selected
option-label="description"
v-model="viewportSelection"
/>
</div>
</VnRow>
<div class="q-mt-lg row justify-end">
<QBtn
:label="t('globals.save')"
type="submit"
color="primary"
:disabled="isLoading"
:loading="isLoading"
/>
<QBtn
:label="t('globals.cancel')"
type="reset"
color="primary"
flat
class="q-ml-sm"
:disabled="isLoading"
:loading="isLoading"
v-close-popup
/>
</div>
</div>
</div>
</QCard>
</QForm>
</template>
<style lang="scss" scoped>
.title {
font-size: 17px;
font-weight: bold;
line-height: 20px;
}
.close-icon {
position: absolute;
top: 20px;
right: 20px;
cursor: pointer;
}
</style>
<i18n>
es:
Edit photo: Editar foto
Select from computer: Seleccionar desde ordenador
Import from external URL: Importar desde URL externa
Vertical: Vertical
Normal: Normal
Panoramic: Panorámica
Orientation: Orientación
File: Fichero
This photo provider doesn't allow remote downloads: Este proveedor de fotos no permite descargas remotas
Rotate left: Girar a la izquierda
Rotate right: Girar a la derecha
Select an image: Selecciona una imagen
</i18n>

View File

@ -75,9 +75,12 @@ onMounted(async () => {
await fetch();
}
// Disparamos el watcher del form después de que se haya cargado la data inicial, si así se desea
// Si así se desea disparamos el watcher del form después de 100ms, asi darle tiempo de que se haya cargado la data inicial
// para evitar que detecte cambios cuando es data inicial default
if ($props.observeFormChanges) {
setTimeout(() => {
startFormWatcher();
}, 100);
}
});
@ -128,14 +131,14 @@ async function save() {
try {
const body = $props.mapper ? $props.mapper(formData.value) : formData.value;
let response
let response;
if ($props.urlCreate) {
response = await axios.post($props.urlCreate, body);
notify('globals.dataCreated', 'positive');
} else {
response = await axios.patch($props.urlUpdate || $props.url, body);
}
emit('onDataSaved', formData.value, response);
emit('onDataSaved', formData.value, response?.data);
originalData.value = JSON.parse(JSON.stringify(formData.value));
hasChanges.value = false;
} catch (err) {

View File

@ -0,0 +1,81 @@
<script setup>
import { reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue';
import FormModelPopup from './FormModelPopup.vue';
const emit = defineEmits(['onDataSaved']);
const props = defineProps({
itemFk: {
type: Number,
default: null,
},
warehouseFk: {
type: Boolean,
default: null,
},
});
const { t } = useI18n();
const regularizeFormData = reactive({
itemFk: props.itemFk,
warehouseFk: props.warehouseFk,
quantity: null,
});
const warehousesOptions = ref([]);
const onDataSaved = (data) => {
emit('onDataSaved', data);
};
</script>
<template>
<FetchData
url="Warehouses"
@on-fetch="(data) => (warehousesOptions = data)"
auto-load
/>
<FormModelPopup
url-create="Items/regularize"
model="Items"
:title="t('Regularize stock')"
:form-initial-data="regularizeFormData"
@on-data-saved="onDataSaved($event)"
>
<template #form-inputs="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
:label="t('Type the visible quantity')"
v-model.number="data.quantity"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('Warehouse')"
v-model="data.warehouseFk"
:options="warehousesOptions"
option-value="id"
option-label="name"
hide-selected
/>
</div>
</VnRow>
</template>
</FormModelPopup>
</template>
<i18n>
es:
Warehouse: Almacén
Type the visible quantity: Introduce la cantidad visible
Regularize stock: Regularizar stock
</i18n>

View File

@ -1,8 +1,8 @@
<script setup>
import {computed, ref} from 'vue';
import { toHour} from 'src/filters';
import {useI18n} from "vue-i18n";
import isValidDate from "filters/isValidDate";
import { computed, ref } from 'vue';
import { toHour } from 'src/filters';
import { useI18n } from 'vue-i18n';
import isValidDate from 'filters/isValidDate';
const props = defineProps({
modelValue: {
@ -25,9 +25,14 @@ const value = computed({
return props.modelValue;
},
set(value) {
const [hours, minutes] = value.split(':')
const date = new Date()
date.setUTCHours(Number.parseInt(hours) || 0, Number.parseInt(minutes) || 0, 0, 0)
const [hours, minutes] = value.split(':');
const date = new Date();
date.setUTCHours(
Number.parseInt(hours) || 0,
Number.parseInt(minutes) || 0,
0,
0
);
emit('update:modelValue', value ? date.toISOString() : null);
},
});
@ -40,14 +45,18 @@ const save = () => {
value.value = internalValue.value;
};
const formatTime = (dateString) => {
if (!isValidDate(dateString)){
return ''
if (!isValidDate(dateString)) {
return '';
}
const date = new Date(dateString || '');
return `${date.getUTCHours().toString().padStart(2, '0')}:${date.getUTCMinutes().toString().padStart(2, '0')}`;
return date.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
});
};
const internalValue = ref(formatTime(value))
const internalValue = ref(formatTime(value));
const styleAttrs = computed(() => {
return props.isOutlined
@ -82,8 +91,19 @@ const styleAttrs = computed(() => {
@update:model-value="onDateUpdate"
>
<div class="row items-center justify-end q-gutter-sm">
<QBtn :label="t('Cancel')" color="primary" flat v-close-popup />
<QBtn label="Ok" color="primary" flat @click="save" v-close-popup />
<QBtn
:label="t('Cancel')"
color="primary"
flat
v-close-popup
/>
<QBtn
label="Ok"
color="primary"
flat
@click="save"
v-close-popup
/>
</div>
</QTime>
</QPopupProxy>

View File

@ -38,7 +38,6 @@ const workers = ref();
minimal
>
</QDate>
<QList dense>
<QSeparator />
<QItem>
<QItemSection v-if="!workers">
@ -59,7 +58,6 @@ const workers = ref();
/>
</QItemSection>
</QItem>
</QList>
</template>
</VnFilterPanel>
</template>

View File

@ -178,3 +178,9 @@ watch(modelValue, (newValue) => {
</template>
</QSelect>
</template>
<style scoped lang="scss">
.q-field--outlined {
max-width: 100%;
}
</style>

View File

@ -79,6 +79,7 @@ function viewSummary(id) {
<div class="descriptor">
<template v-if="entity">
<div class="header bg-primary q-pa-sm justify-between">
<slot name="header-extra-action" />
<QBtn
@click.stop="viewSummary(entity.id)"
round
@ -116,7 +117,7 @@ function viewSummary(id) {
icon="more_vert"
round
size="md"
v-if="slots.menu"
:class="{ invisible: !slots.menu }"
>
<QTooltip>
{{ t('components.cardDescriptor.moreOptions') }}

View File

@ -220,7 +220,9 @@ function formatValue(value) {
</QItem>
<QSeparator />
</QList>
<QList dense class="list q-gutter-y-sm q-mt-sm">
<slot name="body" :params="userParams" :search-fn="search"></slot>
</QList>
<template v-if="props.searchButton">
<QItem>
<QItemSection class="q-py-sm">
@ -246,6 +248,12 @@ function formatValue(value) {
/>
</template>
<style scoped lang="scss">
.list {
width: 256px;
}
</style>
<i18n>
es:
No filters applied: No se han aplicado filtros

View File

@ -25,6 +25,7 @@ export function useState() {
lang: user.value.lang,
darkMode: user.value.darkMode,
companyFk: user.value.companyFk,
warehouseFk: user.value.warehouseFk,
};
});
}
@ -37,6 +38,7 @@ export function useState() {
lang: data.lang,
darkMode: data.darkMode,
companyFk: data.companyFk,
warehouseFk: data.warehouseFk,
};
}

View File

@ -12,6 +12,7 @@ export function useUserConfig() {
const user = state.getUser().value;
user.darkMode = data.darkMode;
user.companyFk = data.companyFk;
user.warehouseFk = data.warehouseFk;
state.setUser(user);
return data;

View File

@ -52,12 +52,21 @@ body.body--dark {
color: var(--vn-text);
}
.color-vn-white {
color: $white;
}
.vn-card {
background-color: var(--vn-gray);
color: var(--vn-text);
border-radius: 8px;
}
.vn-card-list {
width: 100%;
max-width: 60em;
}
/* Estilo para el asterisco en campos requeridos */
.q-field.required .q-field__label:after {
content: ' *';

View File

@ -4,13 +4,8 @@ export default function toHour(date) {
if (!isValidDate(date)) {
return '--:--';
}
const dateHour = new Date(date);
let hours = dateHour.getUTCHours();
hours = hours % 12;
hours = hours ? hours : 12;
let minutes = dateHour.getUTCMinutes();
minutes = minutes < 10 ? minutes.toString().padStart(2, '0') : minutes;
return `${hours}:${minutes} ${dateHour.getUTCHours() >= 12 ? 'PM' : 'AM'}`;
return (new Date(date || '')).toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
});
}

View File

@ -1060,7 +1060,25 @@ export default {
totalEntries: 'Total entries',
},
},
item: {
pageTitles: {
items: 'Items',
list: 'List',
diary: 'Diary',
tags: 'Tags',
},
descriptor: {
item: 'Item',
buyer: 'Buyer',
color: 'Color',
category: 'Category',
stems: 'Stems',
visible: 'Visible',
available: 'Available',
warehouseText: 'Calculated on the warehouse of { warehouseName }',
itemDiary: 'Item diary',
},
},
components: {
topbar: {},
userPanel: {
@ -1084,6 +1102,9 @@ export default {
addToPinned: 'Add to pinned',
removeFromPinned: 'Remove from pinned',
},
editPictureForm: {
allowedFilesText: 'Allowed file types: { allowedContentTypes }',
},
VnLv: {
copyText: '{copyValue} has been copied to the clipboard',
},

View File

@ -1060,6 +1060,25 @@ export default {
totalEntries: 'Ent. totales',
},
},
item: {
pageTitles: {
items: 'Artículos',
list: 'Listado',
diary: 'Histórico',
tags: 'Etiquetas',
},
descriptor: {
item: 'Artículo',
buyer: 'Comprador',
color: 'Color',
category: 'Categoría',
stems: 'Tallos',
visible: 'Visible',
available: 'Disponible',
warehouseText: 'Calculado sobre el almacén de { warehouseName }',
itemDiary: 'Registro de compra-venta',
},
},
components: {
topbar: {},
userPanel: {
@ -1083,6 +1102,9 @@ export default {
addToPinned: 'Añadir a fijados',
removeFromPinned: 'Eliminar de fijados',
},
editPictureForm: {
allowedFilesText: 'Tipos de archivo permitidos: { allowedContentTypes }',
},
VnLv: {
copyText: '{copyValue} se ha copiado al portapepeles',
},

View File

@ -36,7 +36,6 @@ const states = ref();
</div>
</template>
<template #body="{ params, searchFn }">
<QList dense class="list">
<QItem class="q-my-sm">
<QItemSection>
<VnInput
@ -178,20 +177,10 @@ const states = ref();
</QItemSection>
</QItem>
</QExpansionItem>
</QList>
</template>
</VnFilterPanel>
</template>
<style scoped>
.list {
width: 256px;
}
.list * {
max-width: 100%;
}
</style>
<i18n>
en:
params:

View File

@ -71,7 +71,7 @@ function viewSummary(id) {
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<VnPaginate
data-key="ClaimList"
url="Claims/filter"
@ -147,13 +147,6 @@ function viewSummary(id) {
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>
<i18n>
es:
Search claim: Buscar reclamación

View File

@ -84,7 +84,7 @@ async function remove({ id }) {
</QForm>
</QCard>
</QPageSticky>
<div class="card-list">
<div class="vn-card-list">
<VnPaginate
data-key="ClaimRmaList"
url="ClaimRmas"
@ -160,7 +160,6 @@ async function remove({ id }) {
padding-top: 156px;
}
.card-list,
.card {
width: 100%;
max-width: 60em;

View File

@ -37,7 +37,6 @@ const zones = ref();
</div>
</template>
<template #body="{ params, searchFn }">
<QList dense class="list">
<QItem class="q-my-sm">
<QItemSection>
<VnInput :label="t('FI')" v-model="params.fi" is-outlined>
@ -115,11 +114,7 @@ const zones = ref();
<QExpansionItem :label="t('More options')" expand-separator>
<QItem>
<QItemSection>
<VnInput
:label="t('Phone')"
v-model="params.phone"
is-outlined
>
<VnInput :label="t('Phone')" v-model="params.phone" is-outlined>
<template #prepend>
<QIcon name="phone" size="xs" />
</template>
@ -128,11 +123,7 @@ const zones = ref();
</QItem>
<QItem>
<QItemSection>
<VnInput
:label="t('Email')"
v-model="params.email"
is-outlined
>
<VnInput :label="t('Email')" v-model="params.email" is-outlined>
<template #prepend>
<QIcon name="email" size="sm" />
</template>
@ -170,20 +161,10 @@ const zones = ref();
</QItemSection>
</QItem>
</QExpansionItem>
</QList>
</template>
</VnFilterPanel>
</template>
<style scoped>
.list {
width: 256px;
}
.list * {
max-width: 100%;
}
</style>
<i18n>
en:
params:

View File

@ -65,7 +65,7 @@ const redirectToCreateView = () => {
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<VnPaginate
auto-load
data-key="CustomerList"
@ -116,13 +116,6 @@ const redirectToCreateView = () => {
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>
<i18n>
es:
Search customer: Buscar cliente

View File

@ -46,7 +46,6 @@ const authors = ref();
</template>
<template #body="{ params }">
<QList dense class="list">
<QItem class="q-mb-sm q-mt-sm">
<QItemSection v-if="clients">
<VnSelectFilter
@ -161,11 +160,7 @@ const authors = ref();
<QItem class="q-mb-sm">
<QItemSection>
<VnInput
:label="t('L. O. Date')"
is-outlined
v-model="params.date"
/>
<VnInput :label="t('L. O. Date')" is-outlined v-model="params.date" />
</QItemSection>
</QItem>
@ -189,20 +184,10 @@ const authors = ref();
</QItemSection>
</QItem>
<QSeparator />
</QList>
</template>
</VnFilterPanel>
</template>
<style scoped>
.list {
width: 256px;
}
.list * {
max-width: 100%;
}
</style>
<i18n>
en:
params:

View File

@ -145,7 +145,6 @@ const shouldRenderColumn = (colName) => {
</div>
</template>
<template #body="{ params, searchFn }">
<QList dense class="list q-gutter-y-sm q-mt-sm">
<QItem v-if="shouldRenderColumn('id')">
<QItemSection>
<VnInput
@ -204,9 +203,7 @@ const shouldRenderColumn = (colName) => {
<QItemSection v-if="workers">
<VnSelectFilter
:label="
t(
'customer.extendedList.tableVisibleColumns.salesPersonFk'
)
t('customer.extendedList.tableVisibleColumns.salesPersonFk')
"
v-model="params.salesPersonFk"
@update:model-value="searchFn()"
@ -237,9 +234,7 @@ const shouldRenderColumn = (colName) => {
<QItemSection>
<VnInput
:label="
t(
'customer.extendedList.tableVisibleColumns.creditInsurance'
)
t('customer.extendedList.tableVisibleColumns.creditInsurance')
"
v-model="params.creditInsurance"
is-outlined
@ -276,9 +271,7 @@ const shouldRenderColumn = (colName) => {
<QItem v-if="shouldRenderColumn('countryFk')">
<QItemSection>
<VnSelectFilter
:label="
t('customer.extendedList.tableVisibleColumns.countryFk')
"
:label="t('customer.extendedList.tableVisibleColumns.countryFk')"
v-model="params.countryFk"
@update:model-value="searchFn()"
:options="countriesOptions"
@ -295,9 +288,7 @@ const shouldRenderColumn = (colName) => {
<QItem v-if="shouldRenderColumn('provinceFk')">
<QItemSection>
<VnSelectFilter
:label="
t('customer.extendedList.tableVisibleColumns.provinceFk')
"
:label="t('customer.extendedList.tableVisibleColumns.provinceFk')"
v-model="params.provinceFk"
@update:model-value="searchFn()"
:options="provincesOptions"
@ -323,9 +314,7 @@ const shouldRenderColumn = (colName) => {
<QItem v-if="shouldRenderColumn('postcode')">
<QItemSection>
<VnInput
:label="
t('customer.extendedList.tableVisibleColumns.postcode')
"
:label="t('customer.extendedList.tableVisibleColumns.postcode')"
v-model="params.postcode"
is-outlined
/>
@ -345,9 +334,7 @@ const shouldRenderColumn = (colName) => {
<QItemSection>
<VnInputDate
v-model="params.created"
:label="
t('customer.extendedList.tableVisibleColumns.created')
"
:label="t('customer.extendedList.tableVisibleColumns.created')"
@update:model-value="searchFn()"
is-outlined
/>
@ -357,9 +344,7 @@ const shouldRenderColumn = (colName) => {
<QItemSection>
<VnSelectFilter
:label="
t(
'customer.extendedList.tableVisibleColumns.businessTypeFk'
)
t('customer.extendedList.tableVisibleColumns.businessTypeFk')
"
v-model="params.businessTypeFk"
:options="businessTypesOptions"
@ -397,9 +382,7 @@ const shouldRenderColumn = (colName) => {
<QItemSection>
<VnSelectFilter
:label="
t(
'customer.extendedList.tableVisibleColumns.sageTaxTypeFk'
)
t('customer.extendedList.tableVisibleColumns.sageTaxTypeFk')
"
v-model="params.sageTaxTypeFk"
@update:model-value="searchFn()"
@ -435,16 +418,12 @@ const shouldRenderColumn = (colName) => {
/>
</QItemSection>
</QItem>
<QItem
v-if="shouldRenderColumn('isActive') || shouldRenderColumn('isVies')"
>
<QItem v-if="shouldRenderColumn('isActive') || shouldRenderColumn('isVies')">
<QItemSection v-if="shouldRenderColumn('isActive')">
<QCheckbox
v-model="params.isActive"
@update:model-value="searchFn()"
:label="
t('customer.extendedList.tableVisibleColumns.isActive')
"
:label="t('customer.extendedList.tableVisibleColumns.isActive')"
toggle-indeterminate
:false-value="undefined"
/>
@ -483,9 +462,7 @@ const shouldRenderColumn = (colName) => {
v-model="params.isEqualizated"
@update:model-value="searchFn()"
:label="
t(
'customer.extendedList.tableVisibleColumns.isEqualizated'
)
t('customer.extendedList.tableVisibleColumns.isEqualizated')
"
toggle-indeterminate
:false-value="undefined"
@ -494,17 +471,14 @@ const shouldRenderColumn = (colName) => {
</QItem>
<QItem
v-if="
shouldRenderColumn('hasToInvoice') ||
shouldRenderColumn('isFreezed')
shouldRenderColumn('hasToInvoice') || shouldRenderColumn('isFreezed')
"
>
<QItemSection v-if="shouldRenderColumn('isFreezed')">
<QCheckbox
v-model="params.isFreezed"
@update:model-value="searchFn()"
:label="
t('customer.extendedList.tableVisibleColumns.isFreezed')
"
:label="t('customer.extendedList.tableVisibleColumns.isFreezed')"
toggle-indeterminate
:false-value="undefined"
/>
@ -514,9 +488,7 @@ const shouldRenderColumn = (colName) => {
v-model="params.hasToInvoice"
@update:model-value="searchFn()"
:label="
t(
'customer.extendedList.tableVisibleColumns.hasToInvoice'
)
t('customer.extendedList.tableVisibleColumns.hasToInvoice')
"
toggle-indeterminate
:false-value="undefined"
@ -547,9 +519,7 @@ const shouldRenderColumn = (colName) => {
v-model="params.isToBeMailed"
@update:model-value="searchFn()"
:label="
t(
'customer.extendedList.tableVisibleColumns.isToBeMailed'
)
t('customer.extendedList.tableVisibleColumns.isToBeMailed')
"
toggle-indeterminate
:false-value="undefined"
@ -557,9 +527,7 @@ const shouldRenderColumn = (colName) => {
</QItemSection>
</QItem>
<QItem
v-if="
shouldRenderColumn('hasLcr') || shouldRenderColumn('hasCoreVnl')
"
v-if="shouldRenderColumn('hasLcr') || shouldRenderColumn('hasCoreVnl')"
>
<QItemSection v-if="shouldRenderColumn('hasLcr')">
<QCheckbox
@ -574,9 +542,7 @@ const shouldRenderColumn = (colName) => {
<QCheckbox
v-model="params.hasCoreVnl"
@update:model-value="searchFn()"
:label="
t('customer.extendedList.tableVisibleColumns.hasCoreVnl')
"
:label="t('customer.extendedList.tableVisibleColumns.hasCoreVnl')"
toggle-indeterminate
:false-value="undefined"
/>
@ -587,9 +553,7 @@ const shouldRenderColumn = (colName) => {
<QCheckbox
v-model="params.hasSepaVnl"
@update:model-value="searchFn()"
:label="
t('customer.extendedList.tableVisibleColumns.hasSepaVnl')
"
:label="t('customer.extendedList.tableVisibleColumns.hasSepaVnl')"
toggle-indeterminate
:false-value="undefined"
/>
@ -597,20 +561,10 @@ const shouldRenderColumn = (colName) => {
</QItem>
<QSeparator />
</QList>
</template>
</VnFilterPanel>
</template>
<style scoped>
.list {
width: 256px;
}
.list * {
max-width: 100%;
}
</style>
<i18n>
es:
Social name: Razón social

View File

@ -36,7 +36,6 @@ const clients = ref();
</template>
<template #body="{ params, searchFn }">
<QList dense class="list">
<QItem class="q-mb-sm q-mt-sm">
<QItemSection>
<VnInput
@ -107,20 +106,10 @@ const clients = ref();
</QItemSection>
</QItem>
<QSeparator />
</QList>
</template>
</VnFilterPanel>
</template>
<style scoped>
.list {
width: 256px;
}
.list * {
max-width: 100%;
}
</style>
<i18n>
en:
params:

View File

@ -122,7 +122,7 @@ function stateColor(row) {
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md customer-payments">
<div class="card-list">
<div class="vn-card-list">
<QToolbar class="q-pa-none justify-end">
<QBtn
@click="arrayData.refresh()"
@ -278,10 +278,6 @@ function stateColor(row) {
<style lang="scss">
.customer-payments {
.card-list {
width: 100%;
max-width: 60em;
.q-table--dense .q-table th:first-child {
padding-left: 0;
}
@ -290,7 +286,6 @@ function stateColor(row) {
overflow: hidden;
text-overflow: ellipsis;
}
}
}
</style>

View File

@ -27,14 +27,9 @@ function isValidNumber(value) {
</div>
</template>
<template #body="{ params }">
<QList dense class="q-gutter-y-sm q-mt-sm">
<QItem>
<QItemSection>
<VnInput
:label="t('Order ID')"
v-model="params.orderFk"
is-outlined
>
<VnInput :label="t('Order ID')" v-model="params.orderFk" is-outlined>
<template #prepend>
<QIcon name="vn:basket" size="xs" />
</template>
@ -67,8 +62,7 @@ function isValidNumber(value) {
}
"
:rules="[
(val) =>
isValidNumber(val) || !val || 'Please type a number',
(val) => isValidNumber(val) || !val || 'Please type a number',
]"
lazy-rules
>
@ -81,17 +75,12 @@ function isValidNumber(value) {
<QItem>
<QItemSection>
<VnInputDate
v-model="params.from"
:label="t('From')"
is-outlined
/>
<VnInputDate v-model="params.from" :label="t('From')" is-outlined />
</QItemSection>
<QItemSection>
<VnInputDate v-model="params.to" :label="t('To')" is-outlined />
</QItemSection>
</QItem>
</QList>
</template>
</VnFilterPanel>
</template>

View File

@ -54,7 +54,6 @@ const suppliersOptions = ref([]);
</div>
</template>
<template #body="{ params }">
<QList dense class="list q-gutter-y-sm q-mt-sm">
<QItem>
<QItemSection>
<VnInput
@ -198,20 +197,10 @@ const suppliersOptions = ref([]);
/>
</QItemSection>
</QItem>
</QList>
</template>
</VnFilterPanel>
</template>
<style scoped>
.list {
width: 256px;
}
.list * {
max-width: 100%;
}
</style>
<i18n>
en:
params:

View File

@ -47,7 +47,7 @@ onMounted(async () => {
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<VnPaginate
data-key="EntryList"
url="Entries/filter"
@ -128,13 +128,6 @@ onMounted(async () => {
</QPageSticky>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>
<i18n>
es:
Search entries: Buscar entradas

View File

@ -36,7 +36,6 @@ const suppliersRef = ref();
</div>
</template>
<template #body="{ params, searchFn }">
<QList dense class="list q-gutter-y-sm q-mt-sm">
<QItem>
<QItemSection>
<VnInput
@ -186,11 +185,7 @@ const suppliersRef = ref();
</QItem>
<QItem>
<QItemSection>
<VnInputDate
:label="t('To')"
v-model="params.to"
is-outlined
/>
<VnInputDate :label="t('To')" v-model="params.to" is-outlined />
</QItemSection>
</QItem>
<QItem>
@ -203,20 +198,10 @@ const suppliersRef = ref();
</QItemSection>
</QItem>
</QExpansionItem>
</QList>
</template>
</VnFilterPanel>
</template>
<style scoped>
.list {
width: 256px;
}
.list * {
max-width: 100%;
}
</style>
<i18n>
en:
params:

View File

@ -71,7 +71,7 @@ function viewSummary(id) {
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<VnPaginate
data-key="InvoiceInList"
url="InvoiceIns/filter"
@ -156,13 +156,6 @@ function viewSummary(id) {
</QPageSticky>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>
<i18n>
es:
Search invoice: Buscar factura emitida

View File

@ -41,7 +41,6 @@ function setWorkers(data) {
</div>
</template>
<template #body="{ params, searchFn }">
<QList dense class="q-gutter-y-sm q-mt-sm">
<QItem>
<QItemSection>
<VnInput
@ -58,11 +57,7 @@ function setWorkers(data) {
</QItem>
<QItem>
<QItemSection>
<VnInput
:label="t('Amount')"
v-model="params.amount"
is-outlined
/>
<VnInput :label="t('Amount')" v-model="params.amount" is-outlined />
</QItemSection>
</QItem>
<QItem>
@ -129,7 +124,6 @@ function setWorkers(data) {
</QItemSection>
</QItem>
</QExpansionItem>
</QList>
</template>
</VnFilterPanel>
</template>

View File

@ -182,7 +182,7 @@ const downloadCsv = () => {
</template>
</VnSubToolbar>
<div class="flex flex-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<CardList
:element="row"
:id="row.id"
@ -247,13 +247,6 @@ const downloadCsv = () => {
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>
<i18n>
en:
searchInvoice: Search issued invoice

View File

@ -27,7 +27,6 @@ const props = defineProps({
</div>
</template>
<template #body="{ params }">
<QList dense class="q-gutter-y-sm q-mt-sm">
<QItem>
<QItemSection>
<VnInputDate
@ -101,13 +100,10 @@ const props = defineProps({
/>
</QItemSection>
</QItem>
</QList>
</template>
</VnFilterPanel>
</template>
<style scoped></style>
<i18n>
en:
params:

View File

@ -0,0 +1,25 @@
<script setup>
import LeftMenu from 'components/LeftMenu.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import ItemDescriptor from './ItemDescriptor.vue';
import { useStateStore } from 'stores/useStateStore';
const stateStore = useStateStore();
</script>
<template>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<QScrollArea class="fit">
<ItemDescriptor />
<QSeparator />
<LeftMenu source="card" />
</QScrollArea>
</QDrawer>
<QPageContainer>
<QPage>
<VnSubToolbar />
<div class="q-pa-md"><RouterView></RouterView></div>
</QPage>
</QPageContainer>
</template>

View File

@ -0,0 +1,316 @@
<script setup>
import { computed, ref, onMounted, onUnmounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import VnConfirm from 'components/ui/VnConfirm.vue';
import RegularizeStockForm from 'components/RegularizeStockForm.vue';
import EditPictureForm from 'components/EditPictureForm.vue';
import { useState } from 'src/composables/useState';
import useCardDescription from 'src/composables/useCardDescription';
import { useSession } from 'src/composables/useSession';
import axios from 'axios';
const $props = defineProps({
id: {
type: Number,
required: false,
default: null,
},
summary: {
type: Object,
default: null,
},
dated: {
type: String,
default: null,
},
});
const quasar = useQuasar();
const route = useRoute();
const router = useRouter();
const { t } = useI18n();
const { getToken } = useSession();
const state = useState();
const user = state.getUser();
const entityId = computed(() => {
return $props.id || route.params.id;
});
const image = ref(null);
const regularizeStockFormDialog = ref(null);
const editPhotoFormDialog = ref(null);
const item = ref(null);
const available = ref(null);
const visible = ref(null);
const _warehouseFk = ref(null);
const warehouseText = ref(null);
const warehouseFk = computed({
get() {
return _warehouseFk.value;
},
set(val) {
_warehouseFk.value = val;
if (val) {
updateStock();
getWarehouseName(val);
}
},
});
const showWarehouseIconTooltip = ref(true);
const showEditPhotoForm = ref(false);
onMounted(async () => {
await getItemAvatar();
warehouseFk.value = user.value.warehouseFk;
});
const getItemAvatar = async () => {
const token = getToken();
const timeStamp = `timestamp=${Date.now()}`;
image.value = `/api/Images/catalog/200x200/${entityId.value}/download?access_token=${token}&${timeStamp}`;
};
const data = ref(useCardDescription());
const setData = (entity) => {
if (!entity) return;
data.value = useCardDescription(entity.name, entity.id);
};
const getWarehouseName = async (warehouseFk) => {
try {
showWarehouseIconTooltip.value = false;
const filter = {
where: { id: warehouseFk },
};
const { data } = await axios.get('Warehouses/findOne', { filter });
warehouseText.value = t('item.descriptor.warehouseText', {
warehouseName: data.name,
});
showWarehouseIconTooltip.value = true;
} catch (err) {
console.error('Error finding warehouse');
}
};
const updateStock = async () => {
try {
available.value = null;
visible.value = null;
const params = {
warehouseFk: warehouseFk.value,
dated: $props.dated,
};
const { data } = await axios.get(`Items/${entityId.value}/getVisibleAvailable`, {
params,
});
available.value = data.available;
visible.value = data.visible;
} catch (err) {
console.error('Error updating stock');
}
};
const openRegularizeStockForm = () => {
regularizeStockFormDialog.value.show();
};
const toggleEditPictureForm = () => {
showEditPhotoForm.value = !showEditPhotoForm.value;
};
const cloneItem = async () => {
try {
const { data } = await axios.post(`Items/${entityId.value}/clone`);
router.push({ name: 'ItemTags', params: { id: data.id } });
} catch (err) {
console.error('Error cloning item');
}
};
const openCloneDialog = async () => {
quasar
.dialog({
component: VnConfirm,
componentProps: {
title: t("All it's properties will be copied"),
message: t('Do you want to clone this item?'),
},
})
.onOk(async () => {
await cloneItem();
});
};
</script>
<template>
<CardDescriptor
data-key="ItemData"
module="Item"
:title="data.title"
:subtitle="data.subtitle"
:summary="$props.summary"
:url="`Items/${entityId}/getCard`"
@on-fetch="
(data) => {
item = data;
setData(data);
}
"
>
<template #menu="{}">
<QItem v-ripple clickable @click="openRegularizeStockForm()">
<QItemSection>
{{ t('Regularize stock') }}
<QDialog ref="regularizeStockFormDialog">
<RegularizeStockForm
:item-fk="entityId"
:warehouse-fk="warehouseFk"
@on-data-saved="updateStock()"
/>
</QDialog>
</QItemSection>
</QItem>
<QItem v-ripple clickable @click="openCloneDialog()">
<QItemSection>
{{ t('Clone') }}
</QItemSection>
</QItem>
</template>
<template #before>
<div class="relative-position">
<QImg :src="image" spinner-color="primary" class="photo">
<template #error>
<div
class="absolute-full picture text-center q-pa-md flex flex-center"
>
<div>
<div
class="text-grey-5"
style="opacity: 0.4; font-size: 5vh"
>
<QIcon name="vn:item" />
</div>
<div class="text-grey-5" style="opacity: 0.4">
{{ t('item.descriptor.item') }}
</div>
</div>
</div>
</template>
</QImg>
<QBtn
color="primary"
size="lg"
round
class="edit-photo-btn"
@click="toggleEditPictureForm()"
>
<QIcon name="edit" size="sm" />
<QDialog ref="editPhotoFormDialog" v-model="showEditPhotoForm">
<EditPictureForm
collection="catalog"
:id="entityId"
@close-form="toggleEditPictureForm()"
@on-photo-uploaded="getItemAvatar()"
/>
</QDialog>
</QBtn>
</div>
<div
class="row justify-between items-center full-width bg-primary"
style="height: 54px"
>
<div class="col column items-center">
<span class="text-uppercase color-vn-white" style="font-size: 11px">
{{ t('item.descriptor.visible') }}
</span>
<span class="text-weight-bold text-h5 color-vn-white">{{
visible
}}</span>
</div>
<div
class="col column items-center separation-borders"
style="font-size: 11px"
>
<span class="text-uppercase color-vn-white">
{{ t('item.descriptor.available') }}
</span>
<span class="text-weight-bold text-h5 color-vn-white">{{
available
}}</span>
</div>
<div class="col column items-center justify-center">
<QIcon name="info" class="cursor-pointer color-vn-white" size="md">
<QTooltip>{{ warehouseText }}</QTooltip>
</QIcon>
</div>
</div>
</template>
<template #body="{ entity }">
<VnLv :label="t('item.descriptor.buyer')">
<template #value>
<span class="link">
{{ t('item.descriptor.buyer') }}
<WorkerDescriptorProxy :id="entity.itemType?.worker?.id" />
</span>
</template>
</VnLv>
<VnLv :label="t('item.descriptor.color')" :value="entity.value5"> </VnLv>
<VnLv :label="t('item.descriptor.color')" :value="entity.value6" />
<VnLv :label="t('item.descriptor.stems')" :value="entity.value7" />
</template>
<template #actions="{}">
<QCardActions class="row justify-center">
<QBtn
:to="{ name: 'ItemDiary' }"
size="md"
icon="vn:transaction"
color="primary"
>
<QTooltip>{{ t('item.descriptor.itemDiary') }}</QTooltip>
</QBtn>
</QCardActions>
</template>
</CardDescriptor>
</template>
<i18n>
es:
Regularize stock: Regularizar stock
Clone: Clonar
All it's properties will be copied: Todas sus propiedades serán copiadas
Do you want to clone this item?: ¿Desea clonar este artículo?
</i18n>
<style lang="scss" scoped>
.photo {
height: 256px;
}
.edit-photo-btn {
position: absolute;
right: 12px;
bottom: 12px;
z-index: 1;
cursor: pointer;
}
.separation-borders {
border-left: 1px solid $white;
border-right: 1px solid $white;
}
</style>

View File

@ -0,0 +1,26 @@
<script setup>
import ItemDescriptor from './ItemDescriptor.vue';
import ItemSummaryDialog from './ItemSummaryDialog.vue';
const $props = defineProps({
id: {
type: Number,
required: true,
},
dated: {
type: String,
default: null,
},
});
</script>
<template>
<QPopupProxy>
<ItemDescriptor
v-if="$props.id"
:id="$props.id"
:summary="ItemSummaryDialog"
:dated="dated"
/>
</QPopupProxy>
</template>

View File

@ -0,0 +1 @@
<template>Item diary (CREAR CUANDO SE DESARROLLE EL MODULO DE ITEMS)</template>

View File

@ -0,0 +1 @@
<template>Item summary</template>

View File

@ -0,0 +1,5 @@
<template>
<QDialog
>Item summary dialog (A DESARROLLAR CUANDO SE CREE EL MODULO DE ITEMS)</QDialog
>
</template>

View File

@ -0,0 +1 @@
<template>Item tags (CREAR CUANDO SE DESARROLLE EL MODULO DE ITEMS)</template>

View File

@ -0,0 +1 @@
<template>Item list</template>

View File

@ -0,0 +1,18 @@
<script setup>
import LeftMenu from 'src/components/LeftMenu.vue';
import { useStateStore } from 'stores/useStateStore';
const stateStore = useStateStore();
</script>
<template>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<QScrollArea class="fit text-grey-8">
<LeftMenu />
</QScrollArea>
</QDrawer>
<QPageContainer>
<RouterView></RouterView>
</QPageContainer>
</template>

View File

@ -219,7 +219,6 @@ const getCategoryClass = (category, params) => {
</template>
</template>
<template #body="{ params, searchFn }">
<QList dense style="max-width: 256px">
<QItem class="category-filter q-mt-md">
<div
v-for="category in categoryList"
@ -395,7 +394,6 @@ const getCategoryClass = (category, params) => {
</QItemSection>
</QItem>
<QSeparator />
</QList>
</template>
</VnFilterPanel>
</template>

View File

@ -1,10 +1,13 @@
<script setup>
import { useSession } from 'composables/useSession';
import VnLv from 'components/ui/VnLv.vue';
import { useI18n } from 'vue-i18n';
import OrderCatalogItemDialog from 'pages/Order/Card/OrderCatalogItemDialog.vue';
import toCurrency from '../../../filters/toCurrency';
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import VnLv from 'components/ui/VnLv.vue';
import OrderCatalogItemDialog from 'pages/Order/Card/OrderCatalogItemDialog.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import { useSession } from 'composables/useSession';
import toCurrency from '../../../filters/toCurrency';
const DEFAULT_PRICE_KG = 0;
@ -42,7 +45,10 @@ const dialog = ref(null);
</div>
</div>
<div class="content">
<span class="link">{{ item.name }}</span>
<span class="link">
{{ item.name }}
<ItemDescriptorProxy :id="item.id" />
</span>
<p class="subName">{{ item.subName }}</p>
<template v-for="index in 4" :key="`tag-${index}`">
<VnLv

View File

@ -59,7 +59,6 @@ const sourceList = ref(null);
</div>
</template>
<template #body="{ params }">
<QList id="orderFilter" dense>
<QItem>
<QItemSection>
<VnInput
@ -202,19 +201,10 @@ const sourceList = ref(null);
<QCheckbox v-model="params.showEmpty" :label="t('showEmpty')" />
</QItemSection>
</QItem>
</QList>
</template>
</VnFilterPanel>
</template>
<style lang="scss">
#orderFilter {
.q-item {
padding-top: 8px;
}
}
</style>
<i18n>
en:
params:

View File

@ -1,7 +1,7 @@
<script setup>
import { useStateStore } from 'stores/useStateStore';
import { useRoute } from 'vue-router';
import {onMounted, onUnmounted, ref} from 'vue';
import { onMounted, onUnmounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import VnPaginate from 'components/ui/VnPaginate.vue';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
@ -20,7 +20,7 @@ const catalogParams = {
orderBy: JSON.stringify({ field: 'relevancy DESC, name', way: 'ASC', isTag: false }),
};
const tags = ref([])
const tags = ref([]);
function extractTags(items) {
const resultTags = [];
@ -34,7 +34,7 @@ function extractTags(items) {
}
});
});
tags.value = resultTags
tags.value = resultTags;
}
</script>
@ -70,7 +70,7 @@ function extractTags(items) {
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<div class="full-width">
<VnPaginate
data-key="OrderCatalogList"
url="Orders/CatalogFilter"
@ -93,10 +93,6 @@ function extractTags(items) {
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
}
.catalog-list {
display: flex;
align-items: flex-start;

View File

@ -83,7 +83,7 @@ async function confirmOrder() {
auto-load
/>
<QPage :key="componentKey" class="column items-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<div v-if="!orderSummary.total" class="no-result">
{{ t('globals.noResults') }}
</div>
@ -228,11 +228,6 @@ async function confirmOrder() {
}
</style>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
.header {
color: $primary;
font-weight: bold;

View File

@ -4,7 +4,7 @@ import { onMounted, onUnmounted } from 'vue';
import { useRouter } from 'vue-router';
import { useStateStore } from 'stores/useStateStore';
import { toCurrency, toDate } from 'src/filters';
import {useQuasar} from "quasar";
import { useQuasar } from 'quasar';
import CardList from 'components/ui/CardList.vue';
import WorkerDescriptorProxy from 'pages/Worker/Card/WorkerDescriptorProxy.vue';
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
@ -12,7 +12,7 @@ import VnPaginate from 'components/ui/VnPaginate.vue';
import VnLv from 'components/ui/VnLv.vue';
import OrderSearchbar from 'pages/Order/Card/OrderSearchbar.vue';
import OrderFilter from 'pages/Order/Card/OrderFilter.vue';
import OrderSummaryDialog from "pages/Order/Card/OrderSummaryDialog.vue";
import OrderSummaryDialog from 'pages/Order/Card/OrderSummaryDialog.vue';
const stateStore = useStateStore();
const quasar = useQuasar();
@ -63,7 +63,7 @@ function viewSummary(id) {
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<VnPaginate
data-key="OrderList"
url="Orders/filter"
@ -153,10 +153,3 @@ function viewSummary(id) {
</QPageSticky>
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>

View File

@ -35,7 +35,7 @@ const loadVolumes = async (rows) => {
auto-load
/>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<div
v-if="!volumeSummary?.totalVolume && !volumeSummary?.totalBoxes"
class="no-result"
@ -121,11 +121,6 @@ const loadVolumes = async (rows) => {
}
</style>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
.header {
color: $primary;
font-weight: bold;

View File

@ -61,7 +61,6 @@ const warehouseList = ref([]);
</div>
</template>
<template #body="{ params }">
<QList dense>
<QItem class="q-my-sm">
<QItemSection v-if="workerList">
<VnSelectFilter
@ -198,7 +197,6 @@ const warehouseList = ref([]);
/>
</QItemSection>
</QItem>
</QList>
</template>
</VnFilterPanel>
</template>

View File

@ -71,8 +71,8 @@ const routeFilter = {
};
const onSave = (data, response) => {
if (isNew) {
axios.post(`Routes/${response.data?.id}/updateWorkCenter`);
router.push({ name: 'RouteSummary', params: { id: response.data?.id } });
axios.post(`Routes/${response?.id}/updateWorkCenter`);
router.push({ name: 'RouteSummary', params: { id: response?.id } });
}
};
</script>
@ -198,6 +198,13 @@ const onSave = (data, response) => {
clearable
/>
</div>
<div class="col flex items-center">
<QCheckbox
size="sm"
v-model="data.isOk"
:label="t('Is served')"
/>
</div>
</VnRow>
</template>
<VnRow class="row q-gutter-md q-mb-md">
@ -212,3 +219,15 @@ const onSave = (data, response) => {
</template>
</FormModel>
</template>
<i18n>
es:
Worker: Trabajador
Vehicle: Vehículo
Agency: Agencia
Km Start: Km de inicio
Km End: Km de fin
Hour started: Hora inicio
Hour finished: Hora fin
Description: Descripción
Is served: Se ha servido
</i18n>

View File

@ -28,7 +28,6 @@ const countries = ref();
</div>
</template>
<template #body="{ params }">
<QList dense class="q-gutter-y-sm q-mt-sm">
<QItem>
<QItemSection>
<VnInput
@ -124,7 +123,6 @@ const countries = ref();
/>
</QItemSection>
</QItem>
</QList>
</template>
</VnFilterPanel>
</template>

View File

@ -272,6 +272,7 @@ function previewRoute(id) {
selection="multiple"
:rows-per-page-options="[0]"
hide-pagination
:pagination="{ sortBy: 'ID', descending: true }"
>
<template #body-cell-worker="props">
<QTd :props="props">
@ -459,11 +460,12 @@ function previewRoute(id) {
</template>
<template #body-cell-actions="props">
<QTd :props="props">
<div class="table-actions">
<div class="flex items-center table-actions">
<QIcon
name="vn:ticketAdd"
size="xs"
color="primary"
class="cursor-pointer"
>
<QTooltip>{{ t('Add ticket') }}</QTooltip>
</QIcon>
@ -472,6 +474,7 @@ function previewRoute(id) {
size="xs"
color="primary"
@click="previewRoute(props?.row?.id)"
class="cursor-pointer"
>
<QTooltip>{{ t('Preview') }}</QTooltip>
</QIcon>
@ -500,13 +503,7 @@ function previewRoute(id) {
}
.table-actions {
display: flex;
align-items: center;
gap: 12px;
i {
cursor: pointer;
}
}
</style>
<i18n>
@ -528,4 +525,6 @@ es:
Cancel: Cancelar
Clone: Clonar
Mark as served: Marcar como servidas
Add ticket: Añadir tickets
Preview: Vista previa
</i18n>

View File

@ -41,7 +41,11 @@ function setParkings(data) {
@on-fetch="setWorkers"
auto-load
/>
<VnFilterPanel :data-key="props.dataKey" :search-button="true" @search="emit('search')">
<VnFilterPanel
:data-key="props.dataKey"
:search-button="true"
@search="emit('search')"
>
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>
@ -49,7 +53,6 @@ function setParkings(data) {
</div>
</template>
<template #body="{ params }">
<QList dense>
<QItem class="q-my-sm">
<QItemSection v-if="!parkings">
<QSkeleton type="QInput" class="full-width" />
@ -101,7 +104,6 @@ function setParkings(data) {
/>
</QItemSection>
</QItem>
</QList>
</template>
</VnFilterPanel>
</template>

View File

@ -74,7 +74,7 @@ function exprBuilder(param, value) {
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<VnPaginate
data-key="ShelvingList"
url="Shelvings"
@ -129,10 +129,3 @@ function exprBuilder(param, value) {
</QPageSticky>
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>

View File

@ -106,7 +106,7 @@ onMounted(() => {
</div>
<div class="col">
<VnSelectCreate
:label="t('worker.create.bankEntity')"
:label="t('supplier.accounts.bankEntity')"
v-model="row.bankEntityFk"
:options="bankEntitiesOptions"
option-label="name"
@ -135,7 +135,17 @@ onMounted(() => {
<VnInput
:label="t('supplier.accounts.beneficiary')"
v-model="row.beneficiary"
/>
>
<template #append>
<QIcon name="info" class="cursor-pointer">
<QTooltip>{{
t(
'Name of the bank account holder if different from the provider'
)
}}</QTooltip>
</QIcon>
</template>
</VnInput>
</div>
<div class="col-1 row justify-center items-center">
<QIcon
@ -174,4 +184,5 @@ onMounted(() => {
Do you want to change the pay method to wire transfer?: ¿Quieres modificar la forma de pago a transferencia?
Add account: Añadir cuenta
Remove account: Remover cuenta
Name of the bank account holder if different from the provider: Nombre del titular de la cuenta bancaria en caso de ser diferente del proveedor
</i18n>

View File

@ -47,7 +47,7 @@ const redirectToUpdateView = (addressData) => {
<template>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<VnPaginate
data-key="SupplierAddress"
:url="`Suppliers/${route.params.id}/addresses`"
@ -88,10 +88,3 @@ const redirectToUpdateView = (addressData) => {
</QPageSticky>
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>

View File

@ -147,8 +147,6 @@ onMounted(() => {
option-value="id"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('supplier.addresses.province')"
@ -160,21 +158,20 @@ onMounted(() => {
v-model="data.provinceFk"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.phone"
:label="t('supplier.addresses.phone')"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.mobile"
:label="t('supplier.addresses.mobile')"
/>
</div>
<div class="col" />
</VnRow>
</template>
</FormModel>

View File

@ -80,8 +80,6 @@ const onDataSaved = () => {
type="number"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
:label="t('supplier.agencyTerms.m3Price')"
@ -89,6 +87,8 @@ const onDataSaved = () => {
type="number"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
:label="t('supplier.agencyTerms.routePrice')"
@ -96,8 +96,6 @@ const onDataSaved = () => {
type="number"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
:label="t('supplier.agencyTerms.minimumKm')"
@ -105,7 +103,6 @@ const onDataSaved = () => {
type="number"
/>
</div>
<div class="col" />
</VnRow>
</template>
</FormModel>

View File

@ -48,6 +48,13 @@ const workersOptions = ref([]);
map-options
:rules="validate('supplier.workerFk')"
>
<template #append>
<QIcon name="info" class="cursor-pointer">
<QTooltip>{{
t('Responsible for approving invoices')
}}</QTooltip>
</QIcon>
</template>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
@ -88,10 +95,14 @@ const workersOptions = ref([]);
type="textarea"
v-model="data.note"
fill-input
autogrow
/>
</div>
</VnRow>
</template>
</FormModel>
</template>
<i18n>
es:
Responsible for approving invoices: Responsable de aprobar las facturas
</i18n>

View File

@ -13,6 +13,13 @@ const { t } = useI18n();
const paymethodsOptions = ref([]);
const payDemsOptions = ref([]);
const formatPayDems = (data) => {
payDemsOptions.value = data.map(({ id, payDem }) => ({
id: id,
payDem: payDem || '0',
}));
};
</script>
<template>
<FetchData
@ -20,7 +27,7 @@ const payDemsOptions = ref([]);
@on-fetch="(data) => (paymethodsOptions = data)"
auto-load
/>
<FetchData url="PayDems" @on-fetch="(data) => (payDemsOptions = data)" auto-load />
<FetchData url="PayDems" @on-fetch="(data) => formatPayDems(data)" auto-load />
<FormModel
:url="`Suppliers/${route.params.id}`"
:url-update="`Suppliers/${route.params.id}`"
@ -37,7 +44,6 @@ const payDemsOptions = ref([]);
option-value="id"
option-label="name"
hide-selected
map-options
:rules="validate('supplier.payMethodFk')"
/>
</div>
@ -49,7 +55,6 @@ const payDemsOptions = ref([]);
option-value="id"
option-label="payDem"
hide-selected
map-options
:rules="validate('supplier.payDemFk')"
/>
</div>

View File

@ -14,6 +14,7 @@ const { t } = useI18n();
<Teleport to="#searchbar">
<VnSearchbar
data-key="SuppliersList"
url="Suppliers/filter"
:limit="20"
:label="t('Search suppliers')"
/>

View File

@ -1,47 +1,64 @@
<script setup>
import { useRoute } from 'vue-router';
import { ref, computed } from 'vue';
import { computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import axios from 'axios';
import { useStateStore } from 'stores/useStateStore';
import { usePrintService } from 'composables/usePrintService';
import useNotify from 'src/composables/useNotify.js';
import { toDate, toDateString } from 'src/filters';
import { dashIfEmpty } from 'src/filters';
import FetchData from 'components/FetchData.vue';
import FetchedTags from 'components/ui/FetchedTags.vue';
import SendEmailDialog from 'components/common/SendEmailDialog.vue';
import SupplierConsumptionFilter from './SupplierConsumptionFilter.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import { toDate } from 'src/filters';
import { dashIfEmpty } from 'src/filters';
import { usePrintService } from 'composables/usePrintService';
import useNotify from 'src/composables/useNotify.js';
import axios from 'axios';
import { useStateStore } from 'stores/useStateStore';
import { useArrayData } from 'composables/useArrayData';
const stateStore = useStateStore();
const { t } = useI18n();
const route = useRoute();
const stateStore = useStateStore();
const { openReport, sendEmail } = usePrintService();
const quasar = useQuasar();
const { notify } = useNotify();
const suppliersConsumption = ref();
const arrayData = useArrayData('SupplierConsumption', {
url: 'Suppliers/consumption',
order: ['itemTypeFk', 'itemName', 'itemSize'],
userFilter: { where: { supplierFk: route.params.id } },
});
const userParams = computed(() => {
const minDate = Date.vnNew();
minDate.setHours(0, 0, 0, 0);
minDate.setMonth(minDate.getMonth() - 2);
const store = arrayData.store;
const maxDate = Date.vnNew();
maxDate.setHours(23, 59, 59, 59);
return {
from: toDateString(minDate),
to: toDateString(maxDate),
const dateRanges = computed(() => {
const ranges = {
from: null,
to: null,
};
if (route.query && route.query.params) {
const params = JSON.parse(route.query.params);
if (params.from && params.to) {
ranges.from = params.from;
ranges.to = params.to;
}
}
return ranges;
});
const reportParams = computed(() => ({
recipientId: Number(route.params.id),
...userParams.value,
...dateRanges.value,
}));
const rows = computed(() => suppliersConsumption.value || []);
async function getSupplierConsumptionData() {
await arrayData.fetch({ append: false });
}
const rows = computed(() => store.data || []);
const openReportPdf = () => {
openReport(`Suppliers/${route.params.id}/campaign-metrics-pdf`, reportParams.value);
@ -88,24 +105,19 @@ const sendCampaignMetricsEmail = ({ address }) => {
});
};
const calculateTotal = (buysArray) =>
const calculateTotal = (buysArray = []) =>
buysArray.reduce((accumulator, { total }) => accumulator + total, 0);
onMounted(async () => {
stateStore.rightDrawer = true;
await getSupplierConsumptionData();
});
</script>
<template>
<FetchData
url="Suppliers/consumption"
@on-fetch="(data) => (suppliersConsumption = data)"
:filter="{
where: { supplierFk: route.params.id },
order: ['itemTypeFk', 'itemName', 'itemSize'],
}"
:params="userParams"
auto-load
/>
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()">
<QBtn
v-if="userParams.from && userParams.to"
v-if="dateRanges.from && dateRanges.to"
color="primary"
icon-right="picture_as_pdf"
no-caps
@ -117,7 +129,7 @@ const calculateTotal = (buysArray) =>
</QTooltip>
</QBtn>
<QBtn
v-if="userParams.from && userParams.to"
v-if="dateRanges.from && dateRanges.to"
color="primary"
icon-right="email"
no-caps
@ -128,14 +140,20 @@ const calculateTotal = (buysArray) =>
</QTooltip>
</QBtn>
</Teleport>
<QPage class="column items-center q-pa-md">
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<QScrollArea class="fit text-grey-8">
<SupplierConsumptionFilter data-key="SupplierConsumption" />
</QScrollArea>
</QDrawer>
<QTable
:rows="rows"
hide-bottom
row-key="id"
hide-header
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md"
:no-data-label="t('No results')"
>
<template #body="{ row }">
<QTr>
@ -151,7 +169,10 @@ const calculateTotal = (buysArray) =>
<QTd no-hover>{{ row.invoiceNumber }}</QTd>
</QTr>
<QTr v-for="(buy, index) in row.buys" :key="index">
<QTd no-hover> {{ buy.itemName }}</QTd>
<QTd no-hover>
<QBtn flat color="blue" dense>{{ buy.itemName }}</QBtn>
<ItemDescriptorProxy :id="buy.itemFk" />
</QTd>
<QTd no-hover>
<span>{{ buy.subName }}</span>

View File

@ -0,0 +1,181 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import VnInput from 'src/components/common/VnInput.vue';
import FetchData from 'components/FetchData.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
const { t } = useI18n();
const props = defineProps({
dataKey: {
type: String,
required: true,
},
});
const buyersOptions = ref([]);
const itemTypesOptions = ref([]);
const itemCategoriesOptions = ref([]);
</script>
<template>
<FetchData
url="TicketRequests/getItemTypeWorker"
:filter="{
fields: ['id', 'nickname'],
order: 'nickname ASC',
}"
@on-fetch="(data) => (buyersOptions = data)"
auto-load
/>
<FetchData
url="ItemTypes"
:filter="{
fields: ['id', 'name', 'categoryFk'],
include: 'category',
order: 'name ASC',
}"
@on-fetch="(data) => (itemTypesOptions = data)"
auto-load
/>
<FetchData
url="ItemCategories"
:filter="{
fields: ['id', 'name'],
order: 'name ASC',
}"
@on-fetch="(data) => (itemCategoriesOptions = data)"
auto-load
/>
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>
<span>{{ formatFn(tag.value) }}</span>
</div>
</template>
<template #body="{ params, searchFn }">
<QItem>
<QItemSection>
<VnInput
v-model="params.search"
:label="t('params.search')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
v-model="params.itemId"
:label="t('params.itemId')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelectFilter
:label="t('params.buyerId')"
v-model="params.buyerId"
@update:model-value="searchFn()"
:options="buyersOptions"
option-value="id"
option-label="nickname"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelectFilter
:label="t('params.typeId')"
v-model="params.typeId"
@update:model-value="searchFn()"
:options="itemTypesOptions"
option-value="id"
option-label="name"
hide-selected
dense
outlined
rounded
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
<QItemLabel caption>{{
scope.opt?.category?.name
}}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectFilter>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelectFilter
:label="t('params.categoryId')"
v-model="params.categoryId"
@update:model-value="searchFn()"
:options="itemCategoriesOptions"
option-value="id"
option-label="name"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInputDate
:label="t('params.from')"
v-model="params.from"
@update:model-value="searchFn()"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInputDate
:label="t('params.to')"
v-model="params.to"
@update:model-value="searchFn()"
is-outlined
/>
</QItemSection>
</QItem>
</template>
</VnFilterPanel>
</template>
<i18n>
en:
params:
search: General search
itemId: Item id
buyerId: Buyer
typeId: Type
categoryId: Category
from: From
to: To
es:
params:
search: Búsqueda general
itemId: Id Artículo
buyerId: Comprador
typeId: Tipo
categoryId: Reino
from: Desde
to: Hasta
</i18n>

View File

@ -1,10 +1,14 @@
<script setup>
import { ref, computed } from 'vue';
import { ref, computed, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import CardDescriptor from 'components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import { toDateString } from 'src/filters';
import useCardDescription from 'src/composables/useCardDescription';
import { getUrl } from 'src/composables/getUrl';
const $props = defineProps({
id: {
@ -16,6 +20,7 @@ const $props = defineProps({
const route = useRoute();
const { t } = useI18n();
const url = ref();
const filter = {
fields: [
@ -53,6 +58,10 @@ const filter = {
],
};
onMounted(async () => {
url.value = await getUrl('');
});
const entityId = computed(() => {
return $props.id || route.params.id;
});
@ -61,6 +70,27 @@ const data = ref(useCardDescription());
const setData = (entity) => {
data.value = useCardDescription(entity.ref, entity.id);
};
const getEntryQueryParams = (supplier) => {
if (!supplier) return null;
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
const from = new Date(date.getTime());
from.setDate(from.getDate() - 10);
const to = new Date(date.getTime());
to.setDate(to.getDate() + 10);
const params = {
supplierFk: supplier.id,
from: toDateString(from),
to: toDateString(to),
};
return params;
};
</script>
<template>
@ -73,6 +103,22 @@ const setData = (entity) => {
@on-fetch="setData"
data-key="Supplier"
>
<template #header-extra-action>
<QBtn
round
flat
dense
size="md"
icon="vn:supplier"
color="white"
class="link"
:to="{ name: 'SupplierList' }"
>
<QTooltip>
{{ t('Go to module index') }}
</QTooltip>
</QBtn>
</template>
<template #body="{ entity }">
<VnLv :label="t('supplier.summary.taxNumber')" :value="entity.nif" />
<VnLv label="Alias" :value="entity.nickname" />
@ -87,8 +133,69 @@ const setData = (entity) => {
<VnLv :label="t('supplier.summary.payDay')" :value="entity.payDay" />
<VnLv :label="t('supplier.summary.account')" :value="entity.account" />
</template>
<template #icons="{ entity }">
<QCardActions class="q-gutter-x-md">
<QIcon
v-if="!entity.isActive"
name="vn:disabled"
color="primary"
size="xs"
>
<QTooltip>{{ t('Inactive supplier') }}</QTooltip>
</QIcon>
<QIcon
v-if="!entity.isSerious"
name="vn:supplierfalse"
color="primary"
size="xs"
>
<QTooltip>{{ t('Unverified supplier') }}</QTooltip>
</QIcon>
</QCardActions>
</template>
<template #actions="{ entity }">
<QCardActions>
<QBtn
:to="{
name: 'EntryList',
query: { params: JSON.stringify(getEntryQueryParams(entity)) },
}"
size="md"
icon="vn:entry"
color="primary"
>
<QTooltip>{{ t('All entries with current supplier') }}</QTooltip>
</QBtn>
<QBtn
:to="{
name: 'CustomerCard',
params: { id: entity.client?.id },
}"
size="md"
icon="vn:Person"
color="primary"
>
<QTooltip>{{ t('Go to client') }}</QTooltip>
</QBtn>
<QBtn
:href="`${url}invoice-in/create?supplierFk=${entity.id}`"
size="md"
icon="vn:invoice-in-create"
color="primary"
>
<QTooltip>{{ t('Create invoiceIn') }}</QTooltip>
</QBtn>
</QCardActions>
</template>
</CardDescriptor>
</template>
<i18n>
<i18n>
es:
All entries with current supplier: Todas las entradas con proveedor actual
Go to client: Ir a cliente
Create invoiceIn: Crear factura recibida
Go to module index: Ir al índice del módulo
Inactive supplier: Proveedor inactivo
Unverified supplier: Proveedor no verificado
</i18n>

View File

@ -1,6 +1,8 @@
<script setup>
import { reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
@ -9,12 +11,17 @@ import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import { useStateStore } from 'stores/useStateStore';
const router = useRouter();
const { t } = useI18n();
const stateStore = useStateStore();
const newSupplierForm = reactive({
name: null,
});
const redirectToSupplierFiscalData = (_, responseData) => {
router.push({ name: 'SupplierFiscalData', params: { id: responseData.id } });
};
</script>
<template>
@ -34,6 +41,7 @@ const newSupplierForm = reactive({
url-create="Suppliers/newSupplier"
model="supplier"
:form-initial-data="newSupplierForm"
@on-data-saved="redirectToSupplierFiscalData"
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">

View File

@ -1,13 +1,16 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore';
import { useQuasar } from 'quasar';
import { useRouter } from 'vue-router';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import VnPaginate from 'src/components/ui/VnPaginate.vue';
import CardList from 'src/components/ui/CardList.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import { useQuasar } from 'quasar';
import SupplierSummaryDialog from './Card/SupplierSummaryDialog.vue';
import SupplierListFilter from './SupplierListFilter.vue';
import { useStateStore } from 'stores/useStateStore';
const stateStore = useStateStore();
const router = useRouter();
@ -44,7 +47,12 @@ const viewSummary = (id) => {
</template>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<QScrollArea class="fit text-grey-8">
<SupplierListFilter data-key="SuppliersList" />
</QScrollArea>
</QDrawer>
<div class="vn-card-list">
<VnPaginate data-key="SuppliersList" url="Suppliers/filter" auto-load>
<template #body="{ rows }">
<CardList
@ -56,7 +64,7 @@ const viewSummary = (id) => {
>
<template #list-items>
<VnLv label="NIF/CIF" :value="row.nif" />
<VnLv label="Alias" :value="row.nickname" />
<VnLv label="Alias" :value="row.alias" />
<VnLv
:label="t('supplier.list.payMethod')"
:value="row.payMethod"
@ -95,13 +103,6 @@ const viewSummary = (id) => {
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>
<i18n>
en:
Search suppliers: Search suppliers

View File

@ -0,0 +1,122 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import VnInput from 'src/components/common/VnInput.vue';
import FetchData from 'components/FetchData.vue';
const props = defineProps({
dataKey: {
type: String,
required: true,
},
});
const { t } = useI18n();
const provincesOptions = ref([]);
const countriesOptions = ref([]);
</script>
<template>
<FetchData
url="Provinces"
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }"
@on-fetch="(data) => (provincesOptions = data)"
auto-load
/>
<FetchData
url="countries"
:filter="{ fields: ['id', 'country'], order: 'country ASC', limit: 30 }"
@on-fetch="(data) => (countriesOptions = data)"
auto-load
/>
<VnFilterPanel
:data-key="props.dataKey"
:search-button="true"
:unremovable-params="['supplierFk']"
>
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>
<span>{{ formatFn(tag.value) }}</span>
</div>
</template>
<template #body="{ params, searchFn }">
<QItem>
<QItemSection>
<VnInput
v-model="params.search"
:label="t('params.search')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
v-model="params.nickname"
:label="t('params.nickname')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput v-model="params.nif" :label="t('params.nif')" is-outlined />
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelectFilter
:label="t('params.provinceFk')"
v-model="params.provinceFk"
@update:model-value="searchFn()"
:options="provincesOptions"
option-value="id"
option-label="name"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelectFilter
:label="t('params.countryFk')"
v-model="params.countryFk"
@update:model-value="searchFn()"
:options="countriesOptions"
option-value="id"
option-label="country"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
</template>
</VnFilterPanel>
</template>
<i18n>
en:
params:
search: General search
nickname: Alias
nif: Tax number
provinceFk: Province
countryFk: Country
es:
params:
search: Búsqueda general
nickname: Alias
nif: NIF/CIF
provinceFk: Provincia
countryFk: País
</i18n>

View File

@ -56,7 +56,6 @@ const warehouses = ref();
</div>
</template>
<template #body="{ params, searchFn }">
<QList dense class="q-gutter-y-sm q-mt-sm">
<QItem>
<QItemSection>
<VnInput
@ -75,11 +74,7 @@ const warehouses = ref();
</QItem>
<QItem>
<QItemSection>
<VnInputDate
v-model="params.from"
:label="t('From')"
is-outlined
/>
<VnInputDate v-model="params.from" :label="t('From')" is-outlined />
</QItemSection>
<QItemSection>
<VnInputDate v-model="params.to" :label="t('To')" is-outlined />
@ -244,7 +239,6 @@ const warehouses = ref();
</QItemSection>
</QItem>
</QExpansionItem>
</QList>
</template>
</VnFilterPanel>
</template>

View File

@ -74,7 +74,7 @@ function viewSummary(id) {
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<VnPaginate
data-key="TicketList"
url="Tickets/filter"
@ -134,13 +134,6 @@ function viewSummary(id) {
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>
<i18n>
es:
Search ticket: Buscar ticket

View File

@ -66,7 +66,6 @@ const decrement = (paramsObj, key) => {
</div>
</template>
<template #body="{ params }">
<QList dense class="list q-gutter-y-sm q-mt-sm">
<QItem>
<QItemSection>
<VnInput label="id" v-model="params.id" is-outlined />
@ -205,19 +204,11 @@ const decrement = (paramsObj, key) => {
/>
</QItemSection>
</QItem>
</QList>
</template>
</VnFilterPanel>
</template>
<style scoped>
.list {
width: 256px;
}
.list * {
max-width: 100%;
}
.input-number >>> input[type='number'] {
-moz-appearance: textfield;
}

View File

@ -61,7 +61,6 @@ const decrement = (paramsObj, key) => {
</div>
</template>
<template #body="{ params }">
<QList dense class="list q-gutter-y-sm q-mt-sm">
<QItem>
<QItemSection>
<VnInput
@ -258,19 +257,11 @@ const decrement = (paramsObj, key) => {
</QInput>
</QItemSection>
</QItem>
</QList>
</template>
</VnFilterPanel>
</template>
<style scoped>
.list {
width: 256px;
}
.list * {
max-width: 100%;
}
.input-number >>> input[type='number'] {
-moz-appearance: textfield;
}

View File

@ -56,7 +56,7 @@ onMounted(async () => {
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<VnPaginate
data-key="TravelList"
url="Travels/filter"
@ -132,13 +132,6 @@ onMounted(async () => {
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>
<i18n>
en:
addEntry: Add entry

View File

@ -42,7 +42,7 @@ async function remove(row) {
<template>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<VnPaginate
data-key="WagonTypeList"
url="/WagonTypes"
@ -80,10 +80,3 @@ async function remove(row) {
</QPageSticky>
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>

View File

@ -48,7 +48,7 @@ async function remove(row) {
<template>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<VnPaginate
data-key="WagonList"
url="/Wagons"
@ -99,10 +99,3 @@ async function remove(row) {
</QPageSticky>
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>

View File

@ -27,7 +27,6 @@ const departments = ref();
</div>
</template>
<template #body="{ params, searchFn }">
<QList dense class="q-gutter-y-sm q-mt-sm">
<QItem>
<QItemSection>
<VnInput :label="t('FI')" v-model="params.fi" is-outlined
@ -94,7 +93,6 @@ const departments = ref();
/>
</QItemSection>
</QItem>
</QList>
</template>
</VnFilterPanel>
</template>

View File

@ -64,7 +64,7 @@ const redirectToCreateView = () => {
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<div class="vn-card-list">
<VnPaginate
data-key="WorkerList"
url="Workers/filter"
@ -114,13 +114,6 @@ const redirectToCreateView = () => {
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>
<i18n>
es:
Search worker: Buscar trabajador

View File

@ -1,3 +1,4 @@
import Item from './item';
import Customer from './customer';
import Ticket from './ticket';
import Claim from './claim';
@ -14,6 +15,7 @@ import Department from './department';
import Entry from './entry';
export default [
Item,
Customer,
Ticket,
Claim,

View File

@ -0,0 +1,70 @@
import { RouterView } from 'vue-router';
export default {
path: '/item',
name: 'Item',
meta: {
title: 'items',
icon: 'vn:item',
},
component: RouterView,
redirect: { name: 'ItemMain' },
menus: {
main: [],
card: [],
},
children: [
{
path: '',
name: 'ItemMain',
component: () => import('src/pages/Item/ItemMain.vue'),
redirect: { name: 'Itemlist' },
children: [
{
path: 'list',
name: 'ItemList',
meta: {
title: 'list',
icon: 'view_list',
},
component: () => import('src/pages/Item/ItemList.vue'),
},
],
},
{
name: 'ItemCard',
path: ':id',
component: () => import('src/pages/Item/Card/ItemCard.vue'),
redirect: { name: 'ItemSummary' },
children: [
{
name: 'ItemSummary',
path: 'summary',
meta: {
title: 'summary',
icon: 'launch',
},
component: () => import('src/pages/Item/Card/ItemSummary.vue'),
},
{
path: 'diary',
name: 'ItemDiary',
meta: {
title: 'diary',
icon: 'vn:transaction',
},
component: () => import('src/pages/Item/Card/ItemDiary.vue'),
},
{
path: 'tags',
name: 'ItemTags',
meta: {
title: 'Tags',
icon: 'vn:tags',
},
component: () => import('src/pages/Item/Card/ItemTags.vue'),
},
],
},
],
};

View File

@ -1,3 +1,4 @@
import item from './modules/item';
import customer from './modules/customer';
import ticket from './modules/ticket';
import claim from './modules/claim';
@ -51,6 +52,7 @@ const routes = [
component: () => import('../pages/Dashboard/DashboardMain.vue'),
},
// Module routes
item,
customer,
ticket,
claim,

View File

@ -7,19 +7,19 @@ import routes from 'src/router/modules';
export const useNavigationStore = defineStore('navigationStore', () => {
const modules = [
'customer',
'claim',
'ticket',
'invoiceOut',
'invoiceIn',
'worker',
'shelving',
'order',
'wagon',
'route',
'supplier',
'travel',
'customer',
'entry',
'travel',
'invoiceOut',
'invoiceIn',
'supplier',
'claim',
'route',
'ticket',
'worker',
'wagon',
];
const pinnedModules = ref([]);
const role = useRole();