diff --git a/cypress.config.js b/cypress.config.js index dfe963a12..dd7de895c 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -39,10 +39,17 @@ export default defineConfig({ downloadsFolder: 'test/cypress/downloads', video: false, specPattern: 'test/cypress/integration/**/*.spec.js', - experimentalRunAllSpecs: true, - watchForFileChanges: true, - reporter, - reporterOptions, + experimentalRunAllSpecs: false, + watchForFileChanges: false, + reporter: 'cypress-mochawesome-reporter', + reporterOptions: { + charts: true, + reportPageTitle: 'Cypress Inline Reporter', + reportFilename: '[status]_[datetime]-report', + embeddedScreenshots: true, + reportDir: 'test/cypress/reports', + inlineAssets: true, + }, component: { componentFolder: 'src', testFiles: '**/*.spec.js', diff --git a/src/components/FormModelPopup.vue b/src/components/FormModelPopup.vue index 672eeff7a..85943e91e 100644 --- a/src/components/FormModelPopup.vue +++ b/src/components/FormModelPopup.vue @@ -1,12 +1,13 @@ <script setup> -import { ref, computed } from 'vue'; +import { ref, computed, useAttrs, nextTick } from 'vue'; import { useI18n } from 'vue-i18n'; +import { useState } from 'src/composables/useState'; import FormModel from 'components/FormModel.vue'; const emit = defineEmits(['onDataSaved', 'onDataCanceled']); -defineProps({ +const props = defineProps({ title: { type: String, default: '', @@ -22,12 +23,21 @@ defineProps({ }); const { t } = useI18n(); - +const attrs = useAttrs(); +const state = useState(); const formModelRef = ref(null); const closeButton = ref(null); -const isSaveAndContinue = ref(false); -const onDataSaved = (formData, requestResponse) => { - if (closeButton.value && !isSaveAndContinue.value) closeButton.value.click(); +const isSaveAndContinue = ref(props.showSaveAndContinueBtn); +const isLoading = computed(() => formModelRef.value?.isLoading); +const reset = computed(() => formModelRef.value?.reset); + +const onDataSaved = async (formData, requestResponse) => { + if (!isSaveAndContinue.value) closeButton.value?.click(); + if (isSaveAndContinue.value) { + await nextTick(); + state.set(attrs.model, attrs.formInitialData); + } + isSaveAndContinue.value = props.showSaveAndContinueBtn; emit('onDataSaved', formData, requestResponse); }; @@ -36,9 +46,6 @@ const onClick = async (saveAndContinue) => { await formModelRef.value.save(); }; -const isLoading = computed(() => formModelRef.value?.isLoading); -const reset = computed(() => formModelRef.value?.reset); - defineExpose({ isLoading, onDataSaved, @@ -74,10 +81,7 @@ defineExpose({ data-cy="FormModelPopup_cancel" v-close-popup z-max - @click=" - isSaveAndContinue = false; - emit('onDataCanceled'); - " + @click="emit('onDataCanceled')" /> <QBtn :flat="showSaveAndContinueBtn" diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index dd4ea8e2d..13ab4a801 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -165,6 +165,7 @@ const app = inject('app'); const editingRow = ref(null); const editingField = ref(null); const isTableMode = computed(() => mode.value == TABLE_MODE); +const showRightIcon = computed(() => $props.rightSearch || $props.rightSearchIcon); const selectRegex = /select/; const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']); const tableModes = [ @@ -955,14 +956,6 @@ function cardClick(_, row) { transition-show="scale" transition-hide="scale" :full-width="createComplement?.isFullWidth ?? false" - @before-hide=" - () => { - if (createRef.isSaveAndContinue) { - showForm = true; - createForm.formInitialData = { ...create.formInitialData }; - } - } - " data-cy="vn-table-create-dialog" > <FormModelPopup diff --git a/src/components/ui/CardDescriptor.vue b/src/components/ui/CardDescriptor.vue index 0d4c15644..c6972963f 100644 --- a/src/components/ui/CardDescriptor.vue +++ b/src/components/ui/CardDescriptor.vue @@ -204,6 +204,19 @@ const toModule = computed(() => :data-cy="`${$attrs['data-cy'] ?? 'cardDescriptor'}_subtitle`" > #{{ getValueFromPath(subtitle) ?? entity.id }} + <QBtn + round + flat + dense + size="sm" + icon="content_copy" + color="primary" + @click.stop="copyIdText(entity.id)" + > + <QTooltip> + {{ t('globals.copyId') }} + </QTooltip> + </QBtn> </QItemLabel> <QBtn diff --git a/src/pages/Customer/CustomerFilter.vue b/src/pages/Customer/CustomerFilter.vue index 21de8fa9b..1c5a08304 100644 --- a/src/pages/Customer/CustomerFilter.vue +++ b/src/pages/Customer/CustomerFilter.vue @@ -143,6 +143,7 @@ const exprBuilder = (param, value) => { outlined rounded auto-load + sortBy="name ASC" /></QItemSection> </QItem> <QItem class="q-mb-sm"> diff --git a/src/pages/Customer/CustomerList.vue b/src/pages/Customer/CustomerList.vue index b8c1a743e..0bfca7910 100644 --- a/src/pages/Customer/CustomerList.vue +++ b/src/pages/Customer/CustomerList.vue @@ -78,10 +78,20 @@ const columns = computed(() => [ component: 'select', attrs: { url: 'Workers/activeWithInheritedRole', - fields: ['id', 'name'], + fields: ['id', 'name', 'firstName'], where: { role: 'salesPerson' }, optionFilter: 'firstName', }, + columnFilter: { + component: 'select', + attrs: { + url: 'Workers/activeWithInheritedRole', + fields: ['id', 'name', 'firstName'], + where: { role: 'salesPerson' }, + optionLabel: 'firstName', + optionValue: 'id', + }, + }, create: false, columnField: { component: null, diff --git a/src/pages/Customer/components/CustomerNewPayment.vue b/src/pages/Customer/components/CustomerNewPayment.vue index 8f61bac89..6ecccc544 100644 --- a/src/pages/Customer/components/CustomerNewPayment.vue +++ b/src/pages/Customer/components/CustomerNewPayment.vue @@ -77,7 +77,6 @@ onBeforeMount(() => { function setPaymentType(accounting) { if (!accounting) return; accountingType.value = accounting.accountingType; - initialData.description = []; initialData.payed = Date.vnNew(); isCash.value = accountingType.value.code == 'cash'; @@ -87,14 +86,14 @@ function setPaymentType(accounting) { initialData.payed.getDate() + accountingType.value.daysInFuture, ); maxAmount.value = accountingType.value && accountingType.value.maxAmount; - if (accountingType.value.code == 'compensation') return (initialData.description = ''); - if (accountingType.value.receiptDescription) - initialData.description.push(accountingType.value.receiptDescription); - if (initialData.description) initialData.description.push(initialData.description); - initialData.description = initialData.description.join(', '); + let descriptions = []; + if (accountingType.value.receiptDescription) + descriptions.push(accountingType.value.receiptDescription); + if (initialData.description) descriptions.push(initialData.description); + initialData.description = descriptions.join(', '); } const calculateFromAmount = (event) => { diff --git a/src/pages/Customer/components/CustomerSamplesCreate.vue b/src/pages/Customer/components/CustomerSamplesCreate.vue index 8d241441d..1294a5d25 100644 --- a/src/pages/Customer/components/CustomerSamplesCreate.vue +++ b/src/pages/Customer/components/CustomerSamplesCreate.vue @@ -18,6 +18,7 @@ import VnInput from 'src/components/common/VnInput.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue'; import CustomerSamplesPreview from 'src/pages/Customer/components/CustomerSamplesPreview.vue'; import FormPopup from 'src/components/FormPopup.vue'; +import { useArrayData } from 'src/composables/useArrayData'; const { dialogRef, onDialogOK } = useDialogPluginComponent(); @@ -39,7 +40,7 @@ const optionsSamplesVisible = ref([]); const sampleType = ref({ hasPreview: false }); const initialData = reactive({}); const entityId = computed(() => route.params.id); -const customer = computed(() => state.get('Customer')); +const customer = computed(() => useArrayData('Customer').store?.data); const filterEmailUsers = { where: { userFk: user.value.id } }; const filterClientsAddresses = { include: [ @@ -65,9 +66,9 @@ const filterSamplesVisible = { defineEmits(['confirm', ...useDialogPluginComponent.emits]); onBeforeMount(async () => { - initialData.clientFk = customer.value.id; - initialData.recipient = customer.value.email; - initialData.recipientId = customer.value.id; + initialData.clientFk = customer.value?.id; + initialData.recipient = customer.value?.email; + initialData.recipientId = customer.value?.id; }); const setEmailUser = (data) => { diff --git a/src/pages/Item/Card/ItemDiary.vue b/src/pages/Item/Card/ItemDiary.vue index 4b6775183..31b3c328e 100644 --- a/src/pages/Item/Card/ItemDiary.vue +++ b/src/pages/Item/Card/ItemDiary.vue @@ -27,7 +27,7 @@ const user = state.getUser(); const today = Date.vnNew(); today.setHours(0, 0, 0, 0); const warehousesOptions = ref([]); -const itemBalances = computed(() => arrayDataItemBalances.store.data); +const itemBalances = computed(() => arrayDataItemBalances.store.data || []); const where = computed(() => arrayDataItemBalances.store.filter.where || {}); const showWhatsBeforeInventory = ref(false); const inventoriedDate = ref(null); @@ -313,8 +313,8 @@ async function updateWarehouse(warehouseFk) { row.lineFk == row.lastPreparedLineFk ? 'black' : row.balance < 0 - ? 'negative' - : '' + ? 'negative' + : '' " dense style="font-size: 14px" diff --git a/src/pages/Item/Card/ItemTags.vue b/src/pages/Item/Card/ItemTags.vue index ed23ab5a6..ab26b9cae 100644 --- a/src/pages/Item/Card/ItemTags.vue +++ b/src/pages/Item/Card/ItemTags.vue @@ -87,7 +87,7 @@ const insertTag = (rows) => { tagFk: undefined, }" :default-remove="false" - :filter="{ + :user-filter="{ fields: ['id', 'itemFk', 'tagFk', 'value', 'priority'], where: { itemFk: route.params.id }, include: { @@ -119,6 +119,7 @@ const insertTag = (rows) => { " :required="true" :rules="validate('itemTag.tagFk')" + :data-cy="`tag${row?.tag?.name}`" /> <VnSelect v-if="row.tag?.isFree === false" @@ -145,6 +146,7 @@ const insertTag = (rows) => { :label="t('itemTags.value')" :is-clearable="false" @keyup.enter.stop="(data) => itemTagsRef.onSubmit(data)" + :data-cy="`tag${row?.tag?.name}Value`" /> <VnInput :label="t('itemBasicData.relevancy')" @@ -162,6 +164,7 @@ const insertTag = (rows) => { name="delete" size="sm" dense + :data-cy="`deleteTag${row?.tag?.name}`" > <QTooltip> {{ t('itemTags.removeTag') }} @@ -177,6 +180,7 @@ const insertTag = (rows) => { icon="add" v-shortcut="'+'" fab + data-cy="createNewTag" > <QTooltip> {{ t('itemTags.addTag') }} diff --git a/src/pages/Route/RouteExtendedList.vue b/src/pages/Route/RouteExtendedList.vue index a8d847711..f32dcd0d9 100644 --- a/src/pages/Route/RouteExtendedList.vue +++ b/src/pages/Route/RouteExtendedList.vue @@ -335,7 +335,6 @@ const openTicketsDialog = (id) => { <QBtn icon="vn:clone" color="primary" - flat class="q-mr-sm" :disable="!selectedRows?.length" @click="confirmationDialog = true" @@ -345,7 +344,6 @@ const openTicketsDialog = (id) => { <QBtn icon="cloud_download" color="primary" - flat class="q-mr-sm" :disable="!selectedRows?.length" @click="showRouteReport" @@ -355,7 +353,6 @@ const openTicketsDialog = (id) => { <QBtn icon="check" color="primary" - flat class="q-mr-sm" :disable="!selectedRows?.length" @click="markAsServed()" diff --git a/src/pages/Ticket/Card/TicketEditMana.vue b/src/pages/Ticket/Card/TicketEditMana.vue index 14eec9db9..ff40a6592 100644 --- a/src/pages/Ticket/Card/TicketEditMana.vue +++ b/src/pages/Ticket/Card/TicketEditMana.vue @@ -1,32 +1,26 @@ <script setup> -import { ref } from 'vue'; +import axios from 'axios'; import { useI18n } from 'vue-i18n'; +import { computed, ref } from 'vue'; +import { useRoute } from 'vue-router'; import { toCurrency } from 'src/filters'; import VnUsesMana from 'components/ui/VnUsesMana.vue'; const $props = defineProps({ - mana: { - type: Number, - default: null, - }, newPrice: { type: Number, default: 0, }, - usesMana: { - type: Boolean, - default: false, - }, - manaCode: { - type: String, - default: 'mana', - }, sale: { type: Object, default: null, }, }); +const route = useRoute(); +const mana = ref(null); +const usesMana = ref(false); + const emit = defineEmits(['save', 'cancel']); const { t } = useI18n(); @@ -38,32 +32,47 @@ const save = (sale = $props.sale) => { QPopupProxyRef.value.hide(); }; +const getMana = async () => { + const { data } = await axios.get(`Tickets/${route.params.id}/getSalesPersonMana`); + mana.value = data; + await getUsesMana(); +}; + +const getUsesMana = async () => { + const { data } = await axios.get('Sales/usesMana'); + usesMana.value = data; +}; + const cancel = () => { emit('cancel'); QPopupProxyRef.value.hide(); }; +const hasMana = computed(() => typeof mana.value === 'number'); defineExpose({ save }); </script> <template> - <QPopupProxy ref="QPopupProxyRef" data-cy="ticketEditManaProxy"> + <QPopupProxy + ref="QPopupProxyRef" + @before-show="getMana" + data-cy="ticketEditManaProxy" + > <div class="container"> - <QSpinner v-if="!mana" color="primary" size="md" /> - <div v-else> - <div class="header">Mana: {{ toCurrency(mana) }}</div> - <div class="q-pa-md"> - <slot :popup="QPopupProxyRef" /> - <div v-if="usesMana" class="column q-gutter-y-sm q-mt-sm"> - <VnUsesMana :mana-code="manaCode" /> - </div> - <div v-if="newPrice" class="column items-center q-mt-lg"> - <span class="text-primary">{{ t('New price') }}</span> - <span class="text-subtitle1"> - {{ toCurrency($props.newPrice) }} - </span> - </div> + <div class="header">Mana: {{ toCurrency(mana) }}</div> + <QSpinner v-if="!hasMana" color="primary" size="md" /> + <div class="q-pa-md" v-else> + <slot :popup="QPopupProxyRef" /> + <div v-if="usesMana" class="column q-gutter-y-sm q-mt-sm"> + <VnUsesMana :mana-code="manaCode" /> + </div> + <div v-if="newPrice" class="column items-center q-mt-lg"> + <span class="text-primary">{{ t('New price') }}</span> + <span class="text-subtitle1"> + {{ toCurrency($props.newPrice) }} + </span> </div> </div> + <div class="row"> <QBtn color="primary" diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue index e680fb290..e88133ff1 100644 --- a/src/pages/Ticket/Card/TicketSale.vue +++ b/src/pages/Ticket/Card/TicketSale.vue @@ -45,7 +45,6 @@ const isTicketEditable = ref(false); const sales = ref([]); const editableStatesOptions = ref([]); const selectedSales = ref([]); -const mana = ref(null); const manaCode = ref('mana'); const ticketState = computed(() => store.data?.ticketState?.state?.code); const transfer = ref({ @@ -175,17 +174,21 @@ const getSaleTotal = (sale) => { return price - discount; }; +const getRowUpdateInputEvents = (sale) => ({ + 'keyup.enter': () => { + changeQuantity(sale); + }, + blur: () => { + changeQuantity(sale); + }, +}); + const resetChanges = async () => { arrayData.fetch({ append: false }); tableRef.value.reload(); }; -const rowToUpdate = ref(null); const changeQuantity = async (sale) => { - if ( - !sale.itemFk || - sale.quantity == null || - edit.value?.oldQuantity === sale.quantity - ) + if (!sale.itemFk || sale.quantity == null || sale?.originalQuantity === sale.quantity) return; if (!sale.id) return addSale(sale); @@ -197,11 +200,8 @@ const changeQuantity = async (sale) => { const updateQuantity = async (sale) => { try { let { quantity, id } = sale; - if (!rowToUpdate.value) return; - rowToUpdate.value = null; sale.isNew = false; - const params = { quantity: quantity }; - await axios.post(`Sales/${id}/updateQuantity`, params); + await axios.post(`Sales/${id}/updateQuantity`, { quantity }); notify('globals.dataSaved', 'positive'); tableRef.value.reload(); } catch (e) { @@ -258,18 +258,6 @@ const DEFAULT_EDIT = { oldQuantity: null, }; const edit = ref({ ...DEFAULT_EDIT }); -const usesMana = ref(null); - -const getUsesMana = async () => { - const { data } = await axios.get('Sales/usesMana'); - usesMana.value = data; -}; - -const getMana = async () => { - const { data } = await axios.get(`Tickets/${route.params.id}/getSalesPersonMana`); - mana.value = data; - await getUsesMana(); -}; const selectedValidSales = computed(() => { if (!sales.value) return; @@ -277,7 +265,6 @@ const selectedValidSales = computed(() => { }); const onOpenEditPricePopover = async (sale) => { - await getMana(); edit.value = { sale: JSON.parse(JSON.stringify(sale)), price: sale.price, @@ -285,7 +272,6 @@ const onOpenEditPricePopover = async (sale) => { }; const onOpenEditDiscountPopover = async (sale) => { - await getMana(); if (isLocked.value) return; if (sale) { edit.value = { @@ -306,7 +292,6 @@ const changePrice = async (sale) => { await confirmUpdate(() => updatePrice(sale, newPrice)); } else updatePrice(sale, newPrice); } - await getMana(); }; const updatePrice = async (sale, newPrice) => { await axios.post(`Sales/${sale.id}/updatePrice`, { newPrice }); @@ -599,9 +584,7 @@ watch( :is-ticket-editable="isTicketEditable" :sales="selectedValidSales" :disable="!hasSelectedRows" - :mana="mana" :ticket-config="ticketConfig" - @get-mana="getMana()" @update-discounts="updateDiscounts" @refresh-table="resetChanges" /> @@ -772,9 +755,7 @@ watch( v-if="row.isNew || isTicketEditable" type="number" v-model.number="row.quantity" - @blur="changeQuantity(row)" - @keyup.enter.stop="changeQuantity(row)" - @update:model-value="() => (rowToUpdate = row)" + v-on="getRowUpdateInputEvents(row)" @focus="edit.oldQuantity = row.quantity" /> <span v-else>{{ row.quantity }}</span> @@ -786,7 +767,6 @@ watch( </QBtn> <TicketEditManaProxy ref="editPriceProxyRef" - :mana="mana" :sale="row" :new-price="getNewPrice" @save="changePrice" @@ -809,10 +789,8 @@ watch( <TicketEditManaProxy ref="editManaProxyRef" - :mana="mana" :sale="row" :new-price="getNewPrice" - :uses-mana="usesMana" :mana-code="manaCode" @save="changeDiscount" > diff --git a/src/pages/Ticket/Card/TicketSaleMoreActions.vue b/src/pages/Ticket/Card/TicketSaleMoreActions.vue index 4cc96e9e2..840b62507 100644 --- a/src/pages/Ticket/Card/TicketSaleMoreActions.vue +++ b/src/pages/Ticket/Card/TicketSaleMoreActions.vue @@ -34,10 +34,6 @@ const props = defineProps({ type: Array, default: () => [], }, - mana: { - type: Number, - default: null, - }, ticketConfig: { type: Array, default: () => [], @@ -50,6 +46,7 @@ const { dialog } = useQuasar(); const { notify } = useNotify(); const acl = useAcl(); const btnDropdownRef = ref(null); +const editManaProxyRef = ref(null); const { openConfirmationModal } = useVnConfirm(); const newDiscount = ref(null); @@ -131,13 +128,13 @@ const createClaim = () => { openConfirmationModal( t('Claim out of time'), t('Do you want to continue?'), - onCreateClaimAccepted + onCreateClaimAccepted, ); else openConfirmationModal( t('Do you want to create a claim?'), false, - onCreateClaimAccepted + onCreateClaimAccepted, ); }; @@ -216,8 +213,14 @@ const createRefund = async (withWarehouse) => { <QItemSection> <QItemLabel>{{ t('Update discount') }}</QItemLabel> </QItemSection> - <TicketEditManaProxy :mana="props.mana" @save="changeMultipleDiscount()"> + <TicketEditManaProxy + ref="editManaProxyRef" + :sale="row" + @save="changeMultipleDiscount" + > <VnInput + autofocus + @keyup.enter.stop="() => editManaProxyRef.save(row)" v-model.number="newDiscount" :label="t('ticketSale.discount')" type="number" diff --git a/src/pages/Ticket/Card/TicketSummary.vue b/src/pages/Ticket/Card/TicketSummary.vue index 5df08b881..5838efa88 100644 --- a/src/pages/Ticket/Card/TicketSummary.vue +++ b/src/pages/Ticket/Card/TicketSummary.vue @@ -46,6 +46,15 @@ const descriptorData = useArrayData('Ticket'); onMounted(async () => { ticketUrl.value = (await getUrl('ticket/')) + entityId.value + '/'; }); +const formattedAddress = computed(() => { + if (!ticket.value) return ''; + + const address = ticket.value.address; + const postcode = address.postalCode; + const province = address.province ? `(${address.province.name})` : ''; + + return `${address.street} - ${postcode} - ${address.city} ${province}`; +}); function isEditable() { try { @@ -238,7 +247,7 @@ onMounted(async () => { /> <VnLv :label="t('ticket.summary.consigneeStreet')" - :value="entity.address?.street" + :value="formattedAddress" /> </QCard> <QCard class="vn-one" v-if="entity.notes.length"> diff --git a/src/pages/Ticket/TicketFilter.vue b/src/pages/Ticket/TicketFilter.vue index 4b50892b0..c82c0067f 100644 --- a/src/pages/Ticket/TicketFilter.vue +++ b/src/pages/Ticket/TicketFilter.vue @@ -293,6 +293,7 @@ en: clientFk: Customer orderFk: Order from: From + shipped: Shipped to: To salesPersonFk: Salesperson stateFk: State @@ -320,6 +321,7 @@ es: clientFk: Cliente orderFk: Pedido from: Desde + shipped: F. envĂo to: Hasta salesPersonFk: Comercial stateFk: Estado diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue index 88878076d..78bebc297 100644 --- a/src/pages/Ticket/TicketList.vue +++ b/src/pages/Ticket/TicketList.vue @@ -108,13 +108,11 @@ const columns = computed(() => [ }, { align: 'left', - name: 'shippedDate', + name: 'shipped', cardVisible: true, label: t('ticketList.shipped'), columnFilter: { component: 'date', - alias: 't', - inWhere: true, }, format: ({ shippedDate }) => toDate(shippedDate), }, diff --git a/src/pages/Worker/Card/WorkerBusiness.vue b/src/pages/Worker/Card/WorkerBusiness.vue index 6025ae289..e3582a2d5 100644 --- a/src/pages/Worker/Card/WorkerBusiness.vue +++ b/src/pages/Worker/Card/WorkerBusiness.vue @@ -3,7 +3,7 @@ import { ref, computed } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; import VnTable from 'components/VnTable/VnTable.vue'; -import { toDate } from 'src/filters'; +import { dashIfEmpty, toDate } from 'src/filters'; import { useQuasar } from 'quasar'; import axios from 'axios'; @@ -15,7 +15,7 @@ const quasar = useQuasar(); async function reactivateWorker() { const hasToReactive = tableRef.value.CrudModelRef.formData.find( - (data) => !data.ended + (data) => !data.ended, ); if (hasToReactive) { quasar @@ -38,25 +38,25 @@ const columns = computed(() => [ { name: 'started', label: t('worker.business.tableVisibleColumns.started'), - align: 'left', format: ({ started }) => toDate(started), component: 'date', cardVisible: true, create: true, + width: '90px', }, { name: 'ended', label: t('worker.business.tableVisibleColumns.ended'), - align: 'left', format: ({ ended }) => toDate(ended), component: 'date', cardVisible: true, create: true, + width: '90px', }, { label: t('worker.business.tableVisibleColumns.company'), - align: 'left', + toolTip: t('worker.business.tableVisibleColumns.company'), name: 'companyCodeFk', component: 'select', attrs: { @@ -65,23 +65,23 @@ const columns = computed(() => [ optionLabel: 'code', optionValue: 'code', }, - cardVisible: true, create: true, + width: '60px', }, { - align: 'left', name: 'reasonEndFk', component: 'select', label: t('worker.business.tableVisibleColumns.reasonEnd'), + toolTip: t('worker.business.tableVisibleColumns.reasonEnd'), attrs: { url: 'BusinessReasonEnds', fields: ['id', 'reason'], optionLabel: 'reason', }, cardVisible: true, + format: ({ reason }, dashIfEmpty) => dashIfEmpty(reason), }, { - align: 'left', name: 'departmentFk', component: 'select', label: t('worker.business.tableVisibleColumns.department'), @@ -89,15 +89,19 @@ const columns = computed(() => [ url: 'Departments', fields: ['id', 'name'], optionLabel: 'name', + optionValue: 'id', }, cardVisible: true, create: true, + width: '80px', + format: ({ departmentName }, dashIfEmpty) => dashIfEmpty(departmentName), }, { align: 'left', name: 'workerBusinessProfessionalCategoryFk', component: 'select', label: t('worker.business.tableVisibleColumns.professionalCategory'), + toolTip: t('worker.business.tableVisibleColumns.professionalCategory'), attrs: { url: 'WorkerBusinessProfessionalCategories', fields: ['id', 'description', 'code'], @@ -105,6 +109,9 @@ const columns = computed(() => [ }, cardVisible: true, create: true, + width: '100px', + format: ({ professionalDescription }, dashIfEmpty) => + dashIfEmpty(professionalDescription), }, { align: 'left', @@ -118,6 +125,8 @@ const columns = computed(() => [ }, cardVisible: true, create: true, + format: ({ calendarTypeDescription }, dashIfEmpty) => + dashIfEmpty(calendarTypeDescription), }, { align: 'left', @@ -131,6 +140,8 @@ const columns = computed(() => [ }, cardVisible: true, create: true, + width: '100px', + format: ({ workCenterName }, dashIfEmpty) => dashIfEmpty(workCenterName), }, { align: 'left', @@ -144,6 +155,7 @@ const columns = computed(() => [ }, cardVisible: true, create: true, + format: ({ payrollDescription }, dashIfEmpty) => dashIfEmpty(payrollDescription), }, { align: 'left', @@ -157,6 +169,7 @@ const columns = computed(() => [ }, cardVisible: true, create: true, + format: ({ occupationName }, dashIfEmpty) => dashIfEmpty(occupationName), }, { align: 'left', @@ -165,6 +178,7 @@ const columns = computed(() => [ component: 'input', cardVisible: true, create: true, + width: '50px', }, { align: 'left', @@ -177,6 +191,8 @@ const columns = computed(() => [ }, cardVisible: true, create: true, + format: ({ workerBusinessTypeName }, dashIfEmpty) => + dashIfEmpty(workerBusinessTypeName), }, { align: 'left', @@ -185,6 +201,7 @@ const columns = computed(() => [ component: 'input', cardVisible: true, create: true, + width: '70px', }, { align: 'left', @@ -193,6 +210,7 @@ const columns = computed(() => [ component: 'input', cardVisible: true, create: true, + width: '70px', }, { name: 'notes', @@ -208,7 +226,7 @@ const columns = computed(() => [ <VnTable ref="tableRef" data-key="WorkerBusiness" - :url="`Workers/${entityId}/Business`" + :url="`Workers/${entityId}/getWorkerBusiness`" save-url="/Businesses/crud" :create="{ urlCreate: `Workers/${entityId}/Business`, @@ -218,13 +236,12 @@ const columns = computed(() => [ }" order="id DESC" :columns="columns" - default-mode="card" auto-load - :disable-option="{ table: true }" + :disable-option="{ card: true }" :right-search="false" - card-class="grid-two q-gutter-x-xl q-gutter-y-md q-pr-lg q-py-lg" :is-editable="true" :use-model="true" + :right-search-icon="false" @save-changes="(data) => reactivateWorker(data)" /> </template> diff --git a/src/pages/Worker/Card/WorkerDescriptor.vue b/src/pages/Worker/Card/WorkerDescriptor.vue index de3f634e2..0e946f1dd 100644 --- a/src/pages/Worker/Card/WorkerDescriptor.vue +++ b/src/pages/Worker/Card/WorkerDescriptor.vue @@ -111,6 +111,7 @@ const handlePhotoUpdated = (evt = false) => { <template #body="{ entity }"> <VnLv :label="t('globals.user')" :value="entity.user?.name" /> <VnLv + class="ellipsis-text" :label="t('globals.params.email')" :value="entity.user?.emailUser?.email" copy @@ -177,6 +178,12 @@ const handlePhotoUpdated = (evt = false) => { .photo { height: 256px; } +.ellipsis-text { + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} </style> <i18n> diff --git a/src/pages/Zone/ZoneList.vue b/src/pages/Zone/ZoneList.vue index a82bbb285..4df84e4bd 100644 --- a/src/pages/Zone/ZoneList.vue +++ b/src/pages/Zone/ZoneList.vue @@ -65,6 +65,7 @@ const tableFilter = { const columns = computed(() => [ { + align: 'left', name: 'id', label: t('list.id'), chip: { @@ -74,8 +75,6 @@ const columns = computed(() => [ columnFilter: { inWhere: true, }, - columnClass: 'shrink-column', - component: 'number', }, { align: 'left', @@ -107,6 +106,7 @@ const columns = computed(() => [ format: (row, dashIfEmpty) => dashIfEmpty(row?.agencyMode?.name), }, { + align: 'left', name: 'price', label: t('list.price'), cardVisible: true, @@ -114,11 +114,9 @@ const columns = computed(() => [ columnFilter: { inWhere: true, }, - columnClass: 'shrink-column', - component: 'number', }, { - align: 'center', + align: 'left', name: 'hour', label: t('list.close'), cardVisible: true, @@ -180,73 +178,67 @@ function formatRow(row) { <ZoneFilterPanel data-key="ZonesList" /> </template> </RightMenu> - <div class="table-container"> - <div class="column items-center"> - <VnTable - ref="tableRef" - data-key="ZonesList" - url="Zones" - :create="{ - urlCreate: 'Zones', - title: t('list.createZone'), - onDataSaved: ({ id }) => tableRef.redirect(`${id}/location`), - formInitialData: {}, - }" - :user-filter="tableFilter" - :columns="columns" - redirect="zone" - :right-search="false" - table-height="85vh" - order="id ASC" - > - <template #column-addressFk="{ row }"> - {{ dashIfEmpty(formatRow(row)) }} - </template> - <template #more-create-dialog="{ data }"> - <VnSelect - url="AgencyModes" - v-model="data.agencyModeFk" - option-value="id" - option-label="name" - :label="t('list.agency')" - /> - <VnInput - v-model="data.price" - :label="t('list.price')" - min="0" - type="number" - required="true" - /> - <VnInput - v-model="data.bonus" - :label="t('zone.bonus')" - min="0" - type="number" - /> - <VnInput - v-model="data.travelingDays" - :label="t('zone.travelingDays')" - type="number" - min="0" - /> - <VnInputTime v-model="data.hour" :label="t('list.close')" /> - <VnSelect - url="Warehouses" - v-model="data.warehouseFK" - option-value="id" - option-label="name" - :label="t('list.warehouse')" - :options="warehouseOptions" - /> - <QCheckbox - v-model="data.isVolumetric" - :label="t('list.isVolumetric')" - :toggle-indeterminate="false" - /> - </template> - </VnTable> - </div> - </div> + <VnTable + ref="tableRef" + data-key="ZonesList" + url="Zones" + :create="{ + urlCreate: 'Zones', + title: t('list.createZone'), + onDataSaved: ({ id }) => tableRef.redirect(`${id}/location`), + formInitialData: {}, + }" + :user-filter="tableFilter" + :columns="columns" + redirect="zone" + :right-search="false" + > + <template #column-addressFk="{ row }"> + {{ dashIfEmpty(formatRow(row)) }} + </template> + <template #more-create-dialog="{ data }"> + <VnSelect + url="AgencyModes" + v-model="data.agencyModeFk" + option-value="id" + option-label="name" + :label="t('list.agency')" + /> + <VnInput + v-model="data.price" + :label="t('list.price')" + min="0" + type="number" + required="true" + /> + <VnInput + v-model="data.bonus" + :label="t('zone.bonus')" + min="0" + type="number" + /> + <VnInput + v-model="data.travelingDays" + :label="t('zone.travelingDays')" + type="number" + min="0" + /> + <VnInputTime v-model="data.hour" :label="t('list.close')" /> + <VnSelect + url="Warehouses" + v-model="data.warehouseFK" + option-value="id" + option-label="name" + :label="t('list.warehouse')" + :options="warehouseOptions" + /> + <QCheckbox + v-model="data.isVolumetric" + :label="t('list.isVolumetric')" + :toggle-indeterminate="false" + /> + </template> + </VnTable> </template> <i18n> @@ -254,20 +246,3 @@ es: Search zone: Buscar zona You can search zones by id or name: Puedes buscar zonas por id o nombre </i18n> - -<style lang="scss" scoped> -.table-container { - display: flex; - justify-content: center; -} -.column { - display: flex; - flex-direction: column; - align-items: center; - min-width: 70%; -} - -:deep(.shrink-column) { - width: 8%; -} -</style> diff --git a/test/cypress/integration/item/itemTag.spec.js b/test/cypress/integration/item/itemTag.spec.js index d7a9ea4b3..425eaffe6 100644 --- a/test/cypress/integration/item/itemTag.spec.js +++ b/test/cypress/integration/item/itemTag.spec.js @@ -1,4 +1,3 @@ -/// <reference types="cypress" /> describe('Item tag', () => { beforeEach(() => { cy.viewport(1920, 1080); @@ -8,25 +7,27 @@ describe('Item tag', () => { cy.waitForElement('[data-cy="itemTags"]'); }); + const createNewTag = 'createNewTag'; + const saveBtn = 'crudModelDefaultSaveBtn'; + const newTag = 'tagundefined'; + it('should throw an error adding an existent tag', () => { - cy.get('.q-page-sticky > div').click(); - cy.selectOption(':nth-child(8) > .q-select', 'Tallos'); - cy.get(':nth-child(8) > [label="Value"]').type('1'); - cy.dataCy('crudModelDefaultSaveBtn').click(); + cy.dataCy(createNewTag).click(); + cy.dataCy(newTag).should('be.visible').click().type('Genero{enter}'); + cy.dataCy('tagGeneroValue').eq(1).should('be.visible'); + cy.dataCy(saveBtn).click(); cy.checkNotification("The tag or priority can't be repeated for an item"); }); it('should add a new tag', () => { - cy.get('.q-page-sticky > div').click(); - cy.selectOption(':nth-child(8) > .q-select', 'Ancho de la base'); - cy.get(':nth-child(8) > [label="Value"]').type('50'); - cy.dataCy('crudModelDefaultSaveBtn').click(); + cy.dataCy(createNewTag).click(); + cy.dataCy(newTag).should('be.visible').click().type('Forma{enter}'); + cy.dataCy('tagFormaValue').should('be.visible').type('50'); + cy.dataCy(saveBtn).click(); + cy.checkNotification('Data saved'); - cy.dataCy('itemTags') - .children(':nth-child(8)') - .find('.justify-center > .q-icon') - .click(); - cy.dataCy('VnConfirm_confirm').click(); + cy.dataCy('deleteTagForma').should('be.visible').click(); + cy.dataCy('VnConfirm_confirm').should('be.visible').click(); cy.checkNotification('Data saved'); }); });