diff --git a/.gitignore b/.gitignore
index 213384ef5..617169f6f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,5 +29,5 @@ yarn-error.log*
*.sln
# Cypress directories and files
-/tests/cypress/videos
-/tests/cypress/screenshots
\ No newline at end of file
+/test/cypress/videos
+/test/cypress/screenshots
diff --git a/src/components/common/__tests__/VnDms.spec.js b/src/components/common/__tests__/VnDms.spec.js
new file mode 100644
index 000000000..03028aee7
--- /dev/null
+++ b/src/components/common/__tests__/VnDms.spec.js
@@ -0,0 +1,146 @@
+import { createWrapper, axios } from 'app/test/vitest/helper';
+import { vi, afterEach, beforeEach, beforeAll, describe, expect, it } from 'vitest';
+import VnDms from 'src/components/common/VnDms.vue';
+
+class MockFormData {
+ constructor() {
+ this.entries = {};
+ }
+
+ append(key, value) {
+ if (!key) {
+ throw new Error('Key is required for FormData.append');
+ }
+ this.entries[key] = value;
+ }
+
+ get(key) {
+ return this.entries[key] || null;
+ }
+
+ getAll() {
+ return this.entries;
+ }
+}
+
+global.FormData = MockFormData;
+
+describe('VnDms', () => {
+ let wrapper;
+ let vm;
+ let postMock;
+
+ const postResponseMock = { data: { success: true } };
+
+ const data = {
+ hasFile: true,
+ hasFileAttached: true,
+ reference: 'DMS-test',
+ warehouseFk: 1,
+ companyFk: 2,
+ dmsTypeFk: 3,
+ description: 'This is a test description',
+ files: { name: 'example.txt', content: new Blob(['file content'], { type: 'text/plain' })},
+ };
+
+ const expectedBody = {
+ hasFile: true,
+ hasFileAttached: true,
+ reference: 'DMS-test',
+ warehouseId: 1,
+ companyId: 2,
+ dmsTypeId: 3,
+ description: 'This is a test description',
+ };
+
+ beforeAll(() => {
+ wrapper = createWrapper(VnDms, {
+ propsData: {
+ url: '/test',
+ formInitialData: { id: 1, reference: 'test' },
+ model: 'Worker',
+ }
+ });
+ wrapper = wrapper.wrapper;
+ vm = wrapper.vm;
+ vi.spyOn(vm, '$emit');
+ });
+
+ beforeEach(() => {
+ postMock = vi.spyOn(axios, 'post').mockResolvedValue(postResponseMock);
+ vm.dms = data;
+ });
+
+ afterEach(() => {
+ vi.clearAllMocks();
+ });
+
+ describe('mapperDms', () => {
+ it('should map DMS data correctly and add file to FormData', () => {
+ const [formData, params] = vm.mapperDms(data);
+
+ expect(formData.get('example.txt')).toBe(data.files);
+ expect(expectedBody).toEqual(params.params);
+ });
+
+ it('should map DMS data correctly without file', () => {
+ delete data.files;
+
+ const [formData, params] = vm.mapperDms(data);
+
+ expect(formData.getAll()).toEqual({});
+ expect(expectedBody).toEqual(params.params);
+ });
+ });
+
+ describe('getUrl', () => {
+ it('should returns prop url when is set', async () => {
+ expect(vm.getUrl()).toBe('/test');
+ });
+
+ it('should returns url dms/"props.formInitialData.id"/updateFile when prop url is null', async () => {
+ await wrapper.setProps({ url: null });
+ expect(vm.getUrl()).toBe('dms/1/updateFile');
+ });
+
+ it('should returns url "props.model"/"route.params.id"/uploadFile when formInitialData is null', async () => {
+ await wrapper.setProps({ formInitialData: null });
+ vm.route.params.id = '123';
+ expect(vm.getUrl()).toBe('Worker/123/uploadFile');
+ });
+ });
+
+ describe('save', () => {
+ it('should save data correctly', async () => {
+ await vm.save();
+ expect(postMock).toHaveBeenCalledWith(vm.getUrl(), expect.any(FormData), { params: expectedBody });
+ expect(wrapper.emitted('onDataSaved')).toBeTruthy();
+ });
+ });
+
+ describe('defaultData', () => {
+ it('should set dms with formInitialData', async () => {
+ const testData = {
+ hasFile: false,
+ hasFileAttached: false,
+ reference: 'defaultData-test',
+ warehouseFk: 2,
+ companyFk: 3,
+ dmsTypeFk: 2,
+ description: 'This is a test description'
+ }
+ await wrapper.setProps({ formInitialData: testData });
+ vm.defaultData();
+
+ expect(vm.dms).toEqual(testData);
+ });
+
+ it('should add reference with "route.params.id" to dms if formInitialData is null', async () => {
+ await wrapper.setProps({ formInitialData: null });
+ vm.route.params.id= '111';
+ vm.defaultData();
+
+ expect(vm.dms.reference).toBe('111');
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/components/ui/CatalogItem.vue b/src/components/ui/CatalogItem.vue
index 9670d9b68..7806562b2 100644
--- a/src/components/ui/CatalogItem.vue
+++ b/src/components/ui/CatalogItem.vue
@@ -41,7 +41,7 @@ const card = toRef(props, 'item');
-
+
{{ card.name }}
diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue
index aba797678..93f069cc6 100644
--- a/src/components/ui/VnFilterPanel.vue
+++ b/src/components/ui/VnFilterPanel.vue
@@ -190,7 +190,10 @@ const getLocale = (label) => {
const globalLocale = `globals.params.${param}`;
if (te(globalLocale)) return t(globalLocale);
else if (te(t(`params.${param}`)));
- else return t(`${route.meta.moduleName.toLowerCase()}.params.${param}`);
+ else {
+ const camelCaseModuleName = route.meta.moduleName.charAt(0).toLowerCase() + route.meta.moduleName.slice(1);
+ return t(`${camelCaseModuleName}.params.${param}`);
+ }
};
diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
index d7838d58e..89d4dee5c 100644
--- a/src/composables/useArrayData.js
+++ b/src/composables/useArrayData.js
@@ -170,10 +170,9 @@ export function useArrayData(key, userOptions) {
async function addOrder(field, direction = 'ASC') {
const newOrder = field + ' ' + direction;
- let order = store.order || [];
- if (typeof order == 'string') order = [order];
+ const order = toArray(store.order);
- let index = order.findIndex((o) => o.split(' ')[0] === field);
+ let index = getOrderIndex(order, field);
if (index > -1) {
order[index] = newOrder;
} else {
@@ -190,16 +189,23 @@ export function useArrayData(key, userOptions) {
}
async function deleteOrder(field) {
- let order = store.order ?? [];
- if (typeof order == 'string') order = [order];
-
- const index = order.findIndex((o) => o.split(' ')[0] === field);
+ const order = toArray(store.order);
+ const index = getOrderIndex(order, field);
if (index > -1) order.splice(index, 1);
store.order = order;
fetch({});
}
+ function getOrderIndex(order, field) {
+ return order.findIndex((o) => o.split(' ')[0] === field);
+ }
+
+ function toArray(str = []) {
+ if (Array.isArray(str)) return str;
+ if (typeof str === 'string') return str.split(',').map((item) => item.trim());
+ }
+
function sanitizerParams(params, exprBuilder) {
for (const param in params) {
if (params[param] === '' || params[param] === null) {
@@ -290,8 +296,7 @@ export function useArrayData(key, userOptions) {
Object.assign(params, store.userParams);
if (params.filter) params.filter.skip = store.skip;
- if (store?.order && typeof store?.order == 'string') store.order = [store.order];
- if (store.order?.length) params.filter.order = [...store.order];
+ if (store.order) params.filter.order = toArray(store.order);
else delete params.filter.order;
return { filter, params, limit: filter.limit };
diff --git a/src/css/app.scss b/src/css/app.scss
index 7ef52c9ca..59e945f05 100644
--- a/src/css/app.scss
+++ b/src/css/app.scss
@@ -316,11 +316,11 @@ input::-webkit-inner-spin-button {
}
.q-item > .q-item__section:has(.q-checkbox) {
- max-width: min-content;
+ max-width: fit-content;
}
.row > .column:has(.q-checkbox) {
- max-width: min-content;
+ max-width: fit-content;
}
.q-field__inner {
.q-field__control {
diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index cdd75f29a..3d50337ef 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -5,6 +5,7 @@ globals:
quantity: Quantity
language: Language
entity: Entity
+ preview: Preview
user: User
details: Details
collapseMenu: Collapse lateral menu
@@ -734,62 +735,6 @@ travel:
destination: Destination
thermograph: Thermograph
travelFileDescription: 'Travel id { travelId }'
-item:
- descriptor:
- buyer: Buyer
- color: Color
- category: Category
- available: Available
- warehouseText: 'Calculated on the warehouse of { warehouseName }'
- itemDiary: Item diary
- list:
- id: Identifier
- stems: Stems
- category: Category
- typeName: Type
- isActive: Active
- userName: Buyer
- weightByPiece: Weight/Piece
- stemMultiplier: Multiplier
- fixedPrice:
- itemFk: Item ID
- groupingPrice: Grouping price
- packingPrice: Packing price
- hasMinPrice: Has min price
- minPrice: Min price
- started: Started
- ended: Ended
- create:
- priority: Priority
- buyRequest:
- requester: Requester
- requested: Requested
- attender: Atender
- achieved: Achieved
- concept: Concept
- summary:
- otherData: Other data
- tax: Tax
- botanical: Botanical
- barcode: Barcode
- completeName: Complete name
- family: Familiy
- stems: Stems
- multiplier: Multiplier
- buyer: Buyer
- doPhoto: Do photo
- intrastatCode: Intrastat code
- ref: Reference
- relevance: Relevance
- weight: Weight (gram)/stem
- units: Units/box
- expense: Expense
- generic: Generic
- recycledPlastic: Recycled plastic
- nonRecycledPlastic: Non recycled plastic
- minSalesQuantity: Min sales quantity
- genus: Genus
- specie: Specie
components:
topbar: {}
itemsFilterPanel:
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index abadaa2dc..1eae05f51 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -5,6 +5,7 @@ globals:
language: Idioma
quantity: Cantidad
entity: Entidad
+ preview: Vista previa
user: Usuario
details: Detalles
collapseMenu: Contraer menú lateral
@@ -730,62 +731,6 @@ travel:
destination: Destino
thermograph: Termógrafo
travelFileDescription: 'Id envío { travelId }'
-item:
- descriptor:
- buyer: Comprador
- color: Color
- category: Categoría
- available: Disponible
- warehouseText: 'Calculado sobre el almacén de { warehouseName }'
- itemDiary: Registro de compra-venta
- list:
- id: Identificador
- stems: Tallos
- category: Reino
- typeName: Tipo
- isActive: Activo
- weightByPiece: Peso (gramos)/tallo
- userName: Comprador
- stemMultiplier: Multiplicador
- fixedPrice:
- itemFk: ID Artículo
- groupingPrice: Precio grouping
- packingPrice: Precio packing
- hasMinPrice: Tiene precio mínimo
- minPrice: Precio min
- started: Inicio
- ended: Fin
- create:
- priority: Prioridad
- summary:
- otherData: Otros datos
- tax: IVA
- botanical: Botánico
- barcode: Código de barras
- completeName: Nombre completo
- family: Familia
- stems: Tallos
- multiplier: Multiplicador
- buyer: Comprador
- doPhoto: Hacer foto
- intrastatCode: Código intrastat
- ref: Referencia
- relevance: Relevancia
- weight: Peso (gramos)/tallo
- units: Unidades/caja
- expense: Gasto
- generic: Genérico
- recycledPlastic: Plástico reciclado
- nonRecycledPlastic: Plástico no reciclado
- minSalesQuantity: Cantidad mínima de venta
- genus: Genus
- specie: Specie
- buyRequest:
- requester: Solicitante
- requested: Solicitado
- attender: Comprador
- achieved: Conseguido
- concept: Concepto
components:
topbar: {}
itemsFilterPanel:
diff --git a/src/pages/Customer/Card/CustomerConsumption.vue b/src/pages/Customer/Card/CustomerConsumption.vue
index 640e37ed3..f0d8dea47 100644
--- a/src/pages/Customer/Card/CustomerConsumption.vue
+++ b/src/pages/Customer/Card/CustomerConsumption.vue
@@ -152,7 +152,8 @@ const updateDateParams = (value, params) => {
v-if="campaignList"
data-key="CustomerConsumption"
url="Clients/consumption"
- :order="['itemTypeFk', 'itemName', 'itemSize', 'description']"
+ :order="['itemTypeFk', 'itemName', 'itemSize', 'description']"
+ :filter="{ where: { clientFk: route.params.id } }"
:columns="columns"
search-url="consumption"
:user-params="userParams"
diff --git a/src/pages/Customer/Card/CustomerDescriptor.vue b/src/pages/Customer/Card/CustomerDescriptor.vue
index cb49109d0..4a064843a 100644
--- a/src/pages/Customer/Card/CustomerDescriptor.vue
+++ b/src/pages/Customer/Card/CustomerDescriptor.vue
@@ -187,14 +187,18 @@ const debtWarning = computed(() => {
- {{ t('Go to user') }}
+ {{ t('globals.pageTitles.createOrder') }}
{
};
const clientFk = {
ticket: 'clientId',
- order: 'clientFk',
};
const key = clientFk[type];
if (!key) return;
@@ -70,11 +69,6 @@ const openCreateForm = (type) => {
{{ t('globals.pageTitles.createTicket') }}
-
-
- {{ t('globals.pageTitles.createOrder') }}
-
-
{{ t('Send SMS') }}
diff --git a/src/pages/Customer/CustomerList.vue b/src/pages/Customer/CustomerList.vue
index f6458fd64..f345fcda3 100644
--- a/src/pages/Customer/CustomerList.vue
+++ b/src/pages/Customer/CustomerList.vue
@@ -408,7 +408,7 @@ function handleLocation(data, location) {
order: ['id DESC'],
}"
>
-
+
diff --git a/src/pages/Customer/components/CustomerSamplesCreate.vue b/src/pages/Customer/components/CustomerSamplesCreate.vue
index 665e136e4..754693672 100644
--- a/src/pages/Customer/components/CustomerSamplesCreate.vue
+++ b/src/pages/Customer/components/CustomerSamplesCreate.vue
@@ -214,7 +214,7 @@ const toCustomerSamples = () => {
diff --git a/src/pages/Entry/EntryList.vue b/src/pages/Entry/EntryList.vue
index 641042156..3172c6d0e 100644
--- a/src/pages/Entry/EntryList.vue
+++ b/src/pages/Entry/EntryList.vue
@@ -205,7 +205,7 @@ const columns = computed(() => [
userFilter: entryFilter,
}"
>
-
+
@@ -231,7 +231,7 @@ const columns = computed(() => [
>
{{
t(
- 'entry.list.tableVisibleColumns.isExcludedFromAvailable'
+ 'entry.list.tableVisibleColumns.isExcludedFromAvailable',
)
}}
diff --git a/src/pages/Item/Card/ItemCard.vue b/src/pages/Item/Card/ItemCard.vue
index 2412f2bf9..2546982eb 100644
--- a/src/pages/Item/Card/ItemCard.vue
+++ b/src/pages/Item/Card/ItemCard.vue
@@ -1,19 +1,11 @@
-
diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 00d0f5c4e..8057eb7ad 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -6,18 +6,17 @@ import VnImg from 'src/components/ui/VnImg.vue';
import VnTable from 'components/VnTable/VnTable.vue';
import { toDate } from 'src/filters';
import FetchedTags from 'src/components/ui/FetchedTags.vue';
-import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import ItemSummary from '../Item/Card/ItemSummary.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue';
import ItemTypeDescriptorProxy from './ItemType/Card/ItemTypeDescriptorProxy.vue';
import { cloneItem } from 'src/pages/Item/composables/cloneItem';
-import RightMenu from 'src/components/common/RightMenu.vue';
import ItemListFilter from './ItemListFilter.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import axios from 'axios';
+import VnSection from 'src/components/common/VnSection.vue';
const entityId = computed(() => route.params.id);
const { openCloneDialog } = cloneItem();
@@ -25,9 +24,11 @@ const { viewSummary } = useSummaryDialog();
const { t } = useI18n();
const tableRef = ref();
const route = useRoute();
+const dataKey = 'ItemList';
const validPriorities = ref([]);
const defaultTag = ref();
const defaultPriority = ref();
+
const itemFilter = {
include: [
{
@@ -324,166 +325,174 @@ onBeforeMount(async () => {
});
});
+
-
-
-
+
+
-
-
-
-
-
-
-
- {{ row.id }}
-
-
-
-
-
- {{ row.typeName }}
- {{ row.typeFk }}
-
-
-
-
-
- {{ row.userName }}
-
-
-
-
-
- {{ row?.name }}
-
- {{ row?.subName.toUpperCase() }}
-
-
-
-
-
-
-
+
-
-
-
- {{ scope.opt?.name }}
- #{{ scope.opt?.id }}
-
-
+
+
-
-
-
-
-
-
- {{ scope.opt?.name }}
-
- {{ scope.opt?.code }} #{{ scope.opt?.id }}
-
-
-
+
+
+ {{ row.id }}
+
+
-
-
-
-
-
- {{ scope.opt?.description }}
- #{{ scope.opt?.id }}
-
-
+
+
+ {{ row.typeName }}
+ {{ row.typeFk }}
+
+
-
-
-
-
-
- {{ scope.opt?.name }}
-
- {{ scope.opt?.code }} #{{ scope.opt?.id }}
-
-
-
+
+
+ {{ row.userName }}
+
+
-
+
+
+ {{ row?.name }}
+
+ {{ row?.subName.toUpperCase() }}
+
+
+
+
+
+
+
+
+
+
+ {{ scope.opt?.name }}
+
+ #{{ scope.opt?.id }}
+
+
+
+
+
+
+
+
+
+
+ {{ scope.opt?.name }}
+
+ {{ scope.opt?.code }} #{{ scope.opt?.id }}
+
+
+
+
+
+
+
+
+
+ {{ scope.opt?.description }}
+
+ #{{ scope.opt?.id }}
+
+
+
+
+
+
+
+
+
+ {{ scope.opt?.name }}
+
+ {{ scope.opt?.code }} #{{ scope.opt?.id }}
+
+
+
+
+
+
+
-
+