diff --git a/src/components/ui/VnNotes.vue b/src/components/ui/VnNotes.vue
index bcbf0945e..e308ea9bb 100644
--- a/src/components/ui/VnNotes.vue
+++ b/src/components/ui/VnNotes.vue
@@ -6,7 +6,6 @@ import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import { toDateHourMin } from 'src/filters';
-import { useState } from 'src/composables/useState';
import VnPaginate from 'components/ui/VnPaginate.vue';
import VnUserLink from 'components/ui/VnUserLink.vue';
@@ -26,9 +25,7 @@ const $props = defineProps({
});
const { t } = useI18n();
-const state = useState();
const quasar = useQuasar();
-const currentUser = ref(state.getUser());
const newNote = reactive({ text: null, observationTypeFk: null });
const observationTypes = ref([]);
const vnPaginateRef = ref();
diff --git a/src/components/ui/VnPaginate.vue b/src/components/ui/VnPaginate.vue
index 3649ba8f5..a2ccd5d92 100644
--- a/src/components/ui/VnPaginate.vue
+++ b/src/components/ui/VnPaginate.vue
@@ -74,6 +74,10 @@ const props = defineProps({
type: Boolean,
default: false,
},
+ mapKey: {
+ type: String,
+ default: '',
+ },
});
const emit = defineEmits(['onFetch', 'onPaginate', 'onChange']);
@@ -96,15 +100,19 @@ const arrayData = useArrayData(props.dataKey, {
exprBuilder: props.exprBuilder,
keepOpts: props.keepOpts,
searchUrl: props.searchUrl,
+ mapKey: props.mapKey,
});
const store = arrayData.store;
onMounted(async () => {
if (props.autoLoad && !store.data?.length) await fetch();
+ else emit('onFetch', store.data);
mounted.value = true;
});
-onBeforeUnmount(() => arrayData.reset());
+onBeforeUnmount(() => {
+ arrayData.resetPagination();
+});
watch(
() => props.data,
@@ -132,8 +140,8 @@ const addFilter = async (filter, params) => {
async function fetch(params) {
useArrayData(props.dataKey, params);
- arrayData.reset(['filter.skip', 'skip', 'page']);
- await arrayData.fetch({ append: false, updateRouter: mounted.value });
+ arrayData.resetPagination();
+ await arrayData.fetch({ append: false });
return emitStoreData();
}
@@ -195,13 +203,20 @@ async function onLoad(index, done) {
done(isDone);
}
-defineExpose({ fetch, update, addFilter, paginate });
+defineExpose({
+ fetch,
+ update,
+ addFilter,
+ paginate,
+ userParams: arrayData.store.userParams,
+ currentFilter: arrayData.store.currentFilter,
+});
diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue
index ccf87c6d6..4e284d8e4 100644
--- a/src/components/ui/VnSearchbar.vue
+++ b/src/components/ui/VnSearchbar.vue
@@ -51,10 +51,6 @@ const props = defineProps({
type: Object,
default: null,
},
- staticParams: {
- type: Array,
- default: () => [],
- },
exprBuilder: {
type: Function,
default: null,
@@ -67,6 +63,10 @@ const props = defineProps({
type: Function,
default: undefined,
},
+ searchRemoveParams: {
+ type: Boolean,
+ default: true,
+ },
});
const searchText = ref();
@@ -100,17 +100,25 @@ onMounted(() => {
});
async function search() {
- const staticParams = Object.entries(store.userParams);
- arrayData.reset(['skip', 'page']);
+ const staticParams = Object.keys(store.userParams ?? {}).length
+ ? store.userParams
+ : store.defaultParams;
+ arrayData.resetPagination();
const filter = {
params: {
- ...Object.fromEntries(staticParams),
search: searchText.value,
},
- ...{ filter: props.filter },
+ filter: props.filter,
};
+ if (!props.searchRemoveParams || !searchText.value) {
+ filter.params = {
+ ...staticParams,
+ search: searchText.value,
+ };
+ }
+
if (props.whereFilter) {
filter.filter = {
where: props.whereFilter(searchText.value),
diff --git a/test/vitest/__tests__/components/Paginate.spec.js b/src/components/ui/__tests__/Paginate.spec.js
similarity index 76%
rename from test/vitest/__tests__/components/Paginate.spec.js
rename to src/components/ui/__tests__/Paginate.spec.js
index 345903c1a..a67dfcdc6 100644
--- a/test/vitest/__tests__/components/Paginate.spec.js
+++ b/src/components/ui/__tests__/Paginate.spec.js
@@ -4,7 +4,11 @@ import VnPaginate from 'src/components/ui/VnPaginate.vue';
describe('VnPaginate', () => {
const expectedUrl = '/api/customers';
-
+ const defaultData = [
+ { id: 1, name: 'Tony Stark' },
+ { id: 2, name: 'Jessica Jones' },
+ { id: 3, name: 'Bruce Wayne' },
+ ];
let vm;
beforeAll(() => {
const options = {
@@ -28,11 +32,7 @@ describe('VnPaginate', () => {
describe('paginate()', () => {
it('should call to the paginate() method and set the data on the rows property', async () => {
vi.spyOn(vm.arrayData, 'loadMore');
- vm.store.data = [
- { id: 1, name: 'Tony Stark' },
- { id: 2, name: 'Jessica Jones' },
- { id: 3, name: 'Bruce Wayne' },
- ];
+ vm.store.data = defaultData;
await vm.paginate();
@@ -42,26 +42,25 @@ describe('VnPaginate', () => {
it('should call to the paginate() method and then call it again to paginate', async () => {
vi.spyOn(axios, 'get').mockResolvedValue({
- data: [
- { id: 1, name: 'Tony Stark' },
- { id: 2, name: 'Jessica Jones' },
- { id: 3, name: 'Bruce Wayne' },
- ],
+ data: defaultData,
});
vm.store.hasMoreData = true;
await vm.$nextTick();
- vm.store.data = [
- { id: 1, name: 'Tony Stark' },
- { id: 2, name: 'Jessica Jones' },
- { id: 3, name: 'Bruce Wayne' },
- ];
+ vm.store.data = defaultData;
await vm.paginate();
expect(vm.store.skip).toEqual(3);
expect(vm.store.data.length).toEqual(6);
+ vi.spyOn(axios, 'get').mockResolvedValue({
+ data: [
+ { id: 4, name: 'Peter Parker' },
+ { id: 5, name: 'Clark Kent' },
+ { id: 6, name: 'Barry Allen' },
+ ],
+ });
await vm.paginate();
expect(vm.store.skip).toEqual(6);
@@ -85,11 +84,7 @@ describe('VnPaginate', () => {
const index = 1;
const done = vi.fn();
- vm.store.data = [
- { id: 1, name: 'Tony Stark' },
- { id: 2, name: 'Jessica Jones' },
- { id: 3, name: 'Bruce Wayne' },
- ];
+ vm.store.data = defaultData;
await vm.onLoad(index, done);
@@ -105,11 +100,7 @@ describe('VnPaginate', () => {
],
});
- vm.store.data = [
- { id: 1, name: 'Tony Stark' },
- { id: 2, name: 'Jessica Jones' },
- { id: 3, name: 'Bruce Wayne' },
- ];
+ vm.store.data = defaultData;
expect(vm.pagination.page).toEqual(1);
diff --git a/test/vitest/__tests__/components/common/VnLinkPhone.spec.js b/src/components/ui/__tests__/VnLinkPhone.spec.js
similarity index 100%
rename from test/vitest/__tests__/components/common/VnLinkPhone.spec.js
rename to src/components/ui/__tests__/VnLinkPhone.spec.js
diff --git a/test/vitest/__tests__/components/common/VnSms.spec.js b/src/components/ui/__tests__/VnSms.spec.js
similarity index 100%
rename from test/vitest/__tests__/components/common/VnSms.spec.js
rename to src/components/ui/__tests__/VnSms.spec.js
diff --git a/test/vitest/__tests__/composables/downloadFile.spec.js b/src/composables/__tests__/downloadFile.spec.js
similarity index 100%
rename from test/vitest/__tests__/composables/downloadFile.spec.js
rename to src/composables/__tests__/downloadFile.spec.js
diff --git a/src/composables/__tests__/getExchange.spec.js b/src/composables/__tests__/getExchange.spec.js
new file mode 100644
index 000000000..dba31458e
--- /dev/null
+++ b/src/composables/__tests__/getExchange.spec.js
@@ -0,0 +1,45 @@
+import { describe, expect, it, vi } from 'vitest';
+import axios from 'axios';
+import { getExchange } from 'src/composables/getExchange';
+
+vi.mock('axios');
+
+describe('getExchange()', () => {
+ it('should return the correct exchange rate', async () => {
+ axios.get.mockResolvedValue({
+ data: { value: 1.2 },
+ });
+
+ const amount = 100;
+ const currencyFk = 1;
+ const dated = '2023-01-01';
+ const result = await getExchange(amount, currencyFk, dated);
+
+ expect(result).toBe('83.33');
+ });
+
+ it('should return the correct exchange rate with custom decimal places', async () => {
+ axios.get.mockResolvedValue({
+ data: { value: 1.2 },
+ });
+
+ const amount = 100;
+ const currencyFk = 1;
+ const dated = '2023-01-01';
+ const decimalPlaces = 3;
+ const result = await getExchange(amount, currencyFk, dated, decimalPlaces);
+
+ expect(result).toBe('83.333');
+ });
+
+ it('should return null if the API call fails', async () => {
+ axios.get.mockRejectedValue(new Error('Network error'));
+
+ const amount = 100;
+ const currencyFk = 1;
+ const dated = '2023-01-01';
+ const result = await getExchange(amount, currencyFk, dated);
+
+ expect(result).toBeNull();
+ });
+});
diff --git a/src/composables/__tests__/getTotal.spec.js b/src/composables/__tests__/getTotal.spec.js
new file mode 100644
index 000000000..789e3fbcf
--- /dev/null
+++ b/src/composables/__tests__/getTotal.spec.js
@@ -0,0 +1,55 @@
+import { vi, describe, expect, it } from 'vitest';
+import { getTotal } from 'src/composables/getTotal';
+
+vi.mock('src/filters', () => ({
+ toCurrency: vi.fn((value, currency) => `${currency} ${value.toFixed(2)}`),
+}));
+
+describe('getTotal()', () => {
+ const rows = [
+ { amount: 10.5, tax: 2.1 },
+ { amount: 20.75, tax: 3.25 },
+ { amount: 30.25, tax: 4.75 },
+ ];
+
+ it('should calculate the total for a given key', () => {
+ const total = getTotal(rows, 'amount');
+ expect(total).toBe('61.50');
+ });
+
+ it('should calculate the total with a callback function', () => {
+ const total = getTotal(rows, null, { cb: (row) => row.amount + row.tax });
+ expect(total).toBe('71.60');
+ });
+
+ it('should format the total as currency', () => {
+ const total = getTotal(rows, 'amount', { currency: 'USD' });
+ expect(total).toBe('USD 61.50');
+ });
+
+ it('should format the total as currency with default currency', () => {
+ const total = getTotal(rows, 'amount', { currency: 'default' });
+ expect(total).toBe('undefined 61.50');
+ });
+
+ it('should calculate the total with integer formatting', () => {
+ const total = getTotal(rows, 'amount', { decimalPlaces: 0 });
+ expect(total).toBe('62');
+ });
+
+ it('should calculate the total with custom decimal places', () => {
+ const total = getTotal(rows, 'amount', { decimalPlaces: 1 });
+ expect(total).toBe('61.5');
+ });
+
+ it('should handle rows with missing keys', () => {
+ const rowsWithMissingKeys = [{ amount: 10.5 }, { amount: 20.75 }, {}];
+ const total = getTotal(rowsWithMissingKeys, 'amount');
+ expect(total).toBe('31.25');
+ });
+
+ it('should handle empty rows', () => {
+ const total = getTotal([], 'amount');
+ expect(total).toBe('0.00');
+ });
+});
diff --git a/src/composables/__tests__/useAccountShortToStandard.spec.js b/src/composables/__tests__/useAccountShortToStandard.spec.js
new file mode 100644
index 000000000..d24585812
--- /dev/null
+++ b/src/composables/__tests__/useAccountShortToStandard.spec.js
@@ -0,0 +1,9 @@
+import { describe, expect, it } from 'vitest';
+import { useAccountShortToStandard } from 'src/composables/useAccountShortToStandard';
+
+describe('useAccountShortToStandard()', () => {
+ it('should pad the decimal part with zeros for short numbers', () => {
+ expect(useAccountShortToStandard('123.45')).toBe('1230000045');
+ expect(useAccountShortToStandard('123.')).toBe('1230000000');
+ });
+});
diff --git a/test/vitest/__tests__/composables/useAcl.spec.js b/src/composables/__tests__/useAcl.spec.js
similarity index 100%
rename from test/vitest/__tests__/composables/useAcl.spec.js
rename to src/composables/__tests__/useAcl.spec.js
diff --git a/test/vitest/__tests__/composables/useArrayData.spec.js b/src/composables/__tests__/useArrayData.spec.js
similarity index 100%
rename from test/vitest/__tests__/composables/useArrayData.spec.js
rename to src/composables/__tests__/useArrayData.spec.js
diff --git a/test/vitest/__tests__/composables/useRole.spec.js b/src/composables/__tests__/useRole.spec.js
similarity index 100%
rename from test/vitest/__tests__/composables/useRole.spec.js
rename to src/composables/__tests__/useRole.spec.js
diff --git a/test/vitest/__tests__/composables/useSession.spec.js b/src/composables/__tests__/useSession.spec.js
similarity index 100%
rename from test/vitest/__tests__/composables/useSession.spec.js
rename to src/composables/__tests__/useSession.spec.js
diff --git a/test/vitest/__tests__/composables/useTokenConfig.spec.js b/src/composables/__tests__/useTokenConfig.spec.js
similarity index 100%
rename from test/vitest/__tests__/composables/useTokenConfig.spec.js
rename to src/composables/__tests__/useTokenConfig.spec.js
diff --git a/src/composables/getExchange.js b/src/composables/getExchange.js
new file mode 100644
index 000000000..e81e9e895
--- /dev/null
+++ b/src/composables/getExchange.js
@@ -0,0 +1,16 @@
+import axios from 'axios';
+export async function getExchange(amount, currencyFk, dated, decimalPlaces = 2) {
+ try {
+ const { data } = await axios.get('ReferenceRates/findOne', {
+ params: {
+ filter: {
+ fields: ['value'],
+ where: { currencyFk, dated },
+ },
+ },
+ });
+ return (amount / data.value).toFixed(decimalPlaces);
+ } catch (e) {
+ return null;
+ }
+}
diff --git a/src/composables/getTotal.js b/src/composables/getTotal.js
index 41c4330c4..24ac3aa27 100644
--- a/src/composables/getTotal.js
+++ b/src/composables/getTotal.js
@@ -1,10 +1,10 @@
import { toCurrency } from 'src/filters';
export function getTotal(rows, key, opts = {}) {
- const { currency, cb } = opts;
+ const { currency, cb, decimalPlaces } = opts;
const total = rows.reduce((acc, row) => acc + +(cb ? cb(row) : row[key] || 0), 0);
return currency
? toCurrency(total, currency == 'default' ? undefined : currency)
- : total;
+ : parseFloat(total).toFixed(decimalPlaces ?? 2);
}
diff --git a/src/composables/useAccountShortToStandard.js b/src/composables/useAccountShortToStandard.js
new file mode 100644
index 000000000..ca221433e
--- /dev/null
+++ b/src/composables/useAccountShortToStandard.js
@@ -0,0 +1,4 @@
+export function useAccountShortToStandard(val) {
+ if (!val || !/^\d+(\.\d*)$/.test(val)) return;
+ return val?.replace('.', '0'.repeat(11 - val.length));
+}
diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
index da62eee3e..1a91cc50b 100644
--- a/src/composables/useArrayData.js
+++ b/src/composables/useArrayData.js
@@ -25,11 +25,14 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
const searchUrl = store.searchUrl;
if (query[searchUrl]) {
const params = JSON.parse(query[searchUrl]);
- const filter = params?.filter && JSON.parse(params?.filter ?? '{}');
+ const filter =
+ params?.filter && typeof params?.filter == 'object'
+ ? params?.filter
+ : JSON.parse(params?.filter ?? '{}');
delete params.filter;
store.userParams = { ...store.userParams, ...params };
- store.userFilter = { ...filter, ...store.userFilter };
+ store.filter = { ...filter, ...store.userFilter };
if (filter?.order) store.order = filter.order;
}
});
@@ -49,6 +52,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
'exprBuilder',
'searchUrl',
'navigate',
+ 'mapKey',
];
if (typeof userOptions === 'object') {
for (const option in userOptions) {
@@ -60,6 +64,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
store[option] = userOptions.keepOpts?.includes(option)
? Object.assign(defaultOpts, store[option])
: defaultOpts;
+ if (option === 'userParams') store.defaultParams = store[option];
}
}
}
@@ -74,7 +79,6 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
const filter = {
limit: store.limit,
};
-
let userParams = { ...store.userParams };
Object.assign(filter, store.userFilter);
@@ -119,17 +123,12 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
const { limit } = filter;
store.hasMoreData = limit && response.data.length >= limit;
- if (append) {
- if (!store.data) store.data = [];
- for (const row of response.data) store.data.push(row);
- } else {
- store.data = response.data;
- if (!isDialogOpened()) updateRouter && updateStateParams();
- }
+ processData(response.data, { map: !!store.mapKey, append });
+ if (!append && !isDialogOpened()) updateRouter && updateStateParams();
store.isLoading = false;
-
canceller = null;
+
return response;
}
@@ -147,6 +146,10 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
if (arrayDataStore.get(key)) arrayDataStore.reset(key, opts);
}
+ function resetPagination() {
+ if (arrayDataStore.get(key)) arrayDataStore.resetPagination(key);
+ }
+
function cancelRequest() {
if (canceller) {
canceller.abort();
@@ -170,7 +173,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
userParams = sanitizerParams(userParams, store?.exprBuilder);
store.userParams = userParams;
- reset(['skip', 'filter.skip', 'page']);
+ resetPagination();
await fetch({});
return { filter, params };
@@ -197,7 +200,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
}
store.order = order;
- reset(['skip', 'filter.skip', 'page']);
+ resetPagination();
fetch({});
index++;
@@ -280,7 +283,6 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
const pushUrl = { path: to };
if (to.endsWith('/list') || to.endsWith('/'))
pushUrl.query = newUrl.query;
- else destroy();
return router.push(pushUrl);
}
}
@@ -288,6 +290,31 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
router.replace(newUrl);
}
+ function processData(data, { map = true, append = true }) {
+ if (!append) {
+ store.data = [];
+ store.map = new Map();
+ }
+
+ if (!Array.isArray(data)) store.data = data;
+ else if (!map && append) for (const row of data) store.data.push(row);
+ else
+ for (const row of data) {
+ const key = row[store.mapKey];
+ const val = { ...row, key };
+ if (store.map.has(key)) {
+ const { position } = store.map.get(key);
+ val.position = position;
+ store.map.set(key, val);
+ store.data[position] = val;
+ } else {
+ val.position = store.map.size;
+ store.map.set(key, val);
+ store.data.push(val);
+ }
+ }
+ }
+
const totalRows = computed(() => (store.data && store.data.length) || 0);
const isLoading = computed(() => store.isLoading || false);
@@ -307,5 +334,6 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
isLoading,
deleteOption,
reset,
+ resetPagination,
};
}
diff --git a/src/composables/useCau.js b/src/composables/useCau.js
new file mode 100644
index 000000000..29319bd9a
--- /dev/null
+++ b/src/composables/useCau.js
@@ -0,0 +1,73 @@
+import VnInput from 'src/components/common/VnInput.vue';
+import { useVnConfirm } from 'src/composables/useVnConfirm';
+import axios from 'axios';
+import { ref } from 'vue';
+import { i18n } from 'src/boot/i18n';
+import useNotify from 'src/composables/useNotify.js';
+
+export async function useCau(res, message) {
+ const { notify } = useNotify();
+ const { openConfirmationModal } = useVnConfirm();
+ const { config, headers, request, status, statusText, data } = res || {};
+ const { params, url, method, signal, headers: confHeaders } = config || {};
+ const { message: resMessage, code, name } = data?.error || {};
+
+ const additionalData = {
+ path: location.hash,
+ message: resMessage,
+ code,
+ request: request?.responseURL,
+ status,
+ name,
+ statusText: statusText,
+ config: {
+ url,
+ method,
+ params,
+ headers: confHeaders,
+ aborted: signal?.aborted,
+ version: headers?.['salix-version'],
+ },
+ };
+ const opts = {
+ actions: [
+ {
+ icon: 'support_agent',
+ color: 'primary',
+ dense: true,
+ flat: false,
+ round: true,
+ handler: async () => {
+ const locale = i18n.global.t;
+ const reason = ref(
+ code == 'ACCESS_DENIED' ? locale('cau.askPrivileges') : ''
+ );
+ openConfirmationModal(
+ locale('cau.title'),
+ locale('cau.subtitle'),
+ async () => {
+ await axios.post('OsTickets/send-to-support', {
+ reason: reason.value,
+ additionalData,
+ });
+ },
+ null,
+ {
+ component: VnInput,
+ props: {
+ modelValue: reason,
+ 'onUpdate:modelValue': (val) => (reason.value = val),
+ label: locale('cau.inputLabel'),
+ class: 'full-width',
+ required: true,
+ autofocus: true,
+ },
+ }
+ );
+ },
+ },
+ ],
+ };
+
+ notify(message ?? 'globals.error', 'negative', 'error', opts);
+}
diff --git a/src/composables/useFilterParams.js b/src/composables/useFilterParams.js
new file mode 100644
index 000000000..2878e4b76
--- /dev/null
+++ b/src/composables/useFilterParams.js
@@ -0,0 +1,65 @@
+import { useArrayData } from 'src/composables/useArrayData';
+import { onBeforeMount, ref, watch } from 'vue';
+
+export function useFilterParams(key) {
+ if (!key) throw new Error('ArrayData: A key is required to use this composable');
+ const params = ref({});
+ const orders = ref({});
+ const arrayData = ref({});
+
+ onBeforeMount(() => {
+ arrayData.value = useArrayData(key);
+ });
+
+ watch(
+ () => arrayData.value.store?.currentFilter,
+ (val, oldValue) => (val || oldValue) && setUserParams(val),
+ { immediate: true, deep: true }
+ );
+
+ function parseOrder(urlOrders) {
+ const orderObject = {};
+ if (urlOrders) {
+ if (typeof urlOrders == 'string') urlOrders = [urlOrders];
+ for (const [index, orders] of urlOrders.entries()) {
+ const [name, direction] = orders.split(' ');
+ orderObject[name] = { direction, index: index + 1 };
+ }
+ }
+ orders.value = orderObject;
+ }
+
+ function setUserParams(watchedParams) {
+ if (!watchedParams || Object.keys(watchedParams).length == 0) return;
+
+ if (typeof watchedParams == 'string') watchedParams = JSON.parse(watchedParams);
+ if (typeof watchedParams?.filter == 'string')
+ watchedParams.filter = JSON.parse(watchedParams.filter);
+
+ watchedParams = { ...watchedParams, ...watchedParams.filter?.where };
+ parseOrder(watchedParams.filter?.order);
+
+ delete watchedParams.filter;
+ params.value = sanitizer(watchedParams);
+ }
+
+ function sanitizer(params) {
+ for (const [key, value] of Object.entries(params)) {
+ if (key === 'and' && Array.isArray(value)) {
+ value.forEach((item) => {
+ Object.assign(params, item);
+ });
+ delete params[key];
+ } else if (value && typeof value === 'object') {
+ const param = Object.values(value)[0];
+ if (typeof param == 'string') params[key] = param.replaceAll('%', '');
+ }
+ }
+ return params;
+ }
+
+ return {
+ params,
+ orders,
+ };
+}
diff --git a/src/composables/useNotify.js b/src/composables/useNotify.js
index 2f0e1c257..309156d2a 100644
--- a/src/composables/useNotify.js
+++ b/src/composables/useNotify.js
@@ -2,7 +2,7 @@ import { Notify } from 'quasar';
import { i18n } from 'src/boot/i18n';
export default function useNotify() {
- const notify = (message, type, icon) => {
+ const notify = (message, type, icon, opts = {}) => {
const defaultIcons = {
warning: 'warning',
negative: 'error',
@@ -13,6 +13,7 @@ export default function useNotify() {
message: i18n.global.t(message),
type: type,
icon: icon ? icon : defaultIcons[type],
+ ...opts,
});
};
diff --git a/src/composables/useRole.js b/src/composables/useRole.js
index d1a6d6ef3..3ec65dd0a 100644
--- a/src/composables/useRole.js
+++ b/src/composables/useRole.js
@@ -20,7 +20,7 @@ export function useRole() {
function hasAny(roles) {
const roleStore = state.getRoles();
-
+ if (typeof roles === 'string') roles = [roles];
for (const role of roles) {
if (roleStore.value.indexOf(role) !== -1) return true;
}
diff --git a/src/composables/useVnConfirm.js b/src/composables/useVnConfirm.js
index 76c3f4f28..4438ad11d 100644
--- a/src/composables/useVnConfirm.js
+++ b/src/composables/useVnConfirm.js
@@ -1,22 +1,29 @@
+import { h } from 'vue';
+import { Dialog } from 'quasar';
import VnConfirm from 'components/ui/VnConfirm.vue';
-import { useQuasar } from 'quasar';
export function useVnConfirm() {
- const quasar = useQuasar();
-
- const openConfirmationModal = (title, message, promise, successFn) => {
- quasar
- .dialog({
- component: VnConfirm,
- componentProps: {
+ const openConfirmationModal = (
+ title,
+ message,
+ promise,
+ successFn,
+ customHTML = {}
+ ) => {
+ const { component, props } = customHTML;
+ Dialog.create({
+ component: h(
+ VnConfirm,
+ {
title: title,
message: message,
promise: promise,
},
- })
- .onOk(async () => {
- if (successFn) successFn();
- });
+ { customHTML: () => h(component, props) }
+ ),
+ }).onOk(async () => {
+ if (successFn) successFn();
+ });
};
return { openConfirmationModal };
diff --git a/src/css/app.scss b/src/css/app.scss
index 63a9f5c46..e87b37154 100644
--- a/src/css/app.scss
+++ b/src/css/app.scss
@@ -11,6 +11,7 @@ body.body--light {
--vn-text-color: var(--font-color);
--vn-label-color: #5f5f5f;
--vn-accent-color: #e7e3e3;
+ --vn-empty-tag: #acacac;
background-color: var(--vn-page-color);
@@ -26,6 +27,7 @@ body.body--dark {
--vn-text-color: white;
--vn-label-color: #a8a8a8;
--vn-accent-color: #424242;
+ --vn-empty-tag: #2d2d2d;
background-color: var(--vn-page-color);
}
@@ -240,7 +242,7 @@ input::-webkit-inner-spin-button {
.q-table {
th,
td {
- padding: 1px 10px 1px 10px;
+ padding: 1px 3px 1px 3px;
max-width: 130px;
div span {
overflow: hidden;
@@ -264,6 +266,10 @@ input::-webkit-inner-spin-button {
.shrink {
max-width: 75px;
}
+ .number {
+ text-align: right;
+ width: 96px;
+ }
.expand {
max-width: 400px;
}
@@ -292,3 +298,20 @@ input::-webkit-inner-spin-button {
.no-visible {
visibility: hidden;
}
+.q-field__inner {
+ .q-field__control {
+ min-height: auto !important;
+ display: flex;
+ align-items: flex-end;
+ padding-bottom: 2px;
+ .q-field__native.row {
+ min-height: auto !important;
+ }
+ }
+}
+
+.q-date__header-today {
+ border-radius: 12px;
+ border: 1px solid;
+ box-shadow: 0 4px 6px #00000000;
+}
diff --git a/src/filters/getParamWhere.js b/src/filters/getParamWhere.js
index ef00a93ae..baba46f69 100644
--- a/src/filters/getParamWhere.js
+++ b/src/filters/getParamWhere.js
@@ -1,4 +1,3 @@
-// parsing JSON safely
function parseJSON(str, fallback) {
try {
return JSON.parse(str ?? '{}');
diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index 073aee4d5..4a78811e6 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -129,6 +129,7 @@ globals:
small: Small
medium: Medium
big: Big
+ email: Email
pageTitles:
logIn: Login
addressEdit: Update address
@@ -141,7 +142,7 @@ globals:
workCenters: Work centers
modes: Modes
zones: Zones
- zonesList: Zones
+ zonesList: List
deliveryDays: Delivery days
upcomingDeliveries: Upcoming deliveries
role: Role
@@ -329,13 +330,26 @@ globals:
email: Email
SSN: SSN
fi: FI
+ packing: ITP
myTeam: My team
departmentFk: Department
+ from: From
+ to: To
+ supplierFk: Supplier
+ supplierRef: Supplier ref
+ serial: Serial
+ amount: Importe
+ awbCode: AWB
+ correctedFk: Rectified
+ correctingFk: Rectificative
+ daysOnward: Days onward
countryFk: Country
+ companyFk: Company
changePass: Change password
deleteConfirmTitle: Delete selected elements
changeState: Change state
raid: 'Raid {daysInForward} days'
+ isVies: Vies
errors:
statusUnauthorized: Access denied
statusInternalServerError: An internal server error has ocurred
@@ -369,6 +383,11 @@ resetPassword:
repeatPassword: Repeat password
passwordNotMatch: Passwords don't match
passwordChanged: Password changed
+cau:
+ title: Send cau
+ subtitle: By sending this ticket, all the data related to the error, the section, the user, etc., are already sent.
+ inputLabel: Explain why this error should not appear
+ askPrivileges: Ask for privileges
entry:
list:
newEntry: New entry
@@ -398,8 +417,8 @@ entry:
buys: Buys
stickers: Stickers
package: Package
- packing: Packing
- grouping: Grouping
+ packing: Pack.
+ grouping: Group.
buyingValue: Buying value
import: Import
pvp: PVP
@@ -724,7 +743,6 @@ supplier:
sageTransactionTypeFk: Sage transaction type
supplierActivityFk: Supplier activity
isTrucker: Trucker
- isVies: Vies
billingData:
payMethodFk: Billing data
payDemFk: Payment deadline
@@ -843,6 +861,7 @@ components:
ended: To
mine: For me
hasMinPrice: Minimum price
+ warehouseFk: Warehouse
# LatestBuysFilter
salesPersonFk: Buyer
from: From
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index fa615294b..2bfe7ec4b 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -131,6 +131,7 @@ globals:
small: Pequeño/a
medium: Mediano/a
big: Grande
+ email: Correo
pageTitles:
logIn: Inicio de sesión
addressEdit: Modificar consignatario
@@ -143,7 +144,7 @@ globals:
workCenters: Centros de trabajo
modes: Modos
zones: Zonas
- zonesList: Zonas
+ zonesList: Listado
deliveryDays: Días de entrega
upcomingDeliveries: Próximos repartos
role: Role
@@ -335,11 +336,22 @@ globals:
SSN: NSS
fi: NIF
myTeam: Mi equipo
+ from: Desde
+ to: Hasta
+ supplierFk: Proveedor
+ supplierRef: Ref. proveedor
+ serial: Serie
+ amount: Importe
+ awbCode: AWB
+ daysOnward: Días adelante
+ packing: ITP
countryFk: País
+ companyFk: Empresa
changePass: Cambiar contraseña
deleteConfirmTitle: Eliminar los elementos seleccionados
changeState: Cambiar estado
raid: 'Redada {daysInForward} días'
+ isVies: Vies
errors:
statusUnauthorized: Acceso denegado
statusInternalServerError: Ha ocurrido un error interno del servidor
@@ -371,6 +383,11 @@ resetPassword:
repeatPassword: Repetir contraseña
passwordNotMatch: Las contraseñas no coinciden
passwordChanged: Contraseña cambiada
+cau:
+ title: Enviar cau
+ subtitle: Al enviar este cau ya se envían todos los datos relacionados con el error, la sección, el usuario, etc
+ inputLabel: Explique el motivo por el que no deberia aparecer este fallo
+ askPrivileges: Solicitar permisos
entry:
list:
newEntry: Nueva entrada
@@ -401,8 +418,8 @@ entry:
buys: Compras
stickers: Etiquetas
package: Embalaje
- packing: Packing
- grouping: Grouping
+ packing: Pack.
+ grouping: Group.
buyingValue: Coste
import: Importe
pvp: PVP
@@ -492,7 +509,7 @@ invoiceOut:
ticketList: Listado de tickets
summary:
issued: Fecha
- dued: Vencimiento
+ dued: Fecha límite
booked: Contabilizada
taxBreakdown: Desglose impositivo
taxableBase: Base imp.
@@ -719,7 +736,6 @@ supplier:
sageTransactionTypeFk: Tipo de transacción sage
supplierActivityFk: Actividad proveedor
isTrucker: Transportista
- isVies: Vies
billingData:
payMethodFk: Forma de pago
payDemFk: Plazo de pago
@@ -837,6 +853,7 @@ components:
ended: Hasta
mine: Para mi
hasMinPrice: Precio mínimo
+ wareHouseFk: Almacén
# LatestBuysFilter
salesPersonFk: Comprador
active: Activo
diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue
index 754b084fc..2a84e5aa1 100644
--- a/src/layouts/MainLayout.vue
+++ b/src/layouts/MainLayout.vue
@@ -1,50 +1,10 @@
-
-
+
diff --git a/src/pages/Account/AccountAcls.vue b/src/pages/Account/AccountAcls.vue
index d80f835ec..6d3571661 100644
--- a/src/pages/Account/AccountAcls.vue
+++ b/src/pages/Account/AccountAcls.vue
@@ -1,16 +1,15 @@
-
-
-
(roles = data)"
/>
-
+ prefix="acls"
+ :array-data-props="{
+ url: 'ACLs',
+ order: 'id DESC',
+ exprBuilder,
+ }"
+ >
+
+
+
+
diff --git a/src/pages/Account/AccountAliasList.vue b/src/pages/Account/AccountAliasList.vue
index c67283297..f6016fb6c 100644
--- a/src/pages/Account/AccountAliasList.vue
+++ b/src/pages/Account/AccountAliasList.vue
@@ -2,21 +2,12 @@
import { useI18n } from 'vue-i18n';
import { ref, computed } from 'vue';
import VnTable from 'components/VnTable/VnTable.vue';
-import VnSearchbar from 'components/ui/VnSearchbar.vue';
-import { useStateStore } from 'stores/useStateStore';
+import VnSection from 'src/components/common/VnSection.vue';
const tableRef = ref();
const { t } = useI18n();
-const stateStore = useStateStore();
+const dataKey = 'AccountAliasList';
-const exprBuilder = (param, value) => {
- switch (param) {
- case 'search':
- return /^\d+$/.test(value)
- ? { id: value }
- : { alias: { like: `%${value}%` } };
- }
-};
const columns = computed(() => [
{
align: 'left',
@@ -40,40 +31,45 @@ const columns = computed(() => [
create: true,
},
]);
+
+const exprBuilder = (param, value) => {
+ switch (param) {
+ case 'search':
+ return /^\d+$/.test(value)
+ ? { id: value }
+ : { alias: { like: `%${value}%` } };
+ }
+};
-
-
-
-
-
-
+ prefix="mailAlias"
+ :array-data-props="{ url: 'MailAliases', order: 'id DESC', exprBuilder }"
+ >
+
+
+
+
-
es:
Id: Id
diff --git a/src/pages/Account/AccountFilter.vue b/src/pages/Account/AccountFilter.vue
index 46fac875a..50c3ee1ac 100644
--- a/src/pages/Account/AccountFilter.vue
+++ b/src/pages/Account/AccountFilter.vue
@@ -31,7 +31,6 @@ const rolesOptions = ref([]);
diff --git a/src/pages/Account/AccountList.vue b/src/pages/Account/AccountList.vue
index 341dd92a2..c1c75fcee 100644
--- a/src/pages/Account/AccountList.vue
+++ b/src/pages/Account/AccountList.vue
@@ -1,19 +1,20 @@
-
-
-
-
-
-
- (roles = data)" auto-load />
+
-
-
-
-
+
+
+
+
+
+
+
+
-
+
diff --git a/src/pages/Account/Acls/AclFilter.vue b/src/pages/Account/Acls/AclFilter.vue
index 8609672b6..8035f92b8 100644
--- a/src/pages/Account/Acls/AclFilter.vue
+++ b/src/pages/Account/Acls/AclFilter.vue
@@ -37,11 +37,7 @@ onBeforeMount(() => {
@on-fetch="(data) => (rolesOptions = data)"
auto-load
/>
-
+
{{ t(`acls.aclFilter.${tag.label}`) }}:
diff --git a/src/pages/Account/Alias/Card/AliasCard.vue b/src/pages/Account/Alias/Card/AliasCard.vue
index 65951b3bf..3a814edc0 100644
--- a/src/pages/Account/Alias/Card/AliasCard.vue
+++ b/src/pages/Account/Alias/Card/AliasCard.vue
@@ -1,12 +1,12 @@
-
-import { useI18n } from 'vue-i18n';
-import VnCard from 'components/common/VnCard.vue';
+import VnCardBeta from 'components/common/VnCardBeta.vue';
import AccountDescriptor from './AccountDescriptor.vue';
-
-const { t } = useI18n();
-
+
diff --git a/src/pages/Account/Card/AccountDescriptor.vue b/src/pages/Account/Card/AccountDescriptor.vue
index 3156f8e1e..8af817fcc 100644
--- a/src/pages/Account/Card/AccountDescriptor.vue
+++ b/src/pages/Account/Card/AccountDescriptor.vue
@@ -41,7 +41,7 @@ const hasAccount = ref(false);
/>
-
-
+
{{ t('account.card.deactivated') }}
@@ -91,10 +90,10 @@ const hasAccount = ref(false);
{{ t('account.card.enabled') }}
diff --git a/src/pages/Account/Card/AccountDescriptorMenu.vue b/src/pages/Account/Card/AccountDescriptorMenu.vue
index 6f1d2ca1f..1780b4247 100644
--- a/src/pages/Account/Card/AccountDescriptorMenu.vue
+++ b/src/pages/Account/Card/AccountDescriptorMenu.vue
@@ -8,7 +8,7 @@ import { useAcl } from 'src/composables/useAcl';
import { useArrayData } from 'src/composables/useArrayData';
import VnConfirm from 'src/components/ui/VnConfirm.vue';
import VnChangePassword from 'src/components/common/VnChangePassword.vue';
-import useNotify from 'src/composables/useNotify.js';
+import { useQuasar } from 'quasar';
const $props = defineProps({
hasAccount: {
@@ -21,7 +21,7 @@ const { t } = useI18n();
const { hasAccount } = toRefs($props);
const { openConfirmationModal } = useVnConfirm();
const route = useRoute();
-const { notify } = useNotify();
+const { notify } = useQuasar();
const account = computed(() => useArrayData('AccountId').store.data[0]);
account.value.hasAccount = hasAccount.value;
const entityId = computed(() => +route.params.id);
diff --git a/src/pages/Account/Card/AccountSummary.vue b/src/pages/Account/Card/AccountSummary.vue
index 5a21e18a5..e6c21ed34 100644
--- a/src/pages/Account/Card/AccountSummary.vue
+++ b/src/pages/Account/Card/AccountSummary.vue
@@ -30,7 +30,7 @@ const filter = {
$props.id || route.params.id);
-const { viewSummary } = useSummaryDialog();
const columns = computed(() => [
{
align: 'left',
@@ -81,30 +85,32 @@ const exprBuilder = (param, value) => {
-
-
+ prefix="role"
+ :array-data-props="{ url, exprBuilder, order: 'id ASC' }"
+ >
+
+
+
+
diff --git a/src/pages/Account/Role/AccountRolesFilter.vue b/src/pages/Account/Role/AccountRolesFilter.vue
index ff4411897..cbe7a70c8 100644
--- a/src/pages/Account/Role/AccountRolesFilter.vue
+++ b/src/pages/Account/Role/AccountRolesFilter.vue
@@ -13,12 +13,7 @@ const props = defineProps({
-
+
{{ t(`role.${tag.label}`) }}:
diff --git a/src/pages/Account/Role/Card/RoleCard.vue b/src/pages/Account/Role/Card/RoleCard.vue
index a2d5710f4..7664deca8 100644
--- a/src/pages/Account/Role/Card/RoleCard.vue
+++ b/src/pages/Account/Role/Card/RoleCard.vue
@@ -1,20 +1,7 @@
-
+
diff --git a/src/pages/Account/Role/Card/RoleDescriptor.vue b/src/pages/Account/Role/Card/RoleDescriptor.vue
index 693fcdf48..b4b4fe316 100644
--- a/src/pages/Account/Role/Card/RoleDescriptor.vue
+++ b/src/pages/Account/Role/Card/RoleDescriptor.vue
@@ -43,7 +43,7 @@ const removeRole = async () => {
:filter="filter"
module="Role"
@on-fetch="setData"
- data-key="accountData"
+ data-key="Role"
:title="data.title"
:subtitle="data.subtitle"
:summary="$props.summary"
diff --git a/src/pages/Account/Role/Card/RoleSummary.vue b/src/pages/Account/Role/Card/RoleSummary.vue
index fef85f919..f0daa77fb 100644
--- a/src/pages/Account/Role/Card/RoleSummary.vue
+++ b/src/pages/Account/Role/Card/RoleSummary.vue
@@ -27,10 +27,10 @@ const filter = {
(role = data)"
- data-key="RoleSummary"
+ data-key="Role"
>
{{ role.id }} - {{ role.name }}
diff --git a/src/pages/Account/locale/en.yml b/src/pages/Account/locale/en.yml
index f2f563923..88a6b11e9 100644
--- a/src/pages/Account/locale/en.yml
+++ b/src/pages/Account/locale/en.yml
@@ -66,7 +66,7 @@ account:
mailInputInfo: All emails will be forwarded to the specified address.
role:
newRole: New role
- searchRoles: Search role
+ search: Search role
searchInfo: Search role by id or name
description: Description
id: Id
diff --git a/src/pages/Claim/Card/ClaimDevelopment.vue b/src/pages/Claim/Card/ClaimDevelopment.vue
index 61d9f5858..d17c6b4e6 100644
--- a/src/pages/Claim/Card/ClaimDevelopment.vue
+++ b/src/pages/Claim/Card/ClaimDevelopment.vue
@@ -6,6 +6,7 @@ import CrudModel from 'components/CrudModel.vue';
import FetchData from 'components/FetchData.vue';
import VnSelect from 'components/common/VnSelect.vue';
import { tMobile } from 'composables/tMobile';
+import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
const route = useRoute();
@@ -157,7 +158,15 @@ const columns = computed(() => [
auto-width
@keyup.ctrl.enter.stop="claimDevelopmentForm.saveChanges()"
>
+
[
:autofocus="col.tabIndex == 1"
input-debounce="0"
hide-selected
- >
-
-
-
- {{ scope.opt?.name }}
-
- {{ scope.opt?.nickname }}
- {{ scope.opt?.code }}
-
-
-
-
-
+ />
diff --git a/src/pages/Claim/Card/ClaimLines.vue b/src/pages/Claim/Card/ClaimLines.vue
index 60c470d22..7c545b15b 100644
--- a/src/pages/Claim/Card/ClaimLines.vue
+++ b/src/pages/Claim/Card/ClaimLines.vue
@@ -57,6 +57,7 @@ function onFetch(rows, newRows) {
const price = row.quantity * sale.price;
const discount = (sale.discount * price) / 100;
amountClaimed.value = amountClaimed.value + (price - discount);
+
}
}
@@ -207,9 +208,10 @@ async function saveWhenHasChanges() {
selection="multiple"
v-model:selected="selected"
:grid="$q.screen.lt.md"
+
>
-
+
-
+
{{ value }}
-
+
{{ value }}
-
+
-
+
{{ column.value }}
diff --git a/src/pages/Claim/Card/ClaimSummary.vue b/src/pages/Claim/Card/ClaimSummary.vue
index edfa52b4b..8939a0785 100644
--- a/src/pages/Claim/Card/ClaimSummary.vue
+++ b/src/pages/Claim/Card/ClaimSummary.vue
@@ -120,13 +120,13 @@ const developmentColumns = ref([
{
name: 'claimReason',
label: 'claim.reason',
- field: (row) => row.claimReason.description,
+ field: (row) => row.claimReason?.description,
sortable: true,
},
{
name: 'claimResult',
label: 'claim.result',
- field: (row) => row.claimResult.description,
+ field: (row) => row.claimResult?.description,
sortable: true,
},
{
@@ -345,12 +345,9 @@ function claimUrl(section) {
{{
t(col.value)
}}
- {{ col.value }}
+ {{
+ t(col.value)
+ }}
{{ formatFn(tag.value) }}
-
+
+
@@ -153,6 +146,7 @@ en:
created: Created
myTeam: My team
itemFk: Item
+ zoneFk: Zone
es:
params:
search: Contiene
@@ -165,6 +159,7 @@ es:
created: Creada
myTeam: Mi equipo
itemFk: Artículo
+ zoneFk: Zona
Client Name: Nombre del cliente
Salesperson: Comercial
Item: Artículo
diff --git a/src/pages/Claim/ClaimList.vue b/src/pages/Claim/ClaimList.vue
index d561a69f7..bb97162d8 100644
--- a/src/pages/Claim/ClaimList.vue
+++ b/src/pages/Claim/ClaimList.vue
@@ -10,6 +10,7 @@ import ClaimSummary from './Card/ClaimSummary.vue';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import RightMenu from 'src/components/common/RightMenu.vue';
import VnTable from 'src/components/VnTable/VnTable.vue';
+import ZoneDescriptorProxy from '../Zone/Card/ZoneDescriptorProxy.vue';
const { t } = useI18n();
const { viewSummary } = useSummaryDialog();
@@ -95,7 +96,12 @@ const columns = computed(() => [
optionLabel: 'description',
},
},
- orderBy: 'priority',
+ orderBy: 'cs.priority',
+ },
+ {
+ align: 'left',
+ label: t('claim.zone'),
+ name: 'zoneFk',
},
{
align: 'right',
@@ -105,6 +111,7 @@ const columns = computed(() => [
title: t('components.smartCard.viewSummary'),
icon: 'preview',
action: (row) => viewSummary(row.id, ClaimSummary),
+ isPrimary: true,
},
],
},
@@ -131,11 +138,10 @@ const STATE_COLOR = {
@@ -148,6 +154,12 @@ const STATE_COLOR = {
+
+
+ {{ row.zoneName }}
+
+
+
diff --git a/src/pages/Customer/Card/CustomerBasicData.vue b/src/pages/Customer/Card/CustomerBasicData.vue
index 1a86d9f31..e9a349e0b 100644
--- a/src/pages/Customer/Card/CustomerBasicData.vue
+++ b/src/pages/Customer/Card/CustomerBasicData.vue
@@ -8,7 +8,7 @@ import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
-import VnAvatar from 'src/components/ui/VnAvatar.vue';
+import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
import { getDifferences, getUpdatedValues } from 'src/filters';
const route = useRoute();
@@ -16,15 +16,17 @@ const { t } = useI18n();
const businessTypes = ref([]);
const contactChannels = ref([]);
-const title = ref();
-const handleSalesModelValue = (val) => ({
- or: [
- { id: val },
- { name: val },
- { nickname: { like: '%' + val + '%' } },
- { code: { like: `${val}%` } },
- ],
-});
+const handleSalesModelValue = (val) => {
+ if (!val) val = '';
+ return {
+ or: [
+ { id: val },
+ { name: val },
+ { nickname: { like: '%' + val + '%' } },
+ { code: { like: `${val}%` } },
+ ],
+ };
+};
const exprBuilder = (param, value) => {
return {
@@ -117,41 +119,17 @@ function onBeforeSave(formData, originalData) {
/>
-
-
-
-
-
-
-
- {{ scope.opt?.name }}
- {{ scope.opt?.nickname }},
- {{ scope.opt?.code }}
-
-
-
-
+ />
-import { ref, computed } from 'vue';
+import { ref, computed, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
@@ -14,7 +14,12 @@ import CustomerDescriptorMenu from './CustomerDescriptorMenu.vue';
import { useState } from 'src/composables/useState';
const state = useState();
-const customer = computed(() => state.get('customer'));
+const customer = ref();
+
+onMounted(async () => {
+ customer.value = state.get('customer');
+ if (customer.value) customer.value.webAccess = data.value?.account?.isActive;
+});
const $props = defineProps({
id: {
@@ -38,7 +43,6 @@ const entityId = computed(() => {
const data = ref(useCardDescription());
const setData = (entity) => {
data.value = useCardDescription(entity?.name, entity?.id);
- if (customer.value) customer.value.webAccess = data.value?.account?.isActive;
};
const debtWarning = computed(() => {
return customer.value?.debt > customer.value?.credit ? 'negative' : 'primary';
diff --git a/src/pages/Customer/Card/CustomerFiscalData.vue b/src/pages/Customer/Card/CustomerFiscalData.vue
index f0eb11e7c..f55b5f412 100644
--- a/src/pages/Customer/Card/CustomerFiscalData.vue
+++ b/src/pages/Customer/Card/CustomerFiscalData.vue
@@ -111,7 +111,7 @@ function handleLocation(data, location) {
-
+
{{ t('whenActivatingIt') }}
@@ -170,7 +170,6 @@ es:
Active: Activo
Frozen: Congelado
Has to invoice: Factura
- Vies: Vies
Notify by email: Notificar vía e-mail
Invoice by address: Facturar por consignatario
Is equalizated: Recargo de equivalencia
diff --git a/src/pages/Customer/Card/CustomerSummary.vue b/src/pages/Customer/Card/CustomerSummary.vue
index 2cad13115..6650ea395 100644
--- a/src/pages/Customer/Card/CustomerSummary.vue
+++ b/src/pages/Customer/Card/CustomerSummary.vue
@@ -1,12 +1,11 @@
diff --git a/src/pages/Entry/EntryList.vue b/src/pages/Entry/EntryList.vue
index 3487f86c0..84ead85ad 100644
--- a/src/pages/Entry/EntryList.vue
+++ b/src/pages/Entry/EntryList.vue
@@ -49,8 +49,10 @@ const columns = computed(() => [
align: 'left',
label: t('globals.id'),
name: 'id',
- isTitle: true,
- cardVisible: true,
+ isId: true,
+ chip: {
+ condition: () => true,
+ },
},
{
align: 'left',
@@ -177,9 +179,6 @@ const columns = computed(() => [
],
},
]);
-onMounted(async () => {
- stateStore.rightDrawer = true;
-});
{
order="id DESC"
:columns="columns"
redirect="entry"
- auto-load
:right-search="false"
>
diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue
index 209681b7c..83b1aa25e 100644
--- a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue
+++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue
@@ -116,6 +116,7 @@ function deleteFile(dmsFk) {
-import { ref, computed } from 'vue';
-import { useRouter } from 'vue-router';
+import { ref, computed, capitalize } from 'vue';
+import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useArrayData } from 'src/composables/useArrayData';
-import { useCapitalize } from 'src/composables/useCapitalize';
import CrudModel from 'src/components/CrudModel.vue';
import FetchData from 'src/components/FetchData.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
-const { push, currentRoute } = useRouter();
+const route = useRoute();
const { t } = useI18n();
-const invoiceId = +currentRoute.value.params.id;
const arrayData = useArrayData();
const invoiceIn = computed(() => arrayData.store.data);
const invoiceInCorrectionRef = ref();
const filter = {
include: { relation: 'invoiceIn' },
- where: { correctingFk: invoiceId },
+ where: { correctingFk: route.params.id },
};
const columns = computed(() => [
{
@@ -31,7 +29,7 @@ const columns = computed(() => [
},
{
name: 'type',
- label: useCapitalize(t('globals.type')),
+ label: capitalize(t('globals.type')),
field: (row) => row.cplusRectificationTypeFk,
options: cplusRectificationTypes.value,
model: 'cplusRectificationTypeFk',
@@ -43,10 +41,10 @@ const columns = computed(() => [
},
{
name: 'class',
- label: useCapitalize(t('globals.class')),
- field: (row) => row.siiTypeInvoiceOutFk,
- options: siiTypeInvoiceOuts.value,
- model: 'siiTypeInvoiceOutFk',
+ label: capitalize(t('globals.class')),
+ field: (row) => row.siiTypeInvoiceInFk,
+ options: siiTypeInvoiceIns.value,
+ model: 'siiTypeInvoiceInFk',
optionValue: 'id',
optionLabel: 'code',
sortable: true,
@@ -55,7 +53,7 @@ const columns = computed(() => [
},
{
name: 'reason',
- label: useCapitalize(t('globals.reason')),
+ label: capitalize(t('globals.reason')),
field: (row) => row.invoiceCorrectionTypeFk,
options: invoiceCorrectionTypes.value,
model: 'invoiceCorrectionTypeFk',
@@ -67,13 +65,10 @@ const columns = computed(() => [
},
]);
const cplusRectificationTypes = ref([]);
-const siiTypeInvoiceOuts = ref([]);
+const siiTypeInvoiceIns = ref([]);
const invoiceCorrectionTypes = ref([]);
-const rowsSelected = ref([]);
const requiredFieldRule = (val) => val || t('globals.requiredField');
-
-const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`);
data.deletes && push(`/invoice-in/${invoiceId}/summary`
auto-load
/>
(siiTypeInvoiceOuts = data)"
+ @on-fetch="(data) => (siiTypeInvoiceIns = data)"
auto-load
/>
data.deletes && push(`/invoice-in/${invoiceId}/summary`
url="InvoiceInCorrections"
:filter="filter"
auto-load
- v-model:selected="rowsSelected"
primary-key="correctingFk"
- @save-changes="onSave"
+ :default-remove="false"
>
@@ -121,8 +113,17 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
:options="col.options"
:option-value="col.optionValue"
:option-label="col.optionLabel"
- :readonly="row.invoiceIn.isBooked"
- />
+ :disable="row.invoiceIn.isBooked"
+ :filter-options="['description']"
+ >
+
+
+
+ {{ opt.description }}
+
+
+
+
@@ -134,8 +135,20 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
:option-value="col.optionValue"
:option-label="col.optionLabel"
:rules="[requiredFieldRule]"
- :readonly="row.invoiceIn.isBooked"
- />
+ :filter-options="['code', 'description']"
+ :disable="row.invoiceIn.isBooked"
+ >
+
+
+
+ {{ opt.code }} -
+ {{ opt.description }}
+
+
+
+
@@ -147,7 +160,7 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
:option-value="col.optionValue"
:option-label="col.optionLabel"
:rules="[requiredFieldRule]"
- :readonly="row.invoiceIn.isBooked"
+ :disable="row.invoiceIn.isBooked"
/>
@@ -155,7 +168,6 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
-
es:
Original invoice: Factura origen
diff --git a/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue b/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue
index 92f3fffca..cb8a45833 100644
--- a/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue
+++ b/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue
@@ -1,5 +1,5 @@
{
@@ -138,8 +130,8 @@ const formatOpt = (row, { model, options }, prop) => {
@@ -154,7 +146,7 @@ const formatOpt = (row, { model, options }, prop) => {
{{ getTotal(rows, 'net') }}
- {{ getTotal(rows, 'stems') }}
+ {{ getTotal(rows, 'stems', { decimalPlaces: 0 }) }}
@@ -174,7 +166,9 @@ const formatOpt = (row, { model, options }, prop) => {
v-model="props.row['intrastatFk']"
:options="intrastats"
option-value="id"
- option-label="description"
+ :option-label="
+ (row) => `${row.id}:${row.description}`
+ "
:filter-options="['id', 'description']"
>
@@ -248,11 +242,6 @@ const formatOpt = (row, { model, options }, prop) => {
}
}
-
en:
amount: Amount
@@ -261,7 +250,7 @@ const formatOpt = (row, { model, options }, prop) => {
country: Country
es:
Code: Código
- amount: Cantidad
+ amount: Valor mercancía
net: Neto
stems: Tallos
country: País
diff --git a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue
index 08fc11f69..115a33208 100644
--- a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue
+++ b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue
@@ -26,14 +26,14 @@ const intrastatTotals = ref({ amount: 0, net: 0, stems: 0 });
const vatColumns = ref([
{
name: 'expense',
- label: 'invoiceIn.summary.expense',
+ label: 'InvoiceIn.summary.expense',
field: (row) => row.expenseFk,
sortable: true,
align: 'left',
},
{
name: 'landed',
- label: 'invoiceIn.summary.taxableBase',
+ label: 'InvoiceIn.summary.taxableBase',
field: (row) => row.taxableBase,
format: (value) => toCurrency(value),
sortable: true,
@@ -41,7 +41,7 @@ const vatColumns = ref([
},
{
name: 'vat',
- label: 'invoiceIn.summary.sageVat',
+ label: 'InvoiceIn.summary.sageVat',
field: (row) => {
if (row.taxTypeSage) return `#${row.taxTypeSage.id} : ${row.taxTypeSage.vat}`;
},
@@ -51,7 +51,7 @@ const vatColumns = ref([
},
{
name: 'transaction',
- label: 'invoiceIn.summary.sageTransaction',
+ label: 'InvoiceIn.summary.sageTransaction',
field: (row) => {
if (row.transactionTypeSage)
return `#${row.transactionTypeSage.id} : ${row.transactionTypeSage?.transaction}`;
@@ -62,7 +62,7 @@ const vatColumns = ref([
},
{
name: 'rate',
- label: 'invoiceIn.summary.rate',
+ label: 'InvoiceIn.summary.rate',
field: (row) => taxRate(row.taxableBase, row.taxTypeSage?.rate),
format: (value) => toCurrency(value),
sortable: true,
@@ -70,7 +70,7 @@ const vatColumns = ref([
},
{
name: 'currency',
- label: 'invoiceIn.summary.currency',
+ label: 'InvoiceIn.summary.currency',
field: (row) => row.foreignValue,
format: (val) => val && toCurrency(val, currency.value),
sortable: true,
@@ -81,21 +81,21 @@ const vatColumns = ref([
const dueDayColumns = ref([
{
name: 'date',
- label: 'invoiceIn.summary.dueDay',
+ label: 'InvoiceIn.summary.dueDay',
field: (row) => toDate(row.dueDated),
sortable: true,
align: 'left',
},
{
name: 'bank',
- label: 'invoiceIn.summary.bank',
+ label: 'InvoiceIn.summary.bank',
field: (row) => row.bank.bank,
sortable: true,
align: 'left',
},
{
name: 'amount',
- label: 'invoiceIn.list.amount',
+ label: 'InvoiceIn.list.amount',
field: (row) => row.amount,
format: (value) => toCurrency(value),
sortable: true,
@@ -103,7 +103,7 @@ const dueDayColumns = ref([
},
{
name: 'landed',
- label: 'invoiceIn.summary.foreignValue',
+ label: 'InvoiceIn.summary.foreignValue',
field: (row) => row.foreignValue,
format: (val) => val && toCurrency(val, currency.value),
sortable: true,
@@ -114,7 +114,7 @@ const dueDayColumns = ref([
const intrastatColumns = ref([
{
name: 'code',
- label: 'invoiceIn.summary.code',
+ label: 'InvoiceIn.summary.code',
field: (row) => {
return `${row.intrastat.id}: ${row.intrastat?.description}`;
},
@@ -123,21 +123,21 @@ const intrastatColumns = ref([
},
{
name: 'amount',
- label: 'invoiceIn.list.amount',
+ label: 'InvoiceIn.list.amount',
field: (row) => toCurrency(row.amount),
sortable: true,
align: 'left',
},
{
name: 'net',
- label: 'invoiceIn.summary.net',
+ label: 'InvoiceIn.summary.net',
field: (row) => row.net,
sortable: true,
align: 'left',
},
{
name: 'stems',
- label: 'invoiceIn.summary.stems',
+ label: 'InvoiceIn.summary.stems',
field: (row) => row.stems,
format: (value) => value,
sortable: true,
@@ -145,7 +145,7 @@ const intrastatColumns = ref([
},
{
name: 'landed',
- label: 'invoiceIn.summary.country',
+ label: 'InvoiceIn.summary.country',
field: (row) => row.country?.code,
format: (value) => value,
sortable: true,
@@ -210,7 +210,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
/>
@@ -221,14 +221,18 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
-
+
+
@@ -239,21 +243,22 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
+
@@ -263,18 +268,18 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
/>
-
+
@@ -285,11 +290,11 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
-
+
`#/invoice-in/${entityId.value}/${param}`;
:color="amountsNotMatch ? 'negative' : 'transparent'"
:title="
amountsNotMatch
- ? t('invoiceIn.summary.noMatch')
- : t('invoiceIn.summary.dueTotal')
+ ? t('InvoiceIn.summary.noMatch')
+ : t('InvoiceIn.summary.dueTotal')
"
>
{{ toCurrency(entity.totals.totalDueDay) }}
@@ -309,7 +314,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
-
+
`#/invoice-in/${entityId.value}/${param}`;
-
+
@@ -395,7 +400,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
arrayData.store.data);
-const invoiceId = +useRoute().params.id;
const currency = computed(() => invoiceIn.value?.currency?.code);
const expenses = ref([]);
const sageTaxTypes = ref([]);
@@ -39,9 +41,8 @@ const columns = computed(() => [
options: expenses.value,
model: 'expenseFk',
optionValue: 'id',
- optionLabel: 'id',
+ optionLabel: (row) => `${row.id}: ${row.name}`,
sortable: true,
- tabIndex: 1,
align: 'left',
},
{
@@ -50,7 +51,6 @@ const columns = computed(() => [
field: (row) => row.taxableBase,
model: 'taxableBase',
sortable: true,
- tabIndex: 2,
align: 'left',
},
{
@@ -60,9 +60,8 @@ const columns = computed(() => [
options: sageTaxTypes.value,
model: 'taxTypeSageFk',
optionValue: 'id',
- optionLabel: 'id',
+ optionLabel: (row) => `${row.id}: ${row.vat}`,
sortable: true,
- tabindex: 3,
align: 'left',
},
{
@@ -72,16 +71,14 @@ const columns = computed(() => [
options: sageTransactionTypes.value,
model: 'transactionTypeSageFk',
optionValue: 'id',
- optionLabel: 'id',
+ optionLabel: (row) => `${row.id}: ${row.transaction}`,
sortable: true,
- tabIndex: 4,
align: 'left',
},
{
name: 'rate',
label: t('Rate'),
sortable: true,
- tabIndex: 5,
field: (row) => taxRate(row, row.taxTypeSageFk),
align: 'left',
},
@@ -89,7 +86,6 @@ const columns = computed(() => [
name: 'foreignvalue',
label: t('Foreign value'),
sortable: true,
- tabIndex: 6,
field: (row) => row.foreignValue,
align: 'left',
},
@@ -106,7 +102,7 @@ const filter = {
'transactionTypeSageFk',
],
where: {
- invoiceInFk: invoiceId,
+ invoiceInFk: route.params.id,
},
};
@@ -120,14 +116,20 @@ function taxRate(invoiceInTax) {
const taxTypeSage = taxRateSelection?.rate ?? 0;
const taxableBase = invoiceInTax?.taxableBase ?? 0;
- return (taxTypeSage / 100) * taxableBase;
+ return ((taxTypeSage / 100) * taxableBase).toFixed(2);
}
-const formatOpt = (row, { model, options }, prop) => {
- const obj = row[model];
- const option = options.find(({ id }) => id == obj);
- return option ? `${obj}:${option[prop]}` : '';
-};
+function autocompleteExpense(evt, row, col) {
+ const val = evt.target.value;
+ if (!val) return;
+
+ const param = isNaN(val) ? row[col.model] : val;
+ const lookup = expenses.value.find(
+ ({ id }) => id == useAccountShortToStandard(param)
+ );
+
+ if (lookup) row[col.model] = lookup;
+}
{
data-key="InvoiceInTaxes"
url="InvoiceInTaxes"
:filter="filter"
- :data-required="{ invoiceInFk: invoiceId }"
+ :data-required="{ invoiceInFk: $route.params.id }"
auto-load
v-model:selected="rowsSelected"
- :go-to="`/invoice-in/${invoiceId}/due-day`"
+ :go-to="`/invoice-in/${$route.params.id}/due-day`"
>
{
:option-label="col.optionLabel"
:filter-options="['id', 'name']"
:tooltip="t('Create a new expense')"
+ @keydown.tab="autocompleteExpense($event, row, col)"
>
@@ -187,13 +190,7 @@ const formatOpt = (row, { model, options }, prop) => {
- {{ currency }}
{
:option-value="col.optionValue"
:option-label="col.optionLabel"
:filter-options="['id', 'vat']"
- :hide-selected="false"
- :fill-input="false"
- :display-value="formatOpt(row, col, 'vat')"
+ data-cy="vat-sageiva"
>
@@ -233,11 +228,6 @@ const formatOpt = (row, { model, options }, prop) => {
:option-value="col.optionValue"
:option-label="col.optionLabel"
:filter-options="['id', 'transaction']"
- :autofocus="col.tabIndex == 1"
- input-debounce="0"
- :hide-selected="false"
- :fill-input="false"
- :display-value="formatOpt(row, col, 'transaction')"
>
@@ -262,6 +252,16 @@ const formatOpt = (row, { model, options }, prop) => {
}"
:disable="!isNotEuro(currency)"
v-model="row.foreignValue"
+ @update:model-value="
+ async (val) => {
+ if (!isNotEuro(currency)) return;
+ row.taxableBase = await getExchange(
+ val,
+ row.currencyFk,
+ invoiceIn.issued
+ );
+ }
+ "
/>
@@ -305,7 +305,7 @@ const formatOpt = (row, { model, options }, prop) => {
v-model="props.row['expenseFk']"
:options="expenses"
option-value="id"
- option-label="name"
+ :option-label="(row) => `${row.id}:${row.name}`"
:filter-options="['id', 'name']"
:tooltip="t('Create a new expense')"
>
@@ -339,7 +339,7 @@ const formatOpt = (row, { model, options }, prop) => {
v-model="props.row['taxTypeSageFk']"
:options="sageTaxTypes"
option-value="id"
- option-label="vat"
+ :option-label="(row) => `${row.id}:${row.vat}`"
:filter-options="['id', 'vat']"
>
@@ -362,7 +362,9 @@ const formatOpt = (row, { model, options }, prop) => {
v-model="props.row['transactionTypeSageFk']"
:options="sageTransactionTypes"
option-value="id"
- option-label="transaction"
+ :option-label="
+ (row) => `${row.id}:${row.transaction}`
+ "
:filter-options="['id', 'transaction']"
>
@@ -418,11 +420,6 @@ const formatOpt = (row, { model, options }, prop) => {
.bg {
background-color: var(--vn-light-gray);
}
-
-:deep(.q-table tr td:nth-child(n + 4):nth-child(-n + 5) input) {
- display: none;
-}
-
@media (max-width: $breakpoint-xs) {
.q-dialog {
.q-card {
diff --git a/src/pages/InvoiceIn/InvoiceInCreate.vue b/src/pages/InvoiceIn/InvoiceInCreate.vue
index c809e032b..200997f65 100644
--- a/src/pages/InvoiceIn/InvoiceInCreate.vue
+++ b/src/pages/InvoiceIn/InvoiceInCreate.vue
@@ -83,7 +83,7 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
@@ -97,10 +97,10 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
map-options
hide-selected
:required="true"
- :rules="validate('invoiceIn.companyFk')"
+ :rules="validate('InvoiceIn.companyFk')"
/>
diff --git a/src/pages/InvoiceIn/InvoiceInFilter.vue b/src/pages/InvoiceIn/InvoiceInFilter.vue
index 130a77960..653692026 100644
--- a/src/pages/InvoiceIn/InvoiceInFilter.vue
+++ b/src/pages/InvoiceIn/InvoiceInFilter.vue
@@ -1,41 +1,66 @@
- (activities = data)"
- />
-
-
+
+
- {{ t(`params.${tag.label}`) }}:
+ {{ getLocale(tag.label) }}:
{{ formatFn(tag.value) }}
-
+
-
+
-
+
+
+
+
+
+ handleDaysAgo(params, val)"
+ @remove="(val) => handleDaysAgo(params, val)"
+ />
@@ -44,20 +69,18 @@ const activities = ref([]);
v-model="params.supplierFk"
url="Suppliers"
:fields="['id', 'nickname']"
- :label="t('params.supplierFk')"
- option-value="id"
+ :label="getLocale('supplierFk')"
option-label="nickname"
dense
outlined
rounded
- :filter-options="['id', 'name']"
/>
+
+
+
+
+
-
-
-
-
-en:
- params:
- search: Id or supplier name
- supplierRef: Supplier ref.
- supplierFk: Supplier
- fi: Supplier fiscal id
- clientFk: Customer
- amount: Amount
- created: Created
- awb: AWB
- dued: Dued
- serialNumber: Serial Number
- serial: Serial
- account: Ledger account
- isBooked: is booked
- correctedFk: Rectified
- issued: Issued
- to: To
- from: From
- awbCode: AWB
- correctingFk: Rectificative
- supplierActivityFk: Supplier activity
-es:
- params:
- search: Id o nombre proveedor
- supplierRef: Ref. proveedor
- supplierFk: Proveedor
- clientFk: Cliente
- fi: CIF proveedor
- serialNumber: Num. serie
- serial: Serie
- awb: AWB
- amount: Importe
- issued: Emitida
- isBooked: Contabilizada
- account: Cuenta contable
- created: Creada
- dued: Vencida
- correctedFk: Rectificada
- correctingFk: Rectificativa
- supplierActivityFk: Actividad proveedor
- from: Desde
- to: Hasta
- From: Desde
- To: Hasta
- Amount: Importe
- Issued: Fecha factura
- Id or supplier: Id o proveedor
-
diff --git a/src/pages/InvoiceIn/InvoiceInList.vue b/src/pages/InvoiceIn/InvoiceInList.vue
index 8d658bee3..43b9f2c11 100644
--- a/src/pages/InvoiceIn/InvoiceInList.vue
+++ b/src/pages/InvoiceIn/InvoiceInList.vue
@@ -1,7 +1,7 @@
+ (companies = data)" auto-load />
@@ -116,7 +147,7 @@ const cols = computed(() => [
urlCreate: 'InvoiceIns',
title: t('globals.createInvoiceIn'),
onDataSaved: ({ id }) => tableRef.redirect(id),
- formInitialData: {},
+ formInitialData: { companyFk: user.companyFk, issued: Date.vnNew() },
}"
redirect="invoice-in"
:columns="cols"
@@ -151,7 +182,7 @@ const cols = computed(() => [
[
option-label="code"
:required="true"
/>
-
+
diff --git a/src/pages/InvoiceIn/Serial/InvoiceInSerial.vue b/src/pages/InvoiceIn/Serial/InvoiceInSerial.vue
index 4eb9fa69d..a8fb3b0c8 100644
--- a/src/pages/InvoiceIn/Serial/InvoiceInSerial.vue
+++ b/src/pages/InvoiceIn/Serial/InvoiceInSerial.vue
@@ -58,6 +58,14 @@ onBeforeMount(async () => {
:right-search="false"
:user-params="{ daysAgo }"
:disable-option="{ card: true }"
+ :row-click="
+ (row) => {
+ $router.push({
+ name: 'InvoiceInList',
+ query: { table: JSON.stringify({ serial: row.serial }) },
+ });
+ }
+ "
auto-load
/>
diff --git a/src/pages/InvoiceIn/Serial/InvoiceInSerialFilter.vue b/src/pages/InvoiceIn/Serial/InvoiceInSerialFilter.vue
index 4f8c9d70b..19ed73e50 100644
--- a/src/pages/InvoiceIn/Serial/InvoiceInSerialFilter.vue
+++ b/src/pages/InvoiceIn/Serial/InvoiceInSerialFilter.vue
@@ -8,7 +8,11 @@ defineProps({ dataKey: { type: String, required: true } });
const { t } = useI18n();
-
+
{{ t(`params.${tag.label}`) }}:
diff --git a/src/pages/InvoiceIn/locale/en.yml b/src/pages/InvoiceIn/locale/en.yml
index b39511f29..ef7e31ac3 100644
--- a/src/pages/InvoiceIn/locale/en.yml
+++ b/src/pages/InvoiceIn/locale/en.yml
@@ -1,4 +1,4 @@
-invoiceIn:
+InvoiceIn:
serial: Serial
isBooked: Is booked
list:
@@ -7,8 +7,11 @@ invoiceIn:
supplierRef: Supplier ref.
file: File
issued: Issued
+ dueDated: Due dated
awb: AWB
amount: Amount
+ descriptor:
+ ticketList: Ticket list
card:
client: Client
company: Company
@@ -39,3 +42,9 @@ invoiceIn:
net: Net
stems: Stems
country: Country
+ params:
+ search: Id or supplier name
+ account: Ledger account
+ correctingFk: Rectificative
+ correctedFk: Corrected
+ isBooked: Is booked
diff --git a/src/pages/InvoiceIn/locale/es.yml b/src/pages/InvoiceIn/locale/es.yml
index 5f483dd08..ed5943489 100644
--- a/src/pages/InvoiceIn/locale/es.yml
+++ b/src/pages/InvoiceIn/locale/es.yml
@@ -1,15 +1,17 @@
-invoiceIn:
+InvoiceIn:
serial: Serie
isBooked: Contabilizada
list:
ref: Referencia
supplier: Proveedor
supplierRef: Ref. proveedor
- shortIssued: F. emisión
+ issued: F. emisión
+ dueDated: F. vencimiento
file: Fichero
- issued: Fecha emisión
awb: AWB
amount: Importe
+ descriptor:
+ ticketList: Listado de tickets
card:
client: Cliente
company: Empresa
@@ -38,3 +40,8 @@ invoiceIn:
net: Neto
stems: Tallos
country: País
+ params:
+ search: Id o nombre proveedor
+ account: Cuenta contable
+ correctingFk: Rectificativa
+ correctedFk: Rectificada
diff --git a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue
index 3fd3104bf..e6c689523 100644
--- a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue
+++ b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue
@@ -115,6 +115,9 @@ onMounted(async () => {
-import { onMounted, onUnmounted, ref, computed, watchEffect } from 'vue';
+import { ref, computed, watchEffect } from 'vue';
import { useI18n } from 'vue-i18n';
import VnSelect from 'src/components/common/VnSelect.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
@@ -10,7 +10,6 @@ import { usePrintService } from 'src/composables/usePrintService';
import VnTable from 'src/components/VnTable/VnTable.vue';
import InvoiceOutSummary from './Card/InvoiceOutSummary.vue';
import { toCurrency, toDate } from 'src/filters/index';
-import { useStateStore } from 'stores/useStateStore';
import { QBtn } from 'quasar';
import axios from 'axios';
import RightMenu from 'src/components/common/RightMenu.vue';
@@ -21,7 +20,6 @@ import VnInput from 'src/components/common/VnInput.vue';
import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
const { t } = useI18n();
-const stateStore = useStateStore();
const { viewSummary } = useSummaryDialog();
const tableRef = ref();
const invoiceOutSerialsOptions = ref([]);
@@ -147,8 +145,6 @@ const columns = computed(() => [
],
},
]);
-onMounted(() => (stateStore.rightDrawer = true));
-onUnmounted(() => (stateStore.rightDrawer = false));
function openPdf(id) {
openReport(`${MODEL}/${id}/download`);
@@ -214,7 +210,6 @@ watchEffect(selectedRows);
order="id DESC"
:columns="columns"
redirect="invoice-out"
- auto-load
:table="{
'row-key': 'id',
selection: 'multiple',
diff --git a/src/pages/Item/Card/ItemBarcode.vue b/src/pages/Item/Card/ItemBarcode.vue
index 197e9142f..6db5943c7 100644
--- a/src/pages/Item/Card/ItemBarcode.vue
+++ b/src/pages/Item/Card/ItemBarcode.vue
@@ -2,12 +2,15 @@
import { ref, nextTick } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
+import axios from 'axios';
import CrudModel from 'src/components/CrudModel.vue';
import VnInput from 'src/components/common/VnInput.vue';
+import useNotify from 'src/composables/useNotify.js';
const route = useRoute();
const { t } = useI18n();
+const { notify } = useNotify();
const itemBarcodeRef = ref(null);
@@ -23,6 +26,24 @@ const focusLastInput = () => {
if (lastInput) lastInput.focus();
});
};
+
+const removeRow = (row) => {
+ itemBarcodeRef.value.remove([row]);
+};
+
+const submit = async (rows) => {
+ const params = rows[rows.length - 1];
+ let { data } = await axios.get('ItemBarcodes');
+ const code = params.code;
+
+ if (data.some((codes) => codes.code === code)) {
+ notify(t('Codes can not be repeated'), 'negative');
+ itemBarcodeRef.value.reset();
+ return;
+ }
+ await axios.patch(`ItemBarcodes`, params);
+ notify(t('globals.dataSaved'), 'positive');
+};
@@ -39,6 +60,7 @@ const focusLastInput = () => {
ref="itemBarcodeRef"
url="ItemBarcodes"
auto-load
+ :save-fn="submit"
>
@@ -54,7 +76,7 @@ const focusLastInput = () => {
focusable-input
/>
diff --git a/src/pages/Item/Card/ItemBasicData.vue b/src/pages/Item/Card/ItemBasicData.vue
index 1b0342668..a1788617f 100644
--- a/src/pages/Item/Card/ItemBasicData.vue
+++ b/src/pages/Item/Card/ItemBasicData.vue
@@ -70,6 +70,7 @@ const onIntrastatCreated = (response, formData) => {
option-label="name"
hide-selected
map-options
+ required
>
diff --git a/src/pages/Item/Card/ItemBotanical.vue b/src/pages/Item/Card/ItemBotanical.vue
index c4b561772..57774f75e 100644
--- a/src/pages/Item/Card/ItemBotanical.vue
+++ b/src/pages/Item/Card/ItemBotanical.vue
@@ -1,5 +1,5 @@
-
@@ -192,27 +215,45 @@ onUnmounted(() => (stateStore.rightDrawer = false));
dense
v-model="from"
class="q-mr-lg"
+ data-cy="from"
+ />
+
+
-
-
-
+
+
+
+
+
+
@@ -234,8 +275,8 @@ onUnmounted(() => (stateStore.rightDrawer = false));
- {{ value }}
+
+ {{ value }}
{{ t('lastEntries.grouping') }}/{{ t('lastEntries.packing') }}
(stateStore.rightDrawer = false));
-
+
{{ toCurrency(row.cost, 'EUR', 3) }}
@@ -272,10 +313,25 @@ onUnmounted(() => (stateStore.rightDrawer = false));
+
+
+
+
+ {{ row.supplier }}
+
+
+
-
+
+ es:
+ Hide inventory supplier: Ocultar proveedor inventario
+
diff --git a/src/pages/Item/Card/ItemShelving.vue b/src/pages/Item/Card/ItemShelving.vue
index 27e265e6b..7ad60c9e0 100644
--- a/src/pages/Item/Card/ItemShelving.vue
+++ b/src/pages/Item/Card/ItemShelving.vue
@@ -1,19 +1,15 @@
@@ -203,7 +152,7 @@ onMounted(async () => {
{
-
-
-
-
-
-
-
-
-
-
-
- {{ row.longName }}
+
+
+ {{ row.longName }}
-
+
-
+
diff --git a/src/pages/Item/Card/ItemSummary.vue b/src/pages/Item/Card/ItemSummary.vue
index db90ba06f..e1b97d7c9 100644
--- a/src/pages/Item/Card/ItemSummary.vue
+++ b/src/pages/Item/Card/ItemSummary.vue
@@ -46,7 +46,7 @@ const getUrl = (id, param) => `#/Item/${id}/${param}`;
`#/Item/${id}/${param}`;
{
itemTagsRef.value.formData[itemTagsRef.value.formData.length - 1].priority =
getHighestPriority(rows);
};
+
+const submitTags = async (data) => {
+ itemTagsRef.value.onSubmit(data);
+};
@@ -77,7 +80,6 @@ const insertTag = (rows) => {
data-key="ItemTags"
model="ItemTags"
url="ItemTags"
- update-url="Tags/onSubmit"
:data-required="{
$index: undefined,
itemFk: route.params.id,
@@ -147,6 +149,7 @@ const insertTag = (rows) => {
v-model="row.value"
:label="t('itemTags.value')"
:is-clearable="false"
+ @keyup.enter.stop="submitTags(row)"
/>
{
v-model="row.priority"
:required="true"
:rules="validate('itemTag.priority')"
+ @keyup.enter.stop="submitTags(row)"
/>
{
+
+
+es:
+ Tags can not be repeated: Las etiquetas no pueden repetirse
+
diff --git a/src/pages/Item/Card/ItemTax.vue b/src/pages/Item/Card/ItemTax.vue
index 84b5f63f4..8060481f0 100644
--- a/src/pages/Item/Card/ItemTax.vue
+++ b/src/pages/Item/Card/ItemTax.vue
@@ -28,7 +28,7 @@ const taxesFilter = {
],
};
-const ItemTaxRef = ref(null);
+const ItemTaxRef = ref();
const taxesOptions = ref([]);
const submitTaxes = async (data) => {
@@ -36,7 +36,10 @@ const submitTaxes = async (data) => {
id: tax.id,
taxClassFk: tax.taxClassFk,
}));
-
+ if (payload.some((item) => item.taxClassFk === null)) {
+ notify(t('Tax class cannot be blank'), 'negative');
+ return;
+ }
await axios.post(`Items/updateTaxes`, payload);
notify(t('globals.dataSaved'), 'positive');
};
diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue
index 8bf5d33bd..74403d471 100644
--- a/src/pages/Item/ItemFixedPrice.vue
+++ b/src/pages/Item/ItemFixedPrice.vue
@@ -1,5 +1,5 @@
-
[
ref="tableRef"
data-key="ItemList"
url="Items/filter"
- url-create="Items"
:create="{
urlCreate: 'Items',
title: t('Create Item'),
@@ -333,22 +323,36 @@ const columns = computed(() => [
}"
:order="['isActive DESC', 'name', 'id']"
:columns="columns"
- auto-load
redirect="Item"
:is-editable="false"
:right-search="false"
- :filer="itemFilter"
+ :filter="itemFilter"
>
+
+
+
{{ row.id }}
+
+
+ {{ row.typeName }}
+ {{ row.typeFk }}
+
+
+
{{ row.userName }}
-
+
@@ -358,15 +362,15 @@ const columns = computed(() => [
{{ row?.subName.toUpperCase() }}
-
+
-
diff --git a/src/pages/Item/ItemListFilter.vue b/src/pages/Item/ItemListFilter.vue
index c8357ba33..484265b49 100644
--- a/src/pages/Item/ItemListFilter.vue
+++ b/src/pages/Item/ItemListFilter.vue
@@ -199,7 +199,17 @@ onMounted(async () => {
dense
outlined
rounded
- />
+ >
+
+
+
+ {{
+ t(`params.${scope.opt?.name}`)
+ }}
+
+
+
+
@@ -434,6 +444,13 @@ en:
description: Description
name: Name
id: Id
+ Accessories: Accessories
+ Artificial: Artificial
+ Flower: Flower
+ Fruit: Fruit
+ Green: Green
+ Handmade: Handmade
+ Plant: Plant
es:
More fields: Más campos
params:
@@ -450,4 +467,11 @@ es:
description: Descripción
name: Nombre
id: Id
+ Accessories: Accesorios
+ Artificial: Artificial
+ Flower: Flor
+ Fruit: Fruta
+ Green: Verde
+ Handmade: Hecho a mano
+ Plant: Planta
diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 4f037529a..4447d1bcf 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -1,42 +1,35 @@
+
+
+
+
+
{
:columns="columns"
:user-params="userParams"
:is-editable="true"
+ :right-search="false"
auto-load
:disable-option="{ card: true }"
chip-locale="item.params"
@@ -297,39 +291,40 @@ onMounted(async () => {
handleScopeDays(evt.target.value)"
@remove="handleScopeDays()"
class="q-px-xs q-pr-lg"
filled
dense
+ lazy-rules
+ is-outlined
/>
+
-
-
-
- {{ row.response }}
-
-
-
-
- {{ t('Discard') }}
-
-
-
+
+
+ {{ row.response }}
+
+
+
+
+ {{ t('Discard') }}
+
+
diff --git a/src/pages/Item/ItemRequestFilter.vue b/src/pages/Item/ItemRequestFilter.vue
index 64bc0e575..4e8ae0d42 100644
--- a/src/pages/Item/ItemRequestFilter.vue
+++ b/src/pages/Item/ItemRequestFilter.vue
@@ -1,12 +1,13 @@
@@ -79,8 +80,7 @@ const decrement = (paramsObj, key) => {
{{ t(`params.${tag.label}`) }}:
- {{ formatFn(tag.value) }}
- {{ t(`${tag.value}`) }}
+ {{ formatFn(tag.value) }}
@@ -145,109 +145,16 @@ const decrement = (paramsObj, key) => {
-
-
-
-
- {{ scope.opt?.name }}
- {{ scope.opt?.nickname }},
- {{ scope.opt?.code }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ t('dateFiltersTooltip') }}
-
-
-
-
-
-
@@ -267,6 +174,16 @@ const decrement = (paramsObj, key) => {
/>
+
+
+
+
+
diff --git a/src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue b/src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue
index cd12fc238..936e95d2f 100644
--- a/src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue
+++ b/src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue
@@ -6,13 +6,11 @@ import { useI18n } from 'vue-i18n';
import CardDescriptor from 'components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
-
import useCardDescription from 'src/composables/useCardDescription';
const $props = defineProps({
id: {
type: Number,
- required: false,
default: null,
},
summary: {
@@ -24,6 +22,10 @@ const $props = defineProps({
const route = useRoute();
const { t } = useI18n();
+const entityId = computed(() => {
+ return $props.id || route.params.id;
+});
+
const itemTypeFilter = {
include: [
{ relation: 'worker' },
@@ -33,10 +35,6 @@ const itemTypeFilter = {
],
};
-const entityId = computed(() => {
- return $props.id || route.params.id;
-});
-
const data = ref(useCardDescription());
const setData = (entity) => (data.value = useCardDescription(entity.code, entity.id));
@@ -48,8 +46,8 @@ const setData = (entity) => (data.value = useCardDescription(entity.code, entity
:filter="itemTypeFilter"
:title="data.title"
:subtitle="data.subtitle"
+ data-key="itemTypeDescriptor"
@on-fetch="setData"
- data-key="entry"
>
diff --git a/src/pages/Item/ItemType/Card/ItemTypeDescriptorProxy.vue b/src/pages/Item/ItemType/Card/ItemTypeDescriptorProxy.vue
new file mode 100644
index 000000000..7cde9aef8
--- /dev/null
+++ b/src/pages/Item/ItemType/Card/ItemTypeDescriptorProxy.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/pages/Item/ItemType/Card/ItemTypeSummary.vue b/src/pages/Item/ItemType/Card/ItemTypeSummary.vue
index c51d59e13..9ba774ca4 100644
--- a/src/pages/Item/ItemType/Card/ItemTypeSummary.vue
+++ b/src/pages/Item/ItemType/Card/ItemTypeSummary.vue
@@ -78,29 +78,32 @@ async function setItemTypeData(data) {
{{ t('globals.summary.basicData') }}
-
-
-
-
+
+
+
+
{{ itemType.worker?.firstName }}
-
+
-
-
+
+
diff --git a/src/pages/Item/ItemType/ItemTypeSearchbar.vue b/src/pages/Item/ItemType/ItemTypeSearchbar.vue
index 87903a517..749033d43 100644
--- a/src/pages/Item/ItemType/ItemTypeSearchbar.vue
+++ b/src/pages/Item/ItemType/ItemTypeSearchbar.vue
@@ -10,7 +10,6 @@ const { t } = useI18n();
url="ItemTypes"
:label="t('Search item type')"
:info="t('Search itemType by id, name or code')"
- search-url="table"
/>
diff --git a/src/pages/Item/ItemType/locale/en.yml b/src/pages/Item/ItemType/locale/en.yml
index 575d5e402..99c6791f2 100644
--- a/src/pages/Item/ItemType/locale/en.yml
+++ b/src/pages/Item/ItemType/locale/en.yml
@@ -1,16 +1,17 @@
-shared:
- code: Code
- name: Name
- worker: Worker
- category: Category
- temperature: Temperature
- life: Life
- itemPackingType: Item packing type
- maxRefs: Maximum references
- fragile: Fragile
-summary:
- id: id
- life: Life
- promo: Promo
- itemPackingType: Item packing type
- isUnconventionalSize: Is unconventional size
+itemType:
+ shared:
+ code: Code
+ name: Name
+ worker: Worker
+ category: Category
+ temperature: Temperature
+ life: Life
+ itemPackingType: Item packing type
+ maxRefs: Maximum references
+ fragile: Fragile
+ summary:
+ id: id
+ life: Life
+ promo: Promo
+ itemPackingType: Item packing type
+ isUnconventionalSize: Is unconventional size
diff --git a/src/pages/Item/ItemType/locale/es.yml b/src/pages/Item/ItemType/locale/es.yml
index 93f8b0d0e..c91fb4058 100644
--- a/src/pages/Item/ItemType/locale/es.yml
+++ b/src/pages/Item/ItemType/locale/es.yml
@@ -1,16 +1,17 @@
-shared:
- code: Código
- name: Nombre
- worker: Trabajador
- category: Reino
- temperature: Temperatura
- life: Vida
- itemPackingType: Tipo de embalaje
- maxRefs: Referencias máximas
- fragile: Frágil
-summary:
- id: id
- life: Vida
- promo: Promoción
- itemPackingType: Tipo de embalaje
- isUnconventionalSize: Es de tamaño poco convencional
+itemType:
+ shared:
+ code: Código
+ name: Nombre
+ worker: Trabajador
+ category: Reino
+ temperature: Temperatura
+ life: Vida
+ itemPackingType: Tipo de embalaje
+ maxRefs: Referencias máximas
+ fragile: Frágil
+ summary:
+ id: id
+ life: Vida
+ promo: Promoción
+ itemPackingType: Tipo de embalaje
+ isUnconventionalSize: Es de tamaño poco convencional
diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue
index 149de482d..4cea931e2 100644
--- a/src/pages/Item/ItemTypeList.vue
+++ b/src/pages/Item/ItemTypeList.vue
@@ -6,6 +6,7 @@ import VnTable from 'components/VnTable/VnTable.vue';
import FetchData from 'components/FetchData.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import ItemTypeFilter from './ItemType/ItemTypeFilter.vue';
+import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue';
const { t } = useI18n();
const tableRef = ref();
@@ -31,13 +32,14 @@ const columns = computed(() => [
{
align: 'left',
name: 'name',
- label: t('name'),
+ label: t('globals.name'),
cardVisible: true,
create: true,
},
{
align: 'left',
label: t('worker'),
+ name: 'workerFk',
create: true,
component: 'select',
attrs: {
@@ -45,20 +47,20 @@ const columns = computed(() => [
optionLabel: 'nickname',
optionValue: 'id',
},
+ format: (row) => row.worker?.user?.name,
cardVisible: true,
- visible: true,
- columnField: {
- component: 'userLink',
- attrs: ({ row }) => {
- return {
- workerId: row?.worker?.id,
- name: row.worker?.user?.name,
- defaultName: true,
- };
- },
- },
+ columnField: { component: null },
columnFilter: {
- name: 'workerFk',
+ attrs: {
+ url: 'Workers/activeWithInheritedRole',
+ fields: ['id', 'name'],
+ where: { role: 'buyer' },
+ optionFilter: 'firstName',
+ optionLabel: 'name',
+ optionValue: 'id',
+ useLike: false,
+ },
+ inWhere: true,
},
},
{
@@ -135,24 +137,27 @@ const columns = computed(() => [
:columns="columns"
auto-load
:right-search="false"
- :is-editable="false"
- :use-model="true"
redirect="item/item-type"
- />
+ >
+
+
+ {{ row.worker?.user?.name }}
+
+
+
+
es:
id: Id
code: Código
- name: Nombre
worker: Trabajador
ItemCategory: Reino
Temperature: Temperatura
Create ItemTypes: Crear familia
en:
code: Code
- name: Name
worker: Worker
ItemCategory: ItemCategory
Temperature: Temperature
diff --git a/src/pages/Item/locale/en.yml b/src/pages/Item/locale/en.yml
index 78a1c3ff0..74feb512b 100644
--- a/src/pages/Item/locale/en.yml
+++ b/src/pages/Item/locale/en.yml
@@ -66,6 +66,7 @@ lastEntries:
package: Package
freight: Freight
comission: Comission
+ printedStickers: Pri.
itemTags:
removeTag: Remove tag
addTag: Add tag
@@ -95,6 +96,15 @@ item:
mine: For me
state: State
myTeam: My team
+ shipped: Shipped
+ description: Description
+ quantity: Quantity
+ price: Price
+ item: Item
+ achieved: Achieved
+ concept: Concept
+ denyOptions: Deny
+ scopeDays: Scope days
searchbar:
label: Search item
descriptor:
@@ -112,7 +122,7 @@ item:
title: All its properties will be copied
subTitle: Do you want to clone this item?
list:
- id: Identifier
+ id: Id
grouping: Grouping
packing: Packing
description: Description
@@ -122,9 +132,10 @@ item:
intrastat: Intrastat
isActive: Active
size: Size
- origin: Origin
+ origin: Orig.
userName: Buyer
- weightByPiece: Weight/Piece
+ weight: Weight
+ weightByPiece: Weight/stem
stemMultiplier: Multiplier
producer: Producer
landed: Landed
diff --git a/src/pages/Item/locale/es.yml b/src/pages/Item/locale/es.yml
index 5498f4458..3f2a06f1f 100644
--- a/src/pages/Item/locale/es.yml
+++ b/src/pages/Item/locale/es.yml
@@ -56,7 +56,7 @@ lastEntries:
landed: F. Entrega
entry: Entrada
pvp: PVP
- label: Etiquetas
+ label: Eti.
grouping: Grouping
quantity: Cantidad
cost: Coste
@@ -66,6 +66,7 @@ lastEntries:
package: Embalaje
freight: Porte
comission: Comisión
+ printedStickers: Imp.
itemTags:
removeTag: Quitar etiqueta
addTag: Añadir etiqueta
@@ -97,6 +98,15 @@ item:
mine: Para mi
state: Estado
myTeam: Mi equipo
+ shipped: Enviado
+ description: Descripción
+ quantity: Cantidad
+ price: Precio
+ item: Artículo
+ achieved: Conseguido
+ concept: Concepto
+ denyOptions: Denegado
+ scopeDays: Días en adelante
searchbar:
label: Buscar artículo
descriptor:
@@ -114,7 +124,7 @@ item:
title: Todas sus propiedades serán copiadas
subTitle: ¿Desea clonar este artículo?
list:
- id: Identificador
+ id: Id
grouping: Grouping
packing: Packing
description: Descripción
@@ -124,8 +134,9 @@ item:
intrastat: Intrastat
isActive: Activo
size: Medida
- origin: Origen
- weightByPiece: Peso (gramos)/tallo
+ origin: Orig.
+ weight: Peso
+ weightByPiece: Peso/tallo
userName: Comprador
stemMultiplier: Multiplicador
producer: Productor
diff --git a/test/vitest/__tests__/pages/Login/Login.spec.js b/src/pages/Login/__tests__/Login.spec.js
similarity index 100%
rename from test/vitest/__tests__/pages/Login/Login.spec.js
rename to src/pages/Login/__tests__/Login.spec.js
diff --git a/src/pages/Monitor/Ticket/MonitorTicketFilter.vue b/src/pages/Monitor/Ticket/MonitorTicketFilter.vue
index 2205666ec..48710d696 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 VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
defineProps({ dataKey: { type: String, required: true } });
const { t, te } = useI18n();
@@ -112,33 +113,16 @@ const getLocale = (label) => {
-
-
-
-
- {{ opt.name }}
-
- {{ `${opt.nickname}, ${opt.code}` }}
-
-
-
-
-
+
@@ -150,6 +134,7 @@ const getLocale = (label) => {
/>
+
{
/>
+
+
+
+
+
+
+
+
+
+
-import { ref, computed } from 'vue';
+import { ref, computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
@@ -10,20 +10,25 @@ import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue';
import TicketSummary from 'src/pages/Ticket/Card/TicketSummary.vue';
import VnTable from 'components/VnTable/VnTable.vue';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
-import { toDateFormat } from 'src/filters/date.js';
import { toCurrency, dateRange, dashIfEmpty } from 'src/filters';
import RightMenu from 'src/components/common/RightMenu.vue';
import MonitorTicketSearchbar from './MonitorTicketSearchbar.vue';
import MonitorTicketFilter from './MonitorTicketFilter.vue';
import TicketProblems from 'src/components/TicketProblems.vue';
+import VnDateBadge from 'src/components/common/VnDateBadge.vue';
+import { useStateStore } from 'src/stores/useStateStore';
-const DEFAULT_AUTO_REFRESH = 2 * 60 * 1000; // 2min in ms
+const DEFAULT_AUTO_REFRESH = 2 * 60 * 1000;
const { t } = useI18n();
const autoRefresh = ref(false);
const tableRef = ref(null);
const provinceOpts = ref([]);
const stateOpts = ref([]);
const zoneOpts = ref([]);
+const DepartmentOpts = ref([]);
+const PayMethodOpts = ref([]);
+const ItemPackingTypeOpts = ref([]);
+const stateStore = useStateStore();
const { viewSummary } = useSummaryDialog();
const [from, to] = dateRange(Date.vnNew());
@@ -34,6 +39,11 @@ const stateColors = {
alert: 'negative',
};
+onMounted(() => {
+ stateStore.leftDrawer = false;
+ stateStore.rightDrawer = false;
+});
+
function exprBuilder(param, value) {
switch (param) {
case 'stateFk':
@@ -51,6 +61,8 @@ function exprBuilder(param, value) {
case 'nickname':
return { [`t.nickname`]: { like: `%${value}%` } };
case 'zoneFk':
+ case 'department':
+ return { 'd.name': value };
case 'totalWithVat':
return { [`t.${param}`]: value };
}
@@ -137,6 +149,7 @@ const columns = computed(() => [
align: 'left',
format: (row) => row.practicalHour,
columnFilter: false,
+ dense: true,
},
{
label: t('salesTicketsTable.preparation'),
@@ -190,6 +203,7 @@ const columns = computed(() => [
'false-value': 0,
'true-value': 1,
},
+ component: false,
},
{
label: t('salesTicketsTable.zone'),
@@ -206,6 +220,21 @@ const columns = computed(() => [
},
},
},
+ {
+ label: t('salesTicketsTable.payMethod'),
+ name: 'payMethod',
+ align: 'left',
+ columnFilter: {
+ component: 'select',
+ url: 'PayMethods',
+ attrs: {
+ options: PayMethodOpts.value,
+ optionValue: 'id',
+ optionLabel: 'name',
+ dense: true,
+ },
+ },
+ },
{
label: t('salesTicketsTable.total'),
name: 'totalWithVat',
@@ -219,6 +248,33 @@ const columns = computed(() => [
},
},
},
+ {
+ label: t('salesTicketsTable.department'),
+ name: 'department',
+ align: 'left',
+ columnFilter: {
+ component: 'select',
+ attrs: {
+ options: DepartmentOpts.value,
+ dense: true,
+ },
+ },
+ },
+ {
+ label: t('salesTicketsTable.packing'),
+ name: 'packing',
+ align: 'left',
+ columnFilter: {
+ component: 'select',
+ url: 'ItemPackingTypes',
+ attrs: {
+ options: ItemPackingTypeOpts.value,
+ 'option-value': 'code',
+ 'option-label': 'code',
+ dense: true,
+ },
+ },
+ },
{
align: 'right',
name: 'tableActions',
@@ -250,19 +306,6 @@ const columns = computed(() => [
},
]);
-const getBadgeAttrs = (date) => {
- let today = Date.vnNew();
- today.setHours(0, 0, 0, 0);
- let timeTicket = new Date(date);
- timeTicket.setHours(0, 0, 0, 0);
-
- let timeDiff = today - timeTicket;
-
- if (timeDiff == 0) return { color: 'warning', 'text-color': 'black' };
- if (timeDiff < 0) return { color: 'success', 'text-color': 'black' };
- return { color: 'transparent', 'text-color': 'white' };
-};
-
let refreshTimer = null;
const autoRefreshHandler = (value) => {
@@ -279,14 +322,6 @@ const totalPriceColor = (ticket) => {
if (total > 0 && total < 50) return 'warning';
};
-const formatShippedDate = (date) => {
- if (!date) return '-';
- const dateSplit = date.split('T');
- const [year, month, day] = dateSplit[0].split('-');
- const newDate = new Date(year, month - 1, day);
- return toDateFormat(newDate);
-};
-
const openTab = (id) =>
window.open(`#/ticket/${id}/sale`, '_blank', 'noopener, noreferrer');
@@ -318,6 +353,33 @@ const openTab = (id) =>
auto-load
@on-fetch="(data) => (zoneOpts = data)"
/>
+ (ItemPackingTypeOpts = data)"
+ />
+ (DepartmentOpts = data)"
+ />
+ (PayMethodOpts = data)"
+ />
@@ -382,13 +444,7 @@ const openTab = (id) =>
-
- {{ formatShippedDate(row.shippedDate) }}
-
+
diff --git a/src/pages/Monitor/locale/en.yml b/src/pages/Monitor/locale/en.yml
index ff4031654..e61a24979 100644
--- a/src/pages/Monitor/locale/en.yml
+++ b/src/pages/Monitor/locale/en.yml
@@ -26,8 +26,8 @@ salesTicketsTable:
componentLack: Component lack
tooLittle: Ticket too little
identifier: Identifier
- theoretical: Theoretical
- practical: Practical
+ theoretical: H.Theor
+ practical: H.Prac
province: Province
state: State
isFragile: Is fragile
@@ -35,7 +35,10 @@ salesTicketsTable:
goToLines: Go to lines
preview: Preview
total: Total
- preparation: Preparation
+ preparation: H.Prep
+ payMethod: Pay method
+ department: Department
+ packing: ITP
searchBar:
label: Search tickets
info: Search tickets by id or alias
diff --git a/src/pages/Monitor/locale/es.yml b/src/pages/Monitor/locale/es.yml
index a2ed3bb1a..30afb1904 100644
--- a/src/pages/Monitor/locale/es.yml
+++ b/src/pages/Monitor/locale/es.yml
@@ -26,8 +26,8 @@ salesTicketsTable:
componentLack: Faltan componentes
tooLittle: Ticket demasiado pequeño
identifier: Identificador
- theoretical: Teórica
- practical: Práctica
+ theoretical: H.Teór
+ practical: H.Prác
province: Provincia
state: Estado
isFragile: Es frágil
@@ -35,7 +35,10 @@ salesTicketsTable:
goToLines: Ir a líneas
preview: Vista previa
total: Total
- preparation: Preparación
+ preparation: H.Prep
+ payMethod: Método de pago
+ department: Departamento
+ packing: ITP
searchBar:
label: Buscar tickets
info: Buscar tickets por identificador o alias
diff --git a/src/pages/Order/Card/OrderCatalog.vue b/src/pages/Order/Card/OrderCatalog.vue
index a71065521..da2e88aa9 100644
--- a/src/pages/Order/Card/OrderCatalog.vue
+++ b/src/pages/Order/Card/OrderCatalog.vue
@@ -1,7 +1,7 @@
@@ -44,30 +72,33 @@ const canAddToOrder = () => {
-
+
- {{ item.warehouse }}
+ {{ price.warehouse }}
|
{
- item.quantity += item.grouping;
+ price.quantity -= price.grouping;
+ }
+ "
+ @click.exact="
+ () => {
+ price.quantity += price.grouping;
}
"
>
- {{ item.grouping }}
+ {{ price.grouping }}
- x {{ toCurrency(item.price) }}
+ x {{ toCurrency(price.price) }}
|
-
|
diff --git a/src/pages/Order/Card/OrderDescriptor.vue b/src/pages/Order/Card/OrderDescriptor.vue
index 138fcc40f..e0c613aed 100644
--- a/src/pages/Order/Card/OrderDescriptor.vue
+++ b/src/pages/Order/Card/OrderDescriptor.vue
@@ -63,21 +63,26 @@ const setData = (entity) => {
if (!entity) return;
getTotalRef.value && getTotalRef.value.fetch();
data.value = useCardDescription(entity?.client?.name, entity?.id);
- state.set('orderData', entity);
+ state.set('orderTotal', total);
};
const getConfirmationValue = (isConfirmed) => {
return t(isConfirmed ? 'globals.confirmed' : 'order.summary.notConfirmed');
};
-const total = ref(null);
+const orderTotal = computed(() => state.get('orderTotal') ?? 0);
+const total = ref(0);
(total = response)"
+ @on-fetch="
+ (response) => {
+ total = response;
+ }
+ "
/>
-
+
diff --git a/src/pages/Order/Card/OrderFilter.vue b/src/pages/Order/Card/OrderFilter.vue
index 917369919..dc86600ac 100644
--- a/src/pages/Order/Card/OrderFilter.vue
+++ b/src/pages/Order/Card/OrderFilter.vue
@@ -6,6 +6,7 @@ import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelect from 'components/common/VnSelect.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue';
+import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
const { t } = useI18n();
const props = defineProps({
@@ -61,28 +62,16 @@ const sourceList = ref([]);
outlined
rounded
/>
-
-
-
-
- {{ opt.name }}
-
- {{ opt.nickname }},{{ opt.code }}
-
-
-
-
-
+ />
(orderSummary.vat = data)"
auto-load
/>
-
+
-
+
(stateStore.rightDrawer = false));
auto-load
/>
-
+
@@ -111,12 +113,12 @@ onMounted(async () => (stateStore.rightDrawer = false));
-