diff --git a/src/components/common/VnInputDate.vue b/src/components/common/VnInputDate.vue index 3c1a12f243..77ab2692db 100644 --- a/src/components/common/VnInputDate.vue +++ b/src/components/common/VnInputDate.vue @@ -1,5 +1,6 @@ diff --git a/src/pages/Ticket/Card/BasicData/BasicDataTable.vue b/src/pages/Ticket/Card/BasicData/BasicDataTable.vue new file mode 100644 index 0000000000..48b8c882f6 --- /dev/null +++ b/src/pages/Ticket/Card/BasicData/BasicDataTable.vue @@ -0,0 +1,263 @@ + + + + (ticketUpdateActions = data)" + auto-load + /> + + + + + + {{ t('basicData.total') }} + + + + + {{ t('basicData.price') }}: + {{ toCurrency(totalPrice) }} + + + + + {{ t('basicData.newPrice') }}: {{ toCurrency(totalNewPrice) }} + + + + + {{ t('basicData.difference') }}: {{ toCurrency(totalDifference) }} + + + + + + + {{ t('basicData.chargeDifference') }} + + + + + + + + + + + + {{ t('basicData.withoutNegativesInfo') }} + + + + + + + + + + + {{ row.itemFk }} + + + + + + + + {{ row.item.name }} + {{ row.item.subName }} + + + + + + + + + + + diff --git a/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue b/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue new file mode 100644 index 0000000000..c8a8e85c95 --- /dev/null +++ b/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue @@ -0,0 +1,468 @@ + + + (clientsOptions = data)" + auto-load + /> + (warehousesOptions = data)" + auto-load + /> + (companiesOptions = data)" + auto-load + /> + (agenciesOptions = data)" + auto-load + /> + (zonesOptions = data)" + auto-load + /> + + + + + + + #{{ scope.opt?.id }} {{ scope.opt?.name }} + + + + + + + + + + + + + {{ + `${ + !scope.opt?.isActive + ? t('basicData.inactive') + : '' + } ` + }} + {{ scope.opt?.nickname }} + , {{ scope.opt?.street }}, {{ scope.opt?.city }}, + {{ scope.opt?.province?.name }} - + {{ scope.opt?.agencyMode?.name }} + + + + + + + {{ t('basicData.editAddress') }} + + + + + + + + + + + + + {{ scope.opt?.name }} - Max. + {{ toTimeFormat(scope.opt?.hour) }} + h. + + + + + + + + + + + + diff --git a/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue b/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue new file mode 100644 index 0000000000..af47761a2a --- /dev/null +++ b/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue @@ -0,0 +1,195 @@ + + + + + (formData = $event)" + :form-data="formData" + /> + + + (formData = $event)" + /> + + + + + + + + + diff --git a/src/pages/Ticket/Card/TicketBasicData.vue b/src/pages/Ticket/Card/TicketBasicData.vue deleted file mode 100644 index 0c9a8b0427..0000000000 --- a/src/pages/Ticket/Card/TicketBasicData.vue +++ /dev/null @@ -1,3 +0,0 @@ - - Basic Data - diff --git a/src/pages/Ticket/Card/TicketCard.vue b/src/pages/Ticket/Card/TicketCard.vue index 0a0912703d..689a717e62 100644 --- a/src/pages/Ticket/Card/TicketCard.vue +++ b/src/pages/Ticket/Card/TicketCard.vue @@ -14,6 +14,7 @@ const routeName = computed(() => route.name); const searchBarDataKeys = { TicketSummary: 'TicketSummary', TicketSale: 'TicketSale', + TicketPurchaseRequest: 'TicketPurchaseRequest', }; diff --git a/src/pages/Ticket/Card/TicketCreateRequest.vue b/src/pages/Ticket/Card/TicketCreateRequest.vue new file mode 100644 index 0000000000..6b436621f8 --- /dev/null +++ b/src/pages/Ticket/Card/TicketCreateRequest.vue @@ -0,0 +1,69 @@ + + + (attendersOptions = data)" + /> + emit('onRequestCreated')" + > + + + + + + + + + + + + + + + es: + Create request: Crear petición de compra + diff --git a/src/pages/Ticket/Card/TicketLog.vue b/src/pages/Ticket/Card/TicketLog.vue index 94b63a1173..94cff4dfcc 100644 --- a/src/pages/Ticket/Card/TicketLog.vue +++ b/src/pages/Ticket/Card/TicketLog.vue @@ -1,7 +1,10 @@ - + diff --git a/src/pages/Ticket/Card/TicketPurchaseRequest.vue b/src/pages/Ticket/Card/TicketPurchaseRequest.vue new file mode 100644 index 0000000000..281dc46a15 --- /dev/null +++ b/src/pages/Ticket/Card/TicketPurchaseRequest.vue @@ -0,0 +1,267 @@ + + + + + + + redirectToTicketSummary(row.ticketFk)" + > + + + + + + + + + {{ row.requester?.user?.nickname }} + + + + + + + + {{ row.atender?.user?.nickname }} + + + + + + + + + + + + + + + + + + {{ row.sale.itemFk }} + + + + + + + + + {{ t('globals.delete') }} + + + + + + + + + + + + + + {{ t('purchaseRequest.newRequest') }} + + + + + + es: + New: Nueva + Denied: Denegada + Accepted: Aceptada + diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue index 7cc8539cf7..32408bf672 100644 --- a/src/pages/Ticket/Card/TicketSale.vue +++ b/src/pages/Ticket/Card/TicketSale.vue @@ -353,15 +353,14 @@ const newOrderFromTicket = async () => { }; const goToLog = (saleId) => { - //TODO: Redireccionar cuando exista la vista TicketLog - // router.push({ - // name: 'TicketLog', - // params: { - // originId: route.params.id, - // changedModel: 'Sale', - // changedModelId: saleId, - // }, - // }); + router.push({ + name: 'TicketLog', + params: { + originId: route.params.id, + changedModel: 'Sale', + changedModelId: saleId, + }, + }); }; const changeTicketState = async (val) => { @@ -725,8 +724,9 @@ onUnmounted(() => (stateStore.rightDrawer = false)); - + +import { onMounted, ref, computed, reactive, onUnmounted } from 'vue'; +import { useI18n } from 'vue-i18n'; +import { useRouter } from 'vue-router'; + +import FetchData from 'components/FetchData.vue'; +import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue'; +import VnInput from 'src/components/common/VnInput.vue'; +import VnSelect from 'src/components/common/VnSelect.vue'; +import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; +import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; +import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; +import VnPaginate from 'components/ui/VnPaginate.vue'; +import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; +import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; + +import { useStateStore } from 'stores/useStateStore'; +import { dashIfEmpty } from 'src/filters'; +import { useVnConfirm } from 'composables/useVnConfirm'; +import { useArrayData } from 'composables/useArrayData'; +import useNotify from 'src/composables/useNotify.js'; +import axios from 'axios'; + +const router = useRouter(); +const stateStore = useStateStore(); +const { t } = useI18n(); +const { openConfirmationModal } = useVnConfirm(); +const { notify } = useNotify(); + +const paginateRef = ref(null); +const agencyModesOptions = ref([]); +const visibleColumns = ref([]); +const allColumnNames = ref([]); + +const arrayData = useArrayData('WeeklyTickets'); +const { store } = arrayData; + +const weekdays = [ + { id: 0, name: t('weekdays.mon') }, + { id: 1, name: t('weekdays.tue') }, + { id: 2, name: t('weekdays.wed') }, + { id: 3, name: t('weekdays.thu') }, + { id: 4, name: t('weekdays.fri') }, + { id: 5, name: t('weekdays.sat') }, + { id: 6, name: t('weekdays.sun') }, +]; + +const exprBuilder = (param, value) => { + switch (param) { + case 'clientName': + return { 'c.name': value }; + case 'nickName': + return { 'u.name': value }; + } +}; + +const params = reactive({}); + +const applyColumnFilter = async (col) => { + try { + const paramKey = col.columnFilter?.filterParamKey || col.field; + params[paramKey] = col.columnFilter.filterValue; + await paginateRef.value.addFilter(null, params); + } catch (err) { + console.error('Error applying column filter', err); + } +}; + +const getInputEvents = (col) => ({ 'keyup.enter': () => applyColumnFilter(col) }); + +const columns = computed(() => [ + { + label: t('weeklyTickets.id'), + name: 'id', + field: 'ticketFk', + align: 'left', + sortable: true, + columnFilter: null, + }, + { + label: t('weeklyTickets.client'), + name: 'client', + field: 'clientName', + align: 'left', + sortable: true, + columnFilter: { + component: VnInput, + type: 'text', + filterValue: null, + event: getInputEvents, + attrs: { + dense: true, + }, + }, + format: (val) => dashIfEmpty(val), + }, + { + label: t('weeklyTickets.shipment'), + name: 'shipment', + field: 'weekDay', + align: 'left', + sortable: true, + columnFilter: null, + }, + { + label: t('weeklyTickets.agency'), + name: 'agency', + field: 'agencyModeFk', + align: 'left', + sortable: true, + columnFilter: null, + }, + { + label: t('weeklyTickets.warehouse'), + name: 'warehouse', + field: 'warehouseName', + align: 'left', + sortable: true, + columnFilter: null, + }, + { + label: t('weeklyTickets.salesperson'), + field: 'salesperson', + name: 'salesperson', + align: 'left', + sortable: true, + columnFilter: { + component: VnInput, + type: 'text', + filterValue: null, + event: getInputEvents, + filterParamKey: 'nickName', + attrs: { + dense: true, + }, + }, + }, + { + label: '', + name: 'actions', + align: 'left', + columnFilter: null, + }, +]); + +const redirectToTicketSummary = (ticketFk) => + router.push({ name: 'TicketSummary', params: { id: ticketFk } }); + +const deleteWeekly = async (ticketFk) => { + try { + await axios.delete(`TicketWeeklies/${ticketFk}`); + notify(t('globals.dataSaved'), 'positive'); + const ticketIndex = store.data.findIndex((e) => e.ticketFk == ticketFk); + store.data.splice(ticketIndex, 1); + } catch (err) { + console.error('Error deleting weekly', err); + } +}; + +const onUpdate = async (ticketFk, field, value) => { + try { + const params = { ticketFk, [field]: value }; + await axios.patch('TicketWeeklies', params); + } catch (err) { + console.error('Error updating weekly', err); + } +}; + +onMounted(async () => { + stateStore.rightDrawer = true; + const filteredColumns = columns.value.filter((col) => col.name !== 'actions'); + allColumnNames.value = filteredColumns.map((col) => col.name); +}); + +onUnmounted(() => (stateStore.rightDrawer = false)); + + + + (agencyModesOptions = data)" + /> + + + + + + + + + + redirectToTicketSummary(row.ticketFk)" + > + + + + + + + + + + + {{ row.ticketFk }} + + + + + + + + {{ row.userName }} + + + + + + + + {{ row.clientName }} + + + + + + + + + + + + + + + + + deleteWeekly(row.ticketFk) + ) + " + class="q-ml-sm cursor-pointer" + color="primary" + name="delete" + size="sm" + > + + {{ t('globals.delete') }} + + + + + + + + + + + +es: + You are going to delete this weekly ticket: Vas a eliminar este ticket programado + This ticket will be removed from weekly tickets! Continue anyway?: Este ticket se eliminará de tickets programados! ¿Continuar de todas formas? + diff --git a/src/pages/Ticket/locale/en.yml b/src/pages/Ticket/locale/en.yml index a89671a0cd..2c648e7f23 100644 --- a/src/pages/Ticket/locale/en.yml +++ b/src/pages/Ticket/locale/en.yml @@ -22,9 +22,6 @@ ticketSale: shipped: Shipped agency: Agency address: Address -card: - search: Search tickets - searchInfo: You can search by ticket id or alias advanceTickets: origin: Origin destination: Destination @@ -80,3 +77,62 @@ futureTickets: moveTicketSuccess: Tickets moved successfully! searchInfo: Search future tickets by date futureTicket: Future tickets +basicData: + next: Next + back: Back + finalize: Finalize + client: Client + warehouse: Warehouse + address: Address + inactive: (Inactive) + noDeliveryZoneAvailable: No delivery zone available for this landing date + editAddress: Edit address + alias: Alias + company: Company + agency: Agency + zone: Zone + shipped: Shipped + landed: Landed + shippedHour: Shipped hour + priceDifference: Price difference + someFieldsAreInvalid: Some fields are invalid + item: Item + description: Description + movable: Movable + quantity: Quantity + pricePPU: Price (PPU) + newPricePPU: New (PPU) + difference: Difference + total: Total + price: Price + newPrice: New price + chargeDifference: Charge difference to + withoutNegatives: Create without negatives + withoutNegativesInfo: Clone this ticket with the changes and only sales availables + negativesConfirmTitle: Edit basic data + negativesConfirmMessage: Negatives are going to be generated, are you sure you want to advance all the lines? + chooseAnOption: Choose an option + unroutedTicket: The ticket has been unrouted +card: + search: Search tickets + searchInfo: You can search by ticket id or alias +purchaseRequest: + id: Id + description: Description + created: Created + requester: Requester + atender: Atender + quantity: Quantity + price: Price + saleFk: Item id + state: State + newRequest: New request +weeklyTickets: + id: Ticket ID + client: Client + shipment: Shipment + agency: Agency + warehouse: Warehouse + salesperson: Salesperson + search: Search weekly tickets + searchInfo: Search weekly tickets by id or client id diff --git a/src/pages/Ticket/locale/es.yml b/src/pages/Ticket/locale/es.yml index ce52e18bad..3ce4c0545e 100644 --- a/src/pages/Ticket/locale/es.yml +++ b/src/pages/Ticket/locale/es.yml @@ -1,3 +1,62 @@ +card: + search: Buscar tickets + searchInfo: Buscar tickets por identificador o alias +purchaseRequest: + Id: Id + description: Descripción + created: Fecha creación + requester: Solicitante + atender: Comprador + quantity: Cantidad + price: Precio + saleFk: Id artículo + state: Estado + newRequest: Crear petición +basicData: + next: Siguiente + back: Anterior + finalize: Finalizar + client: Cliente + warehouse: Almacén + address: Consignatario + inactive: (Inactivo) + noDeliveryZoneAvailable: No hay una zona de reparto disponible para la fecha de envío seleccionada + editAddress: Editar dirección + alias: Alias + company: Empresa + agency: Agencia + zone: Zona + shipped: F. Envío + landed: F. Entrega + shippedHour: Hora de envío + priceDifference: Diferencia de precio + someFieldsAreInvalid: Algunos campos no son válidos + item: Artículo + description: Descripción + movable: Movible + quantity: Cantidad + pricePPU: Precio (Ud.) + newPricePPU: Nuevo (Ud.) + difference: Diferencia + total: Total + price: Precio + newPrice: Nuevo precio + chargeDifference: Cargar diferencia a + withoutNegatives: Crear sin negativos + withoutNegativesInfo: Clonar este ticket con los cambios y solo ventas disponibles + negativesConfirmTitle: Editar datos básicos + negativesConfirmMessage: Se van a generar negativos, ¿seguro que quieres adelantar todas las líneas? + chooseAnOption: Elige una opción + unroutedTicket: El ticket ha sido desenrutado +weeklyTickets: + id: ID Ticket + client: Cliente + shipment: Salida + agency: Agencia + warehouse: Almacén + salesperson: Comercial + search: Buscar por tickets programados + searchInfo: Buscar tickets programados por el identificador o el identificador del cliente advanceTickets: origin: Origen destination: Destinatario @@ -79,6 +138,3 @@ ticketSale: shipped: F. Envío agency: Agencia address: Consignatario -card: - search: Buscar tickets - searchInfo: Buscar tickets por identificador o alias diff --git a/src/router/modules/ticket.js b/src/router/modules/ticket.js index 9c105be32f..81ca405ee1 100644 --- a/src/router/modules/ticket.js +++ b/src/router/modules/ticket.js @@ -11,8 +11,15 @@ export default { component: RouterView, redirect: { name: 'TicketMain' }, menus: { - main: ['TicketList', 'TicketAdvance', 'TicketFuture'], - card: ['TicketBoxing', 'TicketSms', 'TicketSale', 'TicketLog'], + main: ['TicketList', 'TicketAdvance', 'TicketWeekly', 'TicketFuture'], + card: [ + 'TicketBasicData', + 'TicketBoxing', + 'TicketSms', + 'TicketSale', + 'TicketLog', + 'TicketPurchaseRequest', + ], }, children: [ { @@ -41,13 +48,13 @@ export default { component: () => import('src/pages/Ticket/TicketCreate.vue'), }, { - name: 'TicketAdvance', - path: 'advance', + name: 'TicketWeekly', + path: 'weekly', meta: { - title: 'ticketAdvance', - icon: 'keyboard_double_arrow_left', + title: 'weeklyTickets', + icon: 'access_time', }, - component: () => import('src/pages/Ticket/TicketAdvance.vue'), + component: () => import('src/pages/Ticket/TicketWeekly.vue'), }, { name: 'TicketFuture', @@ -58,6 +65,15 @@ export default { }, component: () => import('src/pages/Ticket/TicketFuture.vue'), }, + { + name: 'TicketAdvance', + path: 'advance', + meta: { + title: 'ticketAdvance', + icon: 'keyboard_double_arrow_left', + }, + component: () => import('src/pages/Ticket/TicketAdvance.vue'), + }, ], }, { @@ -82,7 +98,8 @@ export default { title: 'basicData', icon: 'vn:settings', }, - component: () => import('src/pages/Ticket/Card/TicketBasicData.vue'), + component: () => + import('src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue'), }, { name: 'TicketSale', @@ -93,6 +110,25 @@ export default { }, component: () => import('src/pages/Ticket/Card/TicketSale.vue'), }, + { + path: 'request', + name: 'TicketPurchaseRequest', + meta: { + title: 'purchaseRequest', + icon: 'vn:buyrequest', + }, + component: () => + import('src/pages/Ticket/Card/TicketPurchaseRequest.vue'), + }, + { + path: 'log', + name: 'TicketLog', + meta: { + title: 'log', + icon: 'history', + }, + component: () => import('src/pages/Ticket/Card/TicketLog.vue'), + }, { path: 'boxing', name: 'TicketBoxing', @@ -111,15 +147,6 @@ export default { }, component: () => import('src/pages/Ticket/Card/TicketSms.vue'), }, - { - path: 'log', - name: 'TicketLog', - meta: { - title: 'log', - icon: 'history', - }, - component: () => import('src/pages/Ticket/Card/TicketLog.vue'), - }, ], }, ],