0
0
Fork 0
This commit is contained in:
Carlos Satorres 2024-09-09 13:22:15 +02:00
commit bf4d11545d
59 changed files with 669 additions and 218 deletions

34
src/boot/keyShortcut.js Normal file
View File

@ -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);
}
},
};

View File

@ -1,8 +1,10 @@
import { boot } from 'quasar/wrappers'; import { boot } from 'quasar/wrappers';
import qFormMixin from './qformMixin'; import qFormMixin from './qformMixin';
import mainShortcutMixin from './mainShortcutMixin'; import mainShortcutMixin from './mainShortcutMixin';
import keyShortcut from './keyShortcut';
export default boot(({ app }) => { export default boot(({ app }) => {
app.mixin(qFormMixin); app.mixin(qFormMixin);
app.mixin(mainShortcutMixin); app.mixin(mainShortcutMixin);
app.directive('shortcut', keyShortcut);
}); });

View File

@ -24,7 +24,13 @@ const pinnedModulesRef = ref();
<template> <template>
<QHeader color="white" elevated> <QHeader color="white" elevated>
<QToolbar class="q-py-sm q-px-md"> <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"> <QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }} {{ t('globals.collapseMenu') }}
</QTooltip> </QTooltip>

View File

@ -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>

View File

@ -2,13 +2,12 @@
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useQuasar } from 'quasar'; import { useQuasar, useDialogPluginComponent } from 'quasar';
import VnConfirm from 'components/ui/VnConfirm.vue'; import VnConfirm from 'components/ui/VnConfirm.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
import FormPopup from './FormPopup.vue'; import FormPopup from './FormPopup.vue';
import { useDialogPluginComponent } from 'quasar';
import axios from 'axios'; import axios from 'axios';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
@ -18,19 +17,19 @@ const $props = defineProps({
default: () => {}, default: () => {},
}, },
}); });
const { dialogRef } = useDialogPluginComponent(); const { dialogRef } = useDialogPluginComponent();
const quasar = useQuasar(); const quasar = useQuasar();
const { t } = useI18n(); const { t } = useI18n();
const router = useRouter(); const router = useRouter();
const { notify } = useNotify(); const { notify } = useNotify();
const checked = ref(true);
const transferInvoiceParams = reactive({
id: $props.invoiceOutData?.id,
refFk: $props.invoiceOutData?.ref,
});
const rectificativeTypeOptions = ref([]); const rectificativeTypeOptions = ref([]);
const siiTypeInvoiceOutsOptions = ref([]); const siiTypeInvoiceOutsOptions = ref([]);
const checked = ref(true);
const transferInvoiceParams = reactive({
id: $props.invoiceOutData?.id,
});
const invoiceCorrectionTypesOptions = ref([]); const invoiceCorrectionTypesOptions = ref([]);
const selectedClient = (client) => { const selectedClient = (client) => {
@ -44,10 +43,9 @@ const makeInvoice = async () => {
const params = { const params = {
id: transferInvoiceParams.id, id: transferInvoiceParams.id,
cplusRectificationTypeFk: transferInvoiceParams.cplusRectificationTypeFk, cplusRectificationTypeFk: transferInvoiceParams.cplusRectificationTypeFk,
siiTypeInvoiceOutFk: transferInvoiceParams.siiTypeInvoiceOutFk,
invoiceCorrectionTypeFk: transferInvoiceParams.invoiceCorrectionTypeFk, invoiceCorrectionTypeFk: transferInvoiceParams.invoiceCorrectionTypeFk,
newClientFk: transferInvoiceParams.newClientFk, newClientFk: transferInvoiceParams.newClientFk,
refFk: transferInvoiceParams.refFk,
siiTypeInvoiceOutFk: transferInvoiceParams.siiTypeInvoiceOutFk,
makeInvoice: checked.value, 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'); notify(t('Transferred invoice'), 'positive');
const id = data?.[0]; const id = data?.[0];
if (id) router.push({ name: 'InvoiceOutSummary', params: { id } }); if (id) router.push({ name: 'InvoiceOutSummary', params: { id } });

View File

@ -617,7 +617,7 @@ defineExpose({
</template> </template>
</CrudModel> </CrudModel>
<QPageSticky v-if="create" :offset="[20, 20]" style="z-index: 2"> <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> <QTooltip>
{{ createForm.title }} {{ createForm.title }}
</QTooltip> </QTooltip>

View File

@ -37,7 +37,7 @@ const stateStore = useStateStore();
@click="stateStore.toggleRightDrawer()" @click="stateStore.toggleRightDrawer()"
round round
dense dense
icon="menu" icon="dock_to_left"
> >
<QTooltip bottom anchor="bottom right"> <QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }} {{ t('globals.collapseMenu') }}

View File

@ -1,6 +1,6 @@
<script setup> <script setup>
import { onBeforeMount, computed } from 'vue'; 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 { useArrayData } from 'src/composables/useArrayData';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import useCardSize from 'src/composables/useCardSize'; import useCardSize from 'src/composables/useCardSize';
@ -17,10 +17,12 @@ const props = defineProps({
filterPanel: { type: Object, default: undefined }, filterPanel: { type: Object, default: undefined },
searchDataKey: { type: String, default: undefined }, searchDataKey: { type: String, default: undefined },
searchbarProps: { type: Object, default: undefined }, searchbarProps: { type: Object, default: undefined },
redirectOnError: { type: Boolean, default: false },
}); });
const stateStore = useStateStore(); const stateStore = useStateStore();
const route = useRoute(); const route = useRoute();
const router = useRouter();
const url = computed(() => { const url = computed(() => {
if (props.baseUrl) return `${props.baseUrl}/${route.params.id}`; if (props.baseUrl) return `${props.baseUrl}/${route.params.id}`;
return props.customUrl; return props.customUrl;
@ -35,8 +37,12 @@ const arrayData = useArrayData(props.dataKey, {
}); });
onBeforeMount(async () => { onBeforeMount(async () => {
if (!props.baseUrl) arrayData.store.filter.where = { id: route.params.id }; try {
await arrayData.fetch({ append: false, updateRouter: false }); 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) { if (props.baseUrl) {

View File

@ -135,6 +135,7 @@ const columns = computed(() => [
field: 'hasFile', field: 'hasFile',
label: t('globals.original'), label: t('globals.original'),
name: 'hasFile', name: 'hasFile',
toolTip: t('The documentation is available in paper form'),
component: QCheckbox, component: QCheckbox,
props: (prop) => ({ props: (prop) => ({
disable: true, disable: true,
@ -297,6 +298,14 @@ defineExpose({
row-key="clientFk" row-key="clientFk"
:grid="$q.screen.lt.sm" :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"> <template #body-cell="props">
<QTd :props="props"> <QTd :props="props">
<QTr :props="props"> <QTr :props="props">
@ -397,8 +406,10 @@ defineExpose({
<i18n> <i18n>
en: en:
contentTypesInfo: Allowed file types {allowedContentTypes} contentTypesInfo: Allowed file types {allowedContentTypes}
The documentation is available in paper form: The documentation is available in paper form
es: es:
contentTypesInfo: Tipos de archivo permitidos {allowedContentTypes} contentTypesInfo: Tipos de archivo permitidos {allowedContentTypes}
Generate identifier for original file: Generar identificador para archivo original Generate identifier for original file: Generar identificador para archivo original
Upload file: Subir fichero Upload file: Subir fichero
the documentation is available in paper form: Se tiene la documentación en papel
</i18n> </i18n>

View File

@ -2,10 +2,6 @@
import { computed } from 'vue'; import { computed } from 'vue';
const $props = defineProps({ const $props = defineProps({
maxLength: {
type: Number,
required: true,
},
item: { item: {
type: Object, type: Object,
required: true, required: true,

View File

@ -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() { async function clearFilters() {
try { try {
isLoading.value = true; isLoading.value = true;
@ -231,32 +220,18 @@ function sanitizer(params) {
</QItemLabel> </QItemLabel>
</QItemSection> </QItemSection>
<QItemSection top side> <QItemSection top side>
<div class="q-gutter-xs"> <QBtn
<QBtn @click="clearFilters"
@click="clearFilters" color="primary"
color="primary" dense
dense flat
flat icon="filter_list_off"
icon="filter_list_off" padding="none"
padding="none" round
round size="sm"
size="sm" >
> <QTooltip>{{ t('Remove filters') }}</QTooltip>
<QTooltip>{{ t('Remove filters') }}</QTooltip> </QBtn>
</QBtn>
<QBtn
@click="reload"
color="primary"
dense
flat
icon="refresh"
padding="none"
round
size="sm"
>
<QTooltip>{{ t('Refresh') }}</QTooltip>
</QBtn>
</div>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem class="q-mb-sm"> <QItem class="q-mb-sm">

View File

@ -84,7 +84,7 @@ globals:
description: Description description: Description
id: Id id: Id
order: Order order: Order
original: Original original: Phys. Doc
file: File file: File
selectFile: Select a file selectFile: Select a file
copyClipboard: Copy on clipboard copyClipboard: Copy on clipboard
@ -261,6 +261,7 @@ globals:
clientsActionsMonitor: Clients and actions clientsActionsMonitor: Clients and actions
serial: Serial serial: Serial
business: Business business: Business
medical: Mutual
created: Created created: Created
worker: Worker worker: Worker
now: Now now: Now
@ -880,6 +881,7 @@ worker:
locker: Locker locker: Locker
balance: Balance balance: Balance
business: Business business: Business
medical: Medical
list: list:
name: Name name: Name
email: Email email: Email
@ -966,6 +968,15 @@ worker:
amount: Importe amount: Importe
remark: Bonficado remark: Bonficado
hasDiploma: Diploma hasDiploma: Diploma
medical:
tableVisibleColumns:
date: Date
time: Hour
center: Formation Center
invoice: Invoice
amount: Amount
isFit: Fit
remark: Observations
imageNotFound: Image not found imageNotFound: Image not found
balance: balance:
tableVisibleColumns: tableVisibleColumns:

View File

@ -86,7 +86,7 @@ globals:
description: Descripción description: Descripción
id: Id id: Id
order: Orden order: Orden
original: Original original: Doc. física
file: Fichero file: Fichero
selectFile: Seleccione un fichero selectFile: Seleccione un fichero
copyClipboard: Copiar en portapapeles copyClipboard: Copiar en portapapeles
@ -265,6 +265,7 @@ globals:
clientsActionsMonitor: Clientes y acciones clientsActionsMonitor: Clientes y acciones
serial: Facturas por serie serial: Facturas por serie
business: Contratos business: Contratos
medical: Mutua
created: Fecha creación created: Fecha creación
worker: Trabajador worker: Trabajador
now: Ahora now: Ahora
@ -883,6 +884,8 @@ worker:
locker: Taquilla locker: Taquilla
balance: Balance balance: Balance
business: Contrato business: Contrato
formation: Formación
medical: Mutua
list: list:
name: Nombre name: Nombre
email: Email email: Email
@ -969,6 +972,15 @@ worker:
amount: Importe amount: Importe
remark: Bonficado remark: Bonficado
hasDiploma: Diploma 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 imageNotFound: No se ha encontrado la imagen
balance: balance:
tableVisibleColumns: tableVisibleColumns:

View File

@ -5,7 +5,7 @@ const quasar = useQuasar();
</script> </script>
<template> <template>
<QLayout view="hHh LpR fFf"> <QLayout view="hHh LpR fFf" v-shortcut>
<Navbar /> <Navbar />
<RouterView></RouterView> <RouterView></RouterView>
<QFooter v-if="quasar.platform.is.mobile"></QFooter> <QFooter v-if="quasar.platform.is.mobile"></QFooter>

View File

@ -27,15 +27,15 @@ const filter = {
order: 'created DESC', order: 'created DESC',
}; };
const urlPath = 'AccessTokens'; const urlPath = 'VnTokens';
const refresh = () => paginateRef.value.fetch(); const refresh = () => paginateRef.value.fetch();
const navigate = (id) => router.push({ name: 'AccountSummary', params: { id } }); const navigate = (id) => router.push({ name: 'AccountSummary', params: { id } });
const killSession = async (id) => { const killSession = async ({ userId, created }) => {
try { try {
await axios.delete(`${urlPath}/${id}`); await axios.post(`${urlPath}/killSession`, { userId, created });
paginateRef.value.fetch(); paginateRef.value.fetch();
notify(t('Session killed'), 'positive'); notify(t('Session killed'), 'positive');
} catch (error) { } catch (error) {
@ -84,7 +84,7 @@ const killSession = async (id) => {
openConfirmationModal( openConfirmationModal(
t('Session will be killed'), t('Session will be killed'),
t('Are you sure you want to continue?'), t('Are you sure you want to continue?'),
() => killSession(row.id) () => killSession(row)
) )
" "
outline outline

View File

@ -169,7 +169,13 @@ onMounted(async () => await getAccountData(false));
<AccountMailAliasCreateForm @on-submit-create-alias="createMailAlias" /> <AccountMailAliasCreateForm @on-submit-create-alias="createMailAlias" />
</QDialog> </QDialog>
<QPageSticky position="bottom-right" :offset="[18, 18]"> <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> <QTooltip>{{ t('warehouses.add') }}</QTooltip>
</QBtn> </QBtn>
</QPageSticky> </QPageSticky>

View File

@ -218,7 +218,13 @@ const toCustomerAddressEdit = (addressId) => {
</div> </div>
<QPageSticky :offset="[18, 18]"> <QPageSticky :offset="[18, 18]">
<QBtn @click.stop="toCustomerAddressCreate()" color="primary" fab icon="add" /> <QBtn
@click.stop="toCustomerAddressCreate()"
color="primary"
fab
icon="add"
shortcut="+"
/>
<QTooltip> <QTooltip>
{{ t('New consignee') }} {{ t('New consignee') }}
</QTooltip> </QTooltip>

View File

@ -292,7 +292,13 @@ const showBalancePdf = ({ id }) => {
</template> </template>
</VnTable> </VnTable>
<QPageSticky :offset="[18, 18]" style="z-index: 2"> <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> <QTooltip>
{{ t('New payment') }} {{ t('New payment') }}
</QTooltip> </QTooltip>

View File

@ -193,6 +193,7 @@ const updateData = () => {
color="primary" color="primary"
fab fab
icon="add" icon="add"
shortcut="+"
/> />
<QTooltip> <QTooltip>
{{ t('New contract') }} {{ t('New contract') }}

View File

@ -423,7 +423,7 @@ const lockIconType = (groupingMode, mode) => {
<span v-if="props.row.item.subName" class="subName"> <span v-if="props.row.item.subName" class="subName">
{{ props.row.item.subName }} {{ props.row.item.subName }}
</span> </span>
<FetchedTags :item="props.row.item" :max-length="5" /> <FetchedTags :item="props.row.item" />
</QTd> </QTd>
</QTr> </QTr>
</template> </template>

View File

@ -319,7 +319,7 @@ const fetchEntryBuys = async () => {
<span v-if="row.item.subName" class="subName"> <span v-if="row.item.subName" class="subName">
{{ row.item.subName }} {{ row.item.subName }}
</span> </span>
<FetchedTags :item="row.item" :max-length="5" /> <FetchedTags :item="row.item" />
</QTd> </QTd>
</QTr> </QTr>
<!-- Esta última row es utilizada para agregar un espaciado y así marcar una diferencia visual entre los diferentes buys --> <!-- Esta última row es utilizada para agregar un espaciado y así marcar una diferencia visual entre los diferentes buys -->

View File

@ -5,6 +5,7 @@ import { useRouter } from 'vue-router';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import TransferInvoiceForm from 'src/components/TransferInvoiceForm.vue'; import TransferInvoiceForm from 'src/components/TransferInvoiceForm.vue';
import RefundInvoiceForm from 'src/components/RefundInvoiceForm.vue';
import SendEmailDialog from 'components/common/SendEmailDialog.vue'; import SendEmailDialog from 'components/common/SendEmailDialog.vue';
import useNotify from 'src/composables/useNotify'; import useNotify from 'src/composables/useNotify';
@ -141,6 +142,15 @@ const showTransferInvoiceForm = async () => {
}, },
}); });
}; };
const showRefundInvoiceForm = () => {
quasar.dialog({
component: RefundInvoiceForm,
componentProps: {
invoiceOutData: $props.invoiceOutData,
},
});
};
</script> </script>
<template> <template>
@ -229,10 +239,13 @@ const showTransferInvoiceForm = async () => {
<QMenu anchor="top end" self="top start"> <QMenu anchor="top end" self="top start">
<QList> <QList>
<QItem v-ripple clickable @click="refundInvoice(true)"> <QItem v-ripple clickable @click="refundInvoice(true)">
<QItemSection>{{ t('With warehouse') }}</QItemSection> <QItemSection>{{ t('With warehouse, no invoice') }}</QItemSection>
</QItem> </QItem>
<QItem v-ripple clickable @click="refundInvoice(false)"> <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> </QItem>
</QList> </QList>
</QMenu> </QMenu>
@ -255,8 +268,9 @@ es:
As CSV: como CSV As CSV: como CSV
Send PDF: Enviar PDF Send PDF: Enviar PDF
Send CSV: Enviar CSV Send CSV: Enviar CSV
With warehouse: Con almacén With warehouse, no invoice: Con almacén, sin factura
Without warehouse: Sin almacén Without warehouse, no invoice: Sin almacén, sin factura
Invoiced: Facturado
InvoiceOut deleted: Factura eliminada InvoiceOut deleted: Factura eliminada
Confirm deletion: Confirmar eliminación Confirm deletion: Confirmar eliminación
Are you sure you want to delete this invoice?: Estas seguro de eliminar esta factura? Are you sure you want to delete this invoice?: Estas seguro de eliminar esta factura?

View File

@ -10,8 +10,6 @@ import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.v
import VnConfirm from 'components/ui/VnConfirm.vue'; import VnConfirm from 'components/ui/VnConfirm.vue';
import RegularizeStockForm from 'components/RegularizeStockForm.vue'; import RegularizeStockForm from 'components/RegularizeStockForm.vue';
import ItemDescriptorImage from 'src/pages/Item/Card/ItemDescriptorImage.vue'; import ItemDescriptorImage from 'src/pages/Item/Card/ItemDescriptorImage.vue';
import { useState } from 'src/composables/useState';
import useCardDescription from 'src/composables/useCardDescription'; import useCardDescription from 'src/composables/useCardDescription';
import { getUrl } from 'src/composables/getUrl'; import { getUrl } from 'src/composables/getUrl';
import axios from 'axios'; import axios from 'axios';
@ -35,58 +33,69 @@ const $props = defineProps({
type: Number, type: Number,
default: null, default: null,
}, },
warehouseFk: {
type: Number,
default: null,
},
}); });
const quasar = useQuasar(); const quasar = useQuasar();
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();
const state = useState(); const warehouseConfig = ref(null);
const user = state.getUser();
const entityId = computed(() => { const entityId = computed(() => {
return $props.id || route.params.id; return $props.id || route.params.id;
}); });
const regularizeStockFormDialog = ref(null); const regularizeStockFormDialog = ref(null);
const available = ref(null); const available = ref(null);
const visible = ref(null); const visible = ref(null);
const _warehouseFk = ref(null);
const salixUrl = ref(); const salixUrl = ref();
const warehouseFk = computed({
get() {
return _warehouseFk.value;
},
set(val) {
_warehouseFk.value = val;
if (val) updateStock();
},
});
onMounted(async () => { onMounted(async () => {
warehouseFk.value = user.value.warehouseFk;
salixUrl.value = await getUrl(''); salixUrl.value = await getUrl('');
await getItemConfigs();
await updateStock();
}); });
const data = ref(useCardDescription()); const data = ref(useCardDescription());
const setData = (entity) => { const setData = async (entity) => {
if (!entity) return; try {
data.value = useCardDescription(entity.name, entity.id); 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 () => { const updateStock = async () => {
try { try {
available.value = null; available.value = null;
visible.value = null; visible.value = null;
const params = { const params = {
warehouseFk: warehouseFk.value, warehouseFk: $props.warehouseFk,
dated: $props.dated, dated: $props.dated,
}; };
await getItemConfigs();
if (!params.warehouseFk) {
params.warehouseFk = warehouseConfig.value;
}
const { data } = await axios.get(`Items/${entityId.value}/getVisibleAvailable`, { const { data } = await axios.get(`Items/${entityId.value}/getVisibleAvailable`, {
params, params,
}); });
available.value = data.available; available.value = data.available;
visible.value = data.visible; visible.value = data.visible;
} catch (err) { } catch (err) {

View File

@ -47,8 +47,11 @@ const getWarehouseName = async (warehouseFk) => {
const filter = { const filter = {
where: { id: warehouseFk }, where: { id: warehouseFk },
}; };
const { data } = await axios.get('Warehouses/findOne', {
const { data } = await axios.get('Warehouses/findOne', { filter }); params: {
filter: JSON.stringify(filter),
},
});
if (!data) return; if (!data) return;
warehouseName.value = data.name; warehouseName.value = data.name;
}; };

View File

@ -15,6 +15,10 @@ const $props = defineProps({
type: Number, type: Number,
default: null, default: null,
}, },
warehouseFk: {
type: Number,
default: null,
},
}); });
</script> </script>
@ -26,6 +30,7 @@ const $props = defineProps({
:summary="ItemSummary" :summary="ItemSummary"
:dated="dated" :dated="dated"
:sale-fk="saleFk" :sale-fk="saleFk"
:warehouse-fk="warehouseFk"
/> />
</QPopupProxy> </QPopupProxy>
</template> </template>

View File

@ -436,7 +436,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
{{ row.name }} {{ row.name }}
</span> </span>
<ItemDescriptorProxy :id="row.itemFk" /> <ItemDescriptorProxy :id="row.itemFk" />
<FetchedTags :item="row" :max-length="6" /> <FetchedTags :item="row" />
</QTd> </QTd>
</template> </template>
<template #body-cell-groupingPrice="props"> <template #body-cell-groupingPrice="props">

View File

@ -517,7 +517,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
<template #body-cell-description="{ row }"> <template #body-cell-description="{ row }">
<QTd class="col"> <QTd class="col">
<span>{{ row.name }} {{ row.subName }}</span> <span>{{ row.name }} {{ row.subName }}</span>
<FetchedTags :item="row" :max-length="6" /> <FetchedTags :item="row" />
</QTd> </QTd>
</template> </template>
<template #body-cell-isActive="{ row }"> <template #body-cell-isActive="{ row }">
@ -562,7 +562,13 @@ onUnmounted(() => (stateStore.rightDrawer = false));
</VnPaginate> </VnPaginate>
<QPageSticky :offset="[20, 20]"> <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"> <QTooltip class="text-no-wrap">
{{ t('New item') }} {{ t('New item') }}
</QTooltip> </QTooltip>

View File

@ -99,7 +99,13 @@ const exprBuilder = (param, value) => {
</div> </div>
</QPage> </QPage>
<QPageSticky :offset="[20, 20]"> <QPageSticky :offset="[20, 20]">
<QBtn fab icon="add" color="primary" @click="redirectToCreateView()" /> <QBtn
fab
icon="add"
color="primary"
@click="redirectToCreateView()"
shortcut="+"
/>
<QTooltip> <QTooltip>
{{ t('New item type') }} {{ t('New item type') }}
</QTooltip> </QTooltip>

View File

@ -380,21 +380,6 @@ function addOrder(value, field, params) {
@click="tagValues.push({})" @click="tagValues.push({})"
/> />
</QItem> </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 /> <QSeparator />
</template> </template>
</VnFilterPanel> </VnFilterPanel>

View File

@ -77,10 +77,6 @@ const addToOrder = async () => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
// .container {
// max-width: 768px;
// width: 100%;
// }
.td { .td {
width: 200px; width: 200px;
} }

View File

@ -14,6 +14,7 @@ import FetchData from 'src/components/FetchData.vue';
import VnImg from 'src/components/ui/VnImg.vue'; import VnImg from 'src/components/ui/VnImg.vue';
import VnLv from 'src/components/ui/VnLv.vue'; import VnLv from 'src/components/ui/VnLv.vue';
import FetchedTags from 'src/components/ui/FetchedTags.vue'; import FetchedTags from 'src/components/ui/FetchedTags.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
const router = useRouter(); const router = useRouter();
const stateStore = useStateStore(); const stateStore = useStateStore();
@ -280,7 +281,12 @@ watch(
<VnImg :id="parseInt(row?.item?.image)" class="rounded" /> <VnImg :id="parseInt(row?.item?.image)" class="rounded" />
</div> </div>
</template> </template>
<template #column-id="{ row }">
<span class="link" @click.stop>
{{ row?.item?.id }}
<ItemDescriptorProxy :id="row?.item?.id" />
</span>
</template>
<template #column-itemFk="{ row }"> <template #column-itemFk="{ row }">
<div class="row column full-width justify-between items-start"> <div class="row column full-width justify-between items-start">
{{ row?.item?.name }} {{ row?.item?.name }}
@ -288,7 +294,7 @@ watch(
{{ row?.item?.subName.toUpperCase() }} {{ row?.item?.subName.toUpperCase() }}
</div> </div>
</div> </div>
<FetchedTags :item="row?.item" :max-length="6" /> <FetchedTags :item="row?.item" />
</template> </template>
<template #column-amount="{ row }"> <template #column-amount="{ row }">
{{ toCurrency(row.quantity * row.price) }} {{ toCurrency(row.quantity * row.price) }}

View File

@ -192,7 +192,7 @@ const detailsColumns = ref([
</span> </span>
</div> </div>
</div> </div>
<FetchedTags :item="props.row.item" :max-length="5" /> <FetchedTags :item="props.row.item" />
</QTd> </QTd>
<QTd key="quantity" :props="props"> <QTd key="quantity" :props="props">
{{ props.row.quantity }} {{ props.row.quantity }}

View File

@ -2,8 +2,9 @@
import axios from 'axios'; import axios from 'axios';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { ref } from 'vue'; import { ref, onMounted } from 'vue';
import { dashIfEmpty } from 'src/filters'; import { dashIfEmpty } from 'src/filters';
import { useStateStore } from 'stores/useStateStore';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import FetchedTags from 'components/ui/FetchedTags.vue'; import FetchedTags from 'components/ui/FetchedTags.vue';
@ -58,6 +59,9 @@ const loadVolumes = async (rows) => {
}); });
volumes.value = rows; volumes.value = rows;
}; };
const stateStore = useStateStore();
onMounted(async () => (stateStore.rightDrawer = false));
</script> </script>
<template> <template>
@ -84,6 +88,7 @@ const loadVolumes = async (rows) => {
@on-fetch="(data) => loadVolumes(data)" @on-fetch="(data) => loadVolumes(data)"
:right-search="false" :right-search="false"
:column-search="false" :column-search="false"
:disable-option="{ card: true }"
> >
<template #column-itemFk="{ row }"> <template #column-itemFk="{ row }">
<span class="link"> <span class="link">
@ -92,7 +97,13 @@ const loadVolumes = async (rows) => {
</span> </span>
</template> </template>
<template #column-description="{ row }"> <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>
<template #column-volume="{ rowIndex }"> <template #column-volume="{ rowIndex }">
{{ volumes?.[rowIndex]?.volume }} {{ volumes?.[rowIndex]?.volume }}
@ -121,6 +132,11 @@ const loadVolumes = async (rows) => {
} }
} }
} }
.subName {
color: var(--vn-label-color);
text-transform: uppercase;
}
</style> </style>
<i18n> <i18n>
en: en:

View File

@ -11,6 +11,9 @@ import VnSelect from 'src/components/common/VnSelect.vue';
import OrderSearchbar from './Card/OrderSearchbar.vue'; import OrderSearchbar from './Card/OrderSearchbar.vue';
import RightMenu from 'src/components/common/RightMenu.vue'; import RightMenu from 'src/components/common/RightMenu.vue';
import OrderFilter from './Card/OrderFilter.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 { t } = useI18n();
const { viewSummary } = useSummaryDialog(); const { viewSummary } = useSummaryDialog();
@ -75,7 +78,7 @@ const columns = computed(() => [
label: t('module.created'), label: t('module.created'),
component: 'date', component: 'date',
cardVisible: true, cardVisible: true,
format: (row) => toDate(row?.landed), format: (row) => toDateTimeFormat(row?.landed),
columnField: { columnField: {
component: null, component: null,
}, },
@ -115,6 +118,7 @@ const columns = computed(() => [
}, },
}, },
cardVisible: true, cardVisible: true,
columnClass: 'expand',
}, },
{ {
align: 'left', align: 'left',
@ -132,6 +136,7 @@ const columns = computed(() => [
title: t('InvoiceOutSummary'), title: t('InvoiceOutSummary'),
icon: 'preview', icon: 'preview',
action: (row) => viewSummary(row.id, OrderSummary), action: (row) => viewSummary(row.id, OrderSummary),
isPrimary: true,
}, },
], ],
}, },
@ -154,6 +159,16 @@ async function fetchAgencies({ landed, addressId }) {
}); });
agencyList.value = data; 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> </script>
<template> <template>
<OrderSearchbar /> <OrderSearchbar />
@ -183,6 +198,25 @@ async function fetchAgencies({ landed, addressId }) {
:columns="columns" :columns="columns"
redirect="order" 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 }"> <template #more-create-dialog="{ data }">
<VnSelect <VnSelect
url="Clients" url="Clients"

View File

@ -87,7 +87,7 @@ function exprBuilder(param, value) {
</div> </div>
<QPageSticky :offset="[20, 20]"> <QPageSticky :offset="[20, 20]">
<RouterLink :to="{ name: 'ShelvingCreate' }"> <RouterLink :to="{ name: 'ShelvingCreate' }">
<QBtn fab icon="add" color="primary" /> <QBtn fab icon="add" color="primary" shortcut="+" />
<QTooltip> <QTooltip>
{{ t('shelving.list.newShelving') }} {{ t('shelving.list.newShelving') }}
</QTooltip> </QTooltip>

View File

@ -81,7 +81,13 @@ const redirectToUpdateView = (addressData) => {
</VnPaginate> </VnPaginate>
</div> </div>
<QPageSticky :offset="[20, 20]"> <QPageSticky :offset="[20, 20]">
<QBtn fab icon="add" color="primary" @click="redirectToCreateView()" /> <QBtn
fab
icon="add"
color="primary"
@click="redirectToCreateView()"
shortcut="+"
/>
<QTooltip> <QTooltip>
{{ t('New address') }} {{ t('New address') }}
</QTooltip> </QTooltip>
@ -93,3 +99,4 @@ const redirectToUpdateView = (addressData) => {
es: es:
New address: Nueva dirección New address: Nueva dirección
</i18n> </i18n>
s

View File

@ -109,7 +109,13 @@ const redirectToCreateView = () => {
</template> </template>
</CrudModel> </CrudModel>
<QPageSticky :offset="[20, 20]"> <QPageSticky :offset="[20, 20]">
<QBtn fab icon="add" color="primary" @click="redirectToCreateView()" /> <QBtn
fab
icon="add"
color="primary"
@click="redirectToCreateView()"
shortcut="+"
/>
<QTooltip> <QTooltip>
{{ t('supplier.agencyTerms.addRow') }} {{ t('supplier.agencyTerms.addRow') }}
</QTooltip> </QTooltip>

View File

@ -208,7 +208,7 @@ onMounted(async () => {
<QTd no-hover> <QTd no-hover>
<span>{{ buy.subName }}</span> <span>{{ buy.subName }}</span>
<FetchedTags :item="buy" :max-length="5" /> <FetchedTags :item="buy" />
</QTd> </QTd>
<QTd no-hover> {{ dashIfEmpty(buy.quantity) }}</QTd> <QTd no-hover> {{ dashIfEmpty(buy.quantity) }}</QTd>
<QTd no-hover> {{ dashIfEmpty(buy.price) }}</QTd> <QTd no-hover> {{ dashIfEmpty(buy.price) }}</QTd>

View File

@ -245,7 +245,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
<div class="column"> <div class="column">
<span>{{ row.item.name }}</span> <span>{{ row.item.name }}</span>
<span class="color-vn-label">{{ row.item.subName }}</span> <span class="color-vn-label">{{ row.item.subName }}</span>
<FetchedTags :item="row.item" :max-length="6" /> <FetchedTags :item="row.item" />
</div> </div>
</QTd> </QTd>
</template> </template>

View File

@ -310,7 +310,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
<div class="column"> <div class="column">
<span>{{ row.item.name }}</span> <span>{{ row.item.name }}</span>
<span class="color-vn-label">{{ row.item.subName }}</span> <span class="color-vn-label">{{ row.item.subName }}</span>
<FetchedTags :item="row.item" :max-length="6" /> <FetchedTags :item="row.item" />
</div> </div>
</QTd> </QTd>
</template> </template>

View File

@ -252,7 +252,13 @@ const openCreateModal = () => createTicketRequestDialogRef.value.show();
<TicketCreateRequest @on-request-created="crudModelRef.reload()" /> <TicketCreateRequest @on-request-created="crudModelRef.reload()" />
</QDialog> </QDialog>
<QPageSticky :offset="[20, 20]"> <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"> <QTooltip class="text-no-wrap">
{{ t('purchaseRequest.newRequest') }} {{ t('purchaseRequest.newRequest') }}
</QTooltip> </QTooltip>

View File

@ -656,7 +656,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
<div class="column"> <div class="column">
<span>{{ row.concept }}</span> <span>{{ row.concept }}</span>
<span class="color-vn-label">{{ row.item?.subName }}</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"> <QPopupProxy v-if="row.id && isTicketEditable">
<VnInput v-model="row.concept" @change="updateConcept(row)" /> <VnInput v-model="row.concept" @change="updateConcept(row)" />
</QPopupProxy> </QPopupProxy>

View File

@ -412,7 +412,7 @@ const qCheckBoxController = (sale, action) => {
<span v-if="row.subName" class="color-vn-label"> <span v-if="row.subName" class="color-vn-label">
{{ row.subName }} {{ row.subName }}
</span> </span>
<FetchedTags :item="row" :max-length="6" tag="value" /> <FetchedTags :item="row" tag="value" />
</div> </div>
</QTd> </QTd>
</template> </template>

View File

@ -383,7 +383,11 @@ async function changeState(value) {
<QTd> <QTd>
<QBtn class="link" flat> <QBtn class="link" flat>
{{ props.row.itemFk }} {{ props.row.itemFk }}
<ItemDescriptorProxy :id="props.row.itemFk" /> <ItemDescriptorProxy
:id="props.row.itemFk"
:sale-fk="props.row.id"
:warehouse-fk="ticket.warehouseFk"
/>
</QBtn> </QBtn>
</QTd> </QTd>
<QTd>{{ props.row.visible }}</QTd> <QTd>{{ props.row.visible }}</QTd>
@ -399,7 +403,6 @@ async function changeState(value) {
<FetchedTags <FetchedTags
class="fetched-tags" class="fetched-tags"
:item="props.row.item" :item="props.row.item"
:max-length="5"
></FetchedTags> ></FetchedTags>
</QTd> </QTd>
<QTd>{{ props.row.price }} </QTd> <QTd>{{ props.row.price }} </QTd>

View File

@ -82,7 +82,7 @@ const openCreateModal = () => createTrackingDialogRef.value.show();
data-key="TicketTracking" data-key="TicketTracking"
:filter="paginateFilter" :filter="paginateFilter"
url="TicketTrackings" url="TicketTrackings"
auto-load auto-load
order="created DESC" order="created DESC"
:limit="0" :limit="0"
> >
@ -114,7 +114,13 @@ const openCreateModal = () => createTrackingDialogRef.value.show();
<TicketCreateTracking @on-request-created="paginateRef.fetch()" /> <TicketCreateTracking @on-request-created="paginateRef.fetch()" />
</QDialog> </QDialog>
<QPageSticky :offset="[20, 20]"> <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"> <QTooltip class="text-no-wrap">
{{ t('tracking.addState') }} {{ t('tracking.addState') }}
</QTooltip> </QTooltip>

View File

@ -145,7 +145,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
<div class="column"> <div class="column">
<span>{{ row.item.name }}</span> <span>{{ row.item.name }}</span>
<span class="color-vn-label">{{ row.item.subName }}</span> <span class="color-vn-label">{{ row.item.subName }}</span>
<FetchedTags :item="row.item" :max-length="6" /> <FetchedTags :item="row.item" />
</div> </div>
</QTd> </QTd>
</template> </template>

View File

@ -196,6 +196,7 @@ const removeThermograph = async (id) => {
icon="add" icon="add"
color="primary" color="primary"
@click="redirectToThermographForm('create')" @click="redirectToThermographForm('create')"
shortcut="+"
/> />
<QTooltip class="text-no-wrap"> <QTooltip class="text-no-wrap">
{{ t('Add thermograph') }} {{ t('Add thermograph') }}

View File

@ -74,7 +74,7 @@ async function remove(row) {
</VnPaginate> </VnPaginate>
</div> </div>
<QPageSticky position="bottom-right" :offset="[18, 18]"> <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> </QPageSticky>
</QPage> </QPage>
</template> </template>

View File

@ -94,7 +94,7 @@ async function remove(row) {
</VnPaginate> </VnPaginate>
</div> </div>
<QPageSticky position="bottom-right" :offset="[18, 18]"> <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> </QPageSticky>
</QPage> </QPage>
</template> </template>

View File

@ -15,5 +15,6 @@ import WorkerFilter from '../WorkerFilter.vue';
label: 'Search worker', label: 'Search worker',
info: 'You can search by worker id or name', info: 'You can search by worker id or name',
}" }"
:redirect-on-error="true"
/> />
</template> </template>

View File

@ -1,12 +1,11 @@
<script setup> <script setup>
import { computed, ref, watch } from 'vue'; import { computed, ref } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import CardDescriptor from 'src/components/ui/CardDescriptor.vue'; import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue'; import VnLv from 'src/components/ui/VnLv.vue';
import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue'; import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
import WorkerChangePasswordForm from 'src/pages/Worker/Card/WorkerChangePasswordForm.vue'; import WorkerChangePasswordForm from 'src/pages/Worker/Card/WorkerChangePasswordForm.vue';
import useCardDescription from 'src/composables/useCardDescription';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
import axios from 'axios'; import axios from 'axios';
import VnImg from 'src/components/ui/VnImg.vue'; import VnImg from 'src/components/ui/VnImg.vue';
@ -38,49 +37,6 @@ const entityId = computed(() => {
const worker = ref(); const worker = ref();
const workerExcluded = ref(false); 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 () => { const getIsExcluded = async () => {
try { try {
@ -115,14 +71,12 @@ const refetch = async () => await cardDescriptorRef.value.getData();
ref="cardDescriptorRef" ref="cardDescriptorRef"
module="Worker" module="Worker"
data-key="workerData" data-key="workerData"
:url="`Workers/${entityId}`" url="Workers/descriptor"
:filter="filter" :filter="{ where: { id: entityId } }"
:title="data.title" title="user.nickname"
:subtitle="data.subtitle"
@on-fetch=" @on-fetch="
(data) => { (data) => {
worker = data; worker = data;
setData(data);
getIsExcluded(); getIsExcluded();
} }
" "
@ -141,7 +95,7 @@ const refetch = async () => await cardDescriptorRef.value.getData();
v-if="!worker.user.emailVerified && user.id != worker.id" v-if="!worker.user.emailVerified && user.id != worker.id"
v-ripple v-ripple
clickable clickable
@click="openChangePasswordForm()" @click="$refs.changePasswordFormDialog.show()"
> >
<QItemSection> <QItemSection>
{{ t('Change password') }} {{ t('Change password') }}
@ -209,10 +163,10 @@ const refetch = async () => await cardDescriptorRef.value.getData();
<VnLinkPhone :phone-number="entity.phone" /> <VnLinkPhone :phone-number="entity.phone" />
</template> </template>
</VnLv> </VnLv>
<VnLv :value="sip"> <VnLv :value="worker?.sip?.extension">
<template #label> <template #label>
{{ t('worker.summary.sipExtension') }} {{ t('worker.summary.sipExtension') }}
<VnLinkPhone v-if="sip" :phone-number="sip" /> <VnLinkPhone :phone-number="worker?.sip?.extension" />
</template> </template>
</VnLv> </VnLv>
</template> </template>

View File

@ -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>

View File

@ -116,7 +116,7 @@ function reloadData() {
</template> </template>
</VnPaginate> </VnPaginate>
<QPageSticky :offset="[18, 18]"> <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"> <QDialog ref="dialog">
<FormModelPopup <FormModelPopup
:title="t('Add new device')" :title="t('Add new device')"

View File

@ -34,13 +34,22 @@ const filter = {
{ {
relation: 'user', relation: 'user',
scope: { scope: {
fields: ['email', 'name', 'nickname', 'roleFk'], fields: ['name', 'nickname', 'roleFk'],
include: {
relation: 'role', include: [
scope: { {
fields: ['name'], relation: 'role',
scope: {
fields: ['name'],
},
}, },
}, {
relation: 'emailUser',
scope: {
fields: ['email'],
},
},
],
}, },
}, },
{ {
@ -178,14 +187,18 @@ const filter = {
</QCard> </QCard>
<QCard class="vn-one"> <QCard class="vn-one">
<VnTitle :text="t('worker.summary.userData')" /> <VnTitle :text="t('worker.summary.userData')" />
<VnLv :label="t('worker.summary.userId')" :value="worker.user.id" /> <VnLv :label="t('worker.summary.userId')" :value="worker?.user?.id" />
<VnLv :label="t('worker.card.name')" :value="worker.user.nickname" /> <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.list.email')"
:value="worker.user?.emailUser?.email"
copy
/>
<VnLv :label="t('worker.summary.role')"> <VnLv :label="t('worker.summary.role')">
<template #value> <template #value>
<span class="link"> <span class="link">
{{ worker.user.role.name }} {{ worker?.user?.role?.name }}
<RoleDescriptorProxy :id="worker.user.role.id" /> <RoleDescriptorProxy :id="worker?.user?.role?.id" />
</span> </span>
</template> </template>
</VnLv> </VnLv>

View File

@ -629,6 +629,9 @@ onMounted(async () => {
margin-bottom: 0px; margin-bottom: 0px;
} }
} }
:deep(.q-td) {
min-width: 170px;
}
</style> </style>
<i18n> <i18n>

View File

@ -44,15 +44,34 @@ onUnmounted(() => (stateStore.rightDrawer = false));
</script> </script>
<template> <template>
<Teleport to="#right-panel" v-if="useStateStore().isHeaderMounted()"> <template v-if="stateStore.isHeaderMounted()">
<ZoneEventsPanel <Teleport to="#actions-append">
:first-day="firstDay" <div class="row q-gutter-x-sm">
:last-day="lastDay" <QBtn
:events="events" flat
v-model:formModeName="formModeName" @click="stateStore.toggleRightDrawer()"
@open-zone-form="openForm" round
/> dense
</Teleport> 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"> <QPage class="q-pa-md flex justify-center">
<ZoneCalendarGrid <ZoneCalendarGrid
v-model:events="events" v-model:events="events"
@ -83,6 +102,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
color="primary" color="primary"
fab fab
icon="add" icon="add"
shortcut="+"
/> />
<QTooltip class="text-no-wrap"> <QTooltip class="text-no-wrap">
{{ t('eventsInclusionForm.addEvent') }} {{ t('eventsInclusionForm.addEvent') }}

View File

@ -111,7 +111,13 @@ const openCreateWarehouseForm = () => createWarehouseDialogRef.value.show();
<ZoneCreateWarehouse @on-submit-create-warehouse="createZoneWarehouse" /> <ZoneCreateWarehouse @on-submit-create-warehouse="createZoneWarehouse" />
</QDialog> </QDialog>
<QPageSticky position="bottom-right" :offset="[18, 18]"> <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> <QTooltip>{{ t('warehouses.add') }}</QTooltip>
</QBtn> </QBtn>
</QPageSticky> </QPageSticky>

View File

@ -26,6 +26,7 @@ export default {
'WorkerLocker', 'WorkerLocker',
'WorkerBalance', 'WorkerBalance',
'WorkerFormation', 'WorkerFormation',
'WorkerMedical',
], ],
}, },
children: [ children: [
@ -206,6 +207,15 @@ export default {
}, },
component: () => import('src/pages/Worker/Card/WorkerFormation.vue'), 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'),
},
], ],
}, },
], ],

View File

@ -1,12 +1,12 @@
describe('WorkerLocker', () => { describe('WorkerLocker', () => {
const workerId = 1109; const productionId = 49;
const lockerCode = '2F'; const lockerCode = '2F';
const input = '.q-card input'; const input = '.q-card input';
const thirdOpt = '[role="listbox"] .q-item:nth-child(1)'; const thirdOpt = '[role="listbox"] .q-item:nth-child(1)';
beforeEach(() => { beforeEach(() => {
cy.viewport(1280, 720); cy.viewport(1280, 720);
cy.login('productionBoss'); cy.login('productionBoss');
cy.visit(`/#/worker/${workerId}/locker`); cy.visit(`/#/worker/${productionId}/locker`);
}); });
it('should allocates a locker', () => { it('should allocates a locker', () => {