PR-CUSTOMER #186

Merged
jsegarra merged 105 commits from :PR-CUSTOMER into dev 2024-04-19 15:55:53 +00:00
77 changed files with 8312 additions and 1003 deletions

View File

@ -81,6 +81,7 @@ const emit = defineEmits(['onFetch', 'onDataSaved']);
const componentIsRendered = ref(false);
onMounted(async () => {
originalData.value = $props.formInitialData;
nextTick(() => {
componentIsRendered.value = true;
});
@ -126,7 +127,7 @@ const isLoading = ref(false);
// Si elegimos observar los cambios del form significa que inicialmente las actions estaran deshabilitadas
const isResetting = ref(false);
const hasChanges = ref(!$props.observeFormChanges);
const originalData = ref({ ...$props.formInitialData });
const originalData = ref({});
const formData = computed(() => state.get($props.model));
const formUrl = computed(() => $props.url);
const defaultButtons = computed(() => ({
@ -154,14 +155,18 @@ const startFormWatcher = () => {
};
async function fetch() {
const { data } = await axios.get($props.url, {
params: { filter: JSON.stringify($props.filter) },
});
try {
const { data } = await axios.get($props.url, {
params: { filter: JSON.stringify($props.filter) },
});
state.set($props.model, data);
originalData.value = data && JSON.parse(JSON.stringify(data));
state.set($props.model, data);
originalData.value = data && JSON.parse(JSON.stringify(data));
emit('onFetch', state.get($props.model));
emit('onFetch', state.get($props.model));
} catch (error) {
state.set($props.model, {});
originalData.value = {};
}
}
async function save() {

View File

@ -1,6 +1,6 @@
<script setup>
import axios from 'axios';
import { onMounted, ref } from 'vue';
import { onMounted, ref, reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { QSeparator, useQuasar } from 'quasar';
import { useRoute } from 'vue-router';
@ -22,6 +22,8 @@ const props = defineProps({
},
});
const expansionItemElements = reactive({});
onMounted(async () => {
await navigation.fetchPinned();
getRoutes();
@ -108,6 +110,10 @@ async function togglePinned(item, event) {
type: 'positive',
});
}
const handleItemExpansion = (itemName) => {
expansionItemElements[itemName].scrollToLastElement();
};
</script>
<template>
@ -213,8 +219,12 @@ async function togglePinned(item, event) {
:icon="item.icon"
:label="t(item.title)"
:content-inset-level="0.5"
@after-show="handleItemExpansion(item.name)"
>
<LeftMenuItemGroup :item="item" />
<LeftMenuItemGroup
:ref="(el) => (expansionItemElements[item.name] = el)"
:item="item"
/>
</QExpansionItem>
</QList>
</template>

View File

@ -1,6 +1,7 @@
<script setup>
import { computed } from 'vue';
import { computed, ref, defineExpose } from 'vue';
import LeftMenuItem from './LeftMenuItem.vue';
import { elementIsVisibleInViewport } from 'src/composables/elementIsVisibleInViewport';
const props = defineProps({
item: {
@ -13,10 +14,27 @@ const props = defineProps({
},
});
const groupEnd = ref(null);
const scrollToLastElement = () => {
if (groupEnd.value && !elementIsVisibleInViewport(groupEnd.value)) {
groupEnd.value.scrollIntoView({
behavior: 'smooth',
block: 'end',
});
}
};
const item = computed(() => props.item); // eslint-disable-line vue/no-dupe-keys
defineExpose({
scrollToLastElement,
});
</script>
<template>
<template v-for="section in item.children" :key="section.name">
<LeftMenuItem :item="section" />
</template>
<div ref="groupEnd" />
</template>

View File

@ -21,7 +21,8 @@ const props = defineProps({
},
template: {
type: String,
required: true,
required: false,
jsegarra marked this conversation as resolved
Review

Y si ponemos comillas simples como default reduciríamos la complejidad de usar ternario,no?

Y si ponemos comillas simples como default reduciríamos la complejidad de usar ternario,no?
Review

Sale un warning "Prop 'template' requires default value to be set." Lo digo por poner las comillas y nos evitamos un ternario

Sale un warning "Prop 'template' requires default value to be set." Lo digo por poner las comillas y nos evitamos un ternario
Review

Apliqué un string vacío '' como default de la prop template, pero sinceramente no se me ocurre como mejorar el ternary operator ese.

Pensé algo como: const key = props.template ? templates['${props.template}'] : props.template;.

Pero no se si tiene mucho sentido la verdad.

Commit: bafb938e23

Apliqué un string vacío `''` como default de la prop `template`, pero sinceramente no se me ocurre como mejorar el ternary operator ese. Pensé algo como: `const key = props.template ? `templates['${props.template}']` : props.template;`. Pero no se si tiene mucho sentido la verdad. Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/bafb938e231cbcd55236edf06b6c254f547d7ecb
default: '',
},
locale: {
type: String,
@ -49,7 +50,7 @@ updateMessage();
function updateMessage() {
const params = props.data;
const key = `templates['${props.template}']`;
const key = props.template ? `templates['${props.template}']` : '';
message.value = t(key, params, { locale: locale.value });
}
jgallego marked this conversation as resolved
Review

El estilo del modal no es el mismo

El campo subject no se está enviando en el body del POST, por lo que lo quitaria. En Salix, ese campo no existe

En mensaje falta añadir el icono de info

El estilo del modal no es el mismo El campo subject no se está enviando en el body del POST, por lo que lo quitaria. En Salix, ese campo no existe En mensaje falta añadir el icono de info
Review

Se adapto al estilo de los inputs de salix y se agregó el tooltip.

@jgallego no quise tocar es el input de Subject que @jsegarra menciona porque lo agregó un chico de verdnatura y supongo que habrá sido por algo, prefiero de momento no tocarlo al menos que sea estrictamente necesario.

Commit: 94644de4fa

Se adapto al estilo de los inputs de salix y se agregó el tooltip. @jgallego no quise tocar es el input de `Subject` que @jsegarra menciona porque lo agregó un chico de verdnatura y supongo que habrá sido por algo, prefiero de momento no tocarlo al menos que sea estrictamente necesario. Commit: https://gitea.verdnatura.es/hyervoni/salix-front-mindshore/commit/94644de4fa9c2e15c2fe1435553458672ae3531e
Review

ok, lo dejamos así

ok, lo dejamos así
@ -104,15 +105,14 @@ async function send() {
map-options
:input-debounce="0"
rounded
outlined
dense
/>
</QCardSection>
<QCardSection class="q-pb-xs">
<VnInput :label="t('Phone')" v-model="phone" is-outlined />
<VnInput :label="t('Phone')" v-model="phone" />
</QCardSection>
<QCardSection class="q-pb-xs">
<VnInput v-model="subject" :label="t('Subject')" is-outlined />
<VnInput v-model="subject" :label="t('Subject')" />
</QCardSection>
<QCardSection class="q-mb-md" q-input>
<QInput
@ -125,7 +125,6 @@ async function send() {
:bottom-slots="true"
:rules="[(value) => value.length < maxLength || 'Error!']"
stack-label
outlined
autofocus
>
<template #append>
@ -135,6 +134,11 @@ async function send() {
@click="message = ''"
class="cursor-pointer"
/>
<QIcon name="info" class="cursor-pointer">
<QTooltip>
{{ t('messageTooltip') }}
</QTooltip>
</QIcon>
</template>
<template #counter>
<QChip :color="color" dense>
@ -184,12 +188,14 @@ en:
es: Spanish
fr: French
pt: Portuguese
messageTooltip: Special characters like accents counts as multiple
es:
Send SMS: Enviar SMS
Language: Idioma
Phone: Móvil
Subject: Asunto
Message: Mensaje
messageTooltip: Carácteres especiales como acentos cuentan como varios
templates:
pendingPayment: 'Su pedido está pendiente de pago.
Por favor, entre en la página web y efectue el pago con tarjeta. Muchas gracias.'
@ -207,6 +213,7 @@ fr:
Phone: Mobile
Subject: Affaire
Message: Message
messageTooltip: Les caractères spéciaux comme les accents comptent comme plusieurs
templates:
pendingPayment: 'Votre commande est en attente de paiement.
Veuillez vous connecter sur le site web et effectuer le paiement par carte. Merci beaucoup.'
@ -224,6 +231,7 @@ pt:
Phone: Móvel
Subject: Assunto
Message: Mensagem
messageTooltip: Caracteres especiais como acentos contam como vários
templates:
pendingPayment: 'Seu pedido está pendente de pagamento.
Por favor, acesse o site e faça o pagamento com cartão. Muito obrigado.'

View File

@ -1,6 +1,6 @@
<script setup>
import VnAvatar from 'src/components/ui/VnAvatar.vue';
import { toDateHour } from 'src/filters';
import { toDateHourMinSec } from 'src/filters';
import { ref } from 'vue';
import axios from 'axios';
import { useI18n } from 'vue-i18n';

View File

@ -0,0 +1,6 @@
export const elementIsVisibleInViewport = (el) => {
const { top, left, bottom, right } = el.getBoundingClientRect();
const { innerHeight, innerWidth } = window;
return top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;
};

View File

@ -75,6 +75,10 @@ select:-webkit-autofill {
background-color: #666666;
}
.color-vn-label {
color: var(--vn-label);
}
.color-vn-text {
color: var(--vn-text-color);
}
@ -94,6 +98,10 @@ select:-webkit-autofill {
border-radius: 8px;
}
.card-width {
width: 770px;
}
.vn-card-list {
width: 100%;
max-width: 60em;

View File

@ -80,7 +80,7 @@
<glyph unicode="&#xe94b;" glyph-name="consignatarios" d="M409.6-64v349.867h204.8v-349.867h256v563.2h153.6l-512 460.8-512-460.8h153.6v-563.2h256z" />
<glyph unicode="&#xe94c;" glyph-name="control" d="M418.133 315.733l-128-123.733 256-256 469.333 469.333-128 128-341.333-341.333zM546.133 311.467l34.133 34.133h-68.267zM230.4 128l-59.733 64 153.6 153.6h-68.267v102.4h426.667l204.8 204.8 85.333-85.333v187.733c0 55.467-46.933 102.4-102.4 102.4h-213.333c-21.333 59.733-76.8 102.4-145.067 102.4s-123.733-42.667-145.067-102.4h-213.333c-55.467 0-102.4-46.933-102.4-102.4v-716.8c0-55.467 46.933-102.4 102.4-102.4h273.067l-196.267 192zM512 857.6c29.867 0 51.2-21.333 51.2-51.2s-21.333-51.2-51.2-51.2-51.2 21.333-51.2 51.2c0 29.867 21.333 51.2 51.2 51.2zM256 652.8h512v-102.4h-512v102.4zM665.6-64h204.8c55.467 0 102.4 46.933 102.4 102.4v204.8l-307.2-307.2z" />
<glyph unicode="&#xe94d;" glyph-name="credit" d="M921.6 849.067h-819.2c-55.467 0-102.4-42.667-102.4-98.133v-601.6c0-55.467 46.933-102.4 102.4-102.4h819.2c55.467 0 102.4 42.667 102.4 102.4v601.6c0 55.467-46.933 98.133-102.4 98.133zM921.6 145.067h-819.2v302.933h819.2v-302.933zM921.6 648.533h-819.2v102.4h819.2v-102.4z" />
<glyph unicode="&#xe94e;" glyph-name="deaulter" d="M677.973-64c-30.72 35.84-61.867 70.827-91.307 107.52-40.96 51.2-80.64 103.253-121.173 154.88-16.64 21.333-21.76 20.48-30.72-4.693-13.227-36.693-25.6-73.387-40.107-109.653-5.12-12.8-13.227-26.88-24.32-34.56-51.627-34.987-104.107-69.12-157.867-100.693-10.667-6.4-30.72-5.547-41.813 0.853-8.107 4.693-12.373 23.893-11.093 35.84 0.853 8.96 11.093 19.627 19.627 25.6 39.253 26.453 78.933 51.627 119.040 76.8 18.347 11.52 30.293 26.027 35.84 47.787 12.373 48.213 27.307 95.573 39.253 143.36 8.533 33.707 26.88 58.88 56.32 77.227 40.533 25.173 80.64 52.053 120.747 78.507 6.4 4.267 10.24 11.52 15.36 17.493-7.253 2.56-14.933 7.253-22.187 6.827-75.52-6.4-151.467-13.227-226.987-20.48-2.133 0-4.693-0.853-6.827-0.853-22.613-1.707-39.253 10.24-40.96 29.867s12.373 33.707 35.413 35.84c45.227 4.267 90.88 8.96 136.107 12.8 65.707 5.547 131.84 10.667 197.547 15.36 26.027 1.707 53.76-21.76 67.413-55.467 9.813-23.893 5.12-46.080-18.347-65.28-49.92-40.107-100.693-78.933-151.040-118.187-23.040-17.92-23.893-23.467-6.4-46.507 58.453-78.080 116.48-156.587 174.933-234.667 27.307-36.693 25.173-50.773-12.373-75.52-5.12 0-9.813 0-14.080 0zM791.893 649.813c-43.093 1.28-76.373-31.573-77.227-75.52-0.853-44.373 29.44-76.8 72.107-77.653 45.227-1.28 77.653 29.44 78.080 73.813 0.427 45.227-29.44 78.080-72.96 79.36zM671.147 737.707c0-72.107-34.133-136.107-87.467-176.64l-235.52-21.76c-72.107 36.693-122.027 111.787-122.027 198.4 0 122.88 99.84 222.293 222.72 222.293 122.453 0 222.293-99.413 222.293-222.293zM592.213 680.533l-50.347 18.347c-2.133-8.533-5.12-16.213-9.813-22.613-5.12-6.4-10.24-11.947-16.213-17.067-5.973-4.267-12.373-8.107-19.2-11.093s-13.653-4.693-20.053-5.547c-17.92-2.987-33.707-0.427-48.64 6.827s-26.88 18.347-36.693 32.853l76.373 12.373 7.253 32.427-97.28-15.787c-1.28 5.547-2.987 11.093-3.84 16.64l-0.853 4.267 99.413 16.213 7.253 32.427-106.667-17.493c0.853 9.387 2.987 17.493 6.4 26.027 3.84 8.533 8.107 16.213 14.080 23.040 5.547 6.827 12.8 12.373 21.333 17.067s17.92 8.107 28.587 9.813c6.827 1.28 13.227 1.707 20.907 1.28s14.507-1.707 21.333-3.84c6.827-2.133 13.653-5.973 20.053-10.24 5.973-4.693 11.947-11.093 17.493-18.773l38.827 37.973c-13.227 17.92-30.293 31.147-52.053 39.253-21.76 8.533-46.080 10.667-73.387 6.4-19.627-2.987-36.267-9.387-51.2-17.92-14.507-8.533-26.88-19.2-37.547-32-10.667-12.373-18.773-26.027-23.893-40.96-5.547-14.507-8.96-29.867-9.813-45.653l-21.76-3.84-7.253-32.427 29.013 4.693 0.427-2.987c1.28-6.827 2.56-12.8 4.267-18.347l-23.467-3.84-8.107-32.427 43.52 7.253c6.827-13.653 15.787-26.027 26.027-36.693 10.24-11.52 22.187-20.48 35.413-27.733 13.227-7.68 27.307-12.8 42.667-15.787s31.573-3.413 47.36-0.853c12.373 2.133 24.32 5.12 35.84 10.667s22.613 11.52 32.853 19.2c10.24 8.107 18.347 16.64 26.027 26.453 6.827 9.387 12.373 20.48 15.36 32.427z" />
<glyph unicode="&#xe94e;" glyph-name="defaulter" d="M677.973-64c-30.72 35.84-61.867 70.827-91.307 107.52-40.96 51.2-80.64 103.253-121.173 154.88-16.64 21.333-21.76 20.48-30.72-4.693-13.227-36.693-25.6-73.387-40.107-109.653-5.12-12.8-13.227-26.88-24.32-34.56-51.627-34.987-104.107-69.12-157.867-100.693-10.667-6.4-30.72-5.547-41.813 0.853-8.107 4.693-12.373 23.893-11.093 35.84 0.853 8.96 11.093 19.627 19.627 25.6 39.253 26.453 78.933 51.627 119.040 76.8 18.347 11.52 30.293 26.027 35.84 47.787 12.373 48.213 27.307 95.573 39.253 143.36 8.533 33.707 26.88 58.88 56.32 77.227 40.533 25.173 80.64 52.053 120.747 78.507 6.4 4.267 10.24 11.52 15.36 17.493-7.253 2.56-14.933 7.253-22.187 6.827-75.52-6.4-151.467-13.227-226.987-20.48-2.133 0-4.693-0.853-6.827-0.853-22.613-1.707-39.253 10.24-40.96 29.867s12.373 33.707 35.413 35.84c45.227 4.267 90.88 8.96 136.107 12.8 65.707 5.547 131.84 10.667 197.547 15.36 26.027 1.707 53.76-21.76 67.413-55.467 9.813-23.893 5.12-46.080-18.347-65.28-49.92-40.107-100.693-78.933-151.040-118.187-23.040-17.92-23.893-23.467-6.4-46.507 58.453-78.080 116.48-156.587 174.933-234.667 27.307-36.693 25.173-50.773-12.373-75.52-5.12 0-9.813 0-14.080 0zM791.893 649.813c-43.093 1.28-76.373-31.573-77.227-75.52-0.853-44.373 29.44-76.8 72.107-77.653 45.227-1.28 77.653 29.44 78.080 73.813 0.427 45.227-29.44 78.080-72.96 79.36zM671.147 737.707c0-72.107-34.133-136.107-87.467-176.64l-235.52-21.76c-72.107 36.693-122.027 111.787-122.027 198.4 0 122.88 99.84 222.293 222.72 222.293 122.453 0 222.293-99.413 222.293-222.293zM592.213 680.533l-50.347 18.347c-2.133-8.533-5.12-16.213-9.813-22.613-5.12-6.4-10.24-11.947-16.213-17.067-5.973-4.267-12.373-8.107-19.2-11.093s-13.653-4.693-20.053-5.547c-17.92-2.987-33.707-0.427-48.64 6.827s-26.88 18.347-36.693 32.853l76.373 12.373 7.253 32.427-97.28-15.787c-1.28 5.547-2.987 11.093-3.84 16.64l-0.853 4.267 99.413 16.213 7.253 32.427-106.667-17.493c0.853 9.387 2.987 17.493 6.4 26.027 3.84 8.533 8.107 16.213 14.080 23.040 5.547 6.827 12.8 12.373 21.333 17.067s17.92 8.107 28.587 9.813c6.827 1.28 13.227 1.707 20.907 1.28s14.507-1.707 21.333-3.84c6.827-2.133 13.653-5.973 20.053-10.24 5.973-4.693 11.947-11.093 17.493-18.773l38.827 37.973c-13.227 17.92-30.293 31.147-52.053 39.253-21.76 8.533-46.080 10.667-73.387 6.4-19.627-2.987-36.267-9.387-51.2-17.92-14.507-8.533-26.88-19.2-37.547-32-10.667-12.373-18.773-26.027-23.893-40.96-5.547-14.507-8.96-29.867-9.813-45.653l-21.76-3.84-7.253-32.427 29.013 4.693 0.427-2.987c1.28-6.827 2.56-12.8 4.267-18.347l-23.467-3.84-8.107-32.427 43.52 7.253c6.827-13.653 15.787-26.027 26.027-36.693 10.24-11.52 22.187-20.48 35.413-27.733 13.227-7.68 27.307-12.8 42.667-15.787s31.573-3.413 47.36-0.853c12.373 2.133 24.32 5.12 35.84 10.667s22.613 11.52 32.853 19.2c10.24 8.107 18.347 16.64 26.027 26.453 6.827 9.387 12.373 20.48 15.36 32.427z" />
<glyph unicode="&#xe94f;" glyph-name="deletedTicket" d="M160.672 85.696h693.248v639.776c0 0-2.016 234.528-349.696 234.528s-343.552-234.528-343.552-234.528v-639.776zM291.328 652.704h170.976v152.256h102.336v-152.256h171.008v-102.336h-171.008v-356.96h-102.336v356.96h-170.976v102.336zM64 61.056v-123.456h899.008v123.456h-899.008z" />
<glyph unicode="&#xe950;" glyph-name="deleteline" d="M354.133 192l-98.133 98.133 157.867 153.6-157.867 157.867 98.133 102.4 157.867-157.867 157.867 153.6 98.133-98.133-157.867-157.867 157.867-153.6-98.133-98.133-157.867 157.867-157.867-157.867zM780.8 507.733l-64-64 59.733-55.467h247.467v119.467h-243.2zM307.2 443.733l-64 64h-243.2v-119.467h251.733l55.467 55.467z" />
<glyph unicode="&#xe951;" glyph-name="delivery" d="M789.333 264.533c-55.467 0-102.4-46.933-102.4-102.4s46.933-102.4 102.4-102.4 102.4 46.933 102.4 102.4c0 59.733-46.933 102.4-102.4 102.4zM789.333 110.933c-29.867 0-51.2 21.333-51.2 51.2s21.333 51.2 51.2 51.2 51.2-21.333 51.2-51.2c0-25.6-25.6-51.2-51.2-51.2zM251.733 264.533c-55.467 0-102.4-46.933-102.4-102.4s46.933-102.4 102.4-102.4c55.467 0 102.4 46.933 102.4 102.4 0 59.733-46.933 102.4-102.4 102.4zM251.733 110.933c-29.867 0-51.2 21.333-51.2 51.2s21.333 51.2 51.2 51.2c29.867 0 51.2-21.333 51.2-51.2 0-25.6-25.6-51.2-51.2-51.2zM1006.933 537.6l-196.267 192c-12.8 12.8-29.867 17.067-46.933 17.067h-98.133v38.4c0 25.6-21.333 51.2-51.2 51.2h-563.2c-29.867 0-51.2-21.333-51.2-51.2v-554.667c0-29.867 25.6-51.2 51.2-51.2h68.267c8.533 64 64 115.2 132.267 115.2 64 0 123.733-51.2 132.267-115.2h268.8c8.533 64 64 115.2 132.267 115.2s128-51.2 136.533-115.2h51.2c29.867 0 51.2 25.6 51.2 51.2v260.267c0 17.067-8.533 34.133-17.067 46.933zM725.333 682.667c0 4.267 4.267 8.533 8.533 8.533h34.133c0 0 4.267 0 4.267-4.267l153.6-145.067c4.267 0 0-12.8-4.267-12.8h-187.733c-8.533 0-8.533 4.267-8.533 8.533v145.067zM311.467 597.333c0 46.933 29.867 85.333 59.733 93.867 4.267 0 4.267 0 8.533 0l98.133 12.8v-51.2c0-46.933-29.867-85.333-59.733-93.867-4.267 0-4.267 0-8.533 0l-98.133-17.067v55.467zM311.467 516.267l46.933 8.533c17.067 4.267 29.867-17.067 29.867-38.4l4.267-29.867-51.2-4.267c-17.067-4.267-29.867 12.8-29.867 38.4v25.6zM149.333 597.333v51.2l85.333 12.8c34.133 4.267 55.467-25.6 55.467-72.533v-51.2l-85.333-12.8c-34.133 0-59.733 29.867-55.467 72.533zM285.867 512v-38.4c0-34.133-21.333-64-42.667-68.267h-4.267l-72.533-8.533v38.4c0 34.133 21.333 64 42.667 68.267h4.267l72.533 8.533z" />
@ -132,4 +132,4 @@
<glyph unicode="&#xe989;" glyph-name="polizon" d="M1011.2 456.533l-264.533 106.667 29.867 29.867c4.267 4.267 4.267 12.8 4.267 17.067-4.267 4.267-8.533 8.533-12.8 8.533h-157.867c0 93.867 76.8 157.867 174.933 157.867 4.267 0 8.533 4.267 12.8 8.533s4.267 8.533 0 17.067l-81.067 153.6c-4.267 0-12.8 4.267-17.067 4.267-46.933 0-93.867-17.067-132.267-42.667-25.6-17.067-42.667-38.4-55.467-59.733-12.8 25.6-29.867 42.667-55.467 59.733-38.4 25.6-85.333 42.667-132.267 42.667-4.267 0-12.8-4.267-12.8-8.533l-81.067-153.6c-4.267-4.267-4.267-8.533 0-17.067 4.267-4.267 8.533-8.533 12.8-8.533 98.133 0 174.933-59.733 174.933-153.6v0h-140.8c-4.267 0-12.8-4.267-12.8-8.533-4.267-4.267 0-12.8 4.267-17.067l21.333-21.333-277.333-110.933c-8.533-8.533-12.8-12.8-8.533-21.333 0-8.533 8.533-12.8 17.067-12.8 0 0 0 0 0 0l98.133 4.267-76.8-98.133c0-4.267-4.267-8.533 0-12.8 0-4.267 4.267-8.533 8.533-8.533l85.333-34.133v-179.2c0-8.533 4.267-12.8 8.533-12.8l362.667-145.067c0 0 4.267 0 4.267 0s4.267 0 4.267 0l362.667 145.067c4.267 4.267 8.533 8.533 8.533 12.8v179.2l85.333 34.133c4.267 0 8.533 4.267 8.533 8.533s0 8.533-4.267 12.8l-72.533 98.133 102.4-4.267c8.533 0 12.8 4.267 17.067 12.8 0 8.533-4.267 12.8-12.8 17.067zM110.933 460.8l200.533 81.067 8.533-8.533-170.667-68.267-38.4-4.267zM153.6 294.4v4.267l-72.533 29.867 72.533 98.133 328.533-132.267-72.533-98.133-256 102.4v-4.267zM494.933-25.6l-328.533 132.267v153.6l243.2-98.133c0 0 4.267 0 4.267 0h4.267c0 0 4.267 0 4.267 0v0c0 0 0 0 4.267 0v0c0 0 4.267 0 4.267 4.267l64 85.333v-277.333zM494.933 328.533l-302.933 119.467 149.333 59.733 153.6-162.133v-17.067zM529.067 345.6l162.133 157.867 140.8-55.467-302.933-119.467v17.067zM857.6 106.667l-328.533-132.267v281.6l64-85.333c0 0 0-4.267 4.267-4.267v0c0 0 4.267 0 4.267 0v0c0 0 4.267 0 4.267 0v0 0c0 0 4.267 0 4.267 0l243.2 98.133v-157.867zM942.933 328.533l-328.533-132.267-72.533 98.133 328.533 132.267 72.533-98.133zM874.667 465.067l-162.133 64 12.8 8.533 187.733-76.8-38.4 4.267z" />
<glyph unicode="&#xe98a;" glyph-name="preserved" d="M512-64c16.213 0 28.587 13.227 28.587 28.587v215.040h2.987c73.387 2.133 130.987 25.173 170.667 67.84 68.267 72.107 62.72 177.493 60.16 197.973v0c-1.28 15.36-14.080 28.16-29.44 29.44-7.253 0.853-33.707 2.987-67.84-2.133l-12.373-2.133 11.093 6.827c14.080 8.96 26.453 19.2 37.547 30.72 68.267 72.107 62.72 177.493 60.16 197.973-1.28 15.36-14.080 28.16-29.44 29.44-14.080 1.28-64 4.267-117.76-14.507l-4.267-1.28 0.853 5.12c2.987 15.36 4.267 31.573 4.267 46.933-2.56 98.56-78.933 168.107-93.867 180.48-12.373 10.24-29.44 10.24-41.813 0-15.36-11.947-92.16-81.067-94.72-180.48-0.853-16.213 0.853-31.573 4.267-47.787l0.853-5.12-4.267 1.28c-53.76 19.2-104.533 16.213-118.613 14.507-15.36-1.28-28.16-14.080-29.44-29.44-2.133-20.48-8.107-125.867 60.16-197.973 11.093-11.093 22.613-21.333 36.693-30.293l10.24-6.827-12.373 2.133c-16.213 2.133-30.293 2.987-41.387 2.987s-19.2-0.853-23.467-0.853c-15.36-1.28-28.16-14.080-29.44-29.44-2.133-20.48-8.107-125.013 60.16-197.973 39.68-41.813 97.28-64.853 169.813-67.84h2.987v-215.040c0.427-14.933 12.8-28.16 29.013-28.16zM715.093 418.133h2.987v-2.987c-0.853-30.72-7.253-87.467-45.653-128-28.587-30.72-71.253-46.933-127.147-49.92h-2.987v2.987c2.133 58.88 20.053 103.68 51.627 133.12 38.827 37.973 92.587 44.8 121.173 44.8zM713.813 679.253h2.987v-2.987c-0.853-30.72-7.253-87.467-45.653-128-28.587-30.293-71.253-46.933-127.147-49.493h-2.987v2.987c2.133 58.88 20.053 103.68 51.627 133.12 39.253 37.12 93.013 43.52 121.173 44.373zM454.4 770.56c1.28 55.040 35.413 99.413 55.893 120.747l2.133 2.133 2.133-2.133c20.053-21.333 53.76-65.707 55.893-120.747 1.28-43.52-17.493-87.040-55.040-128.853l-2.133-2.133-2.133 2.133c-39.68 41.813-58.027 85.333-56.747 128.853zM351.573 548.267c-37.547 39.68-44.8 97.28-45.653 128v2.987h2.987c34.56-0.853 84.053-8.96 121.6-43.947v0c31.573-29.44 48.64-74.24 51.627-133.12v-2.987h-2.987c-56.32 2.133-98.987 18.773-127.573 49.067zM479.573 237.653c-55.893 2.133-98.56 19.2-127.147 49.493-37.547 39.68-44.8 97.28-45.653 128v2.987h2.987c28.587-0.853 82.347-7.253 121.6-43.947 31.573-29.44 48.64-74.24 51.627-133.12v-2.987h-3.413zM471.467 416.427c-8.96 8.107-18.347 15.36-29.44 22.613l-11.093 6.827 12.373-1.28c21.333-2.987 41.813-3.84 64-2.987 0.853 0 1.28 0 2.133 0h2.56c0.853 0 1.28 0 2.133 0 5.12 0 9.387 0 14.507 0 17.493 0 35.413 1.28 52.053 3.84l13.227 2.133-11.093-7.253c-11.093-6.827-20.48-14.507-29.44-22.613-14.507-14.080-28.16-31.573-38.827-51.627l-2.133-4.267-2.133 4.267c-10.667 18.773-23.893 36.267-38.827 50.347z" />
<glyph unicode="&#xe98b;" glyph-name="recovery" d="M746.667 477.867c68.267 0 140.8-21.333 196.267-72.533 110.933-102.4 115.2-277.333 8.533-384s-277.333-115.2-384-8.533c-93.867 85.333-110.933 221.867-51.2 328.533l51.2-46.933c-34.133-76.8-17.067-170.667 46.933-230.4 81.067-76.8 209.067-72.533 290.133 8.533 76.8 81.067 72.533 209.067-8.533 290.133-42.667 38.4-93.867 55.467-145.067 55.467l4.267-153.6-170.667 162.133 162.133 170.667v-119.467zM337.067 209.067c0 0 0 0 0 0-17.067 8.533-38.4 17.067-55.467 25.6-21.333 8.533-29.867 25.6-29.867 51.2s0 51.2 0 81.067c29.867-17.067 64-29.867 102.4-38.4 4.267 17.067 12.8 34.133 21.333 51.2-17.067 4.267-34.133 8.533-51.2 17.067-21.333 8.533-38.4 21.333-55.467 29.867-12.8 8.533-17.067 17.067-12.8 34.133 0 29.867 0 55.467 0 85.333 68.267-29.867 132.267-46.933 200.533-51.2 17.067 21.333 38.4 38.4 64 55.467 0 0-4.267 0-4.267 0-51.2 0-106.667 4.267-157.867 21.333-34.133 0-59.733 12.8-81.067 29.867-29.867 21.333-29.867 42.667 4.267 64 38.4 25.6 81.067 34.133 128 42.667 55.467 8.533 110.933 8.533 166.4 4.267 51.2-4.267 102.4-17.067 149.333-38.4 12.8-4.267 42.667-29.867 38.4-55.467 55.467 0 106.667-12.8 153.6-34.133 17.067 4.267 29.867 8.533 46.933 12.8 0-12.8 0-25.6 0-38.4 21.333-12.8 38.4-25.6 55.467-42.667 0 98.133 0 200.533 0 298.667 0 42.667-17.067 72.533-51.2 93.867-46.933 29.867-98.133 38.4-149.333 42.667-34.133 8.533-72.533 8.533-110.933 8.533-72.533-4.267-140.8-21.333-200.533-64-4.267 0-8.533 4.267-8.533 4.267-51.2 34.133-110.933 46.933-170.667 51.2-51.2 0-102.4 0-153.6-8.533-46.933-8.533-93.867-17.067-136.533-46.933-25.6-21.333-38.4-42.667-38.4-76.8 0-145.067 0-294.4 0-439.467 0-38.4 17.067-64 46.933-81.067 21.333-12.8 46.933-25.6 72.533-29.867 25.6-8.533 51.2-12.8 81.067-17.067 17.067-46.933 55.467-68.267 102.4-85.333 12.8-4.267 29.867-8.533 42.667-12.8-4.267 17.067-8.533 34.133-8.533 55.467zM964.267 686.933c0-8.533-8.533-21.333-17.067-25.6-34.133-21.333-76.8-34.133-119.467-38.4-4.267 38.4-17.067 68.267-55.467 89.6 68.267 0 128 12.8 192 42.667 4.267-25.6 4.267-46.933 0-68.267zM610.133 883.2c68.267 17.067 136.533 25.6 204.8 17.067 42.667-4.267 85.333-12.8 128-34.133 12.8-8.533 21.333-12.8 25.6-34.133-8.533-8.533-17.067-17.067-25.6-21.333-29.867-17.067-64-29.867-98.133-34.133-38.4-4.267-76.8-8.533-115.2-8.533-46.933 0-89.6 8.533-132.267 25.6-21.333 8.533-42.667 17.067-55.467 42.667 12.8 29.867 38.4 42.667 68.267 46.933zM68.267 844.8c25.6 25.6 64 34.133 98.133 38.4 29.867 4.267 59.733 12.8 85.333 12.8 68.267 0 132.267-4.267 196.267-34.133 8.533-4.267 17.067-8.533 25.6-17.067 12.8-8.533 12.8-21.333 0-34.133-8.533-8.533-17.067-12.8-29.867-21.333-42.667-21.333-85.333-29.867-132.267-34.133-17.067 0-29.867 0-42.667 0-55.467 4.267-110.933 12.8-162.133 29.867-12.8 4.267-29.867 12.8-42.667 21.333-12.8 17.067-8.533 25.6 4.267 38.4zM196.267 307.2c-25.6 8.533-51.2 12.8-72.533 21.333-8.533 4.267-21.333 8.533-29.867 12.8-25.6 12.8-38.4 29.867-34.133 59.733 0 17.067 0 29.867 0 51.2 46.933-25.6 89.6-34.133 136.533-38.4 0-42.667 0-72.533 0-106.667zM55.467 529.067c0 21.333 0 42.667 0 59.733 46.933-12.8 93.867-21.333 140.8-34.133 0-29.867 0-59.733 0-93.867-34.133 4.267-64 12.8-93.867 21.333-8.533 4.267-21.333 12.8-29.867 17.067-12.8 8.533-17.067 17.067-17.067 29.867zM196.267 618.667c-4.267 0-12.8-4.267-17.067 0-34.133 0-68.267 12.8-102.4 29.867-12.8 8.533-21.333 21.333-21.333 38.4s0 38.4 0 59.733c59.733-25.6 115.2-38.4 179.2-42.667-21.333-25.6-42.667-51.2-38.4-85.333z" />
</font></defs></svg>
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 173 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,402 +1,418 @@
@font-face {
font-family: 'icon';
src: url('fonts/icon.eot?2omjsr');
src: url('fonts/icon.eot?2omjsr#iefix') format('embedded-opentype'),
url('fonts/icon.ttf?2omjsr') format('truetype'),
url('fonts/icon.woff?2omjsr') format('woff'),
url('fonts/icon.svg?2omjsr#icon') format('svg');
font-weight: normal;
font-style: normal;
font-display: block;
font-family: 'icon';
src: url('fonts/icon.eot?2omjsr');
src: url('fonts/icon.eot?2omjsr#iefix') format('embedded-opentype'),
url('fonts/icon.ttf?2omjsr') format('truetype'),
url('fonts/icon.woff?2omjsr') format('woff'),
url('fonts/icon.svg?2omjsr#icon') format('svg');
font-weight: normal;
font-style: normal;
font-display: block;
}
[class^="icon-"], [class*=" icon-"] {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'icon' !important;
speak: never;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
[class^='icon-'],
[class*=' icon-'] {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'icon' !important;
speak: never;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-100:before {
content: "\e926";
content: '\e926';
}
.icon-Client_unpaid:before {
content: "\e925";
content: '\e925';
}
.icon-Client_unpaid:before {
content: '\e925';
}
.icon-History:before {
content: "\e964";
content: '\e964';
}
.icon-Person:before {
content: "\e984";
content: '\e984';
}
.icon-accessory:before {
content: "\e948";
content: '\e948';
}
.icon-account:before {
content: "\e927";
content: '\e927';
}
.icon-actions:before {
content: "\e928";
content: '\e928';
}
.icon-addperson:before {
content: "\e929";
content: '\e929';
}
.icon-agency:before {
content: "\e92a";
content: '\e92a';
}
.icon-agency:before {
content: '\e92a';
}
.icon-agency-term:before {
content: "\e92b";
content: '\e92b';
}
.icon-albaran:before {
content: "\e92c";
content: '\e92c';
}
.icon-albaran:before {
content: '\e92c';
}
.icon-anonymous:before {
content: "\e92d";
content: '\e92d';
}
.icon-apps:before {
content: "\e92e";
content: '\e92e';
}
.icon-artificial:before {
content: "\e92f";
content: '\e92f';
}
.icon-attach:before {
content: "\e930";
content: '\e930';
}
.icon-barcode:before {
content: "\e932";
content: '\e932';
}
.icon-basket:before {
content: "\e933";
content: '\e933';
}
.icon-basketadd:before {
content: "\e934";
content: '\e934';
}
.icon-bin:before {
content: "\e935";
content: '\e935';
}
.icon-botanical:before {
content: "\e936";
content: '\e936';
}
.icon-bucket:before {
content: "\e937";
content: '\e937';
}
.icon-buscaman:before {
content: "\e938";
content: '\e938';
}
.icon-buyrequest:before {
content: "\e939";
content: '\e939';
}
.icon-calc_volum:before {
content: "\e93a";
content: '\e93a';
}
.icon-calendar:before {
content: "\e940";
content: '\e940';
}
.icon-catalog:before {
content: "\e941";
content: '\e941';
}
.icon-claims:before {
content: "\e942";
content: '\e942';
}
.icon-client:before {
content: "\e943";
content: '\e943';
}
.icon-clone:before {
content: "\e945";
content: '\e945';
}
.icon-columnadd:before {
content: "\e946";
content: '\e946';
}
.icon-columndelete:before {
content: "\e947";
content: '\e947';
}
.icon-components:before {
content: "\e949";
content: '\e949';
}
.icon-consignatarios:before {
content: "\e94b";
content: '\e94b';
}
.icon-control:before {
content: "\e94c";
content: '\e94c';
}
.icon-credit:before {
content: "\e94d";
content: '\e94d';
}
.icon-deaulter:before {
content: "\e94e";
.icon-defaulter:before {
content: '\e94e';
}
.icon-deletedTicket:before {
content: "\e94f";
content: '\e94f';
}
.icon-deleteline:before {
content: "\e950";
content: '\e950';
}
.icon-delivery:before {
content: "\e951";
content: '\e951';
}
.icon-deliveryprices:before {
content: "\e952";
content: '\e952';
}
.icon-details:before {
content: "\e954";
content: '\e954';
}
.icon-dfiscales:before {
content: "\e955";
content: '\e955';
}
.icon-disabled:before {
content: "\e965";
content: '\e965';
}
.icon-doc:before {
content: "\e956";
content: '\e956';
}
.icon-entry:before {
content: "\e958";
content: '\e958';
}
.icon-exit:before {
content: "\e959";
content: '\e959';
}
.icon-eye:before {
content: "\e95a";
content: '\e95a';
}
.icon-fixedPrice:before {
content: "\e95b";
content: '\e95b';
}
.icon-flower:before {
content: "\e95c";
content: '\e95c';
}
.icon-frozen:before {
content: "\e95d";
content: '\e95d';
}
.icon-fruit:before {
content: "\e95e";
content: '\e95e';
}
.icon-funeral:before {
content: "\e95f";
content: '\e95f';
}
.icon-grafana:before {
content: "\e931";
content: '\e931';
}
.icon-grafana:before {
content: '\e931';
}
.icon-greenery:before {
content: "\e91e";
content: '\e91e';
}
.icon-greuge:before {
content: "\e960";
content: '\e960';
}
.icon-grid:before {
content: "\e961";
content: '\e961';
}
.icon-handmade:before {
content: "\e94a";
content: '\e94a';
}
.icon-handmadeArtificial:before {
content: "\e962";
content: '\e962';
}
.icon-headercol:before {
content: "\e963";
content: '\e963';
}
.icon-info:before {
content: "\e966";
content: '\e966';
}
.icon-inventory:before {
content: "\e967";
content: '\e967';
}
.icon-invoice:before {
content: "\e969";
content: '\e969';
}
.icon-invoice-in:before {
content: "\e96a";
content: '\e96a';
}
.icon-invoice-in-create:before {
content: "\e96b";
content: '\e96b';
}
.icon-invoice-out:before {
content: "\e96c";
content: '\e96c';
}
.icon-isTooLittle:before {
content: "\e96e";
content: '\e96e';
}
.icon-item:before {
content: "\e96f";
content: '\e96f';
}
.icon-languaje:before {
content: "\e912";
content: '\e912';
}
.icon-lines:before {
content: "\e971";
content: '\e971';
}
.icon-linesprepaired:before {
content: "\e972";
content: '\e972';
}
.icon-link-to-corrected:before {
content: "\e900";
content: '\e900';
}
.icon-link-to-correcting:before {
content: "\e906";
content: '\e906';
}
.icon-logout:before {
content: "\e90a";
content: '\e90a';
}
.icon-mana:before {
content: "\e974";
content: '\e974';
}
.icon-mandatory:before {
content: "\e975";
content: '\e975';
}
.icon-net:before {
content: "\e976";
content: '\e976';
}
.icon-newalbaran:before {
content: "\e977";
content: '\e977';
}
.icon-niche:before {
content: "\e979";
content: '\e979';
}
.icon-no036:before {
content: "\e97a";
content: '\e97a';
}
.icon-noPayMethod:before {
content: "\e97b";
content: '\e97b';
}
.icon-notes:before {
content: "\e97c";
content: '\e97c';
}
.icon-noweb:before {
content: "\e97e";
content: '\e97e';
}
.icon-onlinepayment:before {
content: "\e97f";
content: '\e97f';
}
.icon-package:before {
content: "\e980";
content: '\e980';
}
.icon-payment:before {
content: "\e982";
content: '\e982';
}
.icon-pbx:before {
content: "\e983";
content: '\e983';
}
.icon-pets:before {
content: "\e985";
content: '\e985';
}
.icon-photo:before {
content: "\e986";
content: '\e986';
}
.icon-plant:before {
content: "\e987";
content: '\e987';
}
.icon-polizon:before {
content: "\e989";
content: '\e989';
}
.icon-preserved:before {
content: "\e98a";
content: '\e98a';
}
.icon-recovery:before {
content: "\e98b";
content: '\e98b';
}
.icon-regentry:before {
content: "\e901";
content: '\e901';
}
.icon-reserva:before {
content: "\e902";
content: '\e902';
}
.icon-revision:before {
content: "\e903";
content: '\e903';
}
.icon-risk:before {
content: "\e904";
content: '\e904';
}
.icon-services:before {
content: "\e905";
content: '\e905';
}
.icon-settings:before {
content: "\e907";
content: '\e907';
}
.icon-shipment:before {
content: "\e908";
content: '\e908';
}
.icon-sign:before {
content: "\e909";
content: '\e909';
}
.icon-sms:before {
content: "\e90b";
content: '\e90b';
}
.icon-solclaim:before {
content: "\e90c";
content: '\e90c';
}
.icon-solunion:before {
content: "\e90d";
content: '\e90d';
}
.icon-splitline:before {
content: "\e90e";
content: '\e90e';
}
.icon-splur:before {
content: "\e90f";
content: '\e90f';
}
.icon-stowaway:before {
content: "\e910";
content: '\e910';
}
.icon-supplier:before {
content: "\e911";
content: '\e911';
}
.icon-supplierfalse:before {
content: "\e913";
content: '\e913';
}
.icon-tags:before {
content: "\e914";
content: '\e914';
}
.icon-tax:before {
content: "\e915";
content: '\e915';
}
.icon-thermometer:before {
content: "\e916";
content: '\e916';
}
.icon-ticket:before {
content: "\e917";
content: '\e917';
}
.icon-ticketAdd:before {
content: "\e918";
content: '\e918';
}
.icon-traceability:before {
content: "\e919";
content: '\e919';
}
.icon-transaction:before {
content: "\e93b";
content: '\e93b';
}
.icon-transaction:before {
content: '\e93b';
}
.icon-treatments:before {
content: "\e91c";
content: '\e91c';
}
.icon-trolley:before {
content: "\e91a";
content: '\e91a';
}
.icon-troncales:before {
content: "\e91b";
content: '\e91b';
}
.icon-unavailable:before {
content: "\e91d";
content: '\e91d';
}
.icon-volume:before {
content: "\e91f";
content: '\e91f';
}
.icon-wand:before {
content: "\e920";
content: '\e920';
}
.icon-web:before {
content: "\e921";
content: '\e921';
}
.icon-wiki:before {
content: "\e922";
content: '\e922';
}
.icon-worker:before {
content: "\e923";
content: '\e923';
}
.icon-zone:before {
content: "\e924";
content: '\e924';
}

View File

@ -26,11 +26,11 @@ export function isValidDate(date) {
* // returns "02/12/2022"
* toDateFormat(new Date(2022, 11, 2));
*/
export function toDateFormat(date) {
export function toDateFormat(date, locale = 'es-ES') {
if (!isValidDate(date)) {
return '';
}
return new Date(date).toLocaleDateString('es-ES', {
return new Date(date).toLocaleDateString(locale, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
@ -56,7 +56,7 @@ export function toTimeFormat(date, showSeconds = false) {
if (!isValidDate(date)) {
return '';
}
return new Date(date).toLocaleDateString('es-ES', {
return new Date(date).toLocaleTimeString('es-ES', {
hour: '2-digit',
minute: '2-digit',
second: showSeconds ? '2-digit' : undefined,

View File

@ -1,7 +1,8 @@
import toLowerCase from './toLowerCase';
import toDate from './toDate';
import toDateString from './toDateString';
import toDateHour from './toDateHour';
import toDateHourMin from './toDateHourMin';
import toDateHourMinSec from './toDateHourMinSec';
import toRelativeDate from './toRelativeDate';
import toCurrency from './toCurrency';
import toPercentage from './toPercentage';
@ -16,7 +17,8 @@ export {
toDate,
toHour,
toDateString,
toDateHour,
toDateHourMin,
toDateHourMinSec,
toRelativeDate,
toCurrency,
toPercentage,

View File

@ -0,0 +1,11 @@
export default function toDateHourMin(date) {
jsegarra marked this conversation as resolved
Review

Duda, cuando actualicemos con dev seguirá siendo útil?

Duda, cuando actualicemos con dev seguirá siendo útil?
Review

En el archivo date.js están las nuevas utils que hacen esto así que supongo que ya no sería útil

En el archivo `date.js` están las nuevas utils que hacen esto así que supongo que ya no sería útil
Review

CustomerBalance CustomerCreditOpinion y CustomerCredit usan toDateHourMin

CustomerBalance CustomerCreditOpinion y CustomerCredit usan toDateHourMin
const dateHour = new Date(date).toLocaleDateString('es-ES', {
timeZone: 'Europe/Madrid',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
});
return dateHour;
}

View File

@ -1,4 +1,4 @@
export default function toDateHour(date) {
export default function toDateHourMinSec(date) {
Review

Duda, cuando actualicemos con dev seguirá siendo útil?

Duda, cuando actualicemos con dev seguirá siendo útil?
Review

En el archivo date.js están las nuevas utils que hacen esto así que supongo que ya no sería útil

En el archivo `date.js` están las nuevas utils que hacen esto así que supongo que ya no sería útil
Review

Todavia no, porque hay otras ocurrencias que lo usan, CustomerWebPayment y CustomerCreditContractsInsurance

Lo anoto para eliminar en nuestro lado VnNotes y OrderSummary

Todavia no, porque hay otras ocurrencias que lo usan, CustomerWebPayment y CustomerCreditContractsInsurance Lo anoto para eliminar en nuestro lado VnNotes y OrderSummary
const dateHour = new Date(date).toLocaleDateString('es-ES', {
timeZone: 'Europe/Madrid',
year: 'numeric',

View File

@ -67,6 +67,7 @@ globals:
reason: reason
noResults: No results
system: System
notificationSent: Notification sent
warehouse: Warehouse
company: Company
fieldRequired: Field required
@ -151,6 +152,13 @@ customer:
creditContracts: Credit contracts
creditOpinion: Credit opinion
others: Others
samples: Samples
consumption: Consumption
mandates: Mandates
contacts: Contacts
webPayment: Web payment
fileManagement: File management
unpaid: Unpaid
list:
phone: Phone
email: Email
@ -161,14 +169,18 @@ customer:
customerId: Claim ID
salesPerson: Sales person
credit: Credit
risk: Risk
securedCredit: Secured credit
payMethod: Pay method
debt: Debt
isDisabled: Customer is disabled
isFrozen: Customer is frozen
isFrozen: Customer frozen
hasDebt: Customer has debt
notChecked: Customer not checked
isDisabled: Customer inactive
notChecked: Customer no checked
webAccountInactive: Web account inactive
noWebAccess: Web access is disabled
businessType: Business type
passwordRequirements: 'The password must have at least { length } length characters, {nAlpha} alphabetic characters, {nUpper} capital letters, {nDigits} digits and {nPunct} symbols (Ex: $%&.)\n'
businessTypeFk: Business type
summary:
basicData: Basic data
@ -228,17 +240,20 @@ customer:
recoverySince: Recovery since
businessType: Business Type
city: City
descriptorInfo: Invoices minus payments plus orders not yet
rating: Rating
recommendCredit: Recommended credit
basicData:
socialName: Fiscal name
businessType: Business type
contact: Contact
youCanSaveMultipleEmails: You can save multiple emails
email: Email
phone: Phone
mobile: Mobile
salesPerson: Sales person
contactChannel: Contact channel
previousClient: Previous client
extendedList:
tableVisibleColumns:
id: Identifier
@ -468,6 +483,12 @@ ticket:
weight: Weight
goTo: Go to
summaryAmount: Summary
create:
client: Client
address: Address
landed: Landed
warehouse: Warehouse
agency: Agency
claim:
pageTitles:
claims: Claims

View File

@ -67,6 +67,7 @@ globals:
reason: motivo
noResults: Sin resultados
system: Sistema
notificationSent: Notificación enviada
warehouse: Almacén
company: Empresa
fieldRequired: Campo requerido
@ -150,6 +151,13 @@ customer:
creditContracts: Contratos de crédito
creditOpinion: Opinión de crédito
others: Otros
samples: Plantillas
consumption: Consumo
mandates: Mandatos
contacts: Contactos
webPayment: Pago web
fileManagement: Gestión documental
unpaid: Impago
list:
phone: Teléfono
email: Email
@ -159,14 +167,18 @@ customer:
customerId: ID cliente
salesPerson: Comercial
credit: Crédito
risk: Riesgo
securedCredit: Crédito asegurado
payMethod: Método de pago
debt: Riesgo
isDisabled: El cliente está desactivado
isFrozen: El cliente está congelado
hasDebt: El cliente tiene riesgo
notChecked: El cliente no está comprobado
isFrozen: Cliente congelado
hasDebt: Cliente con riesgo
isDisabled: Cliente inactivo
notChecked: Cliente no comprobado
webAccountInactive: Sin acceso web
noWebAccess: El acceso web está desactivado
businessType: Tipo de negocio
passwordRequirements: 'La contraseña debe tener al menos { length } caracteres de longitud, {nAlpha} caracteres alfabéticos, {nUpper} letras mayúsculas, {nDigits} dígitos y {nPunct} símbolos (Ej: $%&.)'
businessTypeFk: Tipo de negocio
summary:
basicData: Datos básicos
@ -226,17 +238,20 @@ customer:
recoverySince: Recobro desde
businessType: Tipo de negocio
city: Población
descriptorInfo: Facturas menos recibos mas pedidos sin facturar
rating: Clasificación
recommendCredit: Crédito recomendado
basicData:
socialName: Nombre fiscal
businessType: Tipo de negocio
contact: Contacto
youCanSaveMultipleEmails: Puede guardar varios correos electrónicos encadenándolos mediante comas sin espacios{','} ejemplo{':'} user{'@'}dominio{'.'}com, user2{'@'}dominio{'.'}com siendo el primer correo electrónico el principal
email: Email
phone: Teléfono
mobile: Móvil
salesPerson: Comercial
contactChannel: Canal de contacto
previousClient: Cliente anterior
extendedList:
tableVisibleColumns:
id: Identificador
@ -466,6 +481,12 @@ ticket:
weight: Peso
goTo: Ir a
summaryAmount: Resumen
create:
client: Cliente
address: Dirección
landed: F. entrega
warehouse: Almacén
agency: Agencia
claim:
pageTitles:
claims: Reclamaciones

View File

@ -1,18 +1,21 @@
<script setup>
import { ref } from 'vue';
import { onBeforeMount, ref, watch } from 'vue';
jsegarra marked this conversation as resolved
Review

Se que no aplica ni es este fichero, pero podemos eliminar
const route de CustomerCard??
Sale warning de consola

Se que no aplica ni es este fichero, pero podemos eliminar const route de CustomerCard?? Sale warning de consola
Review

Route eliminado

Commit: 3e124581c4

Route eliminado Commit: https://gitea.verdnatura.es/hyervoni/salix-front-mindshore/commit/3e124581c442ca84e1d1e8f003ff6af3daae9239
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import axios from 'axios';
import FetchData from 'components/FetchData.vue';
import VnPaginate from 'src/components/ui/VnPaginate.vue';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const addresses = ref([]);
const client = ref(null);
const provincesLocation = ref([]);
const consigneeFilter = {
const addressFilter = {
fields: [
'id',
'isDefaultAddress',
@ -44,6 +47,42 @@ const consigneeFilter = {
],
};
onBeforeMount(() => {
const { id } = route.params;
getAddressesData(id);
getClientData(id);
});
watch(
() => route.params.id,
(newValue) => {
if (!newValue) return;
getAddressesData(newValue);
getClientData(newValue);
}
);
const getAddressesData = async (id) => {
try {
const { data } = await axios.get(`Clients/${id}/addresses`, {
params: { filter: JSON.stringify(addressFilter) },
});
addresses.value = data;
sortAddresses();
} catch (error) {
return error;
}
};
const getClientData = async (id) => {
try {
const { data } = await axios.get(`Clients/${id}`);
client.value = data;
} catch (error) {
return error;
}
};
const setProvince = (provinceFk) => {
const result = provincesLocation.value.filter(
(province) => province.id === provinceFk
@ -51,16 +90,38 @@ const setProvince = (provinceFk) => {
return result[0]?.name || '';
};
const toCustomerConsigneeCreate = () => {
router.push({ name: 'CustomerConsigneeCreate' });
const isDefaultAddress = (address) => {
return client?.value?.defaultAddressFk === address.id ? 1 : 0;
};
const toCustomerConsigneeEdit = (consigneeId) => {
const setDefault = (address) => {
const url = `Clients/${route.params.id}`;
const payload = { defaultAddressFk: address.id };
axios.patch(url, payload).then((res) => {
if (res.data) {
client.value.defaultAddressFk = res.data.defaultAddressFk;
sortAddresses();
}
});
};

Los consignatarios se ordenan poniendo el predeterminado (star filled) el primero y el resto despues, mirar orden en salix.
La estrella tiene un evento click que sirve para marcar como predeterminado otro de los consignatarios, mirar funcionamiento en salix.

Los consignatarios se ordenan poniendo el predeterminado (star filled) el primero y el resto despues, mirar orden en salix. La estrella tiene un evento click que sirve para marcar como predeterminado otro de los consignatarios, mirar funcionamiento en salix.

Corregido: 980904a382

Corregido: 980904a382

@cfonseca El funcionamiento que reportaba Javi, está OK.

Sin embargo, falta revisar el layout de address/:id/edit:

  1. Hay una label que no coincide
  2. El grid no está igual,

Ver imagen de chat

@cfonseca El funcionamiento que reportaba Javi, está OK. Sin embargo, falta revisar el layout de address/:id/edit: 1. Hay una label que no coincide 2. El grid no está igual, Ver imagen de chat

Corregido: 7eca27b8af

Corregido: 7eca27b8af

Mmm...falta revisar VnLocation porque no está cargando el valor.

Si quieres nos juntamos en post-daily y lo vemos.

Mmm...falta revisar VnLocation porque no está cargando el valor. Si quieres nos juntamos en post-daily y lo vemos.

He hecho la prueba y se actualiza el campo correctamente. También carga el valor correctamente

He hecho la prueba y se actualiza el campo correctamente. También carga el valor correctamente
const sortAddresses = () => {
if (!client.value || !addresses.value) return;
addresses.value = addresses.value.sort((a, b) => {
return isDefaultAddress(b) - isDefaultAddress(a);
});
};
const toCustomerAddressCreate = () => {
router.push({ name: 'CustomerAddressCreate' });
};
const toCustomerAddressEdit = (addressId) => {
router.push({
name: 'CustomerConsigneeEdit',
name: 'CustomerAddressEdit',
params: {
id: route.params.id,
consigneeId,
addressId,
},
});
};
@ -73,26 +134,41 @@ const toCustomerConsigneeEdit = (consigneeId) => {
url="Provinces/location"
/>
<QCard class="q-pa-lg">
<VnPaginate
data-key="CustomerConsignees"
:url="`Clients/${route.params.id}/addresses`"
order="id"
auto-load
:filter="consigneeFilter"
>
<template #body="{ rows }">
<QCard
v-for="(item, index) in rows"
<div class="full-width flex justify-center">
<QCard class="card-width q-pa-lg" v-if="addresses.length">
<QCardSection>
<div
v-for="(item, index) in addresses"
:key="index"
class="address-card"
:class="{
'consignees-card': true,
'q-mb-md': index < rows.length - 1,
'q-mb-md': index < addresses.length - 1,
'item-disabled': !item.isActive,
}"
@click="toCustomerConsigneeEdit(item.id)"
@click="toCustomerAddressEdit(item.id)"
>
<div class="q-ml-xs q-mr-md flex items-center">
<QIcon name="star" size="md" color="primary" />
<QIcon
:style="{
'font-variation-settings': `'FILL' ${isDefaultAddress(
item
)}`,
}"
color="primary"
name="star"
size="md"
@click.stop="!isDefaultAddress(item) && setDefault(item)"
>
<QTooltip>
{{
t(
isDefaultAddress(item)
? 'Default address'
: 'Set as default'
)
}}
</QTooltip>
</QIcon>
</div>
<div>
<div class="text-weight-bold q-mb-sm">
@ -136,12 +212,13 @@ const toCustomerConsigneeEdit = (consigneeId) => {
<div>{{ observation.description }}</div>
</div>
</div>
</QCard>
</template>
</VnPaginate>
</QCard>
</div>
</QCardSection>
</QCard>
</div>
<QPageSticky :offset="[18, 18]">
<QBtn @click.stop="toCustomerConsigneeCreate()" color="primary" fab icon="add" />
<QBtn @click.stop="toCustomerAddressCreate()" color="primary" fab icon="add" />
<QTooltip>
{{ t('New consignee') }}
</QTooltip>
@ -149,8 +226,8 @@ const toCustomerConsigneeEdit = (consigneeId) => {
</template>
<style lang="scss" scoped>
.consignees-card {
border: 2px solid var(--vn-accent-color);
.address-card {
border: 2px solid var(--vn-light-gray);
border-radius: 10px;
padding: 10px;
display: flex;
@ -160,6 +237,10 @@ const toCustomerConsigneeEdit = (consigneeId) => {
background-color: var(--vn-accent-color);
}
}
.item-disabled {
opacity: 0.6;
}
</style>
<i18n>
@ -167,4 +248,6 @@ es:
Is equalizated: Recargo de equivalencia
Is logiflora allowed: Compra directa en Holanda
New consignee: Nuevo consignatario
Default address: Consignatario predeterminado
Set as default: Establecer como predeterminado
</i18n>

View File

@ -1,145 +1,102 @@
<script setup>
Review

Errores:

  1. Columna fecha
  2. Worker descriptor
  3. Referencia descriptor
  4. Referencia puede ser editable
Errores: 1. Columna fecha 2. Worker descriptor 3. Referencia descriptor 4. Referencia puede ser editable
Review

Corregido: 3cf3687b84

Corregido: 3cf3687b84
Review

Revisamos porque la columna referencia no tiene el mismo formato. El campo, debería ser descriptor, si procede

Revisamos porque la columna referencia no tiene el mismo formato. El campo, debería ser descriptor, si procede
Review

Corregido: 17a0afda73

Corregido: 17a0afda73
Review

Revisar porque lago raro pasa

Revisar porque lago raro pasa
Review

@wbuezas y yo hemos visto que las filas no siempre aparecen ordenadas con los customer 1102 y 1103.
He eliminado el "order:id DESC" y parece que va mejor, pero alguna vez aparece en otro orden

@wbuezas y yo hemos visto que las filas no siempre aparecen ordenadas con los customer 1102 y 1103. He eliminado el "order:id DESC" y parece que va mejor, pero alguna vez aparece en otro orden
Review

Pasaban 2 cosas:

  • Estaba mal el orden en la request como vimos
  • Había también un error en el calculo del balance

Ambas cosas quedaron fixeadas en este commit: 1cff622898

Pasaban 2 cosas: - Estaba mal el orden en la request como vimos - Había también un error en el calculo del balance Ambas cosas quedaron fixeadas en este commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/1cff622898264123e0135ec6dfff44ce0d0b6fb8
Review

Aquí también hay un error en los datos que vienen del back porque el campo payed y created, tiene la misma fecha y hora para los registros con id 1

Aquí también hay un error en los datos que vienen del back porque el campo payed y created, tiene la misma fecha y hora para los registros con id 1
import { computed, ref } from 'vue';
import { computed, onBeforeMount, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { date, QCheckbox, QBtn, useQuasar } from 'quasar';
import axios from 'axios';
import { QCheckbox, QBtn, useQuasar } from 'quasar';
import { toCurrency } from 'src/filters';
import { toCurrency, toDate, toDateHourMin } from 'src/filters';
import { useState } from 'src/composables/useState';
import { useStateStore } from 'stores/useStateStore';
import { useValidator } from 'src/composables/useValidator';
import { usePrintService } from 'src/composables/usePrintService';
import VnPaginate from 'src/components/ui/VnPaginate.vue';
import FetchData from 'components/FetchData.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import CustomerNewPayment from 'src/pages/Customer/components/CustomerNewPayment.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue';
const { sendEmail } = usePrintService();
const { t } = useI18n();
const route = useRoute();
const { validate } = useValidator();
const quasar = useQuasar();
const route = useRoute();
const state = useState();
const stateStore = useStateStore();
const user = state.getUser();
const clientRisks = ref(null);
const companiesOptions = ref([]);
const companyId = ref(442);
const rows = ref(null);
const workerId = ref(0);
const receiptsRef = ref(null);
const clientRisksRef = ref(null);
const companiesOptions = ref([]);
const companyId = ref(null);
const receiptsRef = ref(null);
const receiptsData = ref([]);
const filterCompanies = { order: ['code'] };
const params = {
clientId: `${route.params.id}`,
companyId: companyId.value,
filter: { limit: 20 },
const userParams = {
clientId: route.params.id,
companyId: user.value.companyFk,
};
const filter = {
include: { relation: 'company', scope: { fields: ['code'] } },
where: { clientFk: `${route.params.id}`, companyFk: companyId.value },
};
const tableColumnComponents = {
payed: {
component: 'span',
props: () => {},
event: () => {},
},
created: {
component: 'span',
props: () => {},
event: () => {},
},
userName: {
component: QBtn,
props: () => ({ flat: true, color: 'blue' }),
event: (prop) => {
workerId.value = prop.row.clientFk;
},
},
description: {
component: 'span',
props: () => {},
event: () => {},
},
bankFk: {
component: 'span',
props: () => {},
event: () => {},
},
debit: {
component: 'span',
props: () => {},
event: () => {},
},
credit: {
component: 'span',
props: () => {},
event: () => {},
},
balance: {
component: 'span',
props: () => {},
event: () => {},
},
isConciliate: {
component: QCheckbox,
props: (prop) => ({
disable: true,
'model-value': Boolean(prop.value),
}),
event: () => {},
},
where: { clientFk: route.params.id, companyFk: user.value.companyFk },
};
const columns = computed(() => [
{
align: 'left',
field: 'payed',
format: (value) => date.formatDate(value, 'DD/MM/YYYY'),
format: (value) => toDate(value),
label: t('Date'),
name: 'payed',
name: 'date',
},
{
align: 'left',
field: 'created',
format: (value) => date.formatDate(value, 'DD/MM/YYYY hh:mm'),
format: (value) => toDateHourMin(value),
label: t('Creation date'),
name: 'created',
name: 'creationDate',
},
{
align: 'left',
field: 'userName',
label: t('Employee'),
name: 'userName',
name: 'employee',
},
{
align: 'left',
field: 'description',
label: t('Reference'),
name: 'description',
name: 'reference',
},
{
align: 'left',
field: 'bankFk',
label: t('Bank'),
name: 'bankFk',
name: 'bank',
},
{
align: 'left',
align: 'right',
field: 'debit',
format: (value) => value && toCurrency(value),
label: t('Debit'),
name: 'debit',
},
{
align: 'left',
align: 'right',
field: 'credit',
format: (value) => toCurrency(value),
format: (value) => value && toCurrency(value),
label: t('Havings'),
name: 'credit',
name: 'havings',
},
{
align: 'left',
field: (value) => value.debit - value.credit,
format: (value) => toCurrency(value),
align: 'right',
field: 'balance',
format: (value) => value && toCurrency(value),
label: t('Balance'),
jsegarra marked this conversation as resolved Outdated

todos los campos de euros deben de ir alineados a la derecha como en salix.

todos los campos de euros deben de ir alineados a la derecha como en salix.

Así se ve para el cliente 1101. Ver imagen adjunta
Sin embargo, el cursor cambia a "la mano", induciendo a que se puede cambiar, cuando debería aparecer el prohibido o cambiar el color del checkbox a gris

Así se ve para el cliente 1101. Ver imagen adjunta Sin embargo, el cursor cambia a "la mano", induciendo a que se puede cambiar, cuando debería aparecer el prohibido o cambiar el color del checkbox a gris

Corregido, campos de tipo moneda alineados a la derecha y el checkbox deshabilitado: 0aa5a051fb

Corregido, campos de tipo moneda alineados a la derecha y el checkbox deshabilitado: 0aa5a051fb

Yo lo he visto OK
El checkbox es de color naranja pero se ve que está deshabilitado y tiene el cursor en prohibido

Yo lo he visto OK El checkbox es de color naranja pero se ve que está deshabilitado y tiene el cursor en prohibido
name: 'balance',
},
@ -147,16 +104,58 @@ const columns = computed(() => [
align: 'left',
field: 'isConciliate',
label: t('Conciliated'),
name: 'isConciliate',
name: 'conciliated',
},
{
align: 'left',
field: 'totalWithVat',
label: '',
name: 'actions',
},
]);
Review

El usuario en minusculas igual que salix (cambiar para todos los sitios que se muestre)

El usuario en minusculas igual que salix (cambiar para todos los sitios que se muestre)
Review

Corregido: 663010a0d4

Corregido: 663010a0d4
Review

Yo lo he visto OK

Yo lo he visto OK
const getData = () => {
onBeforeMount(() => {
stateStore.rightDrawer = true;
companyId.value = user.value.companyFk;
});
watch(
() => route.params.id,
(newValue) => {
if (!newValue) return;
userParams.clientId = newValue;
filter.where.clientFk = newValue;
getData();
}
);
const getData = () => {

el cambo debit son euros le falta
format: (value) => toCurrency(value),

el cambo debit son euros le falta format: (value) => toCurrency(value),

Corregido: f8b698e4e7

Corregido: f8b698e4e7

Yo lo he visto OK en customer/1101/balance

Yo lo he visto OK en customer/1101/balance
receiptsRef.value?.fetch();
clientRisksRef.value?.fetch();
};
const getCurrentBalance = () => {
const currentBalance = clientRisks.value.find((balance) => {
return balance.companyFk === companyId.value;

en las columnas debe y haber si el valor es cero no mostrarlo
tal como lo hace en salix.

en las columnas debe y haber si el valor es cero no mostrarlo tal como lo hace en salix.

Corregido: d74119f433

Corregido: d74119f433

Yo lo he visto OK

Yo lo he visto OK
});
return currentBalance && currentBalance.amount;
};
const onFetch = (balances) => {
balances.forEach((balance, index) => {
if (index === 0) {
balance.balance = getCurrentBalance();
} else {
let previousBalance = balances[index - 1];
balance.balance =
previousBalance.balance -

Revisar en salix el valor del campo balance
http://localhost:5000/#!/client/1101/balance/index
respecto a lilium
http://localhost:9000/#/customer/1101/balance

el balance es un saldo por tanto, va acumulando el balance anterior +- el valor del debe/haber

Revisar en salix el valor del campo balance http://localhost:5000/#!/client/1101/balance/index respecto a lilium http://localhost:9000/#/customer/1101/balance el balance es un saldo por tanto, va acumulando el balance anterior +- el valor del debe/haber

Corregido: d74119f433

Corregido: d74119f433

Not fixed! Ver imagen

Not fixed! Ver imagen

Corregido el valor del campo balance: 38d3d49ecb

Corregido el valor del campo balance: 38d3d49ecb

He probado con los clientes 1101, 1102 y 1103 y lo he visto OK

He probado con los clientes 1101, 1102 y 1103 y lo he visto OK
(previousBalance.debit - previousBalance.credit);
}
});
receiptsData.value = balances;
};
const showNewPaymentDialog = () => {
quasar.dialog({
component: CustomerNewPayment,

no se porque el conciliado al lado del checkbox tiene un 1, http://localhost:9000/#/customer/1101/balance

no se porque el conciliado al lado del checkbox tiene un 1, http://localhost:9000/#/customer/1101/balance

Corregido: d74119f433

Corregido: d74119f433

Yo lo he visto OK

Yo lo he visto OK
@ -169,25 +168,29 @@ const showNewPaymentDialog = () => {
};
const updateCompanyId = (id) => {
if (id) companyId.value = id;
if (id) {
companyId.value = id;
userParams.companyId = id;
filter.where.companyFk = id;
}
getData();
};
const saveFieldValue = async (row) => {
try {
const payload = { description: row.description };
await axios.patch(`Receipts/${row.id}`, payload);
} catch (err) {
return err;
}
};
const sendEmailAction = () => {
sendEmail(`Suppliers/${route.params.id}/campaign-metrics-email`);
};
</script>
<template>
<FetchData
:filter="filterCompanies"
@on-fetch="(data) => (companiesOptions = data)"
auto-load
url="Companies"
/>
<FetchData
:params="params"
@on-fetch="(data) => (rows = data)"
auto-load
ref="receiptsRef"
url="Receipts/filter"
/>
<FetchData
:filter="filter"
@on-fetch="(data) => (clientRisks = data)"
@ -195,36 +198,121 @@ const updateCompanyId = (id) => {
ref="clientRisksRef"
url="ClientRisks"
/>
<FetchData
:filter="filterCompanies"
@on-fetch="(data) => (companiesOptions = data)"
auto-load
url="Companies"
/>
<QTable
:columns="columns"
:rows="rows"
class="full-width q-mt-md"
row-key="id"
v-if="rows?.length"
<VnPaginate
auto-load
data-key="CustomerBalance"
url="Receipts/filter"
:user-params="userParams"
ref="receiptsRef"
@on-fetch="onFetch"
>
<template #body-cell="props">
<QTd :props="props">
<QTr :props="props" class="cursor-pointer">
<component
:is="tableColumnComponents[props.col.name].component"
class="col-content"
v-bind="tableColumnComponents[props.col.name].props(props)"
@click="tableColumnComponents[props.col.name].event(props)"
>
<template v-if="props.col.name !== 'isConciliate'">
{{ props.value }}
</template>
<WorkerDescriptorProxy :id="workerId" />
</component>
</QTr>
</QTd>
</template>
</QTable>
<template #body="{ rows }">
<QTable
:columns="columns"
:no-data-label="t('globals.noResults')"
:rows-per-page-options="[0]"
:rows="rows"
class="full-width q-mt-md"
row-key="id"
>
<template #body-cell-employee="{ row }">
<QTd auto-width @click.stop>
<QBtn color="blue" flat no-caps>{{ row.userName }}</QBtn>
<WorkerDescriptorProxy :id="row.clientFk" />
</QTd>
</template>
<h5 class="flex justify-center label-color" v-else>
{{ t('globals.noResults') }}
</h5>
<template #body-cell-reference="{ row }">
<QTd auto-width @click.stop v-if="row.isInvoice">
<QBtn color="blue" dense flat>
{{ t('bill', { ref: row.description }) }}
</QBtn>
<InvoiceOutDescriptorProxy :id="row.id" />
</QTd>
<QTd v-else>
<VnInput
@keyup.enter="saveFieldValue(row)"
autofocus
clearable
dense
v-model="row.description"
/>
</QTd>
</template>
<template #body-cell-conciliated="{ row }">
<QTd align="center">
<QCheckbox :model-value="row.isConciliate === 1" disable />
</QTd>
</template>
<template #body-cell-actions="{ row }">
<QTd align="center">
<QIcon
@click.stop="showDialog = true"
class="q-ml-md"
color="primary"
name="outgoing_mail"
size="sm"
style="font-variation-settings: 'FILL' 1"
v-if="row.isCompensation"
>
jsegarra marked this conversation as resolved Outdated

Revisar, si este es el componente que acordamos en la UX/UI.
Hablar con @buezas porque ha hecho un cambio en la parte de travel/extra-community y el tipo de componente quasar que se está usando aquí no es el mismo

Revisar, si este es el componente que acordamos en la UX/UI. Hablar con @buezas porque ha hecho un cambio en la parte de travel/extra-community y el tipo de componente quasar que se está usando aquí no es el mismo

Corregido: 86199d8197

Corregido: 86199d8197
<QTooltip>
{{ t('Send compensation') }}
</QTooltip>
</QIcon>
<QDialog v-model="showDialog">
<QCard class="q-pa-sm">
jsegarra marked this conversation as resolved Outdated

http://localhost:5000/#!/client/100/balance/index en salix hay un registro y el boton de enviar compensación, en lilium no aparece ni el registro ni el boton

http://localhost:5000/#!/client/100/balance/index en salix hay un registro y el boton de enviar compensación, en lilium no aparece ni el registro ni el boton

Corregido: f8b698e4e7 hace falta cambiar el icono del mail, no lo he encontrado en Lilium aun

Corregido: f8b698e4e7 hace falta cambiar el icono del mail, no lo he encontrado en Lilium aun

Efectivamente no está.

Hablamos con el responsable de la iconografía

Gracias

Efectivamente no está. Hablamos con el responsable de la iconografía Gracias

Resuelto

Resuelto
<QCardSection>
<span
ref="closeButton"
class="flex justify-end color-vn-label"
v-close-popup
>
<QIcon name="close" size="sm" />
</span>
<div class="text-h6">
{{ t('Send compensation') }}
</div>
</QCardSection>
<QCardSection>
<div>
{{
t(
'Do you want to report compensation to the client by mail?'
)
}}
</div>
</QCardSection>
<QCardActions class="flex justify-end q-mb-sm">
<QBtn
:label="t('globals.cancel')"
color="primary"
flat
v-close-popup
/>
<QBtn
:label="t('globals.save')"
@click="sendEmailAction"
class="q-ml-sm"
color="primary"
/>
</QCardActions>
</QCard>
</QDialog>
</QTd>
</template>
</QTable>
</template>
</VnPaginate>
<QDrawer :width="256" show-if-above side="right" v-model="stateStore.rightDrawer">
<div class="q-mt-xl q-px-md">
@ -236,10 +324,11 @@ const updateCompanyId = (id) => {
option-label="code"
option-value="id"
v-model="companyId"
:rules="validate('entry.companyFk')"
/>
</div>
<QCard class="q-ma-md q-pa-md q-mt-lg" v-if="rows?.length">
<QCard class="q-ma-md q-pa-md q-mt-lg" v-if="receiptsData?.length">
<QCardSection>
<div class="flex justify-center text-subtitle1 text-bold">
{{ t('Total by company') }}
@ -265,6 +354,8 @@ const updateCompanyId = (id) => {
</template>
<i18n>
en:
bill: 'N/INV {ref}'
es:
Company: Empresa
Total by company: Total por empresa
@ -273,9 +364,12 @@ es:
Creation date: Fecha de creación
Employee: Empleado
Reference: Referencia
bill: 'N/FRA {ref}'
Bank: Caja
Debit: Debe
Havings: Haber
Balance: Balance
Conciliated: Conciliado
Send compensation: Enviar compensación
Do you want to report compensation to the client by mail?: ¿Desea informar de la compensación al cliente por correo?
</i18n>

View File

@ -60,90 +60,105 @@ const filterOptions = {
@on-fetch="(data) => (businessTypes = data)"
auto-load
/>
<fetch-data
:filter="filter"
@on-fetch="(data) => (clients = data)"
auto-load
url="Clients"
/>
<FormModel :url="`Clients/${route.params.id}`" model="customer" auto-load>
<FormModel :url="`Clients/${route.params.id}`" auto-load model="customer">
<template #form="{ data, validate, filter }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.socialName"
:label="t('customer.basicData.socialName')"
:label="t('Comercial name')"
:rules="validate('client.socialName')"
autofocus
clearable
v-model="data.name"
/>
</div>
<div class="col">
<QSelect
v-model="data.businessTypeFk"
:options="businessTypes"
option-value="code"
option-label="description"
emit-value
:label="t('customer.basicData.businessType')"
map-options
:rules="validate('client.businessTypeFk')"
:input-debounce="0"
:label="t('customer.basicData.businessType')"
:options="businessTypes"
:rules="validate('client.businessTypeFk')"
emit-value
map-options
option-label="description"
option-value="code"
v-model="data.businessTypeFk"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.contact"
:label="t('customer.basicData.contact')"
:rules="validate('client.contact')"
clearable
v-model="data.contact"
/>
</div>
<div class="col">
<VnInput
v-model="data.email"
type="email"
:label="t('customer.basicData.email')"
:rules="validate('client.email')"
clearable
/>
type="email"
v-model="data.email"
>
<template #append>
<QIcon name="info" class="cursor-info">
<QTooltip>{{
t('customer.basicData.youCanSaveMultipleEmails')
}}</QTooltip>
</QIcon>
</template>
</VnInput>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="data.phone"
:label="t('customer.basicData.phone')"
:rules="validate('client.phone')"
clearable
v-model="data.phone"
/>
</div>
<div class="col">
<VnInput
v-model="data.mobile"
:label="t('customer.basicData.mobile')"
:rules="validate('client.mobile')"
clearable
v-model="data.mobile"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QSelect
v-model="data.salesPersonFk"
:options="workers"
option-value="id"
option-label="name"
emit-value
:label="t('customer.basicData.salesPerson')"
map-options
use-input
@filter="(value, update) => filter(value, update, filterOptions)"
:rules="validate('client.salesPersonFk')"
:input-debounce="0"
:label="t('customer.basicData.salesPerson')"
:options="workers"
:rules="validate('client.salesPersonFk')"
@filter="(value, update) => filter(value, update, filterOptions)"
emit-value
map-options
option-label="name"
option-value="id"
use-input
v-model="data.salesPersonFk"
>
<template #prepend>
<QAvatar color="orange">
<QImg
v-if="data.salesPersonFk"
:src="`/api/Images/user/160x160/${data.salesPersonFk}/download?access_token=${token}`"
spinner-color="white"
v-if="data.salesPersonFk"
/>
</QAvatar>
</template>
@ -151,18 +166,49 @@ const filterOptions = {
</div>
<div class="col">
<QSelect
v-model="data.contactChannelFk"
:options="contactChannels"
option-value="id"
option-label="name"
emit-value
:label="t('customer.basicData.contactChannel')"
map-options
:rules="validate('client.contactChannelFk')"
:input-debounce="0"
:label="t('customer.basicData.contactChannel')"
:options="contactChannels"
:rules="validate('client.contactChannelFk')"
emit-value
map-options
option-label="name"
option-value="id"
v-model="data.contactChannelFk"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QSelect
:input-debounce="0"
:label="t('customer.basicData.previousClient')"
:options="clients"
:rules="validate('client.transferorFk')"
emit-value
map-options
option-label="name"
option-value="id"
v-model="data.transferorFk"
>
<template #append>
<QIcon name="info" class="cursor-pointer">
<QTooltip>{{
t(
'In case of a company succession, specify the grantor company'
)
}}</QTooltip>
</QIcon>
</template>
</QSelect>
</div>
</VnRow>
</template>
</FormModel>
</template>
<i18n>
es:
In case of a company succession, specify the grantor company: En el caso de que haya habido una sucesión de empresa, indicar la empresa cedente
Comercial name: Nombre comercial
</i18n>

View File

@ -1,10 +1,8 @@
<script setup>
import { onMounted, ref } from 'vue';
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import axios from 'axios';
import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
@ -26,8 +24,9 @@ const filter = {
limit: 30,
};
const getBankEntities = () => {
const getBankEntities = (data, formData) => {
bankEntitiesRef.value.fetch();
formData.bankEntityFk = Number(data.id);
};
</script>
@ -60,17 +59,13 @@ const getBankEntities = () => {
/>
</div>
<div class="col">
<VnInput
:label="t('Due day')"
:rules="validate('client.socialName')"
v-model="data.dueDay"
/>
<VnInput :label="t('Due day')" clearable v-model="data.dueDay" />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('IBAN')" v-model="data.iban">
<VnInput :label="t('IBAN')" clearable v-model="data.iban">
<template #append>
<QIcon name="info" class="cursor-info">
<QTooltip>{{ t('components.iban_tooltip') }}</QTooltip>
@ -90,7 +85,9 @@ const getBankEntities = () => {
v-model="data.bankEntityFk"
>
<template #form>
<CreateBankEntityForm @on-data-saved="getBankEntities()" />
<CreateBankEntityForm
@on-data-saved="getBankEntities($event, data)"
/>
</template>
<template #option="scope">
<QItem v-bind="scope.itemProps">

View File

@ -1,7 +1,6 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore';
import { useRoute } from 'vue-router';
import CustomerDescriptor from './CustomerDescriptor.vue';
import LeftMenu from 'components/LeftMenu.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
@ -9,7 +8,6 @@ import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import useCardSize from 'src/composables/useCardSize';
const stateStore = useStateStore();
const route = useRoute();
const { t } = useI18n();
</script>
<template>

View File

@ -0,0 +1,16 @@
<script setup>
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>
<template>
<h5 class="flex justify-center color-vn-label">
{{ t('Enter a new search') }}
</h5>
</template>
<i18n>
es:
Enter a new search: Introduce una nueva búsqueda
</i18n>

View File

@ -0,0 +1,87 @@
<script setup>
import { ref, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import CrudModel from 'components/CrudModel.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
const route = useRoute();
const { t } = useI18n();
const customerContactsRef = ref(null);
onMounted(() => {
if (customerContactsRef.value) customerContactsRef.value.reload();
});
</script>
<template>
<div class="full-width flex justify-center">
<QPage class="card-width q-pa-lg">
<CrudModel
:data-required="{ clientFk: route.params.id }"
:default-remove="false"
:filter="{
fields: ['id', 'name', 'phone', 'clientFk'],
where: { clientFk: route.params.id },
}"
data-key="CustomerContacts"
model="CustomerContacts"
ref="customerContactsRef"
url="ClientContacts"
>
<template #body="{ rows }">
<QCard class="q-pl-lg q-py-md">
<VnRow
v-for="(row, index) in rows"
:key="index"
class="row q-gutter-md q-mb-md"
>
<div class="col">
<VnInput :label="t('Name')" v-model="row.name" />
</div>
<div class="col">
<VnInput :label="t('Phone')" v-model="row.phone" />
</div>
<div class="col-1 row justify-center items-center">
<QIcon
@click="customerContactsRef.remove([row])"
class="cursor-pointer"
color="primary"
name="delete"
size="sm"
>
<QTooltip>
{{ t('Remove contact') }}
</QTooltip>
</QIcon>
</div>
</VnRow>
<VnRow>
<QIcon
@click="customerContactsRef.insert()"
class="cursor-pointer"
jsegarra marked this conversation as resolved Outdated

No hay notificación de guardado?

No hay notificación de guardado?

Corregido: ab20ec1738

Corregido: ab20ec1738
color="primary"
name="add"
size="sm"
>
<QTooltip>
{{ t('Add contact') }}
</QTooltip>
</QIcon>
</VnRow>
</QCard>
</template>
</CrudModel>
</QPage>
</div>
</template>
jsegarra marked this conversation as resolved
Review

Falta clearable

Falta clearable
Review

Corregido: 7f1c4690e8

Corregido: 7f1c4690e8
<i18n>
es:
Name: Nombre
Phone: Teléfono
Remove contact: Quitar contacto
Add contact: Añadir contacto
</i18n>

View File

@ -1,3 +1,233 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import { useQuasar } from 'quasar';
import VnPaginate from 'src/components/ui/VnPaginate.vue';
import ModalCloseContract from 'src/pages/Customer/components/ModalCloseContract.vue';
import { toDate } from 'src/filters';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const quasar = useQuasar();
const vnPaginateRef = ref(null);
const showQPageSticky = ref(true);
const filter = {
order: 'finished ASC, started DESC',
include: [
{
relation: 'insurances',
scope: {
fields: ['id', 'credit', 'created', 'grade'],
order: 'created DESC',
limit: 2,
},
},
],
where: { client: `${route.params.id}` },
};
const fetch = (data) => {
data.forEach((element) => {
if (!element.finished) {
showQPageSticky.value = false;
return;
}
});
};
const toCustomerCreditContractsCreate = () => {
router.push({ name: 'CustomerCreditContractsCreate' });
};
const openDialog = (item) => {
quasar.dialog({
component: ModalCloseContract,
componentProps: {
id: item.id,
promise: updateData,
},
});
};
const openViewCredit = (credit) => {
router.push({
name: 'CustomerCreditContractsInsurance',
params: {
creditId: credit.id,
},
});
};
const updateData = () => {
vnPaginateRef.value?.fetch();
};
</script>
<template>
<div class="flex justify-center">Customer credit contracts</div>
<div class="full-width flex justify-center">
<QCard class="card-width q-pa-lg">
<VnPaginate
:filter="filter"
@on-fetch="fetch"
auto-load
data-key="CustomerCreditContracts"
order="id DESC"
ref="vnPaginateRef"

cuando en la tabla creditClassification no hay dateEnd no muestra resultados, en salix sí lo hace. Además en este escenario es donde aparece en salix un candado para cerrar el contrato,
ejectura este update, ir a salix y copiar el funcionamiento en lilium
UPDATE vn.creditClassification
SET dateEnd=NULL
WHERE id=5

cuando en la tabla creditClassification no hay dateEnd no muestra resultados, en salix sí lo hace. Además en este escenario es donde aparece en salix un candado para cerrar el contrato, ejectura este update, ir a salix y copiar el funcionamiento en lilium UPDATE vn.creditClassification SET dateEnd=NULL WHERE id=5

Después de la prueba que hicimos en el daily todo funcionó correctamente, quedo pendiente por si debo hacer alguna corrección

Después de la prueba que hicimos en el daily todo funcionó correctamente, quedo pendiente por si debo hacer alguna corrección
url="CreditClassifications"
>
<template #body="{ rows }">
<div v-if="rows.length">
<QCard
v-for="(item, index) in rows"
:key="index"
:class="{
'customer-card': true,
'q-mb-md': index < rows.length - 1,
'is-active': !item.finished,
}"
>
<QCardSection
class="full-width flex justify-between q-py-none"
>
<div class="width-state flex">
<div
class="flex items-center cursor-pointer q-mr-md"
v-if="!item.finished"
>
<QIcon
@click.stop="openDialog(item)"
color="primary"
name="lock"
size="md"
style="font-variation-settings: 'FILL' 1"
>
<QTooltip>{{ t('Close contract') }}</QTooltip>
</QIcon>
</div>
<div>
<div class="flex q-mb-xs">
<div class="q-mr-sm color-vn-label">
{{ t('Since') }}:
</div>
<div class="text-weight-bold">
{{ toDate(item.started) }}
</div>
</div>
<div class="flex">
<div class="q-mr-sm color-vn-label">
{{ t('To') }}:
</div>
<div class="text-weight-bold">
{{ toDate(item.finished) }}
</div>
</div>
</div>
</div>
<QSeparator vertical />
<div class="width-data flex">
<div
class="full-width flex justify-between items-center"
v-if="item?.insurances.length"
>
<div class="flex">
<div class="color-vn-label q-mr-xs">
{{ t('Credit') }}:
</div>
<div class="text-weight-bold">
{{ item.insurances[0].credit }}
jsegarra marked this conversation as resolved Outdated

Si no tiene valor aparece vacío. En Salix aparece un "-"

Si no tiene valor aparece vacío. En Salix aparece un "-"

Corregido: c0a9835e80

Corregido: c0a9835e80
</div>
</div>
<div class="flex">
<div class="color-vn-label q-mr-xs">
{{ t('Grade') }}:
</div>
<div class="text-weight-bold">
{{ item.insurances[0].grade || '-' }}
</div>
</div>
<div class="flex">
<div class="color-vn-label q-mr-xs">
{{ t('Date') }}:
</div>
<div class="text-weight-bold">
{{ toDate(item.insurances[0].created) }}
</div>
</div>
<div class="flex items-center cursor-pointer">
<QIcon
@click.stop="openViewCredit(item)"
color="primary"
name="preview"
size="md"
>
<QTooltip>{{
t('View credits')
}}</QTooltip>
</QIcon>
</div>
</div>
</div>
</QCardSection>
</QCard>
</div>
<h5 class="flex justify-center color-vn-label" v-else>
{{ t('globals.noResults') }}
</h5>
</template>
</VnPaginate>
</QCard>
</div>
<QPageSticky :offset="[18, 18]" v-if="showQPageSticky">
<QBtn
@click.stop="toCustomerCreditContractsCreate()"
color="primary"
fab
icon="add"
/>
<QTooltip>
{{ t('New contract') }}
</QTooltip>
</QPageSticky>
</template>
<style lang="scss" scoped>
.customer-card {
border: 2px solid var(--vn-light-gray);
border-radius: 10px;
padding: 10px;
display: flex;
justify-content: space-between;
}
.is-active {
background-color: var(--vn-light-gray);
}
.width-state {
width: 30%;
}
.width-data {
width: 65%;
}
</style>
<i18n>
es:
Close contract: Cerrar contrato
Since: Desde
To: Hasta
Credit: Crédito
Grade: Grade
Date: Fecha
View credits: Ver créditos
Created: Fecha creación
New contract: Nuevo contrato
</i18n>

View File

@ -1,3 +0,0 @@
<template>
jsegarra marked this conversation as resolved Outdated

No identifico el nuevo componente, sin embargo, faltan clearables

No identifico el nuevo componente, sin embargo, faltan clearables

Componente eliminado hace varios días

Componente eliminado hace varios días
<div class="flex justify-center">Credit management</div>
</template>

View File

@ -1,3 +1,142 @@
<script setup>
import { computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { QBtn } from 'quasar';
import { toCurrency, toDateHourMin } from 'src/filters';
import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
const { t } = useI18n();
const route = useRoute();
const clientInformasRef = ref(null);
const rows = ref([]);
const filter = {
include: [
{
relation: 'worker',
scope: {
fields: ['id'],
include: { relation: 'user', scope: { fields: ['nickname'] } },
},
},
],
where: { clientFk: route.params.id },
order: ['created DESC'],
limit: 20,
};
const columns = computed(() => [
{
align: 'left',
field: 'created',
format: (value) => toDateHourMin(value),
label: t('Since'),
name: 'since',
},
{
align: 'left',
field: (row) => row.worker.user.nickname,
label: t('Employee'),
name: 'employee',
},
{
align: 'right',
field: 'rating',
label: t('Rating'),
name: 'rating',
},
{
align: 'right',
field: 'recommendedCredit',
format: (value) => toCurrency(value),

no inserta, en salix añado una clasificacion ej: 234 sin credito recomendado, lo inserta y lo muestra bajo,
en lilium da un error y no lo muestra, aun teniendo datos en la tabla insertados via salix no son mostrados en lilium

no inserta, en salix añado una clasificacion ej: 234 sin credito recomendado, lo inserta y lo muestra bajo, en lilium da un error y no lo muestra, aun teniendo datos en la tabla insertados via salix no son mostrados en lilium

Hice la prueba y me muestra este error: message: "La razón social debe ir en mayúscula", fui a la pantalla de Datos fiscales y modifique el campo de Razón social y Dirección fiscal y ya con estos cambios se pudo guardar el valor de Clasificación en la pantalla de Opinión de crédito sin problemas, por favor revisar, quedo pendiente

Hice la prueba y me muestra este error: **message: "La razón social debe ir en mayúscula"**, fui a la pantalla de **Datos fiscales** y modifique el campo de **Razón social** y **Dirección fiscal** y ya con estos cambios se pudo guardar el valor de **Clasificación** en la pantalla de **Opinión de crédito** sin problemas, por favor revisar, quedo pendiente

@cfonseca Esto es lo que vimos en la daily antes de Luciano no? Puedes confirmar? Gracias

@cfonseca Esto es lo que vimos en la daily antes de Luciano no? Puedes confirmar? Gracias
label: t('Recommended credit'),
name: 'recommendedCredit',
},
]);
watch(
() => route.params.id,
(newValue) => {
if (!newValue) return;
filter.where.clientFk = newValue;
clientInformasRef.value?.fetch();
}
);
</script>
<template>
<div class="flex justify-center">Customer credit opinion</div>
<FetchData
:filter="filter"
@on-fetch="(data) => (rows = data)"
auto-load
ref="clientInformasRef"
url="ClientInformas"
/>
<FormModel
:form-initial-data="{}"
:observe-form-changes="false"
:url-create="`Clients/${route.params.id}/setRating`"
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Rating')"
clearable
type="number"
v-model.number="data.rating"
/>
</div>
<div class="col">
<VnInput
:label="t('Recommended credit')"
clearable
type="number"
v-model.number="data.recommendedCredit"
/>
</div>
</VnRow>
</template>
</FormModel>
<div class="full-width flex justify-center" v-if="rows.length">
<QTable
:columns="columns"
:pagination="{ rowsPerPage: 0 }"
:rows="rows"
hide-bottom
row-key="id"
v-model:selected="selected"
class="card-width q-px-lg"
>
<template #body-cell-employee="{ row }">
<QTd auto-width @click.stop>
<QBtn color="blue" flat no-caps>{{ row.worker.user.nickname }}</QBtn>
<WorkerDescriptorProxy :id="row.clientFk" />
</QTd>
</template>
</QTable>
</div>
<h5 class="flex justify-center color-vn-label" v-else>
{{ t('globals.noResults') }}
</h5>
</template>
<i18n>
es:
Rating: Clasificación
Recommended credit: Crédito recomendado
Since: Desde
Employee: Empleado
</i18n>

View File

@ -1,47 +1,35 @@
<script setup>
import { ref, computed, onBeforeMount } from 'vue';
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import { date, QBtn } from 'quasar';
import { QBtn } from 'quasar';
import { toCurrency } from 'src/filters';
import { useArrayData } from 'composables/useArrayData';
import { useStateStore } from 'stores/useStateStore';
import { toCurrency, toDateHourMin } from 'src/filters';
import FetchData from 'components/FetchData.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const stateStore = useStateStore();
const arrayData = ref(null);
const workerId = ref(0);
const rows = computed(() => arrayData.value.store.data);
const rows = ref([]);
onBeforeMount(async () => {
const filter = {
include: [
{
relation: 'worker',
scope: {
fields: ['id'],
include: { relation: 'user', scope: { fields: ['name'] } },
},
const filter = {
include: [
{
relation: 'worker',
scope: {
fields: ['id'],
include: { relation: 'user', scope: { fields: ['name'] } },
},
],
where: { clientFk: `${route.params.id}` },
order: ['created DESC'],
limit: 20,
};
arrayData.value = useArrayData('CustomerCreditsCard', {
url: 'ClientCredits',
filter,
});
await arrayData.value.fetch({ append: false });
stateStore.rightDrawer = true;
});
},
],
where: { clientFk: route.params.id },
order: ['created DESC'],
limit: 20,
};
const tableColumnComponents = {
created: {
@ -51,10 +39,8 @@ const tableColumnComponents = {
},
employee: {
component: QBtn,
props: () => ({ flat: true, color: 'blue' }),
event: (prop) => {
selectWorkerId(prop.row.clientFk);
},
props: () => ({ flat: true, color: 'blue', noCaps: true }),
event: () => {},
},
amount: {
component: 'span',
@ -69,7 +55,7 @@ const columns = computed(() => [
field: 'created',
label: t('Since'),
name: 'created',
format: (value) => date.formatDate(value, 'DD/MM/YYYY hh:mm:ss'),
format: (value) => toDateHourMin(value),
jsegarra marked this conversation as resolved Outdated

Revisar, porque en Salix, se muestra dd/mm/yyy HH:MM

Revisar, porque en Salix, se muestra dd/mm/yyy HH:MM

Corregido: 86199d8197

Corregido: 86199d8197

Duda, cuando actualicemos con dev no tendremos conflicto?

Duda, cuando actualicemos con dev no tendremos conflicto?
},
{
align: 'left',
@ -86,35 +72,58 @@ const columns = computed(() => [
},
]);
const selectWorkerId = (id) => {
workerId.value = id;
};
const toCustomerCreditCreate = () => {
router.push({ name: 'CustomerCreditCreate' });
};
</script>
<template>
<QPage class="column items-center q-pa-md">
<QTable :columns="columns" :rows="rows" class="full-width q-mt-md" row-key="id">
<template #body-cell="props">
<QTd :props="props">
<QTr :props="props" class="cursor-pointer">
<component
:is="tableColumnComponents[props.col.name].component"
@click="tableColumnComponents[props.col.name].event(props)"
class="rounded-borders q-pa-sm"
v-bind="tableColumnComponents[props.col.name].props(props)"
>
{{ props.value }}
<WorkerDescriptorProxy :id="workerId" />
</component>
</QTr>
</QTd>
</template>
</QTable>
</QPage>
<FetchData
:filter="filter"
@on-fetch="(data) => (rows = data)"
auto-load
url="ClientCredits"
/>
<div class="full-width flex justify-center">
<QCard class="card-width q-pa-lg">
<QTable
:columns="columns"
:pagination="{ rowsPerPage: 12 }"
:rows="rows"
class="full-width q-mt-md"
row-key="id"
v-if="rows?.length"
>
<template #body-cell="props">
<QTd :props="props">
<QTr :props="props" class="cursor-pointer">
<component
:is="tableColumnComponents[props.col.name].component"
@click="
tableColumnComponents[props.col.name].event(props)
"
class="rounded-borders q-pa-sm"
v-bind="
tableColumnComponents[props.col.name].props(props)
"
>
{{ props.value }}
<WorkerDescriptorProxy
:id="props.row.workerFk"
v-if="props.col.name === 'employee'"
/>
</component>
</QTr>
</QTd>
</template>
</QTable>
<h5 class="flex justify-center color-vn-label" v-else>
{{ t('globals.noResults') }}
</h5>
</QCard>
</div>
<QPageSticky :offset="[18, 18]">
<QBtn @click.stop="toCustomerCreditCreate()" color="primary" fab icon="add" />

View File

@ -2,11 +2,15 @@
import { ref, computed } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { toCurrency } from 'src/filters';
import { toCurrency, toDate } from 'src/filters';
import useCardDescription from 'src/composables/useCardDescription';
import CardDescriptor from 'components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import useCardDescription from 'src/composables/useCardDescription';
import VnUserLink from 'src/components/ui/VnUserLink.vue';
import CustomerDescriptorMenu from './CustomerDescriptorMenu.vue';
const $props = defineProps({
id: {
@ -19,8 +23,10 @@ const $props = defineProps({
default: null,
},
});
const route = useRoute();
const { t } = useI18n();
const entityId = computed(() => {
return $props.id || route.params.id;
});
@ -39,6 +45,23 @@ const setData = (entity) => (data.value = useCardDescription(entity.name, entity
:summary="$props.summary"
data-key="customerData"
>
<template #header-extra-action>
<QBtn
round
flat
size="sm"
icon="vn:Person"
color="white"
:to="{ name: 'CustomerList' }"
>
<QTooltip>
{{ t('Go to module index') }}
</QTooltip>
</QBtn>
</template>
<template #menu="{ entity }">
<CustomerDescriptorMenu :customer="entity" />
</template>
<template #body="{ entity }">
<VnLv :label="t('customer.card.payMethod')" :value="entity.payMethod.name" />
@ -63,23 +86,26 @@ const setData = (entity) => (data.value = useCardDescription(entity.name, entity
/>
</template>
<template #icons="{ entity }">
<QCardActions>
<QCardActions class="q-gutter-x-md">
<QIcon
v-if="entity.isActive == false"
v-if="!entity.isActive"
name="vn:disabled"
size="xs"
color="primary"
>
<QTooltip>{{ t('customer.card.isDisabled') }}</QTooltip>
</QIcon>
<QIcon
v-if="entity.isFreezed == true"
name="vn:frozen"
size="xs"
color="primary"
>
<QIcon v-if="entity.isFreezed" name="vn:frozen" size="xs" color="primary">
<QTooltip>{{ t('customer.card.isFrozen') }}</QTooltip>
</QIcon>
<QIcon
v-if="!entity.account.active"
color="primary"
name="vn:noweb"
size="xs"
>
<QTooltip>{{ t('customer.card.webAccountInactive') }}</QTooltip>
</QIcon>
<QIcon
v-if="entity.debt > entity.credit"
name="vn:risk"
@ -89,17 +115,41 @@ const setData = (entity) => (data.value = useCardDescription(entity.name, entity
<QTooltip>{{ t('customer.card.hasDebt') }}</QTooltip>
</QIcon>
<QIcon
v-if="entity.isTaxDataChecked == false"
v-if="!entity.isTaxDataChecked"
name="vn:no036"
size="xs"
color="primary"
>
<QTooltip>{{ t('customer.card.notChecked') }}</QTooltip>
</QIcon>
<QBtn
v-if="entity.unpaid"
flat
size="sm"
jsegarra marked this conversation as resolved Outdated

Revisar tooltip porque no está igual.
Lo del icono va a parte

Revisar tooltip porque no está igual. Lo del icono va a parte

Corregido: 0a33b8d92a

Corregido: 0a33b8d92a

Para probar hay que añadir valores a la fixtures.before.sql

Para probar hay que añadir valores a la fixtures.before.sql
icon="vn:Client_unpaid"
color="primary"
:to="{ name: 'CustomerUnpaid' }"
>
<QTooltip>
{{ t('Unpaid') }}
<br />
{{
t('unpaidDated', {
dated: toDate(entity.unpaid.dated),
})
}}
<br />
{{
t('unpaidAmount', {
amount: toCurrency(entity.unpaid.amount),
})
}}
</QTooltip>
</QBtn>
</QCardActions>
</template>
<template #actions="{ entity }">
<QCardActions>
<QCardActions class="flex justify-center">
<QBtn
:to="{
name: 'TicketList',
@ -109,7 +159,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.name, entity
icon="vn:ticket"
color="primary"
>
<QTooltip>{{ t('ticketList') }}</QTooltip>
<QTooltip>{{ t('Customer ticket list') }}</QTooltip>
</QBtn>
<QBtn
:to="{
@ -120,21 +170,40 @@ const setData = (entity) => (data.value = useCardDescription(entity.name, entity
icon="vn:invoice-out"
color="primary"
>
<QTooltip>{{ t('invoiceOutList') }}</QTooltip>
<QTooltip>{{ t('Customer invoice out list') }}</QTooltip>
</QBtn>
<QBtn
:to="{
name: 'OrderCreate',
query: { clientFk: entity.id },
}"
size="md"
icon="vn:basketadd"
color="primary"
>
<QTooltip>{{ t('New order') }}</QTooltip>
</QBtn>
<QBtn size="md" icon="face" color="primary">
<!-- TODO:: Redirigir a la vista de usuario cuando exista -->
<QTooltip>{{ t('Go to user') }}</QTooltip>
</QBtn>
</QCardActions>
</template>
</CardDescriptor>
</template>
<i18n>
{
"en": {
"ticketList": "Customer ticket list",
"invoiceOutList": "Customer invoice out list"
},
"es": {
"ticketList": "Listado de tickets del cliente",
"invoiceOutList": "Listado de facturas del cliente"
}
}
en:
unpaidDated: 'Date {dated}'
unpaidAmount: 'Amount {amount}'
es:
Go to module index: Ir al índice del módulo
Customer ticket list: Listado de tickets del cliente
Customer invoice out list: Listado de facturas del cliente
New order: Nuevo pedido
Go to user: Ir al usuario
Customer unpaid: Cliente impago
Unpaid: Impagado
unpaidDated: 'Fecha {dated}'
unpaidAmount: 'Importe {amount}'
</i18n>

View File

@ -0,0 +1,68 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import axios from 'axios';
import { useQuasar } from 'quasar';
import useNotify from 'src/composables/useNotify';
import VnSmsDialog from 'src/components/common/VnSmsDialog.vue';
const $props = defineProps({
customer: {
type: Object,
required: true,
},
});
const { notify } = useNotify();
const { t } = useI18n();
const quasar = useQuasar();
const route = useRoute();
const showSmsDialog = () => {
quasar.dialog({
component: VnSmsDialog,
componentProps: {
phone: $props.customer.phone || $props.customer.mobile,
promise: sendSms,
},
});
};
const sendSms = async (payload) => {
payload.destinationFk = route.params.id;
try {
await axios.post(`Clients/${route.params.id}/sendSms`, payload);
notify('globals.notificationSent', 'positive');
} catch (error) {
notify(error.message, 'positive');
}
};
</script>
<template>
<QItem v-ripple clickable>
<QItemSection>
<RouterLink
:to="{
name: 'TicketCreate',
query: { clientFk: customer.id },
}"
class="color-vn-text"
>
{{ t('Simple ticket') }}
</RouterLink>
</QItemSection>
</QItem>
<QItem v-ripple clickable>
<QItemSection @click="showSmsDialog()">{{ t('Send SMS') }}</QItemSection>
</QItem>
</template>
<i18n>
es:
Simple ticket: Ticket simple
Send SMS: Enviar SMS
</i18n>

View File

@ -0,0 +1,259 @@
<script setup>
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import { QBadge, QBtn, QCheckbox } from 'quasar';
import { downloadFile } from 'src/composables/downloadFile';
import { toDateTimeFormat } from 'src/filters/date';
import FetchData from 'components/FetchData.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import CustomerFileManagementActions from 'src/pages/Customer/components/CustomerFileManagementActions.vue';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const ClientDmsRef = ref(null);
const rows = ref([]);
const filter = {
include: {
relation: 'dms',
scope: {
fields: [
'dmsTypeFk',
'reference',
'hardCopyNumber',
'workerFk',
'description',
'hasFile',
'file',
'created',
],
include: [
{ relation: 'dmsType', scope: { fields: ['name'] } },
{
relation: 'worker',
scope: {
fields: ['id'],
include: { relation: 'user', scope: { fields: ['name'] } },
},
},
],
},
},
where: { clientFk: route.params.id },
order: ['dmsFk DESC'],
limit: 20,
};
const tableColumnComponents = {
id: {
component: 'span',
props: () => {},
event: () => {},
},
type: {
component: 'span',
props: () => {},
event: () => {},
},
order: {
component: QBadge,
props: () => {},
event: () => {},
},
reference: {
component: 'span',
props: () => {},
event: () => {},
},
description: {
component: 'span',
props: () => {},
event: () => {},
},
original: {
component: QCheckbox,
props: (prop) => ({
disable: true,
'model-value': Boolean(prop.value),
}),
event: () => {},
},
file: {
component: QBtn,
props: () => ({ flat: true, color: 'blue' }),
event: ({ row }) => downloadFile(row.dmsFk),
},
employee: {
component: QBtn,
props: () => ({ flat: true, color: 'blue' }),
event: () => {},
},
created: {
component: 'span',
props: () => {},
event: () => {},
},
actions: {
component: CustomerFileManagementActions,
props: (prop) => ({
id: prop.row.dmsFk,
promise: setData,
}),
event: () => {},
},
};
const columns = computed(() => [
{
align: 'left',
field: ({ dms }) => dms.id,
label: t('Id'),
name: 'id',
},
{
align: 'left',
field: ({ dms }) => dms.dmsType.name,
label: t('Type'),
name: 'type',
},
{
align: 'left',
field: ({ dms }) => dms.hardCopyNumber,
label: t('Order'),
name: 'order',
},
{
align: 'left',
field: ({ dms }) => dms.reference,
label: t('Reference'),
name: 'reference',
},
{
align: 'left',
field: ({ dms }) => dms.description,
label: t('Description'),
name: 'description',
},
{
align: 'left',
field: ({ dms }) => dms.hasFile,
label: t('Original'),
name: 'original',
},
{
align: 'left',
field: ({ dms }) => dms.file,
label: t('File'),
name: 'file',
},
{
align: 'left',
field: ({ dms }) => dms.worker.user.name,
label: t('Employee'),
name: 'employee',
},
{
align: 'left',
field: (value) => value.dms.created,
label: t('Created'),
name: 'created',
format: (value) => toDateTimeFormat(value),
jsegarra marked this conversation as resolved Outdated

Duda, cuando actualicemos con dev no tendremos conflicto?

Duda, cuando actualicemos con dev no tendremos conflicto?

Se reemplazo toDateHourMinSec por las nuevas utils ubicadas en date.js

Commit: 3cb045936b

Se reemplazo `toDateHourMinSec` por las nuevas utils ubicadas en `date.js` Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/3cb045936b2d17001fedd87219c904162d29fba4
},
{
align: 'right',
field: 'actions',
label: '',
name: 'actions',
},
]);
const setData = () => {
ClientDmsRef.value.fetch();
};
const toCustomerFileManagementCreate = () => {
router.push({ name: 'CustomerFileManagementCreate' });
};
</script>
<template>
<FetchData
ref="ClientDmsRef"
:filter="filter"
@on-fetch="(data) => (rows = data)"
auto-load
url="ClientDms"
/>
<QPage class="column items-center q-pa-md">
<QTable
:columns="columns"
:pagination="{ rowsPerPage: 12 }"
:rows="rows"
class="full-width q-mt-md"
row-key="id"
v-if="rows?.length"
>
<template #body-cell="props">
<QTd :props="props">
<QTr :props="props" class="cursor-pointer">
<component
:is="
props.col.name === 'order' && !props.value
? 'span'
: tableColumnComponents[props.col.name].component
"
@click="tableColumnComponents[props.col.name].event(props)"
class="col-content"
v-bind="tableColumnComponents[props.col.name].props(props)"
>
<template v-if="props.col.name !== 'original'">
{{ props.value }}
</template>
<WorkerDescriptorProxy
:id="props.row.dms.workerFk"
v-if="props.col.name === 'employee'"
/>
</component>
</QTr>
</QTd>
</template>
</QTable>
<h5 class="flex justify-center color-vn-label" v-else>
{{ t('globals.noResults') }}
</h5>
</QPage>
jsegarra marked this conversation as resolved
Review

El icono de fichero es diferente a otras partes de la aplicación. Unificar criterios. Puesto que este es el último módulo que quiere integrarse, lo reemplazaría por los que ya están.

El icono de fichero es diferente a otras partes de la aplicación. Unificar criterios. Puesto que este es el último módulo que quiere integrarse, lo reemplazaría por los que ya están.
Review

Corregido: 3cf3687b84

Corregido: 3cf3687b84
<QPageSticky :offset="[18, 18]">
<QBtn
@click.stop="toCustomerFileManagementCreate()"
color="primary"
fab
icon="add"
/>
<QTooltip>
{{ t('Upload file') }}
</QTooltip>
</QPageSticky>
</template>
<i18n>
es:
Id: Id
Type: Tipo
Order: Orden
Reference: Referencia
Description: Descripción
Original: Original
File: Fichero
Employee: Empleado
Created: Fecha creación
Upload file: Subir fichero
</i18n>

View File

@ -46,17 +46,24 @@ function handleLocation(data, location) {
:label="t('Social name')"
Review

Un poco mas arriba en esta linea algo debe haber mal porque si intento elegir un Tipo de transaccion Sage me escribe [object object}, con tipo de impuesto va bien.
url="SageTransactionTypes"

Un poco mas arriba en esta linea algo debe haber mal porque si intento elegir un Tipo de transaccion Sage me escribe [object object}, con tipo de impuesto va bien. url="SageTransactionTypes"
Review

Corregido: 3bcd4984f5

Corregido: 3bcd4984f5
Review

Lo veo OK

Lo veo OK
:required="true"
:rules="validate('client.socialName')"
clearable
v-model="data.socialName"
/>
>
<template #append>
<QIcon name="info" class="cursor-info">
<QTooltip>{{ t('onlyLetters') }}</QTooltip>
</QIcon>
</template>
</VnInput>
</div>
<div class="col">
<VnInput :label="t('Tax number')" v-model="data.fi" />
<VnInput :label="t('Tax number')" clearable v-model="data.fi" />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('Street')" v-model="data.street" />
<VnInput :label="t('Street')" clearable v-model="data.street" />
</div>
</VnRow>
@ -76,10 +83,21 @@ function handleLocation(data, location) {
:label="t('Sage transaction type')"
:options="typesTransactions"
hide-selected
option-label="vat"
option-label="transaction"
option-value="id"
v-model="data.sageTransactionTypeFk"
/>
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
<QItemLabel caption>
{{ `${scope.opt.id}: ${scope.opt.transaction}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectFilter>
</div>
</VnRow>
@ -111,6 +129,11 @@ function handleLocation(data, location) {
</div>
<div class="col">
<QCheckbox :label="t('Vies')" v-model="data.isVies" />
<QIcon name="info" class="cursor-info q-ml-sm" size="sm">
<QTooltip>
{{ t('whenActivatingIt') }}
</QTooltip>
</QIcon>
</div>
</VnRow>
@ -135,6 +158,11 @@ function handleLocation(data, location) {
:label="t('Is equalizated')"
v-model="data.isEqualizated"
/>
<QIcon class="cursor-info q-ml-sm" name="info" size="sm">
<QTooltip>
{{ t('inOrderToInvoice') }}
</QTooltip>
</QIcon>
</div>
<div class="col">
<QCheckbox
@ -183,4 +211,11 @@ es:
Verified data: Datos comprobados
Incoterms authorization: Autorización incoterms
Electronic invoice: Factura electrónica
onlyLetters: Sólo se pueden usar letras, números y espacios
jsegarra marked this conversation as resolved Outdated

El campo Tipo de transaccion Stage, muestra mal los datos

El campo Tipo de transaccion Stage, muestra mal los datos

Corregido: 86199d8197

Corregido: 86199d8197
whenActivatingIt: Al activarlo, no informar el código del país en el campo nif
inOrderToInvoice: Para facturar no se consulta este campo, sino el RE de consignatario. Al modificar este campo si no esta marcada la casilla Facturar por consignatario, se propagará automaticamente el cambio a todos lo consignatarios, en caso contrario preguntará al usuario si quiere o no propagar
en:
onlyLetters: Only letters, numbers and spaces can be used
whenActivatingIt: When activating it, do not enter the country code in the ID field
inOrderToInvoice: In order to invoice, this field is not contulted, but the consignee's ET. When modifiying this field if the invoice by address option is not checked, the change will be automatically propagated to all addresses, otherwise the user will be asked if he wants to propagate it or not
</i18n>

View File

@ -1,59 +1,44 @@
<script setup>
import { ref, computed, onBeforeMount } from 'vue';
import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import { date, QBtn } from 'quasar';
import { QBtn } from 'quasar';
import { useArrayData } from 'composables/useArrayData';
import { useStateStore } from 'stores/useStateStore';
import { toCurrency } from 'src/filters';
import { toDateTimeFormat } from 'src/filters/date';
import FetchData from 'components/FetchData.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const stateStore = useStateStore();
const arrayData = ref(null);
const rows = ref([]);
const totalAmount = ref(0);
const workerId = ref(0);
const rows = computed(() => arrayData.value.store.data);
onBeforeMount(async () => {
const filter = {
include: [
{
relation: 'greugeType',
scope: {
fields: ['id', 'name'],
},
const filter = {
include: [
{
relation: 'greugeType',
scope: {
fields: ['id', 'name'],
},
{
relation: 'user',
scope: {
fields: ['id', 'name'],
},
},
],
order: 'shipped DESC, amount',
where: {
clientFk: `${route.params.id}`,
},
limit: 20,
};
arrayData.value = useArrayData('CustomerGreugesCard', {
url: 'greuges',
filter,
});
await arrayData.value.fetch({ append: false });
totalAmount.value = arrayData.value.store.data.reduce((accumulator, currentValue) => {
return accumulator + currentValue.amount;
}, 0);
stateStore.rightDrawer = true;
});
{
relation: 'user',
scope: {
fields: ['id', 'name'],
},
},
],
order: 'shipped DESC, amount',
where: {
clientFk: `${route.params.id}`,
},
limit: 20,
};
const tableColumnComponents = {
date: {
@ -63,10 +48,8 @@ const tableColumnComponents = {
},
createdBy: {
component: QBtn,
props: () => ({ flat: true, color: 'blue' }),
event: (prop) => {
selectWorkerId(prop.row.clientFk);
},
props: () => ({ flat: true, color: 'blue', noCaps: true }),
event: () => {},
},
comment: {
component: 'span',
@ -91,7 +74,7 @@ const columns = computed(() => [
field: 'shipped',
label: t('Date'),
name: 'date',
jsegarra marked this conversation as resolved Outdated

Duda, cuando actualicemos con dev no tendremos conflicto?

Duda, cuando actualicemos con dev no tendremos conflicto?

Se reemplazo toDateHourMinSec por las nuevas utils ubicadas en date.js

Commit: 73ea49df63

Se reemplazo `toDateHourMinSec` por las nuevas utils ubicadas en `date.js` Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/73ea49df6340e32cc035ac132e666c4be2d1fa27
format: (value) => date.formatDate(value, 'DD/MM/YYYY hh:mm:ss'),
format: (value) => toDateTimeFormat(value),
},
{
align: 'left',
@ -120,8 +103,11 @@ const columns = computed(() => [
},
]);
const selectWorkerId = (id) => {
workerId.value = id;
const setRows = (data) => {
rows.value = data;
totalAmount.value = data.reduce((accumulator, currentValue) => {
return accumulator + currentValue.amount;
}, 0);
};
const toCustomerGreugeCreate = () => {
@ -130,44 +116,52 @@ const toCustomerGreugeCreate = () => {
</script>
<template>
<QPage class="column items-center q-pa-md">
<QCard class="full-width" v-if="totalAmount">
<h6 class="flex justify-end q-my-lg q-pr-lg">
<span class="label-color q-mr-md">{{ t('Total') }}:</span>
{{ toCurrency(totalAmount) }}
</h6>
</QCard>
<FetchData :filter="filter" @on-fetch="setRows" auto-load url="greuges" />
<QTable
:columns="columns"
:rows="rows"
class="full-width q-mt-md"
row-key="id"
v-if="rows?.length"
>
<template #body-cell="props">
<QTd :props="props">
<QTr :props="props" class="cursor-pointer">
<component
:is="tableColumnComponents[props.col.name].component"
class="col-content"
v-bind="tableColumnComponents[props.col.name].props(props)"
@click="tableColumnComponents[props.col.name].event(props)"
>
{{ props.value }}
<WorkerDescriptorProxy :id="workerId" />
</component>
</QTr>
</QTd>
</template>
</QTable>
<div class="full-width flex justify-center">
<QPage class="card-width q-pa-lg">
<QCard class="full-width q-pa-sm" v-if="totalAmount">
<h6 class="flex justify-end q-my-lg q-pr-lg">
<span class="color-vn-label q-mr-md">{{ t('Total') }}:</span>
{{ toCurrency(totalAmount) }}
</h6>
</QCard>
<QCard class="full-width" v-else>
<h5 class="flex justify-center label-color">
{{ t('globals.noResults') }}
</h5>
</QCard>
</QPage>
<QCard class="q-pa-sm q-mt-md">
<QTable
:columns="columns"
:no-data-label="t('globals.noResults')"
:pagination="{ rowsPerPage: 12 }"
:rows="rows"
class="full-width q-mt-md"
row-key="id"
>
<template #body-cell="props">
<QTd :props="props">
<QTr :props="props" class="cursor-pointer">
<component
:is="tableColumnComponents[props.col.name].component"
class="col-content"
v-bind="
tableColumnComponents[props.col.name].props(props)
"
@click="
tableColumnComponents[props.col.name].event(props)
"
>
{{ props.value }}
<WorkerDescriptorProxy
:id="props.row.userFk"
v-if="props.col.name === 'createdBy'"
/>
</component>
</QTr>
</QTd>
</template>
</QTable>
</QCard>
</QPage>
</div>
<QPageSticky :offset="[18, 18]">
<QBtn @click.stop="toCustomerGreugeCreate()" color="primary" fab icon="add" />

View File

@ -124,13 +124,13 @@ const setInq = (value, status) => {
:url="urlClientLogsModels"
/>
<h5 class="flex justify-center label-color">
<h5 class="flex justify-center color-vn-label">
{{ t('globals.noResults') }}
</h5>
<QDrawer :width="256" show-if-above side="right" v-model="stateStore.rightDrawer">
<div class="q-mt-sm q-px-md">
<VnInput :label="t('Search')">
<VnInput :label="t('Search')" clearable>
<template #append>
<QIcon name="info" class="cursor-pointer">
<QTooltip>
@ -187,7 +187,7 @@ const setInq = (value, status) => {
option-label="name"
option-value="id"
/>
<VnInput :label="t('Changes')" class="q-mt-sm">
<VnInput :label="t('Changes')" clearable class="q-mt-sm">
<template #append>
<QIcon name="info" class="cursor-pointer">
<QTooltip>
@ -227,7 +227,7 @@ const setInq = (value, status) => {
</div>
<VnInputDate :label="t('Date')" class="q-mt-sm" />
<VnInput :label="t('To')" class="q-mt-md" />
<VnInput :label="t('To')" clearable class="q-mt-md" />
</div>
</QDrawer>

View File

@ -0,0 +1,135 @@
<script setup>
jsegarra marked this conversation as resolved
Review

Revisar comentario.
Si este componente, está así porque no hay datos, utilizar el cliente 1102

Revisar comentario. Si este componente, está así porque no hay datos, utilizar el cliente 1102
Review

Corrección: 08e072100b

Corrección: 08e072100b
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { toDateTimeFormat } from 'src/filters/date';
import FetchData from 'components/FetchData.vue';
const { t } = useI18n();
const route = useRoute();
const rows = ref([]);
const filter = {
include: [
{ relation: 'mandateType', scope: { fields: ['id', 'name'] } },
{ relation: 'company', scope: { fields: ['id', 'code'] } },
],
where: { clientFk: route.params.id },
order: ['created DESC'],
limit: 20,
};
const tableColumnComponents = {
id: {
component: 'span',
props: () => {},
event: () => {},
},
company: {
component: 'span',
props: () => {},
event: () => {},
},
type: {
component: 'span',
props: () => {},
event: () => {},
},
registerDate: {
component: 'span',
props: () => {},
event: () => {},
},
endDate: {
component: 'span',
props: () => {},
event: () => {},
},
};
const columns = computed(() => [
{
align: 'left',
field: 'id',
label: t('Id'),
name: 'id',
},
{
align: 'left',
field: (row) => row.company.code,
label: t('Company'),
name: 'company',
},
{
align: 'left',
field: (row) => row.mandateType.name,
label: t('Type'),
name: 'type',
},
{
align: 'left',
field: 'created',
label: t('Register date'),
name: 'registerDate',
format: (value) => toDateTimeFormat(value),
jsegarra marked this conversation as resolved Outdated

Duda, cuando actualicemos con dev no tendremos conflicto?

Duda, cuando actualicemos con dev no tendremos conflicto?

Se reemplazo toDateHourMinSec por las nuevas utils ubicadas en date.js

Commit: 093c367d7e

Se reemplazo `toDateHourMinSec` por las nuevas utils ubicadas en `date.js` Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/093c367d7eb570320d641dcb0b5e130f8c6f6356
},
{
align: 'left',
field: 'finished',
label: t('End date'),
name: 'endDate',
format: (value) => (value ? toDateTimeFormat(value) : '-'),
},
]);
</script>
<template>
<FetchData
:filter="filter"
@on-fetch="(data) => (rows = data)"
auto-load
url="Mandates"
/>
<QPage class="column items-center q-pa-md">
<QTable
:columns="columns"
:pagination="{ rowsPerPage: 12 }"
:rows="rows"
class="full-width q-mt-md"
row-key="id"
v-if="rows?.length"
>
<template #body-cell="props">
<QTd :props="props">
<QTr :props="props" class="cursor-pointer">
<component
:is="tableColumnComponents[props.col.name].component"
@click="tableColumnComponents[props.col.name].event(props)"
class="rounded-borders q-pa-sm"
v-bind="tableColumnComponents[props.col.name].props(props)"
>
{{ props.value }}
</component>
</QTr>
</QTd>
</template>
</QTable>
<h5 class="flex justify-center color-vn-label" v-else>
{{ t('globals.noResults') }}
</h5>
</QPage>
</template>
<i18n>
es:
Id: Id
Company: Empresa
Type: Tipo
Register date: Fecha alta
End date: Fecha baja
</i18n>

View File

@ -2,7 +2,7 @@
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import { date } from 'quasar';
import { toDateTimeFormat } from 'src/filters/date';
import VnPaginate from 'src/components/ui/VnPaginate.vue';
@ -23,62 +23,48 @@ const toCustomerNoteCreate = () => {
</script>
<template>
<QCard class="q-pa-lg">
<VnPaginate
data-key="CustomerNotes"
:url="'clientObservations'"
auto-load
:filter="noteFilter"
>
<template #body="{ rows }">
<div v-if="rows.length">
<QCard
v-for="(item, index) in rows"
:key="index"
:class="{
'q-pa-md': true,
'q-rounded': true,
'custom-border': true,
'q-mb-md': index < rows.length - 1,
}"
>
<div class="flex justify-between">
<p class="label-color">{{ item.worker.user.nickname }}</p>
<p class="label-color">
{{
date.formatDate(item?.created, 'DD-MM-YYYY HH:mm:ss')
}}
</p>
</div>
<h6 class="q-mt-xs q-mb-none">{{ item.text }}</h6>
</QCard>
</div>
<div class="full-width flex justify-center">
<QCard class="card-width q-pa-lg">
<VnPaginate
data-key="CustomerNotes"
url="clientObservations"
jsegarra marked this conversation as resolved Outdated

Si es texto literal , quitar :

Si es texto literal , quitar :

Aplicado

Commit: fb59ac1f2f

Aplicado Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/fb59ac1f2f90be4469ef31258fcd087477df8ee7
auto-load
:filter="noteFilter"
>
<template #body="{ rows }">
<div v-if="rows.length">
<QCard
v-for="(item, index) in rows"
:key="index"
class="q-pa-md q-rounded custom-border"
:class="{ 'q-mb-md': index < rows.length - 1 }"
jsegarra marked this conversation as resolved Outdated

TODO: si lleva true mover a class

TODO: si lleva true mover a class

Se mejoró la definición de clases

Commit: f6b8a73ae2

Se mejoró la definición de clases Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/f6b8a73ae2af64f343f973db1449f5f9aeee3387
>
<div class="flex justify-between">
<p class="color-vn-label">
{{ item.worker.user.nickname }}
</p>
<p class="color-vn-label">
{{ toDateTimeFormat(item?.created) }}
</p>
</div>
<h6 class="q-mt-xs q-mb-none">{{ item.text }}</h6>
</QCard>
</div>
jsegarra marked this conversation as resolved
Review

Duda, cuando actualicemos con dev no tendremos conflicto?

Duda, cuando actualicemos con dev no tendremos conflicto?
Review

Se reemplazo date.formatDate por las nuevas utils ubicadas en date.js

Commit: e1f8b7217a

Se reemplazo `date.formatDate` por las nuevas utils ubicadas en `date.js` Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/e1f8b7217a1bbfa0b739f25321cefcafb1e3aed5
<div v-else>
<h5 class="flex justify-center label-color">
{{ t('globals.noResults') }}
</h5>
</div>
<QPageSticky :offset="[18, 18]">
<QBtn
@click.stop="toCustomerNoteCreate()"
color="primary"
fab
icon="add"
/>
<QTooltip>
{{ t('New consignee') }}
</QTooltip>
</QPageSticky>
</template>
</VnPaginate>
</QCard>
<div v-else>
<h5 class="flex justify-center color-vn-label">
{{ t('globals.noResults') }}
</h5>
</div>
</template>
</VnPaginate>
</QCard>
</div>
<QPageSticky :offset="[18, 18]">
<QBtn @click.stop="toCustomerNoteCreate()" color="primary" fab icon="add" />
<QTooltip>
{{ t('New consignee') }}
{{ t('New note') }}
</QTooltip>
</QPageSticky>
</template>
@ -89,8 +75,9 @@ const toCustomerNoteCreate = () => {
border-radius: 10px;
padding: 10px;
}
.label-color {
color: var(--vn-label-color);
}
</style>
<i18n>
es:
New note: Nueva nota
</i18n>

View File

@ -1,3 +0,0 @@
<template>
<div class="flex justify-center">Others</div>
</template>

View File

@ -1,39 +1,22 @@
<script setup>
import { ref, computed, onBeforeMount } from 'vue';
import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import { date, QBtn } from 'quasar';
import { toCurrency, toDate } from 'src/filters';
import { useArrayData } from 'composables/useArrayData';
import { useStateStore } from 'stores/useStateStore';
import { toCurrency } from 'src/filters';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import FetchData from 'components/FetchData.vue';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const stateStore = useStateStore();
const rows = ref([]);
const arrayData = ref(null);
const workerId = ref(0);
const rows = computed(() => arrayData.value.store.data);
onBeforeMount(async () => {
const filter = {
where: { clientFk: `${route.params.id}` },
order: ['started DESC'],
limit: 20,
};
arrayData.value = useArrayData('CustomerRecoveriesCard', {
url: 'Recoveries',
filter,
});
await arrayData.value.fetch({ append: false });
stateStore.rightDrawer = true;
});
const filter = {
where: { clientFk: route.params.id },
order: ['started DESC'],
limit: 20,
};
const tableColumnComponents = {
since: {
@ -64,14 +47,14 @@ const columns = computed(() => [
field: 'started',
label: t('Since'),
name: 'since',
format: (value) => date.formatDate(value, 'DD/MM/YYYY'),
format: (value) => toDate(value),
},
{
align: 'left',
field: 'finished',
label: t('To'),
name: 'to',
format: (value) => date.formatDate(value, 'DD/MM/YYYY'),
format: (value) => toDate(value),
},
{
align: 'left',
@ -94,37 +77,44 @@ const toCustomerRecoverieCreate = () => {
</script>
<template>
<QPage class="column items-center q-pa-md">
<QTable
:columns="columns"
:rows="rows"
class="full-width q-mt-md"
row-key="id"
v-if="rows?.length"
>
<template #body-cell="props">
<QTd :props="props">
<QTr :props="props" class="cursor-pointer">
<component
:is="tableColumnComponents[props.col.name].component"
class="col-content"
v-bind="tableColumnComponents[props.col.name].props(props)"
@click="tableColumnComponents[props.col.name].event(props)"
>
{{ props.value }}
<WorkerDescriptorProxy :id="workerId" />
</component>
</QTr>
</QTd>
</template>
</QTable>
<FetchData
:filter="filter"
@on-fetch="(data) => (rows = data)"
auto-load
url="Recoveries"
/>
<QCard class="full-width" v-else>
<h5 class="flex justify-center label-color">
{{ t('globals.noResults') }}
</h5>
</QCard>
</QPage>
<div class="full-width flex justify-center">
<QPage class="card-width q-pa-lg">
<QTable
:columns="columns"
:no-data-label="t('globals.noResults')"
:pagination="{ rowsPerPage: 12 }"
:rows="rows"
class="full-width q-mt-md"
row-key="id"
>
<template #body-cell="props">
<QTd :props="props">
<QTr :props="props" class="cursor-pointer">
<component
:is="tableColumnComponents[props.col.name].component"
class="col-content"
v-bind="
tableColumnComponents[props.col.name].props(props)
"
@click="
tableColumnComponents[props.col.name].event(props)
"
>
{{ props.value }}
</component>
</QTr>
</QTd>
</template>
</QTable>
</QPage>
</div>
<QPageSticky :offset="[18, 18]">
<QBtn @click.stop="toCustomerRecoverieCreate()" color="primary" fab icon="add" />

View File

@ -0,0 +1,144 @@
<script setup>
import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import { QBtn } from 'quasar';
import FetchData from 'components/FetchData.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import { toDateTimeFormat } from 'src/filters/date';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const rows = ref([]);
const filter = {
include: [
{ relation: 'type', scope: { fields: ['code', 'description'] } },
{ relation: 'user', scope: { fields: ['id', 'name'] } },
{ relation: 'company', scope: { fields: ['code'] } },
],
where: { clientFk: route.params.id },
order: ['created DESC'],
limit: 20,
};
const tableColumnComponents = {
sent: {
component: 'span',
props: () => {},
event: () => {},
},
description: {
component: 'span',
props: () => {},
event: () => {},
},
worker: {
component: QBtn,
props: () => ({ flat: true, color: 'blue', noCaps: true }),
event: () => {},
},
company: {
component: 'span',
props: () => {},
event: () => {},
},
};
const columns = computed(() => [
{
align: 'left',
field: 'created',
label: t('Sent'),
name: 'sent',
format: (value) => toDateTimeFormat(value),
jsegarra marked this conversation as resolved Outdated

Duda, cuando actualicemos con dev no tendremos conflicto?

Duda, cuando actualicemos con dev no tendremos conflicto?

Se reemplazo toDateHourMinSec por las nuevas utils ubicadas en date.js

Commit: c856adc8d0

Se reemplazo `toDateHourMinSec` por las nuevas utils ubicadas en `date.js` Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/c856adc8d0defb6c5574f9d6198ddcf2a28356c4
},
{
align: 'left',
field: (value) => value.type.description,
label: t('Description'),
name: 'description',
},
{
align: 'left',
field: (value) => value.user.name,
label: t('Worker'),
name: 'worker',
},
{
align: 'left',
field: (value) => value.company?.code,
label: t('Company'),
name: 'company',
},
]);
const toCustomerSamplesCreate = () => {
router.push({ name: 'CustomerSamplesCreate' });
};
</script>
<template>
<FetchData
:filter="filter"
@on-fetch="(data) => (rows = data)"
auto-load
url="ClientSamples"
/>
<div class="full-width flex justify-center">
<QPage class="card-width q-pa-lg">
<QTable
:columns="columns"
:pagination="{ rowsPerPage: 12 }"
:rows="rows"
class="full-width q-mt-md"
row-key="id"
:no-data-label="t('globals.noResults')"
>
<template #body-cell="props">
<QTd :props="props">
<QTr :props="props" class="cursor-pointer">
<component
:is="tableColumnComponents[props.col.name].component"
class="col-content"
v-bind="
tableColumnComponents[props.col.name].props(props)
"
@click="
tableColumnComponents[props.col.name].event(props)
"
>
{{ props.value }}
<WorkerDescriptorProxy
:id="props.row.userFk"
v-if="props.col.name === 'worker'"
/>
</component>
</QTr>
</QTd>
</template>
</QTable>
</QPage>
</div>
<QPageSticky :offset="[18, 18]">
<QBtn @click.stop="toCustomerSamplesCreate()" color="primary" fab icon="add" />
<QTooltip>
{{ t('Send sample') }}
</QTooltip>
</QPageSticky>
</template>
<i18n>
es:
Sent: Enviado
Description: Descripción
Worker: Trabajador
Company: Empresa
Send sample: Enviar plantilla
</i18n>

View File

@ -7,6 +7,7 @@ import CardSummary from 'components/ui/CardSummary.vue';
import { getUrl } from 'src/composables/getUrl';
import VnLv from 'src/components/ui/VnLv.vue';
import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
import CustomerSummaryTable from 'src/pages/Customer/components/CustomerSummaryTable.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
const route = useRoute();
@ -305,9 +306,16 @@ const creditWarning = computed(() => {
:value="entity.recommendedCredit"
/>
</QCard>
<QCard>
<div class="header">
{{ t('Latest tickets') }}
</div>
<CustomerSummaryTable />
</QCard>
</template>
</CardSummary>
</template>
<style lang="scss" scoped>
@media (min-width: $breakpoint-md) {
.summary .vn-one {
@ -315,9 +323,11 @@ const creditWarning = computed(() => {
}
}
</style>
<i18n>
en:
valueInfo: Value from {min} to {max}. The higher the better value
es:
valueInfo: Valor de {min} a {max}. Cuanto más alto, mejor valor
Latest tickets: Últimos tickets
</i18n>

View File

@ -0,0 +1,167 @@
<script setup>
import { computed, onBeforeMount, ref, watch, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'src/components/common/VnInput.vue';
import axios from 'axios';
import useNotify from 'src/composables/useNotify';
import { useStateStore } from 'stores/useStateStore';
const { t } = useI18n();
const route = useRoute();
const { notify } = useNotify();
const stateStore = useStateStore();
const amountInputRef = ref(null);
jsegarra marked this conversation as resolved Outdated

TODO, revisar si hay otra opción

TODO, revisar si hay otra opción

Modificado

Commit: 998561badd

Modificado Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/998561badd40ba7f32a0265c9865a44dae284586
const initialDated = Date.vnNew();
const unpaidClient = ref(false);
const isLoading = ref(false);
const amount = ref(null);
const dated = ref(initialDated);
const initialData = ref({
dated: initialDated,
});
const hasChanged = computed(() => {
return (
initialData.value.dated !== dated.value ||
initialData.value.amount !== amount.value
);
});
onBeforeMount(() => {
getData(route.params.id);
});
watch(
() => route.params.id,
(newValue) => {

salix pone una fecha por defecto, emular comportamiento

salix pone una fecha por defecto, emular comportamiento

Corregido: 90ee50eab5

Corregido: 90ee50eab5

Lo veo OK

Lo veo OK
if (!newValue) return;
getData(newValue);
}
);
const getData = async (id) => {
const filter = { where: { clientFk: id } };
try {
const { data } = await axios.get('ClientUnpaids', {
jsegarra marked this conversation as resolved Outdated

Porque haces un find por id, si ya lo has hecho en la query/filter? Te devoilverá el resultado
Si filtras por 1101, te llegará [1101]

Porque haces un find por id, si ya lo has hecho en la query/filter? Te devoilverá el resultado Si filtras por 1101, te llegará [1101]

Corregido: 93015d9f98

Corregido: 93015d9f98
params: { filter: JSON.stringify(filter) },
jsegarra marked this conversation as resolved Outdated

Mi propuesta es si data.length>0

Mi propuesta es si data.length>0

Corregido: 93015d9f98

Corregido: 93015d9f98
});
if (data.length) {
setValues(data[0]);
} else {
defaultValues();
}
} catch (error) {
defaultValues();
}
};
const setValues = (data) => {
jsegarra marked this conversation as resolved Outdated

No se muestran los botones

No se muestran los botones

Los botones siempre estaban visibles, corregí que cuando habían datos no se estaban mostrando adecuadamente y no recargaba la data al cambiar manualmente la url: 7eca27b8af

Los botones siempre estaban visibles, corregí que cuando habían datos no se estaban mostrando adecuadamente y no recargaba la data al cambiar manualmente la url: 7eca27b8af
unpaidClient.value = true;
amount.value = data.amount;
dated.value = data.dated;
initialData.value = data;
};
const defaultValues = () => {
unpaidClient.value = false;
initialData.value.amount = null;
setInitialData();
};
const setInitialData = () => {
amount.value = initialData.value.amount;
dated.value = initialData.value.dated;
};
const onSubmit = async () => {
isLoading.value = true;
const payload = {
amount: amount.value,
clientFk: route.params.id,
dated: dated.value,
};
try {
await axios.patch('ClientUnpaids', payload);
notify('globals.dataSaved', 'positive');
unpaidClient.value = true;
} catch (error) {
notify('errors.create', 'negative');
} finally {
isLoading.value = false;
}
};
watch(
() => unpaidClient.value,
async (val) => {
await nextTick();
if (val) amountInputRef.value.focus();
}
);
</script>
<template>
<Teleport v-if="stateStore?.isSubToolbarShown()" to="#st-actions">
<QBtnGroup push class="q-gutter-x-sm">
<QBtn
:disabled="!hasChanged"
:label="t('globals.reset')"
:loading="isLoading"
@click="setInitialData"
color="primary"
flat
icon="restart_alt"
type="reset"
/>
<QBtn
:disabled="!hasChanged"
:label="t('globals.save')"
:loading="isLoading"
@click="onSubmit"
color="primary"
icon="save"
/>
</QBtnGroup>
</Teleport>
<div class="full-width flex justify-center">
<QCard class="card-width q-pa-lg">
<QForm>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QCheckbox :label="t('Unpaid client')" v-model="unpaidClient" />
jsegarra marked this conversation as resolved
Review

autofocus. Aunque no lo apliques porque tenemos un desarrollo para evitar esta propiedad

autofocus. Aunque no lo apliques porque tenemos un desarrollo para evitar esta propiedad
Review

@jsegarra autofocus agregado!

Commit: 82f259be32

@jsegarra autofocus agregado! Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/82f259be327ca373efe530a96a5ddcda120d8afd
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md" v-show="unpaidClient">
<div class="col">
<VnInputDate :label="t('Date')" v-model="dated" />
</div>
<div class="col">
<VnInput
ref="amountInputRef"
:label="t('Amount')"
clearable
type="number"
v-model="amount"
/>
</div>
</VnRow>
</QForm>
</QCard>
</div>
</template>
<i18n>
es:
Unpaid client: Cliente impagado
Date: Fecha
Amount: Importe
</i18n>

View File

@ -1,48 +1,158 @@
<script setup>
jsegarra marked this conversation as resolved
Review

Falta botón de cambiar contraseña
Los campos no son clearables

Falta botón de cambiar contraseña Los campos no son clearables
Review

Corregido: c0a9835e80

Corregido: c0a9835e80
Review

Aparece el botón de cambiar contraseña, sin embargo, el formato del layout no es el estándar

Aparece el botón de cambiar contraseña, sin embargo, el formato del layout no es el estándar
Review

Corregido: ebd1ee07f4

Corregido: ebd1ee07f4
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import FormModel from 'components/FormModel.vue';
import axios from 'axios';
import { useQuasar } from 'quasar';
import { useValidator } from 'src/composables/useValidator';
import useNotify from 'src/composables/useNotify';
import { useStateStore } from 'stores/useStateStore';
import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
import CustomerChangePassword from 'src/pages/Customer/components/CustomerChangePassword.vue';

desde salix http://localhost:9000/#/customer/1101/web-access cambio BruceWayne por BruceWayne4 y funciona, desde http://localhost:9000/#/customer/1101/web-access me da dos errores

message
"ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'WHERE id=1101' at line 1"
name
"Error"
sql

"UPDATE account.user SET WHERE id=1101"

Parece que en el set falta el name = xx

desde salix http://localhost:9000/#/customer/1101/web-access cambio BruceWayne por BruceWayne4 y funciona, desde http://localhost:9000/#/customer/1101/web-access me da dos errores message : "ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'WHERE `id`=1101' at line 1" name : "Error" sql : "UPDATE `account`.`user` SET WHERE `id`=1101" Parece que en el set falta el name = xx

Corregido: 5e2c668fec

Corregido: 5e2c668fec

Yo lo he visto OK

Yo lo he visto OK
const { notify } = useNotify();
const { t } = useI18n();
const { validate } = useValidator();
const quasar = useQuasar();
const route = useRoute();
const stateStore = useStateStore();
const active = ref(false);
const canChangePassword = ref(0);
const email = ref(null);
const isLoading = ref(false);
const name = ref(null);
const usersPreviewRef = ref(null);
const user = ref([]);
const userPasswords = ref(0);
const dataChanges = computed(() => {
return (
user.value.active !== active.value ||
user.value.email !== email.value ||
user.value.name !== name.value
);
});
const filter = { where: { id: `${route.params.id}` } };
const showChangePasswordDialog = () => {
quasar.dialog({
component: CustomerChangePassword,
componentProps: {
id: route.params.id,
userPasswords: userPasswords.value,
promise: usersPreviewRef.value.fetch(),
},
});
};
const setInitialData = () => {
if (user.value.length) {
active.value = user.value[0].active;
email.value = user.value[0].email;
name.value = user.value[0].name;
}
};
const onSubmit = async () => {
isLoading.value = true;
const payload = {
active: active.value,
email: email.value,
name: name.value,
};
try {
await axios.patch(`Clients/${route.params.id}/updateUser`, payload);
notify('globals.dataSaved', 'positive');
if (usersPreviewRef.value) usersPreviewRef.value.fetch();
} catch (error) {
notify('errors.create', 'negative');
} finally {
isLoading.value = false;
}
};
</script>
<template>
<FormModel
<FetchData
:filter="filter"
:observe-form-changes="false"
:url-update="`Clients/${route.params.id}/updateUser`"
:url="'VnUsers/preview'"
model="client"
>
<template #form="{ data }">
<div
v-for="(item, index) in data"
:key="index"
:class="{
'q-mb-md': index < data.length - 1,
}"
>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QCheckbox
:label="t('Enable web access')"
v-model="item.active"
/>
</div>
</VnRow>
@on-fetch="
(data) => {
user = data;
setInitialData();
}
"
auto-load
ref="usersPreviewRef"
url="VnUsers/preview"
/>
<FetchData
:url="`Clients/${route.params.id}/hasCustomerRole`"
@on-fetch="(data) => (canChangePassword = data)"
auto-load
/>
<FetchData
@on-fetch="(data) => (userPasswords = data[0])"
auto-load
url="UserPasswords"
/>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('User')" v-model="item.name" />
</div>
<div class="col">
<VnInput :label="t('Recovery email')" v-model="item.email">
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
<QBtnGroup push class="q-gutter-x-sm">
<QBtn
:disabled="isLoading"
:label="t('globals.reset')"
:loading="isLoading"
@click="setInitialData"
color="primary"
flat
icon="restart_alt"
type="reset"
/>
<QBtn
:disabled="isLoading"
:label="t('Change password')"
:loading="isLoading"
@click.stop="showChangePasswordDialog()"
color="primary"
flat
icon="edit"
v-if="canChangePassword"
/>
<QBtn
:disabled="isLoading || !dataChanges"
:label="t('globals.save')"
:loading="isLoading"
@click="onSubmit"
color="primary"
icon="save"
/>
</QBtnGroup>
</Teleport>
<div class="full-width flex justify-center">
<QCard class="card-width q-pa-lg">
<QCardSection>
<QForm>
<QCheckbox :label="t('Enable web access')" v-model="active" />
<div class="q-px-sm">
<VnInput :label="t('User')" clearable v-model="name" />
<VnInput
:label="t('Recovery email')"
:rules="validate('client.email')"
clearable
type="email"

ubicar el mail bajo del usuario como en salix, solo hay 3 campos la lectura es mas facil

ubicar el mail bajo del usuario como en salix, solo hay 3 campos la lectura es mas facil

Corregido: 3bcd4984f5

Corregido: 3bcd4984f5

Lo veo OK

Lo veo OK
v-model="email"
class="q-mt-sm"
>
<template #append>
<QIcon name="info" class="cursor-pointer">
<QTooltip>{{
@ -54,10 +164,10 @@ const filter = { where: { id: `${route.params.id}` } };
</template>
</VnInput>
</div>
</VnRow>
</div>
</template>
</FormModel>
</QForm>
</QCardSection>
</QCard>
</div>
</template>
<i18n>
@ -66,4 +176,5 @@ es:
User: Usuario
Recovery email: Correo de recuperacion
This email is used for user to regain access their account: Este correo electrónico se usa para que el usuario recupere el acceso a su cuenta
Change password: Cambiar contraseña
</i18n>

View File

@ -0,0 +1,167 @@
<script setup>
jsegarra marked this conversation as resolved
Review

Usar el cliente 1101

Usar el cliente 1101
Review

Corrección: 08e072100b

Corrección: 08e072100b
Review

Los iconos de la columna estado, al hacer hover cambia el cursor induciendo a error al usuario porque parece que se pueda hacer una acción.
El tooltip de icono de la ultima columna( mas a la derecha), no corresponde. en salix es "confirmar transaccion" y en la PR es Transacción confirmada"

Los iconos de la columna estado, al hacer hover cambia el cursor induciendo a error al usuario porque parece que se pueda hacer una acción. El tooltip de icono de la ultima columna( mas a la derecha), no corresponde. en salix es "confirmar transaccion" y en la PR es Transacción confirmada"
Review

Corregido: bb10714824

Corregido: bb10714824
Review

Revisar

Revisar
import { computed, onBeforeMount, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import axios from 'axios';
import { toCurrency, toDateHourMinSec } from 'src/filters';
import FetchData from 'components/FetchData.vue';
import CustomerCloseIconTooltip from '../components/CustomerCloseIconTooltip.vue';
import CustomerCheckIconTooltip from '../components/CustomerCheckIconTooltip.vue';
const { t } = useI18n();
const route = useRoute();
const rows = ref([]);
const filter = {
include: [
{ relation: 'mandateType', scope: { fields: ['id', 'name'] } },
{ relation: 'company', scope: { fields: ['id', 'code'] } },
],
where: { clientFk: null },
order: ['created DESC'],
limit: 20,
};
const tableColumnComponents = {
state: {
component: CustomerCloseIconTooltip,
props: ({ row }) => ({ transaction: row }),
event: () => {},
},
id: {
component: 'span',
props: () => {},
event: () => {},
},
date: {
component: 'span',
props: () => {},
event: () => {},
},
amount: {
component: 'span',
props: () => {},
event: () => {},
},
validate: {
component: CustomerCheckIconTooltip,
props: ({ row }) => ({
transaction: row,
promise: refreshData,
}),
event: () => {},
},
};
const columns = computed(() => [
{
align: 'left',
field: '',
label: t('State'),
name: 'state',
},
{
align: 'left',
field: 'id',
label: t('Id'),
name: 'id',
},
{
align: 'left',
field: 'created',
label: t('Date'),
name: 'date',
format: (value) => toDateHourMinSec(value),
},
{
align: 'left',
field: 'amount',
label: t('Amount'),
name: 'amount',
format: (value) => toCurrency(value),
},
{
align: 'left',
field: '',
name: 'validate',
},
]);
onBeforeMount(() => {
getData(route.params.id);
});
watch(
() => route.params.id,
(newValue) => {
if (!newValue) return;
getData(newValue);
}
);
const getData = async (id) => {
filter.where.clientFk = id;
try {
const { data } = await axios.get('clients/transactions', {
params: { filter: JSON.stringify(filter) },
});
rows.value = data;
} catch (error) {
return error;
}
};
const refreshData = () => {
getData(route.params.id);
};
</script>
<template>
<div class="full-width flex justify-center">
<QPage class="card-width q-pa-lg">
<QTable
:columns="columns"
:pagination="{ rowsPerPage: 12 }"
:rows="rows"
class="full-width q-mt-md"
row-key="id"
v-if="rows?.length"
>
<template #body-cell="props">
<QTd :props="props">
<QTr :props="props">
<component
:is="tableColumnComponents[props.col.name].component"
@click="
tableColumnComponents[props.col.name].event(props)
"
class="rounded-borders q-pa-sm"
v-bind="
tableColumnComponents[props.col.name].props(props)
"
>
{{ props.value }}
</component>
</QTr>
</QTd>
</template>
</QTable>
<h5 class="flex justify-center color-vn-label" v-else>
{{ t('globals.noResults') }}
</h5>
</QPage>
</div>
</template>
<i18n>
es:
State: Estado
Id: Id
Date: Fecha
Amount: Importe
</i18n>

View File

@ -11,20 +11,8 @@ import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
const { t } = useI18n();
const newClientForm = reactive({
const initialData = reactive({
active: true,
name: null,
salesPersonFk: null,
businessTypeFk: null,
fi: null,
socialName: null,
street: null,
postcode: null,
city: null,
provinceFk: null,
countryFk: null,
userName: null,
email: null,
isEqualizated: false,
});
@ -55,7 +43,7 @@ function handleLocation(data, location) {
<QPage>
<VnSubToolbar />
<FormModel
:form-initial-data="newClientForm"
:form-initial-data="initialData"
model="client"
url-create="Clients/createWithUser"
>
@ -94,7 +82,7 @@ function handleLocation(data, location) {
<div class="col">
<QInput
:label="t('Business name')"
:rules="validate('Client.socialName')"
:rules="validate('client.socialName')"
v-model="data.socialName"
/>
</div>
@ -103,7 +91,7 @@ function handleLocation(data, location) {
<div class="col">
<QInput
:label="t('Street')"
:rules="validate('Client.street')"
:rules="validate('client.street')"
v-model="data.street"
/>
</div>
@ -128,12 +116,26 @@ function handleLocation(data, location) {
<QInput v-model="data.userName" :label="t('Web user')" />
</div>
<div class="col">
<QInput v-model="data.email" :label="t('Email')" />
<QInput
:label="t('Email')"
:rules="validate('client.email')"
clearable
type="email"
v-model="data.email"
>
<template #append>
<QIcon name="info" class="cursor-info">
<QTooltip max-width="400px">{{
t('customer.basicData.youCanSaveMultipleEmails')
}}</QTooltip>
</QIcon>
</template>
</QInput>
</div>
</VnRow>
<QCheckbox
:label="t('Is equalizated')"
v-model="newClientForm.isEqualizated"
v-model="initialData.isEqualizated"
/>
</template>
</FormModel>

View File

@ -1,56 +1,52 @@
<script setup>
jsegarra marked this conversation as resolved
Review

En el formulario de crear customer o basic-data, hemos encontrado que el campo email permite añadir cualquier formato. Propuesta, añadir type="email"
Este campo email, tampoco tiene el icono de info

En el formulario de crear customer o basic-data, hemos encontrado que el campo email permite añadir cualquier formato. Propuesta, añadir type="email" Este campo email, tampoco tiene el icono de info
Review

Corregido: 3cf3687b84

Corregido: 3cf3687b84
Review

El formulario de crear customer no tiene el mismo formato que en Salix

El formulario de crear customer no tiene el mismo formato que en Salix
Review

Corregido: 17a0afda73

Corregido: 17a0afda73
import { ref, computed, onBeforeMount } from 'vue';
import { ref, computed } from 'vue';
jsegarra marked this conversation as resolved
Review

En customer/1112/summary, en el componente descriptor tenemos:

  1. Falta el icono de Sin acceso web
  2. Riesgo debería tener un icono de información.
  3. Faltan los botones de hacer nueva compra y añadir usuario.
  4. Los 3 puntos con las acciones

En esa misma ruta pero en la parte central, no se visualiza la tabla de "Últimos tickets"

En customer/1112/summary, en el componente descriptor tenemos: 1. Falta el icono de Sin acceso web 2. Riesgo debería tener un icono de información. 3. Faltan los botones de hacer nueva compra y añadir usuario. 4. Los 3 puntos con las acciones En esa misma ruta pero en la parte central, no se visualiza la tabla de "Últimos tickets"
Review

Corregido: 17a0afda73

Corregido: 17a0afda73
Review

El icono de información de riesgo, no lo veo añadido...es correcto?

El icono de información de riesgo, no lo veo añadido...es correcto?
Review

Se agrega icono faltante: d0cf13d0af

Se agrega icono faltante: d0cf13d0af
Review

Riesgo está repetido 2 veces. En uno está el icono y el otro no. quitar el que corresponda

Riesgo está repetido 2 veces. En uno está el icono y el otro no. quitar el que corresponda
import { useI18n } from 'vue-i18n';
jsegarra marked this conversation as resolved
Review

En el formulario de basic-data:
El campo cliente anterior no aparece

En el formulario de basic-data: El campo cliente anterior no aparece
Review

Corregido: 3cf3687b84

Corregido: 3cf3687b84
import { QBtn, QCheckbox, useQuasar } from 'quasar';
jsegarra marked this conversation as resolved
Review

En fiscal-data, los campos: razón social, vies y recargo de equivalencia deberían tener el icono de info.

En billing-data, iban tampoco tiene.

En fiscal-data, los campos: razón social, vies y recargo de equivalencia deberían tener el icono de info. En billing-data, iban tampoco tiene.
Review

Corregido: 11eb597fa3

Corregido: 11eb597fa3
Review

Carlos, en el comentario anterior indicaba sugerencias para fiscal-data y billing-data.
Billing-data, el campo IBAN sigue sin tener icono

Carlos, en el comentario anterior indicaba sugerencias para fiscal-data y billing-data. Billing-data, el campo IBAN sigue sin tener icono
Review

No se hace ninguna corrección, se hace el commit: 6bf9b7c0c8 solo deshaciendo los cambios que se hicieron en el commit 11eb597fa3, quedando exactamente igual que antes del comentario.

No se hace ninguna corrección, se hace el commit: 6bf9b7c0c8 solo deshaciendo los cambios que se hicieron en el commit 11eb597fa3, quedando exactamente igual que antes del comentario.
import { toCurrency, toDate } from 'filters/index';
Review

Revisar los campos de los formularios de datos básicos, datos fiscales, formas de pago y consignatarios porque hay muchos campos que no tienen añadida la funcionalidad de clearable.
No pongo el nombre de cada campo de cada vista porque, como digo, son muchos.

Revisar los campos de los formularios de datos básicos, datos fiscales, formas de pago y consignatarios porque hay muchos campos que no tienen añadida la funcionalidad de clearable. No pongo el nombre de cada campo de cada vista porque, como digo, son muchos.
Review

Corregidos todos los de Customer: 7f1c4690e8

Corregidos todos los de Customer: 7f1c4690e8
Review

Basic-data: comercialm canal de contacto, tipo de negocio. Son desplegables y otro campos similares si que tienen esa acción.
Habria que revisar porque en VnSelectAction/VnSelectCreate/VnSelectDialog lo añadimos pero no funciona, pero creo que esa pelota no está en vuestro tejado.

Basic-data: comercialm canal de contacto, tipo de negocio. Son desplegables y otro campos similares si que tienen esa acción. Habria que revisar porque en VnSelectAction/VnSelectCreate/VnSelectDialog lo añadimos pero no funciona, pero creo que esa pelota no está en vuestro tejado.
import { useArrayData } from 'composables/useArrayData';
import { useStateStore } from 'stores/useStateStore';
import FetchData from 'components/FetchData.vue';
import CustomerNotificationsFilter from './CustomerDefaulterFilter.vue';
import CustomerBalanceDueTotal from './CustomerBalanceDueTotal.vue';
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import VnInput from 'src/components/common/VnInput.vue';
import CustomerDefaulterAddObservation from './CustomerDefaulterAddObservation.vue';
const { t } = useI18n();
const stateStore = useStateStore();
const quasar = useQuasar();
const arrayData = ref(null);
const balanceDueTotal = ref(0);
const customerId = ref(0);
const selected = ref([]);
const workerId = ref(0);
const rows = computed(() => arrayData.value.store.data);
const rows = ref([]);
const tableColumnComponents = {
client: {
component: QBtn,
props: () => ({ flat: true, color: 'blue' }),
event: ({ row }) => selectCustomerId(row.clientFk),
props: () => ({ flat: true, color: 'blue', noCaps: true }),
event: () => {},
},
isWorker: {
component: QCheckbox,
props: ({ row }) => ({
props: (prop) => ({
disable: true,

http://localhost:9000/#/customer/1101/balance campo isWorker aunque en la BBDD este marcado no se marca el checkBox.

http://localhost:9000/#/customer/1101/balance campo isWorker aunque en la BBDD este marcado no se marca el checkBox.

Corregido: c844d26ccb

Corregido: c844d26ccb

Yo lo he visto OK

Yo lo he visto OK
'model-value': Boolean(row.selected),
'model-value': Boolean(prop.value),
}),
event: () => {},

En salix las columnas tienen un tooltip y son ordenables
ademas se tiene que ajustar al ancho de la pantalla sin scroll lateral.

En salix las columnas tienen un tooltip y son ordenables ademas se tiene que ajustar al ancho de la pantalla sin scroll lateral.

Corregido: 5ff90d127b

Corregido: 5ff90d127b

Yo lo he visto OK, en cuanto al numero de columnas con y sin tooltip

Yo lo he visto OK, en cuanto al numero de columnas con y sin tooltip
},
salesperson: {
salesPerson: {
component: QBtn,
props: () => ({ flat: true, color: 'blue' }),
event: ({ row }) => selectWorkerId(row.salesPersonFk),
props: () => ({ flat: true, color: 'blue', noCaps: true }),
event: () => {},
},
country: {
component: 'span',
props: () => {},
event: () => {},
},
paymentMethod: {
payMethod: {
component: 'span',
props: () => {},
event: () => {},
@ -62,8 +58,8 @@ const tableColumnComponents = {
},
author: {
component: QBtn,
props: () => ({ flat: true, color: 'blue' }),
event: ({ row }) => selectWorkerId(row.workerFk),
props: () => ({ flat: true, color: 'blue', noCaps: true }),
event: () => {},
},
lastObservation: {
component: 'span',
@ -93,6 +89,7 @@ const columns = computed(() => [
field: 'clientName',
label: t('Client'),
name: 'client',
sortable: true,
},
{
align: 'left',
@ -104,85 +101,77 @@ const columns = computed(() => [
align: 'left',
field: 'salesPersonName',
label: t('Salesperson'),
name: 'salesperson',
name: 'salesPerson',
sortable: true,
},
{
align: 'left',
field: 'country',
label: t('Country'),
name: 'country',
sortable: true,
},
{
align: 'left',
field: 'payMethod',
label: t('P. Method'),
name: 'paymentMethod',
name: 'payMethod',
sortable: true,
tooltip: t('Pay method'),
},
{
align: 'left',
field: ({ amount }) => toCurrency(amount),
label: t('Balance D.'),
name: 'balance',
sortable: true,
tooltip: t('Balance due'),
},
{
align: 'left',
field: 'workerName',
label: t('Author'),
name: 'author',
sortable: true,
tooltip: t('Worker who made the last observation'),
},
{
align: 'left',
field: 'observation',
label: t('Last observation'),
name: 'lastObservation',
sortable: true,
},
{
align: 'left',
field: ({ created }) => toDate(created),
label: t('L. O. Date'),
name: 'date',
sortable: true,
tooltip: t('Last observation date'),
},
{
align: 'left',
field: ({ creditInsurance }) => toCurrency(creditInsurance),
label: t('Credit I.'),
name: 'credit',
sortable: true,
tooltip: t('Credit insurance'),
},
{
align: 'left',
field: ({ defaulterSinced }) => toDate(defaulterSinced),
label: t('From'),
name: 'from',
sortable: true,
},
]);
onBeforeMount(() => {
getArrayData();
});
const getArrayData = async () => {
arrayData.value = useArrayData('CustomerDefaulter', {
url: 'Defaulters/filter',
limit: 0,
});
await arrayData.value.fetch({ append: false });
balanceDueTotal.value = arrayData.value.store.data.reduce(
(accumulator, currentValue) => {
return accumulator + (currentValue['amount'] || 0);
},
0
);
stateStore.rightDrawer = true;
};
const selectCustomerId = (id) => {
workerId.value = 0;
customerId.value = id;
};
const selectWorkerId = (id) => {
customerId.value = 0;
workerId.value = id;
const setRows = (data) => {
rows.value = data;
balanceDueTotal.value = data.reduce((accumulator, currentValue) => {
return accumulator + (currentValue['amount'] || 0);
}, 0);
};
const viewAddObservation = (rowsSelected) => {
@ -196,12 +185,21 @@ const viewAddObservation = (rowsSelected) => {
};
const refreshData = () => {
getArrayData();
setRows();
};
const onFetch = (data) => {
for (const element of data) {
element.isWorker = element.businessTypeFk === 'worker';
}
rows.value = data;
};
</script>
<template>
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<FetchData :filter="filter" @on-fetch="onFetch" auto-load url="Defaulters/filter" />
<QDrawer side="right" :width="256" show-if-above>
<QScrollArea class="fit text-grey-8">
<CustomerNotificationsFilter data-key="CustomerDefaulter" />
</QScrollArea>
@ -230,6 +228,18 @@ const refreshData = () => {
selection="multiple"
v-model:selected="selected"
>
<template #header="props">
<QTr :props="props" class="bg">
<QTh>
<QCheckbox v-model="props.selected" />
</QTh>
<QTh v-for="col in props.cols" :key="col.name" :props="props">
{{ t(col.label) }}
<QTooltip v-if="col.tooltip">{{ col.tooltip }}</QTooltip>
</QTh>
</QTr>
</template>
<template #body-cell="props">
<QTd :props="props">
<QTr :props="props" class="cursor-pointer">
@ -239,10 +249,29 @@ const refreshData = () => {
v-bind="tableColumnComponents[props.col.name].props(props)"
@click="tableColumnComponents[props.col.name].event(props)"
>
{{ props.value }}
<template v-if="props.col.name !== 'isWorker'">
<div v-if="props.col.name === 'lastObservation'">
<VnInput
type="textarea"
v-model="props.value"
autogrow
/>
</div>
<div v-else>{{ props.value }}</div>
</template>
<WorkerDescriptorProxy v-if="workerId" :id="workerId" />
<CustomerDescriptorProxy v-else :id="customerId" />
<WorkerDescriptorProxy
:id="props.row.salesPersonFk"
v-if="props.col.name === 'salesPerson'"
/>
<WorkerDescriptorProxy
:id="props.row.workerFk"
v-if="props.col.name === 'author'"
/>
<CustomerDescriptorProxy
:id="props.row.clientFk"
v-if="props.col.name === 'client'"
/>
</component>
</QTr>
</QTd>
@ -265,10 +294,15 @@ es:
Salesperson: Comercial
Country: País
P. Method: F. Pago
Pay method: Forma de pago
Balance D.: Saldo V.
Balance due: Saldo vencido
Author: Autor
Worker who made the last observation: Trabajador que ha realizado la última observación
Last observation: Última observación
L. O. Date: Fecha Ú. O.
Last observation date: Fecha última observación
Credit I.: Crédito A.
Credit insurance: Crédito asegurado
From: Desde
</i18n>

View File

@ -3,7 +3,9 @@ import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import axios from 'axios';
import { useQuasar } from 'quasar';
import { useDialogPluginComponent } from 'quasar';
import useNotify from 'src/composables/useNotify';
import VnRow from 'components/ui/VnRow.vue';
@ -18,8 +20,9 @@ const $props = defineProps({
},
});
const { dialogRef } = useDialogPluginComponent();
const { notify } = useNotify();
const { t } = useI18n();
const quasar = useQuasar();
const newObservation = ref(null);
@ -38,15 +41,9 @@ const onSubmit = async () => {
await $props.promise();
quasar.notify({
message: t('globals.dataSaved'),
type: 'positive',
});
notify('globals.dataSaved', 'positive');
} catch (error) {
quasar.notify({
message: t(`${error.message}`),
type: 'negative',
});
notify(error.message, 'negative');
}
};
</script>

View File

@ -119,6 +119,7 @@ const authors = ref();
<QItemSection>
<VnInput
:label="t('P. Method')"
clearable
is-outlined
v-model="params.paymentMethod"
/>
@ -129,6 +130,7 @@ const authors = ref();
<QItemSection>
<VnInput
:label="t('Balance D.')"
clearable
is-outlined
v-model="params.balance"
/>
@ -160,7 +162,12 @@ 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')"
clearable
is-outlined
v-model="params.date"
/>
</QItemSection>
</QItem>
@ -168,6 +175,7 @@ const authors = ref();
<QItemSection>
<VnInput
:label="t('Credit I.')"
clearable
is-outlined
v-model="params.credit"
/>

View File

@ -1,32 +1,17 @@
<script setup>
import { ref, computed, onBeforeMount } from 'vue';
import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { QBtn } from 'quasar';
import { useArrayData } from 'composables/useArrayData';
import { useStateStore } from 'stores/useStateStore';
import FetchData from 'components/FetchData.vue';
import CustomerNotificationsFilter from './CustomerNotificationsFilter.vue';
import CustomerDescriptorProxy from '../Card/CustomerDescriptorProxy.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
const { t } = useI18n();
const stateStore = useStateStore();
const arrayData = ref(null);
onBeforeMount(async () => {
arrayData.value = useArrayData('CustomerNotifications', {
url: 'Clients',
limit: 0,
});
await arrayData.value.fetch({ append: false });
stateStore.rightDrawer = true;
});
const rows = computed(() => arrayData.value.store.data);
const rows = ref([]);
const selected = ref([]);
const selectedCustomerId = ref(0);
@ -97,7 +82,14 @@ const selectCustomerId = (id) => {
</script>
<template>
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<FetchData
:filter="filter"
@on-fetch="(data) => (rows = data)"
auto-load
url="Clients"
/>
<QDrawer side="right" :width="256" show-if-above>
<QScrollArea class="fit text-grey-8">
<CustomerNotificationsFilter data-key="CustomerNotifications" />
</QScrollArea>

View File

@ -40,6 +40,7 @@ const clients = ref();
<QItemSection>
<VnInput
:label="t('Identifier')"
clearable
is-outlined
v-model="params.identifier"
/>
@ -96,13 +97,24 @@ const clients = ref();
<QItem class="q-mb-sm">
<QItemSection>
<VnInput :label="t('Phone')" is-outlined v-model="params.phone" />
<VnInput
:label="t('Phone')"
clearable
is-outlined
v-model="params.phone"
/>
</QItemSection>
</QItem>
<QItem class="q-mb-sm">
<QItemSection>
<VnInput :label="t('Email')" is-outlined v-model="params.email" />
<VnInput
:label="t('Email')"
clearable
is-outlined
type="email"
v-model="params.email"
/>
</QItemSection>
</QItem>
<QSeparator />

View File

@ -41,9 +41,9 @@ const refreshData = () => {
getCustomsAgents();
};
const toCustomerConsignees = () => {
const toCustomerAddress = () => {
router.push({
name: 'CustomerConsignees',
name: 'CustomerAddress',
params: {
id: route.params.id,
},
@ -70,84 +70,78 @@ function handleLocation(data, location) {
:form-initial-data="formInitialData"
:observe-form-changes="false"
:url-create="urlCreate"
@on-data-saved="toCustomerConsignees()"
@on-data-saved="toCustomerAddress()"
model="client"
>
<template #moreActions>
<QBtn
:label="t('globals.cancel')"
@click="toCustomerAddress"
color="primary"
flat
icon="close"
/>
</template>
<template #form="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QCheckbox :label="t('Default')" v-model="data.isDefaultAddress" />
</div>
</VnRow>
<QCheckbox :label="t('Default')" v-model="data.isDefaultAddress" />
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('Consignee')" v-model="data.nickname" />
</div>
<div class="col">
<VnInput :label="t('Street address')" v-model="data.street" />
</div>
<VnInput :label="t('Consignee')" clearable v-model="data.nickname" />
<VnInput :label="t('Street address')" clearable v-model="data.street" />
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnLocation
:rules="validate('Worker.postcode')"
:roles-allowed-to-create="['deliveryAssistant']"
:options="postcodesOptions"
v-model="data.location"
@update:model-value="(location) => handleLocation(data, location)"
></VnLocation>
</div>
</VnRow>
<VnLocation
:rules="validate('Worker.postcode')"
:roles-allowed-to-create="['deliveryAssistant']"
:options="postcodesOptions"
v-model="data.location"
@update:model-value="(location) => handleLocation(data, location)"
/>
<div class="row justify-between q-gutter-md q-mb-md">
<VnSelectFilter
:label="t('Agency')"
jsegarra marked this conversation as resolved Outdated

El layout debe tener(salto de linea = 1 fila)
1 checkbox
2 inputs
El componente VnLocation ocupa toda la fila
3 inputs
1 inputs y un input con el botón de crear

El layout debe tener(salto de linea = 1 fila) 1 checkbox 2 inputs El componente VnLocation ocupa toda la fila 3 inputs 1 inputs y un input con el botón de crear

Corregido: 38d3d49ecb

Corregido: 38d3d49ecb
:options="agencyModes"
:rules="validate('route.agencyFk')"
hide-selected
option-label="name"
option-value="id"
v-model="data.agencyModeFk"
class="col"
/>
<VnInput class="col" :label="t('Phone')" clearable v-model="data.phone" />
<VnInput
class="col"
:label="t('Mobile')"
clearable
v-model="data.mobile"
/>
</div>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('Agency')"
:options="agencyModes"
hide-selected
option-label="name"
option-value="id"
v-model="data.agencyModeFk"
/>
</div>
</VnRow>
<VnSelectFilter
:label="t('Incoterms')"
:options="incoterms"
hide-selected
option-label="name"
option-value="code"
v-model="data.incotermsFk"
/>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('Phone')" v-model="data.phone" />
</div>
<div class="col">
<VnInput :label="t('Mobile')" v-model="data.mobile" />
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('Incoterms')"
:options="incoterms"
hide-selected
option-label="name"
option-value="code"
v-model="data.incotermsFk"
/>
</div>
<div class="col">
<VnSelectDialog
:label="t('Customs agent')"
:options="customsAgents"
hide-selected
option-label="fiscalName"
option-value="id"
v-model="data.customsAgentFk"
>
<template #form>
<CustomerNewCustomsAgent @on-data-saved="refreshData()" />
</template>
</VnSelectDialog>
</div>
<VnSelectDialog
:label="t('Customs agent')"
:options="customsAgents"
hide-selected
option-label="fiscalName"
option-value="id"
v-model="data.customsAgentFk"
>
<template #form>
<CustomerNewCustomsAgent @on-data-saved="refreshData()" />
</template>
</VnSelectDialog>
</VnRow>
</template>
</FormModel>

View File

@ -1,7 +1,7 @@
<script setup>
import { onBeforeMount, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { useRoute, useRouter } from 'vue-router';
import axios from 'axios';
import VnLocation from 'src/components/common/VnLocation.vue';
@ -16,6 +16,7 @@ import CustomsNewCustomsAgent from 'src/pages/Customer/components/CustomerNewCus
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const urlUpdate = ref('');
const postcodesOptions = ref([]);
@ -24,9 +25,10 @@ const incoterms = ref([]);
const customsAgents = ref([]);
const observationTypes = ref([]);
const notes = ref([]);
const deletes = ref([]);
onBeforeMount(() => {
urlUpdate.value = `Clients/${route.params.id}/updateAddress/${route.params.consigneeId}`;
urlUpdate.value = `Clients/${route.params.id}/updateAddress/${route.params.addressId}`;
});
const getData = async (observations) => {
@ -35,7 +37,7 @@ const getData = async (observations) => {
if (observationTypes.value.length) {
const filter = {
fields: ['id', 'addressFk', 'observationTypeFk', 'description'],
where: { addressFk: `${route.params.consigneeId}` },
where: { addressFk: `${route.params.addressId}` },
};
const { data } = await axios.get('AddressObservations', {
params: { filter: JSON.stringify(filter) },
@ -52,8 +54,9 @@ const getData = async (observations) => {
$isNew: false,
$oldData: null,
$orgIndex: null,
addressFk: `${route.params.consigneeId}`,
addressFk: `${route.params.addressId}`,
description: observation.description,
id: observation.id,
observationTypeFk: type.id,
}
: null;
@ -68,21 +71,39 @@ const addNote = () => {
$isNew: true,
$oldData: null,
$orgIndex: null,
addressFk: `${route.params.consigneeId}`,
addressFk: `${route.params.addressId}`,
description: '',
observationTypeFk: '',
});
};
const deleteNote = (index) => {
const deleteNote = (id, index) => {
deletes.value.push(id);
notes.value.splice(index, 1);
};
const onDataSaved = () => {
const payload = {
creates: notes.value,
};
axios.post('AddressObservations/crud', payload);
const onDataSaved = async () => {
let payload = {};
const creates = notes.value.filter((note) => note.$isNew);
if (creates.length) {
payload.creates = creates;
}
if (deletes.value.length) {
payload.deletes = deletes.value;
}
await axios.post('AddressObservations/crud', payload);
notes.value = [];
deletes.value = [];
toCustomerAddress();
};

http://localhost:9000/#/customer/6/address/259/edit
se añade una primera observacion en itemPicker funciona bien, al añadir una segunda en Delivery dice que no se puede repetir. Esto mismo en salix sí funciona.

http://localhost:9000/#/customer/6/address/259/edit se añade una primera observacion en itemPicker funciona bien, al añadir una segunda en Delivery dice que no se puede repetir. Esto mismo en salix sí funciona.

Corregido: 2c70a73b8e

Corregido: 2c70a73b8e

Yo he probado a guardar Packager, delivery e ItemPicker y ha guardado resultados.
Yo lo veo OK

Yo he probado a guardar Packager, delivery e ItemPicker y ha guardado resultados. Yo lo veo OK
const toCustomerAddress = () => {
router.push({
name: 'CustomerAddress',
params: {
id: route.params.id,
},
});
};
function handleLocation(data, location) {
const { town, code, provinceFk, countryFk } = location ?? {};
@ -110,11 +131,21 @@ function handleLocation(data, location) {
<FormModel
:observe-form-changes="false"
:url-update="urlUpdate"
:url="`Addresses/${route.params.consigneeId}`"
:url="`Addresses/${route.params.addressId}`"
@on-data-saved="onDataSaved()"
auto-load
model="client"
>
<template #moreActions>
<QBtn
:label="t('globals.cancel')"
@click="toCustomerAddress"
color="primary"
flat
icon="close"
/>
</template>
<template #form="{ data, validate }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
@ -136,10 +167,10 @@ function handleLocation(data, location) {
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('Consignee')" v-model="data.nickname" />
<VnInput :label="t('Consignee')" clearable v-model="data.nickname" />
</div>
<div class="col">
<VnInput :label="t('Street address')" v-model="data.street" />
<VnInput :label="t('Street')" clearable v-model="data.street" />
</div>
</VnRow>
@ -153,27 +184,22 @@ function handleLocation(data, location) {
@update:model-value="(location) => handleLocation(data, location)"
></VnLocation>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('Agency')"
:options="agencyModes"
:rules="validate('route.agencyFk')"
hide-selected
option-label="name"
option-value="id"
v-model="data.agencyModeFk"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('Phone')" v-model="data.phone" />
<VnInput :label="t('Phone')" clearable v-model="data.phone" />
</div>
<div class="col">
<VnInput :label="t('Mobile')" v-model="data.mobile" />
<VnInput :label="t('Mobile')" clearable v-model="data.mobile" />
</div>
</VnRow>
@ -221,18 +247,23 @@ function handleLocation(data, location) {
/>
</div>
<div class="col">
<VnInput :label="t('Description')" v-model="note.description" />
<VnInput
:label="t('Description')"
:rules="validate('route.description')"
clearable
v-model="note.description"
/>
</div>
<div class="flex items-center">
<QIcon
@click.stop="deleteNote(index)"
@click.stop="deleteNote(note.id, index)"
class="cursor-pointer"
color="primary"
name="delete"
size="sm"
>
<QTooltip>
{{ t('Remove') }}
{{ t('Remove note') }}
</QTooltip>
</QIcon>
</div>
@ -265,7 +296,7 @@ es:
Is equalizated: Recargo de equivalencia
Is Loginflora allowed: Compra directa en Holanda
Consignee: Consignatario
Street address: Dirección postal
Street: Dirección fiscal
Postcode: Código postal
City: Población
Province: Provincia

View File

@ -0,0 +1,140 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import axios from 'axios';
import { useDialogPluginComponent } from 'quasar';
import useNotify from 'src/composables/useNotify';
import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
const { dialogRef } = useDialogPluginComponent();
const { notify } = useNotify();
const { t } = useI18n();
const $props = defineProps({
id: {
type: String,
required: true,
},
userPasswords: {
type: Object,
required: true,
},
promise: {
type: Function,
required: true,
},
});
const closeButton = ref(null);
const isLoading = ref(false);
const newPassword = ref('');
const requestPassword = ref('');
const onSubmit = async () => {
isLoading.value = true;
if (newPassword.value !== requestPassword.value) {
notify(t("Passwords don't match"), 'negative');
isLoading.value = false;
return;
}
const payload = {
newPassword: newPassword.value,
};
try {
await axios.patch(`Clients/${$props.id}/setPassword`, payload);
await $props.promise();
} catch (error) {
notify('errors.create', 'negative');
} finally {
isLoading.value = false;
if (closeButton.value) closeButton.value.click();
}
};
</script>
<template>
<QDialog ref="dialogRef">
<QCard class="q-pa-lg">
<QCardSection>
<QForm @submit.prevent="onSubmit">
<span
ref="closeButton"
class="row justify-end close-icon"
v-close-popup
>
<QIcon name="close" size="sm" />
</span>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('New password')"
clearable
v-model="newPassword"
type="password"
>
<template #append>
<QIcon name="info" class="cursor-info">
<QTooltip>
{{
t('customer.card.passwordRequirements', {
length: $props.userPasswords.length,
nAlpha: $props.userPasswords.nAlpha,
nDigits: $props.userPasswords.nDigits,
nPunct: $props.userPasswords.nPunct,
nUpper: $props.userPasswords.nUpper,
})
}}
</QTooltip>
</QIcon>
</template>
</VnInput>
</div>
<div class="col">
<VnInput
:label="t('Request password')"
clearable
v-model="requestPassword"
type="password"
/>
</div>
</VnRow>
<div class="q-mt-lg row justify-end">
<QBtn
:disabled="isLoading"
:label="t('globals.cancel')"
:loading="isLoading"
class="q-ml-sm"
color="primary"
flat
type="reset"
v-close-popup
/>
<QBtn
:disabled="isLoading"
:label="t('Change password')"
:loading="isLoading"
color="primary"
type="submit"
/>
</div>
</QForm>
</QCardSection>
</QCard>
</QDialog>
</template>
<i18n>
es:
New password: Nueva contraseña
Request password: Repetir contraseña
Change password: Cambiar contraseña
Passwords don't match: Las contraseñas no coinciden
</i18n>

View File

@ -0,0 +1,47 @@
<script setup>
import { useI18n } from 'vue-i18n';
import axios from 'axios';
const { t } = useI18n();
const $props = defineProps({
transaction: {
type: Object,
required: true,
},
promise: {
type: Function,
required: true,
},
});
const setClientsConfirmTransaction = async () => {
try {
const payload = { id: $props.transaction.id };
await axios.post('Clients/confirmTransaction', payload);
$props.promise();
} catch (error) {
return error;
}
};
</script>
<template>
<div v-if="!$props?.transaction?.isConfirmed">
<QIcon
@click.stop="setClientsConfirmTransaction"
color="primary"
name="done_all"
size="sm"
jsegarra marked this conversation as resolved
Review

Reemplazzar check por done_all que es el nombre del icono que está en Salix

Reemplazzar check por done_all que es el nombre del icono que está en Salix
Review

Corregido: 86199d8197

Corregido: 86199d8197
class="cursor-pointer"
>
<QTooltip>{{ t('Confirm transaction') }}</QTooltip>
</QIcon>
</div>
</template>
<i18n>
es:
Confirm transaction: Confirmar transacción
</i18n>

View File

@ -0,0 +1,48 @@
<script setup>
import { onBeforeMount, ref } from 'vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const tooltip = ref('');
const $props = defineProps({
transaction: {
type: Object,
required: true,
},
});
onBeforeMount(() => {
const errorMessage = $props.transaction.errorMessage
? $props.transaction.errorMessage
: '';
const responseMessage = $props.transaction.responseMessage
? $props.transaction.responseMessage
: '';
tooltip.value = `${errorMessage} ${responseMessage}`;
});
</script>
<template>
<div
v-if="
($props.transaction.errorMessage || $props.transaction.responseMessage) &&
!$props.transaction.isConfirmed
"
>
<QIcon color="negative" name="close" size="sm">
<QTooltip>{{ tooltip }}</QTooltip>
</QIcon>
</div>
<div v-if="$props.transaction.isConfirmed">
<QIcon color="positive" name="check" size="sm">
<QTooltip>{{ t('Confirmed') }}</QTooltip>
</QIcon>
</div>
</template>
<i18n>
es:
Confirmed: Confirmada
</i18n>

View File

@ -0,0 +1,72 @@
<script setup>
jsegarra marked this conversation as resolved
Review

En customer/1112/credits , el descriptor de la columna empleado, no está cogiendo el código del empleado sino del cliente.
Para el mismo cliente, en salix, está llamando a /api/Workers/100

En customer/1112/credits , el descriptor de la columna empleado, no está cogiendo el código del empleado sino del cliente. Para el mismo cliente, en salix, está llamando a /api/Workers/100
Review

Corregido: 11eb597fa3

Corregido: 11eb597fa3
import { reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const initialData = reactive({
clientFK: Number(route.params.id),
});
const toCustomerCreditContracts = () => {
router.push({ name: 'CustomerCreditContracts' });
};
</script>
<template>
<FormModel
:form-initial-data="initialData"
:observe-form-changes="false"
url-create="creditClassifications/createWithInsurance"
jsegarra marked this conversation as resolved
Review

Por defecto, el valor es true, así que creo que no es necesario definirla

Por defecto, el valor es true, así que creo que no es necesario definirla
Review

Corregido: 38d3d49ecb

Corregido: 38d3d49ecb
@on-data-saved="toCustomerCreditContracts()"
>
<template #moreActions>
<QBtn
:label="t('globals.cancel')"
jsegarra marked this conversation as resolved Outdated

Los campos nos son clearables

Los campos nos son clearables

Corregido: 7f1c4690e8

Corregido: 7f1c4690e8
@click="toCustomerCreditContracts"
color="primary"
flat
icon="close"
/>
</template>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Credit')"
clearable
type="number"
v-model.number="data.credit"
/>
</div>
<div class="col">
<VnInput
:label="t('Grade')"
clearable
type="number"
v-model.number="data.grade"
/>
</div>
<div class="col">
<VnInputDate :label="t('Since')" v-model="data.started" />
</div>
</VnRow>
</template>
</FormModel>
</template>
<i18n>
es:
Credit: Crédito
Grade: Grade
Since: Desde
</i18n>

View File

@ -0,0 +1,107 @@
<script setup>
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { toCurrency, toDateHourMinSec } from 'src/filters';
import FetchData from 'components/FetchData.vue';
const { t } = useI18n();
const route = useRoute();
const rows = ref([]);
const filter = {
where: {
creditClassificationFk: `${route.params.creditId}`,
},
limit: 20,
};
const tableColumnComponents = {
created: {
component: 'span',
props: () => {},
event: () => {},
},
grade: {
component: 'span',
props: () => {},
event: () => {},
},
credit: {
component: 'span',
props: () => {},
event: () => {},
},
};
const columns = computed(() => [
{
align: 'left',
field: 'created',
format: (value) => toDateHourMinSec(value),
label: t('Created'),
name: 'created',
},
{
align: 'left',
field: 'grade',
label: t('Grade'),
name: 'grade',
},
{
align: 'left',
field: 'credit',
format: (value) => toCurrency(value),
label: t('Credit'),
name: 'credit',
},
]);
</script>
<template>
<FetchData
:filter="filter"
@on-fetch="(data) => (rows = data)"
auto-load
url="CreditInsurances"
/>
<QPage class="column items-center q-pa-md" v-if="rows.length">
<QTable
:columns="columns"
:pagination="{ rowsPerPage: 12 }"
:rows="rows"
class="full-width q-mt-md"
row-key="id"
>
<template #body-cell="props">
<QTd :props="props">
<QTr :props="props" class="cursor-pointer">
<component
:is="tableColumnComponents[props.col.name].component"
class="col-content"
v-bind="tableColumnComponents[props.col.name].props(props)"
@click="tableColumnComponents[props.col.name].event(props)"
>
{{ props.value }}
</component>
</QTr>
</QTd>
</template>
</QTable>
</QPage>
<h5 class="flex justify-center color-vn-label" v-else>
{{ t('globals.noResults') }}
</h5>
</template>
<i18n>
es:
Created: Fecha creación
Grade: Grade
Credit: Crédito
</i18n>

View File

@ -1,18 +1,21 @@
<script setup>
import { reactive } from 'vue';
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const initialData = reactive({
credit: null,
});
const initialData = ref({});
const setClient = (data) => {
console.log(data.credit);
initialData.value.credit = data.credit;
};
const toCustomerCredits = () => {
router.push({
@ -25,22 +28,36 @@ const toCustomerCredits = () => {
</script>
<template>
<FetchData
:filter="filter"
@on-fetch="setClient"
auto-load
:url="`Clients/${route.params.id}/getCard`"
/>
<FormModel
:form-initial-data="initialData"
:observe-form-changes="false"
:url-update="`/Clients/${route.params.id}`"
@on-data-saved="toCustomerCredits()"
>
<template #moreActions>
<QBtn
:label="t('globals.cancel')"
@click="toCustomerCredits"
color="primary"
flat
icon="close"
/>
</template>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
:label="t('Credit')"
type="number"
v-model.number="data.credit"
/>
</div>
</VnRow>
<QInput
:label="t('Credit')"
clearable
type="number"
v-model.number="data.credit"
/>
</template>
</FormModel>
</template>

View File

@ -0,0 +1,96 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import { useQuasar } from 'quasar';
import { downloadFile } from 'src/composables/downloadFile';
import CustomerFileManagementDelete from 'src/pages/Customer/components/CustomerFileManagementDelete.vue';
const { t } = useI18n();
const quasar = useQuasar();
const route = useRoute();
const router = useRouter();
const $props = defineProps({
id: {
type: Number,
required: true,
},
promise: {
type: Function,
required: true,
},
});
const setDownloadFile = () => downloadFile($props.id);
const toCustomerFileManagementEdit = () => {
router.push({
name: 'CustomerFileManagementEdit',
params: {
id: route.params.id,
dmsId: $props.id,
},
});
};
const showCustomerFileManagementDeleteDialog = () => {
quasar.dialog({
component: CustomerFileManagementDelete,
componentProps: {
id: $props.id,
promise: setData,
},
});
};
const setData = () => {
$props.promise();
};
</script>
<template>
<div>
<QIcon
@click.stop="setDownloadFile"
color="primary"
name="cloud_download"
size="sm"
>
<QTooltip>
{{ t('actionFile', { action: t('globals.download') }) }}
</QTooltip>
</QIcon>
<QIcon
@click.stop="toCustomerFileManagementEdit"
class="q-ml-md"
color="primary"
name="edit"
size="sm"
>
<QTooltip>
{{ t('actionFile', { action: t('globals.edit') }) }}
</QTooltip>
</QIcon>
<QIcon
@click.stop="showCustomerFileManagementDeleteDialog"
class="q-ml-md"
color="primary"
name="delete"
size="sm"
>
<QTooltip>
{{ t('actionFile', { action: t('globals.remove') }) }}
</QTooltip>
</QIcon>
</div>
</template>
<i18n>
en:
jsegarra marked this conversation as resolved Outdated

TIP: quizás usando traducciones con argumentos nos podemos ahorrar 2 líneas

TIP: quizás usando traducciones con argumentos nos podemos ahorrar 2 líneas

Corregido: 1e9b3f8f0e

Corregido: 1e9b3f8f0e
actionFile: '{action} file'
es:
actionFile: '{action} fichero'
</i18n>

View File

@ -0,0 +1,260 @@
<script setup>
import { onBeforeMount, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import axios from 'axios';
import { useState } from 'src/composables/useState';
import { useValidator } from 'src/composables/useValidator';
import useNotify from 'src/composables/useNotify';
import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import VnInput from 'src/components/common/VnInput.vue';
const { notify } = useNotify();
const { t } = useI18n();
const { validate } = useValidator();
const route = useRoute();
const router = useRouter();
const state = useState();
const user = state.getUser();
const filterFindOne = { where: { code: 'paymentsLaw' } };
const filterCompanies = { order: ['code'] };
const filterWarehouses = { order: ['name'] };
const inputFileRef = ref();
const client = ref({});
const findOne = ref([]);
const allowedContentTypes = ref([]);
const optionsCompanies = ref([]);
const optionsWarehouses = ref([]);
const optionsDmsTypes = ref([]);
const isLoading = ref(false);
const dms = ref({
hasFile: false,
});
onBeforeMount(() => {
const { companyFk, warehouseFk } = user.value;
dms.value.reference = route.params.id;
dms.value.companyId = companyFk;
dms.value.warehouseId = warehouseFk;
});
watch([client, findOne], ([newClient, newFindOne]) => {
dms.value.description = t('clientFileDescription', {
dmsTypeName: newFindOne.name?.toUpperCase(),
clientName: newClient.name?.toUpperCase(),
clientId: newClient.id,
});
dms.value.dmsTypeId = newFindOne.id;
});
const saveData = async () => {
try {
const formData = new FormData();
const files = dms.value.files;
if (files && files.length > 0) {
jsegarra marked this conversation as resolved Outdated

Comentario, no es nuestro estilo, hay otras instrucciones para hacer lo mismo pero con menos código.

Comentario, no es nuestro estilo, hay otras instrucciones para hacer lo mismo pero con menos código.

Aunque no es su estilo, la repetición de código en varios archivos me intrigó. Viendo espacio para mejora, investigué su origen y hallé varias instancias. Curioso para ser 'no característico' de su enfoque, ¿no?

Corregido: 38d3d49ecb

Aunque no es su estilo, la repetición de código en varios archivos me intrigó. Viendo espacio para mejora, investigué su origen y hallé varias instancias. Curioso para ser 'no característico' de su enfoque, ¿no? Corregido: 38d3d49ecb

Fantástico Carlos, tomaré tu commit como ejemplo para nuevos casos y cuando surja la oportunidad cambiar las otras ocurrencias!

Fantástico Carlos, tomaré tu commit como ejemplo para nuevos casos y cuando surja la oportunidad cambiar las otras ocurrencias!
for (let file of files) {
formData.append(file.name, file);
}
dms.value.hasFileAttached = true;
const url = `clients/${route.params.id}/uploadFile`;
await axios.post(url, formData, {
params: dms.value,
});
notify('globals.dataSaved', 'positive');
toCustomerFileManagement();
}
} catch (error) {
notify(error.message, 'negative');
}
};
const toCustomerFileManagement = () => {
router.push({ name: 'CustomerFileManagement' });
};
</script>
<template>
<fetch-data
@on-fetch="(data) => (client = data)"
auto-load
:url="`Clients/${route.params.id}/getCard`"
/>
<fetch-data
:filter="filterFindOne"
@on-fetch="(data) => (findOne = data)"
auto-load
url="DmsTypes/findOne"
/>
<fetch-data
@on-fetch="(data) => (allowedContentTypes = data)"
auto-load
url="DmsContainers/allowedContentTypes"
/>
<fetch-data
:filter="filterCompanies"
@on-fetch="(data) => (optionsCompanies = data)"
auto-load
url="Companies"
/>
<fetch-data
:filter="filterWarehouses"
@on-fetch="(data) => (optionsWarehouses = data)"
auto-load
url="Warehouses"
/>
<fetch-data
:filter="filterWarehouses"
@on-fetch="(data) => (optionsDmsTypes = data)"
auto-load
url="DmsTypes"
/>
<Teleport to="#st-actions">
<QBtnGroup push class="q-gutter-x-sm">
<QBtn
:disabled="isLoading"
:label="t('globals.cancel')"
:loading="isLoading"
@click="toCustomerFileManagement"
color="primary"
flat
icon="close"
/>
<QBtn
:disabled="isLoading"
:label="t('globals.save')"
:loading="isLoading"
@click.stop="saveData"
color="primary"
icon="save"
/>
</QBtnGroup>
</Teleport>
<QCard class="q-pa-lg">
<QCardSection>
<QForm>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Reference')"
clearable
v-model="dms.reference"
/>
</div>
<div class="col">
<VnSelectFilter
:label="t('Company')"
:options="optionsCompanies"
:rules="validate('entry.companyFk')"
option-label="code"
option-value="id"
v-model="dms.companyId"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('Warehouse')"
:options="optionsWarehouses"
option-label="name"
option-value="id"
v-model="dms.warehouseId"
/>
</div>
<div class="col">
<VnSelectFilter
:label="t('Type')"
:options="optionsDmsTypes"
option-label="name"
option-value="id"
v-model="dms.dmsTypeId"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Description')"
:rules="validate('route.description')"
clearable
type="textarea"
v-model="dms.description"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QFile
ref="inputFileRef"
class="required"
:label="t('File')"
v-model="dms.files"
multiple
:accept="allowedContentTypes.join(',')"
clearable
clear-icon="close"
>
<template #append>
<QBtn
icon="vn:attach"
flat
round
padding="xs"
@click="inputFileRef.pickFiles()"
>
<QTooltip>
{{ t('Select a file') }}
</QTooltip>
</QBtn>
<QBtn icon="info" flat round padding="xs">
<QTooltip max-width="30rem">
{{
`${t(
'Allowed content types'
)}: ${allowedContentTypes.join(', ')}`
}}
</QTooltip>
</QBtn>
</template>
</QFile>
</div>
</VnRow>
<QCheckbox
:label="t('Generate identifier for original file')"
v-model="dms.hasFile"
/>
</QForm>
</QCardSection>
</QCard>
</template>
<i18n>
en:
clientFileDescription: '{dmsTypeName} FROM CLIENT {clientName} ID {clientId}'
es:
Reference: Referencia
Company: Empresa
Warehouse: Almacén
Type: Tipo
Description: Descripción
clientFileDescription: '{dmsTypeName} DEL CLIENTE {clientName} ID {clientId}'
File: Fichero
Select a file: Selecciona un fichero
Allowed content types: Tipos de archivo permitidos
Generate identifier for original file: Generar identificador para archivo original
</i18n>

View File

@ -0,0 +1,82 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useDialogPluginComponent } from 'quasar';
import axios from 'axios';
import useNotify from 'src/composables/useNotify';
const $props = defineProps({
id: {
type: Number,
required: true,
},
promise: {
type: Function,
required: true,
},
});
const { dialogRef } = useDialogPluginComponent();
const { notify } = useNotify();
const { t } = useI18n();
const closeButton = ref(null);
const isLoading = ref(false);
const deleteDms = async () => {
isLoading.value = true;
try {
await axios.post(`ClientDms/${$props.id}/removeFile`);
if ($props.promise) await $props.promise();
notify('globals.dataDeleted', 'positive');
} catch (error) {
notify(error.message, 'negative');
} finally {
closeButton.value.click();
isLoading.value = false;
}
};
</script>
<template>
<QDialog ref="dialogRef">
<QCard class="q-pa-md q-mb-md">
<span ref="closeButton" class="row justify-end close-icon" v-close-popup>
<QIcon name="close" size="sm" />
</span>
<QCardSection>
<div class="mt-1 text-h6">{{ t('This file will be deleted') }}</div>
<div>{{ t('Are you sure you want to continue?') }}</div>
</QCardSection>
<QCardActions class="flex justify-end">
<QBtn
:disabled="isLoading"
:label="t('globals.cancel')"
:loading="isLoading"
class="q-mr-xl"
color="primary"
flat
v-close-popup
/>
<QBtn
:disabled="isLoading"
:label="t('globals.save')"
:loading="isLoading"
@click.stop="deleteDms"
color="primary"
/>
</QCardActions>
</QCard>
</QDialog>
</template>
<i18n>
es:
This file will be deleted: Este fichero va a ser borrado
Are you sure you want to continue?: ¿Seguro que quieres continuar?
</i18n>

View File

@ -0,0 +1,237 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import axios from 'axios';
import { useValidator } from 'src/composables/useValidator';
import useNotify from 'src/composables/useNotify';
import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import VnInput from 'src/components/common/VnInput.vue';
const { notify } = useNotify();
const { t } = useI18n();
const { validate } = useValidator();
const route = useRoute();
const router = useRouter();
const filterCompanies = { order: ['code'] };
const filterWarehouses = { order: ['name'] };
const inputFileRef = ref();
const allowedContentTypes = ref([]);
const optionsCompanies = ref([]);
const optionsWarehouses = ref([]);
const optionsDmsTypes = ref([]);
const isLoading = ref(false);
const dms = ref({
hasFile: true,
});
const setCurrentDms = (data) => {
dms.value.reference = data.reference;
dms.value.companyId = data.companyFk;
dms.value.warehouseId = data.warehouseFk;
dms.value.dmsTypeId = data.dmsTypeFk;
dms.value.description = data.description;
};
const saveData = async () => {
try {
const formData = new FormData();
const files = dms.value.files;
if (files && files.length > 0) {
for (let file of files) {
formData.append(file.name, file);
}
dms.value.hasFileAttached = true;
const url = `dms/${route.params.dmsId}/updateFile`;
await axios.post(url, formData, {
params: dms.value,
});
notify('globals.dataSaved', 'positive');
toCustomerFileManagement();
}
} catch (error) {
notify(error.message, 'negative');
}
};
const toCustomerFileManagement = () => {
router.push({ name: 'CustomerFileManagement' });
};
</script>
<template>
<fetch-data :url="`Dms/${route.params.dmsId}`" @on-fetch="setCurrentDms" auto-load />
<fetch-data
@on-fetch="(data) => (allowedContentTypes = data)"
auto-load
url="DmsContainers/allowedContentTypes"
/>
<fetch-data
:filter="filterCompanies"
@on-fetch="(data) => (optionsCompanies = data)"
auto-load
url="Companies"
/>
<fetch-data
:filter="filterWarehouses"
@on-fetch="(data) => (optionsWarehouses = data)"
auto-load
url="Warehouses"
/>
<fetch-data
:filter="filterWarehouses"
@on-fetch="(data) => (optionsDmsTypes = data)"
auto-load
url="DmsTypes"
/>
<Teleport to="#st-actions">
<QBtnGroup push class="q-gutter-x-sm">
<QBtn
:disabled="isLoading"
:label="t('globals.cancel')"
:loading="isLoading"
@click="toCustomerFileManagement"
color="primary"
flat
icon="close"
/>
<QBtn
:disabled="isLoading"
:label="t('globals.save')"
:loading="isLoading"
@click.stop="saveData"
color="primary"
icon="save"
/>
</QBtnGroup>
</Teleport>
<QCard class="q-pa-lg">
<QCardSection>
<QForm>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Reference')"
clearable
v-model="dms.reference"
/>
</div>
<div class="col">
<VnSelectFilter
:label="t('Company')"
:options="optionsCompanies"
:rules="validate('entry.companyFk')"
option-label="code"
option-value="id"
v-model="dms.companyId"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('Warehouse')"
:options="optionsWarehouses"
option-label="name"
option-value="id"
v-model="dms.warehouseId"
/>
</div>
<div class="col">
<VnSelectFilter
:label="t('Type')"
:options="optionsDmsTypes"
option-label="name"
option-value="id"
v-model="dms.dmsTypeId"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Description')"
:rules="validate('route.description')"
clearable
type="textarea"
v-model="dms.description"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QFile
ref="inputFileRef"
class="required"
:label="t('File')"
v-model="dms.files"
multiple
:accept="allowedContentTypes.join(',')"
clearable
clear-icon="close"
>
<template #append>
<QBtn
icon="vn:attach"
flat
round
padding="xs"
@click="inputFileRef.pickFiles()"
>
<QTooltip>
{{ t('Select a file') }}
</QTooltip>
</QBtn>
<QBtn icon="info" flat round padding="xs">
<QTooltip max-width="30rem">
{{
`${t(
'Allowed content types'
)}: ${allowedContentTypes.join(', ')}`
}}
</QTooltip>
</QBtn>
</template>
</QFile>
</div>
</VnRow>
<QCheckbox
:label="t('Generate identifier for original file')"
v-model="dms.hasFile"
disable
/>
</QForm>
</QCardSection>
</QCard>
</template>
<i18n>
en:
clientFileDescription: '{dmsTypeName} FROM CLIENT {clientName} ID {clientId}'
es:
Reference: Referencia
Company: Empresa
Warehouse: Almacén
Type: Tipo
Description: Descripción
clientFileDescription: '{dmsTypeName} DEL CLIENTE {clientName} ID {clientId}'
File: Fichero
Select a file: Selecciona un fichero
Allowed content types: Tipos de archivo permitidos
Generate identifier for original file: Generar identificador para archivo original
</i18n>

View File

@ -15,9 +15,6 @@ const route = useRoute();
const router = useRouter();
const initialData = reactive({
amount: null,
description: null,
greugeTypeFk: null,
shipped: '2001-01-01T11:00:00.000Z',
});
@ -47,10 +44,25 @@ const toCustomerGreuges = () => {
model="client"
url-create="Greuges"
>
<template #moreActions>
<QBtn
:label="t('globals.cancel')"
@click="toCustomerGreuges"
color="primary"
flat
icon="close"
/>
</template>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('Amount')" type="number" v-model="data.amount" />
<VnInput
:label="t('Amount')"
clearable
type="number"
v-model="data.amount"
/>
</div>
<div class="col">
<VnInputDate :label="t('Date')" v-model="data.shipped" />
@ -59,7 +71,7 @@ const toCustomerGreuges = () => {
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('Comment')" v-model="data.description" />
<VnInput :label="t('Comment')" clearable v-model="data.description" />
</div>
<div class="col">
<VnSelectFilter

View File

@ -1,5 +1,5 @@
<script setup>
import { reactive, ref } from 'vue';
import { reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import VnRow from 'components/ui/VnRow.vue';
@ -10,13 +10,6 @@ const emit = defineEmits(['onDataSaved']);
const { t } = useI18n();
const initialData = reactive({
nif: null,
fiscalName: null,
street: null,
phone: null,
});
const onDataSaved = (dataSaved) => {
emit('onDataSaved', dataSaved);
};
@ -25,30 +18,35 @@ const onDataSaved = (dataSaved) => {
<template>
<FormModelPopup
:title="t('New customs agent')"
url-create="CustomsAgents"
model="customer"
:form-initial-data="initialData"
@on-data-saved="onDataSaved($event)"
model="customer"
url-create="CustomsAgents"
>
<template #form-inputs="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('NIF')" :required="true" v-model="data.nif" />
<VnInput
:label="t('NIF')"
:required="true"
clearable
v-model="data.nif"
/>
</div>
<div class="col">
<VnInput
:label="t('Fiscal name')"
:required="true"
clearable
v-model="data.fiscalName"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('Street')" v-model="data.street" />
<VnInput :label="t('Street')" clearable v-model="data.street" />
</div>
<div class="col">
<VnInput :label="t('Phone')" v-model="data.phone" />
<VnInput :label="t('Phone')" clearable v-model="data.phone" />
</div>
</VnRow>
</template>

View File

@ -3,6 +3,8 @@ import { onBeforeMount, reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { useDialogPluginComponent } from 'quasar';
import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
@ -12,6 +14,7 @@ import VnInput from 'src/components/common/VnInput.vue';
const { t } = useI18n();
const route = useRoute();
const { dialogRef } = useDialogPluginComponent();
const $props = defineProps({
companyId: {
@ -49,19 +52,15 @@ const filterBanks = {
const filterClientFindOne = {
fields: ['email'],
where: {
id: `${route.params.id}`,
id: route.params.id,
},
};
const initialData = reactive({
amountPaid: $props.totalCredit,
bankFk: null,
clientFk: `${route.params.id}`,
clientFk: route.params.id,
companyFk: $props.companyId,
compensationAccount: null,
description: null,
email: clientFindOne.value.email,
payed: null,
});
onBeforeMount(() => {
@ -128,14 +127,12 @@ const onDataSaved = async () => {
:url-create="urlCreate"
@on-data-saved="onDataSaved()"
>
<template #form="{ data }">
<template #form="{ data, validate }">
<span ref="closeButton" class="row justify-end close-icon" v-close-popup>
<QIcon name="close" size="sm" />
</span>
<h5 class="flex justify-center q-mt-xs">
{{ t('New payment') }}
</h5>
<h5 class="q-mt-none">{{ t('New payment') }}</h5>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
@ -150,6 +147,7 @@ const onDataSaved = async () => {
:label="t('Company')"
:options="companyOptions"
:required="true"
:rules="validate('entry.companyFk')"
hide-selected
option-label="code"
option-value="id"
@ -186,6 +184,7 @@ const onDataSaved = async () => {
:label="t('Amount')"
:required="true"
@update:model-value="calculateFromAmount($event)"
clearable
type="number"
v-model.number="data.amountPaid"
/>
@ -200,6 +199,7 @@ const onDataSaved = async () => {
<div class="col" v-if="data.bankFk === 3 || data.bankFk === 3117">
<VnInput
:label="t('Compensation account')"
clearable
v-model="data.compensationAccount"
/>
</div>
@ -207,6 +207,7 @@ const onDataSaved = async () => {
<VnInput
:label="t('Reference')"
:required="true"
clearable
v-model="data.description"
/>
</div>
@ -220,6 +221,7 @@ const onDataSaved = async () => {
<VnInput
:label="t('Delivered amount')"
@update:model-value="calculateFromDeliveredAmount($event)"
clearable
type="number"
v-model="deliveredAmount"
/>
@ -227,6 +229,7 @@ const onDataSaved = async () => {
<div class="col">
<VnInput
:label="t('Amount to return')"
clearable
disable
type="number"
v-model="amountToReturn"

View File

@ -10,9 +10,7 @@ const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const initialData = reactive({
text: null,
});
const initialData = reactive({});
onMounted(() => {
initialData.clientFk = `${route.params.id}`;
@ -35,6 +33,16 @@ const toCustomerNotes = () => {
@on-data-saved="toCustomerNotes()"
url-create="ClientObservations"
>
<template #moreActions>
jsegarra marked this conversation as resolved
Review

@alexm VnNotes??

@alexm VnNotes??
<QBtn
:label="t('globals.cancel')"
@click="toCustomerNotes"
color="primary"
flat
icon="close"
/>
</template>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">

View File

@ -14,9 +14,6 @@ const router = useRouter();
const initialData = reactive({
started: '2001-01-01T11:00:00.000Z',
finished: null,
amount: null,
period: null,
});
onMounted(() => {
@ -41,6 +38,16 @@ const toCustomerRecoveries = () => {
model="client"
url-create="Recoveries"
>
<template #moreActions>
<QBtn
:label="t('globals.cancel')"
@click="toCustomerRecoveries"
color="primary"
flat
icon="close"
/>
</template>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
@ -53,10 +60,20 @@ const toCustomerRecoveries = () => {
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput :label="t('Amount')" type="number" v-model="data.amount" />
<VnInput
:label="t('Amount')"
clearable
type="number"
v-model="data.amount"
/>
</div>
<div class="col">
<VnInput :label="t('Period')" type="number" v-model="data.period" />
<VnInput
:label="t('Period')"
clearable
type="number"
v-model="data.period"
/>
</div>
</VnRow>
</template>

View File

@ -0,0 +1,385 @@
<script setup>
Review

Revisar fecha de enviado y worker descriptor
En el create, revisar clearable

Revisar fecha de enviado y worker descriptor En el create, revisar clearable
Review

Corregido: 7f1c4690e8

Corregido: 7f1c4690e8
Review

Aparece el botón de vista previa, sin embargo, el formato del layout no es el estándar

Aparece el botón de vista previa, sin embargo, el formato del layout no es el estándar
Review

Corregido: ebd1ee07f4

Corregido: ebd1ee07f4
Review

@alexm @jgallego El funcionamiento del botón de restaurar, es correcto? cuando le pulsas, te elimina los campos del destinatario y responder a. Esta funcionalidad no está en Salix, por lo que quedaría por validar

@alexm @jgallego El funcionamiento del botón de restaurar, es correcto? cuando le pulsas, te elimina los campos del destinatario y responder a. Esta funcionalidad no está en Salix, por lo que quedaría por validar
Review

Restaurar debe dejar el formulario como estaba cuando el usuario lo abrió, no se si con esto respondo a la pregunta.

Restaurar debe dejar el formulario como estaba cuando el usuario lo abrió, no se si con esto respondo a la pregunta.
Review

@cfonseca Revisar restaurar en Consignees(customer/1101/adrress/create porque el campo Ubicación nose refresca.

Antes si que iba bien, pero trayéndote los cambios se ha quedado por validar esto.

Hay 4 ocurrencias de ese mismo componente, revisa las 4 y volvemos a revisar, ok?

@cfonseca Revisar restaurar en Consignees(customer/1101/adrress/create porque el campo Ubicación nose refresca. Antes si que iba bien, pero trayéndote los cambios se ha quedado por validar esto. Hay 4 ocurrencias de ese mismo componente, revisa las 4 y volvemos a revisar, ok?
Review

Sigue sin ir del todo fino, adjunto video por el grupo.

Sigue sin ir del todo fino, adjunto video por el grupo.
Review

Corregido: 5ff90d127b

Corregido: 5ff90d127b
Review

Hay veces que los botones desaparecen, cambias de sección y sale error.
Siempre me sale en vista previa, el documento de Incoterms

Hay veces que los botones desaparecen, cambias de sección y sale error. Siempre me sale en vista previa, el documento de Incoterms
Review

Problema de error al cambiar sección y desaparición de botones resuelto.
Commit: 87f376464b

Se fixea la vista previa del documento.
Commit: ec2604b9ab

Problema de error al cambiar sección y desaparición de botones resuelto. Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/87f376464b7da1309d1ce2fb98adc1d043458570 Se fixea la vista previa del documento. Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/ec2604b9abf3e8d6ee137e2e3f61491078c29456
import { onBeforeMount, reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import axios from 'axios';
import { useQuasar } from 'quasar';
import { useState } from 'src/composables/useState';
import { useValidator } from 'src/composables/useValidator';
import useNotify from 'src/composables/useNotify';
import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import CustomerSamplesPreview from 'src/pages/Customer/components/CustomerSamplesPreview.vue';
import { useStateStore } from 'stores/useStateStore';
const { notify } = useNotify();
const { t } = useI18n();
const { validate } = useValidator();
const quasar = useQuasar();
const route = useRoute();
const router = useRouter();
const state = useState();
const user = state.getUser();
const stateStore = useStateStore();
const client = ref({});
const hasChanged = ref(false);
const isLoading = ref(false);
const optionsClientsAddressess = ref([]);
const optionsCompanies = ref([]);
const optionsEmailUsers = ref([]);
const optionsSamplesVisible = ref([]);
const sampleType = ref({ hasPreview: false });
const filterEmailUsers = { where: { userFk: user.value.id } };
const filterClientsAddresses = {
include: [
{ relation: 'province', scope: { fields: ['name'] } },
{ relation: 'agencyMode', scope: { fields: ['name'] } },
],
};
const filterCompanies = { order: ['code'] };
const filterSamplesVisible = {
fields: [
'id',
'code',
'description',
'model',
'hasCompany',
'hasAddress',
'hasPreview',
'datepickerEnabled',
],
order: ['description'],
};
const initialData = reactive({});
onBeforeMount(async () => {
const { data } = await axios.get(`Clients/1/getCard`);
client.value = data;
initialData.clientFk = route.params?.id;
initialData.recipient = data.email;
initialData.recipientId = data.id;
});
const setEmailUser = (data) => {
optionsEmailUsers.value = data;
initialData.replyTo = data[0]?.email;
};
const setClientsAddresses = (data) => {
initialData.addressId = data[0].id;
optionsClientsAddressess.value = data;
};
const setSampleType = (sampleId) => {
hasChanged.value = true;
const { companyFk } = user.value;
sampleType.value = optionsSamplesVisible.value.find((option) => {
return option.id === sampleId;
});
initialData.companyFk = companyFk;
initialData.companyId = companyFk;
};
const setInitialData = () => {
hasChanged.value = false;
initialData.addressId = optionsClientsAddressess.value[0].id;
initialData.companyFk = null;
initialData.from = null;
initialData.recipient = client.value.email;
initialData.recipientId = client.value.id;
initialData.replyTo = optionsEmailUsers.value[0]?.email;
initialData.typeFk = '';
sampleType.value = {};
};
const validateMessage = () => {
if (!initialData.recipient) return 'Email cannot be blank';
if (!sampleType.value) return 'Choose a sample';
if (sampleType.value.hasCompany && !initialData.companyFk) return 'Choose a company';
if (sampleType.value.datepickerEnabled && !initialData.from) return 'Choose a date';
return '';
};
const setParams = (params) => {
if (sampleType.value.hasCompany) params.companyId = initialData.companyFk;

abrir como en salix que es mas ancho, mas similar a un dina4 que es lo que intuitivamente espera el usuario

abrir como en salix que es mas ancho, mas similar a un dina4 que es lo que intuitivamente espera el usuario

Corregido: 90ee50eab5

Corregido: 90ee50eab5

Yo lo veo OK

Yo lo veo OK
if (sampleType.value.datepickerEnabled) params.from = initialData.from;
if (initialData.addressId) params.addressId = initialData.addressId;
};
jsegarra marked this conversation as resolved Outdated

No hay botón de vista previa

No hay botón de vista previa

Corregido: d0e98010a5

Corregido: d0e98010a5

Aparece el botón de vista previa, sin embargo, el formato del layout no es el estándar

Aparece el botón de vista previa, sin embargo, el formato del layout no es el estándar

Corregido: ebd1ee07f4

Corregido: ebd1ee07f4

Revisar ya que el boton de vista previa está activo a pesar de no tener seleccionada la plantilla.
Si haces F5, el campo plantilla, por defecto no está seleccionada y el boton desactivado, eso está OK.

Revisar ya que el boton de vista previa está activo a pesar de no tener seleccionada la plantilla. Si haces F5, el campo plantilla, por defecto no está seleccionada y el boton desactivado, eso está OK.
const getPreview = async () => {
jsegarra marked this conversation as resolved Outdated

Cuando se muestra el campo dirección,. no muestra el nombre del consignatario sino otro valor
La label del campo debería ser consignatario
¿Debería tener el icono del lápiz para editar dirección? @alexm

Cuando se muestra el campo dirección,. no muestra el nombre del consignatario sino otro valor La label del campo debería ser consignatario ¿Debería tener el icono del lápiz para editar dirección? @alexm

Corrección: 08e072100b

Corrección: 08e072100b
try {
const params = {
recipientId: route.params.id,
};
const validationMessage = validateMessage();
if (validationMessage) return notify(t(validationMessage), 'negative');
setParams(params);
const path = `${sampleType.value.model}/${route.params.id}/${sampleType.value.code}-html`;
const { data } = await axios.get(path, { params });
if (!data) return;
quasar.dialog({
component: CustomerSamplesPreview,
componentProps: {
htmlContent: data,
},
});
} catch (err) {
notify('Errors getting preview', 'negative');
}
};
const onSubmit = async () => {
isLoading.value = true;
try {
const { data } = await axios.patch('ClientSamples', initialData);
notify('globals.dataSaved', 'positive');
onDataSaved(data);
} catch (error) {
notify('errors.create', 'negative');
} finally {
isLoading.value = false;
}
};
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,
});
toCustomerSamples();
};
const updateModelValue = () => {
hasChanged.value = true;
};
const toCustomerSamples = () => {
router.push({ name: 'CustomerSamples' });
};
</script>
<template>
<fetch-data
:filter="filterEmailUsers"
@on-fetch="setEmailUser"
auto-load
url="EmailUsers"
/>
<fetch-data
:filter="filterClientsAddresses"
:url="`Clients/${route.params.id}/addresses`"
@on-fetch="setClientsAddresses"
auto-load
/>
<fetch-data
:filter="filterCompanies"
@on-fetch="(data) => (optionsCompanies = data)"
auto-load
url="Companies"
/>
<fetch-data
:filter="filterSamplesVisible"
@on-fetch="(data) => (optionsSamplesVisible = data)"
auto-load
url="Samples/visible"
/>
<Teleport v-if="stateStore?.isSubToolbarShown()" to="#st-actions">
<QBtnGroup push class="q-gutter-x-sm">
<QBtn
:label="t('globals.cancel')"
@click="toCustomerSamples"
color="primary"
flat
icon="close"
/>
<QBtn
:disabled="isLoading || !sampleType?.hasPreview"
:label="t('Preview')"
:loading="isLoading"
@click.stop="getPreview()"
color="primary"
flat
icon="preview"
/>
<QBtn
:disabled="!hasChanged"
:label="t('globals.reset')"
:loading="isLoading"
@click="setInitialData"
color="primary"
flat
icon="restart_alt"
type="reset"
/>
<QBtn
:disabled="!hasChanged"
:label="t('globals.save')"
:loading="isLoading"
@click="onSubmit"
color="primary"
icon="save"
/>
</QBtnGroup>
</Teleport>
<div class="full-width flex justify-center">
<QCard class="card-width q-pa-lg">
<QForm>
<div class="col">
<VnSelectFilter
:label="t('Sample')"
:options="optionsSamplesVisible"
@update:model-value="setSampleType"
hide-selected
option-label="description"
option-value="id"
v-model="initialData.typeFk"
/>
</div>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
:label="t('Recipient')"
@update:model-value="updateModelValue"
clearable
required="true"
v-model="initialData.recipient"
>
<template #append>
<QIcon name="info" class="cursor-pointer">
<QTooltip>{{
t('Its only used when sample is sent')
}}</QTooltip>
</QIcon>
</template>
</VnInput>
</div>
<div class="col">
<VnInput
:label="t('Reply to')"
@update:model-value="updateModelValue"
clearable
required="true"
v-model="initialData.replyTo"
>
<template #append>
<QIcon name="info" class="cursor-pointer">
<QTooltip>{{
t('To who should the recipient replay?')
}}</QTooltip>
</QIcon>
</template>
</VnInput>
</div>
</VnRow>
<VnRow
class="row q-gutter-md q-mb-md"
v-if="sampleType?.hasCompany || sampleType?.datepickerEnabled"
>
<div class="col">
<VnSelectFilter
:label="t('Company')"
:options="optionsCompanies"
:rules="validate('entry.companyFk')"
hide-selected
option-label="code"
option-value="id"
required="true"
v-model="initialData.companyFk"
v-if="sampleType.hasCompany"
/>
</div>
<div class="col">
<VnSelectFilter
:label="t('Address')"
:options="optionsClientsAddressess"
hide-selected
option-label="nickname"
option-value="id"
required="true"
v-model="initialData.addressId"
v-if="sampleType.id === 20"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{
`${scope.opt.nickname}, ${scope.opt.street}, ${scope.opt.city}, ${scope.opt.province.name} - ${scope.opt.agencyMode.name}`
}}
</QItemLabel>
</QItemSection>
</QItem>
</template>
<template #append>
<QIcon name="edit" class="cursor-pointer">
<QTooltip>{{ t('Edit address') }}</QTooltip>
</QIcon>
</template>
</VnSelectFilter>
</div>
</VnRow>
<VnRow
class="row q-gutter-md q-mb-md"
v-if="sampleType?.datepickerEnabled"
>
<div class="col">
<VnInputDate
:label="t('Since')"
required="true"
v-model="initialData.from"
/>
</div>
</VnRow>
</QForm>
</QCard>
</div>
</template>
<i18n>
es:
Sample: Plantilla
Recipient: Destinatario
Reply to: Responder a
Company: Empresa
Address: Dirección
Since: Desde
Its only used when sample is sent: Se utiliza únicamente cuando se envía la plantilla
To who should the recipient replay?: ¿A quien debería responder el destinatario?
Edit address: Editar dirección
Preview: Vista previa
Email cannot be blank: Debes introducir un email
Choose a sample: Selecciona una plantilla
Choose a company: Selecciona una empresa
Choose a date: Selecciona una fecha
</i18n>

View File

@ -0,0 +1,34 @@
<script setup>
import { ref } from 'vue';
import { useDialogPluginComponent } from 'quasar';
const { dialogRef } = useDialogPluginComponent();
const closeButton = ref(null);
const $props = defineProps({
htmlContent: {
type: String,
required: true,
},
});
</script>
<template>
<QDialog ref="dialogRef" full-width>
<QCard class="dialog-width q-pa-lg">
<QCardSection>
<span ref="closeButton" class="row justify-end close-icon" v-close-popup>
<QIcon name="close" size="sm" />
</span>
<div v-html="$props.htmlContent" />
</QCardSection>
</QCard>
</QDialog>
</template>
<style lang="scss" scoped>
.dialog-width {
max-width: 900px !important;
}
</style>

View File

@ -0,0 +1,247 @@
<script setup>
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import { QBtn, date } from 'quasar';
import { toCurrency } from 'src/filters';
import FetchData from 'components/FetchData.vue';
import CustomerSummaryTableActions from './CustomerSummaryTableActions.vue';
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
import RouteDescriptorProxy from 'src/pages/Route/Card/RouteDescriptorProxy.vue';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const rows = ref([]);
const filter = {
include: [
{
relation: 'ticketState',
scope: {
fields: ['stateFk', 'code', 'alertLevel'],
include: { relation: 'state' },
},
},
{ relation: 'invoiceOut', scope: { fields: ['id'] } },
{ relation: 'agencyMode', scope: { fields: ['name'] } },
],
where: { clientFk: route.params.id },
order: ['shipped DESC', 'id'],
limit: 10,
};
const tableColumnComponents = {
id: {
component: 'span',
props: () => {},
event: () => {},
},
nickname: {
component: QBtn,
props: () => ({ flat: true, color: 'blue', noCaps: true }),
event: () => {},
},
agency: {
component: 'span',
props: () => {},
event: () => {},
},
route: {
component: QBtn,
props: () => ({ flat: true, color: 'blue' }),
event: () => {},
},
packages: {
component: 'span',
props: () => {},
event: () => {},
},
date: {
component: 'span',
props: () => {},
event: () => {},
},
state: {
component: 'span',
props: () => {},
event: () => {},
},
total: {
component: 'span',
props: () => {},
event: () => {},
},
actions: {
component: CustomerSummaryTableActions,
props: (prop) => ({
id: prop.row.id,
}),
event: () => {},
},
};
const columns = computed(() => [
{
align: 'left',
field: 'id',
label: t('Id'),
name: 'id',
},
{
align: 'left',
field: 'nickname',
label: t('Nickname'),
name: 'nickname',
},
{
align: 'left',
field: (row) => row?.agencyMode?.name,
label: t('Agency'),
name: 'agency',
},
{
align: 'left',
field: 'routeFk',
label: t('Route'),
name: 'route',
},
{
align: 'left',
field: 'packages',
label: t('Packages'),
name: 'packages',
},
{
align: 'left',
field: (row) => date.formatDate(row?.shipped, 'DD/MM/YYYY'),
label: t('Date'),
name: 'date',
},
{
align: 'left',
field: (row) => row?.ticketState?.state?.name,
label: t('State'),
name: 'state',
},
{
align: 'left',
field: 'totalWithVat',
label: t('Total'),
name: 'total',
},
{
align: 'left',
field: 'totalWithVat',
label: '',
name: 'actions',
},
]);
const setStateColor = (ticket) => {
const { ticketState } = ticket;
if (!ticketState) return;
const codeColorMap = { OK: 'success', FREE: 'notice' };
const alertLevelColorMap = { 0: 'alert', 1: 'warning' };
if (codeColorMap[ticketState.code]) return codeColorMap[ticketState.code];
return alertLevelColorMap[ticketState.alertLevel];
};
const setTotalPriceColor = (ticket) => {
const total = parseInt(ticket.totalWithVat);
if (total > 0 && total < 50) return 'warning';
};
const navigateToticketSummary = (id) => {
router.push({
name: 'TicketSummary',
params: { id },
});
};
</script>
<template>
<FetchData
:filter="filter"
@on-fetch="(data) => (rows = data)"
auto-load
url="Tickets"
/>
<QTable
:columns="columns"
:pagination="{ rowsPerPage: 12 }"
:rows="rows"
class="full-width q-mt-md"
row-key="id"
v-if="rows?.length"
>
<template #body-cell="props">
<QTd :props="props" @click="navigateToticketSummary(props.row.id)">
<QTr :props="props" class="cursor-pointer">
<component
:is="tableColumnComponents[props.col.name].component"
@click="tableColumnComponents[props.col.name].event(props)"
class="rounded-borders q-pa-sm"
v-bind="tableColumnComponents[props.col.name].props(props)"
>
<template
v-if="
props.col.name === 'id' ||
props.col.name === 'nickname' ||
props.col.name === 'agency' ||
props.col.name === 'route' ||
props.col.name === 'packages'
"
>
{{ props.value }}
</template>
<template v-if="props.col.name === 'date'">
<QBadge class="q-pa-sm" color="warning">
{{ props.value }}
</QBadge>
</template>
<template v-if="props.col.name === 'state'">
<QBadge :color="setStateColor(props.row)" class="q-pa-sm">
{{ props.value }}
</QBadge>
</template>
<template v-if="props.col.name === 'total'">
<QBadge
:color="setTotalPriceColor(props.row)"
class="q-pa-sm"
v-if="setTotalPriceColor(props.row)"
>
{{ toCurrency(props.value) }}
</QBadge>
<div v-else>{{ toCurrency(props.value) }}</div>
</template>
<CustomerDescriptorProxy
:id="props.row.clientFk"
v-if="props.col.name === 'nickname'"
/>
<RouteDescriptorProxy
:id="props.row.routeFk"
v-if="props.col.name === 'route'"
/>
</component>
</QTr>
</QTd>
</template>
</QTable>
</template>
<i18n>
es:
Id: Id
Nickname: Alias
Agency: Agencia
Route: Ruta
Packages: Bultos
Date: Fecha
State: Estado
Total: Total
</i18n>

View File

@ -0,0 +1,44 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import TicketSummary from 'src/pages/Ticket/Card/TicketSummary.vue';
const { t } = useI18n();
defineProps({
jsegarra marked this conversation as resolved Outdated

Eliminar porque sale warning

Eliminar porque sale warning

Asignación de prop eliminada:

Commit: 107eaea704

Asignación de prop eliminada: Commit: https://gitea.verdnatura.es/hyervoni/salix-front-mindshore/commit/107eaea704c5603a1bd72ae85e978199e51b88e9
id: {
type: Number,
required: true,
},
});
const { viewSummary } = useSummaryDialog();
</script>
<template>
<div>
<QIcon color="primary" name="vn:lines" size="sm">
<QTooltip>
{{ t('Go to lines') }}
</QTooltip>
</QIcon>
<QIcon
@click.stop="viewSummary(id, TicketSummary)"
class="q-ml-md"
color="primary"
name="preview"
size="sm"
>
<QTooltip>
{{ t('Preview') }}
</QTooltip>
</QIcon>
</div>
</template>
<i18n>
es:
Go to lines: Ir a lineas
Preview: Vista previa
</i18n>

View File

@ -0,0 +1,73 @@
<script setup>
jsegarra marked this conversation as resolved
Review

Se que no es este archivo, pero está relacionado.
El candado de "cerrar contrato" no está en línea con el texto y le falta el relleno.
Ver adjunto

Se que no es este archivo, pero está relacionado. El candado de "cerrar contrato" no está en línea con el texto y le falta el relleno. Ver adjunto
Review

Corregido: 38d3d49ecb

Corregido: 38d3d49ecb
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import axios from 'axios';
import { useDialogPluginComponent } from 'quasar';
const { t } = useI18n();
const { dialogRef } = useDialogPluginComponent();
const closeButton = ref(null);
const $props = defineProps({
id: {
type: Number,
required: true,
},
promise: {
type: Function,
required: true,
},
});
const isLoading = ref(false);
const saveData = async () => {
const timestamp = new Date().getTime();
const payload = {
finished: timestamp,
};
await axios.patch(`CreditClassifications/${$props.id}`, payload);
$props.promise();
closeButton.value.click();
};
</script>
<template>
<QDialog ref="dialogRef">
<QCard class="q-pa-sm">
<span
ref="closeButton"
class="flex justify-end cursor-pointer q-mb-sm"
v-close-popup
>
<QIcon name="close" size="sm" />
</span>
<QCardSection>
<div class="text-h6 q-mb-sm">
{{ t('Are you sure you want to close this contract?') }}
</div>
<div class="q-mb-sm">{{ t('Close contract') }}</div>
</QCardSection>
<QCardActions align="right">
<QBtn :label="t('globals.cancel')" color="primary" flat v-close-popup />
<QBtn
:label="t('globals.confirm')"
color="primary"
:loading="isLoading"
@click="saveData"
unelevated
/>
</QCardActions>
</QCard>
</QDialog>
</template>
<i18n>
es:
Are you sure you want to close this contract?: ¿Seguro que quieres cerrar este contrato?
Close contract: Cerrar contrato
</i18n>

View File

@ -1,6 +1,6 @@
<script setup>
import { useRoute } from 'vue-router';
import { reactive, ref } from 'vue';
import { reactive, ref, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import axios from 'axios';
import { useState } from 'composables/useState';
@ -27,6 +27,23 @@ const clientList = ref([]);
const agencyList = ref([]);
const addressList = ref([]);
const onClientsFetched = async (data) => {
try {
clientList.value = data;
initialFormState.clientFk = Number(route.query?.clientFk) || null;
if (initialFormState.clientFk) {
const { defaultAddressFk } = clientList.value.find(
(client) => client.id === initialFormState.clientFk
);
if (defaultAddressFk) await fetchAddressList(defaultAddressFk);
}
} catch (err) {
console.error('Error fetching clients', err);
}
};
const fetchAddressList = async (addressId) => {
try {
const { data } = await axios.get('addresses', {
@ -47,6 +64,7 @@ const fetchAddressList = async (addressId) => {
return err.response;
}
};
const fetchAgencyList = async (landed, addressFk) => {
if (!landed || !addressFk) {
return;
@ -108,7 +126,7 @@ const orderFilter = {
<template>
<FetchData
url="Clients"
@on-fetch="(data) => (clientList = data)"
@on-fetch="(data) => onClientsFetched(data)"
:filter="{ fields: ['id', 'name', 'defaultAddressFk'] }"
auto-load
/>

View File

@ -3,7 +3,7 @@ import { computed, ref } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore';
import { dashIfEmpty, toCurrency, toDateHour } from 'src/filters';
import { dashIfEmpty, toCurrency, toDateHourMinSec } from 'src/filters';
import VnLv from 'components/ui/VnLv.vue';
import CardSummary from 'components/ui/CardSummary.vue';
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
@ -86,15 +86,15 @@ const detailsColumns = ref([
<QCard class="vn-one">
<VnLv
:label="t('order.summary.created')"
:value="toDateHour(entity?.created)"
:value="toDateHourMinSec(entity?.created)"
/>
<VnLv
:label="t('order.summary.confirmed')"
:value="toDateHour(entity?.confirmed)"
:value="toDateHourMinSec(entity?.confirmed)"
/>
<VnLv
:label="t('order.summary.landed')"
:value="toDateHour(entity?.landed)"
:value="toDateHourMinSec(entity?.landed)"
/>
<VnLv :label="t('order.summary.phone')">
<template #value>

View File

@ -151,7 +151,7 @@ async function changeState(value) {
</VnLv>
<VnLv
:label="t('ticket.summary.agency')"
:value="ticket.agencyMode.name"
:value="ticket.agencyMode?.name"
/>
<VnLv :label="t('ticket.summary.zone')" :value="ticket?.zone?.name" />
<VnLv

View File

@ -0,0 +1,227 @@
<script setup>
import { useRoute, useRouter } from 'vue-router';
import { onBeforeMount, reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import FormModel from 'components/FormModel.vue';
import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import { useState } from 'composables/useState';
import axios from 'axios';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const state = useState();
const user = state.getUser();
const initialFormState = reactive({
jsegarra marked this conversation as resolved
Review

Estamos viendo en este módulo y otros que da errores al llamar a salix
Lo que pasa es que si solo modificas un campo el resto se envía estando igualmente a null, este planteamiento es correcto?

Estamos viendo en este módulo y otros que da errores al llamar a salix Lo que pasa es que si solo modificas un campo el resto se envía estando igualmente a null, este planteamiento es correcto?
Review

Corregido: efb676e4a4

Corregido: efb676e4a4
Review

Not fixed!

Not fixed!
Review

Me muestra el mensaje de "Desea salir sin guardar", pero se cierra solo y la operación POST se resuelve con éxito

Me muestra el mensaje de "Desea salir sin guardar", pero se cierra solo y la operación POST se resuelve con éxito
Review

Si, aparentemente quedo un problema en el FormModel en el manejo de beforeRouteLeave cuando lo implementaron, quedó solucionado.

Commit: 5ae54d557b

Si, aparentemente quedo un problema en el `FormModel` en el manejo de `beforeRouteLeave` cuando lo implementaron, quedó solucionado. Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/5ae54d557bdc2885d48dbec7eb721088b7e22916
clientId: Number(route.query?.clientFk) || null,
addressId: null,
agencyModeId: null,
warehouseId: user.value.warehouseFk,
landed: null,
});
const clientOptions = ref([]);
const agenciesOptions = ref([]);
const addressesOptions = ref([]);
const warehousesOptions = ref([]);
const selectedClient = ref(null);
onBeforeMount(async () => {
await onClientSelected(initialFormState);
});
const fetchClient = async (formData) => {
try {
const filter = {
include: {
relation: 'defaultAddress',
scope: {
fields: ['id', 'agencyModeFk'],
},
},
where: { id: formData.clientId },
};
const params = { filter: JSON.stringify(filter) };
const { data } = await axios.get('Clients', { params });
const [client] = data;
selectedClient.value = client;
} catch (err) {
console.error('Error fetching client');
}
};
const fetchAddresses = async (formData) => {
try {
if (!formData.clientId) return;
const filter = {
fields: ['nickname', 'street', 'city', 'id'],
where: { isActive: true },
order: 'nickname ASC',
};
const params = { filter: JSON.stringify(filter) };
const { data } = await axios.get(`Clients/${formData.clientId}/addresses`, {
params,
});
addressesOptions.value = data;
const { defaultAddress } = selectedClient.value;
formData.addressId = defaultAddress.id;
console.log();
} catch (err) {
console.error(`Error fetching addresses`, err);
return err.response;
}
};
const onClientSelected = async (formData) => {
await fetchClient(formData);
await fetchAddresses(formData);
};
const fetchAvailableAgencies = async (formData) => {
if (!formData.warehouseId || !formData.addressId || !formData.landed) return;
let params = {
warehouseFk: formData.warehouseId,
addressFk: formData.addressId,
landed: formData.landed,
};
const { data } = await axios.get('Agencies/getAgenciesWithWarehouse', { params });
agenciesOptions.value = data;
const defaultAgency = agenciesOptions.value.find(
(agency) =>
agency.agencyModeFk === selectedClient.value.defaultAddress.agencyModeFk
);
if (defaultAgency) formData.agencyModeId = defaultAgency.agencyModeFk;
};
const redirectToTicketList = (_, { id }) => {
router.push({ name: 'TicketSummary', params: { id } });
};
</script>
<template>
<FetchData
url="Clients"
@on-fetch="(data) => (clientOptions = data)"
:filter="{ fields: ['id', 'name', 'defaultAddressFk'], order: 'id' }"
auto-load
/>
<FetchData
url="Warehouses"
@on-fetch="(data) => (warehousesOptions = data)"
order="name"
auto-load
/>
<VnSubToolbar />
<div class="q-pa-md">
<FormModel
url-create="Tickets/new"
model="ticket"
:form-initial-data="initialFormState"
@on-data-saved="redirectToTicketList"
>
<template #form="{ data }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('ticket.create.client')"
v-model="data.clientId"
:options="clientOptions"
option-value="id"
option-label="name"
hide-selected
@update:model-value="(client) => onClientSelected(data)"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt.name }}
</QItemLabel>
<QItemLabel caption>
{{ `#${scope.opt.id}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectFilter>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('ticket.create.address')"
v-model="data.addressId"
:options="addressesOptions"
option-value="id"
option-label="nickname"
hide-selected
:disable="!data.clientId"
@update:model-value="() => fetchAvailableAgencies(data)"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt.nickname }}
</QItemLabel>
<QItemLabel caption>
{{ `${scope.opt.street}, ${scope.opt.city}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectFilter>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInputDate
placeholder="dd-mm-aaa"
:label="t('ticket.create.landed')"
v-model="data.landed"
@update:model-value="() => fetchAvailableAgencies(data)"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('ticket.create.warehouse')"
v-model="data.warehouseId"
:options="warehousesOptions"
option-value="id"
option-label="name"
hide-selected
@update:model-value="() => fetchAvailableAgencies(data)"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('ticket.create.agency')"
v-model="data.agencyModeId"
:options="agenciesOptions"
option-value="agencyModeFk"
option-label="agencyMode"
hide-selected
:disable="!data.clientId || !data.landed || !data.warehouseId"
/>
</div>
</VnRow>
</template>
</FormModel>
</div>
</template>

View File

@ -123,6 +123,13 @@ function navigate(id) {
</template>
</VnPaginate>
</div>
<QPageSticky :offset="[20, 20]">
<QBtn :to="{ name: 'TicketCreate' }" fab icon="add" color="primary">
<QTooltip>
{{ t('New ticket') }}
</QTooltip>
</QBtn>
</QPageSticky>
</QPage>
</template>
@ -131,4 +138,5 @@ es:
Search ticket: Buscar ticket
You can search by ticket id or alias: Puedes buscar por id o alias del ticket
Zone: Zona
New ticket: Nuevo ticket
</i18n>

View File

@ -22,7 +22,7 @@ export default {
'CustomerBasicData',
'CustomerFiscalData',
'CustomerBillingData',
'CustomerConsignees',
'CustomerAddress',
'CustomerNotes',
'CustomerCredits',
'CustomerGreuges',
@ -153,45 +153,45 @@ export default {
import('src/pages/Customer/Card/CustomerBillingData.vue'),
},
{
path: 'consignees',
name: 'ConsigneesCard',
redirect: { name: 'CustomerConsignees' },
path: 'address',
name: 'AddressCard',
redirect: { name: 'CustomerAddress' },
children: [
{
path: '',
name: 'CustomerConsignees',
name: 'CustomerAddress',
meta: {
icon: 'vn:delivery',
title: 'consignees',
},
component: () =>
import('src/pages/Customer/Card/CustomerConsignees.vue'),
import('src/pages/Customer/Card/CustomerAddress.vue'),
},
{
path: 'create',
name: 'CustomerConsigneeCreate',
name: 'CustomerAddressCreate',
meta: {
title: 'consignee-create',
title: 'address-create',
},
component: () =>
import(
'src/pages/Customer/components/CustomerConsigneeCreate.vue'
'src/pages/Customer/components/CustomerAddressCreate.vue'
),
},
{
path: ':consigneeId',
name: 'CustomerConsigneeEditCard',
redirect: { name: 'CustomerConsigneeEdit' },
path: ':addressId',
name: 'CustomerAddressEditCard',
redirect: { name: 'CustomerAddressEdit' },
children: [
{
path: 'edit',
name: 'CustomerConsigneeEdit',
name: 'CustomerAddressEdit',
meta: {
title: 'consignee-edit',
title: 'address-edit',
},
component: () =>
import(
'src/pages/Customer/components/CustomerConsigneeEdit.vue'
'src/pages/Customer/components/CustomerAddressEdit.vue'
),
},
],
@ -200,7 +200,7 @@ export default {
},
{
path: 'notes',
name: 'NotesCard',
name: 'CustomerNotesCard',
redirect: { name: 'CustomerNotes' },
children: [
{
@ -358,36 +358,55 @@ export default {
{
name: 'CustomerCreditContracts',
title: 'creditContracts',
icon: 'paid',
icon: 'vn:solunion',
},
{
name: 'CustomerCreditOpinion',
title: 'creditOpinion',
icon: 'paid',
icon: 'vn:linesprepaired',
},
],
},
component: () =>
import('src/pages/Customer/Card/CustomerCreditManagement.vue'),
children: [
{
path: 'credit-contracts',
name: 'CustomerCreditContracts',
meta: {
title: 'creditContracts',
icon: 'paid',
},
component: () =>
import(
'src/pages/Customer/Card/CustomerCreditContracts.vue'
),
name: 'CreditContractsCard',
redirect: { name: 'CustomerCreditContracts' },
children: [
{
path: '',
name: 'CustomerCreditContracts',
meta: {
title: 'creditContracts',
},
component: () =>
import(
'src/pages/Customer/Card/CustomerCreditContracts.vue'
),
},
{
path: 'create',
name: 'CustomerCreditContractsCreate',
component: () =>
import(
'src/pages/Customer/components/CustomerCreditContractsCreate.vue'
),
},
{
path: 'insurance/:creditId',
name: 'CustomerCreditContractsInsurance',
component: () =>
import(
'src/pages/Customer/components/CustomerCreditContractsInsurance.vue'
),
},
],
},
{
path: 'credit-opinion',
name: 'CustomerCreditOpinion',
meta: {
title: 'creditOpinion',
icon: 'paid',
},
component: () =>
import(
@ -396,15 +415,177 @@ export default {
},
],
},
{
path: 'others',
name: 'CustomerOthers',
meta: {
title: 'others',
icon: 'pending',
menuChildren: [
{
name: 'CustomerSamples',
title: 'samples',
icon: 'vn:notes',
},
{
name: 'CustomerConsumption',
title: 'consumption',
icon: 'show_chart',

en salix al abrir este menu el scroll baja para poder ver directamente las opciones desplegadas, copiar funcionamiento.

en salix al abrir este menu el scroll baja para poder ver directamente las opciones desplegadas, copiar funcionamiento.

Corregido: c767bcde7d

Corregido: c767bcde7d
},
{
name: 'CustomerMandates',
title: 'mandates',
icon: 'vn:mandatory',
},
{
name: 'CustomerContacts',
title: 'contacts',

salix tiene otro icono

salix tiene otro icono

Corregido: 3bcd4984f5

Corregido: 3bcd4984f5

Yo lo veo OK

Yo lo veo OK
icon: 'contact_phone',
},
{
name: 'CustomerWebPayment',
title: 'webPayment',
icon: 'vn:onlinepayment',
},
{
name: 'CustomerFileManagement',
title: 'fileManagement',
icon: 'Upload',
},
{
name: 'CustomerUnpaid',
title: 'unpaid',
icon: 'vn:defaulter',
},
],
},
component: () => import('src/pages/Customer/Card/CustomerOthers.vue'),
children: [
{
path: 'samples',
name: 'CustomerSamples',
meta: {
title: 'samples',

salix tiene otro icono

salix tiene otro icono

Corregido: 9ae0874ccf

Corregido: 9ae0874ccf

Lo veo OK

Lo veo OK
},
component: () =>
import('src/pages/Customer/Card/CustomerSamples.vue'),
},
{
path: 'samples',
name: 'CustomerSamplesCard',
redirect: { name: 'CustomerSamples' },
children: [
{
path: '',
name: 'CustomerSamples',
meta: {
title: 'samples',
},
component: () =>
import(
'src/pages/Customer/Card/CustomerSamples.vue'
),
},
{
path: 'create',
name: 'CustomerSamplesCreate',
component: () =>
import(
'src/pages/Customer/components/CustomerSamplesCreate.vue'
),
},
],
},
{
path: 'consumption',
name: 'CustomerConsumption',
meta: {
title: 'consumption',
},
component: () =>
import('src/pages/Customer/Card/CustomerConsumption.vue'),
},
{
path: 'mandates',
name: 'CustomerMandates',
meta: {
title: 'mandates',
},
component: () =>
import('src/pages/Customer/Card/CustomerMandates.vue'),
},
{
path: 'contacts',
name: 'CustomerContacts',
meta: {
title: 'contacts',
},
component: () =>
import('src/pages/Customer/Card/CustomerContacts.vue'),
},
{
path: 'web-payment',
name: 'CustomerWebPayment',
meta: {
title: 'webPayment',
},
component: () =>
import('src/pages/Customer/Card/CustomerWebPayment.vue'),
},
{
path: 'file-management',
name: 'CustomerFileManagement',
meta: {
title: 'fileManagement',
},
component: () =>
import(
Review

si estoy en el cliente 1110 que no tiene pagos web, y en el buscador superior me muevo al 1101 que sí los tiene en salix me aparecen en lilium tengo que hacer f5 para que acceda a la información, no pasa en esta seccion solamente, tal vez alguien de vuestro o nuestro equipo esto ya lo haya solucionado en algun otro formulario..preguntar a ver.

si estoy en el cliente 1110 que no tiene pagos web, y en el buscador superior me muevo al 1101 que sí los tiene en salix me aparecen en lilium tengo que hacer f5 para que acceda a la información, no pasa en esta seccion solamente, tal vez alguien de vuestro o nuestro equipo esto ya lo haya solucionado en algun otro formulario..preguntar a ver.
Review

Corregido: b12968f982

Corregido: b12968f982
Review

@jgallego Tenemos un comportamiento similar en el resto de la aplicación.
Ya tenemos un redmine abierto #186 (comment)
La solución de Carlos es similar a la que se ha aplicado en el redmine

@jgallego Tenemos un comportamiento similar en el resto de la aplicación. Ya tenemos un redmine abierto https://gitea.verdnatura.es/verdnatura/salix-front/pulls/186#issuecomment-43382 La solución de Carlos es similar a la que se ha aplicado en el redmine
'src/pages/Customer/Card/CustomerFileManagement.vue'
),
},
{
path: 'file-management',
name: 'CustomerFileManagementCard',
redirect: { name: 'CustomerFileManagement' },
children: [
{
path: '',
name: 'CustomerFileManagement',
meta: {
title: 'fileManagement',
},
component: () =>
import(
'src/pages/Customer/Card/CustomerFileManagement.vue'
),
},
{
path: 'create',
name: 'CustomerFileManagementCreate',
component: () =>
import(
'src/pages/Customer/components/CustomerFileManagementCreate.vue'
),
},
{
path: ':dmsId/edit',
name: 'CustomerFileManagementEdit',
component: () =>
import(
'src/pages/Customer/components/CustomerFileManagementEdit.vue'
),
},
],
},
{
path: 'unpaid',
name: 'CustomerUnpaid',
meta: {
title: 'unpaid',
},
component: () =>
import('src/pages/Customer/Card/CustomerUnpaid.vue'),
},
],
},
],
},

View File

@ -38,7 +38,7 @@ export default {
icon: 'vn:ticketAdd',
roles: ['developer'],
},
component: () => import('src/pages/Ticket/TicketList.vue'),
component: () => import('src/pages/Ticket/TicketCreate.vue'),
},
],
},