diff --git a/src/boot/vnDate.js b/src/boot/vnDate.js
index 33d5ac27f..8f18e2400 100644
--- a/src/boot/vnDate.js
+++ b/src/boot/vnDate.js
@@ -1,4 +1,6 @@
import { boot } from 'quasar/wrappers';
+import { date as quasarDate } from 'quasar';
+const { formatDate } = quasarDate;
export default boot(() => {
Date.vnUTC = () => {
@@ -25,4 +27,34 @@ export default boot(() => {
const date = new Date(Date.vnUTC());
return new Date(date.getFullYear(), date.getMonth() + 1, 0);
};
+
+ Date.getCurrentDateTimeFormatted = (
+ options = {
+ startOfDay: false,
+ endOfDay: true,
+ iso: true,
+ mask: 'DD-MM-YYYY HH:mm',
+ },
+ ) => {
+ const date = Date.vnUTC();
+ if (options.startOfDay) {
+ date.setHours(0, 0, 0);
+ }
+ if (options.endOfDay) {
+ date.setHours(23, 59, 0);
+ }
+ if (options.iso) {
+ return date.toISOString();
+ }
+ return formatDate(date, options.mask);
+ };
+
+ Date.convertToISODateTime = (dateTimeStr) => {
+ const [datePart, timePart] = dateTimeStr.split(' ');
+ const [day, month, year] = datePart.split('-');
+ const [hours, minutes] = timePart.split(':');
+
+ const isoDate = new Date(year, month - 1, day, hours, minutes);
+ return isoDate.toISOString();
+ };
});
diff --git a/src/components/EditPictureForm.vue b/src/components/EditPictureForm.vue
index 1f158e785..29377b559 100644
--- a/src/components/EditPictureForm.vue
+++ b/src/components/EditPictureForm.vue
@@ -140,7 +140,7 @@ const updatePhotoPreview = (value) => {
img.onerror = () => {
notify(
t("This photo provider doesn't allow remote downloads"),
- 'negative'
+ 'negative',
);
};
}
@@ -219,11 +219,7 @@ const makeRequest = async () => {
color="primary"
class="cursor-pointer"
@click="rotateLeft()"
- >
-
-
+ />
@@ -233,11 +229,7 @@ const makeRequest = async () => {
color="primary"
class="cursor-pointer"
@click="rotateRight()"
- >
-
-
+ />
@@ -265,7 +257,6 @@ const makeRequest = async () => {
class="cursor-pointer q-mr-sm"
@click="openInputFile()"
>
-
{{
diff --git a/src/components/NavBar.vue b/src/components/NavBar.vue
index 7329ddae2..65139e1e5 100644
--- a/src/components/NavBar.vue
+++ b/src/components/NavBar.vue
@@ -69,7 +69,7 @@ const refresh = () => window.location.reload();
'no-visible': !stateQuery.isLoading().value,
}"
size="sm"
- data-cy="loading-spinner"
+ data-cy="navBar-spinner"
/>
diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 1b7ec825d..7e9ab85cb 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -751,6 +751,7 @@ const rowCtrlClickFunction = computed(() => {
withFilters
"
:column="col"
+ :data-cy="`column-filter-${col.name}`"
:show-title="true"
:data-key="$attrs['data-key']"
v-model="params[columnName(col)]"
diff --git a/src/components/common/VnAccountNumber.vue b/src/components/common/VnAccountNumber.vue
index 8bff3e261..5bce49459 100644
--- a/src/components/common/VnAccountNumber.vue
+++ b/src/components/common/VnAccountNumber.vue
@@ -8,7 +8,8 @@ const model = defineModel({ prop: 'modelValue' });
diff --git a/src/components/common/VnInputDateTime.vue b/src/components/common/VnInputDateTime.vue
new file mode 100644
index 000000000..b239d528a
--- /dev/null
+++ b/src/components/common/VnInputDateTime.vue
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ es:
+ Open date: Abrir fecha
+
diff --git a/src/components/common/__tests__/VnInputDateTime.spec.js b/src/components/common/__tests__/VnInputDateTime.spec.js
new file mode 100644
index 000000000..23132d77d
--- /dev/null
+++ b/src/components/common/__tests__/VnInputDateTime.spec.js
@@ -0,0 +1,81 @@
+import { createWrapper } from 'app/test/vitest/helper.js';
+import { describe, it, expect, beforeAll } from 'vitest';
+import VnInputDateTime from 'components/common/VnInputDateTime.vue';
+import vnDateBoot from 'src/boot/vnDate';
+
+let vm;
+let wrapper;
+
+beforeAll(() => {
+ // Initialize the vnDate boot
+ vnDateBoot();
+});
+function generateWrapper(date, outlined, showEvent) {
+ wrapper = createWrapper(VnInputDateTime, {
+ props: {
+ modelValue: date,
+ isOutlined: outlined,
+ showEvent: showEvent,
+ },
+ });
+ wrapper = wrapper.wrapper;
+ vm = wrapper.vm;
+}
+
+describe('VnInputDateTime', () => {
+ describe('selectedDate', () => {
+ it('formats a valid datetime correctly', async () => {
+ generateWrapper('2023-12-25T10:30:00', false, true);
+ await vm.$nextTick();
+ expect(vm.selectedDate).toBe('25-12-2023 10:30');
+ });
+
+ it('handles null date value', async () => {
+ generateWrapper(null, false, true);
+ await vm.$nextTick();
+ expect(vm.selectedDate).toBeInstanceOf(Date);
+ });
+
+ it('updates the model value when a new datetime is set', async () => {
+ vm.selectedDate = '31-12-2023 15:45';
+ await vm.$nextTick();
+ expect(wrapper.emitted()['update:modelValue']).toBeTruthy();
+ });
+ });
+
+ describe('styleAttrs', () => {
+ it('should return empty styleAttrs when isOutlined is false', async () => {
+ generateWrapper('2023-12-25T10:30:00', false, true);
+ await vm.$nextTick();
+ expect(vm.styleAttrs).toEqual({});
+ });
+
+ it('should set styleAttrs when isOutlined is true', async () => {
+ generateWrapper('2023-12-25T10:30:00', true, true);
+ await vm.$nextTick();
+ expect(vm.styleAttrs).toEqual({
+ dense: true,
+ outlined: true,
+ rounded: true,
+ });
+ });
+ });
+
+ describe('component rendering', () => {
+ it('should render date and time icons', async () => {
+ generateWrapper('2023-12-25T10:30:00', false, true);
+ await vm.$nextTick();
+ const icons = wrapper.findAllComponents({ name: 'QIcon' });
+ expect(icons.length).toBe(2);
+ expect(icons[0].props('name')).toBe('today');
+ expect(icons[1].props('name')).toBe('access_time');
+ });
+
+ it('should render popup proxies for date and time', async () => {
+ generateWrapper('2023-12-25T10:30:00', false, true);
+ await vm.$nextTick();
+ const popups = wrapper.findAllComponents({ name: 'QPopupProxy' });
+ expect(popups.length).toBe(2);
+ });
+ });
+});
diff --git a/src/components/ui/EntityDescriptor.vue b/src/components/ui/EntityDescriptor.vue
index a5dced551..e6adaa5f7 100644
--- a/src/components/ui/EntityDescriptor.vue
+++ b/src/components/ui/EntityDescriptor.vue
@@ -44,7 +44,8 @@ onBeforeMount(async () => {
});
// It enables to load data only once if the module is the same as the dataKey
- if (!isSameDataKey.value || !route.params.id) await getData();
+ if (!isSameDataKey.value || !route.params.id || $props.id !== route.params.id)
+ await getData();
watch(
() => [$props.url, $props.filter],
async () => {
@@ -58,7 +59,8 @@ async function getData() {
store.filter = $props.filter ?? {};
isLoading.value = true;
try {
- const { data } = await arrayData.fetch({ append: false, updateRouter: false });
+ await arrayData.fetch({ append: false, updateRouter: false });
+ const { data } = store;
state.set($props.dataKey, data);
emit('onFetch', data);
} finally {
diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue
index dc9e4e776..6460499b0 100644
--- a/src/components/ui/VnFilterPanel.vue
+++ b/src/components/ui/VnFilterPanel.vue
@@ -212,6 +212,7 @@ const getLocale = (label) => {
color="primary"
style="position: fixed; z-index: 1; right: 0; bottom: 0"
icon="search"
+ data-cy="vnFilterPanel_search"
@click="search()"
>
@@ -229,6 +230,7 @@ const getLocale = (label) => {
{
-
+
diff --git a/src/pages/Item/components/ItemProposal.vue b/src/pages/Item/components/ItemProposal.vue
index d2dbea7b3..bd0fdc0c2 100644
--- a/src/pages/Item/components/ItemProposal.vue
+++ b/src/pages/Item/components/ItemProposal.vue
@@ -6,10 +6,12 @@ import { toCurrency } from 'filters/index';
import VnStockValueDisplay from 'src/components/ui/VnStockValueDisplay.vue';
import VnTable from 'src/components/VnTable/VnTable.vue';
import axios from 'axios';
-import notifyResults from 'src/utils/notifyResults';
+import { displayResults } from 'src/pages/Ticket/Negative/composables/notifyResults';
import FetchData from 'components/FetchData.vue';
+import { useState } from 'src/composables/useState';
const MATCH = 'match';
+const { notifyResults } = displayResults();
const { t } = useI18n();
const $props = defineProps({
@@ -18,14 +20,20 @@ const $props = defineProps({
required: true,
default: () => {},
},
+ filter: {
+ type: Object,
+ required: true,
+ default: () => {},
+ },
replaceAction: {
type: Boolean,
- required: false,
+ required: true,
default: false,
},
+
sales: {
type: Array,
- required: false,
+ required: true,
default: () => [],
},
});
@@ -36,6 +44,8 @@ const proposalTableRef = ref(null);
const sale = computed(() => $props.sales[0]);
const saleFk = computed(() => sale.value.saleFk);
const filter = computed(() => ({
+ where: $props.filter,
+
itemFk: $props.itemLack.itemFk,
sales: saleFk.value,
}));
@@ -228,11 +238,15 @@ async function handleTicketConfig(data) {
url="TicketConfigs"
:filter="{ fields: ['lackAlertPrice'] }"
@on-fetch="handleTicketConfig"
- auto-load
+ >
+
import ItemProposal from './ItemProposal.vue';
import { useDialogPluginComponent } from 'quasar';
-
const $props = defineProps({
itemLack: {
type: Object,
required: true,
default: () => {},
},
+ filter: {
+ type: Object,
+ required: true,
+ default: () => {},
+ },
replaceAction: {
type: Boolean,
required: false,
@@ -31,7 +35,7 @@ defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.h
- {{ $t('Item proposal') }}
+ {{ $t('itemProposal') }}
diff --git a/src/pages/Monitor/MonitorClientsActions.vue b/src/pages/Monitor/MonitorClientsActions.vue
index 821773bbf..a6ac3ab0b 100644
--- a/src/pages/Monitor/MonitorClientsActions.vue
+++ b/src/pages/Monitor/MonitorClientsActions.vue
@@ -8,14 +8,14 @@ import VnRow from 'src/components/ui/VnRow.vue';
class="q-pa-md"
:style="{ 'flex-direction': $q.screen.lt.lg ? 'column' : 'row', gap: '0px' }"
>
-
+
-
+
useOpenURL(`#/order/${id}/summary`);
-
-
- {{ toDateFormat(row.date_send) }}
-
-
+
diff --git a/src/pages/Monitor/Ticket/MonitorTicketFilter.vue b/src/pages/Monitor/Ticket/MonitorTicketFilter.vue
index 1cadd4cb4..1bc194a5c 100644
--- a/src/pages/Monitor/Ticket/MonitorTicketFilter.vue
+++ b/src/pages/Monitor/Ticket/MonitorTicketFilter.vue
@@ -9,6 +9,7 @@ import VnInput from 'src/components/common/VnInput.vue';
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
import FetchData from 'src/components/FetchData.vue';
import { dateRange } from 'src/filters';
+import VnCheckbox from 'src/components/common/VnCheckbox.vue';
defineProps({ dataKey: { type: String, required: true } });
const { t, te } = useI18n();
@@ -209,7 +210,7 @@ const getLocale = (label) => {
- {
- {
-
import { ref, computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
-import FetchData from 'components/FetchData.vue';
import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
@@ -168,9 +167,11 @@ const columns = computed(() => [
component: 'select',
name: 'provinceFk',
attrs: {
- options: provinceOpts.value,
- 'option-value': 'id',
- 'option-label': 'name',
+ url: 'Provinces',
+ fields: ['id', 'name'],
+ sortBy: ['name ASC'],
+ optionValue: 'id',
+ optionLabel: 'name',
dense: true,
},
},
@@ -183,9 +184,11 @@ const columns = computed(() => [
component: 'select',
name: 'stateFk',
attrs: {
- options: stateOpts.value,
- 'option-value': 'id',
- 'option-label': 'name',
+ sortBy: ['name ASC'],
+ url: 'States',
+ fields: ['id', 'name'],
+ optionValue: 'id',
+ optionLabel: 'name',
dense: true,
},
},
@@ -212,9 +215,12 @@ const columns = computed(() => [
component: 'select',
name: 'zoneFk',
attrs: {
- options: zoneOpts.value,
- 'option-value': 'id',
- 'option-label': 'name',
+ url: 'Zones',
+ fields: ['id', 'name'],
+ sortBy: ['name ASC'],
+
+ optionValue: 'id',
+ optionLabel: 'name',
dense: true,
},
},
@@ -225,11 +231,12 @@ const columns = computed(() => [
align: 'left',
columnFilter: {
component: 'select',
- url: 'PayMethods',
attrs: {
- options: PayMethodOpts.value,
- optionValue: 'id',
+ url: 'PayMethods',
+ fields: ['id', 'name'],
+ sortBy: ['id ASC'],
optionLabel: 'name',
+ optionValue: 'id',
dense: true,
},
},
@@ -254,7 +261,9 @@ const columns = computed(() => [
columnFilter: {
component: 'select',
attrs: {
- options: DepartmentOpts.value,
+ url: 'Departments',
+ fields: ['id', 'name'],
+ sortBy: ['id ASC'],
dense: true,
},
},
@@ -265,11 +274,12 @@ const columns = computed(() => [
align: 'left',
columnFilter: {
component: 'select',
- url: 'ItemPackingTypes',
attrs: {
- options: ItemPackingTypeOpts.value,
- 'option-value': 'code',
- 'option-label': 'code',
+ url: 'ItemPackingTypes',
+ fields: ['code'],
+ sortBy: ['code ASC'],
+ optionValue: 'code',
+ optionCode: 'code',
dense: true,
},
},
@@ -324,60 +334,6 @@ const totalPriceColor = (ticket) => {
const openTab = (id) => useOpenURL(`#/ticket/${id}/sale`);
- (provinceOpts = data)"
- />
- (stateOpts = data)"
- />
- (zoneOpts = data)"
- />
- (ItemPackingTypeOpts = data)"
- />
- (DepartmentOpts = data)"
- />
- (PayMethodOpts = data)"
- />
diff --git a/src/pages/Route/Card/RouteSummary.vue b/src/pages/Route/Card/RouteSummary.vue
index 86bdbb5c5..7e345d69a 100644
--- a/src/pages/Route/Card/RouteSummary.vue
+++ b/src/pages/Route/Card/RouteSummary.vue
@@ -233,10 +233,10 @@ const ticketColumns = ref([
-
-
+
+
- {{ value }}
+ {{ row.clientFk }}
diff --git a/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue b/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue
index 76191b099..a58c934f8 100644
--- a/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue
+++ b/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue
@@ -101,7 +101,7 @@ const onNextStep = async () => {
t('basicData.negativesConfirmMessage'),
submitWithNegatives,
);
- else submit();
+ else await submit();
}
};
diff --git a/src/pages/Ticket/Card/TicketDescriptorMenu.vue b/src/pages/Ticket/Card/TicketDescriptorMenu.vue
index f7389b592..30024fb26 100644
--- a/src/pages/Ticket/Card/TicketDescriptorMenu.vue
+++ b/src/pages/Ticket/Card/TicketDescriptorMenu.vue
@@ -28,6 +28,7 @@ const props = defineProps({
onMounted(() => {
restoreTicket();
+ hasDocuware();
});
watch(
diff --git a/src/pages/Ticket/Card/TicketDescriptorProxy.vue b/src/pages/Ticket/Card/TicketDescriptorProxy.vue
index 583ba35e7..8b872733d 100644
--- a/src/pages/Ticket/Card/TicketDescriptorProxy.vue
+++ b/src/pages/Ticket/Card/TicketDescriptorProxy.vue
@@ -1,7 +1,6 @@
-
+
diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue
index 421085142..4975afffb 100644
--- a/src/pages/Ticket/Card/TicketSale.vue
+++ b/src/pages/Ticket/Card/TicketSale.vue
@@ -773,6 +773,7 @@ watch(
v-model="row.itemFk"
:use-like="false"
@update:model-value="changeItem(row)"
+ autofocus
>
diff --git a/src/pages/Ticket/Card/TicketSplit.vue b/src/pages/Ticket/Card/TicketSplit.vue
index e79057266..462c22264 100644
--- a/src/pages/Ticket/Card/TicketSplit.vue
+++ b/src/pages/Ticket/Card/TicketSplit.vue
@@ -3,7 +3,9 @@ import { ref } from 'vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import split from './components/split';
-const emit = defineEmits(['ticketTransfered']);
+import { displayResults } from 'src/pages/Ticket/Negative/composables/notifyResults';
+const { notifyResults } = displayResults();
+const emit = defineEmits(['ticketTransferred']);
const $props = defineProps({
ticket: {
@@ -16,13 +18,20 @@ const splitDate = ref(Date.vnNew());
const splitSelectedRows = async () => {
const tickets = Array.isArray($props.ticket) ? $props.ticket : [$props.ticket];
- await split(tickets, splitDate.value);
- emit('ticketTransfered', tickets);
+ const results = await split(tickets, splitDate.value);
+ notifyResults(results, 'ticketFk');
+ emit('ticketTransferred', tickets);
};
-
+