feat: #7449 added new button on claimLines, prevented claimAction to import lines with 0 quantity #1550

Open
provira wants to merge 13 commits from 7449-claimBeginningQuantity into dev
12 changed files with 146 additions and 41 deletions

View File

@ -347,8 +347,8 @@ watch(formUrl, async () => {
<QBtnDropdown <QBtnDropdown
v-if="$props.goTo && $props.defaultSave" v-if="$props.goTo && $props.defaultSave"
@click="onSubmitAndGo" @click="onSubmitAndGo"
:label="tMobile('globals.saveAndContinue')" :label="tMobile('globals.saveAndContinue') + ' ' + t('globals.' + $props.goTo.split('/').pop())"
:title="t('globals.saveAndContinue')" :title="t('globals.saveAndContinue') + ' ' + t('globals.' + $props.goTo.split('/').pop())"
:disable="!hasChanges" :disable="!hasChanges"
color="primary" color="primary"
icon="save" icon="save"
@ -397,4 +397,4 @@ watch(formUrl, async () => {
:label="t && t('globals.pleaseWait')" :label="t && t('globals.pleaseWait')"
color="primary" color="primary"
/> />
</template> </template>

View File

@ -380,8 +380,8 @@ defineExpose({
data-cy="saveAndContinueDefaultBtn" data-cy="saveAndContinueDefaultBtn"
v-if="$props.goTo" v-if="$props.goTo"
@click="saveAndGo" @click="saveAndGo"
:label="tMobile('globals.saveAndContinue')" :label="tMobile('globals.saveAndContinue') + ' ' + t('globals.' + $props.goTo.split('/').pop())"
:title="t('globals.saveAndContinue')" :title="t('globals.saveAndContinue') + ' ' + t('globals.' + $props.goTo.split('/').pop())"
:disable="!hasChanges" :disable="!hasChanges"
color="primary" color="primary"
icon="save" icon="save"

View File

@ -1,10 +1,11 @@
<script setup> <script setup>
import axios from 'axios'; import axios from 'axios';
import { ref, reactive, useAttrs, computed } from 'vue'; import { ref, reactive, useAttrs, computed, onMounted, nextTick, } from 'vue';
import { onBeforeRouteLeave } from 'vue-router'; import { onBeforeRouteLeave , useRouter, useRoute} from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useStateStore } from 'stores/useStateStore';
import { tMobile } from 'src/composables/tMobile';
import { toDateHourMin } from 'src/filters'; import { toDateHourMin } from 'src/filters';
import VnPaginate from 'components/ui/VnPaginate.vue'; import VnPaginate from 'components/ui/VnPaginate.vue';
@ -33,10 +34,15 @@ const $props = defineProps({
addNote: { type: Boolean, default: false }, addNote: { type: Boolean, default: false },
selectType: { type: Boolean, default: false }, selectType: { type: Boolean, default: false },
justInput: { type: Boolean, default: false }, justInput: { type: Boolean, default: false },
goTo: { type: String, default: '', },
}); });
const { t } = useI18n(); const { t } = useI18n();
const quasar = useQuasar(); const quasar = useQuasar();
const stateStore = useStateStore();
const router = useRouter();
const route = useRoute();
const componentIsRendered = ref(false);
const newNote = reactive({ text: null, observationTypeFk: null }); const newNote = reactive({ text: null, observationTypeFk: null });
const observationTypes = ref([]); const observationTypes = ref([]);
const vnPaginateRef = ref(); const vnPaginateRef = ref();
@ -45,6 +51,7 @@ const defaultObservationType = computed(() =>
observationTypes.value.find(ot => ot.code === 'salesPerson')?.id observationTypes.value.find(ot => ot.code === 'salesPerson')?.id
); );
let savedNote = false;
let originalText; let originalText;
function handleClick(e) { function handleClick(e) {
@ -68,6 +75,7 @@ async function insert() {
}; };
await axios.post($props.url, newBody); await axios.post($props.url, newBody);
await vnPaginateRef.value.fetch(); await vnPaginateRef.value.fetch();
savedNote = true;
} }
function confirmAndUpdate() { function confirmAndUpdate() {
@ -129,8 +137,29 @@ const handleObservationTypes = (data) => {
} }
}; };
onMounted(() => {
nextTick(() => (componentIsRendered.value = true));
});
async function saveAndGo() {
savedNote = false;
await insert();
await savedNote;
router.push({ path: $props.goTo });
}
</script> </script>
<template> <template>
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown() && componentIsRendered && $props.goTo && !route.path.includes('summary')">
<QBtn
:label="tMobile('globals.saveAndContinue') + ' ' + t('globals.' + $props.goTo.split('/').pop())"
:title="t('globals.saveAndContinue') + ' ' + t('globals.' + $props.goTo.split('/').pop())"
color="primary"
icon="save"
@click="saveAndGo"
data-cy="save-continue-note-button"
/>
</Teleport>
<FetchData <FetchData
v-if="selectType" v-if="selectType"
url="ObservationTypes" url="ObservationTypes"
@ -176,7 +205,7 @@ const handleObservationTypes = (data) => {
:required="'required' in originalAttrs" :required="'required' in originalAttrs"
clearable clearable
> >
<template #append> <template #append v-if="!$props.goTo">
<QBtn <QBtn
:title="t('Save (Enter)')" :title="t('Save (Enter)')"
icon="save" icon="save"

View File

@ -24,13 +24,14 @@ globals:
dataDeleted: Data deleted dataDeleted: Data deleted
delete: Delete delete: Delete
search: Search search: Search
lines: Lines
changes: Changes changes: Changes
dataCreated: Data created dataCreated: Data created
add: Add add: Add
create: Create create: Create
edit: Edit edit: Edit
save: Save save: Save
saveAndContinue: Save and continue saveAndContinue: Save and go to
remove: Remove remove: Remove
reset: Reset reset: Reset
close: Close close: Close
@ -107,6 +108,8 @@ globals:
from: From from: From
to: To to: To
notes: Notes notes: Notes
photos: Photos
due-day: Due day
refresh: Refresh refresh: Refresh
item: Item item: Item
ticket: Ticket ticket: Ticket
@ -394,6 +397,7 @@ errors:
updateUserConfig: Error updating user config updateUserConfig: Error updating user config
tokenConfig: Error fetching token config tokenConfig: Error fetching token config
writeRequest: The requested operation could not be completed writeRequest: The requested operation could not be completed
claimBeginningQuantity: Cannot import a line with a claimed quantity of 0
login: login:
title: Login title: Login
username: Username username: Username

View File

@ -25,12 +25,13 @@ globals:
openDetail: Ver detalle openDetail: Ver detalle
delete: Eliminar delete: Eliminar
search: Buscar search: Buscar
lines: Lineas
changes: Cambios changes: Cambios
add: Añadir add: Añadir
create: Crear create: Crear
edit: Modificar edit: Modificar
save: Guardar save: Guardar
saveAndContinue: Guardar y continuar saveAndContinue: Guardar e ir a
remove: Eliminar remove: Eliminar
reset: Restaurar reset: Restaurar
close: Cerrar close: Cerrar
@ -111,6 +112,8 @@ globals:
from: Desde from: Desde
to: Hasta to: Hasta
notes: Notas notes: Notas
photos: Fotos
due-day: Vencimiento
refresh: Actualizar refresh: Actualizar
item: Artículo item: Artículo
ticket: Ticket ticket: Ticket
@ -390,6 +393,7 @@ errors:
updateUserConfig: Error al actualizar la configuración de usuario updateUserConfig: Error al actualizar la configuración de usuario
tokenConfig: Error al obtener configuración de token tokenConfig: Error al obtener configuración de token
writeRequest: No se pudo completar la operación solicitada writeRequest: No se pudo completar la operación solicitada
claimBeginningQuantity: No se puede importar una linea sin una cantidad reclamada
login: login:
title: Inicio de sesión title: Inicio de sesión
username: Nombre de usuario username: Nombre de usuario

View File

@ -13,8 +13,10 @@ import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
import RightMenu from 'src/components/common/RightMenu.vue'; import RightMenu from 'src/components/common/RightMenu.vue';
import useNotify from 'src/composables/useNotify.js';
const { t } = useI18n(); const { t } = useI18n();
const { notify } = useNotify();
const quasar = useQuasar(); const quasar = useQuasar();
const route = useRoute(); const route = useRoute();
const claim = ref(null); const claim = ref(null);
@ -176,12 +178,17 @@ async function save(data) {
} }
async function importToNewRefundTicket() { async function importToNewRefundTicket() {
await post(`ClaimBeginnings/${claimId}/importToNewRefundTicket`); try{
await claimActionsForm.value.reload(); await post(`ClaimBeginnings/${claimId}/importToNewRefundTicket`);
quasar.notify({ await claimActionsForm.value.reload();
message: t('globals.dataSaved'), quasar.notify({
type: 'positive', message: t('globals.dataSaved'),
}); type: 'positive',
});
} catch (error) {
const errorMessage = error.response?.data?.error?.message;
notify( t(errorMessage), 'negative' );
}
} }
async function post(query, params) { async function post(query, params) {

View File

@ -33,6 +33,7 @@ function onBeforeSave(formData, originalData) {
<FetchData url="ClaimStates" @on-fetch="setClaimStates" auto-load /> <FetchData url="ClaimStates" @on-fetch="setClaimStates" auto-load />
<FormModel <FormModel
model="Claim" model="Claim"
:go-to="`/claim/${route.params.id}/notes`"
:url-update="`Claims/updateClaim/${route.params.id}`" :url-update="`Claims/updateClaim/${route.params.id}`"
:mapper="onBeforeSave" :mapper="onBeforeSave"
auto-load auto-load

View File

@ -78,6 +78,8 @@ const columns = computed(() => [
label: t('Quantity'), label: t('Quantity'),
field: ({ sale }) => sale.quantity, field: ({ sale }) => sale.quantity,
sortable: true, sortable: true,
style: 'padding-right: 20px;',
headerStyle: 'padding-right: 20px;'
}, },
{ {
name: 'claimed', name: 'claimed',
@ -110,6 +112,8 @@ const columns = computed(() => [
field: ({ sale }) => totalRow(sale), field: ({ sale }) => totalRow(sale),
format: (value) => toCurrency(value), format: (value) => toCurrency(value),
sortable: true, sortable: true,
style: 'padding-right: 20px;',
headerStyle: 'padding-right: 20px;'
}, },
]); ]);
@ -152,12 +156,33 @@ function showImportDialog() {
.onOk(() => claimLinesForm.value.reload()); .onOk(() => claimLinesForm.value.reload());
} }
async function saveWhenHasChanges() { function fillClaimedQuantities() {
if (claimLinesForm.value.getChanges().updates) { const formData = claimLinesForm.value.formData;
await claimLinesForm.value.onSubmit(); let hasChanges = false;
onFetch(claimLinesForm.value.formData);
const selectedRows = formData.filter(row => selected.value.includes(row));
for (const row of selectedRows) {
if (row.quantity === 0 || row.quantity === null) {
row.quantity = row.sale.quantity;
hasChanges = true;
}
}
if (hasChanges) {
quasar.notify({
message: t('Cantidades rellenadas automáticamente'),
type: 'positive',
});
} else {
quasar.notify({
message: t('No hay cantidades para rellenar'),
type: 'info',
});
} }
} }
</script> </script>
<template> <template>
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()"> <Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()">
@ -185,15 +210,16 @@ async function saveWhenHasChanges() {
auto-load auto-load
/> />
<div class="q-pa-md"> <div class="q-pa-md">
<CrudModel <CrudModel
data-key="ClaimLines" data-key="ClaimLines"
ref="claimLinesForm" ref="claimLinesForm"
:go-to="`photos`"
:url="`Claims/${route.params.id}/lines`" :url="`Claims/${route.params.id}/lines`"
save-url="ClaimBeginnings/crud" save-url="ClaimBeginnings/crud"
:user-filter="linesFilter" :user-filter="linesFilter"
@on-fetch="onFetch" @on-fetch="onFetch"
v-model:selected="selected" v-model:selected="selected"
:default-save="false"
:default-reset="false" :default-reset="false"
auto-load auto-load
:limit="0" :limit="0"
@ -214,8 +240,7 @@ async function saveWhenHasChanges() {
v-model.number="row.quantity" v-model.number="row.quantity"
type="number" type="number"
dense dense
@keyup.enter="saveWhenHasChanges()"
@blur="saveWhenHasChanges()"
/> />
</QTd> </QTd>
</template> </template>
@ -272,10 +297,7 @@ async function saveWhenHasChanges() {
type="number" type="number"
dense dense
autofocus autofocus
@keyup.enter="
saveWhenHasChanges()
"
@blur="saveWhenHasChanges()"
/> />
</QItemLabel> </QItemLabel>
</template> </template>
@ -313,6 +335,18 @@ async function saveWhenHasChanges() {
</template> </template>
</QTable> </QTable>
</template> </template>
<template #moreBeforeActions>
<QBtn
color="primary"
text-color="white"
:unelevated="true"
:label="t('Rellenar cantidades')"
:title="t('Rellenar cantidades')"
icon="auto_fix_high"
:disabled="!selected.length"
@click="fillClaimedQuantities"
/>
</template>
</CrudModel> </CrudModel>
</div> </div>

View File

@ -35,9 +35,11 @@ const body = {
workerFk: user.value.id, workerFk: user.value.id,
}; };
</script> </script>
<template> <template>
<VnNotes <VnNotes
url="claimObservations" url="claimObservations"
:go-to="`/claim/${route.params.id}/lines`"
:add-note="$props.addNote" :add-note="$props.addNote"
:user-filter="claimFilter" :user-filter="claimFilter"
:filter="{ where: { claimFk: claimId } }" :filter="{ where: { claimFk: claimId } }"

View File

@ -8,9 +8,9 @@ const claimCard = {
meta: { meta: {
menu: [ menu: [
'ClaimBasicData', 'ClaimBasicData',
'ClaimNotes',
'ClaimLines', 'ClaimLines',
'ClaimPhotos', 'ClaimPhotos',
'ClaimNotes',
'ClaimDevelopment', 'ClaimDevelopment',
'ClaimAction', 'ClaimAction',
'ClaimLog', 'ClaimLog',
@ -36,6 +36,15 @@ const claimCard = {
}, },
component: () => import('src/pages/Claim/Card/ClaimBasicData.vue'), component: () => import('src/pages/Claim/Card/ClaimBasicData.vue'),
}, },
{
path: 'notes',
name: 'ClaimNotes',
meta: {
title: 'notes',
icon: 'draft',
},
component: () => import('src/pages/Claim/Card/ClaimNotes.vue'),
},
{ {
path: 'lines', path: 'lines',
name: 'ClaimLines', name: 'ClaimLines',
@ -54,15 +63,6 @@ const claimCard = {
}, },
component: () => import('src/pages/Claim/Card/ClaimPhoto.vue'), component: () => import('src/pages/Claim/Card/ClaimPhoto.vue'),
}, },
{
path: 'notes',
name: 'ClaimNotes',
meta: {
title: 'notes',
icon: 'draft',
},
component: () => import('src/pages/Claim/Card/ClaimNotes.vue'),
},
{ {
path: 'development', path: 'development',
name: 'ClaimDevelopment', name: 'ClaimDevelopment',

View File

@ -0,0 +1,25 @@
/// <reference types="cypress" />
describe('ClaimLines', () => {
const claimId = 1;
beforeEach(() => {
cy.viewport(1920, 1080);
cy.login('developer');
cy.visit(`/#/claim/${claimId}/lines`);
});
it('should add new line', () => {
cy.get('.q-page-sticky > div > .q-btn').click();
cy.get('.q-card > .q-table__container > .q-table__middle > .q-table > thead > tr > .q-table--col-auto-width > .q-checkbox > .q-checkbox__inner > .q-checkbox__bg').click();
cy.get('.q-card__actions > .q-btn--unelevated').click();
cy.get('.q-notification__message').should('have.text', 'Lines added to claim');
})
it('should autofill claimed quantity if 0 or null', () => {
cy.get('thead > tr > .q-table--col-auto-width > .q-checkbox > .q-checkbox__inner > .q-checkbox__bg').click();
cy.get('.q-btn--unelevated').click();
cy.checkNotification('Cantidades rellenadas automáticamente');
cy.get('.q-btn--unelevated').click();
cy.checkNotification('No hay cantidades para rellenar');
})
});

View File

@ -1,5 +1,4 @@
describe('ClaimNotes', () => { describe('ClaimNotes', () => {
const saveBtn = '.q-field__append > .q-btn > .q-btn__content > .q-icon';
const firstNote = '.q-infinite-scroll :nth-child(1) > .q-card__section--vert'; const firstNote = '.q-infinite-scroll :nth-child(1) > .q-card__section--vert';
beforeEach(() => { beforeEach(() => {
cy.login('developer'); cy.login('developer');
@ -11,9 +10,9 @@ describe('ClaimNotes', () => {
cy.get('.q-textarea') cy.get('.q-textarea')
.should('be.visible') .should('be.visible')
.should('not.be.disabled') .should('not.be.disabled')
.type(message); .type(message, "{{enter}}");
cy.get(saveBtn).click(); cy.dataCy("save-continue-note-button").click();
cy.get(firstNote).should('have.text', message); cy.get(firstNote).should('have.text', message);
}); });
}); });