diff --git a/src/components/ui/VnLinkPhone.vue b/src/components/ui/VnLinkPhone.vue
index 4c045968f..4068498cd 100644
--- a/src/components/ui/VnLinkPhone.vue
+++ b/src/components/ui/VnLinkPhone.vue
@@ -1,6 +1,7 @@
diff --git a/src/components/ui/VnPaginate.vue b/src/components/ui/VnPaginate.vue
index f3f6d64f1..3649ba8f5 100644
--- a/src/components/ui/VnPaginate.vue
+++ b/src/components/ui/VnPaginate.vue
@@ -133,7 +133,7 @@ const addFilter = async (filter, params) => {
async function fetch(params) {
useArrayData(props.dataKey, params);
arrayData.reset(['filter.skip', 'skip', 'page']);
- await arrayData.fetch({ append: false });
+ await arrayData.fetch({ append: false, updateRouter: mounted.value });
return emitStoreData();
}
diff --git a/src/filters/index.js b/src/filters/index.js
index ce5c44706..1f7ac77dd 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -12,10 +12,12 @@ import dateRange from './dateRange';
import toHour from './toHour';
import dashOrCurrency from './dashOrCurrency';
import getParamWhere from './getParamWhere';
+import parsePhone from './parsePhone';
import isDialogOpened from './isDialogOpened';
export {
isDialogOpened,
+ parsePhone,
toLowerCase,
toLowerCamel,
toDate,
diff --git a/src/filters/parsePhone.js b/src/filters/parsePhone.js
new file mode 100644
index 000000000..696f55007
--- /dev/null
+++ b/src/filters/parsePhone.js
@@ -0,0 +1,12 @@
+export default function (phone, prefix = 34) {
+ if (phone.startsWith('+')) {
+ return `${phone.slice(1)}`;
+ }
+ if (phone.startsWith('00')) {
+ return `${phone.slice(2)}`;
+ }
+ if (phone.startsWith(prefix) && phone.length === prefix.length + 9) {
+ return `${prefix}${phone.slice(prefix.length)}`;
+ }
+ return `${prefix}${phone}`;
+}
diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index a1016bbe3..fae7d4ae6 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -60,7 +60,7 @@ globals:
reference: Reference
agency: Agency
warehouseOut: Warehouse Out
- wareHouseIn: Warehouse In
+ warehouseIn: Warehouse In
landed: Landed
shipped: Shipped
totalEntries: Total entries
@@ -864,7 +864,6 @@ components:
cardDescriptor:
mainList: Main list
summary: Summary
- moreOptions: More options
leftMenu:
addToPinned: Add to pinned
removeFromPinned: Remove from pinned
diff --git a/src/pages/Customer/CustomerFilter.vue b/src/pages/Customer/CustomerFilter.vue
index 79d48a667..cd567d415 100644
--- a/src/pages/Customer/CustomerFilter.vue
+++ b/src/pages/Customer/CustomerFilter.vue
@@ -101,8 +101,8 @@ const exprBuilder = (param, value) => {
-
+ {
/>
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{
outlined
rounded
auto-load
+ />
+
+
+
+
-
-
-
-
-
-
-
+
+
@@ -203,7 +201,6 @@ es:
Salesperson: Comercial
Province: Provincia
City: Ciudad
- More options: Más opciones
Phone: Teléfono
Email: Email
Zone: Zona
diff --git a/src/pages/Entry/EntryBuysTableDialog.vue b/src/pages/Entry/EntryBuysTableDialog.vue
index c885c2485..77b31aaa3 100644
--- a/src/pages/Entry/EntryBuysTableDialog.vue
+++ b/src/pages/Entry/EntryBuysTableDialog.vue
@@ -86,7 +86,7 @@ const entriesTableColumns = computed(() => [
color="primary"
icon="print"
:loading="isLoading"
- @click="openReport(`Entries/${entityId}/print`)"
+ @click="openReport(`Entries/${entityId}/buy-label-supplier`)"
unelevated
autofocus
/>
diff --git a/src/pages/InvoiceIn/InvoiceInFilter.vue b/src/pages/InvoiceIn/InvoiceInFilter.vue
index d1c0856b5..130a77960 100644
--- a/src/pages/InvoiceIn/InvoiceInFilter.vue
+++ b/src/pages/InvoiceIn/InvoiceInFilter.vue
@@ -184,5 +184,4 @@ es:
Amount: Importe
Issued: Fecha factura
Id or supplier: Id o proveedor
- More options: Más opciones
diff --git a/src/pages/InvoiceOut/InvoiceOutFilter.vue b/src/pages/InvoiceOut/InvoiceOutFilter.vue
index 9ce8cc254..dc1d833a2 100644
--- a/src/pages/InvoiceOut/InvoiceOutFilter.vue
+++ b/src/pages/InvoiceOut/InvoiceOutFilter.vue
@@ -83,36 +83,29 @@ const states = ref();
/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -149,5 +142,4 @@ es:
Issued: Fecha emisión
Created: Fecha creación
Dued: Fecha vencimiento
- More options: Más opciones
diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue
index 6f6d39d9d..04c2c736e 100644
--- a/src/pages/InvoiceOut/InvoiceOutList.vue
+++ b/src/pages/InvoiceOut/InvoiceOutList.vue
@@ -58,8 +58,15 @@ const columns = computed(() => [
label: t('globals.reference'),
isTitle: true,
component: 'select',
- attrs: { url: MODEL, optionLabel: 'ref', optionValue: 'id' },
+ attrs: {
+ url: MODEL,
+ optionLabel: 'ref',
+ optionValue: 'ref',
+ },
columnField: { component: null },
+ columnFilter: {
+ inWhere: true,
+ },
},
{
align: 'left',
@@ -135,25 +142,22 @@ function openPdf(id) {
}
function downloadPdf() {
- if (selectedRows.value.size === 0) return;
- const selectedCardsArray = Array.from(selectedRows.value.values());
+ if (selectedRows.value.size === 0) return;
+ const selectedCardsArray = Array.from(selectedRows.value.values());
- if (selectedRows.value.size === 1) {
- const [invoiceOut] = selectedCardsArray;
- openPdf(invoiceOut.id);
- } else {
- const invoiceOutIdsArray = selectedCardsArray.map(
- (invoiceOut) => invoiceOut.id
- );
- const invoiceOutIds = invoiceOutIdsArray.join(',');
+ if (selectedRows.value.size === 1) {
+ const [invoiceOut] = selectedCardsArray;
+ openPdf(invoiceOut.id);
+ } else {
+ const invoiceOutIdsArray = selectedCardsArray.map((invoiceOut) => invoiceOut.id);
+ const invoiceOutIds = invoiceOutIdsArray.join(',');
- const params = {
- ids: invoiceOutIds,
- };
-
- openReport(`${MODEL}/downloadZip`, params);
- }
+ const params = {
+ ids: invoiceOutIds,
+ };
+ openReport(`${MODEL}/downloadZip`, params);
+ }
}
watchEffect(selectedRows);
diff --git a/src/pages/Monitor/Ticket/MonitorTicketFilter.vue b/src/pages/Monitor/Ticket/MonitorTicketFilter.vue
index 57248d580..3247da014 100644
--- a/src/pages/Monitor/Ticket/MonitorTicketFilter.vue
+++ b/src/pages/Monitor/Ticket/MonitorTicketFilter.vue
@@ -18,7 +18,7 @@ const groupedStates = ref();
const handleScopeDays = (params, days, callback) => {
const [from, to] = dateRange(Date.vnNew());
if (!days) {
- Object.assign(params, { from, to, scopeDays: 1 });
+ Object.assign(params, { from, to, scopeDays: 0 });
} else {
params.from = from;
to.setDate(to.getDate() + days);
@@ -59,7 +59,7 @@ const getLocale = (label) => {
diff --git a/src/pages/Monitor/Ticket/MonitorTickets.vue b/src/pages/Monitor/Ticket/MonitorTickets.vue
index fa9d74406..e5fea3003 100644
--- a/src/pages/Monitor/Ticket/MonitorTickets.vue
+++ b/src/pages/Monitor/Ticket/MonitorTickets.vue
@@ -25,11 +25,8 @@ const provinceOpts = ref([]);
const stateOpts = ref([]);
const zoneOpts = ref([]);
const { viewSummary } = useSummaryDialog();
-const from = Date.vnNew();
-from.setHours(0, 0, 0, 0);
-const to = new Date(from.getTime());
-to.setDate(to.getDate() + 1);
-to.setHours(23, 59, 59, 999);
+
+const [from, to] = dateRange(Date.vnNew());
const stateColors = {
notice: 'info',
success: 'positive',
@@ -340,7 +337,7 @@ const openTab = (id) =>
auto-load
:row-click="({ id }) => openTab(id)"
:disable-option="{ card: true }"
- :user-params="{ from, to, scopeDays: 1 }"
+ :user-params="{ from, to, scopeDays: 0 }"
>
{
const filter = {
fields: ['value'],
order: 'value ASC',
- limit: 30,
};
const url = `Tags/${tag?.id}/filterValue`;
@@ -50,7 +49,7 @@ const getSelectedTagValues = async (tag) => {
-
+
{
:emit-value="false"
use-input
@update:model-value="getSelectedTagValues"
+ data-cy="catalogFilterValueDialogTagSelect"
/>
{
:disable="!value"
is-outlined
class="col"
+ data-cy="catalogFilterValueDialogValueInput"
/>
-
+
diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue
index 6202a6f90..1dd569fb5 100644
--- a/src/pages/Order/Card/OrderCatalogFilter.vue
+++ b/src/pages/Order/Card/OrderCatalogFilter.vue
@@ -178,6 +178,7 @@ function addOrder(value, field, params) {
? resetCategory(params, searchFn)
: removeTagGroupParam(params, searchFn, valIndex)
"
+ data-cy="catalogFilterCustomTag"
>
{{
@@ -211,6 +212,7 @@ function addOrder(value, field, params) {
:name="category.icon"
class="category-icon"
@click="selectCategory(params, category, searchFn)"
+ data-cy="catalogFilterCategory"
>
{{ t(category.name) }}
@@ -234,6 +236,7 @@ function addOrder(value, field, params) {
sort-by="name ASC"
:disable="!params.categoryFk"
@update:model-value="searchFn()"
+ data-cy="catalogFilterType"
>
@@ -285,6 +288,7 @@ function addOrder(value, field, params) {
:is-clearable="false"
v-model="searchByTag"
@keyup.enter="(val) => onSearchByTag(val, params)"
+ data-cy="catalogFilterValueInput"
>
@@ -297,6 +301,7 @@ function addOrder(value, field, params) {
color="primary"
size="md"
dense
+ data-cy="catalogFilterValueDialogBtn"
/>
{
});
async function fetchClientAddress(id, formData = {}) {
const { data } = await axios.get(`Clients/${id}`, {
- params: { filter: { include: { relation: 'addresses' } } },
+ params: {
+ filter: {
+ order: ['isDefaultAddress DESC', 'isActive DESC', 'nickname ASC'],
+ include: { relation: 'addresses' },
+ },
+ },
});
addressesList.value = data.addresses;
formData.addressId = data.defaultAddressFk;
@@ -164,7 +170,7 @@ async function fetchAgencies({ landed, addressId }) {
const { data } = await axios.get('Agencies/landsThatDay', {
params: { addressFk: addressId, landed },
});
- agencyList.value = data;
+ agencyList.value = dataByOrder(data, 'agencyMode ASC');
}
const getDateColor = (date) => {
@@ -255,22 +261,27 @@ const getDateColor = (date) => {
@update:model-value="() => fetchAgencies(data)"
>
-
+
+
+
+
-
- {{
- `${
- !scope.opt?.isActive
- ? t('basicData.inactive')
- : ''
- } `
- }}
- {{ scope.opt?.nickname }}: {{ scope.opt?.street }},
- {{ scope.opt?.city }}
+
+ {{ scope.opt.nickname }}
+
+
+ {{ `${scope.opt.street}, ${scope.opt.city}` }}
diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue
index 67787228f..deeaaa28d 100644
--- a/src/pages/Ticket/Card/TicketSale.vue
+++ b/src/pages/Ticket/Card/TicketSale.vue
@@ -183,18 +183,25 @@ const resetChanges = async () => {
};
const updateQuantity = async (sale) => {
- const payload = { quantity: sale.quantity };
- await axios.post(`Sales/${sale.id}/updateQuantity`, payload);
+ const params = { quantity: sale.quantity };
+ try {
+ await axios.post(`Sales/${sale.id}/updateQuantity`, params);
+ } catch (e) {
+ sale.quantity = tableRef.value.CrudModelRef.originalData.find(
+ (s) => s.id === sale.id
+ ).quantity;
+ throw e;
+ }
notify('globals.dataSaved', 'positive');
};
const addSale = async (sale) => {
- const payload = {
+ const params = {
barcode: sale.itemFk,
quantity: sale.quantity,
};
- const { data } = await axios.post(`tickets/${route.params.id}/addSale`, payload);
+ const { data } = await axios.post(`tickets/${route.params.id}/addSale`, params);
if (!data) return;
@@ -222,7 +229,7 @@ const changeQuantity = async (sale) => {
)
return;
if (!sale.id) return addSale(sale);
- updateQuantity(sale);
+ await updateQuantity(sale);
};
const updateConcept = async (sale) => {
diff --git a/src/pages/Ticket/TicketFilter.vue b/src/pages/Ticket/TicketFilter.vue
index 6f1cac83b..bde27f30e 100644
--- a/src/pages/Ticket/TicketFilter.vue
+++ b/src/pages/Ticket/TicketFilter.vue
@@ -212,81 +212,78 @@ const getGroupedStates = (data) => {
/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -340,7 +337,6 @@ es:
With problems: Con problemas
Invoiced: Facturado
Routed: Enrutado
- More options: Más opciones
Province: Provincia
Agency: Agencia
Warehouse: Almacén
diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue
index 5cf7d028b..5f64a6421 100644
--- a/src/pages/Ticket/TicketList.vue
+++ b/src/pages/Ticket/TicketList.vue
@@ -277,7 +277,7 @@ const fetchAddresses = async (formData) => {
const filter = {
fields: ['nickname', 'street', 'city', 'id', 'isActive'],
- order: 'nickname ASC',
+ order: ['isDefaultAddress DESC', 'isActive DESC', 'nickname ASC'],
};
const params = { filter: JSON.stringify(filter) };
const { data } = await axios.get(`Clients/${formData.clientId}/addresses`, {
@@ -593,7 +593,22 @@ function setReference(data) {
@update:model-value="() => fetchAvailableAgencies(data)"
>
-
+
+
+
+
(agenciesOptions = data)"
auto-load
/>
+ (warehousesOptionsOut = data)"
+ auto-load
+ :filter="{ where: { isOrigin: TRUE } }"
+ />
+ (warehousesOptionsIn = data)"
+ auto-load
+ :filter="{ where: { isDestiny: TRUE } }"
+ />
@@ -39,11 +53,12 @@ const agenciesOptions = ref([]);
+
es:
- raidDays: Si se marca "Redada", la fecha de entrega se moverá automáticamente los días indicados (incluido 0). Si se deja vacio, la fecha no cambiará
+ raidDays: Si se marca "Redada", la fecha de entrega se moverá automáticamente los días indicados.
en:
- raidDays: If "Raid" is checked, the landing date will automatically shift by the specified number of days (including 0). If left empty, the date will stay the same.
+ raidDays: If "Raid" is checked, the landing date will automatically shift by the specified number of days.
diff --git a/src/pages/Travel/Card/TravelDescriptor.vue b/src/pages/Travel/Card/TravelDescriptor.vue
index 9d00d371a..00852e5ee 100644
--- a/src/pages/Travel/Card/TravelDescriptor.vue
+++ b/src/pages/Travel/Card/TravelDescriptor.vue
@@ -44,8 +44,8 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity.
-
-
+
+
diff --git a/test/cypress/integration/Order/orderCatalog.spec.js b/test/cypress/integration/Order/orderCatalog.spec.js
new file mode 100644
index 000000000..45eda6f1f
--- /dev/null
+++ b/test/cypress/integration/Order/orderCatalog.spec.js
@@ -0,0 +1,112 @@
+///
+describe('OrderCatalog', () => {
+ beforeEach(() => {
+ cy.login('developer');
+ cy.viewport(1920, 720);
+ cy.visit('/#/order/8/catalog');
+ });
+
+ const checkCustomFilterTag = (filterName = 'Plant') => {
+ cy.dataCy('catalogFilterCustomTag').should('exist');
+ cy.dataCy('catalogFilterCustomTag').contains(filterName);
+ };
+
+ const checkFilterTag = (filterName = 'Plant') => {
+ cy.dataCy('vnFilterPanelChip').should('exist');
+ cy.dataCy('vnFilterPanelChip').contains(filterName);
+ };
+
+ const selectCategory = (categoryIndex = 1, categoryName = 'Plant') => {
+ cy.get(
+ `div.q-page-container div:nth-of-type(${categoryIndex}) > [data-cy='catalogFilterCategory']`
+ ).should('exist');
+ cy.get(
+ `div.q-page-container div:nth-of-type(${categoryIndex}) > [data-cy='catalogFilterCategory']`
+ ).click();
+ checkCustomFilterTag(categoryName);
+ };
+
+ const searchByCustomTagInput = (option) => {
+ cy.dataCy('catalogFilterValueInput').find('input').last().focus();
+ cy.dataCy('catalogFilterValueInput').find('input').last().type(option);
+ cy.dataCy('catalogFilterValueInput').find('input').last().type('{enter}');
+ checkCustomFilterTag(option);
+ };
+
+ const selectTypeFilter = (option) => {
+ cy.selectOption(
+ 'div.q-page-container div.list > div:nth-of-type(2) div:nth-of-type(3)',
+ option
+ );
+ checkFilterTag(option);
+ };
+
+ it('Shows empty state', () => {
+ cy.dataCy('orderCatalogPage').should('exist');
+ cy.dataCy('orderCatalogPage').contains('No data to display');
+ });
+
+ it('filter by category', () => {
+ selectCategory();
+ cy.dataCy('orderCatalogItem').should('exist');
+ });
+
+ it('filters by type', () => {
+ selectCategory();
+ selectTypeFilter('Anthurium');
+ });
+
+ it('filters by custom value select', () => {
+ selectCategory();
+ searchByCustomTagInput('Silver');
+ });
+
+ it('filters by custom value dialog', () => {
+ Cypress.on('uncaught:exception', (err) => {
+ if (err.message.includes('canceled')) {
+ return false;
+ }
+ });
+ selectCategory();
+ cy.dataCy('catalogFilterValueDialogBtn').should('exist');
+ cy.dataCy('catalogFilterValueDialogBtn').last().click();
+ cy.dataCy('catalogFilterValueDialogTagSelect').should('exist');
+ cy.selectOption("[data-cy='catalogFilterValueDialogTagSelect']", 'Tallos');
+ cy.dataCy('catalogFilterValueDialogValueInput').find('input').focus();
+ cy.dataCy('catalogFilterValueDialogValueInput').find('input').type('2');
+ cy.dataCy('catalogFilterValueDialogValueInput').find('input').type('{enter}');
+ checkCustomFilterTag('2');
+ });
+
+ it('removes a secondary tag', () => {
+ selectCategory();
+ selectTypeFilter('Anthurium');
+ cy.dataCy('vnFilterPanelChip').should('exist');
+ cy.get(
+ "div.q-page-container [data-cy='vnFilterPanelChip'] > i.q-chip__icon--remove"
+ )
+ .contains('cancel')
+ .should('exist');
+ cy.get(
+ "div.q-page-container [data-cy='vnFilterPanelChip'] > i.q-chip__icon--remove"
+ )
+ .contains('cancel')
+ .click();
+ cy.dataCy('vnFilterPanelChip').should('not.exist');
+ });
+
+ it('Removes category tag', () => {
+ selectCategory();
+ cy.get(
+ "div.q-page-container [data-cy='catalogFilterCustomTag'] > i.q-chip__icon--remove"
+ )
+ .contains('cancel')
+ .should('exist');
+ cy.get(
+ "div.q-page-container [data-cy='catalogFilterCustomTag'] > i.q-chip__icon--remove"
+ )
+ .contains('cancel')
+ .click();
+ cy.dataCy('catalogFilterCustomTag').should('not.exist');
+ });
+});
diff --git a/test/cypress/integration/vnComponent/VnLocation.spec.js b/test/cypress/integration/vnComponent/VnLocation.spec.js
index 0e98e5f68..b98d42fdd 100644
--- a/test/cypress/integration/vnComponent/VnLocation.spec.js
+++ b/test/cypress/integration/vnComponent/VnLocation.spec.js
@@ -105,12 +105,12 @@ describe('VnLocation', () => {
const province = 'Valencia';
cy.get(createLocationButton).click();
cy.get('.q-card > h1').should('have.text', 'New postcode');
- cy.get(dialogInputs).eq(0).clear();
- cy.get(dialogInputs).eq(0).type(postCode);
cy.selectOption(
`${createForm.prefix} > :nth-child(4) > .q-select > ${createForm.sufix}`,
province
);
+ cy.get(dialogInputs).eq(0).clear();
+ cy.get(dialogInputs).eq(0).type(postCode);
cy.get('.q-mt-lg > .q-btn--standard').click();
cy.get(`${createForm.prefix}`).should('not.exist');
cy.waitForElement('.q-form');
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index 53f9d1d73..dbaa7871c 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -86,9 +86,10 @@ Cypress.Commands.add('getValue', (selector) => {
});
// Fill Inputs
-Cypress.Commands.add('selectOption', (selector, option) => {
+Cypress.Commands.add('selectOption', (selector, option, timeout) => {
cy.waitForElement(selector);
cy.get(selector).click();
+ cy.wait(timeout || 1000);
cy.get('.q-menu .q-item').contains(option).click();
});
Cypress.Commands.add('countSelectOptions', (selector, option) => {
diff --git a/test/vitest/__tests__/components/common/VnLinkPhone.spec.js b/test/vitest/__tests__/components/common/VnLinkPhone.spec.js
new file mode 100644
index 000000000..e460ab2fc
--- /dev/null
+++ b/test/vitest/__tests__/components/common/VnLinkPhone.spec.js
@@ -0,0 +1,29 @@
+import { describe, it, expect } from 'vitest';
+import parsePhone from 'src/filters/parsePhone';
+
+describe('parsePhone filter', () => {
+ it("adds prefix +34 if it doesn't have one", () => {
+ const resultado = parsePhone('123456789', '34');
+ expect(resultado).toBe('34123456789');
+ });
+
+ it('maintains prefix +34 if it is already correct', () => {
+ const resultado = parsePhone('+34123456789', '34');
+ expect(resultado).toBe('34123456789');
+ });
+
+ it('converts prefix 0034 to +34', () => {
+ const resultado = parsePhone('0034123456789', '34');
+ expect(resultado).toBe('34123456789');
+ });
+
+ it('converts prefix 34 without symbol to +34', () => {
+ const resultado = parsePhone('34123456789', '34');
+ expect(resultado).toBe('34123456789');
+ });
+
+ it('replaces incorrect prefix with the correct one', () => {
+ const resultado = parsePhone('+44123456789', '34');
+ expect(resultado).toBe('44123456789');
+ });
+});