diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index f18892a31..9209eaf7b 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -127,6 +127,7 @@ const splittedColumns = ref({ columns: [] }); const columnsVisibilitySkipped = ref(); const createForm = ref(); const tableFilterRef = ref([]); +const tableRef = ref(); const tableModes = [ { @@ -308,6 +309,7 @@ defineExpose({ selected, CrudModelRef, params, + tableRef, }); function handleOnDataSaved(_) { @@ -398,6 +400,7 @@ function handleOnDataSaved(_) { +import VnSelect from './VnSelect.vue'; + +defineProps({ + selectProps: { type: Object, required: true }, + promise: { type: Function, default: () => {} }, +}); + + + + + + diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 7eb3829fe..6ba3f9b58 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -296,6 +296,9 @@ globals: from: From To: To stateFk: State + myTeam: My team + departmentFk: Department + changeState: Change state errors: statusUnauthorized: Access denied statusInternalServerError: An internal server error has ocurred @@ -538,7 +541,6 @@ ticket: package: Package taxClass: Tax class services: Services - changeState: Change state requester: Requester atender: Atender request: Request diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 9d5cd53f3..e058030c0 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -300,6 +300,9 @@ globals: from: Desde To: Hasta stateFk: Estado + myTeam: Mi equipo + departmentFk: Departamento + changeState: Cambiar estado errors: statusUnauthorized: Acceso denegado statusInternalServerError: Ha ocurrido un error interno del servidor @@ -547,7 +550,6 @@ ticket: package: Embalaje taxClass: Tipo IVA services: Servicios - changeState: Cambiar estado requester: Solicitante atender: Comprador request: Petición de compra diff --git a/src/pages/Claim/Card/ClaimSummary.vue b/src/pages/Claim/Card/ClaimSummary.vue index d77f718c6..edfa52b4b 100644 --- a/src/pages/Claim/Card/ClaimSummary.vue +++ b/src/pages/Claim/Card/ClaimSummary.vue @@ -204,7 +204,7 @@ function claimUrl(section) { top color="black" text-color="white" - :label="t('ticket.summary.changeState')" + :label="t('globals.changeState')" > -import { onMounted, ref, computed, onUnmounted, reactive, watch } from 'vue'; +import { onMounted, ref, computed, onUnmounted } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; -import VnInput from 'src/components/common/VnInput.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; -import TicketEditManaProxy from './TicketEditMana.vue'; -import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import ExpeditionNewTicket from './ExpeditionNewTicket.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; -import VnSelect from 'src/components/common/VnSelect.vue'; import { useStateStore } from 'stores/useStateStore'; -import { toCurrency, toPercentage } from 'src/filters'; import { useArrayData } from 'composables/useArrayData'; import { useVnConfirm } from 'composables/useVnConfirm'; import useNotify from 'src/composables/useNotify.js'; import { toDateTimeFormat } from 'src/filters/date'; import axios from 'axios'; +import VnTable from 'src/components/VnTable/VnTable.vue'; +import VnBtnSelect from 'src/components/common/VnBtnSelect.vue'; +import FetchData from 'src/components/FetchData.vue'; const route = useRoute(); const stateStore = useStateStore(); const { t } = useI18n(); const { notify } = useNotify(); const { openConfirmationModal } = useVnConfirm(); -const editPriceProxyRef = ref(null); const newTicketDialogRef = ref(null); const logsTableDialogRef = ref(null); - +const vnTableRef = ref(); const expeditionsLogsData = ref([]); const selectedExpeditions = ref([]); const allColumnNames = ref([]); -const visibleColumns = ref([]); const newTicketWithRoute = ref(false); - -const exprBuilder = (param, value) => { - switch (param) { - case 'expeditionFk': - return { id: value }; - case 'packageItemName': - return { packagingItemFk: value }; - } -}; +const selectedRows = ref([]); +const hasSelectedRows = computed(() => selectedRows.value.length > 0); +const expeditionStateTypes = ref([]); const expeditionsFilter = computed(() => ({ where: { ticketFk: route.params.id }, order: ['created DESC'], })); -const expeditionsArrayData = useArrayData('ticketExpeditions', { - url: 'Expeditions/filter', - filter: expeditionsFilter.value, - exprBuilder: exprBuilder, -}); -const expeditionsStore = expeditionsArrayData.store; -const ticketExpeditions = computed(() => expeditionsStore.data); - const ticketArrayData = useArrayData('ticketData'); const ticketStore = ticketArrayData.store; const ticketData = computed(() => ticketStore.data); -const refetchExpeditions = async () => { - await expeditionsArrayData.applyFilter({ - filter: expeditionsFilter.value, - }); -}; - -watch( - () => route.params.id, - async () => await refetchExpeditions(), - { immediate: true } -); - -const params = reactive({}); - -const applyColumnFilter = async (col) => { - try { - const paramKey = col.columnFilter?.filterParamKey || col.field; - params[paramKey] = col.columnFilter.filterValue; - await expeditionsArrayData.addFilter({ filter: expeditionsFilter.value, params }); - } catch (err) { - console.error('Error applying column filter', err); - } -}; - -const getInputEvents = (col) => { - return col.columnFilter.type === 'select' - ? { 'update:modelValue': () => applyColumnFilter(col) } - : { - 'keyup.enter': () => applyColumnFilter(col), - }; -}; - const columns = computed(() => [ { + align: 'left', label: t('expedition.id'), name: 'id', - field: 'id', - align: 'left', - sortable: true, + chip: { + condition: () => true, + }, + isId: true, columnFilter: { - component: VnInput, - type: 'text', - filterParamKey: 'expeditionFk', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, + inWhere: true, }, }, { label: t('expedition.item'), - name: 'item', + name: 'packagingItemFk', align: 'left', + cardVisible: true, columnFilter: { - component: VnInput, - type: 'text', - filterParamKey: 'packageItemName', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, + inWhere: true, }, }, { label: t('expedition.name'), - name: 'name', - field: 'packageItemName', + name: 'packageItemName', align: 'left', + isTitle: true, columnFilter: { - component: VnSelect, - type: 'select', - filterValue: null, - event: getInputEvents, - attrs: { - url: 'Items', - fields: ['id', 'name'], - 'sort-by': 'name ASC', - 'option-value': 'id', - 'option-label': 'name', - dense: true, - }, + inWhere: true, }, }, { label: t('expedition.packageType'), - name: 'packageType', - field: 'freightItemName', + name: 'freightItemName', align: 'left', columnFilter: { - component: VnInput, - type: 'text', - // filterParamKey: 'expeditionFk', - filterValue: null, - event: getInputEvents, - attrs: { - dense: true, - }, + inWhere: true, }, }, { label: t('expedition.counter'), name: 'counter', - field: 'counter', align: 'left', - columnFilter: null, + columnFilter: { + inWhere: true, + }, }, { label: t('expedition.externalId'), name: 'externalId', - field: 'externalId', align: 'left', - columnFilter: null, + cardVisible: true, + columnFilter: { + inWhere: true, + }, }, { label: t('expedition.created'), name: 'created', - field: 'created', align: 'left', - columnFilter: null, - format: (value) => toDateTimeFormat(value), + cardVisible: true, + format: (row) => toDateTimeFormat(row.created), }, { label: t('expedition.state'), name: 'state', - field: 'state', align: 'left', - columnFilter: null, + cardVisible: true, + columnFilter: { inWhere: true }, }, { - label: '', - name: 'history', - align: 'left', - columnFilter: null, + align: 'right', + name: 'tableActions', + actions: [ + { + title: t('expedition.historyAction'), + icon: 'history', + isPrimary: true, + action: (row) => showLog(row), + }, + ], }, ]); @@ -204,23 +132,29 @@ const logTableColumns = computed(() => [ label: t('expedition.state'), name: 'state', field: 'state', - align: 'left', + align: 'center', sortable: true, }, { label: t('expedition.name'), name: 'name', - align: 'name', + field: 'name', + align: 'center', columnFilter: null, }, { label: t('expedition.created'), name: 'created', field: 'created', - align: 'left', - columnFilter: null, + align: 'center', format: (value) => toDateTimeFormat(value), }, + { + label: t('expedition.isScanned'), + name: 'isScanned', + field: 'isScanned', + align: 'center', + }, ]); const showNewTicketDialog = (withRoute = false) => { @@ -230,12 +164,10 @@ const showNewTicketDialog = (withRoute = false) => { const deleteExpedition = async () => { try { - const expeditionIds = selectedExpeditions.value.map( - (expedition) => expedition.id - ); + const expeditionIds = selectedRows.value.map((expedition) => expedition.id); const params = { expeditionIds }; await axios.post('Expeditions/deleteExpeditions', params); - await refetchExpeditions(); + vnTableRef.value.reload(); selectedExpeditions.value = []; notify(t('expedition.expeditionRemoved'), 'positive'); } catch (error) { @@ -255,10 +187,20 @@ const getExpeditionState = async (expedition) => { order: ['created DESC'], }; - const { data } = await axios.get(`ExpeditionStates/filter`, { + const { data: expeditionStates } = await axios.get(`ExpeditionStates/filter`, { params: { filter: JSON.stringify(filter) }, }); - expeditionsLogsData.value = data; + const { data: scannedStates } = await axios.get(`ExpeditionStates`, { + params: { filter: JSON.stringify(filter), fields: ['id', 'isScanned'] }, + }); + + expeditionsLogsData.value = expeditionStates.map((state) => { + const scannedState = scannedStates.find((s) => s.id === state.id); + return { + ...state, + isScanned: scannedState ? scannedState.isScanned : false, + }; + }); } catch (error) { console.error(error); } @@ -274,22 +216,39 @@ onUnmounted(() => (stateStore.rightDrawer = false)); + (expeditionStateTypes = data)" + auto-load + /> - - - + {{ t('Select lines to see the options') }} @@ -322,7 +281,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); (stateStore.rightDrawer = false)); deleteExpedition ) " - /> + > + {{ t('expedition.removeExpedition') }} + - - - - - - - - - - - - - {{ row.packagingItemFk }} + + + {{ row.packagingItemFk }} - + - - - - {{ row.available }} - - - - - - - - - {{ toCurrency(row.price) }} - - - - - - {{ toCurrency(row.price) }} - - - - - - - {{ toPercentage(row.discount / 100) }} - - - - - - {{ toPercentage(row.discount / 100) }} - - - - - - - {{ t('expedition.historyAction') }} - - - - - + @@ -454,12 +343,23 @@ onUnmounted(() => (stateStore.rightDrawer = false)); data-key="TicketExpeditionLog" :rows="expeditionsLogsData" :columns="logTableColumns" - class="q-pa-sm" + class="q-pa-md full-width" > - - {{ row.name }} - + + + {{ row.name }} + + + + + + + + {{ + row.isScanned === 1 ? t('expedition.yes') : t('expedition.no') + }} + diff --git a/src/pages/Ticket/Card/TicketSummary.vue b/src/pages/Ticket/Card/TicketSummary.vue index 1f2a7ca79..358f74af2 100644 --- a/src/pages/Ticket/Card/TicketSummary.vue +++ b/src/pages/Ticket/Card/TicketSummary.vue @@ -105,7 +105,7 @@ async function changeState(value) { ref="stateBtnDropdownRef" color="black" text-color="white" - :label="t('ticket.summary.changeState')" + :label="t('globals.changeState')" :disable="!isEditable()" > { userParams.dateFuture = tomorrow; userParams.dateToAdvance = today; userParams.warehouseFk = user.value.warehouseFk; + userParams.ipt = 'H'; + userParams.futureIpt = 'H'; + userParams.isFullMovable = true; const filter = { limit: 0 }; await arrayData.addFilter({ filter, userParams }); }); diff --git a/src/pages/Ticket/TicketAdvanceFilter.vue b/src/pages/Ticket/TicketAdvanceFilter.vue index 209a1a307..182f715a3 100644 --- a/src/pages/Ticket/TicketAdvanceFilter.vue +++ b/src/pages/Ticket/TicketAdvanceFilter.vue @@ -10,7 +10,7 @@ import VnInputDate from 'src/components/common/VnInputDate.vue'; import axios from 'axios'; import { onMounted } from 'vue'; -const { t } = useI18n(); +const { t, te } = useI18n(); const props = defineProps({ dataKey: { type: String, @@ -42,6 +42,11 @@ const getItemPackingTypes = async () => { } }; +const getLocale = (val) => { + const param = `params.${val}`; + return te(param) ? t(param) : t(`globals.${param}`); +}; + onMounted(async () => await getItemPackingTypes()); @@ -59,7 +64,7 @@ onMounted(async () => await getItemPackingTypes()); > - {{ t(`params.${tag.label}`) }}: + {{ getLocale(tag.label) }}: {{ formatFn(tag.value) }} @@ -126,6 +131,19 @@ onMounted(async () => await getItemPackingTypes()); /> + + + + + + +describe('Ticket expedtion', () => { + const tableContent = '.q-table .q-virtual-scroll__content'; + const stateTd = 'td:nth-child(9)'; + + beforeEach(() => { + cy.login('developer'); + cy.viewport(1920, 1080); + }); + + it('should change the state', () => { + cy.visit('#/ticket/1/expedition'); + cy.intercept('GET', /\/api\/Expeditions\/filter/).as('expeditions'); + cy.intercept('POST', /\/api\/Expeditions\/crud/).as('crud'); + + cy.wait('@expeditions'); + + cy.selectRows([1, 2]); + cy.get('#subToolbar [aria-controls]:nth-child(1)').click(); + cy.get('.q-menu .q-item').contains('Perdida').click(); + cy.wait('@crud'); + + cy.get(`${tableContent} tr:nth-child(-n+2) ${stateTd}`).each(($el) => { + cy.wrap($el).contains('Perdida'); + }); + }); +}); diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 43788f59f..f895d7bb3 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -152,6 +152,14 @@ Cypress.Commands.add('notificationHas', (selector, text) => { cy.get(selector).should('have.text', text); }); +Cypress.Commands.add('selectRows', (rows) => { + rows.forEach((row) => { + cy.get('.q-table .q-virtual-scroll__content tr .q-checkbox__inner') + .eq(row - 1) + .click(); + }); +}); + Cypress.Commands.add('fillRow', (rowSelector, data) => { // Usar el selector proporcionado para obtener la fila deseada cy.waitForElement('tbody');