diff --git a/src/boot/keyShortcut.js b/src/boot/keyShortcut.js new file mode 100644 index 000000000..5afb5b74a --- /dev/null +++ b/src/boot/keyShortcut.js @@ -0,0 +1,34 @@ +export default { + mounted: function (el, binding) { + const shortcut = binding.value ?? '+'; + + const { key, ctrl, alt, callback } = + typeof shortcut === 'string' + ? { + key: shortcut, + ctrl: true, + alt: true, + callback: () => + document + .querySelector(`button[shortcut="${shortcut}"]`) + ?.click(), + } + : binding.value; + + const handleKeydown = (event) => { + if (event.key === key && (!ctrl || event.ctrlKey) && (!alt || event.altKey)) { + callback(); + } + }; + + // Attach the event listener to the window + window.addEventListener('keydown', handleKeydown); + + el._handleKeydown = handleKeydown; + }, + unmounted: function (el) { + if (el._handleKeydown) { + window.removeEventListener('keydown', el._handleKeydown); + } + }, +}; diff --git a/src/boot/quasar.js b/src/boot/quasar.js index caf573ac7..e2035c880 100644 --- a/src/boot/quasar.js +++ b/src/boot/quasar.js @@ -1,8 +1,10 @@ import { boot } from 'quasar/wrappers'; import qFormMixin from './qformMixin'; import mainShortcutMixin from './mainShortcutMixin'; +import keyShortcut from './keyShortcut'; export default boot(({ app }) => { app.mixin(qFormMixin); app.mixin(mainShortcutMixin); + app.directive('shortcut', keyShortcut); }); diff --git a/src/components/NavBar.vue b/src/components/NavBar.vue index e80a293c6..00faaebc2 100644 --- a/src/components/NavBar.vue +++ b/src/components/NavBar.vue @@ -24,7 +24,13 @@ const pinnedModulesRef = ref(); <template> <QHeader color="white" elevated> <QToolbar class="q-py-sm q-px-md"> - <QBtn @click="stateStore.toggleLeftDrawer()" icon="menu" round dense flat> + <QBtn + @click="stateStore.toggleLeftDrawer()" + icon="dock_to_right" + round + dense + flat + > <QTooltip bottom anchor="bottom right"> {{ t('globals.collapseMenu') }} </QTooltip> diff --git a/src/components/RefundInvoiceForm.vue b/src/components/RefundInvoiceForm.vue new file mode 100644 index 000000000..c21c892dd --- /dev/null +++ b/src/components/RefundInvoiceForm.vue @@ -0,0 +1,174 @@ +<script setup> +import { ref, reactive } from 'vue'; +import { useI18n } from 'vue-i18n'; +import { useRouter } from 'vue-router'; +import { useDialogPluginComponent } from 'quasar'; +import VnRow from 'components/ui/VnRow.vue'; +import FetchData from 'components/FetchData.vue'; +import VnSelect from 'components/common/VnSelect.vue'; +import FormPopup from './FormPopup.vue'; +import axios from 'axios'; +import useNotify from 'src/composables/useNotify.js'; + +const $props = defineProps({ + invoiceOutData: { + type: Object, + default: () => {}, + }, +}); + +const { dialogRef } = useDialogPluginComponent(); +const { t } = useI18n(); +const router = useRouter(); +const { notify } = useNotify(); + +const rectificativeTypeOptions = ref([]); +const siiTypeInvoiceOutsOptions = ref([]); +const inheritWarehouse = ref(true); +const invoiceParams = reactive({ + id: $props.invoiceOutData?.id, +}); +const invoiceCorrectionTypesOptions = ref([]); + +const refund = async () => { + const params = { + id: invoiceParams.id, + withWarehouse: invoiceParams.inheritWarehouse, + cplusRectificationTypeFk: invoiceParams.cplusRectificationTypeFk, + siiTypeInvoiceOutFk: invoiceParams.siiTypeInvoiceOutFk, + invoiceCorrectionTypeFk: invoiceParams.invoiceCorrectionTypeFk, + }; + + try { + const { data } = await axios.post('InvoiceOuts/refundAndInvoice', params); + notify(t('Refunded invoice'), 'positive'); + const [id] = data?.refundId || []; + if (id) router.push({ name: 'InvoiceOutSummary', params: { id } }); + } catch (err) { + console.error('Error refunding invoice', err); + } +}; +</script> + +<template> + <FetchData + url="CplusRectificationTypes" + :filter="{ order: 'description' }" + @on-fetch=" + (data) => ( + (rectificativeTypeOptions = data), + (invoiceParams.cplusRectificationTypeFk = data.filter( + (type) => type.description == 'I – Por diferencias' + )[0].id) + ) + " + auto-load + /> + <FetchData + url="SiiTypeInvoiceOuts" + :filter="{ where: { code: { like: 'R%' } } }" + @on-fetch=" + (data) => ( + (siiTypeInvoiceOutsOptions = data), + (invoiceParams.siiTypeInvoiceOutFk = data.filter( + (type) => type.code == 'R4' + )[0].id) + ) + " + auto-load + /> + <FetchData + url="InvoiceCorrectionTypes" + @on-fetch="(data) => (invoiceCorrectionTypesOptions = data)" + auto-load + /> + + <QDialog ref="dialogRef"> + <FormPopup + @on-submit="refund()" + :custom-submit-button-label="t('Accept')" + :default-cancel-button="false" + > + <template #form-inputs> + <VnRow> + <VnSelect + :label="t('Rectificative type')" + :options="rectificativeTypeOptions" + hide-selected + option-label="description" + option-value="id" + v-model="invoiceParams.cplusRectificationTypeFk" + :required="true" + /> + </VnRow> + <VnRow> + <VnSelect + :label="t('Class')" + :options="siiTypeInvoiceOutsOptions" + hide-selected + option-label="description" + option-value="id" + v-model="invoiceParams.siiTypeInvoiceOutFk" + :required="true" + > + <template #option="scope"> + <QItem v-bind="scope.itemProps"> + <QItemSection> + <QItemLabel> + {{ scope.opt?.code }} - + {{ scope.opt?.description }} + </QItemLabel> + </QItemSection> + </QItem> + </template> + </VnSelect> + </VnRow> + + <VnRow> + <VnSelect + :label="t('Type')" + :options="invoiceCorrectionTypesOptions" + hide-selected + option-label="description" + option-value="id" + v-model="invoiceParams.invoiceCorrectionTypeFk" + :required="true" + /> </VnRow + ><VnRow> + <div> + <QCheckbox + :label="t('Inherit warehouse')" + v-model="inheritWarehouse" + /> + <QIcon name="info" class="cursor-info q-ml-sm" size="sm"> + <QTooltip>{{ t('Inherit warehouse tooltip') }}</QTooltip> + </QIcon> + </div> + </VnRow> + </template> + </FormPopup> + </QDialog> +</template> + +<i18n> +en: + Refund invoice: Refund invoice + Rectificative type: Rectificative type + Class: Class + Type: Type + Refunded invoice: Refunded invoice + Inherit warehouse: Inherit the warehouse + Inherit warehouse tooltip: Select this option to inherit the warehouse when refunding the invoice + Accept: Accept + Error refunding invoice: Error refunding invoice +es: + Refund invoice: Abonar factura + Rectificative type: Tipo rectificativa + Class: Clase + Type: Tipo + Refunded invoice: Factura abonada + Inherit warehouse: Heredar el almacén + Inherit warehouse tooltip: Seleccione esta opción para heredar el almacén al abonar la factura. + Accept: Aceptar + Error refunding invoice: Error abonando factura +</i18n> diff --git a/src/components/TransferInvoiceForm.vue b/src/components/TransferInvoiceForm.vue index 17c11d87e..f7050cdba 100644 --- a/src/components/TransferInvoiceForm.vue +++ b/src/components/TransferInvoiceForm.vue @@ -2,13 +2,12 @@ import { ref, reactive } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRouter } from 'vue-router'; -import { useQuasar } from 'quasar'; +import { useQuasar, useDialogPluginComponent } from 'quasar'; import VnConfirm from 'components/ui/VnConfirm.vue'; import VnRow from 'components/ui/VnRow.vue'; import FetchData from 'components/FetchData.vue'; import VnSelect from 'components/common/VnSelect.vue'; import FormPopup from './FormPopup.vue'; -import { useDialogPluginComponent } from 'quasar'; import axios from 'axios'; import useNotify from 'src/composables/useNotify.js'; @@ -18,19 +17,19 @@ const $props = defineProps({ default: () => {}, }, }); + const { dialogRef } = useDialogPluginComponent(); const quasar = useQuasar(); const { t } = useI18n(); const router = useRouter(); const { notify } = useNotify(); -const checked = ref(true); -const transferInvoiceParams = reactive({ - id: $props.invoiceOutData?.id, - refFk: $props.invoiceOutData?.ref, -}); const rectificativeTypeOptions = ref([]); const siiTypeInvoiceOutsOptions = ref([]); +const checked = ref(true); +const transferInvoiceParams = reactive({ + id: $props.invoiceOutData?.id, +}); const invoiceCorrectionTypesOptions = ref([]); const selectedClient = (client) => { @@ -44,10 +43,9 @@ const makeInvoice = async () => { const params = { id: transferInvoiceParams.id, cplusRectificationTypeFk: transferInvoiceParams.cplusRectificationTypeFk, + siiTypeInvoiceOutFk: transferInvoiceParams.siiTypeInvoiceOutFk, invoiceCorrectionTypeFk: transferInvoiceParams.invoiceCorrectionTypeFk, newClientFk: transferInvoiceParams.newClientFk, - refFk: transferInvoiceParams.refFk, - siiTypeInvoiceOutFk: transferInvoiceParams.siiTypeInvoiceOutFk, makeInvoice: checked.value, }; @@ -74,7 +72,7 @@ const makeInvoice = async () => { } } - const { data } = await axios.post('InvoiceOuts/transferInvoice', params); + const { data } = await axios.post('InvoiceOuts/transfer', params); notify(t('Transferred invoice'), 'positive'); const id = data?.[0]; if (id) router.push({ name: 'InvoiceOutSummary', params: { id } }); diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index 1e93ac186..5876dd39b 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -617,7 +617,7 @@ defineExpose({ </template> </CrudModel> <QPageSticky v-if="create" :offset="[20, 20]" style="z-index: 2"> - <QBtn @click="showForm = !showForm" color="primary" fab icon="add" /> + <QBtn @click="showForm = !showForm" color="primary" fab icon="add" shortcut="+" /> <QTooltip> {{ createForm.title }} </QTooltip> diff --git a/src/components/common/RightMenu.vue b/src/components/common/RightMenu.vue index 732e5367d..3aa1891f9 100644 --- a/src/components/common/RightMenu.vue +++ b/src/components/common/RightMenu.vue @@ -37,7 +37,7 @@ const stateStore = useStateStore(); @click="stateStore.toggleRightDrawer()" round dense - icon="menu" + icon="dock_to_left" > <QTooltip bottom anchor="bottom right"> {{ t('globals.collapseMenu') }} diff --git a/src/components/common/VnCard.vue b/src/components/common/VnCard.vue index a7fe651ad..5cee06582 100644 --- a/src/components/common/VnCard.vue +++ b/src/components/common/VnCard.vue @@ -1,6 +1,6 @@ <script setup> import { onBeforeMount, computed } from 'vue'; -import { useRoute, onBeforeRouteUpdate } from 'vue-router'; +import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router'; import { useArrayData } from 'src/composables/useArrayData'; import { useStateStore } from 'stores/useStateStore'; import useCardSize from 'src/composables/useCardSize'; @@ -17,10 +17,12 @@ const props = defineProps({ filterPanel: { type: Object, default: undefined }, searchDataKey: { type: String, default: undefined }, searchbarProps: { type: Object, default: undefined }, + redirectOnError: { type: Boolean, default: false }, }); const stateStore = useStateStore(); const route = useRoute(); +const router = useRouter(); const url = computed(() => { if (props.baseUrl) return `${props.baseUrl}/${route.params.id}`; return props.customUrl; @@ -35,8 +37,12 @@ const arrayData = useArrayData(props.dataKey, { }); onBeforeMount(async () => { - if (!props.baseUrl) arrayData.store.filter.where = { id: route.params.id }; - await arrayData.fetch({ append: false, updateRouter: false }); + try { + if (!props.baseUrl) arrayData.store.filter.where = { id: route.params.id }; + await arrayData.fetch({ append: false, updateRouter: false }); + } catch (e) { + router.push({ name: 'WorkerList' }); + } }); if (props.baseUrl) { diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue index c42de6690..68ff0e359 100644 --- a/src/components/common/VnDmsList.vue +++ b/src/components/common/VnDmsList.vue @@ -135,6 +135,7 @@ const columns = computed(() => [ field: 'hasFile', label: t('globals.original'), name: 'hasFile', + toolTip: t('The documentation is available in paper form'), component: QCheckbox, props: (prop) => ({ disable: true, @@ -297,6 +298,14 @@ defineExpose({ row-key="clientFk" :grid="$q.screen.lt.sm" > + <template #header="props"> + <QTr :props="props" class="bg"> + <QTh v-for="col in props.cols" :key="col.name" :props="props"> + <QTooltip v-if="col.toolTip">{{ col.toolTip }}</QTooltip + >{{ col.label }} + </QTh> + </QTr> + </template> <template #body-cell="props"> <QTd :props="props"> <QTr :props="props"> @@ -397,8 +406,10 @@ defineExpose({ <i18n> en: contentTypesInfo: Allowed file types {allowedContentTypes} + The documentation is available in paper form: The documentation is available in paper form es: contentTypesInfo: Tipos de archivo permitidos {allowedContentTypes} Generate identifier for original file: Generar identificador para archivo original Upload file: Subir fichero + the documentation is available in paper form: Se tiene la documentación en papel </i18n> diff --git a/src/components/ui/FetchedTags.vue b/src/components/ui/FetchedTags.vue index beaa85bfe..a0edf85f8 100644 --- a/src/components/ui/FetchedTags.vue +++ b/src/components/ui/FetchedTags.vue @@ -2,10 +2,6 @@ import { computed } from 'vue'; const $props = defineProps({ - maxLength: { - type: Number, - required: true, - }, item: { type: Object, required: true, diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index 931767516..cebdc4bbf 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -132,17 +132,6 @@ async function search(evt) { } } -async function reload() { - isLoading.value = true; - const params = Object.values(userParams.value).filter((param) => param); - store.skip = 0; - store.page = 1; - await arrayData.fetch({ append: false }); - if (!$props.showAll && !params.length) store.data = []; - isLoading.value = false; - emit('refresh'); -} - async function clearFilters() { try { isLoading.value = true; @@ -231,32 +220,18 @@ function sanitizer(params) { </QItemLabel> </QItemSection> <QItemSection top side> - <div class="q-gutter-xs"> - <QBtn - @click="clearFilters" - color="primary" - dense - flat - icon="filter_list_off" - padding="none" - round - size="sm" - > - <QTooltip>{{ t('Remove filters') }}</QTooltip> - </QBtn> - <QBtn - @click="reload" - color="primary" - dense - flat - icon="refresh" - padding="none" - round - size="sm" - > - <QTooltip>{{ t('Refresh') }}</QTooltip> - </QBtn> - </div> + <QBtn + @click="clearFilters" + color="primary" + dense + flat + icon="filter_list_off" + padding="none" + round + size="sm" + > + <QTooltip>{{ t('Remove filters') }}</QTooltip> + </QBtn> </QItemSection> </QItem> <QItem class="q-mb-sm"> diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 73707b9be..98f66646d 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -84,7 +84,7 @@ globals: description: Description id: Id order: Order - original: Original + original: Phys. Doc file: File selectFile: Select a file copyClipboard: Copy on clipboard @@ -261,6 +261,7 @@ globals: clientsActionsMonitor: Clients and actions serial: Serial business: Business + medical: Mutual created: Created worker: Worker now: Now @@ -880,6 +881,7 @@ worker: locker: Locker balance: Balance business: Business + medical: Medical list: name: Name email: Email @@ -966,6 +968,15 @@ worker: amount: Importe remark: Bonficado hasDiploma: Diploma + medical: + tableVisibleColumns: + date: Date + time: Hour + center: Formation Center + invoice: Invoice + amount: Amount + isFit: Fit + remark: Observations imageNotFound: Image not found balance: tableVisibleColumns: diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index ba4de742e..7ce0762f2 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -86,7 +86,7 @@ globals: description: Descripción id: Id order: Orden - original: Original + original: Doc. física file: Fichero selectFile: Seleccione un fichero copyClipboard: Copiar en portapapeles @@ -265,6 +265,7 @@ globals: clientsActionsMonitor: Clientes y acciones serial: Facturas por serie business: Contratos + medical: Mutua created: Fecha creación worker: Trabajador now: Ahora @@ -883,6 +884,8 @@ worker: locker: Taquilla balance: Balance business: Contrato + formation: Formación + medical: Mutua list: name: Nombre email: Email @@ -969,6 +972,15 @@ worker: amount: Importe remark: Bonficado hasDiploma: Diploma + medical: + tableVisibleColumns: + date: Fecha + time: Hora + center: Centro de Formación + invoice: Factura + amount: Importe + isFit: Apto + remark: Observaciones imageNotFound: No se ha encontrado la imagen balance: tableVisibleColumns: diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index 88c5ee293..51cd20071 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -5,7 +5,7 @@ const quasar = useQuasar(); </script> <template> - <QLayout view="hHh LpR fFf"> + <QLayout view="hHh LpR fFf" v-shortcut> <Navbar /> <RouterView></RouterView> <QFooter v-if="quasar.platform.is.mobile"></QFooter> diff --git a/src/pages/Account/AccountConnections.vue b/src/pages/Account/AccountConnections.vue index 4d3450665..057745831 100644 --- a/src/pages/Account/AccountConnections.vue +++ b/src/pages/Account/AccountConnections.vue @@ -27,15 +27,15 @@ const filter = { order: 'created DESC', }; -const urlPath = 'AccessTokens'; +const urlPath = 'VnTokens'; const refresh = () => paginateRef.value.fetch(); const navigate = (id) => router.push({ name: 'AccountSummary', params: { id } }); -const killSession = async (id) => { +const killSession = async ({ userId, created }) => { try { - await axios.delete(`${urlPath}/${id}`); + await axios.post(`${urlPath}/killSession`, { userId, created }); paginateRef.value.fetch(); notify(t('Session killed'), 'positive'); } catch (error) { @@ -84,7 +84,7 @@ const killSession = async (id) => { openConfirmationModal( t('Session will be killed'), t('Are you sure you want to continue?'), - () => killSession(row.id) + () => killSession(row) ) " outline diff --git a/src/pages/Account/Card/AccountMailAlias.vue b/src/pages/Account/Card/AccountMailAlias.vue index 594353219..15d03c665 100644 --- a/src/pages/Account/Card/AccountMailAlias.vue +++ b/src/pages/Account/Card/AccountMailAlias.vue @@ -169,7 +169,13 @@ onMounted(async () => await getAccountData(false)); <AccountMailAliasCreateForm @on-submit-create-alias="createMailAlias" /> </QDialog> <QPageSticky position="bottom-right" :offset="[18, 18]"> - <QBtn fab icon="add" color="primary" @click="openCreateMailAliasForm()"> + <QBtn + fab + icon="add" + color="primary" + @click="openCreateMailAliasForm()" + shortcut="+" + > <QTooltip>{{ t('warehouses.add') }}</QTooltip> </QBtn> </QPageSticky> diff --git a/src/pages/Customer/Card/CustomerAddress.vue b/src/pages/Customer/Card/CustomerAddress.vue index 6c8279d84..a9e7affef 100644 --- a/src/pages/Customer/Card/CustomerAddress.vue +++ b/src/pages/Customer/Card/CustomerAddress.vue @@ -218,7 +218,13 @@ const toCustomerAddressEdit = (addressId) => { </div> <QPageSticky :offset="[18, 18]"> - <QBtn @click.stop="toCustomerAddressCreate()" color="primary" fab icon="add" /> + <QBtn + @click.stop="toCustomerAddressCreate()" + color="primary" + fab + icon="add" + shortcut="+" + /> <QTooltip> {{ t('New consignee') }} </QTooltip> diff --git a/src/pages/Customer/Card/CustomerBalance.vue b/src/pages/Customer/Card/CustomerBalance.vue index 346e76681..548a67b0b 100644 --- a/src/pages/Customer/Card/CustomerBalance.vue +++ b/src/pages/Customer/Card/CustomerBalance.vue @@ -292,7 +292,13 @@ const showBalancePdf = ({ id }) => { </template> </VnTable> <QPageSticky :offset="[18, 18]" style="z-index: 2"> - <QBtn @click.stop="showNewPaymentDialog()" color="primary" fab icon="add" /> + <QBtn + @click.stop="showNewPaymentDialog()" + color="primary" + fab + icon="add" + shortcut="+" + /> <QTooltip> {{ t('New payment') }} </QTooltip> diff --git a/src/pages/Customer/Card/CustomerCreditContracts.vue b/src/pages/Customer/Card/CustomerCreditContracts.vue index 568adcf0b..12719b2cf 100644 --- a/src/pages/Customer/Card/CustomerCreditContracts.vue +++ b/src/pages/Customer/Card/CustomerCreditContracts.vue @@ -193,6 +193,7 @@ const updateData = () => { color="primary" fab icon="add" + shortcut="+" /> <QTooltip> {{ t('New contract') }} diff --git a/src/pages/Entry/Card/EntryBuys.vue b/src/pages/Entry/Card/EntryBuys.vue index 6e66f4ce7..e9a9ab815 100644 --- a/src/pages/Entry/Card/EntryBuys.vue +++ b/src/pages/Entry/Card/EntryBuys.vue @@ -423,7 +423,7 @@ const lockIconType = (groupingMode, mode) => { <span v-if="props.row.item.subName" class="subName"> {{ props.row.item.subName }} </span> - <FetchedTags :item="props.row.item" :max-length="5" /> + <FetchedTags :item="props.row.item" /> </QTd> </QTr> </template> diff --git a/src/pages/Entry/Card/EntrySummary.vue b/src/pages/Entry/Card/EntrySummary.vue index b32dc70a9..379be1d2f 100644 --- a/src/pages/Entry/Card/EntrySummary.vue +++ b/src/pages/Entry/Card/EntrySummary.vue @@ -319,7 +319,7 @@ const fetchEntryBuys = async () => { <span v-if="row.item.subName" class="subName"> {{ row.item.subName }} </span> - <FetchedTags :item="row.item" :max-length="5" /> + <FetchedTags :item="row.item" /> </QTd> </QTr> <!-- Esta última row es utilizada para agregar un espaciado y así marcar una diferencia visual entre los diferentes buys --> diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue index 4c02ccf84..213e25b4a 100644 --- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue +++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue @@ -5,6 +5,7 @@ import { useRouter } from 'vue-router'; import { useQuasar } from 'quasar'; import TransferInvoiceForm from 'src/components/TransferInvoiceForm.vue'; +import RefundInvoiceForm from 'src/components/RefundInvoiceForm.vue'; import SendEmailDialog from 'components/common/SendEmailDialog.vue'; import useNotify from 'src/composables/useNotify'; @@ -141,6 +142,15 @@ const showTransferInvoiceForm = async () => { }, }); }; + +const showRefundInvoiceForm = () => { + quasar.dialog({ + component: RefundInvoiceForm, + componentProps: { + invoiceOutData: $props.invoiceOutData, + }, + }); +}; </script> <template> @@ -229,10 +239,13 @@ const showTransferInvoiceForm = async () => { <QMenu anchor="top end" self="top start"> <QList> <QItem v-ripple clickable @click="refundInvoice(true)"> - <QItemSection>{{ t('With warehouse') }}</QItemSection> + <QItemSection>{{ t('With warehouse, no invoice') }}</QItemSection> </QItem> <QItem v-ripple clickable @click="refundInvoice(false)"> - <QItemSection>{{ t('Without warehouse') }}</QItemSection> + <QItemSection>{{ t('Without warehouse, no invoice') }}</QItemSection> + </QItem> + <QItem v-ripple clickable @click="showRefundInvoiceForm()"> + <QItemSection>{{ t('Invoiced') }}</QItemSection> </QItem> </QList> </QMenu> @@ -255,8 +268,9 @@ es: As CSV: como CSV Send PDF: Enviar PDF Send CSV: Enviar CSV - With warehouse: Con almacén - Without warehouse: Sin almacén + With warehouse, no invoice: Con almacén, sin factura + Without warehouse, no invoice: Sin almacén, sin factura + Invoiced: Facturado InvoiceOut deleted: Factura eliminada Confirm deletion: Confirmar eliminación Are you sure you want to delete this invoice?: Estas seguro de eliminar esta factura? diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue index 8381f0624..baac0c608 100644 --- a/src/pages/Item/Card/ItemDescriptor.vue +++ b/src/pages/Item/Card/ItemDescriptor.vue @@ -10,8 +10,6 @@ import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.v import VnConfirm from 'components/ui/VnConfirm.vue'; import RegularizeStockForm from 'components/RegularizeStockForm.vue'; import ItemDescriptorImage from 'src/pages/Item/Card/ItemDescriptorImage.vue'; - -import { useState } from 'src/composables/useState'; import useCardDescription from 'src/composables/useCardDescription'; import { getUrl } from 'src/composables/getUrl'; import axios from 'axios'; @@ -35,58 +33,69 @@ const $props = defineProps({ type: Number, default: null, }, + warehouseFk: { + type: Number, + default: null, + }, }); const quasar = useQuasar(); const route = useRoute(); const router = useRouter(); const { t } = useI18n(); -const state = useState(); -const user = state.getUser(); - +const warehouseConfig = ref(null); const entityId = computed(() => { return $props.id || route.params.id; }); + const regularizeStockFormDialog = ref(null); const available = ref(null); const visible = ref(null); -const _warehouseFk = ref(null); const salixUrl = ref(); -const warehouseFk = computed({ - get() { - return _warehouseFk.value; - }, - set(val) { - _warehouseFk.value = val; - if (val) updateStock(); - }, -}); onMounted(async () => { - warehouseFk.value = user.value.warehouseFk; salixUrl.value = await getUrl(''); + await getItemConfigs(); + await updateStock(); }); const data = ref(useCardDescription()); -const setData = (entity) => { - if (!entity) return; - data.value = useCardDescription(entity.name, entity.id); +const setData = async (entity) => { + try { + if (!entity) return; + data.value = useCardDescription(entity.name, entity.id); + await updateStock(); + } catch (err) { + console.error('Error item'); + } }; +const getItemConfigs = async () => { + try { + const { data } = await axios.get('ItemConfigs/findOne'); + if (!data) return; + return (warehouseConfig.value = data.warehouseFk); + } catch (err) { + console.error('Error item'); + } +}; const updateStock = async () => { try { available.value = null; visible.value = null; const params = { - warehouseFk: warehouseFk.value, + warehouseFk: $props.warehouseFk, dated: $props.dated, }; + await getItemConfigs(); + if (!params.warehouseFk) { + params.warehouseFk = warehouseConfig.value; + } const { data } = await axios.get(`Items/${entityId.value}/getVisibleAvailable`, { params, }); - available.value = data.available; visible.value = data.visible; } catch (err) { diff --git a/src/pages/Item/Card/ItemDescriptorImage.vue b/src/pages/Item/Card/ItemDescriptorImage.vue index d83f534b8..a4ef22ce3 100644 --- a/src/pages/Item/Card/ItemDescriptorImage.vue +++ b/src/pages/Item/Card/ItemDescriptorImage.vue @@ -47,8 +47,11 @@ const getWarehouseName = async (warehouseFk) => { const filter = { where: { id: warehouseFk }, }; - - const { data } = await axios.get('Warehouses/findOne', { filter }); + const { data } = await axios.get('Warehouses/findOne', { + params: { + filter: JSON.stringify(filter), + }, + }); if (!data) return; warehouseName.value = data.name; }; diff --git a/src/pages/Item/Card/ItemDescriptorProxy.vue b/src/pages/Item/Card/ItemDescriptorProxy.vue index 2b7b39a65..2ffc9080f 100644 --- a/src/pages/Item/Card/ItemDescriptorProxy.vue +++ b/src/pages/Item/Card/ItemDescriptorProxy.vue @@ -15,6 +15,10 @@ const $props = defineProps({ type: Number, default: null, }, + warehouseFk: { + type: Number, + default: null, + }, }); </script> @@ -26,6 +30,7 @@ const $props = defineProps({ :summary="ItemSummary" :dated="dated" :sale-fk="saleFk" + :warehouse-fk="warehouseFk" /> </QPopupProxy> </template> diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue index 2ecd1f21b..6b469d13a 100644 --- a/src/pages/Item/ItemFixedPrice.vue +++ b/src/pages/Item/ItemFixedPrice.vue @@ -436,7 +436,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); {{ row.name }} </span> <ItemDescriptorProxy :id="row.itemFk" /> - <FetchedTags :item="row" :max-length="6" /> + <FetchedTags :item="row" /> </QTd> </template> <template #body-cell-groupingPrice="props"> diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index f1e3629cd..e3fcceb3a 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -517,7 +517,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); <template #body-cell-description="{ row }"> <QTd class="col"> <span>{{ row.name }} {{ row.subName }}</span> - <FetchedTags :item="row" :max-length="6" /> + <FetchedTags :item="row" /> </QTd> </template> <template #body-cell-isActive="{ row }"> @@ -562,7 +562,13 @@ onUnmounted(() => (stateStore.rightDrawer = false)); </VnPaginate> <QPageSticky :offset="[20, 20]"> - <QBtn @click="redirectToItemCreate()" color="primary" fab icon="add" /> + <QBtn + @click="redirectToItemCreate()" + color="primary" + fab + icon="add" + shortcut="+" + /> <QTooltip class="text-no-wrap"> {{ t('New item') }} </QTooltip> diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue index 125672d60..d874a5dcb 100644 --- a/src/pages/Item/ItemTypeList.vue +++ b/src/pages/Item/ItemTypeList.vue @@ -99,7 +99,13 @@ const exprBuilder = (param, value) => { </div> </QPage> <QPageSticky :offset="[20, 20]"> - <QBtn fab icon="add" color="primary" @click="redirectToCreateView()" /> + <QBtn + fab + icon="add" + color="primary" + @click="redirectToCreateView()" + shortcut="+" + /> <QTooltip> {{ t('New item type') }} </QTooltip> diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue index 850abb755..938cc4fe2 100644 --- a/src/pages/Order/Card/OrderCatalogFilter.vue +++ b/src/pages/Order/Card/OrderCatalogFilter.vue @@ -380,21 +380,6 @@ function addOrder(value, field, params) { @click="tagValues.push({})" /> </QItem> - <!-- <QItem> - <QItemSection class="q-py-sm"> - <QBtn - :label="t('Search')" - class="full-width" - color="primary" - dense - icon="search" - rounded - type="button" - unelevated - @click.stop="applyTagFilter(params, searchFn)" - /> - </QItemSection> - </QItem> --> <QSeparator /> </template> </VnFilterPanel> diff --git a/src/pages/Order/Card/OrderCatalogItemDialog.vue b/src/pages/Order/Card/OrderCatalogItemDialog.vue index 3f97443ca..46a50c021 100644 --- a/src/pages/Order/Card/OrderCatalogItemDialog.vue +++ b/src/pages/Order/Card/OrderCatalogItemDialog.vue @@ -77,10 +77,6 @@ const addToOrder = async () => { </template> <style lang="scss" scoped> -// .container { -// max-width: 768px; -// width: 100%; -// } .td { width: 200px; } diff --git a/src/pages/Order/Card/OrderLines.vue b/src/pages/Order/Card/OrderLines.vue index dab4a959c..17a157797 100644 --- a/src/pages/Order/Card/OrderLines.vue +++ b/src/pages/Order/Card/OrderLines.vue @@ -14,6 +14,7 @@ import FetchData from 'src/components/FetchData.vue'; import VnImg from 'src/components/ui/VnImg.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import FetchedTags from 'src/components/ui/FetchedTags.vue'; +import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; const router = useRouter(); const stateStore = useStateStore(); @@ -280,7 +281,12 @@ watch( <VnImg :id="parseInt(row?.item?.image)" class="rounded" /> </div> </template> - + <template #column-id="{ row }"> + <span class="link" @click.stop> + {{ row?.item?.id }} + <ItemDescriptorProxy :id="row?.item?.id" /> + </span> + </template> <template #column-itemFk="{ row }"> <div class="row column full-width justify-between items-start"> {{ row?.item?.name }} @@ -288,7 +294,7 @@ watch( {{ row?.item?.subName.toUpperCase() }} </div> </div> - <FetchedTags :item="row?.item" :max-length="6" /> + <FetchedTags :item="row?.item" /> </template> <template #column-amount="{ row }"> {{ toCurrency(row.quantity * row.price) }} diff --git a/src/pages/Order/Card/OrderSummary.vue b/src/pages/Order/Card/OrderSummary.vue index 09c47d4a7..60358f744 100644 --- a/src/pages/Order/Card/OrderSummary.vue +++ b/src/pages/Order/Card/OrderSummary.vue @@ -192,7 +192,7 @@ const detailsColumns = ref([ </span> </div> </div> - <FetchedTags :item="props.row.item" :max-length="5" /> + <FetchedTags :item="props.row.item" /> </QTd> <QTd key="quantity" :props="props"> {{ props.row.quantity }} diff --git a/src/pages/Order/Card/OrderVolume.vue b/src/pages/Order/Card/OrderVolume.vue index b16022c2f..27ee24197 100644 --- a/src/pages/Order/Card/OrderVolume.vue +++ b/src/pages/Order/Card/OrderVolume.vue @@ -2,8 +2,9 @@ import axios from 'axios'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; -import { ref } from 'vue'; +import { ref, onMounted } from 'vue'; import { dashIfEmpty } from 'src/filters'; +import { useStateStore } from 'stores/useStateStore'; import FetchData from 'components/FetchData.vue'; import FetchedTags from 'components/ui/FetchedTags.vue'; @@ -58,6 +59,9 @@ const loadVolumes = async (rows) => { }); volumes.value = rows; }; + +const stateStore = useStateStore(); +onMounted(async () => (stateStore.rightDrawer = false)); </script> <template> @@ -84,6 +88,7 @@ const loadVolumes = async (rows) => { @on-fetch="(data) => loadVolumes(data)" :right-search="false" :column-search="false" + :disable-option="{ card: true }" > <template #column-itemFk="{ row }"> <span class="link"> @@ -92,7 +97,13 @@ const loadVolumes = async (rows) => { </span> </template> <template #column-description="{ row }"> - <FetchedTags :item="row.item" :max-length="5" /> + <div class="row column full-width justify-between items-start"> + {{ row?.item?.name }} + <div v-if="row?.item?.subName" class="subName"> + {{ row?.item?.subName.toUpperCase() }} + </div> + </div> + <FetchedTags :item="row?.item" /> </template> <template #column-volume="{ rowIndex }"> {{ volumes?.[rowIndex]?.volume }} @@ -121,6 +132,11 @@ const loadVolumes = async (rows) => { } } } + +.subName { + color: var(--vn-label-color); + text-transform: uppercase; +} </style> <i18n> en: diff --git a/src/pages/Order/OrderList.vue b/src/pages/Order/OrderList.vue index 9870be9b3..d96a33ef5 100644 --- a/src/pages/Order/OrderList.vue +++ b/src/pages/Order/OrderList.vue @@ -11,6 +11,9 @@ import VnSelect from 'src/components/common/VnSelect.vue'; import OrderSearchbar from './Card/OrderSearchbar.vue'; import RightMenu from 'src/components/common/RightMenu.vue'; import OrderFilter from './Card/OrderFilter.vue'; +import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue'; +import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue'; +import { toDateTimeFormat } from 'src/filters/date'; const { t } = useI18n(); const { viewSummary } = useSummaryDialog(); @@ -75,7 +78,7 @@ const columns = computed(() => [ label: t('module.created'), component: 'date', cardVisible: true, - format: (row) => toDate(row?.landed), + format: (row) => toDateTimeFormat(row?.landed), columnField: { component: null, }, @@ -115,6 +118,7 @@ const columns = computed(() => [ }, }, cardVisible: true, + columnClass: 'expand', }, { align: 'left', @@ -132,6 +136,7 @@ const columns = computed(() => [ title: t('InvoiceOutSummary'), icon: 'preview', action: (row) => viewSummary(row.id, OrderSummary), + isPrimary: true, }, ], }, @@ -154,6 +159,16 @@ async function fetchAgencies({ landed, addressId }) { }); agencyList.value = data; } + +const getDateColor = (date) => { + const today = Date.vnNew(); + today.setHours(0, 0, 0, 0); + const timeTicket = new Date(date); + timeTicket.setHours(0, 0, 0, 0); + const comparation = today - timeTicket; + if (comparation == 0) return 'bg-warning'; + if (comparation < 0) return 'bg-success'; +}; </script> <template> <OrderSearchbar /> @@ -183,6 +198,25 @@ async function fetchAgencies({ landed, addressId }) { :columns="columns" redirect="order" > + <template #column-clientFk="{ row }"> + <span class="link" @click.stop> + {{ row?.clientName }} + <CustomerDescriptorProxy :id="row?.clientFk" /> + </span> + </template> + <template #column-salesPersonFk="{ row }"> + <span class="link" @click.stop> + {{ row?.name }} + <WorkerDescriptorProxy :id="row?.salesPersonFk" /> + </span> + </template> + <template #column-landed="{ row }"> + <span v-if="getDateColor(row.landed)"> + <QChip :class="getDateColor(row.landed)" dense square> + {{ toDate(row?.landed) }} + </QChip> + </span> + </template> <template #more-create-dialog="{ data }"> <VnSelect url="Clients" diff --git a/src/pages/Shelving/ShelvingList.vue b/src/pages/Shelving/ShelvingList.vue index 3f16f8ef7..d29f6ff15 100644 --- a/src/pages/Shelving/ShelvingList.vue +++ b/src/pages/Shelving/ShelvingList.vue @@ -87,7 +87,7 @@ function exprBuilder(param, value) { </div> <QPageSticky :offset="[20, 20]"> <RouterLink :to="{ name: 'ShelvingCreate' }"> - <QBtn fab icon="add" color="primary" /> + <QBtn fab icon="add" color="primary" shortcut="+" /> <QTooltip> {{ t('shelving.list.newShelving') }} </QTooltip> diff --git a/src/pages/Supplier/Card/SupplierAddresses.vue b/src/pages/Supplier/Card/SupplierAddresses.vue index 735b50625..4bb6d5622 100644 --- a/src/pages/Supplier/Card/SupplierAddresses.vue +++ b/src/pages/Supplier/Card/SupplierAddresses.vue @@ -81,7 +81,13 @@ const redirectToUpdateView = (addressData) => { </VnPaginate> </div> <QPageSticky :offset="[20, 20]"> - <QBtn fab icon="add" color="primary" @click="redirectToCreateView()" /> + <QBtn + fab + icon="add" + color="primary" + @click="redirectToCreateView()" + shortcut="+" + /> <QTooltip> {{ t('New address') }} </QTooltip> @@ -93,3 +99,4 @@ const redirectToUpdateView = (addressData) => { es: New address: Nueva dirección </i18n> +s diff --git a/src/pages/Supplier/Card/SupplierAgencyTerm.vue b/src/pages/Supplier/Card/SupplierAgencyTerm.vue index 26a410e8c..99b672cc4 100644 --- a/src/pages/Supplier/Card/SupplierAgencyTerm.vue +++ b/src/pages/Supplier/Card/SupplierAgencyTerm.vue @@ -109,7 +109,13 @@ const redirectToCreateView = () => { </template> </CrudModel> <QPageSticky :offset="[20, 20]"> - <QBtn fab icon="add" color="primary" @click="redirectToCreateView()" /> + <QBtn + fab + icon="add" + color="primary" + @click="redirectToCreateView()" + shortcut="+" + /> <QTooltip> {{ t('supplier.agencyTerms.addRow') }} </QTooltip> diff --git a/src/pages/Supplier/Card/SupplierConsumption.vue b/src/pages/Supplier/Card/SupplierConsumption.vue index 100a38b2a..8fa6a1e5c 100644 --- a/src/pages/Supplier/Card/SupplierConsumption.vue +++ b/src/pages/Supplier/Card/SupplierConsumption.vue @@ -208,7 +208,7 @@ onMounted(async () => { <QTd no-hover> <span>{{ buy.subName }}</span> - <FetchedTags :item="buy" :max-length="5" /> + <FetchedTags :item="buy" /> </QTd> <QTd no-hover> {{ dashIfEmpty(buy.quantity) }}</QTd> <QTd no-hover> {{ dashIfEmpty(buy.price) }}</QTd> diff --git a/src/pages/Ticket/Card/BasicData/BasicDataTable.vue b/src/pages/Ticket/Card/BasicData/BasicDataTable.vue index 48b8c882f..7f2f100ad 100644 --- a/src/pages/Ticket/Card/BasicData/BasicDataTable.vue +++ b/src/pages/Ticket/Card/BasicData/BasicDataTable.vue @@ -245,7 +245,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); <div class="column"> <span>{{ row.item.name }}</span> <span class="color-vn-label">{{ row.item.subName }}</span> - <FetchedTags :item="row.item" :max-length="6" /> + <FetchedTags :item="row.item" /> </div> </QTd> </template> diff --git a/src/pages/Ticket/Card/TicketComponents.vue b/src/pages/Ticket/Card/TicketComponents.vue index 3954b5a62..6131c92db 100644 --- a/src/pages/Ticket/Card/TicketComponents.vue +++ b/src/pages/Ticket/Card/TicketComponents.vue @@ -310,7 +310,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); <div class="column"> <span>{{ row.item.name }}</span> <span class="color-vn-label">{{ row.item.subName }}</span> - <FetchedTags :item="row.item" :max-length="6" /> + <FetchedTags :item="row.item" /> </div> </QTd> </template> diff --git a/src/pages/Ticket/Card/TicketPurchaseRequest.vue b/src/pages/Ticket/Card/TicketPurchaseRequest.vue index 281dc46a1..8d84e2b46 100644 --- a/src/pages/Ticket/Card/TicketPurchaseRequest.vue +++ b/src/pages/Ticket/Card/TicketPurchaseRequest.vue @@ -252,7 +252,13 @@ const openCreateModal = () => createTicketRequestDialogRef.value.show(); <TicketCreateRequest @on-request-created="crudModelRef.reload()" /> </QDialog> <QPageSticky :offset="[20, 20]"> - <QBtn @click="openCreateModal()" color="primary" fab icon="add" /> + <QBtn + @click="openCreateModal()" + color="primary" + fab + icon="add" + shortcut="+" + /> <QTooltip class="text-no-wrap"> {{ t('purchaseRequest.newRequest') }} </QTooltip> diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue index bd7297b56..2ea12bb05 100644 --- a/src/pages/Ticket/Card/TicketSale.vue +++ b/src/pages/Ticket/Card/TicketSale.vue @@ -656,7 +656,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); <div class="column"> <span>{{ row.concept }}</span> <span class="color-vn-label">{{ row.item?.subName }}</span> - <FetchedTags v-if="row.item" :item="row.item" :max-length="6" /> + <FetchedTags v-if="row.item" :item="row.item" /> <QPopupProxy v-if="row.id && isTicketEditable"> <VnInput v-model="row.concept" @change="updateConcept(row)" /> </QPopupProxy> diff --git a/src/pages/Ticket/Card/TicketSaleTracking.vue b/src/pages/Ticket/Card/TicketSaleTracking.vue index e699d2bfd..6978d92c8 100644 --- a/src/pages/Ticket/Card/TicketSaleTracking.vue +++ b/src/pages/Ticket/Card/TicketSaleTracking.vue @@ -412,7 +412,7 @@ const qCheckBoxController = (sale, action) => { <span v-if="row.subName" class="color-vn-label"> {{ row.subName }} </span> - <FetchedTags :item="row" :max-length="6" tag="value" /> + <FetchedTags :item="row" tag="value" /> </div> </QTd> </template> diff --git a/src/pages/Ticket/Card/TicketSummary.vue b/src/pages/Ticket/Card/TicketSummary.vue index 177384663..3851bf5d6 100644 --- a/src/pages/Ticket/Card/TicketSummary.vue +++ b/src/pages/Ticket/Card/TicketSummary.vue @@ -383,7 +383,11 @@ async function changeState(value) { <QTd> <QBtn class="link" flat> {{ props.row.itemFk }} - <ItemDescriptorProxy :id="props.row.itemFk" /> + <ItemDescriptorProxy + :id="props.row.itemFk" + :sale-fk="props.row.id" + :warehouse-fk="ticket.warehouseFk" + /> </QBtn> </QTd> <QTd>{{ props.row.visible }}</QTd> @@ -399,7 +403,6 @@ async function changeState(value) { <FetchedTags class="fetched-tags" :item="props.row.item" - :max-length="5" ></FetchedTags> </QTd> <QTd>{{ props.row.price }} €</QTd> diff --git a/src/pages/Ticket/Card/TicketTracking.vue b/src/pages/Ticket/Card/TicketTracking.vue index 53f59adf9..f7cbb14e9 100644 --- a/src/pages/Ticket/Card/TicketTracking.vue +++ b/src/pages/Ticket/Card/TicketTracking.vue @@ -82,7 +82,7 @@ const openCreateModal = () => createTrackingDialogRef.value.show(); data-key="TicketTracking" :filter="paginateFilter" url="TicketTrackings" - auto-load + auto-load order="created DESC" :limit="0" > @@ -114,7 +114,13 @@ const openCreateModal = () => createTrackingDialogRef.value.show(); <TicketCreateTracking @on-request-created="paginateRef.fetch()" /> </QDialog> <QPageSticky :offset="[20, 20]"> - <QBtn @click="openCreateModal()" color="primary" fab icon="add" /> + <QBtn + @click="openCreateModal()" + color="primary" + fab + icon="add" + shortcut="+" + /> <QTooltip class="text-no-wrap"> {{ t('tracking.addState') }} </QTooltip> diff --git a/src/pages/Ticket/Card/TicketVolume.vue b/src/pages/Ticket/Card/TicketVolume.vue index 93da31e53..68d2a1f73 100644 --- a/src/pages/Ticket/Card/TicketVolume.vue +++ b/src/pages/Ticket/Card/TicketVolume.vue @@ -145,7 +145,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); <div class="column"> <span>{{ row.item.name }}</span> <span class="color-vn-label">{{ row.item.subName }}</span> - <FetchedTags :item="row.item" :max-length="6" /> + <FetchedTags :item="row.item" /> </div> </QTd> </template> diff --git a/src/pages/Travel/Card/TravelThermographs.vue b/src/pages/Travel/Card/TravelThermographs.vue index 9f224154c..6d83581ee 100644 --- a/src/pages/Travel/Card/TravelThermographs.vue +++ b/src/pages/Travel/Card/TravelThermographs.vue @@ -196,6 +196,7 @@ const removeThermograph = async (id) => { icon="add" color="primary" @click="redirectToThermographForm('create')" + shortcut="+" /> <QTooltip class="text-no-wrap"> {{ t('Add thermograph') }} diff --git a/src/pages/Wagon/Type/WagonTypeList.vue b/src/pages/Wagon/Type/WagonTypeList.vue index 3ecca1ea3..b7bbf8c5d 100644 --- a/src/pages/Wagon/Type/WagonTypeList.vue +++ b/src/pages/Wagon/Type/WagonTypeList.vue @@ -74,7 +74,7 @@ async function remove(row) { </VnPaginate> </div> <QPageSticky position="bottom-right" :offset="[18, 18]"> - <QBtn @click="create" fab icon="add" color="primary" /> + <QBtn @click="create" fab icon="add" color="primary" shortcut="+" /> </QPageSticky> </QPage> </template> diff --git a/src/pages/Wagon/WagonList.vue b/src/pages/Wagon/WagonList.vue index a8b6728c3..c4824b861 100644 --- a/src/pages/Wagon/WagonList.vue +++ b/src/pages/Wagon/WagonList.vue @@ -94,7 +94,7 @@ async function remove(row) { </VnPaginate> </div> <QPageSticky position="bottom-right" :offset="[18, 18]"> - <QBtn @click="create" fab icon="add" color="primary" /> + <QBtn @click="create" fab icon="add" color="primary" shortcut="+" /> </QPageSticky> </QPage> </template> diff --git a/src/pages/Worker/Card/WorkerCard.vue b/src/pages/Worker/Card/WorkerCard.vue index 0abcdcafd..5f9fa0f8e 100644 --- a/src/pages/Worker/Card/WorkerCard.vue +++ b/src/pages/Worker/Card/WorkerCard.vue @@ -15,5 +15,6 @@ import WorkerFilter from '../WorkerFilter.vue'; label: 'Search worker', info: 'You can search by worker id or name', }" + :redirect-on-error="true" /> </template> diff --git a/src/pages/Worker/Card/WorkerDescriptor.vue b/src/pages/Worker/Card/WorkerDescriptor.vue index 154db1258..8d7f4f683 100644 --- a/src/pages/Worker/Card/WorkerDescriptor.vue +++ b/src/pages/Worker/Card/WorkerDescriptor.vue @@ -1,12 +1,11 @@ <script setup> -import { computed, ref, watch } from 'vue'; +import { computed, ref } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; import CardDescriptor from 'src/components/ui/CardDescriptor.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue'; import WorkerChangePasswordForm from 'src/pages/Worker/Card/WorkerChangePasswordForm.vue'; -import useCardDescription from 'src/composables/useCardDescription'; import { useState } from 'src/composables/useState'; import axios from 'axios'; import VnImg from 'src/components/ui/VnImg.vue'; @@ -38,49 +37,6 @@ const entityId = computed(() => { const worker = ref(); const workerExcluded = ref(false); -const filter = { - include: [ - { - relation: 'user', - scope: { - fields: ['email', 'name', 'nickname'], - }, - }, - { - relation: 'department', - scope: { - include: [ - { - relation: 'department', - }, - ], - }, - }, - { - relation: 'sip', - }, - ], -}; - -const sip = ref(null); -watch( - () => [worker.value?.sip?.extension, state.get('extension')], - ([newWorkerSip, newStateExtension], [oldWorkerSip, oldStateExtension]) => { - if (newStateExtension !== oldStateExtension || sip.value === oldStateExtension) { - sip.value = newStateExtension; - } else if (newWorkerSip !== oldWorkerSip && sip.value !== newStateExtension) { - sip.value = newWorkerSip; - } - } -); - -const data = ref(useCardDescription()); -const setData = (entity) => { - if (!entity) return; - data.value = useCardDescription(entity.user?.nickname, entity.id); -}; - -const openChangePasswordForm = () => changePasswordFormDialog.value.show(); const getIsExcluded = async () => { try { @@ -115,14 +71,12 @@ const refetch = async () => await cardDescriptorRef.value.getData(); ref="cardDescriptorRef" module="Worker" data-key="workerData" - :url="`Workers/${entityId}`" - :filter="filter" - :title="data.title" - :subtitle="data.subtitle" + url="Workers/descriptor" + :filter="{ where: { id: entityId } }" + title="user.nickname" @on-fetch=" (data) => { worker = data; - setData(data); getIsExcluded(); } " @@ -141,7 +95,7 @@ const refetch = async () => await cardDescriptorRef.value.getData(); v-if="!worker.user.emailVerified && user.id != worker.id" v-ripple clickable - @click="openChangePasswordForm()" + @click="$refs.changePasswordFormDialog.show()" > <QItemSection> {{ t('Change password') }} @@ -209,10 +163,10 @@ const refetch = async () => await cardDescriptorRef.value.getData(); <VnLinkPhone :phone-number="entity.phone" /> </template> </VnLv> - <VnLv :value="sip"> + <VnLv :value="worker?.sip?.extension"> <template #label> {{ t('worker.summary.sipExtension') }} - <VnLinkPhone v-if="sip" :phone-number="sip" /> + <VnLinkPhone :phone-number="worker?.sip?.extension" /> </template> </VnLv> </template> diff --git a/src/pages/Worker/Card/WorkerMedical.vue b/src/pages/Worker/Card/WorkerMedical.vue new file mode 100644 index 000000000..6bca4ae85 --- /dev/null +++ b/src/pages/Worker/Card/WorkerMedical.vue @@ -0,0 +1,91 @@ +<script setup> +import { ref, computed } from 'vue'; +import { useI18n } from 'vue-i18n'; +import { useRoute } from 'vue-router'; +import VnTable from 'components/VnTable/VnTable.vue'; +const tableRef = ref(); +const { t } = useI18n(); +const route = useRoute(); +const entityId = computed(() => route.params.id); + +const columns = [ + { + align: 'left', + name: 'date', + label: t('worker.medical.tableVisibleColumns.date'), + create: true, + component: 'date', + }, + { + align: 'left', + name: 'time', + label: t('worker.medical.tableVisibleColumns.time'), + create: true, + component: 'time', + attrs: { + timeOnly: true, + }, + }, + { + align: 'left', + name: 'centerFk', + label: t('worker.medical.tableVisibleColumns.center'), + create: true, + component: 'select', + attrs: { + url: 'medicalCenters', + fields: ['id', 'name'], + }, + }, + { + align: 'left', + name: 'invoice', + label: t('worker.medical.tableVisibleColumns.invoice'), + create: true, + component: 'input', + }, + { + align: 'left', + name: 'amount', + label: t('worker.medical.tableVisibleColumns.amount'), + create: true, + component: 'input', + }, + { + align: 'left', + name: 'isFit', + label: t('worker.medical.tableVisibleColumns.isFit'), + create: true, + component: 'checkbox', + }, + { + align: 'left', + name: 'remark', + label: t('worker.medical.tableVisibleColumns.remark'), + create: true, + component: 'input', + }, +]; +</script> +<template> + <VnTable + ref="tableRef" + data-key="WorkerMedical" + :url="`Workers/${entityId}/medicalReview`" + save-url="MedicalReviews/crud" + :create="{ + urlCreate: 'medicalReviews', + title: t('Create medicalReview'), + onDataSaved: () => tableRef.reload(), + formInitialData: { + workerFk: entityId, + }, + }" + order="date DESC" + :columns="columns" + auto-load + :right-search="false" + :is-editable="true" + :use-model="true" + /> +</template> diff --git a/src/pages/Worker/Card/WorkerPda.vue b/src/pages/Worker/Card/WorkerPda.vue index 4e0abc20c..a53aac270 100644 --- a/src/pages/Worker/Card/WorkerPda.vue +++ b/src/pages/Worker/Card/WorkerPda.vue @@ -116,7 +116,7 @@ function reloadData() { </template> </VnPaginate> <QPageSticky :offset="[18, 18]"> - <QBtn @click.stop="dialog.show()" color="primary" fab icon="add"> + <QBtn @click.stop="dialog.show()" color="primary" fab icon="add" shortcut="+"> <QDialog ref="dialog"> <FormModelPopup :title="t('Add new device')" diff --git a/src/pages/Worker/Card/WorkerSummary.vue b/src/pages/Worker/Card/WorkerSummary.vue index 57d2036fe..839b1b26c 100644 --- a/src/pages/Worker/Card/WorkerSummary.vue +++ b/src/pages/Worker/Card/WorkerSummary.vue @@ -34,13 +34,22 @@ const filter = { { relation: 'user', scope: { - fields: ['email', 'name', 'nickname', 'roleFk'], - include: { - relation: 'role', - scope: { - fields: ['name'], + fields: ['name', 'nickname', 'roleFk'], + + include: [ + { + relation: 'role', + scope: { + fields: ['name'], + }, }, - }, + { + relation: 'emailUser', + scope: { + fields: ['email'], + }, + }, + ], }, }, { @@ -178,14 +187,18 @@ const filter = { </QCard> <QCard class="vn-one"> <VnTitle :text="t('worker.summary.userData')" /> - <VnLv :label="t('worker.summary.userId')" :value="worker.user.id" /> - <VnLv :label="t('worker.card.name')" :value="worker.user.nickname" /> - <VnLv :label="t('worker.list.email')" :value="worker.user.email" copy /> + <VnLv :label="t('worker.summary.userId')" :value="worker?.user?.id" /> + <VnLv :label="t('worker.card.name')" :value="worker?.user?.nickname" /> + <VnLv + :label="t('worker.list.email')" + :value="worker.user?.emailUser?.email" + copy + /> <VnLv :label="t('worker.summary.role')"> <template #value> <span class="link"> - {{ worker.user.role.name }} - <RoleDescriptorProxy :id="worker.user.role.id" /> + {{ worker?.user?.role?.name }} + <RoleDescriptorProxy :id="worker?.user?.role?.id" /> </span> </template> </VnLv> diff --git a/src/pages/Worker/Card/WorkerTimeControl.vue b/src/pages/Worker/Card/WorkerTimeControl.vue index f560fcfed..f68614575 100644 --- a/src/pages/Worker/Card/WorkerTimeControl.vue +++ b/src/pages/Worker/Card/WorkerTimeControl.vue @@ -629,6 +629,9 @@ onMounted(async () => { margin-bottom: 0px; } } +:deep(.q-td) { + min-width: 170px; +} </style> <i18n> diff --git a/src/pages/Zone/Card/ZoneEvents.vue b/src/pages/Zone/Card/ZoneEvents.vue index 21991481a..32a7db4c4 100644 --- a/src/pages/Zone/Card/ZoneEvents.vue +++ b/src/pages/Zone/Card/ZoneEvents.vue @@ -44,15 +44,34 @@ onUnmounted(() => (stateStore.rightDrawer = false)); </script> <template> - <Teleport to="#right-panel" v-if="useStateStore().isHeaderMounted()"> - <ZoneEventsPanel - :first-day="firstDay" - :last-day="lastDay" - :events="events" - v-model:formModeName="formModeName" - @open-zone-form="openForm" - /> - </Teleport> + <template v-if="stateStore.isHeaderMounted()"> + <Teleport to="#actions-append"> + <div class="row q-gutter-x-sm"> + <QBtn + flat + @click="stateStore.toggleRightDrawer()" + round + dense + icon="dock_to_left" + > + <QTooltip bottom anchor="bottom right"> + {{ t('globals.collapseMenu') }} + </QTooltip> + </QBtn> + </div> + </Teleport> + </template> + <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> + <QScrollArea class="fit text-grey-8"> + <ZoneEventsPanel + :first-day="firstDay" + :last-day="lastDay" + :events="events" + v-model:formModeName="formModeName" + @open-zone-form="openForm" + /> + </QScrollArea> + </QDrawer> <QPage class="q-pa-md flex justify-center"> <ZoneCalendarGrid v-model:events="events" @@ -83,6 +102,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); color="primary" fab icon="add" + shortcut="+" /> <QTooltip class="text-no-wrap"> {{ t('eventsInclusionForm.addEvent') }} diff --git a/src/pages/Zone/Card/ZoneWarehouses.vue b/src/pages/Zone/Card/ZoneWarehouses.vue index 1d28bf391..6b2933224 100644 --- a/src/pages/Zone/Card/ZoneWarehouses.vue +++ b/src/pages/Zone/Card/ZoneWarehouses.vue @@ -111,7 +111,13 @@ const openCreateWarehouseForm = () => createWarehouseDialogRef.value.show(); <ZoneCreateWarehouse @on-submit-create-warehouse="createZoneWarehouse" /> </QDialog> <QPageSticky position="bottom-right" :offset="[18, 18]"> - <QBtn fab icon="add" color="primary" @click="openCreateWarehouseForm()"> + <QBtn + fab + icon="add" + color="primary" + @click="openCreateWarehouseForm()" + shortcut="+" + > <QTooltip>{{ t('warehouses.add') }}</QTooltip> </QBtn> </QPageSticky> diff --git a/src/router/modules/worker.js b/src/router/modules/worker.js index 98baab41e..573bff8ac 100644 --- a/src/router/modules/worker.js +++ b/src/router/modules/worker.js @@ -26,6 +26,7 @@ export default { 'WorkerLocker', 'WorkerBalance', 'WorkerFormation', + 'WorkerMedical', ], }, children: [ @@ -206,6 +207,15 @@ export default { }, component: () => import('src/pages/Worker/Card/WorkerFormation.vue'), }, + { + name: 'WorkerMedical', + path: 'medical', + meta: { + title: 'medical', + icon: 'medical_information', + }, + component: () => import('src/pages/Worker/Card/WorkerMedical.vue'), + }, ], }, ], diff --git a/test/cypress/integration/worker/workerLocker.spec.js b/test/cypress/integration/worker/workerLocker.spec.js index 9a4066f54..8a169dfb2 100644 --- a/test/cypress/integration/worker/workerLocker.spec.js +++ b/test/cypress/integration/worker/workerLocker.spec.js @@ -1,12 +1,12 @@ describe('WorkerLocker', () => { - const workerId = 1109; + const productionId = 49; const lockerCode = '2F'; const input = '.q-card input'; const thirdOpt = '[role="listbox"] .q-item:nth-child(1)'; beforeEach(() => { cy.viewport(1280, 720); cy.login('productionBoss'); - cy.visit(`/#/worker/${workerId}/locker`); + cy.visit(`/#/worker/${productionId}/locker`); }); it('should allocates a locker', () => {