diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue index e3cb2874a..769ba9959 100644 --- a/src/components/common/VnDmsList.vue +++ b/src/components/common/VnDmsList.vue @@ -35,7 +35,7 @@ const $props = defineProps({ downloadModel: { type: String, required: false, - default: null, + default: undefined, }, defaultDmsCode: { type: String, diff --git a/src/components/common/VnInputTime.vue b/src/components/common/VnInputTime.vue index 4bd75eeaf..0b271dee7 100644 --- a/src/components/common/VnInputTime.vue +++ b/src/components/common/VnInputTime.vue @@ -2,6 +2,7 @@ import { computed, ref } from 'vue'; import { useI18n } from 'vue-i18n'; import isValidDate from 'filters/isValidDate'; +import VnInput from 'components/common/VnInput.vue'; const props = defineProps({ modelValue: { @@ -74,7 +75,7 @@ const styleAttrs = computed(() => { @click="isPopupOpen = true" > - + { // Clases para modificar el color de fecha seleccionada en componente QCalendarMonth .q-dark div .q-calendar-mini .q-calendar-month__day.q-selected .q-calendar__button { background-color: $primary !important; - color: white !important; } .q-calendar-mini .q-calendar-month__day.q-selected .q-calendar__button { background-color: $primary !important; - color: white !important; } .q-calendar-month__head--weekday { @@ -112,7 +110,6 @@ const containerClasses = computed(() => { cursor: pointer; } } - .q-calendar-month__week--days > div:nth-child(6), .q-calendar-month__week--days > div:nth-child(7) { // Cambia el color de los días sábado y domingo diff --git a/src/css/app.scss b/src/css/app.scss index 2e524e693..fd5f29600 100644 --- a/src/css/app.scss +++ b/src/css/app.scss @@ -169,6 +169,13 @@ select:-webkit-autofill { /* q-notification row items-stretch q-notification--standard bg-negative text-white */ +.q-card, +.q-table, +.q-table__bottom, +.q-drawer { + background-color: var(--vn-section-color); +} + input[type='number'] { -moz-appearance: textfield; } diff --git a/src/filters/dateRange.js b/src/filters/dateRange.js index 4c0cfe654..7aa2869e5 100644 --- a/src/filters/dateRange.js +++ b/src/filters/dateRange.js @@ -1,7 +1,7 @@ export default function dateRange(value) { const minHour = new Date(value); minHour.setHours(0, 0, 0, 0); - const maxHour = new Date(value); + const maxHour = new Date(); maxHour.setHours(23, 59, 59, 59); return [minHour, maxHour]; diff --git a/src/pages/Customer/Defaulter/CustomerDefaulter.vue b/src/pages/Customer/Defaulter/CustomerDefaulter.vue index c1e537c0a..5ed6af1a9 100644 --- a/src/pages/Customer/Defaulter/CustomerDefaulter.vue +++ b/src/pages/Customer/Defaulter/CustomerDefaulter.vue @@ -14,9 +14,10 @@ import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.v import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnInput from 'src/components/common/VnInput.vue'; import CustomerDefaulterAddObservation from './CustomerDefaulterAddObservation.vue'; +import axios from 'axios'; const stateStore = useStateStore(); -const { t, locale } = useI18n(); +const { t } = useI18n(); const quasar = useQuasar(); const dataRef = ref(null); @@ -26,7 +27,7 @@ const selected = ref([]); const tableColumnComponents = { client: { component: QBtn, - props: () => ({ flat: true, color: 'blue', noCaps: true }), + props: () => ({ flat: true, class: 'link', noCaps: true }), event: () => {}, }, isWorker: { @@ -39,7 +40,12 @@ const tableColumnComponents = { }, salesPerson: { component: QBtn, - props: () => ({ flat: true, color: 'blue', noCaps: true }), + props: () => ({ flat: true, class: 'link', noCaps: true }), + event: () => {}, + }, + department: { + component: 'span', + props: () => {}, event: () => {}, }, country: { @@ -59,7 +65,7 @@ const tableColumnComponents = { }, author: { component: QBtn, - props: () => ({ flat: true, color: 'blue', noCaps: true }), + props: () => ({ flat: true, class: 'link', noCaps: true }), event: () => {}, }, lastObservation: { @@ -82,6 +88,16 @@ const tableColumnComponents = { props: () => {}, event: () => {}, }, + finished: { + component: QCheckbox, + + props: (prop) => ({ + disable: true, + 'model-value': prop.value, + class: 'disabled-checkbox', + }), + event: () => {}, + }, }; const columns = computed(() => [ @@ -105,6 +121,13 @@ const columns = computed(() => [ name: 'salesPerson', sortable: true, }, + { + align: 'left', + field: 'departmentName', + label: t('Department'), + name: 'department', + sortable: true, + }, { align: 'left', field: 'country', @@ -166,6 +189,12 @@ const columns = computed(() => [ name: 'from', sortable: true, }, + { + align: 'left', + field: 'finished', + label: t('Has recover'), + name: 'finished', + }, ]); const viewAddObservation = (rowsSelected) => { @@ -178,7 +207,39 @@ const viewAddObservation = (rowsSelected) => { }); }; -const onFetch = (data) => { +const departments = ref(new Map()); + +const onFetch = async (data) => { + const salesPersonFks = data.map((item) => item.salesPersonFk); + const departmentNames = salesPersonFks.map(async (salesPersonFk) => { + try { + const { data: workerDepartment } = await axios.get( + `WorkerDepartments/${salesPersonFk}` + ); + const { data: department } = await axios.get( + `Departments/${workerDepartment.departmentFk}` + ); + departments.value.set(salesPersonFk, department.name); + } catch (error) { + console.error('Err: ', error); + } + }); + const recoveryData = await axios.get('Recoveries'); + + const recoveries = recoveryData.data.map(({ clientFk, finished }) => ({ + clientFk, + finished, + })); + + await Promise.all(departmentNames); + + data.forEach((item) => { + item.departmentName = departments.value.get(item.salesPersonFk); + item.isWorker = item.businessTypeFk === 'worker'; + const recovery = recoveries.find(({ clientFk }) => clientFk === item.clientFk); + item.finished = recovery?.finished === null; + }); + for (const element of data) element.isWorker = element.businessTypeFk === 'worker'; balanceDueTotal.value = data.reduce((acc, { amount = 0 }) => acc + amount, 0); @@ -191,6 +252,7 @@ function exprBuilder(param, value) { case 'creditInsurance': case 'amount': case 'workerFk': + case 'departmentFk': case 'countryFk': case 'payMethod': case 'salesPersonFk': @@ -243,7 +305,6 @@ function exprBuilder(param, value) { - - + - + + @@ -85,6 +86,7 @@ const authors = ref(); rounded use-input v-model="params.salesPersonFk" + @update:model-value="searchFn()" /> @@ -108,6 +110,7 @@ const authors = ref(); rounded use-input v-model="params.countryFk" + @update:model-value="searchFn()" /> @@ -153,6 +156,7 @@ const authors = ref(); rounded use-input v-model="params.workerFk" + @update:model-value="searchFn()" /> diff --git a/src/pages/Item/locale/en.yml b/src/pages/Item/locale/en.yml index 99dbeb9fd..363e9fcbe 100644 --- a/src/pages/Item/locale/en.yml +++ b/src/pages/Item/locale/en.yml @@ -81,3 +81,10 @@ itemTags: searchbar: label: Search item info: Search by item id +itemType: + shared: + code: Code + name: Name + worker: Worker + category: Category + temperature: Temperature diff --git a/src/pages/Item/locale/es.yml b/src/pages/Item/locale/es.yml index 60e589932..3ef040098 100644 --- a/src/pages/Item/locale/es.yml +++ b/src/pages/Item/locale/es.yml @@ -81,3 +81,10 @@ itemTags: searchbar: label: Buscar artículo info: Buscar por id de artículo +itemType: + shared: + code: Código + name: Nombre + worker: Trabajador + category: Reino + temperature: Temperatura diff --git a/src/pages/Route/Card/RouteForm.vue b/src/pages/Route/Card/RouteForm.vue index 892a0333c..7c25c6a02 100644 --- a/src/pages/Route/Card/RouteForm.vue +++ b/src/pages/Route/Card/RouteForm.vue @@ -28,7 +28,7 @@ const defaultInitialData = { workerFk: null, isOk: false, }; - +const maxDistance = ref(); const workerList = ref([]); const agencyList = ref([]); const vehicleList = ref([]); @@ -81,12 +81,7 @@ const onSave = (data, response) => { }; - - - - - - + { @on-fetch="(data) => (vehicleList = data)" auto-load /> + (maxDistance = kmMax)" + auto-load + sort-by="id ASC" + /> { - + diff --git a/src/pages/Route/RouteList.vue b/src/pages/Route/RouteList.vue index 73801e13a..3e7c14dd1 100644 --- a/src/pages/Route/RouteList.vue +++ b/src/pages/Route/RouteList.vue @@ -12,6 +12,7 @@ import VnInput from 'components/common/VnInput.vue'; import VnInputTime from 'components/common/VnInputTime.vue'; import axios from 'axios'; import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue'; +import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue'; import RouteFilter from 'pages/Route/Card/RouteFilter.vue'; import RouteSummary from 'pages/Route/Card/RouteSummary.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; @@ -19,6 +20,7 @@ import { useSession } from 'composables/useSession'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import RouteListTicketsDialog from 'pages/Route/Card/RouteListTicketsDialog.vue'; import { useQuasar } from 'quasar'; +import { useArrayData } from 'composables/useArrayData'; const stateStore = useStateStore(); const { t } = useI18n(); @@ -26,10 +28,7 @@ const { validate } = useValidator(); const quasar = useQuasar(); const session = useSession(); const { viewSummary } = useSummaryDialog(); - -onMounted(() => (stateStore.rightDrawer = true)); -onUnmounted(() => (stateStore.rightDrawer = false)); - +const visibleColumns = ref([]); const selectedRows = ref([]); const columns = computed(() => [ { @@ -83,14 +82,14 @@ const columns = computed(() => [ }, { name: 'started', - label: t('Hour started'), + label: t('hourStarted'), field: (row) => toHour(row.started), sortable: true, align: 'left', }, { name: 'finished', - label: t('Hour finished'), + label: t('hourFinished'), field: (row) => toHour(row.finished), sortable: true, align: 'left', @@ -109,7 +108,10 @@ const columns = computed(() => [ align: 'right', }, ]); - +const arrayData = useArrayData('EntryLatestBuys', { + url: 'Buys/latestBuysFilter', + order: ['itemFk DESC'], +}); const refreshKey = ref(0); const workers = ref([]); const agencyList = ref([]); @@ -121,7 +123,7 @@ const updateRoute = async (route) => { return err; } }; - +const allColumnNames = ref([]); const confirmationDialog = ref(false); const startingDate = ref(null); @@ -174,6 +176,13 @@ const openTicketsDialog = (id) => { }) .onOk(() => refreshKey.value++); }; + +onMounted(async () => { + stateStore.rightDrawer = true; + allColumnNames.value = columns.value.map((col) => col.name); + await arrayData.fetch({ append: false }); +}); +onUnmounted(() => (stateStore.rightDrawer = false)); @@ -231,7 +240,16 @@ const openTicketsDialog = (id) => { (agencyList = data)" auto-load /> (vehicleList = data)" auto-load /> - + + + + { :key="refreshKey" data-key="RouteList" url="Routes/filter" - :order="['created DESC', 'id DESC']" + :order="['created ASC', 'started ASC', 'id ASC']" :limit="20" auto-load > @@ -281,9 +299,10 @@ const openTicketsDialog = (id) => { row-key="id" selection="multiple" :rows-per-page-options="[0]" + :visible-columns="visibleColumns" hide-pagination - :pagination="{ sortBy: 'ID', descending: true }" :no-data-label="t('globals.noResults')" + style="max-height: 82vh" > @@ -336,7 +355,7 @@ const openTicketsDialog = (id) => { - + { - + { - + { - + { - + {{ @@ -486,15 +505,18 @@ const openTicketsDialog = (id) => { .table-actions { gap: 12px; } - -.lock-icon-cell { - text-align: center; - margin-left: -20%; +th:last-child, +td:last-child { + background-color: var(--vn-section-color); + position: sticky; + right: 0; } en: newRoute: New Route + hourStarted: Started hour + hourFinished: Finished hour es: ID: ID Worker: Trabajador diff --git a/src/pages/Route/RouteMain.vue b/src/pages/Route/RouteMain.vue index 66ce78f23..bbf19068e 100644 --- a/src/pages/Route/RouteMain.vue +++ b/src/pages/Route/RouteMain.vue @@ -1,8 +1,10 @@ diff --git a/src/pages/Route/RouteTickets.vue b/src/pages/Route/RouteTickets.vue index eb2035011..0efcd6e0c 100644 --- a/src/pages/Route/RouteTickets.vue +++ b/src/pages/Route/RouteTickets.vue @@ -222,11 +222,6 @@ const openSmsDialog = async () => { - - - - - (routeEntity = data)" auto-load diff --git a/src/pages/Worker/Card/WorkerCalendar.vue b/src/pages/Worker/Card/WorkerCalendar.vue index 2e525aa30..ea6e62683 100644 --- a/src/pages/Worker/Card/WorkerCalendar.vue +++ b/src/pages/Worker/Card/WorkerCalendar.vue @@ -82,6 +82,7 @@ const onFetchAbsences = (data) => { type: type.code, absenceId: absence.id, isFestive: false, + isHoliday: false, }); }); } @@ -170,23 +171,6 @@ watch([year, businessFk], () => refreshData()); ref="WorkerFreelanceRef" auto-load /> - - - - - - {{ t('globals.collapseMenu') }} - - - - - { } const date = new Date(year, month - 1, day); - if (!event.absenceId) createEvent(date); + if (!event?.absenceId) createEvent(date); else if (event.type == props.absenceType.code) deleteEvent(event, date); else editEvent(event); }; @@ -136,24 +136,31 @@ const getEventByTimestamp = ({ year, month, day }) => { return props.events[stamp] || null; }; +const isFestive = (timestamp) => { + const event = getEventByTimestamp(timestamp); + if (!event) return false; + + const { isFestive } = event; + return isFestive; +}; const getEventAttrs = (timestamp) => { const event = getEventByTimestamp(timestamp); if (!event) return {}; - const { name, color, isFestive } = event; + const { name, color, isFestive, type } = event; // Atributos a asignar a cada slot que representa un evento en el calendario const attrs = { title: name, - style: color ? `background-color: ${color};` : '', + style: color ? `background-color: ${color};` : ``, label: timestamp.day, }; if (isFestive) { attrs.class = '--festive'; - attrs.label = event.absenceId ? timestamp.day : ''; - } + attrs.label = event.absenceId ?? timestamp.day; + } else attrs.class = `--${type}`; return attrs; }; @@ -162,7 +169,6 @@ const isToday = (timestamp) => { const { year, month, day } = timestamp; return todayTimestamp.value === new Date(year, month - 1, day).getTime(); }; - onBeforeMount(() => { updateSelectedDate(_year.value); }); @@ -203,7 +209,6 @@ watch(_year, (newValue) => { { diff --git a/src/pages/Zone/locale/en.yml b/src/pages/Zone/locale/en.yml index dd79f6ac4..bae89fda9 100644 --- a/src/pages/Zone/locale/en.yml +++ b/src/pages/Zone/locale/en.yml @@ -5,7 +5,7 @@ zone: zoneCreate: Create zone locations: Locations deliveryDays: Delivery days - upcomingList: Upcoming deliveries + upcomingDeliveries: Upcoming deliveries warehouses: Warehouses list: clone: Clone @@ -64,3 +64,7 @@ warehouses: deleteSubtitle: Are you sure you want to continue? warehouse: Warehouse add: Add +upcomingDeliveries: + province: Province + closing: Closing + id: Id diff --git a/src/pages/Zone/locale/es.yml b/src/pages/Zone/locale/es.yml index 43b636d4f..d74238a6e 100644 --- a/src/pages/Zone/locale/es.yml +++ b/src/pages/Zone/locale/es.yml @@ -5,7 +5,7 @@ zone: zoneCreate: Nueva zona locations: Localizaciones deliveryDays: Días de entrega - upcomingList: Próximos repartos + upcomingDeliveries: Próximos repartos warehouses: Almacenes list: clone: Clonar @@ -66,3 +66,7 @@ warehouses: deleteSubtitle: ¿Seguro que quieres continuar? warehouse: Almacén add: Añadir +upcomingDeliveries: + province: Provincia + closing: Cierre + id: Id diff --git a/src/router/modules/zone.js b/src/router/modules/zone.js index b79c430e5..c355856b1 100644 --- a/src/router/modules/zone.js +++ b/src/router/modules/zone.js @@ -11,7 +11,7 @@ export default { component: RouterView, redirect: { name: 'ZoneMain' }, menus: { - main: ['ZoneList', 'ZoneDeliveryDays', 'ZoneUpcomingList'], + main: ['ZoneList', 'ZoneDeliveryDays', 'ZoneUpcomingDeliveries'], card: ['ZoneBasicData', 'ZoneWarehouses', 'ZoneHistory', 'ZoneLocations'], }, children: [ @@ -57,6 +57,24 @@ export default { }, component: () => import('src/pages/Zone/ZoneCreate.vue'), }, + // { + // path: 'counter', + // name: 'ZoneCounter', + // meta: { + // title: 'zoneCounter', + // icon: 'add_circle', + // }, + // component: () => import('src/pages/Zone/ZoneCounter.vue'), + // }, + { + name: 'ZoneUpcomingDeliveries', + path: 'upcoming-deliveries', + meta: { + title: 'upcomingDeliveries', + icon: 'vn:calendar', + }, + component: () => import('src/pages/Zone/ZoneUpcoming.vue'), + }, ], }, {