diff --git a/package.json b/package.json index ead0193c9..eaaa0b812 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "salix-front", - "version": "24.40.0", + "version": "24.42.0", "description": "Salix frontend", "productName": "Salix", "author": "Verdnatura", diff --git a/src/components/CreateNewCityForm.vue b/src/components/CreateNewCityForm.vue index 9a7d8666c..85d13beb1 100644 --- a/src/components/CreateNewCityForm.vue +++ b/src/components/CreateNewCityForm.vue @@ -1,35 +1,42 @@ - (provincesOptions = data)" - auto-load - url="Provinces" - /> { - + diff --git a/src/components/CreateNewPostcodeForm.vue b/src/components/CreateNewPostcodeForm.vue index 4c44d29e2..99cba5360 100644 --- a/src/components/CreateNewPostcodeForm.vue +++ b/src/components/CreateNewPostcodeForm.vue @@ -1,5 +1,5 @@ (provincesOptions = data)" + @on-fetch="handleProvinces" auto-load url="Provinces/location" /> (countriesOptions = data)" + ref="townsFetchDataRef" + @on-fetch="handleTowns" auto-load - url="Countries" + url="Towns/location" /> + setTown(value, data)" + :tooltip="t('Create city')" v-model="data.townFk" + :options="townsOptions" option-label="name" option-value="id" :rules="validate('postcode.city')" :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" :emit-value="false" - clearable + :clearable="true" > @@ -122,6 +178,9 @@ async function setProvince(id, data) { onCityCreated(requestResponse, data) @@ -132,8 +191,13 @@ async function setProvince(id, data) { setProvince(value, data)" v-model="data.provinceFk" + :clearable="true" + :provinces="provincesOptions" + @on-province-created="onProvinceCreated" /> es: New postcode: Nuevo código postal + Create city: Crear población Please, ensure you put the correct data!: ¡Por favor, asegúrese de poner los datos correctos! City: Población Province: Provincia diff --git a/src/components/CreateNewProvinceForm.vue b/src/components/CreateNewProvinceForm.vue index e32684a98..4c1d96930 100644 --- a/src/components/CreateNewProvinceForm.vue +++ b/src/components/CreateNewProvinceForm.vue @@ -16,7 +16,16 @@ const provinceFormData = reactive({ name: null, autonomyFk: null, }); - +const $props = defineProps({ + countryFk: { + type: Number, + default: null, + }, + provinces: { + type: Array, + default: () => [], + }, +}); const autonomiesOptions = ref([]); const onDataSaved = (dataSaved, requestResponse) => { @@ -31,6 +40,11 @@ const onDataSaved = (dataSaved, requestResponse) => { (autonomiesOptions = data)" auto-load + :filter="{ + where: { + countryFk: $props.countryFk, + }, + }" url="Autonomies/location" /> { model="thermograph" :title="t('New thermograph')" :form-initial-data="thermographFormData" - @on-data-saved="onDataSaved($event)" + @on-data-saved="(_, response) => onDataSaved(response)" > diff --git a/src/components/CrudModel.vue b/src/components/CrudModel.vue index 0386e037b..a4cb55a2c 100644 --- a/src/components/CrudModel.vue +++ b/src/components/CrudModel.vue @@ -234,6 +234,8 @@ async function remove(data) { newData = newData.filter((form) => !ids.some((id) => id == form[pk])); fetch(newData); }); + } else { + reset(); } emit('update:selected', []); } diff --git a/src/components/LeftMenuItem.vue b/src/components/LeftMenuItem.vue index ab74c1de5..a3112b17f 100644 --- a/src/components/LeftMenuItem.vue +++ b/src/components/LeftMenuItem.vue @@ -44,7 +44,6 @@ const itemComputed = computed(() => { - diff --git a/src/components/common/VnInput.vue b/src/components/common/VnInput.vue index 26160929c..d93ad7465 100644 --- a/src/components/common/VnInput.vue +++ b/src/components/common/VnInput.vue @@ -103,6 +103,7 @@ const mixinRules = [ @click=" () => { value = null; + vnInputRef.focus(); emit('remove'); } " @@ -129,4 +130,24 @@ const mixinRules = [ .q-field__append { padding-inline: 0; } + +.q-field__append.q-field__marginal.row.no-wrap.items-center.row { + height: 20px; +} +.q-field--outlined .q-field__append.q-field__marginal.row.no-wrap.items-center.row { + height: auto; +} +.q-field__control { + height: unset; +} + +.q-field--labeled { + .q-field__native, + .q-field__prefix, + .q-field__suffix, + .q-field__input { + padding-bottom: 0; + min-height: 15px; + } +} diff --git a/src/components/common/VnInputDate.vue b/src/components/common/VnInputDate.vue index fd8993d6f..3d5afaf80 100644 --- a/src/components/common/VnInputDate.vue +++ b/src/components/common/VnInputDate.vue @@ -20,6 +20,7 @@ const { validations } = useValidator(); const { t } = useI18n(); const requiredFieldRule = (val) => validations().required($attrs.required, val); +const vnInputDateRef = ref(null); const dateFormat = 'DD/MM/YYYY'; const isPopupOpen = ref(); @@ -91,6 +92,7 @@ const styleAttrs = computed(() => { { !$attrs.disable " @click=" + vnInputDateRef.focus(); model = null; isPopupOpen = false; " diff --git a/src/components/common/VnInputTime.vue b/src/components/common/VnInputTime.vue index 42ec79479..a5e7d3002 100644 --- a/src/components/common/VnInputTime.vue +++ b/src/components/common/VnInputTime.vue @@ -16,6 +16,7 @@ const props = defineProps({ default: false, }, }); +const vnInputTimeRef = ref(null); const initialDate = ref(model.value ?? Date.vnNew()); const { t } = useI18n(); const requiredFieldRule = (val) => validations().required($attrs.required, val); @@ -69,6 +70,7 @@ function dateToTime(newDate) { + obj.city + ? `${obj.city}${obj.province?.name ? `(${obj.province.name})` : ''}` + : null, + (obj) => obj.country?.name, +]; + const formatLocation = (obj, properties) => { const parts = properties.map((prop) => { if (typeof prop === 'string') { @@ -29,23 +39,10 @@ const formatLocation = (obj, properties) => { return filteredParts.join(', '); }; -const locationProperties = [ - 'postcode', - (obj) => - obj.city - ? `${obj.city}${obj.province?.name ? `(${obj.province.name})` : ''}` - : null, - (obj) => obj.country?.name, -]; - const modelValue = ref( props.location ? formatLocation(props.location, locationProperties) : null ); -const handleModelValue = (data) => { - emit('update:model-value', data); -}; - function showLabel(data) { const dataProperties = [ 'code', @@ -54,6 +51,10 @@ function showLabel(data) { ]; return formatLocation(data, dataProperties); } + +const handleModelValue = (data) => { + emit('update:model-value', data); +}; en: search_by_postalcode: Search by postalcode, town, province or country + Create new location: Create new location es: Location: Ubicación + Create new location: Crear nueva ubicación search_by_postalcode: Buscar por código postal, ciudad o país diff --git a/src/components/common/VnSelect.vue b/src/components/common/VnSelect.vue index aa629767d..84ab4b4b6 100644 --- a/src/components/common/VnSelect.vue +++ b/src/components/common/VnSelect.vue @@ -283,4 +283,15 @@ const getVal = (val) => ($props.useLike ? { like: `%${val}%` } : val); .q-field--outlined { max-width: 100%; } +.q-field__inner { + .q-field__control { + min-height: auto !important; + + display: flex; + align-items: flex-end; + .q-field__native.row { + min-height: auto !important; + } + } +} diff --git a/src/components/common/VnSelectDialog.vue b/src/components/common/VnSelectDialog.vue index 17f893255..350aa9272 100644 --- a/src/components/common/VnSelectDialog.vue +++ b/src/components/common/VnSelectDialog.vue @@ -1,5 +1,5 @@ @@ -219,7 +211,7 @@ function aliasField(field) { icon="search" @click="search()" > - + diff --git a/src/components/ui/VnRow.vue b/src/components/ui/VnRow.vue index 16bcfab7d..0df1fb7d4 100644 --- a/src/components/ui/VnRow.vue +++ b/src/components/ui/VnRow.vue @@ -9,6 +9,7 @@ defineProps({ wrap: { type: Boolean, default: false } }); + + es: + Edit travel: Editar envío + Travel: Envíos + Booked trucks: Camiones reservados + Buyer: Comprador + Reserve: Reservado + Bought: Comprado + Date: Fecha + View more details: Ver más detalles + Reserve some space: Reservar espacio + This buyer has already made a reservation for this date: Este comprador ya ha hecho una reserva para esta fecha + diff --git a/src/pages/Entry/EntryStockBoughtDetail.vue b/src/pages/Entry/EntryStockBoughtDetail.vue new file mode 100644 index 000000000..0fd775ee6 --- /dev/null +++ b/src/pages/Entry/EntryStockBoughtDetail.vue @@ -0,0 +1,126 @@ + + + + + + + + {{ row?.entryFk }} + + + + + + {{ row?.itemName }} + + + + + + + + + + es: + Buyer: Comprador + Reserve: Reservado + Bought: Comprado + More: Más + Date: Fecha + Entry: Entrada + Item: Artículo + Name: Nombre + Volume: Volumen + Packaging: Embalage + diff --git a/src/pages/Entry/EntryStockBoughtFilter.vue b/src/pages/Entry/EntryStockBoughtFilter.vue new file mode 100644 index 000000000..7694cfe6c --- /dev/null +++ b/src/pages/Entry/EntryStockBoughtFilter.vue @@ -0,0 +1,63 @@ + + + + + + + {{ t(`params.${tag.label}`) }}: + {{ formatFn(tag.value) }} + + + + + + + + + + + + + en: + params: + dated: Date + workerFk: Worker + es: + Date: Fecha + params: + dated: Date + workerFk: Trabajador + diff --git a/src/pages/Entry/MyEntries.vue b/src/pages/Entry/MyEntries.vue index 1c56427f4..2c37c2c42 100644 --- a/src/pages/Entry/MyEntries.vue +++ b/src/pages/Entry/MyEntries.vue @@ -9,22 +9,27 @@ import VnTable from 'components/VnTable/VnTable.vue'; const { t } = useI18n(); const quasar = useQuasar(); +const params = { + daysOnward: 7, + daysAgo: 3, +}; + const columns = computed(() => [ { align: 'left', name: 'id', - label: t('customer.extendedList.tableVisibleColumns.id'), + label: t('myEntries.id'), columnFilter: false, isTitle: true, }, { visible: false, align: 'right', - label: t('shipped'), + label: t('myEntries.shipped'), name: 'shipped', columnFilter: { name: 'fromShipped', - label: t('fromShipped'), + label: t('myEntries.fromShipped'), component: 'date', }, format: ({ shipped }) => toDate(shipped), @@ -32,11 +37,11 @@ const columns = computed(() => [ { visible: false, align: 'left', - label: t('shipped'), + label: t('myEntries.shipped'), name: 'shipped', columnFilter: { name: 'toShipped', - label: t('toShipped'), + label: t('myEntries.toShipped'), component: 'date', }, format: ({ shipped }) => toDate(shipped), @@ -44,14 +49,14 @@ const columns = computed(() => [ }, { align: 'right', - label: t('shipped'), + label: t('myEntries.shipped'), name: 'shipped', columnFilter: false, format: ({ shipped }) => toDate(shipped), }, { align: 'right', - label: t('landed'), + label: t('myEntries.landed'), name: 'landed', columnFilter: false, format: ({ landed }) => toDate(landed), @@ -59,26 +64,36 @@ const columns = computed(() => [ { align: 'right', - label: t('globals.wareHouseIn'), + label: t('myEntries.wareHouseIn'), name: 'warehouseInFk', - format: (row) => row.warehouseInName, + format: (row) => { + row.warehouseInName; + }, cardVisible: true, columnFilter: { + name: 'warehouseInFk', + label: t('myEntries.warehouseInFk'), component: 'select', attrs: { url: 'warehouses', fields: ['id', 'name'], optionLabel: 'name', optionValue: 'id', + alias: 't', }, - alias: 't', inWhere: true, }, }, { align: 'left', - label: t('globals.daysOnward'), - name: 'days', + label: t('myEntries.daysOnward'), + name: 'daysOnward', + visible: false, + }, + { + align: 'left', + label: t('myEntries.daysAgo'), + name: 'daysAgo', visible: false, }, { @@ -88,6 +103,7 @@ const columns = computed(() => [ { title: t('printLabels'), icon: 'print', + isPrimary: true, action: (row) => printBuys(row.id), }, ], @@ -114,9 +130,11 @@ const printBuys = (rowId) => { data-key="myEntriesList" url="Entries/filter" :columns="columns" + :user-params="params" default-mode="card" order="shipped DESC" auto-load + chip-locale="myEntries" /> diff --git a/src/pages/Entry/locale/en.yml b/src/pages/Entry/locale/en.yml index a9faa814b..b4d7c33bc 100644 --- a/src/pages/Entry/locale/en.yml +++ b/src/pages/Entry/locale/en.yml @@ -6,9 +6,15 @@ entryFilter: filter: search: General search reference: Reference -landed: Landed -shipped: Shipped -fromShipped: Shipped(from) -toShipped: Shipped(to) -printLabels: Print stickers -viewLabel: View sticker +myEntries: + id: ID + landed: Landed + shipped: Shipped + fromShipped: Shipped(from) + toShipped: Shipped(to) + printLabels: Print stickers + viewLabel: View sticker + wareHouseIn: Warehouse in + warehouseInFk: Warehouse in + daysOnward: Days onward + daysAgo: Days ago diff --git a/src/pages/Entry/locale/es.yml b/src/pages/Entry/locale/es.yml index eb1e3f88a..2dfd601b1 100644 --- a/src/pages/Entry/locale/es.yml +++ b/src/pages/Entry/locale/es.yml @@ -9,10 +9,15 @@ entryFilter: filter: search: Búsqueda general reference: Referencia - -landed: F. llegada -shipped: F. salida -fromShipped: F. salida(desde) -toShipped: F. salida(hasta) -printLabels: Imprimir etiquetas -viewLabel: Ver etiqueta +myEntries: + id: ID + landed: F. llegada + shipped: F. salida + fromShipped: F. salida(desde) + toShipped: F. salida(hasta) + printLabels: Imprimir etiquetas + viewLabel: Ver etiqueta + wareHouseIn: Alm. entrada + warehouseInFk: Alm. entrada + daysOnward: Días adelante + daysAgo: Días atras diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue index 9ef0ed4b8..8e49fa16a 100644 --- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue +++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue @@ -1,5 +1,6 @@ - + diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue index 5157d957b..3dc565251 100644 --- a/src/pages/InvoiceOut/InvoiceOutList.vue +++ b/src/pages/InvoiceOut/InvoiceOutList.vue @@ -27,13 +27,16 @@ const { openReport } = usePrintService(); const columns = computed(() => [ { - align: 'left', + align: 'center', name: 'id', label: t('invoiceOutList.tableVisibleColumns.id'), chip: { condition: () => true, }, isId: true, + columnFilter: { + name: 'search', + }, }, { align: 'left', diff --git a/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue b/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue index 774b42478..1ea19033c 100644 --- a/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue +++ b/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue @@ -9,6 +9,7 @@ import { useArrayData } from 'src/composables/useArrayData'; import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue'; import TicketDescriptorProxy from '../Ticket/Card/TicketDescriptorProxy.vue'; import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue'; +import VnInputDate from 'components/common/VnInputDate.vue'; const { t } = useI18n(); const tableRef = ref(); @@ -64,7 +65,8 @@ const columns = computed(() => [ cardVisible: true, attrs: { url: 'Clients', - fields: ['id', 'name'], + optionLabel: 'socialName', + optionValue: 'socialName', }, columnField: { component: null, @@ -192,10 +194,33 @@ const downloadCSV = async () => { + + + + es: Download as CSV: Descargar como CSV + params: + from: Desde + to: Hasta +en: + params: + from: From + to: To diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue index fddf154a2..7d4d6b896 100644 --- a/src/pages/Item/ItemFixedPrice.vue +++ b/src/pages/Item/ItemFixedPrice.vue @@ -514,7 +514,7 @@ function handleOnDataSave({ CrudModelRef }) { - + (orderSummary.vat = data)" auto-load /> - + [ { align: 'left', @@ -169,6 +170,13 @@ const getDateColor = (date) => { if (comparation == 0) return 'bg-warning'; if (comparation < 0) return 'bg-success'; }; + +onMounted(() => { + if (!route.query.createForm) return; + const clientId = route.query.createForm; + const id = JSON.parse(clientId); + fetchClientAddress(id.clientFk, id); +}); @@ -184,13 +192,14 @@ const getDateColor = (date) => { :order="['landed DESC', 'clientFk ASC', 'id DESC']" :create="{ urlCreate: 'Orders/new', - title: 'Create Order', + title: t('module.cerateOrder'), onDataSaved: (url) => { tableRef.redirect(url); }, formInitialData: { active: true, addressId: null, + clientFk: null, }, }" :user-params="{ showEmpty: false }" @@ -221,7 +230,7 @@ const getDateColor = (date) => { fetchClientAddress(id, data)" /> diff --git a/src/pages/Order/locale/en.yml b/src/pages/Order/locale/en.yml index b630a18ed..4349bc76f 100644 --- a/src/pages/Order/locale/en.yml +++ b/src/pages/Order/locale/en.yml @@ -10,6 +10,7 @@ module: total: Total salesPerson: Sales Person address: Address + cerateOrder: Create order lines: item: Item warehouse: Warehouse diff --git a/src/pages/Order/locale/es.yml b/src/pages/Order/locale/es.yml index 055d22719..cef06cb6d 100644 --- a/src/pages/Order/locale/es.yml +++ b/src/pages/Order/locale/es.yml @@ -10,6 +10,7 @@ module: total: Total salesPerson: Comercial address: Dirección + cerateOrder: Crear cesta lines: item: Artículo warehouse: Almacén diff --git a/src/pages/Route/Agency/AgencyList.vue b/src/pages/Route/Agency/AgencyList.vue index 42aede8a0..9d456c1da 100644 --- a/src/pages/Route/Agency/AgencyList.vue +++ b/src/pages/Route/Agency/AgencyList.vue @@ -27,12 +27,15 @@ const columns = computed(() => [ condition: () => true, }, isId: true, + columnFilter: false, }, { align: 'left', label: t('globals.name'), name: 'name', isTitle: true, + columnFilter: false, + columnClass: 'expand', }, { align: 'left', @@ -70,18 +73,33 @@ const columns = computed(() => [ data-key="AgencyList" :expr-builder="exprBuilder" /> - + + + + + + es: isOwn: Tiene propietario diff --git a/src/pages/Route/Card/RouteListTicketsDialog.vue b/src/pages/Route/Card/RouteListTicketsDialog.vue index ba07f859f..b9b2ee36b 100644 --- a/src/pages/Route/Card/RouteListTicketsDialog.vue +++ b/src/pages/Route/Card/RouteListTicketsDialog.vue @@ -167,8 +167,8 @@ const setTicketsRoute = async () => { {{ props.value }} - + diff --git a/src/pages/Route/RouteExtendedList.vue b/src/pages/Route/RouteExtendedList.vue new file mode 100644 index 000000000..51da4ec12 --- /dev/null +++ b/src/pages/Route/RouteExtendedList.vue @@ -0,0 +1,360 @@ + + + + + + + + {{ t('route.Select the starting date') }} + + + + + + + + + {{ t('globals.clone') }} + + + + + + + + + + + + + + {{ t('route.Clone Selected Routes') }} + + + {{ t('route.Download selected routes as PDF') }} + + + {{ t('route.Mark as served') }} + + + + diff --git a/src/pages/Route/RouteList.vue b/src/pages/Route/RouteList.vue index 1e20df99c..d0feb9a65 100644 --- a/src/pages/Route/RouteList.vue +++ b/src/pages/Route/RouteList.vue @@ -1,34 +1,19 @@ - - - - - {{ t('Select the starting date') }} - - - - - - - - - {{ t('globals.clone') }} - - - - - - - - {{ t('Clone Selected Routes') }} - - - {{ t('Download selected routes as PDF') }} - - - {{ t('Mark as served') }} - + + + {{ row?.workerUserName }} + + - - - -en: - newRoute: New Route - hourStarted: Started hour - hourFinished: Finished hour -es: - From: Desde - To: Hasta - Worker: Trabajador - Agency: Agencia - Vehicle: Vehículo - Volume: Volumen - Date: Fecha - Description: Descripción - Hour started: Hora inicio - Hour finished: Hora fin - KmStart: Km inicio - KmEnd: Km fin - Served: Servida - newRoute: Nueva Ruta - Clone Selected Routes: Clonar rutas seleccionadas - Select the starting date: Seleccione la fecha de inicio - Stating date: Fecha de inicio - Cancel: Cancelar - Mark as served: Marcar como servidas - Download selected routes as PDF: Descargar rutas seleccionadas como PDF - Add ticket: Añadir tickets - Preview: Vista previa - Summary: Resumen - Route is closed: La ruta está cerrada - Route is not served: La ruta no está servida - hourStarted: Hora de inicio - hourFinished: Hora de fin - diff --git a/src/pages/Route/RouteRoadmap.vue b/src/pages/Route/RouteRoadmap.vue index d921dab1f..3687442f5 100644 --- a/src/pages/Route/RouteRoadmap.vue +++ b/src/pages/Route/RouteRoadmap.vue @@ -87,7 +87,7 @@ const columns = computed(() => [ actions: [ { title: t('Ver cmr'), - icon: 'visibility', + icon: 'preview', isPrimary: true, action: (row) => viewSummary(row?.id, RoadmapSummary), }, diff --git a/src/pages/Route/RouteTickets.vue b/src/pages/Route/RouteTickets.vue index 5960636b0..3bdad4fec 100644 --- a/src/pages/Route/RouteTickets.vue +++ b/src/pages/Route/RouteTickets.vue @@ -342,10 +342,7 @@ const openSmsDialog = async () => { - + {{ value }} {{ t('Open buscaman') }} @@ -353,7 +350,7 @@ const openSmsDialog = async () => { - + {{ value }} @@ -361,7 +358,7 @@ const openSmsDialog = async () => { - + {{ value }} diff --git a/src/pages/Route/locale/en.yml b/src/pages/Route/locale/en.yml new file mode 100644 index 000000000..617d704d2 --- /dev/null +++ b/src/pages/Route/locale/en.yml @@ -0,0 +1,25 @@ +route: + Worker: Worker + Agency: Agency + Vehicle: Vehicle + Description: Description + hourStarted: H.Start + hourFinished: H.End + createRoute: Create route + From: From + To: To + Date: Date + KmStart: Km start + KmEnd: Km end + Served: Served + Clone Selected Routes: Clone selected routes + Select the starting date: Select the starting date + Stating date: Starting date + Cancel: Cancel + Mark as served: Mark as served + Download selected routes as PDF: Download selected routes as PDF + Add ticket: Add ticket + Preview: Preview + Summary: Summary + Route is closed: Route is closed + Route is not served: Route is not served diff --git a/src/pages/Route/locale/es.yml b/src/pages/Route/locale/es.yml new file mode 100644 index 000000000..ed96ad915 --- /dev/null +++ b/src/pages/Route/locale/es.yml @@ -0,0 +1,25 @@ +route: + Worker: Trabajador + Agency: Agencia + Vehicle: Vehículo + Description: Descripción + hourStarted: H.Inicio + hourFinished: H.Fin + createRoute: Crear ruta + From: Desde + To: Hasta + Date: Fecha + KmStart: Km inicio + KmEnd: Km fin + Served: Servida + Clone Selected Routes: Clonar rutas seleccionadas + Select the starting date: Seleccione la fecha de inicio + Stating date: Fecha de inicio + Cancel: Cancelar + Mark as served: Marcar como servidas + Download selected routes as PDF: Descargar rutas seleccionadas como PDF + Add ticket: Añadir tickets + Preview: Vista previa + Summary: Resumen + Route is closed: La ruta está cerrada + Route is not served: La ruta no está servida diff --git a/src/pages/Supplier/Card/SupplierFiscalData.vue b/src/pages/Supplier/Card/SupplierFiscalData.vue index 396cf15a2..169c4d796 100644 --- a/src/pages/Supplier/Card/SupplierFiscalData.vue +++ b/src/pages/Supplier/Card/SupplierFiscalData.vue @@ -52,7 +52,22 @@ function handleLocation(data, location) { :url-update="`Suppliers/${route.params.id}/updateFiscalData`" model="supplier" :filter="{ - fields: ['id', 'name', 'city', 'postCode', 'countryFk', 'provinceFk'], + fields: [ + 'id', + 'nif', + 'city', + 'name', + 'account', + 'postCode', + 'countryFk', + 'provinceFk', + 'sageTaxTypeFk', + 'sageWithholdingFk', + 'sageTransactionTypeFk', + 'supplierActivityFk', + 'healthRegister', + 'street', + ], include: [ { relation: 'province', diff --git a/src/pages/Ticket/Card/BasicData/BasicDataTable.vue b/src/pages/Ticket/Card/BasicData/TicketBasicData.vue similarity index 60% rename from src/pages/Ticket/Card/BasicData/BasicDataTable.vue rename to src/pages/Ticket/Card/BasicData/TicketBasicData.vue index 7f2f100ad..ab96a6e75 100644 --- a/src/pages/Ticket/Card/BasicData/BasicDataTable.vue +++ b/src/pages/Ticket/Card/BasicData/TicketBasicData.vue @@ -4,7 +4,6 @@ import { useI18n } from 'vue-i18n'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import FetchedTags from 'components/ui/FetchedTags.vue'; -import RightMenu from 'src/components/common/RightMenu.vue'; import FetchData from 'components/FetchData.vue'; import { useStateStore } from 'stores/useStateStore'; @@ -115,7 +114,7 @@ const totalNewPrice = computed(() => { const totalDifference = computed(() => { return rows.value.reduce((acc, item) => acc + item.component?.difference || 0, 0); }); -const showMovablecolumn = computed(() => (haveDifferences.value > 0 ? ['movable'] : [])); +const showMovableColumn = computed(() => (haveDifferences.value > 0 ? ['movable'] : [])); const haveDifferences = computed(() => _ticketData.value.sale?.haveDifferences); const ticketHaveNegatives = () => { let _haveNegatives = false; @@ -145,85 +144,83 @@ onUnmounted(() => (stateStore.rightDrawer = false)); @on-fetch="(data) => (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.total') }} - - - - - {{ t('basicData.price') }}: - {{ toCurrency(totalPrice) }} - - - - - {{ t('basicData.newPrice') }}: {{ toCurrency(totalNewPrice) }} - - - - - {{ t('basicData.difference') }}: {{ toCurrency(totalDifference) }} - - - - - - - {{ t('basicData.chargeDifference') }} - - - - - - - - - - - - {{ t('basicData.withoutNegativesInfo') }} - - - - - - + + + + + + + + + {{ t('basicData.withoutNegativesInfo') }} + + + + + (stateStore.rightDrawer = false)); flat > - - + + {{ 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 index 28c6fcf15..fdc75abda 100644 --- a/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue +++ b/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue @@ -12,6 +12,7 @@ import VnInputTime from 'components/common/VnInputTime.vue'; import axios from 'axios'; import useNotify from 'src/composables/useNotify.js'; +import { useValidator } from 'src/composables/useValidator'; import { toTimeFormat } from 'filters/date.js'; const $props = defineProps({ @@ -23,7 +24,7 @@ const $props = defineProps({ }); const emit = defineEmits(['updateForm']); - +const { validate } = useValidator(); const { notify } = useNotify(); const router = useRouter(); const { t } = useI18n(); @@ -51,18 +52,18 @@ const agencyByWarehouseFilter = computed(() => ({ }, })); -const zonesFilter = computed(() => ({ - fields: ['id', 'name'], - order: 'name ASC', - where: formData.value?.agencyModeFk - ? { - shipped: formData.value?.shipped, - addressFk: formData.value?.addressFk, - agencyModeFk: formData.value?.agencyModeFk, - warehouseFk: formData.value?.warehouseFk, - } - : {}, -})); +function zoneWhere() { + if (formData?.value?.agencyModeFk) { + return formData.value?.agencyModeFk + ? { + shipped: formData.value?.shipped, + addressFk: formData.value?.addressFk, + agencyModeFk: formData.value?.agencyModeFk, + warehouseFk: formData.value?.warehouseFk, + } + : {}; + } +} const getLanded = async (params) => { try { @@ -293,13 +294,6 @@ onMounted(() => onFormModelInit()); @on-fetch="(data) => (agenciesOptions = data)" auto-load /> - (zonesOptions = data)" - auto-load - /> onFormModelInit()); hide-selected map-options :required="true" + :rules="validate('basicData.client')" > @@ -333,6 +328,7 @@ onMounted(() => onFormModelInit()); hide-selected map-options :required="true" + :rules="validate('basicData.warehouse')" /> @@ -345,6 +341,7 @@ onMounted(() => onFormModelInit()); hide-selected map-options :required="true" + :rules="validate('basicData.address')" > @@ -392,6 +389,7 @@ onMounted(() => onFormModelInit()); :label="t('basicData.alias')" v-model="formData.nickname" :required="true" + :rules="validate('basicData.alias')" /> @@ -404,6 +402,7 @@ onMounted(() => onFormModelInit()); hide-selected map-options :required="true" + :rules="validate('basicData.company')" /> onFormModelInit()); hide-selected map-options @focus="agencyFetchRef.fetch()" + :rules="validate('basicData.agency')" /> @@ -444,16 +448,19 @@ onMounted(() => onFormModelInit()); :label="t('basicData.shipped')" v-model="formData.shipped" :required="true" + :rules="validate('basicData.shipped')" /> diff --git a/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue b/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue index af47761a2..92640f898 100644 --- a/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue +++ b/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue @@ -3,7 +3,7 @@ import { ref, onBeforeMount } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRoute, useRouter } from 'vue-router'; -import BasicDataTable from './BasicDataTable.vue'; +import TicketBasicData from './TicketBasicData.vue'; import TicketBasicDataForm from './TicketBasicDataForm.vue'; import { useVnConfirm } from 'composables/useVnConfirm'; @@ -158,7 +158,10 @@ onBeforeMount(async () => await getTicketData()); color="primary" animated keep-alive - style="max-width: 800px; margin: auto" + style="margin: auto" + :style="{ + 'max-width': step > 1 ? 'none' : '800px', + }" > await getTicketData()); /> - (formData = $event)" diff --git a/src/pages/Ticket/Card/ExpeditionNewTicket.vue b/src/pages/Ticket/Card/ExpeditionNewTicket.vue index 55ca700bc..9183ae405 100644 --- a/src/pages/Ticket/Card/ExpeditionNewTicket.vue +++ b/src/pages/Ticket/Card/ExpeditionNewTicket.vue @@ -31,6 +31,7 @@ const router = useRouter(); const { notify } = useNotify(); const newTicketFormData = reactive({}); +const date = new Date(); const createTicket = async () => { try { @@ -64,7 +65,11 @@ const createTicket = async () => { > - + [ name: 'item', align: 'left', }, + { + align: 'left', + label: t('lines.image'), + name: 'image', + columnField: { + component: VnImg, + attrs: (id) => { + return { + id, + width: '50px', + }; + }, + }, + columnFilter: false, + }, { label: t('ticketComponents.description'), name: 'description', align: 'left', + columnClass: 'expand', }, { label: t('ticketComponents.quantity'), name: 'quantity', field: 'quantity', align: 'left', - format: (val) => dashIfEmpty(val), + format: (row) => dashIfEmpty(row.quantity), }, { label: t('ticketComponents.serie'), name: 'serie', align: 'left', + format: (row) => dashIfEmpty(row.serie), }, { label: t('ticketComponents.components'), @@ -174,181 +192,166 @@ onUnmounted(() => (stateStore.rightDrawer = false)); @on-fetch="(data) => (components = data)" auto-load /> - - - + + + + {{ t('ticketComponents.total') }} + + + + {{ t('ticketComponents.baseToCommission') }}: + + {{ toCurrency(getBase) }} + + + {{ t('ticketComponents.totalWithoutVat') }}: + + {{ toCurrency(getTotal) }} + + + + + + {{ t('ticketComponents.components') }} + + + - - - {{ t('ticketComponents.total') }} - - - - {{ t('ticketComponents.baseToCommission') }}: - - {{ toCurrency(getBase) }} - - - {{ t('ticketComponents.totalWithoutVat') }}: - - {{ toCurrency(getTotal) }} - - - - - - {{ t('ticketComponents.components') }} - - - - - {{ component.name }}: - - {{ - toCurrency(component.value, 'EUR', 3) - }} - - - - - - {{ t('ticketComponents.zoneBreakdown') }} - - - - - {{ t('ticketComponents.price') }}: - - {{ toCurrency(ticketData?.zonePrice, 'EUR', 2) }} - - - - {{ t('ticketComponents.bonus') }}: - - {{ toCurrency(ticketData?.zoneBonus, 'EUR', 2) }} - - - - {{ t('ticketComponents.zone') }}: - - - {{ dashIfEmpty(ticketData?.zone?.name) }} - - - - - - {{ t('ticketComponents.volume') }}: - - {{ ticketVolume }} - - - - {{ t('ticketComponents.packages') }}: - - {{ dashIfEmpty(ticketData?.packages) }} - - - - - - {{ t('ticketComponents.theoricalCost') }} - - - - - {{ t('ticketComponents.totalPrice') }}: - - {{ toCurrency(theoricalCost, 'EUR', 2) }} - - - - - + {{ component.name }}: + + {{ + toCurrency(component.value, 'EUR', 3) + }} + + + + + + {{ t('ticketComponents.zoneBreakdown') }} + + + + + {{ t('ticketComponents.price') }}: + + {{ toCurrency(ticketData?.zonePrice, 'EUR', 2) }} + + + + {{ t('ticketComponents.bonus') }}: + + {{ toCurrency(ticketData?.zoneBonus, 'EUR', 2) }} + + + + {{ t('ticketComponents.zone') }}: + + + {{ dashIfEmpty(ticketData?.zone?.name) }} + + + + + + {{ t('ticketComponents.volume') }}: + + {{ ticketVolume }} + + + + {{ t('ticketComponents.packages') }}: + + {{ dashIfEmpty(ticketData?.packages) }} + + + + + + {{ t('ticketComponents.theoricalCost') }} + + + + + {{ t('ticketComponents.totalPrice') }}: + + {{ toCurrency(theoricalCost, 'EUR', 2) }} + + + + - - - - {{ row.itemFk }} - - - + + + {{ row.itemFk }} + + - - - - {{ row.item.name }} - {{ row.item.subName }} - - - + + + + - - - - - {{ saleComponent.component?.componentType?.name }} - - - + + + {{ row.item.name }} + {{ row.item.subName }} + + - - - - - {{ saleComponent.component?.name }} - - - + + + + {{ saleComponent.component?.componentType?.name }} + + - - - - + + + + {{ saleComponent.component?.name }} + + + + + + + {{ toCurrency(saleComponent.value, 'EUR', 3) }} + + + + + + + {{ toCurrency(saleComponent.value * row.quantity, 'EUR', 3) }} + + + - - - - - {{ toCurrency(saleComponent.value * row.quantity, 'EUR', 3) }} - - - - - + + + diff --git a/src/pages/Ticket/Card/TicketDescriptor.vue b/src/pages/Ticket/Card/TicketDescriptor.vue index f68c897db..70fd9ab1e 100644 --- a/src/pages/Ticket/Card/TicketDescriptor.vue +++ b/src/pages/Ticket/Card/TicketDescriptor.vue @@ -2,13 +2,13 @@ import { ref, computed } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; -import { toDate } from 'src/filters'; import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue'; import CardDescriptor from 'components/ui/CardDescriptor.vue'; import TicketDescriptorMenu from './TicketDescriptorMenu.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import useCardDescription from 'src/composables/useCardDescription'; import VnUserLink from 'src/components/ui/VnUserLink.vue'; +import { toDateTimeFormat } from 'src/filters/date'; const $props = defineProps({ id: { @@ -30,13 +30,24 @@ const filter = { { relation: 'address', scope: { - fields: ['id', 'name', 'mobile', 'phone'], + fields: ['id', 'name', 'mobile', 'phone', 'incotermsFk'], }, }, { relation: 'client', scope: { - fields: ['id', 'name', 'salesPersonFk', 'phone', 'mobile', 'email'], + fields: [ + 'id', + 'name', + 'salesPersonFk', + 'phone', + 'mobile', + 'email', + 'isActive', + 'isFreezed', + 'isTaxDataChecked', + 'hasElectronicInvoice', + ], include: [ { relation: 'user', @@ -87,6 +98,10 @@ const filter = { }; const data = ref(useCardDescription()); + +function ticketFilter(ticket) { + return JSON.stringify({ clientFk: ticket.clientFk }); +} @@ -128,7 +143,10 @@ const data = ref(useCardDescription()); /> - + - + + + {{ t('Client inactive') }} + + + {{ t('Client Frozen') }} + + + {{ t('Client has debt') }} + + + {{ t('Client not checked') }} + {{ t('ticket.card.customerCard') }} + + {{ t('ticket.card.ticketList') }} + + + {{ t('ticket.card.newOrder') }} + @@ -168,4 +239,8 @@ const data = ref(useCardDescription()); es: This ticket is deleted: Este ticket está eliminado Go to module index: Ir al índice del modulo + Client inactive: Cliente inactivo + Client not checked: Cliente no verificado + Client has debt: Cliente con deuda + Client Frozen: Cliente congelado diff --git a/src/pages/Ticket/Card/TicketDescriptorMenu.vue b/src/pages/Ticket/Card/TicketDescriptorMenu.vue index 4cf4e633f..73104fe27 100644 --- a/src/pages/Ticket/Card/TicketDescriptorMenu.vue +++ b/src/pages/Ticket/Card/TicketDescriptorMenu.vue @@ -1,6 +1,6 @@ + + + + + + {{ t('Transfer client') }} + + + + + + + + + + {{ `#${scope.opt.id} - ` }} + {{ scope.opt.name }} + + + + + + + + + + + + + {{ t('addTurn') }} + + + + + + + + + + + + + + + + - {{ t('Open Delivery Note...') }} + {{ t('Show Delivery Note...') }} - {{ t('With prices') }} + {{ t('as PDF') }} - {{ t('Without prices') }} + {{ t('as PDF without prices') }} - {{ t('As CSV') }} + {{ t('as CSV') }} @@ -220,21 +485,21 @@ function openConfirmDialog(callback) { v-ripple clickable > - {{ t('With prices') }} + {{ t('Send PDF') }} - {{ t('Without prices') }} + {{ t('Send PDF to tablet') }} - {{ t('As CSV') }} + {{ t('Send CSV') }} @@ -243,8 +508,26 @@ function openConfirmDialog(callback) { - {{ t('Open Proforma Invoice') }} + {{ t('Show Proforma') }} + + + + + {{ t('Change shipped hour') }} + + + + + + + + @@ -259,24 +542,72 @@ function openConfirmDialog(callback) { {{ t('Pending payment') }} - {{ t('Minimum amount') }} + {{ t('Minimum import') }} - {{ t('Order changes') }} + {{ t('Notify changes') }} + + + + + {{ t('Make invoice') }} + + + + + + {{ + hasPdf ? t('Regenerate PDF invoice') : t('Generate PDF invoice') + }} + {{ t('To clone ticket') }} + + + + + {{ t('Recalculate components') }} + + + + + + {{ t('Refund all...') }} + + + + + + + + {{ t('with warehouse') }} + + + + + {{ t('without warehouse') }} + + + + + @@ -307,29 +638,61 @@ function openConfirmDialog(callback) { + + en: + addTurn: Add turn invoiceIds: "Invoices have been generated with the following ids: {invoiceIds}" es: - Open Delivery Note...: Abrir albarán... + Show Delivery Note...: Ver albarán... Send Delivery Note...: Enviar albarán... - With prices: Con precios - Without prices: Sin precios - As CSV: Como CSV - Open Proforma Invoice: Abrir factura proforma + as PDF: como PDF + as PDF without prices: como PDF sin precios + as CSV: Como CSV + Send PDF: Enviar PDF + Send PDF to tablet: Enviar PDF a tablet + Send CSV: Enviar CSV + Show Proforma: Ver proforma Delete ticket: Eliminar ticket - Send SMS...: Enviar SMS + Send SMS...: Enviar SMS... Pending payment: Pago pendiente - Minimum amount: Importe mínimo - Order changes: Cambios del pedido + Minimum import: Importe mínimo + Notify changes: Notificar cambios Ticket deleted: Ticket eliminado You can undo this action within the first hour: Puedes deshacer esta acción dentro de la primera hora To clone ticket: Clonar ticket Ticket cloned: Ticked clonado It was not able to clone the ticket: No se pudo clonar el ticket + Generate PDF invoice: Generar PDF factura + Regenerate PDF invoice: Regenerar PDF factura + The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado + Transfer client: Transferir cliente + Client: Cliente + addTurn: Añadir a turno + What is the day of receipt of the ticket?: ¿Cuál es el día de preparación del pedido? + Current ticket deleted and added to shift: Ticket actual eliminado y añadido al turno + Refund all...: Abonar todo... + with warehouse: con almacén + without warehouse: sin almacén + Make invoice: Crear factura + Change shipped hour: Cambiar hora de envío + Shipped hour: Hora de envío + Recalculate components: Recalcular componentes + Are you sure you want to recalculate components?: ¿Seguro que quieres recalcular los componentes? + Data saved: Datos guardados + Are you sure you want to invoice this ticket?: ¿Seguro que quieres facturar este ticket? + You are going to invoice this ticket: Vas a facturar este ticket + Ticket invoiced: Ticket facturado Set weight: Establecer peso Weight set: Peso establecido This ticket may be invoiced, do you want to continue?: Es posible que se facture este ticket, desea continuar? invoiceIds: "Se han generado las facturas con los siguientes ids: {invoiceIds}" + This ticket will be removed from current route! Continue anyway?: ¡Se eliminará el ticket de la ruta actual! ¿Continuar de todas formas? + You are going to delete this ticket: Vas a eliminar este ticket diff --git a/src/pages/Ticket/Card/TicketExpedition.vue b/src/pages/Ticket/Card/TicketExpedition.vue index c4ab63b39..4becb3db3 100644 --- a/src/pages/Ticket/Card/TicketExpedition.vue +++ b/src/pages/Ticket/Card/TicketExpedition.vue @@ -1,39 +1,35 @@ @@ -73,13 +85,14 @@ watch( :label="t('ticketNotes.description')" v-model="row.description" class="col" + @keyup.enter="handleSave" /> {{ t('ticketNotes.removeNote') }} diff --git a/src/pages/Ticket/Card/TicketPackage.vue b/src/pages/Ticket/Card/TicketPackage.vue index c071d4f7f..c0418bcf6 100644 --- a/src/pages/Ticket/Card/TicketPackage.vue +++ b/src/pages/Ticket/Card/TicketPackage.vue @@ -10,6 +10,7 @@ import FetchData from 'components/FetchData.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue'; import { useArrayData } from 'src/composables/useArrayData'; +import VnRow from 'src/components/ui/VnRow.vue'; const route = useRoute(); const { t } = useI18n(); diff --git a/src/pages/Ticket/Card/TicketPurchaseRequest.vue b/src/pages/Ticket/Card/TicketPurchaseRequest.vue index 8d84e2b46..d0af9efd0 100644 --- a/src/pages/Ticket/Card/TicketPurchaseRequest.vue +++ b/src/pages/Ticket/Card/TicketPurchaseRequest.vue @@ -1,27 +1,35 @@ - - - - redirectToTicketSummary(row.ticketFk)" - > - - - - - - - - - {{ row.requester?.user?.nickname }} - - - - - - - - {{ row.atender?.user?.nickname }} - - - - - - - - - - - - - - - - - - {{ row.sale.itemFk }} - - - - - - - - - {{ t('globals.delete') }} - - - - - - - - - - - - (attendersOptions = data)" + /> + + + + + + + {{ row.requester?.user?.nickname }} + + + + + + {{ row.atender?.user?.nickname }} + + + + + + + + + + {{ row.price }} + + + + + + + {{ dashIfEmpty(row.sale?.itemFk) }} + + + + {{ t(getRequestState(row.isOk)) }} + + + + - - {{ t('purchaseRequest.newRequest') }} - - - + + + + + + es: New: Nueva diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue index f86863f05..6d3e6269d 100644 --- a/src/pages/Ticket/Card/TicketSale.vue +++ b/src/pages/Ticket/Card/TicketSale.vue @@ -16,11 +16,12 @@ import TicketSaleMoreActions from './TicketSaleMoreActions.vue'; import TicketTransfer from './TicketTransfer.vue'; import { useStateStore } from 'stores/useStateStore'; -import { toCurrency, toPercentage, dashIfEmpty } from 'src/filters'; +import { toCurrency, toPercentage } from 'src/filters'; import { useArrayData } from 'composables/useArrayData'; import { useVnConfirm } from 'composables/useVnConfirm'; import useNotify from 'src/composables/useNotify.js'; import axios from 'axios'; +import VnTable from 'src/components/VnTable/VnTable.vue'; const route = useRoute(); const router = useRouter(); @@ -33,7 +34,8 @@ const stateBtnDropdownRef = ref(null); const arrayData = useArrayData('ticketData'); const { store } = arrayData; - +const selectedRows = ref([]); +const hasSelectedRows = computed(() => selectedRows.value.length > 0); const ticketConfig = ref(null); const isLocked = ref(false); const isTicketEditable = ref(false); @@ -47,94 +49,98 @@ const transfer = ref({ lastActiveTickets: [], sales: [], }); +const tableRef = ref([]); watch( () => route.params.id, - async () => await getSales() + () => tableRef.value.reload() ); const columns = computed(() => [ { - label: '', + align: 'left', name: 'statusIcons', - align: 'left', }, { - label: '', - name: 'picture', - align: 'left', + align: 'center', + label: t('lines.image'), + name: 'image', + columnField: { + component: VnImg, + attrs: (id) => { + return { + id, + width: '50px', + }; + }, + }, + columnFilter: false, }, { + align: 'left', label: t('ticketSale.visible'), name: 'visible', - field: 'visible', - align: 'left', - sortable: true, + format: (row, dashIfEmpty) => dashIfEmpty(row.visible), }, { + align: 'left', label: t('ticketSale.available'), name: 'available', - field: 'available', - align: 'left', - sortable: true, + format: (row, dashIfEmpty) => dashIfEmpty(row.available), }, { + align: 'left', label: t('ticketSale.id'), name: 'itemFk', - field: 'itemFk', - align: 'left', - sortable: true, }, { + align: 'left', label: t('ticketSale.quantity'), name: 'quantity', - field: 'quantity', - align: 'left', - sortable: true, + format: (row) => toCurrency(row.quantity), }, { + align: 'left', label: t('ticketSale.item'), name: 'item', - field: 'item', - align: 'left', - sortable: true, + format: (row) => row?.item?.name, + columnClass: 'expand', }, { + align: 'left', label: t('ticketSale.price'), name: 'price', - field: 'price', - align: 'left', - sortable: true, - format: (val) => toCurrency(val), + format: (row) => toCurrency(row.price), }, { + align: 'left', label: t('ticketSale.discount'), name: 'discount', - field: 'discount', - align: 'left', - sortable: true, + format: (row) => toPercentage(row.discount), }, { + align: 'left', label: t('ticketSale.amount'), name: 'amount', - field: 'amount', - align: 'left', - sortable: true, - format: (val) => toCurrency(val), + format: (row) => parseInt(row.amount * row.quantity), }, { + align: 'left', label: t('ticketSale.packaging'), name: 'itemPackingTypeFk', - field: 'item', - align: 'left', - sortable: true, - format: (val) => dashIfEmpty(val?.itemPackingTypeFk), + format: (row, dashIfEmpty) => dashIfEmpty(row?.item?.itemPackingTypeFk), }, { - label: '', - name: 'history', - align: 'left', - columnFilter: null, + align: 'right', + name: 'tableActions', + actions: [ + { + title: t('ticketSale.history'), + icon: 'history', + isPrimary: true, + action: (row) => goToLog(row.id), + }, + ], }, ]); @@ -155,15 +161,6 @@ const onSalesFetched = (salesData) => { for (let sale of salesData) sale.amount = getSaleTotal(sale); }; -const getSales = async () => { - try { - const { data } = await axios.get(`Tickets/${route.params.id}/getSales`); - onSalesFetched(data); - } catch (err) { - console.error('Error fetching sales', err); - } -}; - const getSaleTotal = (sale) => { if (sale.quantity == null || sale.price == null) return null; @@ -175,7 +172,7 @@ const getSaleTotal = (sale) => { const resetChanges = async () => { arrayData.fetch({ append: false }); - getSales(); + tableRef.value.reload(); }; const updateQuantity = async (sale) => { @@ -210,6 +207,7 @@ const addSale = async (sale) => { sale.item = newSale.item; notify('globals.dataSaved', 'positive'); + window.location.reload(); } catch (err) { console.error('Error adding sale', err); } @@ -259,7 +257,7 @@ const getMana = async () => { const selectedValidSales = computed(() => { if (!sales.value) return; - return selectedSales.value.filter((sale) => sale.id != undefined); + return [...selectedRows.value]; }); const onOpenEditPricePopover = async (sale) => { @@ -374,7 +372,7 @@ const changeTicketState = async (val) => { }; const removeSelectedSales = () => { - selectedSales.value.forEach((sale) => { + selectedRows.value.forEach((sale) => { const index = sales.value.indexOf(sale); sales.value.splice(index, 1); }); @@ -382,19 +380,29 @@ const removeSelectedSales = () => { const removeSales = async () => { try { - const params = { sales: selectedValidSales.value, ticketId: store.data.id }; + const params = { + sales: selectedRows.value.filter((sale) => sale.id), + ticketId: store.data.id, + }; + selectedRows.value + .filter((sale) => !sale.id) + .forEach((sale) => + tableRef.value.CrudModelRef.formData.splice(sale.$index, 1) + ); + + if (params.sales.length == 0) return; await axios.post('Sales/deleteSales', params); removeSelectedSales(); notify('globals.dataSaved', 'positive'); + window.location.reload(); } catch (err) { console.error('Error deleting sales', err); } }; -const insertRow = () => sales.value.push({ ...DEFAULT_EDIT }); - const setTransferParams = async () => { try { + selectedSales.value = selectedValidSales.value; const checkedSales = JSON.parse(JSON.stringify(selectedSales.value)); transfer.value = { lastActiveTickets: [], @@ -417,10 +425,64 @@ const setTransferParams = async () => { onMounted(async () => { stateStore.rightDrawer = true; getConfig(); - getSales(); }); onUnmounted(() => (stateStore.rightDrawer = false)); + +const items = ref([]); +const newRow = ref({}); + +const updateItem = (row) => { + const selectedItem = items.value.find((item) => item.id === row.itemFk); + if (selectedItem) { + row.item = selectedItem; + row.itemFk = selectedItem.id; + row.price = selectedItem.price; + row.discount = 0; + row.quantity = 0; + row.amount = row.price * row.quantity; + } + endNewRow(selectedItem); +}; + +function handleOnDataSave({ CrudModelRef }) { + const { copy } = addRow(CrudModelRef.formData); + CrudModelRef.insert(copy); +} + +const addRow = (original = null) => { + let copy = null; + if (!original) { + copy = { isNew: true }; + } else { + copy = { + itemFk: original.itemFk, + item: original.item, + quantity: original.quantity, + price: original.price, + discount: original.discount, + amount: original.amount, + isNew: true, + }; + } + newRow.value = copy; + return { original, copy }; +}; + +const endNewRow = (row) => { + if (row.itemFk && row.quantity) { + row.isNew = false; + } +}; + +watch( + () => newRow.value.itemFk, + (newItemFk) => { + if (newItemFk) { + updateItem(newRow.value); + } + } +); @@ -471,7 +533,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); :ticket="store.data" :is-ticket-editable="isTicketEditable" :sales="selectedValidSales" - :disable="!selectedSales.length" + :disable="!hasSelectedRows" :mana="mana" :ticket-config="ticketConfig" @get-mana="getMana()" @@ -480,7 +542,7 @@ onUnmounted(() => (stateStore.rightDrawer = false)); (stateStore.rightDrawer = false)); {{ t('Transfer lines') }} @@ -507,246 +569,215 @@ onUnmounted(() => (stateStore.rightDrawer = false)); - + - + {{ t('ticketSale.subtotal') }}: {{ toCurrency(store.data?.totalWithoutVat) }} - + {{ t('ticketSale.tax') }}: {{ toCurrency(store.data?.totalWithVat - store.data?.totalWithoutVat) }} - + {{ t('ticketSale.total') }}: {{ toCurrency(store.data?.totalWithVat) }} - - - - - - - {{ t('ticketSale.claim') }}: - {{ row.claim?.claimFk }} - - - - + + + - {{ t('ticketSale.visible') }}: {{ row.visible || 0 }} + {{ t('ticketSale.claim') }}: + {{ row.claim?.claimFk }} - - - {{ t('ticketSale.reserved') }} - - - - - {{ t('ticketSale.noVisible') }} - - - - - {{ t('ticketSale.hasComponentLack') }} - - - - - - - - - - - - - - - {{ row.visible }} - - - - - - - {{ row.available }} - - - - - - - - {{ row.itemFk }} - - - - - - - - #{{ scope.opt?.id }} - {{ scope.opt?.name }} - - - - - - - - - - {{ row.quantity }} - - - - - - {{ row.concept }} - {{ row.item?.subName }} - - - - - - - - - - - - {{ toCurrency(row.price) }} - - - - - - {{ toCurrency(row.price) }} - - - - - - - {{ toPercentage(row.discount / 100) }} - - - - - - {{ toPercentage(row.discount / 100) }} - - - - - - - {{ t('ticketSale.history') }} - - - - - - + + + {{ t('ticketSale.visible') }}: {{ row.visible || 0 }} + + + + + {{ t('ticketSale.reserved') }} + + + - {{ t('Add item') }} + {{ t('ticketSale.noVisible') }} - + + + + {{ t('ticketSale.hasComponentLack') }} + + - + + + + + + + + {{ row.visible }} + + + + + {{ row.available }} + + + + + + + + + {{ scope.opt?.id }} - {{ scope.opt?.name }} + + + + + + {{ row?.itemFk }} + + + + + + {{ row?.item?.name }} + + {{ row?.item?.subName.toUpperCase() }} + + + + + + + + + + + {{ row.quantity }} + + + + + {{ toCurrency(row.price) }} + + + + + + {{ toCurrency(row.price) }} + + + + + {{ toPercentage(row.discount / 100) }} + + + + + + {{ toPercentage(row.discount / 100) }} + + + {{ toCurrency(row.quantity * row.price) }} + + - + {{ t('Add item to basket') }} @@ -754,6 +785,18 @@ onUnmounted(() => (stateStore.rightDrawer = false)); + + es: New item: Nuevo artículo diff --git a/src/pages/Ticket/Card/TicketSaleMoreActions.vue b/src/pages/Ticket/Card/TicketSaleMoreActions.vue index 94db67be2..2ec519d2d 100644 --- a/src/pages/Ticket/Card/TicketSaleMoreActions.vue +++ b/src/pages/Ticket/Card/TicketSaleMoreActions.vue @@ -44,7 +44,7 @@ const props = defineProps({ }, }); -const router = useRouter(); +const { push } = useRouter(); const { t } = useI18n(); const { dialog } = useQuasar(); const { notify } = useNotify(); @@ -142,7 +142,7 @@ const onCreateClaimAccepted = async () => { try { const params = { ticketId: ticket.value.id, sales: props.sales }; const { data } = await axios.post(`Claims/createFromSales`, params); - router.push({ name: 'ClaimBasicData', params: { id: data.id } }); + push({ name: 'ClaimBasicData', params: { id: data.id } }); } catch (error) { console.error('Error creating claim: ', error); } @@ -169,7 +169,7 @@ const createRefund = async (withWarehouse) => { const { data } = await axios.post('Tickets/cloneAll', params); const [refundTicket] = data; notify(t('refundTicketCreated', { ticketId: refundTicket.id }), 'positive'); - router.push({ name: 'TicketSale', params: { id: refundTicket.id } }); + push({ name: 'TicketSale', params: { id: refundTicket.id } }); } catch (error) { console.error(error); } diff --git a/src/pages/Ticket/Card/TicketSaleTracking.vue b/src/pages/Ticket/Card/TicketSaleTracking.vue index 6978d92c8..e7830bf37 100644 --- a/src/pages/Ticket/Card/TicketSaleTracking.vue +++ b/src/pages/Ticket/Card/TicketSaleTracking.vue @@ -25,7 +25,7 @@ const saleTrackingFetchDataRef = ref(null); const sales = ref([]); const saleTrackings = ref([]); -const itemShelvignsSales = ref([]); +const itemShelvingsSales = ref([]); const saleTrackingUrl = computed(() => `SaleTrackings/${route.params.id}/filter`); const oldQuantity = ref(null); @@ -88,7 +88,7 @@ const logTableColumns = computed(() => [ label: t('ticketSaleTracking.original'), name: 'original', field: 'originalQuantity', - align: 'original', + align: 'left', sortable: true, }, { @@ -177,7 +177,7 @@ const getItemShelvingSales = async (sale) => { const { data } = await axios.get(`ItemShelvingSales/filter`, { params: { filter: JSON.stringify(filter) }, }); - itemShelvignsSales.value = data; + itemShelvingsSales.value = data; } catch (error) { console.error(error); } @@ -337,7 +337,7 @@ const qCheckBoxController = (sale, action) => { :no-data-label="t('globals.noResults')" > - + { - + - - {{ row.itemFk }} - - + + + {{ row.itemFk }} + + + @@ -416,8 +418,18 @@ const qCheckBoxController = (sale, action) => { + + + {{ row.quantity }} + + + + + {{ dashIfEmpty(row.parkingFk) }} + + - + { data-key="saleTrackingLog" :rows="saleTrackings" :columns="logTableColumns" - class="q-pa-sm" + class="q-pa-sm full-width" > - - {{ row.name }} - + + + {{ row.name }} + + @@ -469,12 +483,12 @@ const qCheckBoxController = (sale, action) => { > - + { - - {{ row.name }} - + + + {{ row.name }} + + - + { - + div { + max-width: 900px; + } +} diff --git a/src/pages/Ticket/Card/TicketService.vue b/src/pages/Ticket/Card/TicketService.vue index 873051676..45a870f7f 100644 --- a/src/pages/Ticket/Card/TicketService.vue +++ b/src/pages/Ticket/Card/TicketService.vue @@ -25,7 +25,7 @@ const { notify } = useNotify(); const selected = ref([]); const defaultTaxClass = ref(null); - +const isSaving = ref(false); const crudModelFilter = computed(() => ({ where: { ticketFk: route.params.id }, })); @@ -50,7 +50,7 @@ const createRefund = async () => { if (!selected.value.length) return; const params = { - servicesIds: selected.value.map((s) => +s.ticketFk), + servicesIds: selected.value.map((s) => +s.id), withWarehouse: false, negative: true, }; @@ -104,7 +104,31 @@ const columns = computed(() => [ sortable: true, align: 'left', }, + { + label: '', + name: 'actions', + align: 'left', + columnFilter: null, + }, ]); + +async function deleteService(row) { + const serviceId = row.id; + if (!row.id) ticketServiceCrudRef.value.reset(); + else { + const { data } = await axios.delete(`TicketServices/${serviceId}`); + if (data) notify('Service deleted successfully', 'positive'); + ticketServiceCrudRef.value.reload(); + } +} + +async function handleSave() { + if (!isSaving.value) { + isSaving.value = true; + await ticketServiceCrudRef.value?.saveChanges(); + isSaving.value = false; + } +} @@ -123,6 +147,8 @@ const columns = computed(() => [ :data-required="crudModelRequiredData" auto-load v-model:selected="selected" + :order="['description ASC']" + :default-remove="false" > [ v-model.number="row.price" type="number" min="0" + @keyup.enter="handleSave" /> + + + + + {{ t('globals.delete') }} + + + + @@ -193,3 +235,4 @@ const columns = computed(() => [ /> +ñ diff --git a/src/pages/Ticket/Card/TicketSummary.vue b/src/pages/Ticket/Card/TicketSummary.vue index 2e85be000..2ca0b5727 100644 --- a/src/pages/Ticket/Card/TicketSummary.vue +++ b/src/pages/Ticket/Card/TicketSummary.vue @@ -1,4 +1,5 @@ (editableStates = data)" + :filter="{ fields: ['code', 'name', 'id', 'alertLevel'], order: 'name ASC' }" auto-load + @on-fetch="(data) => (editableStates = data)" /> - + - - {{ entity.ticketState?.state?.name }} - + + {{ entity.ticketState.state.name }} + @@ -144,7 +177,14 @@ async function changeState(value) { :label="t('ticket.summary.agency')" :value="entity.agencyMode?.name" /> - + + + + {{ entity?.zone?.name }} + + + + - + + + + {{ entity.routeFk }} + + + + @@ -181,9 +228,9 @@ async function changeState(value) { :value="dashIfEmpty(entity.weight)" /> - + @@ -242,6 +289,7 @@ async function changeState(value) { type="textarea" class="notes" readonly + autogrow /> @@ -259,13 +307,14 @@ async function changeState(value) { /> @@ -288,11 +337,10 @@ async function changeState(value) { - + - {{ t('ticket.summary.claim') }}: - {{ props.row.claim.claimFk }} + + {{ t('ticket.summary.claim') }}: + {{ props.row.claim.claimFk }} + - {{ t('ticket.summary.claim') }}: - {{ props.row.claimBeginning.claimFk }} + + {{ t('ticket.summary.claim') }}: + {{ props.row.claimBeginning.claimFk }} + - {{ t('ticket.summary.visible') }}: - {{ props.row.visible }} + + {{ t('ticket.summary.visible') }}: + {{ props.row.visible }} + {{ t('ticket.summary.reserved') }} @@ -351,8 +398,8 @@ async function changeState(value) { {{ t('ticket.summary.itemShortage') }} @@ -361,8 +408,8 @@ async function changeState(value) { {{ t('ticket.summary.hasComponentLack') }} @@ -379,8 +426,34 @@ async function changeState(value) { /> - {{ props.row.visible }} - {{ props.row.available }} + + + {{ props.row.visible }} + + + {{ props.row.visible }} + + + + + {{ props.row.available }} + + + {{ props.row.available }} + + {{ props.row.quantity }} @@ -410,14 +483,11 @@ async function changeState(value) { - - - + + + - + {{ t('ticket.summary.created') }} {{ t('ticket.summary.package') }} {{ t('ticket.summary.quantity') }} @@ -431,13 +501,15 @@ async function changeState(value) { + + - + - + {{ t('ticket.summary.quantity') }} {{ t('globals.description') }} {{ t('ticket.summary.price') }} @@ -458,12 +530,62 @@ async function changeState(value) { + + + + + + {{ t('ticket.summary.description') }} + {{ t('ticket.summary.created') }} + {{ t('ticket.summary.requester') }} + {{ t('ticket.summary.attender') }} + {{ t('ticket.summary.quantity') }} + {{ t('ticket.summary.price') }} + {{ t('ticket.summary.item') }} + {{ t('ticket.summary.ok') }} + + + + + {{ props.row.description }} + {{ toDate(props.row.created) }} + {{ props.row.requester?.user?.username }} + {{ props.row.atender?.user?.username }} + {{ props.row.quantity }} + {{ toCurrency(props.row.price) }} + + + {{ props.row.itemFk }} + + + + + + + {{ t('Accepted') }} + + + {{ t('Denied') }} + + + + + + + + + +en: + ItemPicker: Item Picker + Packager: Packager + Delivery: Delivery + SalesPerson: Sales Person + Administrative: Administrative + Weight: Weight + InvoiceOut: Invoice Out + DropOff: Drop Off + Sustitución: Sustitution +es: + ItemPicker: Items + Packager: Encajador + Delivery: Envío + SalesPerson: Comercial + Administrative: Administrativa + Weight: Peso + InvoiceOut: Facturas + DropOff: Despacho + Sustitución: Sustitución + Accepted: Aceptado + Denied: Denegado + diff --git a/src/pages/Ticket/Card/TicketTracking.vue b/src/pages/Ticket/Card/TicketTracking.vue index f7cbb14e9..00b517765 100644 --- a/src/pages/Ticket/Card/TicketTracking.vue +++ b/src/pages/Ticket/Card/TicketTracking.vue @@ -96,8 +96,8 @@ const openCreateModal = () => createTrackingDialogRef.value.show(); :no-data-label="t('globals.noResults')" > - - + + {{ row.user?.name }} diff --git a/src/pages/Ticket/Card/TicketTransfer.vue b/src/pages/Ticket/Card/TicketTransfer.vue index 1944b80f4..e1a011a84 100644 --- a/src/pages/Ticket/Card/TicketTransfer.vue +++ b/src/pages/Ticket/Card/TicketTransfer.vue @@ -1,13 +1,11 @@ - {{ _transfer }} route.params.id, @@ -34,37 +35,42 @@ const salesFilter = computed(() => ({ const sales = ref([]); const packingTypeVolume = ref([]); -const rows = computed(() => sales.value); const columns = computed(() => [ { + align: 'left', label: t('volume.item'), - name: 'item', - align: 'left', + name: 'itemFk', + chip: { + condition: () => true, + }, + isId: true, }, { + align: 'left', label: t('volume.description'), - name: 'description', - align: 'left', + name: 'concept', + columnClass: 'expand', + cardVisible: true, }, { + align: 'left', label: t('volume.packingType'), - name: 'quantity', - field: (row) => row.item.itemPackingTypeFk, - align: 'left', - format: (val) => dashIfEmpty(val), + name: 'itemPackingTypeFk', + cardVisible: true, + format: (row, dashIfEmpty) => dashIfEmpty(row.item.itemPackingTypeFk), }, { + align: 'left', label: t('volume.quantity'), name: 'quantity', - field: 'quantity', - align: 'left', + cardVisible: true, }, { - label: t('volume.volumeQuantity'), - name: 'quantity', - field: (row) => row.saleVolume?.volume, align: 'left', + label: t('volume.volumeQuantity'), + name: 'volume', + cardVisible: true, }, ]); @@ -87,9 +93,7 @@ const applyVolumes = async (salesData) => { } }; -onMounted(() => { - stateStore.rightDrawer = true; -}); +onMounted(() => (stateStore.rightDrawer = true)); onUnmounted(() => (stateStore.rightDrawer = false)); @@ -102,52 +106,55 @@ onUnmounted(() => (stateStore.rightDrawer = false)); @on-fetch="(data) => applyVolumes(data)" auto-load /> - - - - - - {{ t('volume.type') }}: - {{ dashIfEmpty(packingType.description) }} - - - - {{ t('volume.volume') }}: {{ packingType.volume }} - - - - - - - - - {{ row.itemFk }} - - - + + + + {{ t('volume.type') }}: + {{ dashIfEmpty(packingType.description) }} + + + + {{ t('volume.volume') }}: {{ packingType.volume }} + + + + + + + + {{ row.itemFk }} + + - - - - {{ row.item.name }} - {{ row.item.subName }} - - - + + {{ row.item.name }} + {{ row.item.subName }} + - + + {{ packingTypeVolume?.[rowIndex]?.volume }} + + diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue index 177b3a29b..43b500dc0 100644 --- a/src/pages/Ticket/TicketAdvance.vue +++ b/src/pages/Ticket/TicketAdvance.vue @@ -456,11 +456,12 @@ const handleCloseProgressDialog = () => { const handleCancelProgress = () => (cancelProgress.value = true); onMounted(async () => { - let today = Date.vnNew().toISOString(); + let today = Date.vnNew(); const tomorrow = new Date(today); tomorrow.setDate(tomorrow.getDate() + 1); userParams.dateFuture = tomorrow; userParams.dateToAdvance = today; + userParams.scopeDays = 1; userParams.warehouseFk = user.value.warehouseFk; const filter = { limit: 0 }; await arrayData.addFilter({ filter, userParams }); @@ -550,6 +551,7 @@ onMounted(async () => { style="max-width: 99%" > + {{ userParams.scopeDays }} await getItemPackingTypes()); + + + + + + + + + + diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue index bd0bda6e3..ad97e75c1 100644 --- a/src/pages/Ticket/TicketList.vue +++ b/src/pages/Ticket/TicketList.vue @@ -4,7 +4,9 @@ import { computed, ref, onMounted } from 'vue'; import { useRoute } from 'vue-router'; import { useStateStore } from 'stores/useStateStore'; import { useI18n } from 'vue-i18n'; -import { toDate, toCurrency } from 'src/filters/index'; +import { useQuasar } from 'quasar'; +import { toDate, toCurrency, dashIfEmpty } from 'src/filters/index'; +import useNotify from 'src/composables/useNotify'; import TicketSummary from './Card/TicketSummary.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; @@ -14,11 +16,19 @@ import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnRow from 'src/components/ui/VnRow.vue'; import RightMenu from 'src/components/common/RightMenu.vue'; import TicketFilter from './TicketFilter.vue'; +import VnInput from 'src/components/common/VnInput.vue'; +import FetchData from 'src/components/FetchData.vue'; +import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue'; +import ZoneDescriptorProxy from '../Zone/Card/ZoneDescriptorProxy.vue'; +import { toTimeFormat } from 'src/filters/date'; +import InvoiceOutDescriptorProxy from '../InvoiceOut/Card/InvoiceOutDescriptorProxy.vue'; const route = useRoute(); const { t } = useI18n(); const { viewSummary } = useSummaryDialog(); const tableRef = ref(); +const quasar = useQuasar(); +const { notify } = useNotify(); const clientsOptions = ref([]); const addressesOptions = ref([]); const agenciesOptions = ref([]); @@ -26,6 +36,7 @@ const selectedClient = ref(); const stateStore = useStateStore(); const from = Date.vnNew(); from.setHours(0, 0, 0, 0); +from.setDate(from.getDate() - 7); const to = Date.vnNew(); to.setHours(23, 59, 0, 0); to.setDate(to.getDate() + 1); @@ -44,19 +55,21 @@ const initializeFromQuery = () => { Object.assign(userParams, { from, to }); }; +const selectedRows = ref([]); +const hasSelectedRows = computed(() => selectedRows.value.length > 0); +const showForm = ref(false); +const dialogData = ref(); +const companiesOptions = ref([]); +const accountingOptions = ref([]); +const amountToReturn = ref(); + const columns = computed(() => [ { align: 'left', - name: 'stateFk', - label: t('ticketList.state'), - columnFilter: { - name: 'stateFk', - component: 'select', - attrs: { - url: 'States', - fields: ['id', 'name'], - }, - }, + name: 'statusIcons', + hidden: true, + format: () => '', + columnClass: 'expand', }, { align: 'left', @@ -67,40 +80,6 @@ const columns = computed(() => [ }, isId: true, }, - { - align: 'left', - name: 'nickname', - label: t('ticketList.nickname'), - isTitle: true, - }, - - { - align: 'left', - name: 'shipped', - cardVisible: true, - label: t('ticketList.shipped'), - columnFilter: { - component: 'date', - alias: 't', - inWhere: true, - }, - format: ({ shipped }) => toDate(shipped), - }, - { - align: 'left', - name: 'zoneFk', - label: t('ticketList.zone'), - columnFilter: { - component: 'select', - attrs: { - url: 'Zones', - fields: ['id', 'name'], - }, - alias: 't', - inWhere: true, - }, - format: (row, dashIfEmpty) => dashIfEmpty(row.zoneName), - }, { align: 'left', label: t('ticketList.salesPerson'), @@ -118,6 +97,89 @@ const columns = computed(() => [ }, format: (row, dashIfEmpty) => dashIfEmpty(row.salesPerson), }, + { + align: 'left', + name: 'shippedDate', + cardVisible: true, + label: t('ticketList.shipped'), + columnFilter: { + component: 'date', + alias: 't', + inWhere: true, + }, + format: ({ shippedDate }) => toDate(shippedDate), + }, + { + align: 'left', + name: 'shipped', + label: t('ticketList.hour'), + format: (row) => toTimeFormat(row.shipped), + }, + { + align: 'left', + name: 'zoneLanding', + label: t('ticketList.closure'), + format: (row, dashIfEmpty) => dashIfEmpty(toTimeFormat(row.zoneLanding)), + }, + { + align: 'left', + name: 'nickname', + label: t('ticketList.nickname'), + columnClass: 'expand', + }, + { + align: 'left', + name: 'addressNickname', + label: t('ticketList.addressNickname'), + columnClass: 'expand', + }, + { + align: 'left', + name: 'province', + label: t('ticketList.province'), + columnClass: 'expand', + }, + { + align: 'left', + name: 'stateFk', + label: t('ticketList.state'), + columnFilter: { + name: 'stateFk', + component: 'select', + attrs: { + url: 'States', + fields: ['id', 'name'], + }, + }, + columnClass: 'expand', + }, + { + align: 'left', + name: 'refFk', + label: t('ticketList.ref'), + }, + { + align: 'left', + name: 'zoneFk', + label: t('ticketList.zone'), + columnFilter: { + component: 'select', + attrs: { + url: 'Zones', + fields: ['id', 'name'], + }, + alias: 't', + inWhere: true, + }, + columnClass: 'expand', + format: (row, dashIfEmpty) => dashIfEmpty(row.zoneName), + }, + { + align: 'left', + name: 'warehouse', + label: t('ticketList.warehouse'), + columnClass: 'expand', + }, { align: 'left', name: 'totalWithVat', @@ -133,15 +195,27 @@ const columns = computed(() => [ align: 'right', name: 'tableActions', actions: [ + { + title: t('ticketList.toLines'), + icon: 'list', + isPrimary: true, + action: (row) => redirectToLines(row.id), + }, { title: t('ticketList.summary'), icon: 'preview', + isPrimary: true, action: (row) => viewSummary(row.id, TicketSummary), }, ], }, ]); +function redirectToLines(id) { + const url = `#/ticket/${id}/sale`; + window.open(url, '_blank'); +} + const onClientSelected = async (formData) => { await fetchClient(formData); await fetchAddresses(formData); @@ -210,16 +284,174 @@ const fetchAddresses = async (formData) => { } }; const getColor = (row) => { - return row?.classColor ? `bg-${row.classColor}` : 'bg-orange'; + if (row.alertLevelCode === 'OK') return 'bg-success'; + else if (row.alertLevelCode === 'FREE') return 'bg-notice'; + else if (row.alertLevel === 1) return 'bg-warning'; + else if (row.alertLevel === 0) return 'bg-alert'; +}; + +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'; }; onMounted(() => { initializeFromQuery(); stateStore.rightDrawer = true; }); + +async function makeInvoice(ticket) { + const ticketsIds = ticket.map((item) => item.id); + const { data } = await axios.post(`Tickets/invoiceTicketsAndPdf`, { ticketsIds }); + const response = data; + if (response) + quasar.notify({ + message: t('globals.dataSaved'), + type: 'positive', + }); +} + +async function sendDocuware(ticket) { + try { + let ticketIds = ticket.map((item) => item.id); + + const { data } = await axios.post(`Docuwares/upload`, { + fileCabinet: 'deliveryNote', + ticketIds, + }); + + for (let ticket of ticketIds) { + ticket.stateFk = data.id; + ticket.state = data.name; + ticket.alertLevel = data.alertLevel; + ticket.alertLevelCode = data.code; + } + notify('globals.dataSaved', 'positive'); + } catch (err) { + console.err('err: ', err); + } +} + +function openBalanceDialog(ticket) { + const checkedTickets = ticket; + const amountPaid = ref(0); + const clientFk = ref(null); + const description = ref([]); + const firstTicketClientId = checkedTickets[0].clientFk; + const isSameClient = checkedTickets.every( + (ticket) => ticket.clientFk === firstTicketClientId + ); + + if (!isSameClient) { + throw new Error('You cannot make a payment on account from multiple clients'); + } + + for (let ticketData of checkedTickets) { + amountPaid.value += ticketData.totalWithVat; + clientFk.value = ticketData.clientFk; + description.value.push(ticketData.id); + } + + const balanceCreateDialog = ref({ + amountPaid: amountPaid.value, + clientFk: clientFk.value, + description: `Albaran: ${description.value.join(', ')}`, + }); + dialogData.value = balanceCreateDialog; + showForm.value = true; +} + +async function onSubmit() { + const { data: email } = await axios.get('Clients', { + params: { + filter: JSON.stringify({ where: { id: dialogData.value.value.clientFk } }), + }, + }); + + const { data } = await axios.post( + `Clients/${dialogData.value.value.clientFk}/createReceipt`, + { + payed: dialogData.value.payed, + companyFk: dialogData.value.companyFk, + bankFk: dialogData.value.bankFk, + amountPaid: dialogData.value.value.amountPaid, + description: dialogData.value.value.description, + clientFk: dialogData.value.value.clientFk, + email: email[0].email, + } + ); + + if (data) notify('globals.dataSaved', 'positive'); + showForm.value = false; +} + +const setAmountToReturn = (newAmountGiven) => { + const amountPaid = dialogData.value.value.amountPaid; + + amountToReturn.value = newAmountGiven - amountPaid; +}; + +function setReference(data) { + let newDescription = ''; + + switch (data) { + case 1: + newDescription = `${t( + 'ticketList.creditCard' + )}, ${dialogData.value.value.description.replace( + /^(Credit Card, |Cash, |Transfers, )/, + '' + )}`; + break; + case 2: + newDescription = `${t( + 'ticketList.cash' + )}, ${dialogData.value.value.description.replace( + /^(Credit Card, |Cash, |Transfers, )/, + '' + )}`; + break; + case 3: + newDescription = `${newDescription.replace( + /^(Credit Card, |Cash, |Transfers, )/, + '' + )}`; + break; + case 4: + newDescription = `${t( + 'ticketList.transfers' + )}, ${dialogData.value.value.description.replace( + /^(Credit Card, |Cash, |Transfers, )/, + '' + )}`; + break; + case 3317: + newDescription = ''; + break; + default: + break; + } + + dialogData.value.value.description = newDescription; +} + (companiesOptions = data)" + auto-load + /> + (accountingOptions = data)" + auto-load + /> { :user-params="userParams" :right-search="false" redirect="ticket" - auto-load + v-model:selected="selectedRows" + :table="{ + 'row-key': 'id', + selection: 'multiple', + }" > + + + + + {{ t('No verified data') }} + + + + + {{ t('Purchase request') }} + + + + + {{ t('Not visible') }} + + + + + {{ t('Client frozen') }} + + + + {{ t('Risk') }}: {{ row.risk }} + + + + {{ t('Component lack') }} + + + + + {{ t('Rounding') }} + + + + + + + {{ row.salesPerson }} + + + + + + + {{ toDate(row.shippedDate) }} + + + + + + {{ row.nickname }} + + + + + + {{ row.addressNickname }} + + + + + + + {{ row.state }} + + + + {{ row.state }} + + + + + {{ dashIfEmpty(row.refFk) }} + + + + + + {{ dashIfEmpty(row.zoneName) }} + + + + + + {{ row.totalWithVat }} + + { option-label="name" hide-selected @update:model-value="(client) => onClientSelected(data)" + :sort-by="'id ASC'" > @@ -339,12 +696,136 @@ onMounted(() => { - - - {{ row.state }} - - + + + + {{ t('ticketList.createInvoice') }} + + + + + + {{ t('ticketList.accountPayment') }} + + + + + + {{ t('ticketList.addPayment') }} + + + + + + + + + + + + {{ t('ticketList.cash') }} + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ t('ticketList.sendDocuware') }} + + @@ -353,4 +834,5 @@ es: You can search by ticket id or alias: Puedes buscar por id o alias del ticket Zone: Zona New ticket: Nuevo ticket + Component lack: Faltan componentes diff --git a/src/pages/Ticket/locale/en.yml b/src/pages/Ticket/locale/en.yml index 06699e00b..58367e3a2 100644 --- a/src/pages/Ticket/locale/en.yml +++ b/src/pages/Ticket/locale/en.yml @@ -67,6 +67,8 @@ advanceTickets: advanceWithoutNegativeTitle: Advance tickets (without negatives) advanceWithoutNegativeSubtitle: Advance {selectedTickets} tickets confirmation errorsList: Errors list + search: Search advance tickets + searchInfo: Search advance tickets by ID or client ID futureTickets: problems: Problems ticketId: ID @@ -105,7 +107,7 @@ expedition: name: Name packageType: Package type counter: Counter - externalId: externalId + externalId: external Id created: Created state: State historyAction: Status log @@ -118,6 +120,10 @@ expedition: removeExpeditionSubtitle: Are you sure you want to delete this expedition? worker: Worker move: Move + isScanned: Is scanned + yes: Yes + no: No + removeExpedition: Delete expedition basicData: next: Next back: Back @@ -233,7 +239,7 @@ package: removePackage: Remove package ticketList: id: Id - nickname: Nickname + nickname: Ticket nickname state: State shipped: Shipped zone: Zone @@ -242,3 +248,27 @@ ticketList: summary: Summary client: Customer createTicket: Create ticket + createInvoice: Create invoice + accountPayment: Account payment + sendDocuware: Set delivered and send delivery note(s) to the tablet + addPayment: Add payment + date: Date + company: Company + amount: Amount + reference: Reference + bank: Bank + cash: Cash + deliveredAmount: Delivered amount + amountToReturn: Amount to return + viewReceipt: View receipt + sendEmail: Send email + compensation: Compensation + creditCard: Credit card + transfers: Transfers + province: Province + warehouse: Warehouse + hour: Hour + closure: Closure + toLines: Go to lines + addressNickname: Address nickname + ref: Reference diff --git a/src/pages/Ticket/locale/es.yml b/src/pages/Ticket/locale/es.yml index 0a27519ad..1acaf154b 100644 --- a/src/pages/Ticket/locale/es.yml +++ b/src/pages/Ticket/locale/es.yml @@ -114,6 +114,8 @@ advanceTickets: advanceWithoutNegativeTitle: Adelantar tickets (sin negativos) advanceWithoutNegativeSubtitle: '¿Desea adelantar {selectedTickets} tickets?' errorsList: Lista de errores + search: Buscar por tickets adelantados + searchInfo: Buscar tickets adelantados por el identificador o el identificador del cliente futureTickets: problems: Problemas ticketId: ID @@ -192,9 +194,9 @@ expedition: id: Expedición item: Artículo name: Nombre - packageType: Package type + packageType: Embalaje counter: Contador - externalId: externalId + externalId: ID externo created: Fecha creación state: Estado historyAction: Historial de estados @@ -207,6 +209,10 @@ expedition: removeExpeditionSubtitle: ¿Está seguro de eliminar esta expedición? worker: Trabajador move: Mover + isScanned: Escaneado + yes: Sí + no: No + removeExpedition: Eliminar expedición package: package: Embalaje quantity: Cantidad @@ -236,7 +242,7 @@ You can search by ticket id or alias: Puedes buscar por id o alias del ticket Select lines to see the options: Selecciona líneas para ver las opciones ticketList: id: Id - nickname: Alias + nickname: Alias ticket state: Estado shipped: F. Envío zone: Zona @@ -245,3 +251,27 @@ ticketList: summary: Resumen client: Cliente createTicket: Crear ticket + createInvoice: Crear factura + accountPayment: Pago a cuenta... + sendDocuware: Marcar como servido/s y enviar albarán/es a la tablet + addPayment: Añadir pago + date: Fecha + company: Empresa + amount: Importe + reference: Referencia + bank: Caja + cash: Efectivo + deliveredAmount: Cantidad entregada + amountToReturn: Cantidad a devolver + viewReceipt: Ver recibido + sendEmail: Enviar correo + compensation: Compensación + creditCard: Tarjeta de crédito + transfers: Transferencias + province: Provincia + warehouse: Almacén + hour: Hora + closure: Cierre + toLines: Ir a lineas + addressNickname: Alias consignatario + ref: Referencia diff --git a/src/pages/Travel/Card/TravelSummary.vue b/src/pages/Travel/Card/TravelSummary.vue index d7dce911b..4be198493 100644 --- a/src/pages/Travel/Card/TravelSummary.vue +++ b/src/pages/Travel/Card/TravelSummary.vue @@ -121,6 +121,20 @@ const thermographsTableColumns = computed(() => { name: 'temperatureFk', align: 'left', }, + { + label: t('globals.maxTemperature'), + field: 'maxTemperature', + name: 'maxTemperature', + align: 'left', + format: (val) => (val ? `${val}°` : ''), + }, + { + label: t('globals.minTemperature'), + field: 'minTemperature', + name: 'minTemperature', + align: 'left', + format: (val) => (val ? `${val}°` : ''), + }, { label: t('travel.thermographs.state'), field: 'result', @@ -133,7 +147,7 @@ const thermographsTableColumns = computed(() => { name: 'destination', align: 'left', format: (val) => - warehouses.value.find((warehouse) => warehouse.id === val).name, + warehouses.value.find((warehouse) => warehouse.id === val)?.name, }, { label: t('travel.thermographs.created'), @@ -319,7 +333,7 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`; - + {{ value }} diff --git a/src/pages/Travel/Card/TravelThermographs.vue b/src/pages/Travel/Card/TravelThermographs.vue index 6d83581ee..e308243e3 100644 --- a/src/pages/Travel/Card/TravelThermographs.vue +++ b/src/pages/Travel/Card/TravelThermographs.vue @@ -47,6 +47,20 @@ const TableColumns = computed(() => { name: 'temperatureFk', align: 'left', }, + { + label: t('globals.maxTemperature'), + field: 'maxTemperature', + name: 'maxTemperature', + align: 'left', + format: (val) => (val ? `${val}°` : ''), + }, + { + label: t('globals.minTemperature'), + field: 'minTemperature', + name: 'minTemperature', + align: 'left', + format: (val) => (val ? `${val}°` : ''), + }, { label: t('travel.thermographs.state'), field: 'result', @@ -92,11 +106,7 @@ const openRemoveDialog = async (id) => { }, }) .onOk(async () => { - try { - await removeThermograph(id); - } catch (err) { - console.error('Error removing thermograph'); - } + await removeThermograph(id); }); }; @@ -106,9 +116,7 @@ const redirectToThermographForm = (action, id) => { }; if (action === 'edit' && id) { - const params = {}; - params.thermographId = id; - routeDetails.params = params; + routeDetails.query = { travelThermographFk: id }; } router.push(routeDetails); }; diff --git a/src/pages/Travel/Card/TravelThermographsForm.vue b/src/pages/Travel/Card/TravelThermographsForm.vue index 4f16b0a49..42acf30b9 100644 --- a/src/pages/Travel/Card/TravelThermographsForm.vue +++ b/src/pages/Travel/Card/TravelThermographsForm.vue @@ -1,6 +1,6 @@ @@ -185,6 +167,18 @@ const onThermographCreated = async (data) => { :filter="{ order: 'name' }" auto-load /> + (temperaturesOptions = data)" + auto-load + url="Temperatures" + /> + (thermographsOptions = data)" + :filter="thermographFilter" + auto-load + /> { (thermographForm.thermographId = data.id) - " + @on-data-saved="onThermographCreated" /> @@ -271,6 +259,26 @@ const onThermographCreated = async (data) => { option-label="name" /> + + + + + + { -es: - Select files: Selecciona ficheros - Thermograph created: Termógrafo creado - New thermograph: Nuevo termógrafo + es: + Select files: Selecciona ficheros + Thermograph created: Termógrafo creado + New thermograph: Nuevo termógrafo diff --git a/src/pages/Worker/WorkerList.vue b/src/pages/Worker/WorkerList.vue index 6b3b117b6..f4ae20591 100644 --- a/src/pages/Worker/WorkerList.vue +++ b/src/pages/Worker/WorkerList.vue @@ -214,6 +214,7 @@ async function autofillBic(worker) { diff --git a/src/pages/Zone/Card/ZoneEventExclusionForm.vue b/src/pages/Zone/Card/ZoneEventExclusionForm.vue index 0ba2e640a..215c12f46 100644 --- a/src/pages/Zone/Card/ZoneEventExclusionForm.vue +++ b/src/pages/Zone/Card/ZoneEventExclusionForm.vue @@ -154,7 +154,7 @@ onMounted(() => { (stateStore.rightDrawer = false)); - - - - - - {{ t('globals.collapseMenu') }} - - - - - import { onMounted, ref, computed, watch, onUnmounted } from 'vue'; import { useRoute } from 'vue-router'; - +import VnInput from 'src/components/common/VnInput.vue'; import { useState } from 'src/composables/useState'; import axios from 'axios'; import { useArrayData } from 'composables/useArrayData'; @@ -144,7 +144,8 @@ watch(storeData, async (val) => { }); const reFetch = async () => { - await arrayData.fetch({ append: false }); + const { data } = await arrayData.fetch({ append: false }); + nodes.value = data; }; onMounted(async () => { @@ -182,6 +183,16 @@ onUnmounted(() => { + + + + + import('src/pages/Entry/EntryLatestBuys.vue'), }, + { + path: 'stock-Bought', + name: 'EntryStockBought', + meta: { + title: 'reserves', + icon: 'deployed_code_history', + }, + component: () => import('src/pages/Entry/EntryStockBought.vue'), + }, ], }, { diff --git a/src/router/modules/route.js b/src/router/modules/route.js index 3c5c860cf..9a7b16df3 100644 --- a/src/router/modules/route.js +++ b/src/router/modules/route.js @@ -11,7 +11,14 @@ export default { component: RouterView, redirect: { name: 'RouteMain' }, menus: { - main: ['RouteList', 'RouteAutonomous', 'RouteRoadmap', 'CmrList', 'AgencyList'], + main: [ + 'RouteList', + 'RouteExtendedList', + 'RouteAutonomous', + 'RouteRoadmap', + 'CmrList', + 'AgencyList', + ], card: ['RouteBasicData', 'RouteTickets', 'RouteLog'], }, children: [ @@ -19,9 +26,6 @@ export default { path: '/route', name: 'RouteMain', component: () => import('src/components/common/VnSectionMain.vue'), - props: { - leftDrawer: false, - }, redirect: { name: 'RouteList' }, children: [ { @@ -33,6 +37,15 @@ export default { }, component: () => import('src/pages/Route/RouteList.vue'), }, + { + path: 'extended-list', + name: 'RouteExtendedList', + meta: { + title: 'RouteExtendedList', + icon: 'format_list_bulleted', + }, + component: () => import('src/pages/Route/RouteExtendedList.vue'), + }, { path: 'create', name: 'RouteCreate', @@ -78,7 +91,7 @@ export default { name: 'AgencyList', meta: { title: 'agencyList', - icon: 'view_list', + icon: 'list', }, component: () => import('src/pages/Route/Agency/AgencyList.vue'), diff --git a/src/router/modules/ticket.js b/src/router/modules/ticket.js index dcc238f95..6e407b88b 100644 --- a/src/router/modules/ticket.js +++ b/src/router/modules/ticket.js @@ -198,7 +198,7 @@ export default { name: 'TicketPackage', meta: { title: 'packages', - icon: 'vn:bin', + icon: 'vn:bucket', }, component: () => import('src/pages/Ticket/Card/TicketPackage.vue'), }, diff --git a/test/cypress/integration/entry/stockBought.spec.js b/test/cypress/integration/entry/stockBought.spec.js new file mode 100644 index 000000000..66e06b79e --- /dev/null +++ b/test/cypress/integration/entry/stockBought.spec.js @@ -0,0 +1,39 @@ +describe('EntryStockBought', () => { + beforeEach(() => { + cy.viewport(1920, 1080); + cy.login('buyer'); + cy.visit(`/#/entry/stock-Bought`); + }); + it('Should edit the reserved space', () => { + cy.get('.q-field__native.q-placeholder').should('have.value', '01/01/2001'); + cy.get('input[name="reserve"]').type('10{enter}'); + cy.get('button[title="Save"]').click(); + cy.get('.q-notification__message').should('have.text', 'Data saved'); + }); + it('Should add a new reserved space for buyerBoss', () => { + cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click(); + cy.get('input[aria-label="Reserve"]').type('1'); + cy.get('input[aria-label="Date"]').eq(1).clear(); + cy.get('input[aria-label="Date"]').eq(1).type('01-01'); + cy.get('input[aria-label="Buyer"]').type('buyerboss{downarrow}{enter}'); + cy.get('.q-notification__message').should('have.text', 'Data created'); + }); + it('Should check detail for the buyer', () => { + cy.get(':nth-child(1) > .sticky > .q-btn > .q-btn__content > .q-icon').click(); + cy.get('tBody > tr').eq(1).its('length').should('eq', 1); + }); + it('Should check detail for the buyerBoss and had no content', () => { + cy.get(':nth-child(2) > .sticky > .q-btn > .q-btn__content > .q-icon').click(); + cy.get('.q-table__bottom.row.items-center.q-table__bottom--nodata').should( + 'have.text', + 'warningNo data available' + ); + }); + it('Should edit travel m3 and refresh', () => { + cy.get('.vn-row > div > .q-btn > .q-btn__content > .q-icon').click(); + cy.get('input[aria-label="m3"]').clear(); + cy.get('input[aria-label="m3"]').type('60'); + cy.get('.q-mt-lg > .q-btn--standard > .q-btn__content > .block').click(); + cy.get('.vn-row > div > :nth-child(2)').should('have.text', '60'); + }); +}); diff --git a/test/cypress/integration/vnComponent/UserPanel.spec.js b/test/cypress/integration/vnComponent/UserPanel.spec.js new file mode 100644 index 000000000..e83d07954 --- /dev/null +++ b/test/cypress/integration/vnComponent/UserPanel.spec.js @@ -0,0 +1,58 @@ +/// +describe('UserPanel', () => { + beforeEach(() => { + cy.viewport(1280, 720); + cy.login('developer'); + cy.visit(`/#dashboard`); + cy.waitForElement('.q-page', 6000); + }); + + it('should notify when update user warehouse', () => { + const userWarehouse = + '.q-menu .q-gutter-xs > :nth-child(3) > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > .q-field__native> .q-field__input'; + + // Abro el panel + cy.openUserPanel(); + + // Compruebo la opcion inicial + cy.get(userWarehouse).should('have.value', 'VNL').click(); + + // Actualizo la opción + getOption(3); + + //Compruebo la notificación + cy.get('.q-notification').should('be.visible'); + cy.get(userWarehouse).should('have.value', 'VNH'); + + //Restauro el valor + cy.get(userWarehouse).click(); + getOption(2); + }); + it('should notify when update user company', () => { + const userCompany = + '.q-menu .q-gutter-xs > :nth-child(2) > .q-field--float > .q-field__inner > .q-field__control > .q-field__control-container > .q-field__native> .q-field__input'; + + // Abro el panel + cy.openUserPanel(); + + // Compruebo la opcion inicial + cy.get(userCompany).should('have.value', 'Warehouse One').click(); + + //Actualizo la opción + getOption(2); + + //Compruebo la notificación + cy.get('.q-notification').should('be.visible'); + cy.get(userCompany).should('have.value', 'Warehouse Two'); + + //Restauro el valor + cy.get(userCompany).click(); + getOption(1); + }); +}); + +function getOption(index) { + cy.waitForElement('[role="listbox"]'); + const option = `[role="listbox"] .q-item:nth-child(${index})`; + cy.get(option).click(); +} diff --git a/test/cypress/integration/vnComponent/vnLocation.spec.js b/test/cypress/integration/vnComponent/vnLocation.spec.js index 1872d3591..78dc38899 100644 --- a/test/cypress/integration/vnComponent/vnLocation.spec.js +++ b/test/cypress/integration/vnComponent/vnLocation.spec.js @@ -3,25 +3,90 @@ describe('VnLocation', () => { const dialogInputs = '.q-dialog label input'; const createLocationButton = '.q-form > .q-card > .vn-row:nth-child(6) .--add-icon'; const inputLocation = '.q-form input[aria-label="Location"]'; + const createForm = { + prefix: '.q-dialog__inner > .column > #formModel > .q-card', + sufix: ' .q-field__inner > .q-field__control', + }; + describe('CreateFormDialog ', () => { + beforeEach(() => { + cy.viewport(1280, 720); + cy.login('developer'); + cy.visit('/#/supplier/567/fiscal-data', { timeout: 7000 }); + cy.waitForElement('.q-card'); + cy.get(createLocationButton).click(); + }); + it('should filter provinces based on selected country', () => { + // Select a country + cy.selectOption( + `${createForm.prefix} > :nth-child(5) > .q-field:nth-child(5)> ${createForm.sufix}`, + 'Ecuador' + ); + // Verify that provinces are filtered + cy.get( + `${createForm.prefix} > :nth-child(5) > .q-field:nth-child(3)> ${createForm.sufix}` + ).should('have.length', 1); + + // Verify that towns are filtered + cy.get( + `${createForm.prefix} > :nth-child(4) > .q-field:nth-child(3)> ${createForm.sufix}` + ).should('have.length', 1); + }); + + it('should filter towns based on selected province', () => { + // Select a country + cy.selectOption( + `${createForm.prefix} > :nth-child(5) > .q-field:nth-child(3)> ${createForm.sufix}`, + 'Ecuador' + ); + // Verify that provinces are filtered + cy.get( + `${createForm.prefix} > :nth-child(5) > .q-field:nth-child(3)> ${createForm.sufix}` + ).should('have.length', 1); + + // Verify that towns are filtered + cy.get( + `${createForm.prefix} > :nth-child(4) > .q-field:nth-child(3)> ${createForm.sufix}` + ).should('have.length', 1); + }); + it('should pass selected country', () => { + // Select a country + const country = 'Ecuador'; + const province = 'Province five'; + cy.selectOption( + `${createForm.prefix} > :nth-child(5) > .q-field:nth-child(5)> ${createForm.sufix}`, + country + ); + cy.selectOption( + `${createForm.prefix} > :nth-child(4) > .q-select > ${createForm.sufix}`, + province + ); + cy.get( + `${createForm.prefix} > :nth-child(4) > .q-select > ${createForm.sufix} > :nth-child(3) > .q-icon` + ).click(); + cy.get( + `#q-portal--dialog--4 > .q-dialog > ${createForm.prefix} > .vn-row > .q-select > ${createForm.sufix} > :nth-child(1) input` + ).should('have.value', province); + }); + }); describe('Worker Create', () => { beforeEach(() => { cy.viewport(1280, 720); cy.login('developer'); cy.visit('/#/worker/create', { timeout: 5000 }); cy.waitForElement('.q-card'); + cy.get(inputLocation).click(); }); it('Show all options', function () { - cy.get(inputLocation).click(); cy.get(locationOptions).should('have.length.at.least', 5); }); it('input filter location as "al"', function () { - cy.get(inputLocation).click(); + // cy.get(inputLocation).click(); cy.get(inputLocation).clear(); cy.get(inputLocation).type('al'); cy.get(locationOptions).should('have.length.at.least', 4); }); it('input filter location as "ecuador"', function () { - cy.get(inputLocation).click(); + // cy.get(inputLocation).click(); cy.get(inputLocation).clear(); cy.get(inputLocation).type('ecuador'); cy.get(locationOptions).should('have.length.at.least', 1); @@ -63,13 +128,11 @@ describe('VnLocation', () => { cy.get(dialogInputs).eq(0).clear(); cy.get(dialogInputs).eq(0).type(postCode); cy.selectOption( - '.q-dialog__inner > .column > #formModel > .q-card > :nth-child(4) > .q-select > .q-field__inner > .q-field__control ', + `${createForm.prefix} > :nth-child(4) > .q-select > ${createForm.sufix}`, province ); cy.get('.q-mt-lg > .q-btn--standard').click(); - cy.get('.q-dialog__inner > .column > #formModel > .q-card').should( - 'not.exist' - ); + cy.get(`${createForm.prefix}`).should('not.exist'); checkVnLocation(postCode, province); }); it('Create city', () => { @@ -79,7 +142,7 @@ describe('VnLocation', () => { cy.get(dialogInputs).eq(0).type(postCode); // city create button cy.get( - '.q-dialog__inner > .column > #formModel > .q-card > :nth-child(4) > .q-select > .q-field__inner > .q-field__control > :nth-child(2) > .q-icon' + `${createForm.prefix} > :nth-child(4) > .q-select > ${createForm.sufix} > :nth-child(2) > .q-icon` ).click(); cy.selectOption('#q-portal--dialog--2 .q-select', 'one'); cy.get('#q-portal--dialog--2 .q-input').type(province); @@ -89,9 +152,7 @@ describe('VnLocation', () => { }); function checkVnLocation(postCode, province) { - cy.get('.q-dialog__inner > .column > #formModel > .q-card').should( - 'not.exist' - ); + cy.get(`${createForm.prefix}`).should('not.exist'); cy.get('.q-form > .q-card > .vn-row:nth-child(6)') .find('input') .invoke('val') diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 43788f59f..83f45b721 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -248,3 +248,9 @@ Cypress.Commands.add('validateContent', (selector, expectedValue) => { Cypress.Commands.add('openActionsDescriptor', () => { cy.get('.header > :nth-child(3) > .q-btn__content > .q-icon').click(); }); + +Cypress.Commands.add('openUserPanel', () => { + cy.get( + '.column > .q-avatar > .q-avatar__content > .q-img > .q-img__container > .q-img__image' + ).click(); +});
{{ t('route.Select the starting date') }}
{{ t('Select the starting date') }}