diff --git a/src/components/FormModel.vue b/src/components/FormModel.vue index 5a59f301e..a92ba29ee 100644 --- a/src/components/FormModel.vue +++ b/src/components/FormModel.vue @@ -134,7 +134,8 @@ onMounted(async () => { if (!$props.formInitialData) { if ($props.autoLoad && $props.url) await fetch(); - else if (arrayData.store.data) updateAndEmit('onFetch', arrayData.store.data); + else if (arrayData.store.data) + updateAndEmit('onFetch', { val: arrayData.store.data }); } if ($props.observeFormChanges) { watch( @@ -154,7 +155,7 @@ onMounted(async () => { if (!$props.url) watch( () => arrayData.store.data, - (val) => updateAndEmit('onFetch', val), + (val) => updateAndEmit('onFetch', { val }), ); watch( @@ -200,7 +201,7 @@ async function fetch() { }); if (Array.isArray(data)) data = data[0] ?? {}; - updateAndEmit('onFetch', data); + updateAndEmit('onFetch', { val: data }); } catch (e) { state.set(modelValue, {}); throw e; @@ -228,7 +229,11 @@ async function save(prevent = false) { if ($props.urlCreate) notify('globals.dataCreated', 'positive'); - updateAndEmit('onDataSaved', formData.value, response?.data); + updateAndEmit('onDataSaved', { + val: formData.value, + res: response?.data, + old: originalData.value, + }); if ($props.reload) await arrayData.fetch({}); hasChanges.value = false; } finally { @@ -242,8 +247,7 @@ async function saveAndGo() { } function reset() { - formData.value = JSON.parse(JSON.stringify(originalData.value)); - updateAndEmit('onFetch', originalData.value); + updateAndEmit('onFetch', { val: originalData.value }); if ($props.observeFormChanges) { hasChanges.value = false; isResetting.value = true; @@ -265,11 +269,11 @@ function filter(value, update, filterOptions) { ); } -function updateAndEmit(evt, val, res) { +function updateAndEmit(evt, { val, res, old } = { val: null, res: null, old: null }) { state.set(modelValue, val); if (!$props.url) arrayData.store.data = val; - emit(evt, state.get(modelValue), res); + emit(evt, state.get(modelValue), res, old); } function trimData(data) { diff --git a/src/components/TicketProblems.vue b/src/components/TicketProblems.vue index a24735a5f..17d9602af 100644 --- a/src/components/TicketProblems.vue +++ b/src/components/TicketProblems.vue @@ -1,4 +1,6 @@ <script setup> +import { toCurrency } from 'src/filters'; + defineProps({ row: { type: Object, required: true } }); </script> <template> @@ -10,7 +12,8 @@ defineProps({ row: { type: Object, required: true } }); size="xs" > <QTooltip> - {{ $t('salesTicketsTable.risk') }}: {{ row.risk - row.credit }} + {{ $t('salesTicketsTable.risk') }}: + {{ toCurrency(row.risk - row.credit) }} </QTooltip> </QIcon> <QIcon diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index 9f95a1843..72110a399 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -353,14 +353,14 @@ const clickHandler = async (event) => { const column = $props.columns.find((col) => col.name === colField); if (editingRow.value !== null && editingField.value !== null) { - if (editingRow.value === rowIndex && editingField.value === colField) { - return; - } + if (editingRow.value == rowIndex && editingField.value == colField) return; destroyInput(editingRow.value, editingField.value); } - if (isEditableColumn(column)) + + if (isEditableColumn(column)) { await renderInput(Number(rowIndex), colField, clickedElement); + } }; async function handleTabKey(event, rowIndex, colField) { @@ -492,9 +492,7 @@ async function handleTabNavigation(rowIndex, colName, direction) { if (isEditableColumn(columns[newColumnIndex])) break; } while (iterations < totalColumns); - if (iterations >= totalColumns) { - return; - } + if (iterations >= totalColumns + 1) return; if (direction === 1 && newColumnIndex <= currentColumnIndex) { rowIndex++; @@ -767,6 +765,7 @@ function cardClick(_, row) { : 'hidden' }`" @click="btn.action(row)" + :data-cy="btn?.name ?? `tableAction-${index}`" /> </QTd> </template> diff --git a/src/components/common/VnCardBeta.vue b/src/components/common/VnCardBeta.vue index d2bed6257..16fc14c2c 100644 --- a/src/components/common/VnCardBeta.vue +++ b/src/components/common/VnCardBeta.vue @@ -38,17 +38,23 @@ onBeforeMount(async () => { } }); -onBeforeRouteUpdate(async (to, from) => { - const id = to.params.id; - if (id !== from.params.id) await fetch(id, true); -}); - -async function fetch(id, append = false) { - const regex = /\/(\d+)/; - if (props.idInWhere) arrayData.store.filter.where = { id }; - else if (!regex.test(props.url)) arrayData.store.url = `${props.url}/${id}`; - else arrayData.store.url = props.url.replace(regex, `/${id}`); - await arrayData.fetch({ append, updateRouter: false }); +if (props.baseUrl) { + onBeforeRouteUpdate(async (to, from) => { + if (hasRouteParam(to.params)) { + const { matched } = router.currentRoute.value; + const { name } = matched.at(-3); + if (name) { + router.push({ name, params: to.params }); + } + } + if (to.params.id !== from.params.id) { + arrayData.store.url = `${props.baseUrl}/${to.params.id}`; + await arrayData.fetch({ append: false, updateRouter: false }); + } + }); +} +function hasRouteParam(params, valueToCheck = ':addressId') { + return Object.values(params).includes(valueToCheck); } </script> <template> diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue index 3ede24274..5028e876d 100644 --- a/src/components/common/VnLocation.vue +++ b/src/components/common/VnLocation.vue @@ -85,6 +85,7 @@ const handleModelValue = (data) => { :tooltip="t('Create new location')" :rules="mixinRules" :lazy-rules="true" + required > <template #form> <CreateNewPostcode diff --git a/src/components/common/VnSelect.vue b/src/components/common/VnSelect.vue index 339f90e0e..acef9e308 100644 --- a/src/components/common/VnSelect.vue +++ b/src/components/common/VnSelect.vue @@ -10,12 +10,7 @@ const emit = defineEmits(['update:modelValue', 'update:options', 'remove']); const $attrs = useAttrs(); const { t } = useI18n(); -const isRequired = computed(() => { - return useRequired($attrs).isRequired; -}); -const requiredFieldRule = computed(() => { - return useRequired($attrs).requiredFieldRule; -}); +const { isRequired, requiredFieldRule } = useRequired($attrs); const $props = defineProps({ modelValue: { diff --git a/src/composables/__tests__/useRequired.spec.js b/src/composables/__tests__/useRequired.spec.js new file mode 100644 index 000000000..e035a80c6 --- /dev/null +++ b/src/composables/__tests__/useRequired.spec.js @@ -0,0 +1,66 @@ +import { describe, it, expect, vi } from 'vitest'; +import { useRequired } from '../useRequired'; + +vi.mock('../useValidator', () => ({ + useValidator: () => ({ + validations: () => ({ + required: vi.fn((isRequired, val) => { + if (!isRequired) return true; + return val !== null && val !== undefined && val !== ''; + }), + }), + }), +})); + +describe('useRequired', () => { + it('should detect required when attr is boolean true', () => { + const attrs = { required: true }; + const { isRequired } = useRequired(attrs); + expect(isRequired).toBe(true); + }); + + it('should detect required when attr is boolean false', () => { + const attrs = { required: false }; + const { isRequired } = useRequired(attrs); + expect(isRequired).toBe(false); + }); + + it('should detect required when attr exists without value', () => { + const attrs = { required: '' }; + const { isRequired } = useRequired(attrs); + expect(isRequired).toBe(true); + }); + + it('should return false when required attr does not exist', () => { + const attrs = { someOtherAttr: 'value' }; + const { isRequired } = useRequired(attrs); + expect(isRequired).toBe(false); + }); + + describe('requiredFieldRule', () => { + it('should validate required field with value', () => { + const attrs = { required: true }; + const { requiredFieldRule } = useRequired(attrs); + expect(requiredFieldRule('some value')).toBe(true); + }); + + it('should validate required field with empty value', () => { + const attrs = { required: true }; + const { requiredFieldRule } = useRequired(attrs); + expect(requiredFieldRule('')).toBe(false); + }); + + it('should pass validation when field is not required', () => { + const attrs = { required: false }; + const { requiredFieldRule } = useRequired(attrs); + expect(requiredFieldRule('')).toBe(true); + }); + + it('should handle null and undefined values', () => { + const attrs = { required: true }; + const { requiredFieldRule } = useRequired(attrs); + expect(requiredFieldRule(null)).toBe(false); + expect(requiredFieldRule(undefined)).toBe(false); + }); + }); +}); diff --git a/src/composables/useRequired.js b/src/composables/useRequired.js index d211b96b4..4e84b9e48 100644 --- a/src/composables/useRequired.js +++ b/src/composables/useRequired.js @@ -2,14 +2,10 @@ import { useValidator } from 'src/composables/useValidator'; export function useRequired($attrs) { const { validations } = useValidator(); - const hasRequired = Object.keys($attrs).includes('required'); - let isRequired = false; - if (hasRequired) { - const required = $attrs['required']; - if (typeof required === 'boolean') { - isRequired = required; - } - } + const isRequired = + typeof $attrs['required'] === 'boolean' + ? $attrs['required'] + : Object.keys($attrs).includes('required'); const requiredFieldRule = (val) => validations().required(isRequired, val); return { diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 1dbe25366..e0d4cf27d 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -839,6 +839,7 @@ supplier: verified: Verificado isActive: Está activo billingData: Forma de pago + financialData: Datos financieros payDeadline: Plazo de pago payDay: Día de pago account: Cuenta diff --git a/src/pages/Customer/Card/CustomerConsumption.vue b/src/pages/Customer/Card/CustomerConsumption.vue index eef9d55b5..f3949bb32 100644 --- a/src/pages/Customer/Card/CustomerConsumption.vue +++ b/src/pages/Customer/Card/CustomerConsumption.vue @@ -232,7 +232,6 @@ const updateDateParams = (value, params) => { :include="'category'" :sortBy="'name ASC'" dense - @update:model-value="(data) => updateDateParams(data, params)" > <template #option="scope"> <QItem v-bind="scope.itemProps"> @@ -254,7 +253,6 @@ const updateDateParams = (value, params) => { :fields="['id', 'name']" :sortBy="'name ASC'" dense - @update:model-value="(data) => updateDateParams(data, params)" /> <VnSelect v-model="params.campaign" @@ -303,12 +301,14 @@ en: valentinesDay: Valentine's Day mothersDay: Mother's Day allSaints: All Saints' Day + frenchMothersDay: Mother's Day in France es: Enter a new search: Introduce una nueva búsqueda Group by items: Agrupar por artículos valentinesDay: Día de San Valentín mothersDay: Día de la Madre allSaints: Día de Todos los Santos + frenchMothersDay: (Francia) Día de la Madre Campaign consumption: Consumo campaña Campaign: Campaña From: Desde diff --git a/src/pages/Customer/Card/CustomerFiscalData.vue b/src/pages/Customer/Card/CustomerFiscalData.vue index 8f334b110..93909eb9c 100644 --- a/src/pages/Customer/Card/CustomerFiscalData.vue +++ b/src/pages/Customer/Card/CustomerFiscalData.vue @@ -2,7 +2,10 @@ import { ref } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; +import { useQuasar } from 'quasar'; +import axios from 'axios'; +import useNotify from 'src/composables/useNotify.js'; import FetchData from 'components/FetchData.vue'; import FormModel from 'components/FormModel.vue'; import VnRow from 'components/ui/VnRow.vue'; @@ -11,9 +14,12 @@ import VnSelect from 'src/components/common/VnSelect.vue'; import VnLocation from 'src/components/common/VnLocation.vue'; import VnCheckbox from 'src/components/common/VnCheckbox.vue'; import { getDifferences, getUpdatedValues } from 'src/filters'; +import VnConfirm from 'src/components/ui/VnConfirm.vue'; +const quasar = useQuasar(); const { t } = useI18n(); const route = useRoute(); +const { notify } = useNotify(); const typesTaxes = ref([]); const typesTransactions = ref([]); @@ -31,6 +37,31 @@ function onBeforeSave(formData, originalData) { formData, ); } + +async function checkEtChanges(data, _, originalData) { + const equalizatedHasChanged = originalData.isEqualizated != data.isEqualizated; + const hasToInvoiceByAddress = + originalData.hasToInvoiceByAddress || data.hasToInvoiceByAddress; + if (equalizatedHasChanged && hasToInvoiceByAddress) { + quasar.dialog({ + component: VnConfirm, + componentProps: { + title: t('You changed the equalization tax'), + message: t('Do you want to spread the change?'), + promise: () => acceptPropagate(data), + }, + }); + } else if (equalizatedHasChanged) { + await acceptPropagate(data); + } +} + +async function acceptPropagate({ isEqualizated }) { + await axios.patch(`Clients/${route.params.id}/addressesPropagateRe`, { + isEqualizated, + }); + notify(t('Equivalent tax spreaded'), 'warning'); +} </script> <template> @@ -45,6 +76,8 @@ function onBeforeSave(formData, originalData) { auto-load model="Customer" :mapper="onBeforeSave" + observe-form-changes + @on-data-saved="checkEtChanges" > <template #form="{ data, validate }"> <VnRow> @@ -180,6 +213,9 @@ es: whenActivatingIt: Al activarlo, no informar el código del país en el campo nif inOrderToInvoice: Para facturar no se consulta este campo, sino el RE de consignatario. Al modificar este campo si no esta marcada la casilla Facturar por consignatario, se propagará automaticamente el cambio a todos lo consignatarios, en caso contrario preguntará al usuario si quiere o no propagar Daily invoice: Facturación diaria + Equivalent tax spreaded: Recargo de equivalencia propagado + You changed the equalization tax: Has cambiado el recargo de equivalencia + Do you want to spread the change?: ¿Deseas propagar el cambio a sus consignatarios? en: onlyLetters: Only letters, numbers and spaces can be used whenActivatingIt: When activating it, do not enter the country code in the ID field diff --git a/src/pages/Customer/Card/CustomerSummary.vue b/src/pages/Customer/Card/CustomerSummary.vue index d2eb125d7..324da0771 100644 --- a/src/pages/Customer/Card/CustomerSummary.vue +++ b/src/pages/Customer/Card/CustomerSummary.vue @@ -270,7 +270,7 @@ const sumRisk = ({ clientRisks }) => { <VnTitle target="_blank" :url="`${grafanaUrl}/d/40buzE4Vk/comportamiento-pagos-clientes?orgId=1&var-clientFk=${entityId}`" - :text="t('customer.summary.payMethodFk')" + :text="t('customer.summary.financialData')" icon="vn:grafana" /> <VnLv diff --git a/src/pages/Customer/components/CustomerAddressCreate.vue b/src/pages/Customer/components/CustomerAddressCreate.vue index 32b4078db..e1be6b150 100644 --- a/src/pages/Customer/components/CustomerAddressCreate.vue +++ b/src/pages/Customer/components/CustomerAddressCreate.vue @@ -98,7 +98,6 @@ function onAgentCreated({ id, fiscalName }, data) { :rules="validate('Worker.postcode')" :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" v-model="data.location" - :required="true" @update:model-value="(location) => handleLocation(data, location)" /> diff --git a/src/pages/Customer/components/CustomerAddressEdit.vue b/src/pages/Customer/components/CustomerAddressEdit.vue index 5b36650f7..af1b9c160 100644 --- a/src/pages/Customer/components/CustomerAddressEdit.vue +++ b/src/pages/Customer/components/CustomerAddressEdit.vue @@ -96,11 +96,11 @@ const updateObservations = async (payload) => { await axios.post('AddressObservations/crud', payload); notes.value = []; deletes.value = []; - toCustomerAddress(); }; async function updateAll({ data, payload }) { await updateObservations(payload); await updateAddress(data); + toCustomerAddress(); } function getPayload() { return { @@ -137,15 +137,12 @@ async function handleDialog(data) { .onOk(async () => { await updateAddressTicket(); await updateAll(body); - toCustomerAddress(); }) .onCancel(async () => { await updateAll(body); - toCustomerAddress(); }); } else { - updateAll(body); - toCustomerAddress(); + await updateAll(body); } } diff --git a/src/pages/Entry/Card/EntryBuys.vue b/src/pages/Entry/Card/EntryBuys.vue index e159c5356..f3b73cb04 100644 --- a/src/pages/Entry/Card/EntryBuys.vue +++ b/src/pages/Entry/Card/EntryBuys.vue @@ -209,13 +209,14 @@ const columns = [ row['amount'] = row['quantity'] * row['buyingValue']; }, }, - width: '20px', + width: '30px', style: (row) => { if (row.groupingMode === 'grouping') return { color: 'var(--vn-label-color)' }; }, }, { + align: 'center', labelAbbreviation: 'GM', label: t('Grouping selector'), toolTip: t('Grouping selector'), @@ -249,7 +250,7 @@ const columns = [ toolTip: 'Grouping', name: 'grouping', component: 'number', - width: '20px', + width: '30px', create: true, style: (row) => { if (row.groupingMode === 'packing') return { color: 'var(--vn-label-color)' }; @@ -508,7 +509,7 @@ async function setBuyUltimate(itemFk, data) { allowedKeys.forEach((key) => { if (buyUltimateData.hasOwnProperty(key) && key !== 'entryFk') { - data[key] = buyUltimateData[key]; + if (!['stickers', 'quantity'].includes(key)) data[key] = buyUltimateData[key]; } }); } @@ -600,7 +601,6 @@ onMounted(() => { ref="entryBuysRef" data-key="EntryBuys" :url="`Entries/${entityId}/getBuyList`" - order="name DESC" save-url="Buys/crud" :disable-option="{ card: true }" v-model:selected="selectedRows" @@ -644,7 +644,8 @@ onMounted(() => { :is-editable="editableMode" :without-header="!editableMode" :with-filters="editableMode" - :right-search="editableMode" + :right-search="false" + :right-search-icon="false" :row-click="false" :columns="columns" :beforeSaveFn="beforeSave" diff --git a/src/pages/Entry/EntryList.vue b/src/pages/Entry/EntryList.vue index 845d65604..d50f6b219 100644 --- a/src/pages/Entry/EntryList.vue +++ b/src/pages/Entry/EntryList.vue @@ -44,28 +44,32 @@ const entryQueryFilter = { const columns = computed(() => [ { - label: 'Ex', + labelAbbreviation: 'Ex', + label: t('entry.list.tableVisibleColumns.isExcludedFromAvailable'), toolTip: t('entry.list.tableVisibleColumns.isExcludedFromAvailable'), name: 'isExcludedFromAvailable', component: 'checkbox', width: '35px', }, { - label: 'Pe', + labelAbbreviation: 'Pe', + label: t('entry.list.tableVisibleColumns.isOrdered'), toolTip: t('entry.list.tableVisibleColumns.isOrdered'), name: 'isOrdered', component: 'checkbox', width: '35px', }, { - label: 'Le', + labelAbbreviation: 'LE', + label: t('entry.list.tableVisibleColumns.isConfirmed'), toolTip: t('entry.list.tableVisibleColumns.isConfirmed'), name: 'isConfirmed', component: 'checkbox', width: '35px', }, { - label: 'Re', + labelAbbreviation: 'Re', + label: t('entry.list.tableVisibleColumns.isReceived'), toolTip: t('entry.list.tableVisibleColumns.isReceived'), name: 'isReceived', component: 'checkbox', @@ -89,6 +93,7 @@ const columns = computed(() => [ chip: { condition: () => true, }, + width: '50px', }, { label: t('entry.list.tableVisibleColumns.supplierFk'), @@ -99,8 +104,10 @@ const columns = computed(() => [ attrs: { url: 'suppliers', fields: ['id', 'name'], + where: { order: 'name DESC' }, }, format: (row, dashIfEmpty) => dashIfEmpty(row.supplierName), + width: '110px', }, { align: 'left', @@ -124,6 +131,7 @@ const columns = computed(() => [ label: 'AWB', name: 'awbCode', component: 'input', + width: '100px', }, { align: 'left', @@ -160,6 +168,7 @@ const columns = computed(() => [ component: null, }, format: (row, dashIfEmpty) => dashIfEmpty(row.warehouseOutName), + width: '65px', }, { align: 'left', @@ -175,12 +184,24 @@ const columns = computed(() => [ component: null, }, format: (row, dashIfEmpty) => dashIfEmpty(row.warehouseInName), + width: '65px', }, { align: 'left', + labelAbbreviation: t('Type'), label: t('entry.list.tableVisibleColumns.entryTypeDescription'), + toolTip: t('entry.list.tableVisibleColumns.entryTypeDescription'), name: 'entryTypeCode', + component: 'select', + attrs: { + url: 'entryTypes', + fields: ['code', 'description'], + optionValue: 'code', + optionLabel: 'description', + }, cardVisible: true, + width: '65px', + format: (row, dashIfEmpty) => dashIfEmpty(row.entryTypeDescription), }, { name: 'companyFk', @@ -320,4 +341,5 @@ es: Search entries: Buscar entradas You can search by entry reference: Puedes buscar por referencia de la entrada Create entry: Crear entrada + Type: Tipo </i18n> diff --git a/src/pages/Entry/EntryStockBought.vue b/src/pages/Entry/EntryStockBought.vue index fa0bdc12e..da8557828 100644 --- a/src/pages/Entry/EntryStockBought.vue +++ b/src/pages/Entry/EntryStockBought.vue @@ -34,18 +34,20 @@ const columns = computed(() => [ label: t('entryStockBought.buyer'), isTitle: true, component: 'select', + isEditable: false, cardVisible: true, create: true, attrs: { url: 'Workers/activeWithInheritedRole', - fields: ['id', 'name'], + fields: ['id', 'name', 'nickname'], where: { role: 'buyer' }, optionFilter: 'firstName', - optionLabel: 'name', + optionLabel: 'nickname', optionValue: 'id', useLike: false, }, columnFilter: false, + width: '70px', }, { align: 'center', @@ -55,6 +57,7 @@ const columns = computed(() => [ create: true, component: 'number', summation: true, + width: '60px', }, { align: 'center', @@ -78,6 +81,7 @@ const columns = computed(() => [ actions: [ { title: t('entryStockBought.viewMoreDetails'), + name: 'searchBtn', icon: 'search', isPrimary: true, action: (row) => { @@ -91,6 +95,7 @@ const columns = computed(() => [ }, }, ], + 'data-cy': 'table-actions', }, ]); @@ -158,7 +163,7 @@ function round(value) { @on-fetch=" (data) => { travel = data.find( - (data) => data.warehouseIn?.code.toLowerCase() === 'vnh' + (data) => data.warehouseIn?.code.toLowerCase() === 'vnh', ); } " @@ -179,6 +184,7 @@ function round(value) { @click="openDialog()" :title="t('entryStockBought.editTravel')" color="primary" + data-cy="edit-travel" /> </div> </VnRow> @@ -239,10 +245,11 @@ function round(value) { table-height="80vh" auto-load :column-search="false" + :without-header="true" > <template #column-workerFk="{ row }"> <span class="link" @click.stop> - {{ row?.worker?.user?.name }} + {{ row?.worker?.user?.nickname }} <WorkerDescriptorProxy :id="row?.workerFk" /> </span> </template> @@ -279,10 +286,11 @@ function round(value) { justify-content: center; } .column { + min-width: 30%; + margin-top: 5%; display: flex; flex-direction: column; align-items: center; - min-width: 35%; } .text-negative { color: $negative !important; diff --git a/src/pages/Entry/EntryStockBoughtDetail.vue b/src/pages/Entry/EntryStockBoughtDetail.vue index 812171825..9d382f23a 100644 --- a/src/pages/Entry/EntryStockBoughtDetail.vue +++ b/src/pages/Entry/EntryStockBoughtDetail.vue @@ -21,7 +21,7 @@ const $props = defineProps({ const customUrl = `StockBoughts/getStockBoughtDetail?workerFk=${$props.workerFk}&dated=${$props.dated}`; const columns = [ { - align: 'left', + align: 'right', label: t('Entry'), name: 'entryFk', isTitle: true, @@ -29,7 +29,7 @@ const columns = [ columnFilter: false, }, { - align: 'left', + align: 'right', name: 'itemFk', label: t('Item'), columnFilter: false, @@ -44,21 +44,21 @@ const columns = [ cardVisible: true, }, { - align: 'left', + align: 'right', name: 'volume', label: t('Volume'), columnFilter: false, cardVisible: true, }, { - align: 'left', + align: 'right', label: t('Packaging'), name: 'packagingFk', columnFilter: false, cardVisible: true, }, { - align: 'left', + align: 'right', label: 'Packing', name: 'packing', columnFilter: false, @@ -73,12 +73,14 @@ const columns = [ ref="tableRef" data-key="StockBoughtsDetail" :url="customUrl" - order="itemName DESC" + order="volume DESC" :columns="columns" :right-search="false" :disable-infinite-scroll="true" :disable-option="{ card: true }" :limit="0" + :without-header="true" + :with-filters="false" auto-load > <template #column-entryFk="{ row }"> @@ -105,7 +107,7 @@ const columns = [ align-items: center; margin: auto; background-color: var(--vn-section-color); - padding: 4px; + padding: 2%; } .container > div > div > .q-table__top.relative-position.row.items-center { background-color: red !important; diff --git a/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue b/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue index 3ceb447dd..161f2ab45 100644 --- a/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue +++ b/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue @@ -125,7 +125,7 @@ const ticketsColumns = ref([ :value="toDate(invoiceOut.issued)" /> <VnLv - :label="t('invoiceOut.summary.dued')" + :label="t('invoiceOut.summary.expirationDate')" :value="toDate(invoiceOut.dued)" /> <VnLv :label="t('globals.created')" :value="toDate(invoiceOut.created)" /> diff --git a/src/pages/InvoiceOut/locale/en.yml b/src/pages/InvoiceOut/locale/en.yml index cb0dfdca7..ee6ba57e6 100644 --- a/src/pages/InvoiceOut/locale/en.yml +++ b/src/pages/InvoiceOut/locale/en.yml @@ -19,6 +19,7 @@ invoiceOut: summary: issued: Issued dued: Due + expirationDate: Expiration date booked: Booked taxBreakdown: Tax breakdown taxableBase: Taxable base diff --git a/src/pages/InvoiceOut/locale/es.yml b/src/pages/InvoiceOut/locale/es.yml index a35c33c4e..a059ce18d 100644 --- a/src/pages/InvoiceOut/locale/es.yml +++ b/src/pages/InvoiceOut/locale/es.yml @@ -19,6 +19,7 @@ invoiceOut: summary: issued: Fecha dued: Fecha límite + expirationDate: Fecha vencimiento booked: Contabilizada taxBreakdown: Desglose impositivo taxableBase: Base imp. diff --git a/test/cypress/integration/client/clientAddress.spec.js b/test/cypress/integration/client/clientAddress.spec.js index db876b64b..434180047 100644 --- a/test/cypress/integration/client/clientAddress.spec.js +++ b/test/cypress/integration/client/clientAddress.spec.js @@ -3,11 +3,46 @@ describe('Client consignee', () => { beforeEach(() => { cy.viewport(1280, 720); cy.login('developer'); - cy.visit('#/customer/1110/address', { - timeout: 5000, - }); + cy.visit('#/customer/1107/address'); + cy.domContentLoad(); }); it('Should load layout', () => { cy.get('.q-card').should('be.visible'); }); + + it('check as equalizated', function () { + cy.get('.q-card__section > .address-card').then(($el) => { + let addressCards_before = $el.length; + + cy.get('.q-page-sticky > div > .q-btn').click(); + const addressName = 'test'; + cy.dataCy('Consignee_input').type(addressName); + cy.dataCy('Location_select').click(); + cy.get('[role="listbox"] .q-item:nth-child(1)').click(); + cy.dataCy('Street address_input').type('TEST ADDRESS'); + cy.get('.q-btn-group > .q-btn--standard').click(); + cy.location('href').should('contain', '#/customer/1107/address'); + cy.get('.q-card__section > .address-card').should( + 'have.length', + addressCards_before + 1, + ); + cy.get('.q-card__section > .address-card') + .eq(addressCards_before) + .should('be.visible') + .get('.text-weight-bold') + .eq(addressCards_before - 1) + .should('contain', addressName) + .click(); + }); + cy.get( + '.q-card > :nth-child(1) > :nth-child(2) > .q-checkbox > .q-checkbox__inner', + ) + .should('have.class', 'q-checkbox__inner--falsy') + .click(); + + cy.get('.q-btn-group > .q-btn--standard > .q-btn__content').click(); + cy.get( + ':nth-child(2) > :nth-child(2) > .flex > .q-mr-lg > .q-checkbox__inner', + ).should('have.class', 'q-checkbox__inner--truthy'); + }); }); diff --git a/test/cypress/integration/client/clientFiscalData.spec.js b/test/cypress/integration/client/clientFiscalData.spec.js index 05e0772e9..d189f896a 100644 --- a/test/cypress/integration/client/clientFiscalData.spec.js +++ b/test/cypress/integration/client/clientFiscalData.spec.js @@ -3,9 +3,8 @@ describe('Client fiscal data', () => { beforeEach(() => { cy.viewport(1280, 720); cy.login('developer'); - cy.visit('#/customer/1107/fiscal-data', { - timeout: 5000, - }); + cy.visit('#/customer/1107/fiscal-data'); + cy.domContentLoad(); }); it('Should change required value when change customer', () => { cy.get('.q-card').should('be.visible'); @@ -15,4 +14,25 @@ describe('Client fiscal data', () => { cy.get('.q-item > .q-item__label').should('have.text', ' #1'); cy.dataCy('sageTaxTypeFk').filter('input').should('have.attr', 'required'); }); + + it('check as equalizated', () => { + cy.get( + ':nth-child(1) > .q-checkbox > .q-checkbox__inner > .q-checkbox__bg', + ).click(); + cy.get('.q-btn-group > .q-btn--standard > .q-btn__content').click(); + + cy.get('.q-card > :nth-child(1) > span').should( + 'contain', + 'You changed the equalization tax', + ); + + cy.get('.q-card > :nth-child(2) > span').should( + 'have.text', + 'Do you want to spread the change?', + ); + cy.get('[data-cy="VnConfirm_confirm"] > .q-btn__content > .block').click(); + cy.get( + '.bg-warning > .q-notification__wrapper > .q-notification__content > .q-notification__message', + ).should('have.text', 'Equivalent tax spreaded'); + }); }); diff --git a/test/cypress/integration/entry/entryList.spec.js b/test/cypress/integration/entry/entryList.spec.js index 5e2fa0c01..4f99f0cb6 100644 --- a/test/cypress/integration/entry/entryList.spec.js +++ b/test/cypress/integration/entry/entryList.spec.js @@ -106,8 +106,9 @@ describe('Entry', () => { cy.get(`td[data-col-field="${field}"][data-row-index="${row}"]`); const selectSpan = (field, row = 0) => selectCell(field, row).find('div > span'); const selectButton = (cySelector) => cy.get(`button[data-cy="${cySelector}"]`); - const clickAndType = (field, value, row = 0) => - selectCell(field, row).click().type(value); + const clickAndType = (field, value, row = 0) => { + selectCell(field, row).click().type(`${value}{esc}`); + }; const checkText = (field, expectedText, row = 0) => selectCell(field, row).should('have.text', expectedText); const checkColor = (field, expectedColor, row = 0) => @@ -115,21 +116,18 @@ describe('Entry', () => { createEntryAndBuy(); - selectCell('isIgnored') - .click() - .click() - .trigger('keydown', { key: 'Tab', keyCode: 9, which: 9 }); - checkText('isIgnored', 'check'); - checkColor('quantity', COLORS.negative); + selectCell('isIgnored').click().click().type('{esc}'); + checkText('isIgnored', 'close'); clickAndType('stickers', '1'); - checkText('quantity', '11'); - checkText('amount', '550.00'); + checkText('stickers', '0/01'); + checkText('quantity', '1'); + checkText('amount', '50.00'); clickAndType('packing', '2'); - checkText('packing', '12close'); + checkText('packing', '12'); checkText('weight', '12.0'); - checkText('quantity', '132'); - checkText('amount', '6600.00'); + checkText('quantity', '12'); + checkText('amount', '600.00'); checkColor('packing', COLORS.enabled); selectCell('groupingMode').click().click().click(); @@ -137,7 +135,7 @@ describe('Entry', () => { checkColor('grouping', COLORS.enabled); selectCell('buyingValue').click().clear().type('{backspace}{backspace}1'); - checkText('amount', '132.00'); + checkText('amount', '12.00'); checkColor('minPrice', COLORS.disable); selectCell('hasMinPrice').click().click(); @@ -145,7 +143,7 @@ describe('Entry', () => { selectCell('hasMinPrice').click(); cy.saveCard(); - cy.get('span[data-cy="footer-stickers"]').should('have.text', '11'); + cy.get('span[data-cy="footer-stickers"]').should('have.text', '1'); cy.get('.q-notification__message').contains('Data saved'); selectButton('change-quantity-sign').should('be.disabled'); @@ -156,9 +154,9 @@ describe('Entry', () => { selectButton('change-quantity-sign').click(); selectButton('set-negative-quantity').click(); - checkText('quantity', '-132'); + checkText('quantity', '-12'); selectButton('set-positive-quantity').click(); - checkText('quantity', '132'); + checkText('quantity', '12'); checkColor('amount', COLORS.disable); selectButton('check-buy-amount').click(); diff --git a/test/cypress/integration/entry/stockBought.spec.js b/test/cypress/integration/entry/stockBought.spec.js index d2d2b414d..bc36156b4 100644 --- a/test/cypress/integration/entry/stockBought.spec.js +++ b/test/cypress/integration/entry/stockBought.spec.js @@ -6,7 +6,7 @@ describe('EntryStockBought', () => { }); it('Should edit the reserved space', () => { cy.get('.q-field__native.q-placeholder').should('have.value', '01/01/2001'); - cy.get('td[data-col-field="reserve"]').click(); + cy.get('[data-col-field="reserve"][data-row-index="0"]').click(); cy.get('input[name="reserve"]').type('10{enter}'); cy.get('button[title="Save"]').click(); cy.get('.q-notification__message').should('have.text', 'Data saved'); @@ -16,25 +16,35 @@ describe('EntryStockBought', () => { 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('input[aria-label="Buyer"]').type('buyerBossNick'); + cy.get('div[role="listbox"] > div > div[role="option"]') + .eq(0) + .should('be.visible') + .click(); + + cy.get('[data-cy="FormModelPopup_save"]').click(); cy.get('.q-notification__message').should('have.text', 'Data created'); + + cy.get('[data-col-field="reserve"][data-row-index="1"]').click().clear(); + cy.get('[data-cy="searchBtn"]').eq(1).click(); + cy.get('.q-table__bottom.row.items-center.q-table__bottom--nodata') + .should('have.text', 'warningNo data available') + .type('{esc}'); + cy.get('[data-col-field="reserve"][data-row-index="1"]') + .click() + .type('{backspace}{enter}'); + cy.get('[data-cy="crudModelDefaultSaveBtn"]').should('be.enabled').click(); + cy.get('.q-notification__message').eq(1).should('have.text', 'Data saved'); }); it('Should check detail for the buyer', () => { - cy.get(':nth-child(1) > .sticky > .q-btn > .q-btn__content > .q-icon').click(); + cy.get('[data-cy="searchBtn"]').eq(0).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('[data-cy="edit-travel"]').should('be.visible').click(); + cy.get('input[aria-label="m3"]').clear().type('60'); + cy.get('[data-cy="FormModelPopup_save"]').click(); cy.get('.vn-row > div > :nth-child(2)').should('have.text', '60'); }); });