diff --git a/src/components/common/VnSection.vue b/src/components/common/VnSection.vue index 11fd6013f57..510865530ed 100644 --- a/src/components/common/VnSection.vue +++ b/src/components/common/VnSection.vue @@ -80,7 +80,6 @@ onBeforeMount(() => { /> <div :id="searchbarId"></div> </slot> - <RightMenu> <template #right-panel v-if="$slots['rightMenu'] || rightFilter"> <slot name="rightMenu"> diff --git a/src/components/common/__tests__/VnInputTime.spec.js b/src/components/common/__tests__/VnInputTime.spec.js new file mode 100644 index 00000000000..2692ac71bf2 --- /dev/null +++ b/src/components/common/__tests__/VnInputTime.spec.js @@ -0,0 +1,63 @@ +import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest'; +import { createWrapper } from 'app/test/vitest/helper'; +import VnInputTime from 'components/common/VnInputTime.vue'; + +describe('VnInputTime', () => { + let wrapper; + let vm; + + beforeAll(() => { + wrapper = createWrapper(VnInputTime, { + props: { + isOutlined: true, + timeOnly: false, + }, + }); + vm = wrapper.vm; + wrapper = wrapper.wrapper; + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should return the correct data if isOutlined is true', () => { + expect(vm.isOutlined).toBe(true); + expect(vm.styleAttrs).toEqual({ dense: true, outlined: true, rounded: true }); + }); + + it('should return the formatted data', () => { + expect(vm.dateToTime('2022-01-01T03:23:43')).toBe('03:23'); + }); + + describe('formattedTime', () => { + it('should return the formatted time for a valid ISO date', () => { + vm.model = '2025-01-02T15:45:00'; + expect(vm.formattedTime).toBe('15:45'); + }); + + it('should handle null model value gracefully', () => { + vm.model = null; + expect(vm.formattedTime).toBe(null); + }); + + it('should handle time-only input correctly', async () => { + await wrapper.setProps({ timeOnly: true }); + vm.formattedTime = '14:30'; + expect(vm.model).toBe('14:30'); + }); + + it('should pad short time values correctly', async () => { + await wrapper.setProps({ timeOnly: true }); + vm.formattedTime = '9'; + expect(vm.model).toBe('09:00'); + }); + + it('should not update the model if the value is unchanged', () => { + vm.model = '14:30'; + const previousModel = vm.model; + vm.formattedTime = '14:30'; + expect(vm.model).toBe(previousModel); + }); + }); +}); \ No newline at end of file diff --git a/src/components/ui/CardSummary.vue b/src/components/ui/CardSummary.vue index a1de3eee392..cf52bcd4063 100644 --- a/src/components/ui/CardSummary.vue +++ b/src/components/ui/CardSummary.vue @@ -2,7 +2,6 @@ import { ref, computed, watch, onBeforeMount } from 'vue'; import { useRoute } from 'vue-router'; import SkeletonSummary from 'components/ui/SkeletonSummary.vue'; -import VnLv from 'src/components/ui/VnLv.vue'; import { useArrayData } from 'src/composables/useArrayData'; import { isDialogOpened } from 'src/filters'; import VnMoreOptions from './VnMoreOptions.vue'; diff --git a/src/components/ui/FetchedTags.vue b/src/components/ui/FetchedTags.vue index 6e159087c27..b3912f77944 100644 --- a/src/components/ui/FetchedTags.vue +++ b/src/components/ui/FetchedTags.vue @@ -18,8 +18,7 @@ const $props = defineProps({ }, columns: { type: Number, - required: false, - default: null, + default: 3, }, }); diff --git a/src/components/ui/SkeletonSummary.vue b/src/components/ui/SkeletonSummary.vue index e8407ee7b36..659d4c53d7b 100644 --- a/src/components/ui/SkeletonSummary.vue +++ b/src/components/ui/SkeletonSummary.vue @@ -1,38 +1,49 @@ <template> <div class="header bg-primary q-pa-sm q-mb-md"> <QSkeleton type="rect" square /> + <QSkeleton type="rect" square /> </div> <div class="row q-pa-md q-col-gutter-md q-mb-md"> - <QSkeleton type="rect" class="q-mb-md" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="rect" class="q-mb-md" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="rect" class="q-mb-md" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="rect" class="q-mb-md" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="rect" class="q-mb-md" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> - <QSkeleton type="text" square /> + <div class="col"> + <QSkeleton type="rect" class="q-mb-md" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + </div> + <div class="col"> + <QSkeleton type="rect" class="q-mb-md" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + </div> + <div class="col"> + <QSkeleton type="rect" class="q-mb-md" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + </div> + <div class="col"> + <QSkeleton type="rect" class="q-mb-md" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + </div> + <div class="col"> + <QSkeleton type="rect" class="q-mb-md" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + <QSkeleton type="text" square /> + </div> </div> </template> diff --git a/src/components/ui/__tests__/CardSummary.spec.js b/src/components/ui/__tests__/CardSummary.spec.js new file mode 100644 index 00000000000..411ebf9bb60 --- /dev/null +++ b/src/components/ui/__tests__/CardSummary.spec.js @@ -0,0 +1,78 @@ +import { vi, describe, expect, it, beforeAll, afterEach, beforeEach } from 'vitest'; +import { createWrapper, axios } from 'app/test/vitest/helper'; +import CardSummary from 'src/components/ui/CardSummary.vue'; +import * as vueRouter from 'vue-router'; + +describe('CardSummary', () => { + let vm; + let wrapper; + + beforeAll(() => { + vi.spyOn(axios, 'get').mockResolvedValue({ data: [] }); + }); + + vi.spyOn(vueRouter, 'useRoute').mockReturnValue({ + query: {}, + params: {}, + meta: { moduleName: 'mockName' }, + path: 'mockName/1/summary', + name: 'CardSummary', + }); + + beforeEach(() => { + wrapper = createWrapper(CardSummary, { + propsData: { + dataKey: 'cardSummaryKey', + url: 'cardSummaryUrl', + filter: 'cardFilter', + }, + }); + vm = wrapper.vm; + wrapper = wrapper.wrapper; + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should fetch data correctly', async () => { + const fetchSpy = vi + .spyOn(vm.arrayData, 'fetch') + .mockResolvedValue({ data: [{ id: 1, name: 'Test Entity' }] }); + await vm.fetch(); + + expect(fetchSpy).toHaveBeenCalledWith({ append: false, updateRouter: false }); + expect(wrapper.emitted('onFetch')).toBeTruthy(); + expect(vm.isLoading).toBe(false); + }); + + it('should set correct props to the store', () => { + expect(vm.store.url).toEqual('cardSummaryUrl'); + expect(vm.store.filter).toEqual('cardFilter'); + }); + + it('should compute entity correctly from store data', () => { + vm.store.data = [{ id: 1, name: 'Entity 1' }]; + expect(vm.entity).toEqual({ id: 1, name: 'Entity 1' }); + }); + + it('should handle empty data gracefully', () => { + vm.store.data = []; + expect(vm.entity).toBeUndefined(); + }); + + it('should respond to prop changes and refetch data', async () => { + const newUrl = 'CardSummary/35'; + const newKey = 'cardSummaryKey/35'; + const fetchSpy = vi.spyOn(vm.arrayData, 'fetch'); + await wrapper.setProps({ url: newUrl, filter: { key: newKey } }); + + expect(fetchSpy).toHaveBeenCalled(); + expect(vm.store.url).toBe(newUrl); + expect(vm.store.filter).toEqual({ key: newKey }); + }); + + it('should return true if route path ends with /summary' , () => { + expect(vm.isSummary).toBe(true); + }); +}); \ No newline at end of file diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 7c733eae8f0..dbc3e4077bc 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -505,27 +505,6 @@ parking: searchBar: info: You can search by parking code label: Search parking... -order: - field: - salesPersonFk: Sales Person - form: - clientFk: Client - addressFk: Address - agencyModeFk: Agency - list: - newOrder: New Order - summary: - basket: Basket - notConfirmed: Not confirmed - created: Created - createdFrom: Created From - address: Address - total: Total - items: Items - orderTicketList: Order Ticket List - amount: Amount - confirm: Confirm - confirmLines: Confirm lines department: chat: Chat bossDepartment: Boss Department diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index d13ccf3f91f..1b6ed7b715c 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -489,30 +489,6 @@ invoiceOut: comercial: Comercial errors: downloadCsvFailed: Error al descargar CSV -order: - field: - salesPersonFk: Comercial - form: - clientFk: Cliente - addressFk: Dirección - agencyModeFk: Agencia - list: - newOrder: Nuevo Pedido - summary: - basket: Cesta - notConfirmed: No confirmada - created: Creado - createdFrom: Creado desde - address: Dirección - total: Total - vat: IVA - state: Estado - alias: Alias - items: Artículos - orderTicketList: Tickets del pedido - amount: Monto - confirm: Confirmar - confirmLines: Confirmar lineas shelving: list: parking: Parking diff --git a/src/pages/Customer/components/CustomerAddressCreate.vue b/src/pages/Customer/components/CustomerAddressCreate.vue index bc4d6a1285a..32b4078db88 100644 --- a/src/pages/Customer/components/CustomerAddressCreate.vue +++ b/src/pages/Customer/components/CustomerAddressCreate.vue @@ -11,6 +11,7 @@ import VnInput from 'src/components/common/VnInput.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelectDialog from 'src/components/common/VnSelectDialog.vue'; import CustomerNewCustomsAgent from 'src/pages/Customer/components/CustomerNewCustomsAgent.vue'; +import VnInputNumber from 'src/components/common/VnInputNumber.vue'; const { t } = useI18n(); const route = useRoute(); @@ -150,6 +151,22 @@ function onAgentCreated({ id, fiscalName }, data) { </template> </VnSelectDialog> </VnRow> + <VnRow> + <VnInputNumber + :label="t('Longitude')" + clearable + v-model="data.longitude" + :decimal-places="7" + :positive="false" + /> + <VnInputNumber + :label="t('Latitude')" + clearable + v-model="data.latitude" + :decimal-places="7" + :positive="false" + /> + </VnRow> </template> </FormModel> </template> @@ -175,4 +192,6 @@ es: Mobile: Movíl Incoterms: Incoterms Customs agent: Agente de aduanas + Longitude: Longitud + Latitude: Latitud </i18n> diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue index f04563791d5..00d0f5c4e2e 100644 --- a/src/pages/Item/ItemList.vue +++ b/src/pages/Item/ItemList.vue @@ -391,7 +391,7 @@ onBeforeMount(async () => { {{ row?.subName.toUpperCase() }} </div> </div> - <FetchedTags :item="row" :columns="3" /> + <FetchedTags :item="row" /> </template> <template #more-create-dialog="{ data }"> <VnInput diff --git a/src/pages/Order/Card/OrderCard.vue b/src/pages/Order/Card/OrderCard.vue index 67c0f1de510..823815f59b9 100644 --- a/src/pages/Order/Card/OrderCard.vue +++ b/src/pages/Order/Card/OrderCard.vue @@ -1,38 +1,12 @@ <script setup> -import { computed } from 'vue'; -import { useRoute } from 'vue-router'; -import VnCard from 'components/common/VnCard.vue'; +import VnCardBeta from 'components/common/VnCardBeta.vue'; import OrderDescriptor from 'pages/Order/Card/OrderDescriptor.vue'; -import OrderFilter from './OrderFilter.vue'; -import OrderSearchbar from './OrderSearchbar.vue'; -import OrderCatalogFilter from './OrderCatalogFilter.vue'; -const config = { - OrderCatalog: OrderCatalogFilter, -}; -const route = useRoute(); - -const routeName = computed(() => route.name); -const customRouteRedirectName = computed(() => { - const route = config[routeName.value]; - if (route) return null; - return 'OrderList'; -}); -const customFilterPanel = computed(() => { - const filterPanel = config[routeName.value] ?? OrderFilter; - return filterPanel; -}); </script> <template> - <VnCard + <VnCardBeta data-key="Order" base-url="Orders" :descriptor="OrderDescriptor" - :filter-panel="customFilterPanel" - :search-data-key="customRouteRedirectName" - > - <template #searchbar> - <OrderSearchbar /> - </template> - </VnCard> + /> </template> diff --git a/src/pages/Order/Card/OrderCatalog.vue b/src/pages/Order/Card/OrderCatalog.vue index da2e88aa990..186f216fb1b 100644 --- a/src/pages/Order/Card/OrderCatalog.vue +++ b/src/pages/Order/Card/OrderCatalog.vue @@ -15,15 +15,18 @@ const router = useRouter(); const stateStore = useStateStore(); const { t } = useI18n(); const dataKey = 'OrderCatalogList'; -const arrayData = useArrayData(dataKey); -const store = arrayData.store; -const tags = ref([]); -const itemRefs = ref({}); - -let catalogParams = { +const catalogParams = { orderFk: route.params.id, orderBy: JSON.stringify({ field: 'relevancy DESC, name', way: 'ASC', isTag: false }), }; +const arrayData = useArrayData(dataKey, { + url: 'Orders/CatalogFilter', + limit: 50, + userParams: catalogParams, +}); +const store = arrayData.store; +const tags = ref([]); +const itemRefs = ref({}); onMounted(() => { stateStore.rightDrawer = true; @@ -66,7 +69,6 @@ function extractValueTags(items) { ); tagValue.value = resultValueTags; } -const autoLoad = computed(() => !!JSON.parse(route?.query.table ?? '{}')?.categoryFk); watch( () => store.data, @@ -78,16 +80,15 @@ watch( </script> <template> - <VnSearchbar - :data-key="dataKey" - :user-params="catalogParams" - :static-params="['orderFk', 'orderBy']" - :redirect="false" - url="Orders/CatalogFilter" - :label="t('Search items')" - :info="t('You can search items by name or id')" - :search-remove-params="false" - /> + <Teleport to="#section-searchbar" v-if="stateStore.isHeaderMounted()"> + <VnSearchbar + :data-key="dataKey" + :redirect="false" + :label="t('Search items')" + :info="t('You can search items by name or id')" + :search-remove-params="false" + /> + </Teleport> <Teleport to="#right-panel" v-if="stateStore.isHeaderMounted()"> <OrderCatalogFilter :data-key="dataKey" @@ -98,13 +99,7 @@ watch( </Teleport> <QPage class="column items-center q-pa-md" data-cy="orderCatalogPage"> <div class="full-width"> - <VnPaginate - :data-key="dataKey" - url="Orders/CatalogFilter" - :limit="50" - :user-params="catalogParams" - :auto-load="autoLoad" - > + <VnPaginate :data-key="dataKey"> <template #body="{ rows }"> <div class="catalog-list"> <div v-if="rows && !rows?.length" class="no-result"> diff --git a/src/pages/Order/Card/OrderLines.vue b/src/pages/Order/Card/OrderLines.vue index 6093addb5eb..9d802c55739 100644 --- a/src/pages/Order/Card/OrderLines.vue +++ b/src/pages/Order/Card/OrderLines.vue @@ -208,6 +208,20 @@ async function remove(item) { async function handleConfirm() { const result = await confirm(route.params.id); if (result) { + const sale = await axios.get(`OrderRows`, { + params: { + filter: JSON.stringify({ + where: { orderFk: route.params.id }, + }), + }, + }); + const ticket = await axios.get(`Sales`, { + params: { + filter: JSON.stringify({ + where: { id: sale.data[0].saleFk }, + }), + }, + }); quasar.notify({ message: t('globals.dataSaved'), type: 'positive', @@ -215,7 +229,7 @@ async function handleConfirm() { router.push({ name: 'TicketSale', query: { - table: JSON.stringify({ id: route.params.id }), + table: JSON.stringify({ id: ticket.data[0].ticketFk }), }, }); } diff --git a/src/pages/Order/Card/OrderSearchbar.vue b/src/pages/Order/Card/OrderSearchbar.vue deleted file mode 100644 index fa30a097fd2..00000000000 --- a/src/pages/Order/Card/OrderSearchbar.vue +++ /dev/null @@ -1,22 +0,0 @@ -<script setup> -import { useI18n } from 'vue-i18n'; -import VnSearchbar from 'components/ui/VnSearchbar.vue'; - -const { t } = useI18n(); -</script> - -<template> - <VnSearchbar - data-key="OrderList" - url="Orders/filter" - :label="t('Search order')" - :info="t('Search orders by ticket id')" - /> -</template> - -<style scoped lang="scss"></style> -<i18n> -es: - Search order: Buscar orden - Search orders by ticket id: Buscar pedido por id ticket -</i18n> diff --git a/src/pages/Order/OrderList.vue b/src/pages/Order/OrderList.vue index baa20354152..ae1fe68bd47 100644 --- a/src/pages/Order/OrderList.vue +++ b/src/pages/Order/OrderList.vue @@ -8,15 +8,14 @@ import { useRoute } from 'vue-router'; import axios from 'axios'; import OrderSummary from 'pages/Order/Card/OrderSummary.vue'; -import OrderSearchbar from './Card/OrderSearchbar.vue'; import OrderFilter from './Card/OrderFilter.vue'; -import RightMenu from 'src/components/common/RightMenu.vue'; import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue'; import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue'; import VnTable from 'src/components/VnTable/VnTable.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; +import VnSection from 'src/components/common/VnSection.vue'; const { t } = useI18n(); const { viewSummary } = useSummaryDialog(); @@ -24,6 +23,8 @@ const tableRef = ref(); const agencyList = ref([]); const route = useRoute(); const addressOptions = ref([]); +const dataKey = 'OrderList'; + const columns = computed(() => [ { align: 'left', @@ -178,117 +179,126 @@ const getDateColor = (date) => { if (difference < 0) return 'bg-success'; }; </script> + <template> - <OrderSearchbar /> - <RightMenu> - <template #right-panel> + <VnSection + :data-key="dataKey" + :columns="columns" + prefix="order" + :array-data-props="{ + url: 'Orders/filter', + order: ['landed DESC', 'clientFk ASC', 'id DESC'], + }" + > + <template #rightMenu> <OrderFilter data-key="OrderList" /> </template> - </RightMenu> - <VnTable - ref="tableRef" - data-key="OrderList" - url="Orders/filter" - :order="['landed DESC', 'clientFk ASC', 'id DESC']" - :create="{ - urlCreate: 'Orders/new', - title: t('module.cerateOrder'), - onDataSaved: (url) => { - tableRef.redirect(`${url}/catalog`); - }, - formInitialData: { - active: true, - addressId: null, - clientFk: null, - }, - }" - :user-params="{ showEmpty: false }" - :columns="columns" - :right-search="false" - redirect="order" - > - <template #column-clientFk="{ row }"> - <span class="link" @click.stop> - {{ row?.clientName }} - <CustomerDescriptorProxy :id="row?.clientFk" /> - </span> - </template> - <template #column-salesPersonFk="{ row }"> - <span class="link" @click.stop> - {{ row?.name }} - <WorkerDescriptorProxy :id="row?.salesPersonFk" /> - </span> - </template> - <template #column-landed="{ row }"> - <span v-if="getDateColor(row.landed)"> - <QChip :class="getDateColor(row.landed)" dense square> - {{ toDate(row?.landed) }} - </QChip> - </span> - </template> - <template #more-create-dialog="{ data }"> - <VnSelect - url="Clients" - :include="{ relation: 'addresses' }" - v-model="data.clientFk" - :label="t('module.customer')" - @update:model-value="(id) => fetchClientAddress(id, data)" + <template #body> + <VnTable + ref="tableRef" + :data-key="dataKey" + :create="{ + urlCreate: 'Orders/new', + title: t('module.cerateOrder'), + onDataSaved: (url) => { + tableRef.redirect(`${url}/catalog`); + }, + formInitialData: { + active: true, + addressId: null, + clientFk: null, + }, + }" + :user-params="{ showEmpty: false }" + :columns="columns" + :right-search="false" + redirect="order" > - <template #option="scope"> - <QItem v-bind="scope.itemProps"> - <QItemSection> - <QItemLabel> - {{ scope.opt.name }} - </QItemLabel> - <QItemLabel caption> - {{ `#${scope.opt.id}` }} - </QItemLabel> - </QItemSection> - </QItem> + <template #column-clientFk="{ row }"> + <span class="link" @click.stop> + {{ row?.clientName }} + <CustomerDescriptorProxy :id="row?.clientFk" /> + </span> </template> - </VnSelect> - <VnSelect - v-model="data.addressId" - :options="addressOptions" - :label="t('module.address')" - option-value="id" - option-label="nickname" - @update:model-value="() => fetchAgencies(data)" - > - <template #option="scope"> - <QItem v-bind="scope.itemProps"> - <QItemSection> - <QItemLabel - :class="{ - 'color-vn-label': !scope.opt?.isActive, - }" - > - {{ - `${ - !scope.opt?.isActive - ? t('basicData.inactive') - : '' - } ` - }} - {{ scope.opt?.nickname }}: {{ scope.opt?.street }}, - {{ scope.opt?.city }} - </QItemLabel> - </QItemSection> - </QItem> + <template #column-salesPersonFk="{ row }"> + <span class="link" @click.stop> + {{ row?.name }} + <WorkerDescriptorProxy :id="row?.salesPersonFk" /> + </span> </template> - </VnSelect> - <VnInputDate - v-model="data.landed" - :label="t('module.landed')" - @update:model-value="() => fetchAgencies(data)" - /> - <VnSelect - v-model="data.agencyModeId" - :label="t('module.agency')" - :options="agencyList" - option-value="agencyModeFk" - option-label="agencyMode" - /> + <template #column-landed="{ row }"> + <span v-if="getDateColor(row.landed)"> + <QChip :class="getDateColor(row.landed)" dense square> + {{ toDate(row?.landed) }} + </QChip> + </span> + </template> + <template #more-create-dialog="{ data }"> + <VnSelect + url="Clients" + :include="{ relation: 'addresses' }" + v-model="data.clientFk" + :label="t('module.customer')" + @update:model-value="(id) => fetchClientAddress(id, data)" + > + <template #option="scope"> + <QItem v-bind="scope.itemProps"> + <QItemSection> + <QItemLabel> + {{ scope.opt.name }} + </QItemLabel> + <QItemLabel caption> + {{ `#${scope.opt.id}` }} + </QItemLabel> + </QItemSection> + </QItem> + </template> + </VnSelect> + <VnSelect + v-model="data.addressId" + :options="addressOptions" + :label="t('module.address')" + option-value="id" + option-label="nickname" + @update:model-value="() => fetchAgencies(data)" + > + <template #option="scope"> + <QItem v-bind="scope.itemProps"> + <QItemSection> + <QItemLabel + :class="{ + 'color-vn-label': !scope.opt?.isActive, + }" + > + {{ + `${ + !scope.opt?.isActive + ? t('basicData.inactive') + : '' + } ` + }} + {{ scope.opt?.nickname }}: + {{ scope.opt?.street }}, + {{ scope.opt?.city }} + </QItemLabel> + </QItemSection> + </QItem> + </template> + </VnSelect> + <VnInputDate + v-model="data.landed" + :label="t('module.landed')" + @update:model-value="() => fetchAgencies(data)" + /> + <VnSelect + v-model="data.agencyModeId" + :label="t('module.agency')" + :options="agencyList" + option-value="agencyModeFk" + option-label="agencyMode" + /> + </template> + </VnTable> </template> - </VnTable> + </VnSection> </template> diff --git a/src/pages/Order/locale/en.yml b/src/pages/Order/locale/en.yml index 4349bc76f58..14e41c55983 100644 --- a/src/pages/Order/locale/en.yml +++ b/src/pages/Order/locale/en.yml @@ -21,3 +21,26 @@ lines: image: Image params: tagGroups: Tags +order: + field: + salesPersonFk: Sales Person + form: + clientFk: Client + addressFk: Address + agencyModeFk: Agency + list: + newOrder: New Order + summary: + basket: Basket + notConfirmed: Not confirmed + created: Created + createdFrom: Created From + address: Address + total: Total + items: Items + orderTicketList: Order Ticket List + amount: Amount + confirm: Confirm + confirmLines: Confirm lines + search: Search orders + searchInfo: You can search orders by ticket id diff --git a/src/pages/Order/locale/es.yml b/src/pages/Order/locale/es.yml index cef06cb6d97..44e243ad117 100644 --- a/src/pages/Order/locale/es.yml +++ b/src/pages/Order/locale/es.yml @@ -21,3 +21,29 @@ lines: image: Imagen params: tagGroups: Tags +order: + field: + salesPersonFk: Comercial + form: + clientFk: Cliente + addressFk: Dirección + agencyModeFk: Agencia + list: + newOrder: Nuevo Pedido + summary: + basket: Cesta + notConfirmed: No confirmada + created: Creado + createdFrom: Creado desde + address: Dirección + total: Total + vat: IVA + state: Estado + alias: Alias + items: Artículos + orderTicketList: Tickets del pedido + amount: Monto + confirm: Confirmar + confirmLines: Confirmar lineas + search: Buscar pedido + searchInfo: Buscar pedidos por el número de ticket diff --git a/src/router/modules/order.js b/src/router/modules/order.js index 77af812cf79..bdd080e7fee 100644 --- a/src/router/modules/order.js +++ b/src/router/modules/order.js @@ -1,35 +1,102 @@ import { RouterView } from 'vue-router'; +const orderCard = { + name: 'OrderCard', + path: ':id', + component: () => import('src/pages/Order/Card/OrderCard.vue'), + redirect: { name: 'OrderSummary' }, + meta: { + menu: [ + 'OrderBasicData', + 'OrderCatalog', + 'OrderVolume', + 'OrderLines', + ], + }, + children: [ + { + path: 'summary', + name: 'OrderSummary', + meta: { + title: 'summary', + icon: 'launch', + }, + component: () => import('src/pages/Order/Card/OrderSummary.vue'), + }, + { + path: 'basic-data', + name: 'OrderBasicData', + meta: { + title: 'basicData', + icon: 'vn:settings', + }, + component: () => import('src/pages/Order/Card/OrderBasicData.vue'), + }, + { + path: 'catalog', + name: 'OrderCatalog', + meta: { + title: 'catalog', + icon: 'vn:basket', + }, + component: () => import('src/pages/Order/Card/OrderCatalog.vue'), + }, + { + path: 'volume', + name: 'OrderVolume', + meta: { + title: 'volume', + icon: 'vn:volume', + }, + component: () => import('src/pages/Order/Card/OrderVolume.vue'), + }, + { + path: 'line', + name: 'OrderLines', + meta: { + title: 'lines', + icon: 'vn:lines', + }, + component: () => import('src/pages/Order/Card/OrderLines.vue'), + }, + ], +}; + export default { - path: '/order', name: 'Order', + path: '/order', meta: { title: 'order', icon: 'vn:basket', moduleName: 'Order', keyBinding: 'o', + menu: ['OrderList'], }, component: RouterView, redirect: { name: 'OrderMain' }, - menus: { - main: ['OrderList'], - card: ['OrderBasicData', 'OrderCatalog', 'OrderVolume', 'OrderLines'], - }, children: [ { - path: '', name: 'OrderMain', + path: '', component: () => import('src/components/common/VnModule.vue'), - redirect: { name: 'OrderList' }, + redirect: { name: 'OrderIndexMain' }, children: [ { - path: 'list', - name: 'OrderList', - meta: { - title: 'orderList', - icon: 'view_list', - }, + path: '', + name: 'OrderIndexMain', + redirect: { name: 'OrderList' }, component: () => import('src/pages/Order/OrderList.vue'), + children: [ + { + name: 'OrderList', + path: 'list', + meta: { + title: 'orderList', + icon: 'view_list', + }, + }, + orderCard, + ], }, { path: 'create', @@ -42,58 +109,5 @@ export default { }, ], }, - { - name: 'OrderCard', - path: ':id', - component: () => import('src/pages/Order/Card/OrderCard.vue'), - redirect: { name: 'OrderSummary' }, - children: [ - { - name: 'OrderSummary', - path: 'summary', - meta: { - title: 'summary', - icon: 'launch', - }, - component: () => import('src/pages/Order/Card/OrderSummary.vue'), - }, - { - name: 'OrderBasicData', - path: 'basic-data', - meta: { - title: 'basicData', - icon: 'vn:settings', - }, - component: () => import('src/pages/Order/Card/OrderBasicData.vue'), - }, - { - name: 'OrderCatalog', - path: 'catalog', - meta: { - title: 'catalog', - icon: 'vn:basket', - }, - component: () => import('src/pages/Order/Card/OrderCatalog.vue'), - }, - { - name: 'OrderVolume', - path: 'volume', - meta: { - title: 'volume', - icon: 'vn:volume', - }, - component: () => import('src/pages/Order/Card/OrderVolume.vue'), - }, - { - name: 'OrderLines', - path: 'line', - meta: { - title: 'lines', - icon: 'vn:lines', - }, - component: () => import('src/pages/Order/Card/OrderLines.vue'), - }, - ], - }, ], -}; +}; \ No newline at end of file