diff --git a/src/components/common/VnSelect.vue b/src/components/common/VnSelect.vue index ed842103f..aa629767d 100644 --- a/src/components/common/VnSelect.vue +++ b/src/components/common/VnSelect.vue @@ -38,6 +38,10 @@ const $props = defineProps({ type: [Array], default: () => [], }, + exprBuilder: { + type: Function, + default: null, + }, isClearable: { type: Boolean, default: true, @@ -179,6 +183,7 @@ async function fetchFilter(val) { }, {}); } else defaultWhere = { [key]: getVal(val) }; const where = { ...(val ? defaultWhere : {}), ...$props.where }; + $props.exprBuilder && Object.assign(where, $props.exprBuilder(key, val)); const fetchOptions = { where, include, limit }; if (fields) fetchOptions.fields = fields; if (sortBy) fetchOptions.order = sortBy; diff --git a/src/composables/usePrintService.js b/src/composables/usePrintService.js index 68a009d7b..c6c212ad8 100644 --- a/src/composables/usePrintService.js +++ b/src/composables/usePrintService.js @@ -1,15 +1,18 @@ import { useSession } from './useSession'; import axios from 'axios'; import { useQuasar } from 'quasar'; +import { useI18n } from 'vue-i18n'; export function usePrintService() { const quasar = useQuasar(); + const { t } = useI18n(); + const { getTokenMultimedia } = useSession(); function sendEmail(path, params) { return axios.post(path, params).then(() => quasar.notify({ - message: 'Notification sent', + message: t('globals.notificationSent'), type: 'positive', icon: 'check', }) diff --git a/src/pages/Customer/Card/CustomerBalance.vue b/src/pages/Customer/Card/CustomerBalance.vue index 2a1991bbd..f148194f8 100644 --- a/src/pages/Customer/Card/CustomerBalance.vue +++ b/src/pages/Customer/Card/CustomerBalance.vue @@ -16,7 +16,7 @@ import { useVnConfirm } from 'composables/useVnConfirm'; import VnTable from 'components/VnTable/VnTable.vue'; import VnInput from 'components/common/VnInput.vue'; import VnSubToolbar from 'components/ui/VnSubToolbar.vue'; -import VnFilter from 'components/VnTable/VnFilter.vue'; +import VnSelect from 'src/components/common/VnSelect.vue'; import CustomerNewPayment from 'src/pages/Customer/components/CustomerNewPayment.vue'; import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue'; @@ -33,9 +33,9 @@ const stateStore = useStateStore(); const user = state.getUser(); const clientRisk = ref([]); +const companies = ref([]); const tableRef = ref(); const companyId = ref(user.value.companyFk); -const companyLastId = ref(user.value.companyFk); const balances = ref([]); const vnFilterRef = ref({}); const filter = computed(() => { @@ -45,33 +45,6 @@ const filter = computed(() => { }; }); -const companyFilterColumn = { - align: 'left', - name: 'companyId', - label: t('Company'), - component: 'select', - attrs: { - url: 'Companies', - optionLabel: 'code', - sortBy: 'code', - limit: 0, - }, - columnFilter: { - event: { - remove: () => (companyId.value = null), - 'update:modelValue': (newCompanyFk) => { - if (!newCompanyFk) return; - vnFilterRef.value.addFilter(newCompanyFk); - companyLastId.value = newCompanyFk; - }, - blur: () => - !companyId.value && - (companyId.value = companyLastId.value ?? user.value.companyFk), - }, - }, - visible: false, -}; - const columns = computed(() => [ { align: 'right', @@ -166,6 +139,11 @@ onBeforeMount(() => { }); async function getCurrentBalance(data) { + currentBalance.value[companyId.value] = { + amount: 0, + code: companies.value.find((c) => c.id === companyId.value)?.code, + }; + for (const balance of data) { currentBalance.value[balance.companyFk] = { code: balance.company.code, @@ -192,14 +170,21 @@ const showBalancePdf = ({ id }) => { <template> <FetchData + url="Companies" + auto-load + @on-fetch="(data) => (companies = data)" + ></FetchData> + <FetchData + v-if="companies.length > 0" url="clientRisks" :filter="{ include: { relation: 'company', scope: { fields: ['code'] } }, - where: { clientFk: route.params.id, companyFk: companyId }, + where: { clientFk: route.params.id }, }" auto-load @on-fetch="getCurrentBalance" ></FetchData> + <VnSubToolbar class="q-mb-md"> <template #st-data> <div class="column justify-center q-px-md q-py-sm"> @@ -212,13 +197,15 @@ const showBalancePdf = ({ id }) => { </template> <template #st-actions> <div> - <VnFilter + <VnSelect + :label="t('Company')" ref="vnFilterRef" v-model="companyId" data-key="CustomerBalance" - :column="companyFilterColumn" - search-url="balance" - /> + :options="companies" + option-label="code" + option-value="id" + ></VnSelect> </div> </template> </VnSubToolbar> diff --git a/src/pages/Customer/Card/CustomerBasicData.vue b/src/pages/Customer/Card/CustomerBasicData.vue index 91d9edc05..6472a47ff 100644 --- a/src/pages/Customer/Card/CustomerBasicData.vue +++ b/src/pages/Customer/Card/CustomerBasicData.vue @@ -16,6 +16,19 @@ const { t } = useI18n(); const businessTypes = ref([]); const contactChannels = ref([]); const title = ref(); +const handleSalesModelValue = (val) => ({ + or: [ + { name: val }, + { nickname: { like: '%' + val + '%' } }, + { code: { like: `${val}%` } }, + ], +}); + +const exprBuilder = (param, value) => { + return { + and: [{ active: { neq: false } }, handleSalesModelValue(value)], + }; +}; </script> <template> <FetchData @@ -34,7 +47,7 @@ const title = ref(); <VnRow> <VnInput :label="t('globals.name')" - :rules="validate('client.socialName')" + :rules="validate('client.name')" autofocus clearable v-model="data.name" @@ -99,7 +112,7 @@ const title = ref(); :fields="['id', 'nickname']" sort-by="nickname ASC" :rules="validate('client.salesPersonFk')" - :use-like="false" + :expr-builder="exprBuilder" emit-value auto-load > diff --git a/src/pages/Customer/Card/CustomerDescriptor.vue b/src/pages/Customer/Card/CustomerDescriptor.vue index b18f90d20..43103bd68 100644 --- a/src/pages/Customer/Card/CustomerDescriptor.vue +++ b/src/pages/Customer/Card/CustomerDescriptor.vue @@ -190,6 +190,18 @@ const setData = (entity) => (data.value = useCardDescription(entity?.name, entit > <QTooltip>{{ t('Go to user') }}</QTooltip> </QBtn> + <QBtn + v-if="entity.supplier" + :to="{ + name: 'SupplierSummary', + params: { id: entity.supplier.id }, + }" + size="md" + icon="vn:supplier" + color="primary" + > + <QTooltip>{{ t('Go to supplier') }}</QTooltip> + </QBtn> </QCardActions> </template> </CardDescriptor> @@ -204,6 +216,7 @@ es: Customer ticket list: Listado de tickets del cliente Customer invoice out list: Listado de facturas del cliente Go to user: Ir al usuario + Go to supplier: Ir al proveedor Customer unpaid: Cliente impago Unpaid: Impagado unpaidDated: 'Fecha {dated}' diff --git a/src/pages/Customer/Card/CustomerFiscalData.vue b/src/pages/Customer/Card/CustomerFiscalData.vue index d8c07a46f..300d275c0 100644 --- a/src/pages/Customer/Card/CustomerFiscalData.vue +++ b/src/pages/Customer/Card/CustomerFiscalData.vue @@ -134,15 +134,17 @@ function handleLocation(data, location) { </QTooltip> </QIcon> </div> - <QCheckbox :label="t('Verified data')" v-model="data.isTaxDataChecked" /> + <QCheckbox :label="t('Daily invoice')" v-model="data.hasDailyInvoice" /> </VnRow> <VnRow> <QCheckbox :label="t('Electronic invoice')" v-model="data.hasElectronicInvoice" + /><QCheckbox + :label="t('Verified data')" + v-model="data.isTaxDataChecked" /> - <QCheckbox :label="t('Daily invoice')" v-model="data.hasDailyInvoice" /> </VnRow> </template> </FormModel> diff --git a/src/pages/Customer/CustomerFilter.vue b/src/pages/Customer/CustomerFilter.vue index 043bcdadb..6c50cc9df 100644 --- a/src/pages/Customer/CustomerFilter.vue +++ b/src/pages/Customer/CustomerFilter.vue @@ -1,35 +1,20 @@ <script setup> -import { ref } from 'vue'; import { useI18n } from 'vue-i18n'; - -import FetchData from 'components/FetchData.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnSelect from 'components/common/VnSelect.vue'; import VnInput from 'src/components/common/VnInput.vue'; const { t } = useI18n(); -const props = defineProps({ +defineProps({ dataKey: { type: String, required: true, }, }); - -const provinces = ref(); -const workers = ref(); -const zones = ref(); </script> <template> - <FetchData url="Provinces" @on-fetch="(data) => (provinces = data)" auto-load /> - <FetchData url="Zones" @on-fetch="(data) => (zones = data)" auto-load /> - <FetchData - url="Workers/activeWithInheritedRole" - :filter="{ where: { role: 'salesPerson' } }" - @on-fetch="(data) => (workers = data)" - auto-load - /> - <VnFilterPanel :data-key="props.dataKey" :search-button="true" search-url="table"> + <VnFilterPanel :data-key="dataKey" :search-button="true" search-url="table"> <template #tags="{ tag, formatFn }"> <div class="q-gutter-x-xs"> <strong>{{ t(`params.${tag.label}`) }}: </strong> @@ -65,15 +50,14 @@ const zones = ref(); </QItemSection> </QItem> <QItem class="q-mb-sm"> - <QItemSection v-if="!workers"> - <QSkeleton type="QInput" class="full-width" /> - </QItemSection> - <QItemSection v-if="workers"> + <QItemSection> <VnSelect + url="Workers/activeWithInheritedRole" + :filter="{ where: { role: 'salesPerson' } }" + auto-load :label="t('Salesperson')" v-model="params.salesPersonFk" @update:model-value="searchFn()" - :options="workers" option-value="id" option-label="name" emit-value @@ -88,15 +72,12 @@ const zones = ref(); </QItemSection> </QItem> <QItem class="q-mb-sm"> - <QItemSection v-if="!provinces"> - <QSkeleton type="QInput" class="full-width" /> - </QItemSection> - <QItemSection v-if="provinces"> - <VnSelect + <QItemSection + ><VnSelect + url="Provinces" :label="t('Province')" v-model="params.provinceFk" @update:model-value="searchFn()" - :options="provinces" option-value="id" option-label="name" emit-value @@ -105,6 +86,7 @@ const zones = ref(); dense outlined rounded + auto-load :input-debounce="0" /> </QItemSection> @@ -135,25 +117,21 @@ const zones = ref(); </QItemSection> </QItem> <QItem> - <QItemSection v-if="!zones"> - <QSkeleton type="QInput" class="full-width" /> - </QItemSection> - <QItemSection v-if="zones"> - <VnSelect - :label="t('Zone')" - v-model="params.zoneFk" - @update:model-value="searchFn()" - :options="zones" - option-value="id" - option-label="name" - emit-value - map-options - hide-selected - dense - outlined - rounded - /> - </QItemSection> + <VnSelect + url="Zones" + :label="t('Zone')" + v-model="params.zoneFk" + @update:model-value="searchFn()" + option-value="id" + option-label="name" + emit-value + map-options + hide-selected + dense + outlined + rounded + auto-load + /> </QItem> <QItem> <QItemSection> diff --git a/src/pages/Customer/CustomerList.vue b/src/pages/Customer/CustomerList.vue index f6758bf4e..0428c0c7b 100644 --- a/src/pages/Customer/CustomerList.vue +++ b/src/pages/Customer/CustomerList.vue @@ -8,10 +8,10 @@ import VnLocation from 'src/components/common/VnLocation.vue'; import VnSearchbar from 'components/ui/VnSearchbar.vue'; import CustomerSummary from './Card/CustomerSummary.vue'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; - +import RightMenu from 'src/components/common/RightMenu.vue'; import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue'; - import { toDate } from 'src/filters'; +import CustomerFilter from './CustomerFilter.vue'; const { t } = useI18n(); const router = useRouter(); @@ -397,6 +397,11 @@ function handleLocation(data, location) { :label="t('Search customer')" data-key="Customer" /> + <RightMenu> + <template #right-panel> + <CustomerFilter data-key="Customer" /> + </template> + </RightMenu> <VnTable ref="tableRef" data-key="Customer" @@ -413,6 +418,7 @@ function handleLocation(data, location) { order="id DESC" :columns="columns" redirect="customer" + :right-search="false" auto-load > <template #more-create-dialog="{ data }"> @@ -425,7 +431,6 @@ function handleLocation(data, location) { }" :fields="['id', 'nickname']" sort-by="nickname ASC" - :use-like="false" emit-value auto-load > diff --git a/src/pages/Customer/components/CustomerAddressCreate.vue b/src/pages/Customer/components/CustomerAddressCreate.vue index 7826c3579..659114744 100644 --- a/src/pages/Customer/components/CustomerAddressCreate.vue +++ b/src/pages/Customer/components/CustomerAddressCreate.vue @@ -134,6 +134,7 @@ function handleLocation(data, location) { option-label="fiscalName" option-value="id" v-model="data.customsAgentFk" + :tooltip="t('Create a new expense')" > <template #form> <CustomerNewCustomsAgent @on-data-saved="refreshData()" /> diff --git a/src/pages/Customer/components/CustomerAddressEdit.vue b/src/pages/Customer/components/CustomerAddressEdit.vue index 82a190ae1..606867388 100644 --- a/src/pages/Customer/components/CustomerAddressEdit.vue +++ b/src/pages/Customer/components/CustomerAddressEdit.vue @@ -11,7 +11,7 @@ import VnRow from 'components/ui/VnRow.vue'; 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 CustomsNewCustomsAgent from 'src/pages/Customer/components/CustomerNewCustomsAgent.vue'; +import CustomerNewCustomsAgent from 'src/pages/Customer/components/CustomerNewCustomsAgent.vue'; const { t } = useI18n(); const route = useRoute(); @@ -226,9 +226,10 @@ function handleLocation(data, location) { option-label="fiscalName" option-value="id" v-model="data.customsAgentFk" + :tooltip="t('New customs agent')" > <template #form> - <CustomsNewCustomsAgent /> + <CustomerNewCustomsAgent /> </template> </VnSelectDialog> </div> @@ -309,6 +310,7 @@ es: Mobile: Movíl Incoterms: Incoterms Customs agent: Agente de aduanas + New customs agent: Nuevo agente de aduanas Notes: Notas Observation type: Tipo de observación Description: Descripción diff --git a/src/pages/Customer/components/CustomerChangePassword.vue b/src/pages/Customer/components/CustomerChangePassword.vue index 1bfc5e103..d92a10c5c 100644 --- a/src/pages/Customer/components/CustomerChangePassword.vue +++ b/src/pages/Customer/components/CustomerChangePassword.vue @@ -46,7 +46,6 @@ const onSubmit = async () => { }; try { await axios.patch(`Clients/${$props.id}/setPassword`, payload); - await $props.promise(); } catch (error) { notify('errors.create', 'negative'); } finally { diff --git a/src/pages/Customer/locale/es.yml b/src/pages/Customer/locale/es.yml index 4fcbe3fa2..451784425 100644 --- a/src/pages/Customer/locale/es.yml +++ b/src/pages/Customer/locale/es.yml @@ -58,7 +58,7 @@ customer: vies: VIES payMethod: Método de pago bankAccount: Cuenta bancaria - dueDay: Día de pago + dueDay: Vencimiento hasLcr: Recibido LCR hasCoreVnl: Recibido core VNL hasB2BVnl: Recibido B2B VNL diff --git a/src/pages/Ticket/locale/en.yml b/src/pages/Ticket/locale/en.yml index fb944730e..58367e3a2 100644 --- a/src/pages/Ticket/locale/en.yml +++ b/src/pages/Ticket/locale/en.yml @@ -39,6 +39,7 @@ ticketSale: agency: Agency address: Address advanceTickets: + preparation: Preparation origin: Origin destination: Destination originAgency: 'Origin agency: {agency}' diff --git a/src/pages/Ticket/locale/es.yml b/src/pages/Ticket/locale/es.yml index 24971529c..1acaf154b 100644 --- a/src/pages/Ticket/locale/es.yml +++ b/src/pages/Ticket/locale/es.yml @@ -86,6 +86,7 @@ weeklyTickets: search: Buscar por tickets programados searchInfo: Buscar tickets programados por el identificador o el identificador del cliente advanceTickets: + preparation: Preparación origin: Origen destination: Destinatario originAgency: 'Agencia origen: {agency}'