6157-actionClaim #106

Merged
carlossa merged 22 commits from 6157-actionClaim into dev 2023-11-24 07:46:30 +00:00
19 changed files with 661 additions and 71 deletions

View File

@ -269,7 +269,7 @@ watch(formUrl, async () => {
</VnPaginate> </VnPaginate>
<SkeletonTable v-if="!formData" /> <SkeletonTable v-if="!formData" />
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()"> <Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
carlossa marked this conversation as resolved Outdated

Este estilo, es el que corregimos que permitía separar los botones pero tenerlos pegados al margen?
Si es así, quizás, ese estilo, puede ser usado en un futuro o recurrente en otros componentes, por lo que la propiedad column-gap la movería a una clase

Este estilo, es el que corregimos que permitía separar los botones pero tenerlos pegados al margen? Si es así, quizás, ese estilo, puede ser usado en un futuro o recurrente en otros componentes, por lo que la propiedad column-gap la movería a una clase
<QBtnGroup push class="q-gutter-x-sm"> <QBtnGroup push style="column-gap: 10px">
<slot name="moreBeforeActions" /> <slot name="moreBeforeActions" />
<QBtn <QBtn
:label="tMobile('globals.remove')" :label="tMobile('globals.remove')"

View File

@ -4,7 +4,7 @@ const emit = defineEmits(['update:modelValue', 'update:options']);
const $props = defineProps({ const $props = defineProps({
modelValue: { modelValue: {
type: [String, Number], type: [String, Number, Object],
default: null, default: null,
}, },
options: { options: {
@ -81,7 +81,6 @@ const value = computed({
map-options map-options
use-input use-input
@filter="filterHandler" @filter="filterHandler"
hide-selected
fill-input fill-input
ref="vnSelectRef" ref="vnSelectRef"
> >

View File

@ -1,8 +1,9 @@
<script setup> <script setup>
import { onMounted, useSlots, ref, watch } from 'vue'; import { onMounted, useSlots, ref, watch, computed } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import axios from 'axios'; import axios from 'axios';
import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue'; import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
import { useArrayData } from 'composables/useArrayData';
const $props = defineProps({ const $props = defineProps({
url: { url: {
@ -25,33 +26,37 @@ const $props = defineProps({
type: Number, type: Number,
default: 0, default: 0,
}, },
dataKey: {
type: String,
default: '',
},
}); });
const slots = useSlots(); const slots = useSlots();
const { t } = useI18n(); const { t } = useI18n();
const entity = ref(); const entity = computed(() => useArrayData($props.dataKey).store.data);
carlossa marked this conversation as resolved
Review

Esta tb borrala

Esta tb borrala
onMounted(async () => { onMounted(async () => {
await fetch(); await getData();
watch(
() => $props.url,
async (newUrl, lastUrl) => {
if (newUrl == lastUrl) return;
entity.value = null;
await getData();
}
);
}); });
const emit = defineEmits(['onFetch']); async function getData() {
const arrayData = useArrayData($props.dataKey, {
async function fetch() { url: $props.url,
const params = {}; filter: $props.filter,
skip: 0,
if ($props.filter) params.filter = JSON.stringify($props.filter); });
const { data } = await arrayData.fetch({ append: false });
const { data } = await axios.get($props.url, { params });
entity.value = data;
emit('onFetch', data); emit('onFetch', data);
} }
const emit = defineEmits(['onFetch']);
watch($props, async () => {
entity.value = null;
await fetch();
});
</script> </script>
<template> <template>
carlossa marked this conversation as resolved Outdated
Outdated
Review

Borrala si no se gasta ni fa res

Borrala si no se gasta ni fa res

View File

@ -29,6 +29,10 @@ export function useArrayData(key, userOptions) {
} }
}); });
if (key && userOptions) {
setOptions();
}
function setOptions() { function setOptions() {
const allowedOptions = [ const allowedOptions = [
'url', 'url',
@ -42,7 +46,7 @@ export function useArrayData(key, userOptions) {
]; ];
if (typeof userOptions === 'object') { if (typeof userOptions === 'object') {
for (const option in userOptions) { for (const option in userOptions) {
const isEmpty = userOptions[option] == null || userOptions[option] == ''; const isEmpty = userOptions[option] == null || userOptions[option] === '';
if (isEmpty || !allowedOptions.includes(option)) continue; if (isEmpty || !allowedOptions.includes(option)) continue;
if (Object.prototype.hasOwnProperty.call(store, option)) { if (Object.prototype.hasOwnProperty.call(store, option)) {
@ -97,6 +101,7 @@ export function useArrayData(key, userOptions) {
store.isLoading = false; store.isLoading = false;
canceller = null; canceller = null;
return response;
} }
function destroy() { function destroy() {
@ -147,6 +152,7 @@ export function useArrayData(key, userOptions) {
if (store.userParams && Object.keys(store.userParams).length !== 0) if (store.userParams && Object.keys(store.userParams).length !== 0)
query.params = JSON.stringify(store.userParams); query.params = JSON.stringify(store.userParams);
if (router)
router.replace({ router.replace({
path: route.path, path: route.path,
query: query, query: query,

View File

@ -45,3 +45,9 @@ body.body--dark {
.bg-vn-dark { .bg-vn-dark {
background-color: var(--vn-dark); background-color: var(--vn-dark);
} }
.vn-card {
background-color: var(--vn-gray);
color: var(--vn-text);
border-radius: 8px;
}

View File

@ -274,6 +274,7 @@ export default {
development: 'Development', development: 'Development',
log: 'Audit logs', log: 'Audit logs',
notes: 'Notes', notes: 'Notes',
action: 'Action',
}, },
list: { list: {
customer: 'Customer', customer: 'Customer',

View File

@ -273,6 +273,7 @@ export default {
photos: 'Fotos', photos: 'Fotos',
log: 'Registros de auditoría', log: 'Registros de auditoría',
notes: 'Notas', notes: 'Notas',
action: 'Acción',
}, },
list: { list: {
customer: 'Cliente', customer: 'Cliente',

View File

@ -0,0 +1,518 @@
<script setup>
import { ref, computed, onMounted } from 'vue';
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import axios from 'axios';
import { useStateStore } from 'src/stores/useStateStore';
import { toDate, toPercentage, toCurrency } from 'filters/index';
import { tMobile } from 'src/composables/tMobile';
import CrudModel from 'src/components/CrudModel.vue';
import FetchData from 'src/components/FetchData.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import VnConfirm from 'src/components/ui/VnConfirm.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import { useArrayData } from 'composables/useArrayData';
const { t } = useI18n();
const quasar = useQuasar();
const route = useRoute();
const router = useRouter();
const stateStore = computed(() => useStateStore());
const claim = ref(null);
const claimRef = ref();
const claimId = route.params.id;
const dialogDestination = ref(false);
const claimDestinationFk = ref(null);
const resolvedStateId = ref(null);
const claimActionsForm = ref();
const rows = ref([]);
const selectedRows = ref([]);
carlossa marked this conversation as resolved Outdated
Outdated
Review

Si es un numero li pots possar const maxResponsibility = 5;

Si es un numero li pots possar const maxResponsibility = 5;

Si que puedes,
Sin embargo, te diría que ese 5, quizás moverlo a un const DEFAULT_MAX_RESPONSABILITY = 5.

Ya me dices

Si que puedes, Sin embargo, te diría que ese 5, quizás moverlo a un const DEFAULT_MAX_RESPONSABILITY = 5. Ya me dices
const destinationTypes = ref([]);
const totalClaimed = ref(null);
const DEFAULT_MAX_RESPONSABILITY = 5;
const DEFAULT_MIN_RESPONSABILITY = 1;
const arrayData = useArrayData('claimData');
const marker_labels = [
{ value: DEFAULT_MIN_RESPONSABILITY, label: t('claim.summary.company') },
{ value: DEFAULT_MAX_RESPONSABILITY, label: t('claim.summary.person') },
];
const columns = computed(() => [
{
name: 'Id',
label: t('Id item'),
field: (row) => row.itemFk,
},
{
name: 'ticket',
carlossa marked this conversation as resolved
Review

Pq no pots possar row.claimDestinationFk ?

Pq no pots possar row.claimDestinationFk ?
label: t('Ticket'),
field: (row) => row.ticketFk,
align: 'center',
},
{
name: 'destination',
label: t('Destination'),
field: (row) => row.claimDestinationFk,
align: 'left',
},
{
name: 'Landed',
label: t('Landed'),
field: (row) => toDate(row.landed),
},
{
name: 'quantity',
label: t('Quantity'),
field: (row) => row.quantity,
},
{
name: 'concept',
label: t('Description'),
field: (row) => row.concept,
align: 'left',
},
{
name: 'price',
label: t('Price'),
field: (row) => row.price,
format: (value) => value,
align: 'center',
},
{
name: 'discount',
label: t('Discount'),
field: (row) => row.discount,
format: (value) => toPercentage(value / 100),
align: 'left',
},
{
name: 'total',
label: t('Total'),
field: (row) => row.total,
format: (value) => value,
carlossa marked this conversation as resolved Outdated
Outdated
Review

? si lo que fas es sumar no seria mes facil sumar en el onmouted y quan afegixes o borres una linea?

? si lo que fas es sumar no seria mes facil sumar en el onmouted y quan afegixes o borres una linea?
align: 'center',
},
{
name: 'delete',
},
]);
onMounted(() => {
getTotal();
});
function setData(data) {
rows.value = data;
carlossa marked this conversation as resolved Outdated
Outdated
Review

Esta funcio no se crida mai

Esta funcio no se crida mai
getTotal();
}
function getTotal() {
if (rows.value.length) {
totalClaimed.value = rows.value.reduce((total, row) => total + row.total, 0);
carlossa marked this conversation as resolved Outdated

Lo revisamos!!

**Lo revisamos!!**
}
}
async function updateDestinations(claimDestinationFk) {
await updateDestination(claimDestinationFk, selectedRows.value, { reload: true });
}
async function updateDestination(claimDestinationFk, row, options = {}) {
if (claimDestinationFk) {
await axios.post('Claims/updateClaimDestination', {
carlossa marked this conversation as resolved
Review

Y esto también!!

Y esto también!!
claimDestinationFk,
rows: Array.isArray(row) ? row : [row],
});
options.reload && claimActionsForm.value.reload();
}
}
async function regularizeClaim() {
const query = `Claims/${claimId}/regularizeClaim`;
await axios.post(query);
if (claim.value.responsibility >= Math.ceil(DEFAULT_MAX_RESPONSABILITY) / 2) {
await claimRef.value.fetch();
quasar
.dialog({
component: VnConfirm,
carlossa marked this conversation as resolved Outdated

Lo que le pasas a notify podría ser una constante, no? Porque lo hacemos en la línea 148, 166, 195

Lo que le pasas a notify podría ser una constante, no? Porque lo hacemos en la línea 148, 166, 195
componentProps: {
title: t('confirmGreuges'),
message: t('confirmGreugesMessage'),
},
})
.onOk(async () => await onUpdateGreugeAccept());
} else {
quasar.notify({
message: t('globals.dataSaved'),
type: 'positive',
});
}
await arrayData.fetch({ append: false });
}
async function updateGreuge(greuges) {
const { data } = await axios.post(`Greuges`, greuges);
quasar.notify({
message: t('globals.dataSaved'),
type: 'positive',
});
return data;
}
async function onUpdateGreugeAccept() {
const greugeTypeFreightId = await getGreugeTypeId();
const freightPickUpPrice = await getGreugeConfig();
await updateGreuge({
clientFk: claim.value.clientFk,
description: `${t('ClaimGreugeDescription')} ${claimId}`.toUpperCase(),
amount: freightPickUpPrice,
greugeTypeFk: greugeTypeFreightId,
ticketFk: claim.value.ticketFk,
});
quasar.notify({
message: t('globals.dataSaved'),
type: 'positive',
});
}
async function getGreugeTypeId() {
const params = { filter: { where: { code: 'freightPickUp' } } };
const query = `GreugeTypes/findOne`;
const { data } = await axios.get(query, { params });
return data.id;
}
async function getGreugeConfig() {
const query = `GreugeConfigs/findOne`;
const { data } = await axios.get(query);
return data.freightPickUpPrice;
}
async function save(data) {
const query = `Claims/${claimId}/updateClaimAction`;
await axios.patch(query, data);
}
async function importToNewRefundTicket() {
const query = `ClaimBeginnings/${claimId}/importToNewRefundTicket`;
await axios.post(query);
claimActionsForm.value.reload();
quasar.notify({
message: t('globals.dataSaved'),
type: 'positive',
});
}
</script>
<template>
<FetchData
ref="claimRef"
:url="`Claims/${claimId}`"
@on-fetch="(data) => (claim = data)"
auto-load
/>
<FetchData
url="ClaimStates/findOne"
@on-fetch="(data) => (resolvedStateId = data.id)"
auto-load
:where="{ code: 'resolved' }"
/>
<FetchData
url="ClaimDestinations"
auto-load
@on-fetch="(data) => (destinationTypes = data)"
/>
<template v-if="stateStore.isHeaderMounted()">
<Teleport to="#actions-append">
<div class="row q-gutter-x-sm">
<QBtn
flat
@click="stateStore.toggleRightDrawer()"
round
dense
icon="menu"
>
<QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }}
</QTooltip>
</QBtn>
</div>
</Teleport>
</template>
<QDrawer
v-model="stateStore.rightDrawer"
side="right"
:width="300"
show-if-above
v-if="claim"
>
<QCard class="totalClaim vn-card q-my-md q-pa-sm">
{{ `${t('Total claimed')}: ${toCurrency(totalClaimed)}` }}
</QCard>
<QCard class="vn-card q-mb-md q-pa-sm">
<QItem class="justify-between">
<QItemLabel class="slider-container">
<p class="text-primary">
{{ t('claim.summary.actions') }}
</p>
<QSlider
class="responsibility { 'background-color:primary': quasar.platform.is.mobile }"
v-model="claim.responsibility"
:label-value="t('claim.summary.responsibility')"
carlossa marked this conversation as resolved
Review

Si, los labels cambian, tienes que venir hasta el HTML?
Quizás, mejor definirlo en el apartado de

Si, los labels cambian, tienes que venir hasta el HTML? Quizás, mejor definirlo en el apartado de <script setup>
@change="(value) => save({ responsibility: value })"
label-always
color="primary"
markers
carlossa marked this conversation as resolved
Review

Podemos definir este valor como constante?
Tipo
DEFAULT_MIN_SLIDER =1
DEFAULT_MAX_SLIDER =5

Podemos definir este valor como constante? Tipo DEFAULT_MIN_SLIDER =1 DEFAULT_MAX_SLIDER =5
:marker-labels="marker_labels"
:min="DEFAULT_MIN_RESPONSABILITY"
:max="DEFAULT_MAX_RESPONSABILITY"
/>
</QItemLabel>
</QItem>
</QCard>
<QItemLabel class="mana q-mb-md">
<QCheckbox
v-model="claim.isChargedToMana"
@update:model-value="(value) => save({ isChargedToMana: value })"
/>
<span>{{ t('mana') }}</span>
</QItemLabel>
</QDrawer>
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()"> </Teleport>
<CrudModel
v-if="claim"
data-key="ClaimEnds"
url="ClaimEnds/filter"
save-url="ClaimEnds/crud"
ref="claimActionsForm"
v-model:selected="selectedRows"
:filter="{ where: { claimFk: claimId } }"
:default-remove="true"
:default-save="false"
:default-reset="false"
@on-fetch="setData"
auto-load
>
<template #body>
<QTable
:columns="columns"
:rows="rows"
:dense="$q.screen.lt.md"
row-key="id"
selection="multiple"
v-model:selected="selectedRows"
:grid="$q.screen.lt.md"
>
<template #body-cell-ticket="{ value }">
<QTd align="center">
<span class="link">
{{ value }}
<TicketDescriptorProxy :id="value" />
</span>
</QTd>
</template>
<template #body-cell-destination="{ row }">
<QTd>
<VnSelectFilter
v-model="row.claimDestinationFk"
:options="destinationTypes"
option-label="description"
option-value="id"
:autofocus="true"
dense
input-debounce="0"
hide-selected
@update:model-value="(value) => updateDestination(value, row)"
/>
</QTd>
</template>
<template #body-cell-price="{ value }">
<QTd align="center">
{{ toCurrency(value) }}
</QTd>
</template>
<template #body-cell-total="{ value }">
<QTd align="center">
{{ toCurrency(value) }}
</QTd>
</template>
<!-- View for grid mode -->
<template #item="props">
<div class="q-mb-md col-12 grid-style-transition">
<QCard>
<QCardSection class="row justify-between">
<QCheckbox v-model="props.selected" />
<QBtn color="primary" icon="delete" flat round />
</QCardSection>
<QSeparator inset />
<QList dense>
<QItem v-for="column of props.cols" :key="column.name">
<QItemSection>
<QItemLabel caption>
{{ column.label }}
</QItemLabel>
</QItemSection>
<QItemSection side>
<QItemLabel v-if="column.name === 'destination'">
{{ column.value.description }}
</QItemLabel>
<QItemLabel v-else>
{{ column.value }}
</QItemLabel>
</QItemSection>
</QItem>
</QList>
</QCard>
</div>
</template>
</QTable>
</template>
<template #moreBeforeActions>
<QBtn
color="primary"
text-color="white"
:unelevated="true"
:label="tMobile('Regularize')"
:title="t('Regularize')"
icon="check"
@click="regularizeClaim"
:disable="claim.claimStateFk == resolvedStateId"
/>
<QBtn
color="primary"
text-color="white"
:unelevated="true"
:disable="!selectedRows.length"
:label="tMobile('Change destination')"
:title="t('Change destination')"
icon="swap_horiz"
@click="dialogDestination = !dialogDestination"
/>
<QBtn
color="primary"
text-color="white"
:unelevated="true"
:label="tMobile('Import claim')"
:title="t('Import claim')"
icon="Upload"
@click="importToNewRefundTicket"
:disable="claim.claimStateFk == resolvedStateId"
/>
</template>
</CrudModel>
<QDialog v-model="dialogDestination">
<QCard>
<QCardSection>
<QItem class="q-pa-sm">
<span class="q-dialog__title text-white">
{{ t('dialog title') }}
</span>
<QBtn icon="close" flat round dense v-close-popup />
</QItem>
</QCardSection>
<QItemSection>
<VnSelectFilter
class="q-pa-sm"
v-model="claimDestinationFk"
:options="destinationTypes"
option-label="description"
option-value="id"
:autofocus="true"
dense
input-debounce="0"
hide-selected
/>
</QItemSection>
<QCardActions class="justify-end q-mr-sm">
<QBtn flat :label="t('globals.close')" color="primary" v-close-popup />
<QBtn
:disable="!claimDestinationFk"
:label="t('globals.save')"
color="primary"
v-close-popup
@click="updateDestinations(claimDestinationFk)"
/>
</QCardActions>
</QCard>
</QDialog>
<!-- <QDialog v-model="dialogGreuge">
<QCardSection>
<QItem class="q-pa-sm">
<span class="q-pa-sm q-dialog__title text-white">
{{ t('dialogGreuge title') }}
</span>
<QBtn class="q-pa-sm" icon="close" flat round dense v-close-popup />
</QItem>
<QCardActions class="justify-end q-mr-sm">
<QBtn flat :label="t('globals.close')" color="primary" v-close-popup />
<QBtn
:label="t('globals.save')"
color="primary"
v-close-popup
@click="onUpdateGreugeAccept"
/>
</QCardActions>
</QCardSection>
</QDialog> -->
</template>
<style lang="scss" scoped>
carlossa marked this conversation as resolved
Review

Com?

Com?
.slider-container {
width: 50%;
}
@media (max-width: $breakpoint-xs) {
.slider-container {
width: 90%;
}
}
.q-table {
.q-item {
min-height: min-content;
height: 0;
}
}
.q-dialog {
.q-btn {
height: min-content;
}
}
.responsibility {
max-width: 100%;
margin-left: 40px;
}
.mana {
carlossa marked this conversation as resolved
Review

Aço si ja esta en els descriptors, en els summarys te que haver alguna clase ja feta (si no fesla)

Aço si ja esta en els descriptors, en els summarys te que haver alguna clase ja feta (si no fesla)
float: inline-start;
}
</style>
<i18n>
en:
mana: Is paid with mana
dialog title: Change destination to all selected rows
confirmGreuges: Do you want to insert complaints?
confirmGreugesMessage: Insert complaints into the client's record
es:
mana: Cargado al maná
Delivered: Descripción
Quantity: Cantidad
Claimed: Rec
Description: Descripción
Price: Precio
Discount: Dto.
Destination: Destino
Landed: F.entrega
Remove line: Eliminar línea
Total claimed: Total reclamado
Regularize: Regularizar
Change destination: Cambiar destino
Import claim: Importar reclamación
dialog title: Cambiar destino en todas las filas seleccionadas
Remove: Eliminar
dialogGreuge title: Insertar greuges en la ficha del cliente
ClaimGreugeDescription: Id reclamación
Id item: Id artículo
confirmGreuges: ¿Desea insertar greuges?
confirmGreugesMessage: Insertar greuges en la ficha del cliente
</i18n>

View File

@ -22,11 +22,6 @@ const $props = defineProps({
const entityId = computed(() => { const entityId = computed(() => {
carlossa marked this conversation as resolved
Review

esto no se usa, no?

esto no se usa, no?
return $props.id || route.params.id; return $props.id || route.params.id;
}); });
let salixUrl;
onMounted(async () => {
salixUrl = await getUrl(`claim/${entityId.value}`);
});
</script> </script>
<template> <template>
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()"> <Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
@ -42,18 +37,6 @@ onMounted(async () => {
<ClaimDescriptor /> <ClaimDescriptor />
<QSeparator /> <QSeparator />
<LeftMenu source="card" /> <LeftMenu source="card" />
<QSeparator />
<QList>
<QItem
active-class="text-primary"
clickable
v-ripple
:href="`${salixUrl}/action`"
>
<QItemSection avatar><QIcon name="vn:actions"></QIcon></QItemSection>
<QItemSection>{{ t('Action') }}</QItemSection>
</QItem>
</QList>
</QScrollArea> </QScrollArea>
</QDrawer> </QDrawer>
<QPageContainer> <QPageContainer>

View File

@ -62,13 +62,18 @@ const filter = {
], ],
}; };
const STATE_COLOR = {
pending: 'positive',
managed: 'warning',
resolved: 'negative',
};
function stateColor(code) { function stateColor(code) {
if (code === 'pending') return 'positive'; return STATE_COLOR[code];
carlossa marked this conversation as resolved Outdated

Revisamos linea 65!

**Revisamos linea 65!**
if (code === 'managed') return 'warning';
if (code === 'resolved') return 'negative';
} }
const data = ref(useCardDescription()); const data = ref(useCardDescription());
const setData = (entity) => { const setData = (entity) => {
if (!entity) return;
data.value = useCardDescription(entity.client.name, entity.id); data.value = useCardDescription(entity.client.name, entity.id);
state.set('ClaimDescriptor', entity); state.set('ClaimDescriptor', entity);
}; };
@ -83,6 +88,7 @@ const setData = (entity) => {
:title="data.title" :title="data.title"
:subtitle="data.subtitle" :subtitle="data.subtitle"
@on-fetch="setData" @on-fetch="setData"
data-key="claimData"
> >
<template #menu="{ entity }"> <template #menu="{ entity }">
<ClaimDescriptorMenu :claim="entity" /> <ClaimDescriptorMenu :claim="entity" />
@ -120,16 +126,16 @@ const setData = (entity) => {
<VnLv :label="t('claim.card.commercial')"> <VnLv :label="t('claim.card.commercial')">
<template #value> <template #value>
<span class="link"> <span class="link">
{{ entity.client.salesPersonUser.name }} {{ entity.client?.salesPersonUser?.name }}
<WorkerDescriptorProxy :id="entity.client.salesPersonFk" /> <WorkerDescriptorProxy :id="entity.client?.salesPersonFk" />
</span> </span>
</template> </template>
</VnLv> </VnLv>
<VnLv <VnLv
:label="t('claim.card.province')" :label="t('claim.card.province')"
:value="entity.ticket.address.province.name" :value="entity.ticket?.address?.province?.name"
/> />
<VnLv :label="t('claim.card.zone')" :value="entity.ticket.zone.name" /> <VnLv :label="t('claim.card.zone')" :value="entity.ticket?.zone?.name" />
</template> </template>
<template #actions="{ entity }"> <template #actions="{ entity }">
<QCardActions> <QCardActions>

View File

@ -7,6 +7,7 @@ import FetchData from 'components/FetchData.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import VnSelectFilter from 'components/common/VnSelectFilter.vue';
import { getUrl } from 'composables/getUrl'; import { getUrl } from 'composables/getUrl';
import { tMobile } from 'composables/tMobile'; import { tMobile } from 'composables/tMobile';
import router from 'src/router';
const route = useRoute(); const route = useRoute();
@ -102,10 +103,6 @@ const columns = computed(() => [
tabIndex: 5, tabIndex: 5,
}, },
]); ]);
function goToAction() {
location.href = `${salixUrl}/action`;
}
</script> </script>
<template> <template>
<FetchData <FetchData
@ -148,7 +145,7 @@ function goToAction() {
:data-required="{ claimFk: route.params.id }" :data-required="{ claimFk: route.params.id }"
v-model:selected="selected" v-model:selected="selected"
auto-load auto-load
@save-changes="goToAction" @save-changes="$router.push(`/claim/${route.params.id}/action`)"
:default-save="false" :default-save="false"
> >
<template #body="{ rows }"> <template #body="{ rows }">
@ -175,6 +172,7 @@ function goToAction() {
:option-label="col.optionLabel" :option-label="col.optionLabel"
:autofocus="col.tabIndex == 1" :autofocus="col.tabIndex == 1"
input-debounce="0" input-debounce="0"
hide-selected
> >
<template #option="scope" v-if="col.name == 'worker'"> <template #option="scope" v-if="col.name == 'worker'">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -213,6 +211,7 @@ function goToAction() {
dense dense
input-debounce="0" input-debounce="0"
:autofocus="col.tabIndex == 1" :autofocus="col.tabIndex == 1"
hide-selected
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>

View File

@ -85,10 +85,15 @@ const detailsColumns = ref([
}, },
]); ]);
const STATE_COLOR = {
pending: 'positive',
managed: 'warning',
resolved: 'negative',
};
function stateColor(code) { function stateColor(code) {
if (code === 'pending') return 'green'; return STATE_COLOR[code];
if (code === 'managed') return 'orange';
if (code === 'resolved') return 'red';
} }
const developmentColumns = ref([ const developmentColumns = ref([

View File

@ -17,12 +17,14 @@ const router = useRouter();
const quasar = useQuasar(); const quasar = useQuasar();
const { t } = useI18n(); const { t } = useI18n();
const STATE_COLOR = {
pending: 'positive',
managed: 'warning',
resolved: 'negative',
};
carlossa marked this conversation as resolved
Review

Els introoooooooooooos, tens prettier?

Els introoooooooooooos, tens prettier?
function stateColor(code) { function stateColor(code) {
if (code === 'pending') return 'green'; return STATE_COLOR[code];
if (code === 'managed') return 'orange';
if (code === 'resolved') return 'red';
} }
function navigate(id) { function navigate(id) {
router.push({ path: `/claim/${id}` }); router.push({ path: `/claim/${id}` });
} }

View File

@ -68,7 +68,7 @@ function viewSummary(id) {
</div> </div>
</Teleport> </Teleport>
</template> </template>
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256">
<QScrollArea class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<TicketFilter data-key="TicketList" /> <TicketFilter data-key="TicketList" />
</QScrollArea> </QScrollArea>

View File

@ -47,19 +47,22 @@ const filter = {
], ],
}; };
const sip = computed(() => worker.value.sip && worker.value.sip.extension); const sip = computed(() => worker.value?.sip && worker.value.sip.extension);
function getWorkerAvatar() { function getWorkerAvatar() {
const token = getToken(); const token = getToken();
return `/api/Images/user/160x160/${route.params.id}/download?access_token=${token}`; return `/api/Images/user/160x160/${route.params.id}/download?access_token=${token}`;
} }
const data = ref(useCardDescription()); const data = ref(useCardDescription());
const setData = (entity) => const setData = (entity) => {
(data.value = useCardDescription(entity.user.nickname, entity.id)); if (!entity) return;
data.value = useCardDescription(entity.user.nickname, entity.id);
};
</script> </script>
<template> <template>
<CardDescriptor <CardDescriptor
module="Worker" module="Worker"
data-key="workerData"
:url="`Workers/${entityId}`" :url="`Workers/${entityId}`"
:filter="filter" :filter="filter"
:title="data.title" :title="data.title"
@ -90,8 +93,8 @@ const setData = (entity) =>
</QImg> </QImg>
</template> </template>
<template #body="{ entity }"> <template #body="{ entity }">
<VnLv :label="t('worker.card.name')" :value="entity.user.nickname" /> <VnLv :label="t('worker.card.name')" :value="entity.user?.nickname" />
<VnLv :label="t('worker.card.email')" :value="entity.user.email"> </VnLv> <VnLv :label="t('worker.card.email')" :value="entity.user?.email"> </VnLv>
<VnLv <VnLv
:label="t('worker.list.department')" :label="t('worker.list.department')"
:value="entity.department ? entity.department.department.name : null" :value="entity.department ? entity.department.department.name : null"

View File

@ -19,6 +19,7 @@ export default {
'ClaimLog', 'ClaimLog',
'ClaimNotes', 'ClaimNotes',
'ClaimDevelopment', 'ClaimDevelopment',
'ClaimAction',
], ],
}, },
children: [ children: [
@ -130,6 +131,15 @@ export default {
}, },
component: () => import('src/pages/Claim/Card/ClaimNotes.vue'), component: () => import('src/pages/Claim/Card/ClaimNotes.vue'),
}, },
{
name: 'ClaimAction',
path: 'action',
meta: {
title: 'action',
icon: 'vn:actions',
},
component: () => import('src/pages/Claim/Card/ClaimAction.vue'),
},
], ],
}, },
], ],

View File

@ -18,7 +18,7 @@ export const useArrayDataStore = defineStore('arrayDataStore', () => {
skip: 0, skip: 0,
order: '', order: '',
data: ref(), data: ref(),
isLoading: false isLoading: false,
}; };
} }

View File

@ -0,0 +1,45 @@
/// <reference types="cypress" />
describe('ClaimAction', () => {
const claimId = 2;
const firstRow = 'tbody > :nth-child(1)';
const destinationRow = '.q-item__section > .q-field';
beforeEach(() => {
cy.viewport(1920, 1080);
cy.login('developer');
cy.visit(`/#/claim/${claimId}/action`);
});
it('should import claim', () => {
cy.get('[title="Import claim"]').click();
});
it('should change destination', () => {
carlossa marked this conversation as resolved Outdated
Outdated
Review

Crec q els ultims nulls no fan falta

Crec q els ultims nulls no fan falta
const rowData = [true, null, null, 'Bueno'];
cy.fillRow(firstRow, rowData);
});
it('should change destination from other button', () => {
carlossa marked this conversation as resolved Outdated
Outdated
Review

Aci deuries comprovar que fa lo que esperes

Aci deuries comprovar que fa lo que esperes
const rowData = [true];
carlossa marked this conversation as resolved Outdated
Outdated
Review

Te falta per fer tests a un boto no?
I borrar per a netejar les fixtures

Te falta per fer tests a un boto no? I borrar per a netejar les fixtures
cy.fillRow(firstRow, rowData);
cy.get('[title="Change destination"]').click();
cy.selectOption(destinationRow, 'Confeccion');
cy.get('.q-card > .q-card__actions > .q-btn--standard').click();
});
it('should regularize', () => {
cy.get('[title="Regularize"]').click();
cy.clickConfirm();
});
it('should remove the line', () => {
cy.fillRow(firstRow, [true]);
cy.removeCard();
cy.clickConfirm();
cy.reload();
cy.get(firstRow).should('not.exist');
});
});

View File

@ -88,7 +88,8 @@ Cypress.Commands.add('addCard', () => {
cy.get('.q-page-sticky > div > .q-btn').click(); cy.get('.q-page-sticky > div > .q-btn').click();
}); });
Cypress.Commands.add('clickConfirm', () => { Cypress.Commands.add('clickConfirm', () => {
cy.get('.q-btn--unelevated > .q-btn__content > .block').click(); cy.waitForElement('.q-dialog__inner > .q-card');
cy.get('.q-card__actions > .q-btn--unelevated > .q-btn__content > .block').click();
}); });
Cypress.Commands.add('notificationHas', (selector, text) => { Cypress.Commands.add('notificationHas', (selector, text) => {