From 4ed1021a67c3c714807ee3fbf233d073e727769c Mon Sep 17 00:00:00 2001 From: pablone <pablone@verdnatura.es> Date: Fri, 21 Mar 2025 13:19:43 +0100 Subject: [PATCH 1/8] feat: refs #8638 add AWB field to travel and entry forms, update translations and styles --- src/components/VnTable/VnTable.vue | 2 +- src/components/common/VnDmsInput.vue | 166 ++++++++++++++++++ src/css/app.scss | 1 - src/i18n/locale/en.yml | 1 + src/i18n/locale/es.yml | 1 + src/pages/Entry/Card/EntryBasicData.vue | 47 +++-- src/pages/Entry/Card/EntryBuys.vue | 64 ++++++- src/pages/Entry/Card/EntryDescriptor.vue | 4 +- src/pages/Entry/Card/EntrySummary.vue | 6 +- src/pages/Entry/EntryStockBought.vue | 15 +- src/pages/Entry/locale/es.yml | 11 +- .../InvoiceIn/Card/InvoiceInBasicData.vue | 122 +------------ src/pages/Travel/Card/TravelBasicData.vue | 7 +- src/pages/Travel/Card/TravelFilter.js | 1 + 14 files changed, 290 insertions(+), 158 deletions(-) create mode 100644 src/components/common/VnDmsInput.vue diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index 6a547d95d..9ad032ee5 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -1154,7 +1154,7 @@ es: .grid-create { display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: repeat(auto-fit, minmax(150px, max-content)); max-width: 100%; grid-gap: 20px; margin: 0 auto; diff --git a/src/components/common/VnDmsInput.vue b/src/components/common/VnDmsInput.vue new file mode 100644 index 000000000..25d625d5d --- /dev/null +++ b/src/components/common/VnDmsInput.vue @@ -0,0 +1,166 @@ +<script setup> +import VnConfirm from '../ui/VnConfirm.vue'; +import VnInput from './VnInput.vue'; +import VnDms from './VnDms.vue'; +import axios from 'axios'; +import { useQuasar } from 'quasar'; +import { ref } from 'vue'; +import { useI18n } from 'vue-i18n'; +import { downloadFile } from 'src/composables/downloadFile'; + +const { t } = useI18n(); +const quasar = useQuasar(); +const documentDialogRef = ref({}); +const editDownloadDisabled = ref(false); +const $props = defineProps({ + defaultDmsCode: { + type: String, + default: 'InvoiceIn', + }, + disable: { + type: Boolean, + default: true, + }, + data: { + type: Object, + default: null, + }, + formRef: { + type: Object, + default: null, + }, +}); + +function deleteFile(dmsFk) { + quasar + .dialog({ + component: VnConfirm, + componentProps: { + title: t('globals.confirmDeletion'), + message: t('globals.confirmDeletionMessage'), + }, + }) + .onOk(async () => { + await axios.post(`dms/${dmsFk}/removeFile`); + $props.formRef.formData.dmsFk = null; + $props.formRef.formData.dms = undefined; + $props.formRef.hasChanges = true; + $props.formRef.save(); + }); +} +</script> +<template> + <div class="row no-wrap"> + <VnInput + :label="t('Document')" + v-model="data.dmsFk" + clearable + clear-icon="close" + class="full-width" + :disable="disable" + /> + <div + v-if="data.dmsFk" + class="row no-wrap q-pa-xs q-gutter-x-xs" + data-cy="dms-buttons" + > + <QBtn + :disable="editDownloadDisabled" + @click="downloadFile(data.dmsFk)" + icon="cloud_download" + color="primary" + flat + :class="{ + 'no-pointer-events': editDownloadDisabled, + }" + padding="xs" + round + > + <QTooltip>{{ t('Download file') }}</QTooltip> + </QBtn> + <QBtn + :disable="editDownloadDisabled" + @click=" + () => { + documentDialogRef.show = true; + documentDialogRef.dms = data.dms; + } + " + icon="edit" + color="primary" + flat + :class="{ + 'no-pointer-events': editDownloadDisabled, + }" + padding="xs" + round + > + <QTooltip>{{ t('Edit document') }}</QTooltip> + </QBtn> + <QBtn + :disable="editDownloadDisabled" + @click="deleteFile(data.dmsFk)" + icon="delete" + color="primary" + flat + round + :class="{ + 'no-pointer-events': editDownloadDisabled, + }" + padding="xs" + > + <QTooltip>{{ t('Delete file') }}</QTooltip> + </QBtn> + </div> + <QBtn + v-else + icon="add_circle" + color="primary" + flat + round + v-shortcut="'+'" + padding="xs" + @click=" + () => { + documentDialogRef.show = true; + delete documentDialogRef.dms; + } + " + data-cy="dms-create" + > + <QTooltip>{{ t('Create document') }}</QTooltip> + </QBtn> + </div> + <QDialog v-model="documentDialogRef.show"> + <VnDms + model="dms" + :default-dms-code="defaultDmsCode" + :form-initial-data="documentDialogRef.dms" + :url=" + documentDialogRef.dms + ? `Dms/${documentDialogRef.dms.id}/updateFile` + : 'Dms/uploadFile' + " + :description="documentDialogRef.supplierName" + @on-data-saved=" + (_, { data }) => { + let dmsData = data; + if (Array.isArray(data)) dmsData = data[0]; + formRef.formData.dmsFk = dmsData.id; + formRef.formData.dms = dmsData; + formRef.hasChanges = true; + formRef.save(); + } + " + /> + </QDialog> +</template> +<i18n> +es: + Document: Documento + Download file: Descargar archivo + Edit document: Editar documento + Delete file: Eliminar archivo + Create document: Crear documento + +</i18n> diff --git a/src/css/app.scss b/src/css/app.scss index 5befd150b..b299973d1 100644 --- a/src/css/app.scss +++ b/src/css/app.scss @@ -325,7 +325,6 @@ input::-webkit-inner-spin-button { min-height: auto !important; display: flex; align-items: flex-end; - padding-bottom: 2px; .q-field__native.row { min-height: auto !important; } diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index c1286267c..594722b96 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -816,6 +816,7 @@ travel: search: Search travel searchInfo: You can search by travel id or name id: Id + awbFk: AWB travelList: tableVisibleColumns: ref: Reference diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 681781d11..a0eb3835d 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -899,6 +899,7 @@ travel: search: Buscar envío searchInfo: Buscar envío por id o nombre id: Id + awbFk: Guía aérea travelList: tableVisibleColumns: ref: Referencia diff --git a/src/pages/Entry/Card/EntryBasicData.vue b/src/pages/Entry/Card/EntryBasicData.vue index 34e4a0f9c..f6d15a977 100644 --- a/src/pages/Entry/Card/EntryBasicData.vue +++ b/src/pages/Entry/Card/EntryBasicData.vue @@ -14,6 +14,8 @@ import VnInputNumber from 'src/components/common/VnInputNumber.vue'; import VnSelectTravelExtended from 'src/components/common/VnSelectTravelExtended.vue'; import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue'; import VnCheckbox from 'src/components/common/VnCheckbox.vue'; +import VnSelectWorker from 'src/components/common/VnSelectWorker.vue'; +import VnDmsInput from 'src/components/common/VnDmsInput.vue'; const route = useRoute(); const { t } = useI18n(); @@ -24,6 +26,7 @@ const user = state.getUser().fn(); const companiesOptions = ref([]); const currenciesOptions = ref([]); +const entryRef = ref({}); onMounted(() => { checkEntryLock(route.params.id, user.id); @@ -48,10 +51,11 @@ onMounted(() => { auto-load /> <FormModel - :url-update="`Entries/${route.params.id}`" + ref="entryRef" model="Entry" - auto-load + :url-update="`Entries/${route.params.id}`" :clear-store-on-unmount="false" + auto-load > <template #form="{ data }"> <VnRow class="q-py-sm"> @@ -67,11 +71,18 @@ onMounted(() => { /> </VnRow> <VnRow class="q-py-sm"> - <VnInput v-model="data.reference" :label="t('globals.reference')" /> - <VnInputNumber - v-model="data.invoiceAmount" - :label="t('entry.summary.invoiceAmount')" - :positive="false" + <VnInput + v-model="data.reference" + :label="t('entry.list.tableVisibleColumns.reference')" + /> + <VnSelect + v-model="data.typeFk" + url="entryTypes" + :fields="['code', 'description']" + option-value="code" + optionLabel="description" + sortBy="description" + :label="t('entry.list.tableVisibleColumns.entryTypeDescription')" /> </VnRow> <VnRow class="q-py-sm"> @@ -113,7 +124,6 @@ onMounted(() => { name="initialTemperature" :label="t('entry.basicData.initialTemperature')" :step="0.5" - :decimal-places="2" :positive="false" /> <VnInputNumber @@ -121,20 +131,21 @@ onMounted(() => { name="finalTemperature" :label="t('entry.basicData.finalTemperature')" :step="0.5" - :decimal-places="2" :positive="false" /> - <VnSelect - v-model="data.typeFk" - url="entryTypes" - :fields="['code', 'description']" - option-value="code" - optionLabel="description" - sortBy="description" - /> </VnRow> <VnRow class="q-py-sm"> - <QInput + <VnInputNumber + v-model="data.invoiceAmount" + :label="t('entry.list.tableVisibleColumns.invoiceAmount')" + :positive="false" + @update:model-value="data.buyerFk = user.id" + /> + <VnSelectWorker v-model="data.buyerFk" hide-selected /> + <VnDmsInput :data="data" :formRef="entryRef" :disable="false" /> + </VnRow> + <VnRow class="q-py-sm"> + <VnInputNumber :label="t('entry.basicData.observation')" type="textarea" v-model="data.observation" diff --git a/src/pages/Entry/Card/EntryBuys.vue b/src/pages/Entry/Card/EntryBuys.vue index 3990fde19..85da5cf1d 100644 --- a/src/pages/Entry/Card/EntryBuys.vue +++ b/src/pages/Entry/Card/EntryBuys.vue @@ -18,6 +18,7 @@ import VnSelectEnum from 'src/components/common/VnSelectEnum.vue'; import { checkEntryLock } from 'src/composables/checkEntryLock'; import VnRow from 'src/components/ui/VnRow.vue'; import VnInput from 'src/components/common/VnInput.vue'; +import VnInputNumber from 'src/components/common/VnInputNumber.vue'; const $props = defineProps({ id: { @@ -44,6 +45,8 @@ const entityId = ref($props.id ?? route.params.id); const entryBuysRef = ref(); const footerFetchDataRef = ref(); const footer = ref({}); +const dialogRef = ref(false); +const newEntryRef = ref(null); const columns = [ { align: 'center', @@ -250,6 +253,7 @@ const columns = [ component: 'number', attrs: { positive: false, + decimalPlaces: 3, }, cellEvent: { 'update:modelValue': async (value, oldValue, row) => { @@ -497,6 +501,23 @@ async function setBuyUltimate(itemFk, data) { }); } +async function transferBuys(rows, newEntry) { + if (!newEntry) return; + + const promises = rows.map((row) => { + return axios.patch('Buys', { id: row.id, entryFk: newEntry }); + }); + + await Promise.all(promises); + + await axios.post(`Entries/${newEntry}/recalcEntryPrices`); + await axios.post(`Entries/${entityId.value}/recalcEntryPrices`); + + entryBuysRef.value.reload(); + newEntryRef.value = null; + dialogRef.value = false; +} + onMounted(() => { stateStore.rightDrawer = false; if ($props.editableMode) checkEntryLock(entityId.value, user.id); @@ -571,6 +592,45 @@ onMounted(() => { </QItem> </QList> </QBtnDropdown> + <QBtn + icon="move_group" + color="primary" + :title="t('Transfer buys')" + flat + @click="dialogRef = true" + :disable="!selectedRows.length" + /> + <QDialog v-model="dialogRef"> + <QCard> + <QCardSection> + <span>{{ t('Transfer buys') }}</span> + </QCardSection> + <QCardSection> + <VnInputNumber + v-model="newEntryRef" + :label="t('Entry')" + type="number" + data-cy="transfer-buy-entry" + /> + </QCardSection> + <QCardSection> + <QCardActions> + <QBtn + label="Cancel" + flat + color="primary" + @click="dialogRef = false" + /> + <QBtn + label="Transfer" + flat + color="primary" + @click="transferBuys(selectedRows, newEntryRef)" + /> + </QCardActions> + </QCardSection> + </QCard> + </QDialog> </QBtnGroup> </Teleport> <FetchData @@ -620,7 +680,7 @@ onMounted(() => { }, columnGridStyle: { 'max-width': '50%', - 'margin-right': '30px', + 'margin-right': '5%', flex: 1, }, previousStyle: { @@ -816,6 +876,8 @@ es: Create buy: Crear compra Invert quantity value: Invertir valor de cantidad Check buy amount: Marcar como correcta la cantidad de compra + Transfer buys: Transferir compras + Entry: Entrada </i18n> <style lang="scss" scoped> .centered-container { diff --git a/src/pages/Entry/Card/EntryDescriptor.vue b/src/pages/Entry/Card/EntryDescriptor.vue index 313ed3d72..784f6e8a3 100644 --- a/src/pages/Entry/Card/EntryDescriptor.vue +++ b/src/pages/Entry/Card/EntryDescriptor.vue @@ -92,7 +92,7 @@ const getEntryRedirectionFilter = (entry) => { }; function showEntryReport() { - openReport(`Entries/${entityId.value}/entry-order-pdf`); + openReport(`Entries/${entityId.value}/entry-order-pdf`, {}, true); } function showNotification(type, message) { @@ -147,7 +147,7 @@ async function deleteEntry() { <template> <CardDescriptor :url="`Entries/${entityId}`" - :filter="entryFilter" + :user-filter="entryFilter" title="supplier.nickname" data-key="Entry" width="lg-width" diff --git a/src/pages/Entry/Card/EntrySummary.vue b/src/pages/Entry/Card/EntrySummary.vue index 53967e66f..37a28968c 100644 --- a/src/pages/Entry/Card/EntrySummary.vue +++ b/src/pages/Entry/Card/EntrySummary.vue @@ -84,7 +84,10 @@ onMounted(async () => { :label="t('globals.company')" :value="entry?.company?.code" /> - <VnLv :label="t('globals.reference')" :value="entry?.reference" /> + <VnLv + :label="t('entry.list.tableVisibleColumns.reference')" + :value="entry?.reference" + /> <VnLv :label="t('entry.summary.invoiceNumber')" :value="entry?.invoiceNumber" @@ -159,6 +162,7 @@ onMounted(async () => { /> </div> <div class="card-content"> + <VnLv :label="t('travel.awbFk')" :value="entry.travel.awbFk" /> <VnCheckbox :label="t('entry.summary.travelDelivered')" v-model="entry.travel.isDelivered" diff --git a/src/pages/Entry/EntryStockBought.vue b/src/pages/Entry/EntryStockBought.vue index 5da51d5a6..6168f0737 100644 --- a/src/pages/Entry/EntryStockBought.vue +++ b/src/pages/Entry/EntryStockBought.vue @@ -162,8 +162,8 @@ async function beforeSave(data, getChanges) { } await Promise.all(patchPromises); - const filteredChanges = changes.filter((change) => change?.isReal !== false); - data.creates = filteredChanges; + data.creates = []; + return data; } </script> <template> @@ -203,7 +203,7 @@ async function beforeSave(data, getChanges) { </VnRow> </template> </VnSubToolbar> - <QDialog v-model="travelDialogRef" :maximized="true" :class="['vn-row', 'wrap']"> + <QDialog v-model="travelDialogRef" :class="['vn-row', 'wrap']"> <FormModelPopup :url-update="`Travels/${travel?.id}`" model="travel" @@ -252,12 +252,15 @@ async function beforeSave(data, getChanges) { </span> </template> <template #column-footer-reserve> - <span> + <span class="q-pr-xs"> {{ round(footer.reserve) }} </span> </template> <template #column-footer-bought> - <span :style="boughtStyle(footer?.bought, footer?.reserve)"> + <span + :style="boughtStyle(footer?.bought, footer?.reserve)" + class="q-pr-xs" + > {{ round(footer.bought) }} </span> </template> @@ -275,7 +278,7 @@ async function beforeSave(data, getChanges) { } .column { min-width: 35%; - margin-top: 5%; + margin-top: 1%; } .text-negative { color: $negative !important; diff --git a/src/pages/Entry/locale/es.yml b/src/pages/Entry/locale/es.yml index 10d863ea2..2c80299bc 100644 --- a/src/pages/Entry/locale/es.yml +++ b/src/pages/Entry/locale/es.yml @@ -25,7 +25,7 @@ entry: entryTypeDescription: Tipo entrada invoiceAmount: Importe dated: Fecha - inventoryEntry: Es inventario + inventoryEntry: Es inventario summary: commission: Comisión currency: Moneda @@ -33,7 +33,8 @@ entry: invoiceAmount: Importe ordered: Pedida booked: Contabilizada - excludedFromAvailable: Excluido + excludedFromAvailable: Excluir del disponible + isConfirmed: Lista para etiquetar travelReference: Referencia travelAgency: Agencia travelShipped: F. envio @@ -56,7 +57,7 @@ entry: observation: Observación commission: Comisión booked: Contabilizada - excludedFromAvailable: Excluido + excludedFromAvailable: Excluir del disponible initialTemperature: Ini °C finalTemperature: Fin °C buys: @@ -119,9 +120,9 @@ entry: supplierName: Proveedor entryFilter: params: - isExcludedFromAvailable: Excluido + isExcludedFromAvailable: Excluir del disponible isOrdered: Pedida - isConfirmed: Confirmado + isConfirmed: Lista para etiquetar isReceived: Recibida isRaid: Raid landed: Fecha diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue index 905ddebb2..0995b75b9 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue @@ -2,24 +2,18 @@ import { ref, computed } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; -import { useQuasar } from 'quasar'; -import { downloadFile } from 'src/composables/downloadFile'; import FormModel from 'components/FormModel.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; import FetchData from 'src/components/FetchData.vue'; import VnRow from 'components/ui/VnRow.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInput from 'src/components/common/VnInput.vue'; -import VnDms from 'src/components/common/VnDms.vue'; -import VnConfirm from 'src/components/ui/VnConfirm.vue'; -import axios from 'axios'; import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue'; +import VnDmsInput from 'src/components/common/VnDmsInput.vue'; const { t } = useI18n(); const route = useRoute(); -const quasar = useQuasar(); -const editDownloadDisabled = ref(false); const userConfig = ref(null); const invoiceId = computed(() => +route.params.id); @@ -37,24 +31,6 @@ const allowedContentTypes = ref([]); const sageWithholdings = ref([]); const documentDialogRef = ref({}); const invoiceInRef = ref({}); - -function deleteFile(dmsFk) { - quasar - .dialog({ - component: VnConfirm, - componentProps: { - title: t('globals.confirmDeletion'), - message: t('globals.confirmDeletionMessage'), - }, - }) - .onOk(async () => { - await axios.post(`dms/${dmsFk}/removeFile`); - invoiceInRef.value.formData.dmsFk = null; - invoiceInRef.value.formData.dms = undefined; - invoiceInRef.value.hasChanges = true; - invoiceInRef.value.save(); - }); -} </script> <template> <FetchData @@ -157,78 +133,7 @@ function deleteFile(dmsFk) { </QItem> </template> </VnSelect> - - <div class="row no-wrap"> - <VnInput - :label="t('Document')" - v-model="data.dmsFk" - clearable - clear-icon="close" - class="full-width" - :disable="true" - /> - <div - v-if="data.dmsFk" - class="row no-wrap q-pa-xs q-gutter-x-xs" - data-cy="dms-buttons" - > - <QBtn - :class="{ - 'no-pointer-events': editDownloadDisabled, - }" - :disable="editDownloadDisabled" - icon="cloud_download" - :title="t('Download file')" - padding="xs" - round - @click="downloadFile(data.dmsFk)" - /> - <QBtn - :class="{ - 'no-pointer-events': editDownloadDisabled, - }" - :disable="editDownloadDisabled" - icon="edit" - round - padding="xs" - @click=" - () => { - documentDialogRef.show = true; - documentDialogRef.dms = data.dms; - } - " - > - <QTooltip>{{ t('Edit document') }}</QTooltip> - </QBtn> - <QBtn - :class="{ - 'no-pointer-events': editDownloadDisabled, - }" - :disable="editDownloadDisabled" - icon="delete" - :title="t('Delete file')" - padding="xs" - round - @click="deleteFile(data.dmsFk)" - /> - </div> - <QBtn - v-else - icon="add_circle" - round - v-shortcut="'+'" - padding="xs" - @click=" - () => { - documentDialogRef.show = true; - delete documentDialogRef.dms; - } - " - data-cy="dms-create" - > - <QTooltip>{{ t('Create document') }}</QTooltip> - </QBtn> - </div> + <VnDmsInput :data="data" :formRef="invoiceInRef" /> </VnRow> <VnRow> <VnSelect @@ -264,29 +169,6 @@ function deleteFile(dmsFk) { </VnRow> </template> </FormModel> - <QDialog v-model="documentDialogRef.show"> - <VnDms - model="dms" - default-dms-code="invoiceIn" - :form-initial-data="documentDialogRef.dms" - :url=" - documentDialogRef.dms - ? `Dms/${documentDialogRef.dms.id}/updateFile` - : 'Dms/uploadFile' - " - :description="documentDialogRef.supplierName" - @on-data-saved=" - (_, { data }) => { - let dmsData = data; - if (Array.isArray(data)) dmsData = data[0]; - invoiceInRef.formData.dmsFk = dmsData.id; - invoiceInRef.formData.dms = dmsData; - invoiceInRef.hasChanges = true; - invoiceInRef.save(); - } - " - /> - </QDialog> </template> <style lang="scss" scoped> @media (max-width: $breakpoint-xs) { diff --git a/src/pages/Travel/Card/TravelBasicData.vue b/src/pages/Travel/Card/TravelBasicData.vue index b1adc8126..a6ef8ad19 100644 --- a/src/pages/Travel/Card/TravelBasicData.vue +++ b/src/pages/Travel/Card/TravelBasicData.vue @@ -36,7 +36,7 @@ const warehousesOptionsIn = ref([]); auto-load :filter="{ where: { isDestiny: TRUE } }" /> - <FormModel :url-update="`Travels/${route.params.id}`" model="Travel" auto-load> + <FormModel :url-update="`Travels/${route.params.id}`" model="Travel"> <template #form="{ data }"> <VnRow> <VnInput v-model="data.ref" :label="t('globals.reference')" /> @@ -57,8 +57,8 @@ const warehousesOptionsIn = ref([]); <VnRow> <VnInputDate v-model="data.availabled" - :label="t('travel.summary.availabled')" - /> + :label="t('travel.summary.availabled')" + /> <VnInputTime v-model="data.availabled" :label="t('travel.summary.availabledHour')" @@ -96,6 +96,7 @@ const warehousesOptionsIn = ref([]); </QIcon> </template> </VnInput> + <VnInput v-model="data.awbFk" :label="t('travel.awbFk')" /> </VnRow> <VnRow> <QCheckbox v-model="data.isRaid" :label="t('travel.basicData.isRaid')" /> diff --git a/src/pages/Travel/Card/TravelFilter.js b/src/pages/Travel/Card/TravelFilter.js index 05436834f..0799e449c 100644 --- a/src/pages/Travel/Card/TravelFilter.js +++ b/src/pages/Travel/Card/TravelFilter.js @@ -12,6 +12,7 @@ export default { 'isRaid', 'daysInForward', 'availabled', + 'awbFk', ], include: [ { From a39f648da045428d1eec2b371d9fc54c9b511fcb Mon Sep 17 00:00:00 2001 From: pablone <pablone@verdnatura.es> Date: Fri, 21 Mar 2025 13:25:17 +0100 Subject: [PATCH 2/8] fix: refs #8638 update comment formatting in VnTable.vue --- src/components/VnTable/VnTable.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index 9ad032ee5..8f64dc857 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -895,7 +895,7 @@ const rowCtrlClickFunction = computed(() => { {{ row[splittedColumns.title.name] }} </span> </QCardSection> - <!-- Fields --> + <!-- Fields --> <QCardSection class="q-pl-sm q-py-xs" :class="$props.cardClass" From eb6046f3382b9500563fd3676669931247babb25 Mon Sep 17 00:00:00 2001 From: pablone <pablone@verdnatura.es> Date: Fri, 21 Mar 2025 13:28:22 +0100 Subject: [PATCH 3/8] fix: refs #8638 restore invoiceInBasicData --- .../InvoiceIn/Card/InvoiceInBasicData.vue | 122 +++++++++++++++++- 1 file changed, 120 insertions(+), 2 deletions(-) diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue index 0995b75b9..905ddebb2 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue @@ -2,18 +2,24 @@ import { ref, computed } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; +import { useQuasar } from 'quasar'; +import { downloadFile } from 'src/composables/downloadFile'; import FormModel from 'components/FormModel.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; import FetchData from 'src/components/FetchData.vue'; import VnRow from 'components/ui/VnRow.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInput from 'src/components/common/VnInput.vue'; +import VnDms from 'src/components/common/VnDms.vue'; +import VnConfirm from 'src/components/ui/VnConfirm.vue'; +import axios from 'axios'; import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue'; -import VnDmsInput from 'src/components/common/VnDmsInput.vue'; const { t } = useI18n(); const route = useRoute(); +const quasar = useQuasar(); +const editDownloadDisabled = ref(false); const userConfig = ref(null); const invoiceId = computed(() => +route.params.id); @@ -31,6 +37,24 @@ const allowedContentTypes = ref([]); const sageWithholdings = ref([]); const documentDialogRef = ref({}); const invoiceInRef = ref({}); + +function deleteFile(dmsFk) { + quasar + .dialog({ + component: VnConfirm, + componentProps: { + title: t('globals.confirmDeletion'), + message: t('globals.confirmDeletionMessage'), + }, + }) + .onOk(async () => { + await axios.post(`dms/${dmsFk}/removeFile`); + invoiceInRef.value.formData.dmsFk = null; + invoiceInRef.value.formData.dms = undefined; + invoiceInRef.value.hasChanges = true; + invoiceInRef.value.save(); + }); +} </script> <template> <FetchData @@ -133,7 +157,78 @@ const invoiceInRef = ref({}); </QItem> </template> </VnSelect> - <VnDmsInput :data="data" :formRef="invoiceInRef" /> + + <div class="row no-wrap"> + <VnInput + :label="t('Document')" + v-model="data.dmsFk" + clearable + clear-icon="close" + class="full-width" + :disable="true" + /> + <div + v-if="data.dmsFk" + class="row no-wrap q-pa-xs q-gutter-x-xs" + data-cy="dms-buttons" + > + <QBtn + :class="{ + 'no-pointer-events': editDownloadDisabled, + }" + :disable="editDownloadDisabled" + icon="cloud_download" + :title="t('Download file')" + padding="xs" + round + @click="downloadFile(data.dmsFk)" + /> + <QBtn + :class="{ + 'no-pointer-events': editDownloadDisabled, + }" + :disable="editDownloadDisabled" + icon="edit" + round + padding="xs" + @click=" + () => { + documentDialogRef.show = true; + documentDialogRef.dms = data.dms; + } + " + > + <QTooltip>{{ t('Edit document') }}</QTooltip> + </QBtn> + <QBtn + :class="{ + 'no-pointer-events': editDownloadDisabled, + }" + :disable="editDownloadDisabled" + icon="delete" + :title="t('Delete file')" + padding="xs" + round + @click="deleteFile(data.dmsFk)" + /> + </div> + <QBtn + v-else + icon="add_circle" + round + v-shortcut="'+'" + padding="xs" + @click=" + () => { + documentDialogRef.show = true; + delete documentDialogRef.dms; + } + " + data-cy="dms-create" + > + <QTooltip>{{ t('Create document') }}</QTooltip> + </QBtn> + </div> </VnRow> <VnRow> <VnSelect @@ -169,6 +264,29 @@ const invoiceInRef = ref({}); </VnRow> </template> </FormModel> + <QDialog v-model="documentDialogRef.show"> + <VnDms + model="dms" + default-dms-code="invoiceIn" + :form-initial-data="documentDialogRef.dms" + :url=" + documentDialogRef.dms + ? `Dms/${documentDialogRef.dms.id}/updateFile` + : 'Dms/uploadFile' + " + :description="documentDialogRef.supplierName" + @on-data-saved=" + (_, { data }) => { + let dmsData = data; + if (Array.isArray(data)) dmsData = data[0]; + invoiceInRef.formData.dmsFk = dmsData.id; + invoiceInRef.formData.dms = dmsData; + invoiceInRef.hasChanges = true; + invoiceInRef.save(); + } + " + /> + </QDialog> </template> <style lang="scss" scoped> @media (max-width: $breakpoint-xs) { From c5e1ebec82478f5298fa707f17bcaecdfeda46e2 Mon Sep 17 00:00:00 2001 From: pablone <pablone@verdnatura.es> Date: Fri, 21 Mar 2025 13:51:05 +0100 Subject: [PATCH 4/8] fix: refs #8638 update null check for maxlength validation in VnInput.vue --- src/components/common/VnInput.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/common/VnInput.vue b/src/components/common/VnInput.vue index 9821992cb..474d68116 100644 --- a/src/components/common/VnInput.vue +++ b/src/components/common/VnInput.vue @@ -84,7 +84,7 @@ const mixinRules = [ ...($attrs.rules ?? []), (val) => { const maxlength = $props.maxlength; - if (maxlength && +val.length > maxlength) + if (maxlength && +val?.length > maxlength) return t(`maxLength`, { value: maxlength }); const { min, max } = vnInputRef.value.$attrs; if (!min) return null; From bfa375bacd0a8c5efff6831e555d9a58dcfe7270 Mon Sep 17 00:00:00 2001 From: pablone <pablone@verdnatura.es> Date: Mon, 24 Mar 2025 08:22:13 +0100 Subject: [PATCH 5/8] feat: refs #8638 add data attributes for transfer buys functionality in EntryBuys.vue and corresponding tests --- src/pages/Entry/Card/EntryBuys.vue | 4 +++- test/cypress/integration/entry/entryCard/entryBuys.spec.js | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pages/Entry/Card/EntryBuys.vue b/src/pages/Entry/Card/EntryBuys.vue index 85da5cf1d..67f97e09d 100644 --- a/src/pages/Entry/Card/EntryBuys.vue +++ b/src/pages/Entry/Card/EntryBuys.vue @@ -596,6 +596,7 @@ onMounted(() => { icon="move_group" color="primary" :title="t('Transfer buys')" + data-cy="transferBuys" flat @click="dialogRef = true" :disable="!selectedRows.length" @@ -610,7 +611,7 @@ onMounted(() => { v-model="newEntryRef" :label="t('Entry')" type="number" - data-cy="transfer-buy-entry" + data-cy="entryDestinyInput" /> </QCardSection> <QCardSection> @@ -623,6 +624,7 @@ onMounted(() => { /> <QBtn label="Transfer" + data-cy="transferBuysBtn" flat color="primary" @click="transferBuys(selectedRows, newEntryRef)" diff --git a/test/cypress/integration/entry/entryCard/entryBuys.spec.js b/test/cypress/integration/entry/entryCard/entryBuys.spec.js index f8f5e6b80..b5e185a8e 100644 --- a/test/cypress/integration/entry/entryCard/entryBuys.spec.js +++ b/test/cypress/integration/entry/entryCard/entryBuys.spec.js @@ -80,6 +80,11 @@ describe('EntryBuys', () => { checkColor('amount', COLORS.positive); cy.saveCard(); + cy.get('tbody > tr [tabindex="0"][role="checkbox"]').click(); + cy.dataCy('transferBuys').should('be.enabled').click(); + cy.dataCy('entryDestinyInput').should('be.visible').type('100'); + cy.dataCy('transferBuysBtn').click(); + cy.deleteEntry(); }); From 2bdebc1e0d2a0f14fab2f5f4fa2c43782a783de9 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Mon, 24 Mar 2025 09:27:35 +0100 Subject: [PATCH 6/8] test: waitSpinner() when load dialog --- test/cypress/integration/ticket/ticketList.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/cypress/integration/ticket/ticketList.spec.js b/test/cypress/integration/ticket/ticketList.spec.js index a3d8fe908..5613a5854 100644 --- a/test/cypress/integration/ticket/ticketList.spec.js +++ b/test/cypress/integration/ticket/ticketList.spec.js @@ -35,7 +35,7 @@ describe('TicketList', () => { cy.get('.summaryBody').should('exist'); }); - it.skip('filter client and create ticket', () => { + it('filter client and create ticket', () => { cy.intercept('GET', /\/api\/Tickets\/filter/).as('ticketSearchbar'); searchResults(); cy.wait('@ticketSearchbar'); @@ -44,6 +44,7 @@ describe('TicketList', () => { cy.dataCy('Customer ID_input').type('1101{enter}'); cy.get('[data-cy="vnTableCreateBtn"] > .q-btn__content > .q-icon').click(); + cy.waitSpinner(); cy.dataCy('Customer_select').should('have.value', 'Bruce Wayne'); cy.dataCy('Address_select').click(); From 798371682c6eede4de615913b50227f33b8db78e Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Mon, 24 Mar 2025 12:39:40 +0100 Subject: [PATCH 7/8] feat: refs #6919 enhance filter in AccountSummary component to include entity ID --- src/pages/Account/Card/AccountSummary.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Account/Card/AccountSummary.vue b/src/pages/Account/Card/AccountSummary.vue index f7a16e8c3..0b108807e 100644 --- a/src/pages/Account/Card/AccountSummary.vue +++ b/src/pages/Account/Card/AccountSummary.vue @@ -17,7 +17,7 @@ const entityId = computed(() => $props.id || route.params.id); data-key="Account" ref="AccountSummary" url="VnUsers/preview" - :filter="filter" + :filter="{ ...filter, where: { id: entityId } }" > <template #header="{ entity }">{{ entity.id }} - {{ entity.nickname }}</template> <template #menu> From e1ef6f87f3f4c334d3e7f65cb6fc8617bf67dc24 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Mon, 24 Mar 2025 14:01:43 +0100 Subject: [PATCH 8/8] feat: implement onBeforeSave function to handle form data updates --- src/pages/Claim/Card/ClaimBasicData.vue | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/pages/Claim/Card/ClaimBasicData.vue b/src/pages/Claim/Card/ClaimBasicData.vue index 43941d1dc..7e7d42ae8 100644 --- a/src/pages/Claim/Card/ClaimBasicData.vue +++ b/src/pages/Claim/Card/ClaimBasicData.vue @@ -2,6 +2,7 @@ import { ref } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; +import { getDifferences, getUpdatedValues } from 'src/filters'; import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelectEnum from 'src/components/common/VnSelectEnum.vue'; import FetchData from 'components/FetchData.vue'; @@ -9,12 +10,18 @@ import FormModel from 'components/FormModel.vue'; import VnRow from 'components/ui/VnRow.vue'; import VnInput from 'src/components/common/VnInput.vue'; import VnInputDate from 'components/common/VnInputDate.vue'; - import VnAvatar from 'src/components/ui/VnAvatar.vue'; const route = useRoute(); const { t } = useI18n(); const workersOptions = ref([]); + +function onBeforeSave(formData, originalData) { + return getUpdatedValues( + Object.keys(getDifferences(formData, originalData)), + formData, + ); +} </script> <template> <FetchData @@ -27,6 +34,7 @@ const workersOptions = ref([]); <FormModel model="Claim" :url-update="`Claims/updateClaim/${route.params.id}`" + :mapper="onBeforeSave" auto-load > <template #form="{ data, validate }">