WIP: Added searchbar in Wagon module #835
243
CHANGELOG.md
243
CHANGELOG.md
|
@ -1,3 +1,246 @@
|
|||
# Version 25.00 - 2025-01-14
|
||||
|
||||
### Added 🆕
|
||||
|
||||
- chore: refs #7056 move test by:jorgep
|
||||
- feat: refs #7050 7050 add object check by:Jtubau
|
||||
- feat: refs #7050 7050 add test to isEmpty() by:Jtubau
|
||||
- feat: refs #7056 add tests in FormModel by:jorgep
|
||||
- feat: refs #7056 update route meta information and add FormModel tests by:jorgep
|
||||
- feat: refs #7074 tests for fns setData(), parseDms() and showFormDialog() (7074-makeFrontTestToVnDmsList) by:Jtubau
|
||||
- feat: refs #7079 created VnLocation front test by:provira
|
||||
- feat: refs #7189 add Accept-Language header to axios requests by:jorgep
|
||||
- feat: refs #7924 add custom inspection checkbox and localization support by:jgallego
|
||||
- feat: refs #7924 update custom inspection label for clarity in English and Spanish locales by:jgallego
|
||||
- feat: refs #8004 enhance FetchedTags component with column support and styling updates by:pablone
|
||||
- feat: refs #8004 hide rightFilter by:pablone
|
||||
- feat: refs #8246 added new field in list by:Jon
|
||||
- feat: refs #8266 added descriptor to item name by:jtubau
|
||||
- feat: refs #8293 add zone filter by:Jtubau
|
||||
- feat: refs #8293 include zone data in each record by:Jtubau
|
||||
- fix: refs #8004 more list style issues by:pablone
|
||||
- fix: refs #8004 some style issues on all list by:pablone
|
||||
- style: refs #8004 update layout and styling in FetchedTags and ItemList components by:pablone
|
||||
- style: update CustomerBalance.vue to set label color by:jgallego
|
||||
|
||||
### Changed 📦
|
||||
|
||||
- perf: order by:alexm
|
||||
- perf: redirect transition list to card by:alexm
|
||||
- perf: refs #8201 onDataSaved fetch by:Jon
|
||||
- perf: revert processData by:alexm
|
||||
- perf: simplify if by:alexm
|
||||
- perf: simplify if (perf_redirectTransition) by:alexm
|
||||
- refactor: item fixedPrice by:Jon
|
||||
- refactor: refs #7050 refactorize by:jtubau
|
||||
- refactor: refs #7050 removed blank spaces by:jtubau
|
||||
- refactor: refs #7052 move EditTableCellValueForm tests to a new location and enhance test coverage by:jgallego
|
||||
- refactor: refs #7052 remove unnecessary console logs from EditTableCellValueForm tests by:jgallego
|
||||
- refactor: refs #7074 move dms constant to global scope by:Jtubau
|
||||
- refactor: refs #7079 removed useless code by:provira
|
||||
- refactor: refs #7924 simplify custom inspection icon rendering in ExtraCommunity.vue by:jgallego
|
||||
- refactor: refs #8004 remove console log from CardSummary component on mount by:pablone
|
||||
- refactor: refs #8004 remove consoleLogs by:pablone
|
||||
- refactor: refs #8004 remove unused stateStore import in InvoiceInList.vue by:pablone
|
||||
- refactor: refs #8004 remove unused travelFilterRef and chip definition in TravelList.vue by:pablone
|
||||
- refactor: refs #8004 replace VnSelect with VnSelectWorker in CustomerList component by:pablone
|
||||
- refactor: refs #8201 added onMounted to stablish the value to show icons by:Jon
|
||||
- refactor: refs #8201 deleted condition by:Jon
|
||||
- refactor: refs #8201 deleted log by:Jon
|
||||
- refactor: refs #8201 deleted logs by:Jon
|
||||
- refactor: refs #8266 8266 change expedition item name by:Jtubau
|
||||
- refactor: refs #8266 change expedition label by:Jtubau
|
||||
- refactor: refs #8293 remove redundant attributes by:Jtubau
|
||||
- refactor: refs #8320 changed folder names from "specs" to "**tests**" by:provira
|
||||
- refactor: refs #8320 moved front tests to their respective sections by:provira
|
||||
- refactor: refs #8813 removed unused class property by:provira
|
||||
|
||||
### Fixed 🛠️
|
||||
|
||||
- fix: discount class by:PAU ROVIRA ROSALENY
|
||||
- fix: duplicate transalation after test to dev by:alexm
|
||||
- fix: fix translations by:carlossa
|
||||
- fix: redirect to sales when confirming lines by:Jon
|
||||
- fix: refs #7050 delete import added by mistake by:Jtubau
|
||||
- fix: refs #7133 handleSalesModelValue function to handle empty input by:jorgep
|
||||
- fix: refs #7189 update user language on sessionStorage by:jorgep
|
||||
- fix: refs #7935 remove unused 'companyFk' column from InvoiceInList component by:jorgep
|
||||
- fix: refs #8004 more list style issues by:pablone
|
||||
- fix: refs #8004 some style issues on all list by:pablone
|
||||
- fix: refs #8004 update label for daysOnward in TravelFilter component and add translations by:pablone
|
||||
- fix: refs #8004 vnTable card with and add permanent labels by:pablone
|
||||
- fix: refs #8201 added onDataSaved emi to refetch when cahnges are made by:Jon
|
||||
- fix: refs #8201 use arrayData to fix the error by:Jon
|
||||
- fix: refs #8314 space between label and value by:jtubau
|
||||
- fix: refs #8813 fixed ClaimLines format by:provira
|
||||
- fix: update button sizes in ExtraCommunity.vue for better visibility by:jgallego
|
||||
- perf: revert processData by:alexm
|
||||
- refactor: item fixedPrice by:Jon
|
||||
|
||||
# Version 24.52 - 2024-01-07
|
||||
|
||||
### Added 🆕
|
||||
|
||||
- chore: refs #8197 remove console log by:alexm
|
||||
- chore: refs #8197 replace name by:alexm
|
||||
- chore: refs #8197 unnecessary file by:alexm
|
||||
- feat: #8110 apply mixin in quasar components by:Javier Segarra
|
||||
- feat(Account & AccountRole): refs #8197 add VnCardMain by:alexm
|
||||
- feat: addDptoLink by:Jtubau
|
||||
- feat: added restore ticket function in ticket descriptor menu by:Jon
|
||||
- feat: add support service wip by:jorgep
|
||||
- feat: focus menu searchbar by:jorgep
|
||||
- feat: make additional data object by:jorgep
|
||||
- feat: message to grant access by:jorgep
|
||||
- feat: refs #6583 add default param by:jorgep
|
||||
- feat: refs #6583 add destination opt filter by:jorgep
|
||||
- feat: refs #6583 add icon by:jorgep
|
||||
- feat: refs #6583 add locale by:jorgep
|
||||
- feat: refs #7072 added test to computed fn total by:Jtubau
|
||||
- feat: refs #7235 update invoice out global form to fetch config based on serial type by:jgallego
|
||||
- feat: refs #7301 add exclude inventory supplier from list by:pablone
|
||||
- feat: refs #7301 enhance VnDateBadge styling and improve ItemLastEntries component by:pablone
|
||||
- feat: refs #7882 Added distribution point by:guillermo
|
||||
- feat: refs #7882 Added longitude & latitude by:guillermo
|
||||
- feat: refs #7936 add autocomplete on tab fn by:jorgep
|
||||
- feat: refs #7936 add company filter by:jorgep
|
||||
- feat: refs #7936 add currency check before fetching by:jorgep
|
||||
- feat: refs #7936 add dueDated field by:jorgep
|
||||
- feat: refs #7936 add number validation to VnInputNumber & new daysAgo filter in InvoiceInFilter by:jorgep
|
||||
- feat: refs #7936 add optionCaption by:jorgep
|
||||
- feat: refs #7936 add row click navigation to InvoiceInSerial by:jorgep
|
||||
- feat: refs #7936 add unit tests by:jorgep
|
||||
- feat: refs #7936 add useAccountShortToStandard composable by:jorgep
|
||||
- feat: refs #7936 calculate exchange & update taxable base by:jorgep
|
||||
- feat: refs #7936 enhance downloadFile function to support opening in a new tab by:jorgep
|
||||
- feat: refs #7936 enhance getTotal fn & add unit tests by:jorgep
|
||||
- feat: refs #7936 enhance vn-select by:jorgep
|
||||
- feat: refs #7936 improve optionLabel logic in InvoiceInVat component for better handling of numeric values by:jorgep
|
||||
- feat: refs #7936 limit decimal places by:jorgep
|
||||
- feat: refs #7936 make fields required by:jorgep
|
||||
- feat: refs #7936 show country code & isVies fields by:jorgep
|
||||
- feat: refs #7936 show id & value by:jorgep
|
||||
- feat: refs #7936 simplify optionLabel wip by:jorgep
|
||||
- feat: refs #7936 update 'isVies' label to use global translation key by:jorgep
|
||||
- feat: refs #7936 update option labels in InvoiceIn components for better clarity by:jorgep
|
||||
- feat: refs #7936 use default invoice data by:jorgep
|
||||
- feat: refs #8001 change request by:robert
|
||||
- feat: refs #8001 ticketExpeditionGrafana by:robert
|
||||
- feat: refs #8194 created VnSelectWorker component and use it in Lilium by:Jon
|
||||
- feat: refs #8197 better leftMenu and VnCardMain improvements by:alexm
|
||||
- feat: refs #8197 default leftMenu by:alexm
|
||||
- feat: refs #8197 default sectionName by:alexm
|
||||
- feat: refs #8197 keepData in VnSection by:alexm
|
||||
- feat: refs #8197 vnTableFilter by:alexm
|
||||
- feat: refs #8197 working rightMenu by:alexm
|
||||
- feat: remove re-fetch when add element by:Javier Segarra
|
||||
- feat: remove search after category by:Javier Segarra
|
||||
- feat: requested changes in item module by:Jon
|
||||
- feat: update quantity by:Javier Segarra
|
||||
- feat(VnPaginate): refs #8197 hold data when change to Card by:alexm
|
||||
|
||||
### Changed 📦
|
||||
|
||||
- perf: #6896 REMOVE COMMENTS by:Javier Segarra
|
||||
- perf: qFormMixin by:Javier Segarra
|
||||
- perf: qFormMixin improvement by:Javier Segarra
|
||||
- perf: refs #8194 select worker component by:Jon
|
||||
- perf: refs #8197 perf by:alexm
|
||||
- perf: remove comments by:Javier Segarra
|
||||
- perf: remove unused variables (origin/warmfix_noUsedVars) by:Javier Segarra
|
||||
- refactor: added again search emit by:Jon
|
||||
- refactor: add useCau composable by:jorgep
|
||||
- refactor: deleted log by:Jon
|
||||
- refactor: deleted onUnmounted code by:Jon
|
||||
- refactor: deleted useless hidden tag by:Jon
|
||||
- refactor: deleted warnings and corrected itemTag by:Jon
|
||||
- refactor: drop logic by:jorgep
|
||||
- refactor: ignore params when searching by id on searchbar (origin/VnSearchbar-SearchRemoveParams) by:Jon
|
||||
- refactor: log error by:Jon
|
||||
- refactor: refs #7936 locale by:jorgep
|
||||
- refactor: refs #7936 simplify getTotal fn by:jorgep
|
||||
- refactor: refs #7936 update label capitalization and replace invoice type options by:jorgep
|
||||
- refactor: refs #8194 deleted unnecessary label by:Jon
|
||||
- refactor: refs #8194 modified select worker template by:Jon
|
||||
- refactor: refs #8194 modified select worker to allow no one filter from monitor ticket by:Jon
|
||||
- refactor: refs #8194 moved translation to the correct place by:Jon
|
||||
- refactor: refs #8194 requested changes by:Jon
|
||||
- refactor: refs #8194 structure changes in component and related files by:Jon
|
||||
- refactor: refs #8197 adapt AccountAcls to VnCardMain by:alexm
|
||||
- refactor: refs #8197 adapt AccountAlias by:alexm
|
||||
- refactor: refs #8197 adapt Ticket to VnCardMain by:alexm
|
||||
- refactor: refs #8197 backward compatible (8197-VnCardMain_backwardCompatibility) by:alexm
|
||||
- refactor: refs #8197 rename VnSectionMain to VnModule and VnCardMain to VnSection by:alexm
|
||||
- refactor: refs #8288 changed invoice out spanish translation by:provira
|
||||
- refactor: use locale keys by:jorgep
|
||||
- refactor: use teleport to avoid qdrawer overlapping by:Jon
|
||||
- refactor: use VnSelectWorker by:Jon
|
||||
|
||||
### Fixed 🛠️
|
||||
|
||||
- fix: account by:carlossa
|
||||
- fix: account create by:carlossa
|
||||
- fix: accountList create by:carlossa
|
||||
- fix(AccountList): use $refs by:alexm
|
||||
- fix: add data-key by:alexm
|
||||
- fix: addLocales by:Jtubau
|
||||
- fix: dated field by:Jon
|
||||
- fix: e2e by:jorgep
|
||||
- fix: fix department filter by:carlossa
|
||||
- fix: fixed translations by:Javier Segarra
|
||||
- fix: fixed translations by:provira
|
||||
- fix: get total from api by:Javier Segarra
|
||||
- fix: handle non-object options by:jorgep
|
||||
- fix: monitorPayMethodFilter by:carlossa
|
||||
- fix: orderBy priority by:Javier Segarra
|
||||
- fix: prevent null by:jorgep
|
||||
- fix: redirection vnTable VnTableFilter by:alexm
|
||||
- fix: refs #6389 fix filter trad by:carlossa
|
||||
- fix: refs #6389 fix front, filters, itp by:carlossa
|
||||
- fix: refs #6389 front add packing filter by:carlossa
|
||||
- fix: refs #6389 front by:carlossa
|
||||
- fix: refs #6389 front filters by:carlossa
|
||||
- fix: refs #6389 ipt by:carlossa
|
||||
- fix: refs #6389 packing by:carlossa
|
||||
- fix: refs #6583 update checkbox for filtering by destination in TicketAdvanceFilter by:jorgep
|
||||
- fix: refs #7031 add test e2e by:carlossa
|
||||
- fix: refs #7031 fix zoneTest by:carlossa
|
||||
- fix: refs #7301 unnecessary console logs from ItemLastEntries.vue by:pablone
|
||||
- fix: refs #7936 changes by:jorgep
|
||||
- fix: refs #7936 decimal places & locale by:jorgep
|
||||
- fix: refs #7936 descriptor & dueday by:jorgep
|
||||
- fix: refs #7936 exclude disabled els on tab by:jorgep
|
||||
- fix: refs #7936 format tax calculation to two decimal places by:jorgep
|
||||
- fix: refs #7936 improve error handling by:jorgep
|
||||
- fix: refs #7936 redirection by:jorgep
|
||||
- fix: refs #7936 rollback by:jorgep
|
||||
- fix: refs #7936 serial by:jorgep
|
||||
- fix: refs #7936 tabulation wip by:jorgep
|
||||
- fix: refs #7936 test by:jorgep
|
||||
- fix: refs #8114 clean by:carlossa
|
||||
- fix: refs #8114 fix agencyList by:carlossa
|
||||
- fix: refs #8114 fix lifeCycle hooks by:carlossa
|
||||
- fix: refs #8114 fix pr by:carlossa
|
||||
- fix: refs #8114 fix removeAddress by:carlossa
|
||||
- fix: refs #8114 orderList by:carlossa
|
||||
- fix: refs #8114 remove logs by:carlossa
|
||||
- fix: refs #8197 mapKey (origin/8197-perf_vnTableInside, 8197-perf_vnTableInside) by:alexm
|
||||
- fix: refs #8197 redirection (8197-perf_redirection) by:alexm
|
||||
- fix: refs #8197 staticParams and redirect by:alexm
|
||||
- fix: refs #8197 vnPaginate onFetch emit by:alexm
|
||||
- fix: refs #8197 vnPaginate when change :id by:alexm
|
||||
- fix: refs #8197 vnTableFilter in vnTable by:alexm
|
||||
- fix: refs #8315 ticketBoxing test by:alexm
|
||||
- fix: remove url by:carlossa
|
||||
- fix: rollback by:jorgep
|
||||
- fix: test by:jorgep
|
||||
- fix(VnDmsList): refs #8197 add mapKey by:alexm
|
||||
- revert: refs #8197 arrayData changes by:alexm
|
||||
- test: refs #8197 fix e2e by:alexm
|
||||
- test: refs #8315 fix claimDevelopment fixtures by:alexm
|
||||
- test: refs #8315 fix clientList by:alexm
|
||||
- test: refs #8315 fix VnSelect in e2e by:alexm
|
||||
|
||||
# Version 24.50 - 2024-12-10
|
||||
|
||||
### Added 🆕
|
||||
|
|
|
@ -11,6 +11,7 @@ module.exports = defineConfig({
|
|||
screenshotsFolder: 'test/cypress/screenshots',
|
||||
supportFile: 'test/cypress/support/index.js',
|
||||
videosFolder: 'test/cypress/videos',
|
||||
downloadsFolder: 'test/cypress/downloads',
|
||||
video: false,
|
||||
specPattern: 'test/cypress/integration/**/*.spec.js',
|
||||
experimentalRunAllSpecs: true,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "salix-front",
|
||||
"version": "25.02.0",
|
||||
"version": "25.04.0",
|
||||
"description": "Salix frontend",
|
||||
"productName": "Salix",
|
||||
"author": "Verdnatura",
|
||||
|
@ -25,6 +25,7 @@
|
|||
"axios": "^1.4.0",
|
||||
"chromium": "^3.0.3",
|
||||
"croppie": "^2.6.5",
|
||||
"moment": "^2.30.1",
|
||||
"pinia": "^2.1.3",
|
||||
"quasar": "^2.14.5",
|
||||
"validator": "^13.9.0",
|
||||
|
|
|
@ -20,6 +20,9 @@ dependencies:
|
|||
croppie:
|
||||
specifier: ^2.6.5
|
||||
version: 2.6.5
|
||||
moment:
|
||||
specifier: ^2.30.1
|
||||
version: 2.30.1
|
||||
pinia:
|
||||
specifier: ^2.1.3
|
||||
version: 2.1.7(typescript@5.5.4)(vue@3.4.19)
|
||||
|
@ -832,8 +835,8 @@ packages:
|
|||
vue-i18n:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@intlify/message-compiler': 10.0.0
|
||||
'@intlify/shared': 10.0.0
|
||||
'@intlify/message-compiler': 11.0.0-rc.1
|
||||
'@intlify/shared': 11.0.0-rc.1
|
||||
jsonc-eslint-parser: 1.4.1
|
||||
source-map: 0.6.1
|
||||
vue-i18n: 9.9.1(vue@3.4.19)
|
||||
|
@ -847,11 +850,11 @@ packages:
|
|||
'@intlify/message-compiler': 9.9.1
|
||||
'@intlify/shared': 9.9.1
|
||||
|
||||
/@intlify/message-compiler@10.0.0:
|
||||
resolution: {integrity: sha512-OcaWc63NC/9p1cMdgoNKBj4d61BH8sUW1Hfs6YijTd9656ZR4rNqXAlRnBrfS5ABq0vjQjpa8VnyvH9hK49yBw==}
|
||||
/@intlify/message-compiler@11.0.0-rc.1:
|
||||
resolution: {integrity: sha512-TGw2uBfuTFTegZf/BHtUQBEKxl7Q/dVGLoqRIdw8lFsp9g/53sYn5iD+0HxIzdYjbWL6BTJMXCPUHp9PxDTRPw==}
|
||||
engines: {node: '>= 16'}
|
||||
dependencies:
|
||||
'@intlify/shared': 10.0.0
|
||||
'@intlify/shared': 11.0.0-rc.1
|
||||
source-map-js: 1.0.2
|
||||
dev: true
|
||||
|
||||
|
@ -862,8 +865,8 @@ packages:
|
|||
'@intlify/shared': 9.9.1
|
||||
source-map-js: 1.0.2
|
||||
|
||||
/@intlify/shared@10.0.0:
|
||||
resolution: {integrity: sha512-6ngLfI7DOTew2dcF9WMJx+NnMWghMBhIiHbGg+wRvngpzD5KZJZiJVuzMsUQE1a5YebEmtpTEfUrDp/NqVGdiw==}
|
||||
/@intlify/shared@11.0.0-rc.1:
|
||||
resolution: {integrity: sha512-8tR1xe7ZEbkabTuE/tNhzpolygUn9OaYp9yuYAF4MgDNZg06C3Qny80bes2/e9/Wm3aVkPUlCw6WgU7mQd0yEg==}
|
||||
engines: {node: '>= 16'}
|
||||
dev: true
|
||||
|
||||
|
@ -887,7 +890,7 @@ packages:
|
|||
optional: true
|
||||
dependencies:
|
||||
'@intlify/bundle-utils': 4.0.0(vue-i18n@9.9.1)
|
||||
'@intlify/shared': 10.0.0
|
||||
'@intlify/shared': 11.0.0-rc.1
|
||||
'@rollup/pluginutils': 4.2.1
|
||||
'@vue/compiler-sfc': 3.4.19
|
||||
debug: 4.3.4(supports-color@8.1.1)
|
||||
|
@ -4960,6 +4963,10 @@ packages:
|
|||
uuid: 8.3.2
|
||||
dev: true
|
||||
|
||||
/moment@2.30.1:
|
||||
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
|
||||
dev: false
|
||||
|
||||
/ms@2.0.0:
|
||||
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ export default {
|
|||
console.error(error);
|
||||
}
|
||||
form.addEventListener('keyup', function (evt) {
|
||||
if (evt.key === 'Enter') {
|
||||
if (evt.key === 'Enter' && !that.$attrs['prevent-submit']) {
|
||||
const input = evt.target;
|
||||
if (input.type == 'textarea' && evt.shiftKey) {
|
||||
evt.preventDefault();
|
||||
|
|
|
@ -62,6 +62,7 @@ async function setProvince(id, data) {
|
|||
postcodeFormData.provinceFk = id;
|
||||
await fetchTowns();
|
||||
}
|
||||
|
||||
async function onProvinceCreated(data) {
|
||||
postcodeFormData.provinceFk = data.id;
|
||||
}
|
||||
|
|
|
@ -198,6 +198,7 @@ async function fetch() {
|
|||
} catch (e) {
|
||||
state.set(modelValue, {});
|
||||
originalData.value = {};
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ const onDataSaved = (data) => {
|
|||
<FormModelPopup
|
||||
url-create="Items/regularize"
|
||||
model="Items"
|
||||
:title="t('Regularize stock')"
|
||||
:title="t('item.regularizeStock')"
|
||||
:form-initial-data="regularizeFormData"
|
||||
@on-data-saved="onDataSaved($event)"
|
||||
>
|
||||
|
@ -55,6 +55,7 @@ const onDataSaved = (data) => {
|
|||
v-model.number="data.quantity"
|
||||
type="number"
|
||||
autofocus
|
||||
data-cy="regularizeStockInput"
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
|
||||
import VnFilter from 'components/VnTable/VnFilter.vue';
|
||||
|
@ -11,16 +10,11 @@ defineProps({
|
|||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
chipLocale: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
searchUrl: {
|
||||
type: [String, Boolean],
|
||||
default: 'table',
|
||||
},
|
||||
});
|
||||
const { t } = useI18n();
|
||||
|
||||
const tableFilterRef = ref([]);
|
||||
|
||||
|
@ -62,9 +56,9 @@ function columnName(col) {
|
|||
:columns="columns"
|
||||
/>
|
||||
</template>
|
||||
<template #tags="{ tag, formatFn }" v-if="chipLocale">
|
||||
<template #tags="{ tag, formatFn, getLocale }">
|
||||
<div class="q-gutter-x-xs">
|
||||
<strong>{{ t(`${chipLocale}.${tag.label}`) }}: </strong>
|
||||
<strong>{{ getLocale(`${tag.label}`) }}: </strong>
|
||||
<span>{{ formatFn(tag.value) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import { createWrapper, axios } from 'app/test/vitest/helper';
|
||||
import FilterItemForm from 'src/components/FilterItemForm.vue';
|
||||
import { vi, beforeAll, describe, expect, it } from 'vitest';
|
||||
|
||||
describe('FilterItemForm', () => {
|
||||
let vm;
|
||||
let wrapper;
|
||||
|
||||
beforeAll(() => {
|
||||
wrapper = createWrapper(FilterItemForm, {
|
||||
props: {
|
||||
url: 'Items/withName',
|
||||
},
|
||||
});
|
||||
vm = wrapper.vm;
|
||||
wrapper = wrapper.wrapper;
|
||||
|
||||
vi.spyOn(axios, 'get').mockResolvedValue({
|
||||
data: [
|
||||
{
|
||||
id: 999996,
|
||||
name: 'bolas de madera',
|
||||
size: 2,
|
||||
inkFk: null,
|
||||
producerFk: null,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter data and populate tableRows for table display', async () => {
|
||||
vm.itemFilterParams.name = 'bolas de madera';
|
||||
|
||||
await vm.onSubmit();
|
||||
|
||||
const expectedFilter = {
|
||||
include: [
|
||||
{ relation: 'producer', scope: { fields: ['name'] } },
|
||||
{ relation: 'ink', scope: { fields: ['name'] } },
|
||||
],
|
||||
where: {"name":{"like":"%bolas de madera%"}},
|
||||
};
|
||||
|
||||
expect(axios.get).toHaveBeenCalledWith('Items/withName', {
|
||||
params: { filter: JSON.stringify(expectedFilter) },
|
||||
});
|
||||
|
||||
expect(vm.tableRows).toEqual([
|
||||
{
|
||||
id: 999996,
|
||||
name: 'bolas de madera',
|
||||
size: 2,
|
||||
inkFk: null,
|
||||
producerFk: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle an empty itemFilterParams correctly', async () => {
|
||||
vm.itemFilterParams.name = null;
|
||||
vm.itemFilterParams = {};
|
||||
|
||||
await vm.onSubmit();
|
||||
|
||||
const expectedFilter = {
|
||||
include: [
|
||||
{ relation: 'producer', scope: { fields: ['name'] } },
|
||||
{ relation: 'ink', scope: { fields: ['name'] } },
|
||||
],
|
||||
where: {},
|
||||
};
|
||||
|
||||
expect(axios.get).toHaveBeenCalledWith('Items/withName', {
|
||||
params: { filter: JSON.stringify(expectedFilter) },
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit "itemSelected" with the correct id and close the form', () => {
|
||||
vm.selectItem({ id: 12345 });
|
||||
expect(wrapper.emitted('itemSelected')[0]).toEqual([12345]);
|
||||
});
|
||||
});
|
|
@ -2,9 +2,9 @@
|
|||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import VnRow from '../ui/VnRow.vue';
|
||||
import VnInput from './VnInput.vue';
|
||||
import FetchData from '../FetchData.vue';
|
||||
import useNotify from 'src/composables/useNotify';
|
||||
import VnInputPassword from 'src/components/common/VnInputPassword.vue';
|
||||
|
||||
const props = defineProps({
|
||||
submitFn: { type: Function, default: () => {} },
|
||||
|
@ -70,19 +70,19 @@ defineExpose({ show: () => changePassDialog.value.show() });
|
|||
</QCardSection>
|
||||
<QForm ref="form">
|
||||
<QCardSection>
|
||||
<VnInput
|
||||
<VnInputPassword
|
||||
v-if="props.askOldPass"
|
||||
:label="t('Old password')"
|
||||
v-model="passwords.oldPassword"
|
||||
type="password"
|
||||
:required="true"
|
||||
:toggle-visibility="true"
|
||||
autofocus
|
||||
/>
|
||||
<VnInput
|
||||
<VnInputPassword
|
||||
:label="t('New password')"
|
||||
v-model="passwords.newPassword"
|
||||
type="password"
|
||||
:required="true"
|
||||
:toggle-visibility="true"
|
||||
:info="
|
||||
t('passwordRequirements', {
|
||||
length: requirements.length,
|
||||
|
@ -95,10 +95,10 @@ defineExpose({ show: () => changePassDialog.value.show() });
|
|||
autofocus
|
||||
/>
|
||||
|
||||
<VnInput
|
||||
<VnInputPassword
|
||||
:label="t('Repeat password')"
|
||||
v-model="passwords.repeatPassword"
|
||||
type="password"
|
||||
:toggle-visibility="true"
|
||||
/>
|
||||
</QCardSection>
|
||||
</QForm>
|
||||
|
|
|
@ -45,6 +45,7 @@ const $props = defineProps({
|
|||
});
|
||||
|
||||
const vnInputRef = ref(null);
|
||||
const showPassword = ref(false);
|
||||
const value = computed({
|
||||
get() {
|
||||
return $props.modelValue;
|
||||
|
@ -134,20 +135,23 @@ const handleInsertMode = (e) => {
|
|||
hide-bottom-space
|
||||
:data-cy="$attrs.dataCy ?? $attrs.label + '_input'"
|
||||
>
|
||||
<template v-if="$slots.prepend" #prepend>
|
||||
<template #prepend>
|
||||
<slot name="prepend" />
|
||||
</template>
|
||||
<template #append>
|
||||
<QIcon
|
||||
name="close"
|
||||
size="xs"
|
||||
v-if="
|
||||
hover &&
|
||||
value &&
|
||||
!$attrs.disabled &&
|
||||
!$attrs.readonly &&
|
||||
$props.clearable
|
||||
"
|
||||
:style="{
|
||||
visibility:
|
||||
hover &&
|
||||
value &&
|
||||
!$attrs.disabled &&
|
||||
!$attrs.readonly &&
|
||||
$props.clearable
|
||||
? 'visible'
|
||||
: 'hidden',
|
||||
}"
|
||||
@click="
|
||||
() => {
|
||||
value = null;
|
||||
|
@ -155,7 +159,7 @@ const handleInsertMode = (e) => {
|
|||
emit('remove');
|
||||
}
|
||||
"
|
||||
></QIcon>
|
||||
/>
|
||||
<slot name="append" v-if="$slots.append && !$attrs.disabled" />
|
||||
<QIcon v-if="info" name="info">
|
||||
<QTooltip max-width="350px">
|
||||
|
@ -166,18 +170,3 @@ const handleInsertMode = (e) => {
|
|||
</QInput>
|
||||
</div>
|
||||
</template>
|
||||
<i18n>
|
||||
en:
|
||||
inputMin: Must be more than {value}
|
||||
maxLength: The value exceeds {value} characters
|
||||
inputMax: Must be less than {value}
|
||||
es:
|
||||
inputMin: Debe ser mayor a {value}
|
||||
maxLength: El valor excede los {value} carácteres
|
||||
inputMax: Debe ser menor a {value}
|
||||
</i18n>
|
||||
<style lang="scss">
|
||||
.q-field__append {
|
||||
padding-inline: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
<script setup>
|
||||
import { onMounted, watch, computed, ref, useAttrs } from 'vue';
|
||||
import { date } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import VnDate from './VnDate.vue';
|
||||
import { useRequired } from 'src/composables/useRequired';
|
||||
|
||||
const $attrs = useAttrs();
|
||||
const { isRequired, requiredFieldRule } = useRequired($attrs);
|
||||
const model = defineModel({ type: [String, Date] });
|
||||
const { t } = useI18n();
|
||||
|
||||
const $props = defineProps({
|
||||
isOutlined: {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<script setup>
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const model = defineModel({ type: [Number, String] });
|
||||
const $props = defineProps({
|
||||
toggleVisibility: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const showPassword = ref(false);
|
||||
</script>
|
||||
<template>
|
||||
<VnInput
|
||||
v-bind="{ ...$attrs }"
|
||||
v-model="model"
|
||||
:type="
|
||||
$props.toggleVisibility ? (showPassword ? 'text' : 'password') : $attrs.type
|
||||
"
|
||||
>
|
||||
<template #append v-if="toggleVisibility">
|
||||
<QIcon
|
||||
:name="showPassword ? 'visibility_off' : 'visibility'"
|
||||
class="cursor-pointer"
|
||||
@click="showPassword = !showPassword"
|
||||
/>
|
||||
</template>
|
||||
</VnInput>
|
||||
</template>
|
|
@ -1,13 +1,11 @@
|
|||
<script setup>
|
||||
import { computed, ref, useAttrs } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { date } from 'quasar';
|
||||
import VnTime from './VnTime.vue';
|
||||
import { useRequired } from 'src/composables/useRequired';
|
||||
|
||||
const $attrs = useAttrs();
|
||||
const { isRequired, requiredFieldRule } = useRequired($attrs);
|
||||
const { t } = useI18n();
|
||||
const model = defineModel({ type: String });
|
||||
const props = defineProps({
|
||||
timeOnly: {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||
import VnSelectDialog from 'components/common/VnSelectDialog.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { computed } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
import { useAttrs } from 'vue';
|
||||
import { useRequired } from 'src/composables/useRequired';
|
||||
const { t } = useI18n();
|
||||
|
@ -16,6 +16,14 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.location,
|
||||
(newValue) => {
|
||||
if (!modelValue.value) return;
|
||||
modelValue.value = formatLocation(newValue) ?? null;
|
||||
}
|
||||
);
|
||||
|
||||
const mixinRules = [requiredFieldRule];
|
||||
const locationProperties = [
|
||||
'postcode',
|
||||
|
@ -43,9 +51,7 @@ const formatLocation = (obj, properties = locationProperties) => {
|
|||
return filteredParts.join(', ');
|
||||
};
|
||||
|
||||
const modelValue = computed(() =>
|
||||
props.location ? formatLocation(props.location, locationProperties) : null
|
||||
);
|
||||
const modelValue = ref(props.location ? formatLocation(props.location) : null);
|
||||
|
||||
function showLabel(data) {
|
||||
const dataProperties = [
|
||||
|
|
|
@ -113,8 +113,15 @@ const $props = defineProps({
|
|||
});
|
||||
|
||||
const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
|
||||
const { optionLabel, optionValue, optionFilter, optionFilterValue, options, modelValue } =
|
||||
toRefs($props);
|
||||
const {
|
||||
optionLabel,
|
||||
optionValue,
|
||||
optionCaption,
|
||||
optionFilter,
|
||||
optionFilterValue,
|
||||
options,
|
||||
modelValue,
|
||||
} = toRefs($props);
|
||||
const myOptions = ref([]);
|
||||
const myOptionsOriginal = ref([]);
|
||||
const vnSelectRef = ref();
|
||||
|
@ -198,10 +205,10 @@ function filter(val, options) {
|
|||
}
|
||||
|
||||
if (!row) return;
|
||||
const id = row[$props.optionValue];
|
||||
const id = String(row[$props.optionValue]);
|
||||
const optionLabel = String(row[$props.optionLabel]).toLowerCase();
|
||||
|
||||
return id == search || optionLabel.includes(search);
|
||||
return id.includes(search) || optionLabel.includes(search);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -321,6 +328,11 @@ function handleKeyDown(event) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getCaption(opt) {
|
||||
if (optionCaption.value === false) return;
|
||||
return opt[optionCaption.value] || opt[optionValue.value];
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -391,8 +403,8 @@ function handleKeyDown(event) {
|
|||
<QItemLabel>
|
||||
{{ opt[optionLabel] }}
|
||||
</QItemLabel>
|
||||
<QItemLabel caption v-if="optionCaption !== false">
|
||||
{{ `#${opt[optionCaption] || opt[optionValue]}` }}
|
||||
<QItemLabel caption v-if="getCaption(opt)">
|
||||
{{ `#${getCaption(opt)}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
|
|
|
@ -51,6 +51,7 @@ const url = computed(() => {
|
|||
option-value="id"
|
||||
option-label="nickname"
|
||||
:fields="['id', 'name', 'nickname', 'code']"
|
||||
:filter-options="['id', 'name', 'nickname', 'code']"
|
||||
sort-by="nickname ASC"
|
||||
>
|
||||
<template #prepend v-if="$props.hasAvatar">
|
||||
|
@ -71,7 +72,7 @@ const url = computed(() => {
|
|||
{{ scope.opt.nickname }}
|
||||
</QItemLabel>
|
||||
<QItemLabel caption v-else>
|
||||
{{ scope.opt.nickname }}, {{ scope.opt.code }}
|
||||
#{{ scope.opt.id }}, {{ scope.opt.nickname }}, {{ scope.opt.code }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import { createWrapper } from 'app/test/vitest/helper.js';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
|
||||
let vm;
|
||||
let wrapper;
|
||||
|
||||
function generateWrapper(date, outlined, required) {
|
||||
wrapper = createWrapper(VnInputDate, {
|
||||
props: {
|
||||
modelValue: date,
|
||||
},
|
||||
attrs: {
|
||||
isOutlined: outlined,
|
||||
required: required
|
||||
},
|
||||
});
|
||||
wrapper = wrapper.wrapper;
|
||||
vm = wrapper.vm;
|
||||
};
|
||||
|
||||
describe('VnInputDate', () => {
|
||||
|
||||
describe('formattedDate', () => {
|
||||
it('formats a valid date correctly', async () => {
|
||||
generateWrapper('2023-12-25', false, false);
|
||||
await vm.$nextTick();
|
||||
expect(vm.formattedDate).toBe('25/12/2023');
|
||||
});
|
||||
|
||||
it('updates the model value when a new date is set', async () => {
|
||||
const input = wrapper.find('input');
|
||||
await input.setValue('31/12/2023');
|
||||
expect(wrapper.emitted()['update:modelValue']).toBeTruthy();
|
||||
expect(wrapper.emitted()['update:modelValue'][0][0]).toBe('2023-12-31T00:00:00.000Z');
|
||||
});
|
||||
|
||||
it('should not update the model value when an invalid date is set', async () => {
|
||||
const input = wrapper.find('input');
|
||||
await input.setValue('invalid-date');
|
||||
expect(wrapper.emitted()['update:modelValue'][0][0]).toBe('2023-12-31T00:00:00.000Z');
|
||||
});
|
||||
});
|
||||
|
||||
describe('styleAttrs', () => {
|
||||
it('should return empty styleAttrs when isOutlined is false', async () => {
|
||||
generateWrapper('2023-12-25', false, false);
|
||||
await vm.$nextTick();
|
||||
expect(vm.styleAttrs).toEqual({});
|
||||
});
|
||||
|
||||
it('should set styleAttrs when isOutlined is true', async () => {
|
||||
generateWrapper('2023-12-25', true, false);
|
||||
await vm.$nextTick();
|
||||
expect(vm.styleAttrs.outlined).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('required', () => {
|
||||
it('should not applies required class when isRequired is false', async () => {
|
||||
generateWrapper('2023-12-25', false, false);
|
||||
await vm.$nextTick();
|
||||
expect(wrapper.find('.vn-input-date').classes()).not.toContain('required');
|
||||
});
|
||||
|
||||
it('should applies required class when isRequired is true', async () => {
|
||||
generateWrapper('2023-12-25', false, true);
|
||||
await vm.$nextTick();
|
||||
expect(wrapper.find('.vn-input-date').classes()).toContain('required');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,95 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import VnJsonValue from 'src/components/common/VnJsonValue.vue';
|
||||
import { createWrapper } from 'app/test/vitest/helper';
|
||||
|
||||
const buildComponent = (props) => {
|
||||
return createWrapper(VnJsonValue, {
|
||||
props,
|
||||
}).wrapper;
|
||||
};
|
||||
|
||||
describe('VnJsonValue', () => {
|
||||
it('renders null value correctly', async () => {
|
||||
const wrapper = buildComponent({ value: null });
|
||||
const span = wrapper.find('span');
|
||||
expect(span.text()).toBe('∅');
|
||||
expect(span.classes()).toContain('json-null');
|
||||
});
|
||||
|
||||
it('renders boolean true correctly', async () => {
|
||||
const wrapper = buildComponent({ value: true });
|
||||
const span = wrapper.find('span');
|
||||
expect(span.text()).toBe('✓');
|
||||
expect(span.classes()).toContain('json-true');
|
||||
});
|
||||
|
||||
it('renders boolean false correctly', async () => {
|
||||
const wrapper = buildComponent({ value: false });
|
||||
const span = wrapper.find('span');
|
||||
expect(span.text()).toBe('✗');
|
||||
expect(span.classes()).toContain('json-false');
|
||||
});
|
||||
|
||||
it('renders a short string correctly', async () => {
|
||||
const wrapper = buildComponent({ value: 'Hello' });
|
||||
const span = wrapper.find('span');
|
||||
expect(span.text()).toBe('Hello');
|
||||
expect(span.classes()).toContain('json-string');
|
||||
});
|
||||
|
||||
it('renders a long string correctly with ellipsis', async () => {
|
||||
const longString = 'a'.repeat(600);
|
||||
const wrapper = buildComponent({ value: longString });
|
||||
const span = wrapper.find('span');
|
||||
expect(span.text()).toContain('...');
|
||||
expect(span.text().length).toBeLessThanOrEqual(515);
|
||||
expect(span.attributes('title')).toBe(longString);
|
||||
expect(span.classes()).toContain('json-string');
|
||||
});
|
||||
|
||||
it('renders a number correctly', async () => {
|
||||
const wrapper = buildComponent({ value: 123.4567 });
|
||||
const span = wrapper.find('span');
|
||||
expect(span.text()).toBe('123.457');
|
||||
expect(span.classes()).toContain('json-number');
|
||||
});
|
||||
|
||||
it('renders an integer correctly', async () => {
|
||||
const wrapper = buildComponent({ value: 42 });
|
||||
const span = wrapper.find('span');
|
||||
expect(span.text()).toBe('42');
|
||||
expect(span.classes()).toContain('json-number');
|
||||
});
|
||||
|
||||
it('renders a date correctly', async () => {
|
||||
const date = new Date('2023-01-01');
|
||||
const wrapper = buildComponent({ value: date });
|
||||
const span = wrapper.find('span');
|
||||
expect(span.text()).toBe('2023-01-01');
|
||||
expect(span.classes()).toContain('json-object');
|
||||
});
|
||||
|
||||
it('renders an object correctly', async () => {
|
||||
const obj = { key: 'value' };
|
||||
const wrapper = buildComponent({ value: obj });
|
||||
const span = wrapper.find('span');
|
||||
expect(span.text()).toBe(obj.toString());
|
||||
expect(span.classes()).toContain('json-object');
|
||||
});
|
||||
|
||||
it('renders an array correctly', async () => {
|
||||
const arr = [1, 2, 3];
|
||||
const wrapper = buildComponent({ value: arr });
|
||||
const span = wrapper.find('span');
|
||||
expect(span.text()).toBe(arr.toString());
|
||||
expect(span.classes()).toContain('json-object');
|
||||
});
|
||||
|
||||
it('updates value when prop changes', async () => {
|
||||
const wrapper = buildComponent({ value: true });
|
||||
await wrapper.setProps({ value: 123 });
|
||||
const span = wrapper.find('span');
|
||||
expect(span.text()).toBe('123');
|
||||
expect(span.classes()).toContain('json-number');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,107 @@
|
|||
import { describe, it, expect, vi, beforeAll, afterEach, beforeEach } from 'vitest';
|
||||
import { createWrapper, axios } from 'app/test/vitest/helper';
|
||||
import VnNotes from 'src/components/ui/VnNotes.vue';
|
||||
|
||||
describe('VnNotes', () => {
|
||||
let vm;
|
||||
let wrapper;
|
||||
let spyFetch;
|
||||
let postMock;
|
||||
let expectedBody;
|
||||
const mockData= {name: 'Tony', lastName: 'Stark', text: 'Test Note', observationTypeFk: 1};
|
||||
|
||||
function generateExpectedBody() {
|
||||
expectedBody = {...vm.$props.body, ...{ text: vm.newNote.text, observationTypeFk: vm.newNote.observationTypeFk }};
|
||||
}
|
||||
|
||||
async function setTestParams(text, observationType, type){
|
||||
vm.newNote.text = text;
|
||||
vm.newNote.observationTypeFk = observationType;
|
||||
wrapper.setProps({ selectType: type });
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
vi.spyOn(axios, 'get').mockReturnValue({ data: [] });
|
||||
|
||||
wrapper = createWrapper(VnNotes, {
|
||||
propsData: {
|
||||
url: '/test',
|
||||
body: { name: 'Tony', lastName: 'Stark' },
|
||||
}
|
||||
});
|
||||
wrapper = wrapper.wrapper;
|
||||
vm = wrapper.vm;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
postMock = vi.spyOn(axios, 'post').mockResolvedValue(mockData);
|
||||
spyFetch = vi.spyOn(vm.vnPaginateRef, 'fetch').mockImplementation(() => vi.fn());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
expectedBody = {};
|
||||
});
|
||||
|
||||
describe('insert', () => {
|
||||
it('should not call axios.post and vnPaginateRef.fetch if newNote.text is null', async () => {
|
||||
await setTestParams( null, null, true );
|
||||
|
||||
await vm.insert();
|
||||
|
||||
expect(postMock).not.toHaveBeenCalled();
|
||||
expect(spyFetch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not call axios.post and vnPaginateRef.fetch if newNote.text is empty', async () => {
|
||||
await setTestParams( "", null, false );
|
||||
|
||||
await vm.insert();
|
||||
|
||||
expect(postMock).not.toHaveBeenCalled();
|
||||
expect(spyFetch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not call axios.post and vnPaginateRef.fetch if observationTypeFk is missing and selectType is true', async () => {
|
||||
await setTestParams( "Test Note", null, true );
|
||||
|
||||
await vm.insert();
|
||||
|
||||
expect(postMock).not.toHaveBeenCalled();
|
||||
expect(spyFetch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call axios.post and vnPaginateRef.fetch if observationTypeFk is missing and selectType is false', async () => {
|
||||
await setTestParams( "Test Note", null, false );
|
||||
|
||||
generateExpectedBody();
|
||||
|
||||
await vm.insert();
|
||||
|
||||
expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody);
|
||||
expect(spyFetch).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call axios.post and vnPaginateRef.fetch if observationTypeFk is setted and selectType is false', async () => {
|
||||
await setTestParams( "Test Note", 1, false );
|
||||
|
||||
generateExpectedBody();
|
||||
|
||||
await vm.insert();
|
||||
|
||||
expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody);
|
||||
expect(spyFetch).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call axios.post and vnPaginateRef.fetch when newNote is valid', async () => {
|
||||
await setTestParams( "Test Note", 1, true );
|
||||
|
||||
generateExpectedBody();
|
||||
|
||||
await vm.insert();
|
||||
|
||||
expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody);
|
||||
expect(spyFetch).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -6,6 +6,7 @@ import { useArrayData } from 'composables/useArrayData';
|
|||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||
import { useState } from 'src/composables/useState';
|
||||
import { useRoute } from 'vue-router';
|
||||
import VnMoreOptions from './VnMoreOptions.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
url: {
|
||||
|
@ -47,7 +48,6 @@ let store;
|
|||
let entity;
|
||||
const isLoading = ref(false);
|
||||
const isSameDataKey = computed(() => $props.dataKey === route.meta.moduleName);
|
||||
const menuRef = ref();
|
||||
defineExpose({ getData });
|
||||
|
||||
onBeforeMount(async () => {
|
||||
|
@ -159,25 +159,11 @@ const toModule = computed(() =>
|
|||
</QTooltip>
|
||||
</QBtn>
|
||||
</RouterLink>
|
||||
<QBtn
|
||||
v-if="$slots.menu"
|
||||
color="white"
|
||||
dense
|
||||
flat
|
||||
icon="more_vert"
|
||||
round
|
||||
size="md"
|
||||
data-cy="descriptor-more-opts"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('components.cardDescriptor.moreOptions') }}
|
||||
</QTooltip>
|
||||
<QMenu :ref="menuRef">
|
||||
<QList>
|
||||
<slot name="menu" :entity="entity" :menu-ref="menuRef" />
|
||||
</QList>
|
||||
</QMenu>
|
||||
</QBtn>
|
||||
<VnMoreOptions v-if="$slots.menu">
|
||||
<template #menu="{ menuRef }">
|
||||
<slot name="menu" :entity="entity" :menu-ref="menuRef" />
|
||||
</template>
|
||||
</VnMoreOptions>
|
||||
</div>
|
||||
<slot name="before" />
|
||||
<div class="body q-py-sm">
|
||||
|
@ -222,8 +208,8 @@ const toModule = computed(() =>
|
|||
/>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.body) {
|
||||
<style lang="scss">
|
||||
.body {
|
||||
background-color: var(--vn-section-color);
|
||||
.text-h5 {
|
||||
font-size: 20px;
|
||||
|
@ -262,7 +248,9 @@ const toModule = computed(() =>
|
|||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<script setup>
|
||||
import { ref, computed, watch, onBeforeMount, onMounted } from 'vue';
|
||||
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 { useStateStore } from 'src/stores/useStateStore';
|
||||
import VnMoreOptions from './VnMoreOptions.vue';
|
||||
|
||||
const props = defineProps({
|
||||
url: {
|
||||
|
@ -40,7 +41,6 @@ const { store } = arrayData;
|
|||
const entity = computed(() => (Array.isArray(store.data) ? store.data[0] : store.data));
|
||||
const isLoading = ref(false);
|
||||
|
||||
const stateStore = useStateStore();
|
||||
defineExpose({
|
||||
entity,
|
||||
fetch,
|
||||
|
@ -52,9 +52,6 @@ onBeforeMount(async () => {
|
|||
watch(props, async () => await fetch());
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
stateStore.rightDrawerChangeValue(false);
|
||||
});
|
||||
async function fetch() {
|
||||
store.url = props.url;
|
||||
store.filter = props.filter ?? {};
|
||||
|
@ -64,6 +61,7 @@ async function fetch() {
|
|||
isLoading.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="summary container">
|
||||
<QCard class="cardSummary">
|
||||
|
@ -86,9 +84,14 @@ async function fetch() {
|
|||
<slot name="header" :entity="entity" dense>
|
||||
{{ entity.id + ' - ' + entity.name }}
|
||||
</slot>
|
||||
<slot name="header-right" :entity="entity">
|
||||
<span></span>
|
||||
</slot>
|
||||
<span class="row no-wrap">
|
||||
<slot name="header-right" :entity="entity" />
|
||||
<VnMoreOptions v-if="$slots.menu && isDialogOpened()">
|
||||
<template #menu="{ menuRef }">
|
||||
<slot name="menu" :entity="entity" :menu-ref="menuRef" />
|
||||
</template>
|
||||
</VnMoreOptions>
|
||||
</span>
|
||||
</div>
|
||||
<div class="summaryBody row q-mb-md">
|
||||
<slot name="body" :entity="entity" />
|
||||
|
@ -97,6 +100,7 @@ async function fetch() {
|
|||
</QCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.summary.container {
|
||||
display: flex;
|
||||
|
|
|
@ -190,7 +190,7 @@ const getLocale = (label) => {
|
|||
const globalLocale = `globals.params.${param}`;
|
||||
if (te(globalLocale)) return t(globalLocale);
|
||||
else if (te(t(`params.${param}`)));
|
||||
else return t(`${route.meta.moduleName}.params.${param}`);
|
||||
else return t(`${route.meta.moduleName.toLowerCase()}.params.${param}`);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ onBeforeMount(async () => {
|
|||
.data;
|
||||
if (!channel) channel = defaultChannel;
|
||||
|
||||
phone.value = await parsePhone(props.phoneNumber, props.country.toLowerCase());
|
||||
phone.value = await parsePhone(props.phoneNumber, props.country?.toLowerCase());
|
||||
config[
|
||||
type
|
||||
].url = `${url}?customerIdentity=%2B${phone.value}&channelId=${channel}`;
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<template>
|
||||
<QBtn
|
||||
color="white"
|
||||
dense
|
||||
flat
|
||||
icon="more_vert"
|
||||
round
|
||||
size="md"
|
||||
data-cy="descriptor-more-opts"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ $t('components.cardDescriptor.moreOptions') }}
|
||||
</QTooltip>
|
||||
<QMenu ref="menuRef">
|
||||
<QList>
|
||||
<slot name="menu" :menu-ref="$refs.menuRef" />
|
||||
</QList>
|
||||
</QMenu>
|
||||
</QBtn>
|
||||
</template>
|
|
@ -110,7 +110,7 @@ onBeforeRouteLeave((to, from, next) => {
|
|||
:url="$props.url"
|
||||
order="created DESC"
|
||||
:limit="0"
|
||||
:filter="$props.filter"
|
||||
:user-filter="$props.filter"
|
||||
auto-load
|
||||
ref="vnPaginateRef"
|
||||
class="show"
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
<script setup>
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import { onMounted, ref, computed, watch } from 'vue';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useArrayData } from 'composables/useArrayData';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useStateStore } from 'src/stores/useStateStore';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const quasar = useQuasar();
|
||||
const { t } = useI18n();
|
||||
const state = useStateStore();
|
||||
const route = useRoute();
|
||||
|
||||
const props = defineProps({
|
||||
dataKey: {
|
||||
|
@ -83,6 +85,17 @@ if (props.redirect)
|
|||
};
|
||||
let arrayData = useArrayData(props.dataKey, arrayDataProps);
|
||||
let store = arrayData.store;
|
||||
const to = computed(() => {
|
||||
const url = { path: route.path, query: { ...(route.query ?? {}) } };
|
||||
const searchUrl = arrayData.store.searchUrl;
|
||||
const currentFilter = {
|
||||
...arrayData.store.currentFilter,
|
||||
search: searchText.value || undefined,
|
||||
};
|
||||
|
||||
if (searchUrl) url.query[searchUrl] = JSON.stringify(currentFilter);
|
||||
return url;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.dataKey,
|
||||
|
@ -132,23 +145,32 @@ async function search() {
|
|||
<template>
|
||||
<Teleport to="#searchbar" v-if="state.isHeaderMounted()">
|
||||
<QForm @submit="search" id="searchbarForm">
|
||||
<RouterLink
|
||||
:to="to"
|
||||
@click="
|
||||
!$event.shiftKey && !$event.ctrlKey && search();
|
||||
$refs.input.focus();
|
||||
"
|
||||
>
|
||||
<QIcon
|
||||
v-if="!quasar.platform.is.mobile"
|
||||
class="cursor-pointer"
|
||||
name="search"
|
||||
size="sm"
|
||||
>
|
||||
<QTooltip>{{ t('link') }}</QTooltip>
|
||||
</QIcon>
|
||||
</RouterLink>
|
||||
<VnInput
|
||||
id="searchbar"
|
||||
ref="input"
|
||||
v-model.trim="searchText"
|
||||
:placeholder="t(props.label)"
|
||||
dense
|
||||
standout
|
||||
autofocus
|
||||
data-cy="vnSearchBar"
|
||||
data-cy="vn-searchbar"
|
||||
borderless
|
||||
>
|
||||
<template #prepend>
|
||||
<QIcon
|
||||
v-if="!quasar.platform.is.mobile"
|
||||
class="cursor-pointer"
|
||||
name="search"
|
||||
@click="search"
|
||||
/>
|
||||
</template>
|
||||
<template #append>
|
||||
<QIcon
|
||||
v-if="props.info && $q.screen.gt.xs"
|
||||
|
@ -173,20 +195,52 @@ async function search() {
|
|||
.q-field {
|
||||
transition: width 0.36s;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
:deep(.q-field__native) {
|
||||
padding-top: 10px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
:deep(.q-field--dark .q-field__native:focus) {
|
||||
color: black;
|
||||
}
|
||||
|
||||
:deep(.q-field--focused) {
|
||||
.q-icon {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
.cursor-info {
|
||||
cursor: help;
|
||||
}
|
||||
#searchbar {
|
||||
.q-field--standout.q-field--highlighted .q-field__control {
|
||||
|
||||
.q-form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
padding: 0 5px;
|
||||
background-color: var(--vn-search-color);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--vn-search-color-hover);
|
||||
}
|
||||
&:focus-within {
|
||||
background-color: white;
|
||||
color: black;
|
||||
.q-field__native,
|
||||
|
||||
.q-icon {
|
||||
color: black !important;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.q-icon {
|
||||
color: var(--vn-label-color);
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
en:
|
||||
link: click to search, ctrl + click to open in a new tab, shift + click to open in a new window
|
||||
es:
|
||||
link: clic para buscar, ctrl + clic para abrir en una nueva pestaña, shift + clic para abrir en una nueva ventana
|
||||
</i18n>
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import { createWrapper } from 'app/test/vitest/helper';
|
||||
import FetchedTags from 'src/components/ui/FetchedTags.vue';
|
||||
|
||||
describe('tags computed property', () => {
|
||||
it('returns an object with the correct keys and values', () => {
|
||||
const vm = createWrapper(FetchedTags, {
|
||||
props: {
|
||||
item: {
|
||||
tag1: 'JavaScript',
|
||||
value1: 'Programming Language',
|
||||
tag2: 'Vue',
|
||||
value2: 'Framework',
|
||||
tag3: 'EmptyTag',
|
||||
},
|
||||
tag: 'tag',
|
||||
value: 'value',
|
||||
columns: 2,
|
||||
},
|
||||
}).vm;
|
||||
expect(vm.tags).toEqual({
|
||||
JavaScript: 'Programming Language',
|
||||
Vue: 'Framework',
|
||||
EmptyTag: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an empty object if the item prop is an empty object', () => {
|
||||
const vm = createWrapper(FetchedTags, {
|
||||
props: {
|
||||
item: {},
|
||||
tag: 'tag',
|
||||
value: 'value',
|
||||
},
|
||||
}).vm;
|
||||
expect(vm.tags).toEqual({});
|
||||
});
|
||||
|
||||
it('should calculate the correct columnStyle when columns prop is defined', () => {
|
||||
const vm = createWrapper(FetchedTags, {
|
||||
props: {
|
||||
item: {
|
||||
tag1: 'JavaScript',
|
||||
value1: 'Programming Language',
|
||||
tag2: 'Vue',
|
||||
value2: 'Framework',
|
||||
tag3: 'EmptyTag',
|
||||
},
|
||||
tag: 'tag',
|
||||
value: 'value',
|
||||
columns: 2,
|
||||
},
|
||||
}).vm;
|
||||
|
||||
const expectedStyle = {
|
||||
'grid-template-columns': 'repeat(2, 1fr)',
|
||||
'max-width': '8rem',
|
||||
};
|
||||
|
||||
expect(vm.columnStyle).toEqual(expectedStyle);
|
||||
});
|
||||
|
||||
it('should return an empty object for columnStyle when columns prop is not defined', () => {
|
||||
const vm = createWrapper(FetchedTags, {
|
||||
props: {
|
||||
item: {
|
||||
tag1: 'JavaScript',
|
||||
value1: 'Programming Language',
|
||||
tag2: 'Vue',
|
||||
value2: 'Framework',
|
||||
tag3: 'EmptyTag',
|
||||
},
|
||||
tag: 'tag',
|
||||
value: 'value',
|
||||
columns: null,
|
||||
},
|
||||
}).vm;
|
||||
|
||||
expect(vm.columnStyle).toEqual({});
|
||||
});
|
||||
});
|
|
@ -76,26 +76,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
|||
|
||||
cancelRequest();
|
||||
canceller = new AbortController();
|
||||
|
||||
const filter = {
|
||||
limit: store.limit,
|
||||
};
|
||||
let userParams = { ...store.userParams };
|
||||
|
||||
Object.assign(filter, store.userFilter);
|
||||
|
||||
let where;
|
||||
if (filter?.where || store.filter?.where)
|
||||
where = Object.assign(filter?.where ?? {}, store.filter?.where ?? {});
|
||||
Object.assign(filter, store.filter);
|
||||
filter.where = where;
|
||||
const params = { filter };
|
||||
|
||||
Object.assign(params, userParams);
|
||||
if (params.filter) params.filter.skip = store.skip;
|
||||
if (store?.order && typeof store?.order == 'string') store.order = [store.order];
|
||||
if (store.order?.length) params.filter.order = [...store.order];
|
||||
else delete params.filter.order;
|
||||
const { params, limit } = getCurrentFilter();
|
||||
|
||||
store.currentFilter = JSON.parse(JSON.stringify(params));
|
||||
delete store.currentFilter.filter.include;
|
||||
|
@ -121,15 +102,16 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
|||
params,
|
||||
});
|
||||
|
||||
const { limit } = filter;
|
||||
store.hasMoreData = limit && response.data.length >= limit;
|
||||
|
||||
processData(response.data, { map: !!store.mapKey, append });
|
||||
if (!append && !isDialogOpened()) updateRouter && updateStateParams();
|
||||
|
||||
if (!append && !isDialogOpened() && updateRouter) {
|
||||
if (updateStateParams(response.data)?.redirect) return;
|
||||
}
|
||||
store.isLoading = false;
|
||||
canceller = null;
|
||||
|
||||
processData(response.data, { map: !!store.mapKey, append });
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
@ -259,7 +241,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
|||
if (Object.values(store.userParams).length) await fetch({});
|
||||
}
|
||||
|
||||
function updateStateParams() {
|
||||
function updateStateParams(data) {
|
||||
if (!route?.path) return;
|
||||
const usesModuleName = `/${store.data[0].id}` + userOptions.moduleName;
|
||||
const newUrl = { path: route.path, query: { ...(route.query ?? {}) } };
|
||||
|
@ -277,21 +259,46 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
|||
const { path } = matches.at(-1);
|
||||
|
||||
const to =
|
||||
store?.data?.length === 1
|
||||
? path.replace(/\/(list|:id)|-list/, usesModuleName)
|
||||
data?.length === 1
|
||||
? path.replace(/\/(list|:id)|-list/, `/${data[0].id}`)
|
||||
: path.replace(/:id.*/, '');
|
||||
|
||||
|
||||
if (route.path != to) {
|
||||
const pushUrl = { path: to };
|
||||
if (to.endsWith('/list') || to.endsWith('/'))
|
||||
pushUrl.query = newUrl.query;
|
||||
return router.push(pushUrl);
|
||||
return router.push(pushUrl) && { redirect: true };
|
||||
}
|
||||
}
|
||||
|
||||
router.replace(newUrl);
|
||||
}
|
||||
|
||||
function getCurrentFilter() {
|
||||
const filter = {
|
||||
limit: store.limit,
|
||||
};
|
||||
|
||||
let userParams = { ...store.userParams };
|
||||
|
||||
Object.assign(filter, store.userFilter);
|
||||
|
||||
let where;
|
||||
if (filter?.where || store.filter?.where)
|
||||
where = Object.assign(filter?.where ?? {}, store.filter?.where ?? {});
|
||||
Object.assign(filter, store.filter);
|
||||
filter.where = where;
|
||||
const params = { filter };
|
||||
|
||||
Object.assign(params, userParams);
|
||||
if (params.filter) params.filter.skip = store.skip;
|
||||
if (store?.order && typeof store?.order == 'string') store.order = [store.order];
|
||||
if (store.order?.length) params.filter.order = [...store.order];
|
||||
else delete params.filter.order;
|
||||
|
||||
return { filter, params, limit: filter.limit };
|
||||
}
|
||||
|
||||
function processData(data, { map = true, append = true }) {
|
||||
if (!append) {
|
||||
store.data = [];
|
||||
|
@ -324,6 +331,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
|||
fetch,
|
||||
applyFilter,
|
||||
addFilter,
|
||||
getCurrentFilter,
|
||||
addFilterWhere,
|
||||
addOrder,
|
||||
deleteOrder,
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import axios from 'axios';
|
||||
|
||||
export default async (id) => {
|
||||
const { data } = await axios.get(`Accounts/${id}/exists`);
|
||||
return data.exists;
|
||||
};
|
|
@ -10,6 +10,8 @@ body.body--light {
|
|||
--vn-text-color: black;
|
||||
--vn-label-color: #5f5f5f;
|
||||
--vn-accent-color: #e7e3e3;
|
||||
--vn-search-color: #d4d4d4;
|
||||
--vn-search-color-hover: #cfcfcf;
|
||||
--vn-empty-tag: #acacac;
|
||||
--vn-black-text-color: black;
|
||||
--vn-text-color-contrast: white;
|
||||
|
@ -28,6 +30,8 @@ body.body--dark {
|
|||
--vn-text-color: white;
|
||||
--vn-label-color: #a8a8a8;
|
||||
--vn-accent-color: #424242;
|
||||
--vn-search-color: #4b4b4b;
|
||||
--vn-search-color-hover: #5b5b5b;
|
||||
--vn-empty-tag: #2d2d2d;
|
||||
--vn-black-text-color: black;
|
||||
--vn-text-color-contrast: black;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
export default function toDateHourMin(date) {
|
||||
const dateHour = new Date(date).toLocaleDateString('es-ES', {
|
||||
if (!date) return date;
|
||||
return new Date(date).toLocaleDateString('es-ES', {
|
||||
timeZone: 'Europe/Madrid',
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
|
@ -7,5 +8,4 @@ export default function toDateHourMin(date) {
|
|||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
return dateHour;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
export default function toDateHourMinSec(date) {
|
||||
const dateHour = new Date(date).toLocaleDateString('es-ES', {
|
||||
if (!date) return date;
|
||||
return new Date(date).toLocaleDateString('es-ES', {
|
||||
timeZone: 'Europe/Madrid',
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
|
@ -8,5 +9,4 @@ export default function toDateHourMinSec(date) {
|
|||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
});
|
||||
return dateHour;
|
||||
}
|
||||
|
|
|
@ -455,7 +455,26 @@ entry:
|
|||
packingOut: Package out
|
||||
landing: Landing
|
||||
isExcludedFromAvailable: Es inventory
|
||||
params:
|
||||
toShipped: To
|
||||
fromShipped: From
|
||||
warehouseiNFk: Warehouse
|
||||
daysOnward: Days onward
|
||||
daysAgo: Days ago
|
||||
warehouseInFk: Warehouse in
|
||||
ticket:
|
||||
params:
|
||||
ticketFk: Ticket ID
|
||||
weekDay: Weekday
|
||||
agencyModeFk: Agency
|
||||
id: Worker
|
||||
state: State
|
||||
created: Created
|
||||
externalId: External ID
|
||||
counter: Counter
|
||||
freightItemName: Freight item name
|
||||
packageItemName: Package item name
|
||||
longName: Long name
|
||||
card:
|
||||
customerId: Customer ID
|
||||
customerCard: Customer card
|
||||
|
@ -506,6 +525,7 @@ invoiceOut:
|
|||
card:
|
||||
issued: Issued
|
||||
customerCard: Customer card
|
||||
ticketList: Ticket List
|
||||
summary:
|
||||
issued: Issued
|
||||
dued: Due
|
||||
|
@ -697,6 +717,11 @@ wagon:
|
|||
minHeightBetweenTrays: 'The minimum height between trays is '
|
||||
maxWagonHeight: 'The maximum height of the wagon is '
|
||||
uncompleteTrays: There are incomplete trays
|
||||
params:
|
||||
label: Label
|
||||
plate: Plate
|
||||
volume: Volume
|
||||
name: Name
|
||||
|
||||
supplier:
|
||||
list:
|
||||
|
|
|
@ -456,7 +456,25 @@ entry:
|
|||
packingOut: Embalaje envíos
|
||||
landing: Llegada
|
||||
isExcludedFromAvailable: Es inventario
|
||||
params:
|
||||
toShipped: Hasta
|
||||
fromShipped: Desde
|
||||
warehouseInFk: Alm. entrada
|
||||
daysOnward: Días adelante
|
||||
daysAgo: Días atras
|
||||
ticket:
|
||||
params:
|
||||
ticketFk: ID de ticket
|
||||
weekDay: Salida
|
||||
agencyModeFk: Agencia
|
||||
id: Comercial
|
||||
created: Creado
|
||||
state: Estado
|
||||
externalId: ID externo
|
||||
counter: Contador
|
||||
freightItemName: Nombre
|
||||
packageItemName: Embalaje
|
||||
longName: Descripción
|
||||
card:
|
||||
customerId: ID cliente
|
||||
customerCard: Ficha del cliente
|
||||
|
@ -700,6 +718,11 @@ wagon:
|
|||
minHeightBetweenTrays: 'La distancia mínima entre bandejas es '
|
||||
maxWagonHeight: 'La altura máxima del vagón es '
|
||||
uncompleteTrays: Hay bandejas sin completar
|
||||
params:
|
||||
label: Etiqueta
|
||||
plate: Matrícula
|
||||
volume: Volumen
|
||||
name: Nombre
|
||||
supplier:
|
||||
list:
|
||||
payMethod: Método de pago
|
||||
|
|
|
@ -6,6 +6,7 @@ import FormModelPopup from 'components/FormModelPopup.vue';
|
|||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnInputPassword from 'src/components/common/VnInputPassword.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
|
@ -61,10 +62,10 @@ const redirectToAccountBasicData = (_, { id }) => {
|
|||
hide-selected
|
||||
:rules="validate('VnUser.roleFk')"
|
||||
/>
|
||||
<VnInput
|
||||
<VnInputPassword
|
||||
v-model="data.password"
|
||||
:label="t('ldap.password')"
|
||||
type="password"
|
||||
:toggle-visibility="true"
|
||||
:rules="validate('VnUser.password')"
|
||||
/>
|
||||
<QCheckbox
|
||||
|
|
|
@ -8,6 +8,7 @@ import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
|||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import axios from 'axios';
|
||||
import VnInputPassword from 'src/components/common/VnInputPassword.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { notify } = useNotify();
|
||||
|
@ -128,10 +129,9 @@ onMounted(async () => await getInitialLdapConfig());
|
|||
:required="true"
|
||||
:rules="validate('LdapConfig.rdn')"
|
||||
/>
|
||||
<VnInput
|
||||
<VnInputPassword
|
||||
:label="t('ldap.password')"
|
||||
clearable
|
||||
type="password"
|
||||
v-model="data.password"
|
||||
:required="true"
|
||||
:rules="validate('LdapConfig.password')"
|
||||
|
|
|
@ -4,9 +4,9 @@ import { computed, ref } from 'vue';
|
|||
import VnTable from 'components/VnTable/VnTable.vue';
|
||||
import AccountSummary from './Card/AccountSummary.vue';
|
||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSection from 'src/components/common/VnSection.vue';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnInputPassword from 'src/components/common/VnInputPassword.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { viewSummary } = useSummaryDialog();
|
||||
|
@ -168,10 +168,9 @@ function exprBuilder(param, value) {
|
|||
>
|
||||
<template #more-create-dialog="{ data }">
|
||||
<QCardSection>
|
||||
<VnInput
|
||||
<VnInputPassword
|
||||
:label="t('Password')"
|
||||
v-model="data.password"
|
||||
type="password"
|
||||
:required="true"
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
|
|
|
@ -8,6 +8,7 @@ import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
|||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import axios from 'axios';
|
||||
import VnInputPassword from 'src/components/common/VnInputPassword.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { notify } = useNotify();
|
||||
|
@ -143,10 +144,9 @@ onMounted(async () => await getInitialSambaConfig());
|
|||
v-model="data.adUser"
|
||||
:rules="validate('SambaConfigs.adUser')"
|
||||
/>
|
||||
<VnInput
|
||||
<VnInputPassword
|
||||
:label="t('samba.passwordAD')"
|
||||
clearable
|
||||
type="password"
|
||||
v-model="data.adPassword"
|
||||
/>
|
||||
<VnInput
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import useCardDescription from 'src/composables/useCardDescription';
|
||||
import AccountDescriptorMenu from './AccountDescriptorMenu.vue';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnImg from 'src/components/ui/VnImg.vue';
|
||||
import useHasAccount from 'src/composables/useHasAccount.js';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
|
@ -23,6 +23,7 @@ const entityId = computed(() => {
|
|||
return $props.id || route.params.id;
|
||||
});
|
||||
const data = ref(useCardDescription());
|
||||
const hasAccount = ref();
|
||||
const setData = (entity) => (data.value = useCardDescription(entity.nickname, entity.id));
|
||||
|
||||
const filter = {
|
||||
|
@ -30,18 +31,16 @@ const filter = {
|
|||
fields: ['id', 'nickname', 'name', 'role'],
|
||||
include: { relation: 'role', scope: { fields: ['id', 'name'] } },
|
||||
};
|
||||
const hasAccount = ref(false);
|
||||
|
||||
onMounted(async () => {
|
||||
hasAccount.value = await useHasAccount(entityId.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
:url="`Accounts/${entityId}/exists`"
|
||||
auto-load
|
||||
@on-fetch="(data) => (hasAccount = data.exists)"
|
||||
/>
|
||||
<CardDescriptor
|
||||
ref="descriptor"
|
||||
url="VnUsers/preview"
|
||||
:url="`VnUsers/preview`"
|
||||
:filter="filter"
|
||||
module="Account"
|
||||
@on-fetch="setData"
|
||||
|
@ -50,7 +49,7 @@ const hasAccount = ref(false);
|
|||
:subtitle="data.subtitle"
|
||||
>
|
||||
<template #menu>
|
||||
<AccountDescriptorMenu :has-account="hasAccount" />
|
||||
<AccountDescriptorMenu :entity-id="entityId" />
|
||||
</template>
|
||||
<template #before>
|
||||
<VnImg :id="entityId" collection="user" resolution="520x520" class="photo">
|
||||
|
@ -74,7 +73,7 @@ const hasAccount = ref(false);
|
|||
<VnLv :label="t('account.card.nickname')" :value="entity.name" />
|
||||
<VnLv :label="t('account.card.role')" :value="entity.role.name" />
|
||||
</template>
|
||||
<template #icons="{ entity }">
|
||||
<template #actions="{ entity }">
|
||||
<QCardActions class="q-gutter-x-md">
|
||||
<QIcon
|
||||
v-if="!entity.active"
|
||||
|
@ -82,7 +81,7 @@ const hasAccount = ref(false);
|
|||
name="vn:disabled"
|
||||
flat
|
||||
round
|
||||
size="xs"
|
||||
size="sm"
|
||||
class="fill-icon"
|
||||
>
|
||||
<QTooltip>{{ t('account.card.deactivated') }}</QTooltip>
|
||||
|
@ -93,7 +92,7 @@ const hasAccount = ref(false);
|
|||
v-if="hasAccount"
|
||||
flat
|
||||
round
|
||||
size="xs"
|
||||
size="sm"
|
||||
class="fill-icon"
|
||||
>
|
||||
<QTooltip>{{ t('account.card.enabled') }}</QTooltip>
|
||||
|
|
|
@ -1,36 +1,37 @@
|
|||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { computed, ref, toRefs } from 'vue';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useVnConfirm } from 'composables/useVnConfirm';
|
||||
import { useRoute } from 'vue-router';
|
||||
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 { useQuasar } from 'quasar';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import useHasAccount from 'src/composables/useHasAccount.js';
|
||||
import VnInputPassword from 'src/components/common/VnInputPassword.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
hasAccount: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
entityId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const { hasAccount } = toRefs($props);
|
||||
const { openConfirmationModal } = useVnConfirm();
|
||||
const route = useRoute();
|
||||
const { notify } = useQuasar();
|
||||
const { notify } = useNotify();
|
||||
const account = computed(() => useArrayData('AccountId').store.data[0]);
|
||||
account.value.hasAccount = hasAccount.value;
|
||||
const entityId = computed(() => +route.params.id);
|
||||
|
||||
onMounted(async () => {
|
||||
account.value.hasAccount = await useHasAccount($props.entityId);
|
||||
});
|
||||
|
||||
async function updateStatusAccount(active) {
|
||||
if (active) {
|
||||
await axios.post(`Accounts`, { id: entityId.value });
|
||||
await axios.post(`Accounts`, { id: $props.entityId });
|
||||
} else {
|
||||
await axios.delete(`Accounts/${entityId.value}`);
|
||||
await axios.delete(`Accounts/${$props.entityId}`);
|
||||
}
|
||||
|
||||
account.value.hasAccount = active;
|
||||
|
@ -41,7 +42,7 @@ async function updateStatusAccount(active) {
|
|||
});
|
||||
}
|
||||
async function updateStatusUser(active) {
|
||||
await axios.patch(`VnUsers/${entityId.value}`, { active });
|
||||
await axios.patch(`VnUsers/${$props.entityId}`, { active });
|
||||
account.value.active = active;
|
||||
const status = active ? 'activate' : 'deactivate';
|
||||
notify({
|
||||
|
@ -97,7 +98,7 @@ async function sync() {
|
|||
<QTooltip>{{ t('account.card.actions.sync.tooltip') }}</QTooltip>
|
||||
</QIcon></QCheckbox
|
||||
>
|
||||
<QInput
|
||||
<VnInputPassword
|
||||
v-if="shouldSyncPassword"
|
||||
:label="t('login.password')"
|
||||
v-model="syncPassword"
|
||||
|
|
|
@ -9,6 +9,7 @@ import AccountMailAliasCreateForm from './AccountMailAliasCreateForm.vue';
|
|||
import { useVnConfirm } from 'composables/useVnConfirm';
|
||||
import { useArrayData } from 'composables/useArrayData';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import useHasAccount from 'src/composables/useHasAccount.js';
|
||||
import axios from 'axios';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
@ -50,16 +51,6 @@ const columns = computed(() => [
|
|||
},
|
||||
]);
|
||||
|
||||
const fetchAccountExistence = async () => {
|
||||
try {
|
||||
const { data } = await axios.get(`Accounts/${route.params.id}/exists`);
|
||||
return data.exists;
|
||||
} catch (error) {
|
||||
console.error('Error fetching account existence', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteMailAlias = async (row) => {
|
||||
await axios.delete(`${urlPath}/${row.id}`);
|
||||
fetchMailAliases();
|
||||
|
@ -79,7 +70,7 @@ const fetchMailAliases = async () => {
|
|||
|
||||
const getAccountData = async (reload = true) => {
|
||||
loading.value = true;
|
||||
hasAccount.value = await fetchAccountExistence();
|
||||
hasAccount.value = await useHasAccount(route.params.id);
|
||||
if (!hasAccount.value) {
|
||||
loading.value = false;
|
||||
store.data = [];
|
||||
|
|
|
@ -9,6 +9,7 @@ import VnRow from 'components/ui/VnRow.vue';
|
|||
import axios from 'axios';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import useHasAccount from 'src/composables/useHasAccount';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
@ -30,23 +31,9 @@ const hasDataChanged = computed(
|
|||
initialData.value.hasData !== hasData.value
|
||||
);
|
||||
|
||||
const fetchAccountExistence = async () => {
|
||||
try {
|
||||
const { data } = await axios.get(`Accounts/${route.params.id}/exists`);
|
||||
return data.exists;
|
||||
} catch (error) {
|
||||
console.error('Error fetching account existence', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const fetchMailForwards = async () => {
|
||||
try {
|
||||
const response = await axios.get(`MailForwards/${route.params.id}`);
|
||||
return response.data;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
const response = await axios.get(`MailForwards/${route.params.id}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const deleteMailForward = async () => {
|
||||
|
@ -72,7 +59,7 @@ const setInitialData = async () => {
|
|||
loading.value = true;
|
||||
initialData.value.account = route.params.id;
|
||||
formData.value.account = route.params.id;
|
||||
hasAccount.value = await fetchAccountExistence(route.params.id);
|
||||
hasAccount.value = await useHasAccount(route.params.id);
|
||||
if (!hasAccount.value) {
|
||||
loading.value = false;
|
||||
return;
|
||||
|
|
|
@ -7,6 +7,7 @@ import CardSummary from 'components/ui/CardSummary.vue';
|
|||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import AccountDescriptorMenu from './AccountDescriptorMenu.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
@ -31,12 +32,14 @@ const filter = {
|
|||
<template>
|
||||
<CardSummary
|
||||
data-key="AccountId"
|
||||
ref="AccountSummary"
|
||||
url="VnUsers/preview"
|
||||
:filter="filter"
|
||||
@on-fetch="(data) => (account = data)"
|
||||
>
|
||||
<template #header>{{ account.id }} - {{ account.nickname }}</template>
|
||||
<template #menu="">
|
||||
<AccountDescriptorMenu :entity-id="entityId" />
|
||||
</template>
|
||||
<template #body>
|
||||
<QCard class="vn-one">
|
||||
<QCardSection class="q-pa-none">
|
||||
|
|
|
@ -1,4 +1,18 @@
|
|||
account:
|
||||
params:
|
||||
id: Id
|
||||
name: Name
|
||||
roleFk: Role
|
||||
nickname: Nickname
|
||||
password: Password
|
||||
active: Active
|
||||
search: Id
|
||||
description: Description
|
||||
alias: Alias
|
||||
model: Model
|
||||
principalId: Role
|
||||
property: Property
|
||||
accessType: Access
|
||||
card:
|
||||
nickname: User
|
||||
role: Role
|
||||
|
|
|
@ -1,4 +1,20 @@
|
|||
accessType: Acceso
|
||||
property: Propiedad
|
||||
account:
|
||||
params:
|
||||
id: Id
|
||||
name: Nombre
|
||||
roleFk: Rol
|
||||
nickname: Nickname
|
||||
password: Contraseña
|
||||
active: Activo
|
||||
search: Id
|
||||
description: Descripción
|
||||
alias: Alias
|
||||
model: Modelo
|
||||
principalId: Rol
|
||||
property: Propiedad
|
||||
accessType: Acceso
|
||||
card:
|
||||
nickname: Usuario
|
||||
role: Rol
|
||||
|
|
|
@ -1,21 +1,13 @@
|
|||
<script setup>
|
||||
import VnCard from 'components/common/VnCard.vue';
|
||||
import VnCardBeta from 'components/common/VnCardBeta.vue';
|
||||
import ClaimDescriptor from './ClaimDescriptor.vue';
|
||||
import ClaimFilter from '../ClaimFilter.vue';
|
||||
import filter from './ClaimFilter.js';
|
||||
</script>
|
||||
<template>
|
||||
<VnCard
|
||||
<VnCardBeta
|
||||
data-key="Claim"
|
||||
base-url="Claims"
|
||||
:descriptor="ClaimDescriptor"
|
||||
:filter-panel="ClaimFilter"
|
||||
search-data-key="ClaimList"
|
||||
:filter="filter"
|
||||
:searchbar-props="{
|
||||
url: 'Claims/filter',
|
||||
label: 'Search claim',
|
||||
info: 'You can search by claim id or customer name',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -19,6 +19,7 @@ import ClaimNotes from 'src/pages/Claim/Card/ClaimNotes.vue';
|
|||
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
||||
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||
import ClaimDescriptorMenu from './ClaimDescriptorMenu.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
@ -228,6 +229,9 @@ function claimUrl(section) {
|
|||
</QList>
|
||||
</QBtnDropdown>
|
||||
</template>
|
||||
<template #menu="{ entity }">
|
||||
<ClaimDescriptorMenu :claim="entity.claim" />
|
||||
</template>
|
||||
<template #body="{ entity: { claim, salesClaimed, developments } }">
|
||||
<QCard class="vn-one" v-if="$route.name != 'ClaimSummary'">
|
||||
<VnTitle
|
||||
|
|
|
@ -93,16 +93,7 @@ defineExpose({ states });
|
|||
outlined
|
||||
rounded
|
||||
dense
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel> #{{ scope.opt?.id }} </QItemLabel>
|
||||
<QItemLabel caption>{{ scope.opt?.name }}</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
/>
|
||||
<VnSelect
|
||||
:label="t('claim.responsible')"
|
||||
v-model="params.claimResponsibleFk"
|
||||
|
|
|
@ -2,18 +2,18 @@
|
|||
import { ref, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { toDate } from 'filters/index';
|
||||
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||
import ClaimFilter from './ClaimFilter.vue';
|
||||
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||
import VnUserLink from 'src/components/ui/VnUserLink.vue';
|
||||
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';
|
||||
import VnSection from 'src/components/common/VnSection.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { viewSummary } = useSummaryDialog();
|
||||
const dataKey = 'ClaimList';
|
||||
|
||||
const claimFilterRef = ref();
|
||||
const columns = computed(() => [
|
||||
|
@ -125,48 +125,50 @@ const STATE_COLOR = {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<VnSearchbar
|
||||
data-key="ClaimList"
|
||||
:label="t('Search claim')"
|
||||
:info="t('You can search by claim id or customer name')"
|
||||
/>
|
||||
<RightMenu>
|
||||
<template #right-panel>
|
||||
<VnSection
|
||||
:data-key="dataKey"
|
||||
:columns="columns"
|
||||
prefix="claim"
|
||||
:array-data-props="{
|
||||
url: 'Claims/filter',
|
||||
order: ['cs.priority ASC', 'created ASC'],
|
||||
}"
|
||||
>
|
||||
<template #rightMenu>
|
||||
<ClaimFilter data-key="ClaimList" ref="claimFilterRef" />
|
||||
</template>
|
||||
</RightMenu>
|
||||
<VnTable
|
||||
data-key="ClaimList"
|
||||
url="Claims/filter"
|
||||
:order="['cs.priority ASC', 'created ASC']"
|
||||
:columns="columns"
|
||||
redirect="claim"
|
||||
:right-search="false"
|
||||
>
|
||||
<template #column-clientFk="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.clientName }}
|
||||
<CustomerDescriptorProxy :id="row.clientFk" />
|
||||
</span>
|
||||
<template #body>
|
||||
<VnTable
|
||||
:data-key="dataKey"
|
||||
:columns="columns"
|
||||
redirect="claim"
|
||||
:right-search="false"
|
||||
auto-load
|
||||
>
|
||||
<template #column-clientFk="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.clientName }}
|
||||
<CustomerDescriptorProxy :id="row.clientFk" />
|
||||
</span>
|
||||
</template>
|
||||
<template #column-attendedBy="{ row }">
|
||||
<span @click.stop>
|
||||
<VnUserLink :name="row.workerName" :worker-id="row.workerFk" />
|
||||
</span>
|
||||
</template>
|
||||
<template #column-zoneFk="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.zoneName }}
|
||||
<ZoneDescriptorProxy :id="row.zoneId" />
|
||||
</span>
|
||||
</template>
|
||||
</VnTable>
|
||||
</template>
|
||||
<template #column-attendedBy="{ row }">
|
||||
<span @click.stop>
|
||||
<VnUserLink :name="row.workerName" :worker-id="row.workerFk" />
|
||||
</span>
|
||||
</template>
|
||||
<template #column-zoneFk="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.zoneName }}
|
||||
<ZoneDescriptorProxy :id="row.zoneId" />
|
||||
</span>
|
||||
</template>
|
||||
</VnTable>
|
||||
</VnSection>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Search claim: Buscar reclamación
|
||||
You can search by claim id or customer name: Puedes buscar por id de la reclamación o nombre del cliente
|
||||
params:
|
||||
stateCode: Estado
|
||||
en:
|
||||
|
|
|
@ -44,3 +44,5 @@ claim:
|
|||
fileDescription: 'Claim id {claimId} from client {clientName} id {clientId}'
|
||||
noData: 'There are no images/videos, click here or drag and drop the file'
|
||||
dragDrop: Drag and drop it here
|
||||
search: Search claims
|
||||
searchInfo: You can search by claim id or customer name
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
Search claim: Buscar reclamación
|
||||
You can search by claim id or customer name: Puedes buscar por id de la reclamación o nombre del cliente
|
||||
claim:
|
||||
customer: Cliente
|
||||
code: Código
|
||||
|
@ -46,3 +44,5 @@ claim:
|
|||
fileDescription: 'ID de reclamación {claimId} del cliente {clientName} con ID {clientId}'
|
||||
noData: 'No hay imágenes/videos, haz clic aquí o arrastra y suelta el archivo'
|
||||
dragDrop: Arrastra y suelta aquí
|
||||
search: Buscar reclamación
|
||||
searchInfo: Puedes buscar por ID de la reclamación o nombre del cliente
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
@ -11,16 +11,9 @@ import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
|||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import VnUserLink from 'src/components/ui/VnUserLink.vue';
|
||||
import CustomerDescriptorMenu from './CustomerDescriptorMenu.vue';
|
||||
import { useState } from 'src/composables/useState';
|
||||
const state = useState();
|
||||
|
||||
const customer = ref();
|
||||
|
||||
onMounted(async () => {
|
||||
customer.value = state.get('customer');
|
||||
if (customer.value) customer.value.webAccess = data.value?.account?.isActive;
|
||||
});
|
||||
|
||||
const customerDebt = ref();
|
||||
const customerCredit = ref();
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
|
@ -42,10 +35,12 @@ const entityId = computed(() => {
|
|||
|
||||
const data = ref(useCardDescription());
|
||||
const setData = (entity) => {
|
||||
customerDebt.value = entity?.debt;
|
||||
customerCredit.value = entity?.credit;
|
||||
data.value = useCardDescription(entity?.name, entity?.id);
|
||||
};
|
||||
const debtWarning = computed(() => {
|
||||
return customer.value?.debt > customer.value?.credit ? 'negative' : 'primary';
|
||||
return customerDebt.value > customerCredit.value ? 'negative' : 'primary';
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -97,26 +92,21 @@ const debtWarning = computed(() => {
|
|||
:value="entity.businessType.description"
|
||||
/>
|
||||
</template>
|
||||
<template #icons>
|
||||
<QCardActions v-if="customer" class="q-gutter-x-md">
|
||||
<template #icons="{ entity }">
|
||||
<QCardActions class="q-gutter-x-md">
|
||||
<QIcon
|
||||
v-if="!customer.isActive"
|
||||
v-if="!entity.isActive"
|
||||
name="vn:disabled"
|
||||
size="xs"
|
||||
color="primary"
|
||||
>
|
||||
<QTooltip>{{ t('customer.card.isDisabled') }}</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon
|
||||
v-if="customer.isFreezed"
|
||||
name="vn:frozen"
|
||||
size="xs"
|
||||
color="primary"
|
||||
>
|
||||
<QIcon v-if="entity.isFreezed" name="vn:frozen" size="xs" color="primary">
|
||||
<QTooltip>{{ t('customer.card.isFrozen') }}</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon
|
||||
v-if="!customer.account?.active"
|
||||
v-if="!entity.account?.active"
|
||||
color="primary"
|
||||
name="vn:noweb"
|
||||
size="xs"
|
||||
|
@ -124,7 +114,7 @@ const debtWarning = computed(() => {
|
|||
<QTooltip>{{ t('customer.card.webAccountInactive') }}</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon
|
||||
v-if="customer.debt > customer.credit"
|
||||
v-if="entity.debt > entity.credit"
|
||||
name="vn:risk"
|
||||
size="xs"
|
||||
:color="debtWarning"
|
||||
|
@ -132,7 +122,7 @@ const debtWarning = computed(() => {
|
|||
<QTooltip>{{ t('customer.card.hasDebt') }}</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon
|
||||
v-if="!customer.isTaxDataChecked"
|
||||
v-if="!entity.isTaxDataChecked"
|
||||
name="vn:no036"
|
||||
size="xs"
|
||||
color="primary"
|
||||
|
@ -140,7 +130,7 @@ const debtWarning = computed(() => {
|
|||
<QTooltip>{{ t('customer.card.notChecked') }}</QTooltip>
|
||||
</QIcon>
|
||||
<QBtn
|
||||
v-if="customer.unpaid"
|
||||
v-if="entity.unpaid"
|
||||
flat
|
||||
size="sm"
|
||||
icon="vn:Client_unpaid"
|
||||
|
|
|
@ -12,6 +12,7 @@ import VnLinkMail from 'src/components/ui/VnLinkMail.vue';
|
|||
import CustomerSummaryTable from 'src/pages/Customer/components/CustomerSummaryTable.vue';
|
||||
import VnTitle from 'src/components/common/VnTitle.vue';
|
||||
import VnRow from 'src/components/ui/VnRow.vue';
|
||||
import CustomerDescriptorMenu from './CustomerDescriptorMenu.vue';
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const grafanaUrl = 'https://grafana.verdnatura.es';
|
||||
|
@ -70,6 +71,9 @@ const sumRisk = ({ clientRisks }) => {
|
|||
data-key="CustomerSummary"
|
||||
module-name="Customer"
|
||||
>
|
||||
<template #menu="{ entity }">
|
||||
<CustomerDescriptorMenu :customer="entity" />
|
||||
</template>
|
||||
<template #body="{ entity }">
|
||||
<QCard class="vn-one">
|
||||
<VnTitle
|
||||
|
@ -94,14 +98,13 @@ const sumRisk = ({ clientRisks }) => {
|
|||
:phone-number="entity.mobile"
|
||||
:channel="entity.country?.saySimpleCountry?.channel"
|
||||
class="q-ml-xs"
|
||||
:country="entity.country?.code"
|
||||
/>
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv :value="entity.email" copy
|
||||
><template #label>
|
||||
{{ t('globals.params.email') }}
|
||||
<VnLinkMail :email="entity.email"></VnLinkMail> </template
|
||||
<VnLinkMail email="entity.email"></VnLinkMail> </template
|
||||
></VnLv>
|
||||
<VnLv
|
||||
:label="t('customer.summary.salesPerson')"
|
||||
|
@ -173,7 +176,7 @@ const sumRisk = ({ clientRisks }) => {
|
|||
:label="t('customer.summary.notifyByEmail')"
|
||||
:value="entity.isToBeMailed"
|
||||
/>
|
||||
<VnLv :label="t('globals.isVies')" :value="entity.isVies" />
|
||||
<VnLv :label="t('customer.summary.vies')" :value="entity.isVies" />
|
||||
</VnRow>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
|
|
|
@ -247,8 +247,16 @@ function handleLocation(data, location) {
|
|||
: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"
|
||||
/>
|
||||
<VnInputNumber :label="t('Latitude')" clearable v-model="data.latitude" />
|
||||
</VnRow>
|
||||
<h4 class="q-mb-xs">{{ t('Notes') }}</h4>
|
||||
<VnRow
|
||||
|
|
|
@ -94,3 +94,24 @@ customer:
|
|||
hasToInvoiceByAddress: Invoice by address
|
||||
isToBeMailed: Mailing
|
||||
hasSepaVnl: VNL B2B received
|
||||
params:
|
||||
id: Id
|
||||
isWorker: Is Worker
|
||||
payMethod: Payment Method
|
||||
workerFk: Author
|
||||
observation: Last Observation
|
||||
created: Last Update Date
|
||||
creditInsurance: Credit Insurance
|
||||
defaulterSinced: Defaulted Since
|
||||
hasRecovery: Has Recovery
|
||||
socialName: Social name
|
||||
city: City
|
||||
phone: Phone
|
||||
postcode: Postcode
|
||||
campaign: Campaign
|
||||
grouped: Grouped
|
||||
search: Contains
|
||||
itemId: Item Id
|
||||
ticketFk: Ticket Id
|
||||
description: Description
|
||||
quantity: Quantity
|
||||
|
|
|
@ -96,3 +96,24 @@ customer:
|
|||
hasToInvoiceByAddress: Factura por consigna
|
||||
isToBeMailed: Env. emails
|
||||
hasSepaVnl: Recibido B2B VNL
|
||||
params:
|
||||
id: ID
|
||||
isWorker: Es trabajador
|
||||
payMethod: F. Pago
|
||||
workerFk: Autor
|
||||
observation: Última observación
|
||||
created: Fecha Ú. O.
|
||||
creditInsurance: Crédito A.
|
||||
defaulterSinced: Desde
|
||||
hasRecovery: Tiene recobro
|
||||
socialName: Razón social
|
||||
campaign: Campaña
|
||||
city: Ciudad
|
||||
phone: Teléfono
|
||||
postcode: Código postal
|
||||
grouped: Agrupado
|
||||
search: Contiene
|
||||
itemId: Id Artículo
|
||||
ticketFk: Id Ticket
|
||||
description: Descripción
|
||||
quantity: Cantidad
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<script setup>
|
||||
import VnCard from 'components/common/VnCard.vue';
|
||||
import VnCardBeta from 'components/common/VnCardBeta.vue';
|
||||
import DepartmentDescriptor from 'pages/Department/Card/DepartmentDescriptor.vue';
|
||||
</script>
|
||||
<template>
|
||||
<VnCard
|
||||
<VnCardBeta
|
||||
class="q-pa-md column items-center"
|
||||
v-bind="{ ...$attrs }"
|
||||
data-key="Department"
|
||||
|
|
|
@ -83,7 +83,11 @@ const { openConfirmationModal } = useVnConfirm();
|
|||
</template>
|
||||
<template #body="{ entity }">
|
||||
<VnLv :label="t('department.chat')" :value="entity.chatName" />
|
||||
<VnLv :label="t('globals.email')" :value="entity.notificationEmail" copy />
|
||||
<VnLv
|
||||
:label="t('globals.params.email')"
|
||||
:value="entity.notificationEmail"
|
||||
copy
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('department.selfConsumptionCustomer')"
|
||||
:value="entity.client?.name"
|
||||
|
|
|
@ -40,7 +40,7 @@ onMounted(async () => {
|
|||
<template #body="{ entity: department }">
|
||||
<QCard class="column">
|
||||
<VnTitle
|
||||
:url="`#/department/department/${entityId}/basic-data`"
|
||||
:url="`#/worker/department/${entityId}/basic-data`"
|
||||
:text="t('Basic data')"
|
||||
/>
|
||||
<div class="full-width row wrap justify-between content-between">
|
||||
|
@ -58,7 +58,7 @@ onMounted(async () => {
|
|||
dash
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('globals.email')"
|
||||
:label="t('globals.params.email')"
|
||||
:value="department.notificationEmail"
|
||||
dash
|
||||
/>
|
||||
|
|
|
@ -7,9 +7,9 @@ import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
|||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
|
||||
import { toDate } from 'src/filters';
|
||||
import { usePrintService } from 'composables/usePrintService';
|
||||
import { getUrl } from 'src/composables/getUrl';
|
||||
import filter from './EntryFilter.js';
|
||||
import EntryDescriptorMenu from './EntryDescriptorMenu.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
|
@ -21,7 +21,6 @@ const $props = defineProps({
|
|||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const { openReport } = usePrintService();
|
||||
const entryDescriptorRef = ref(null);
|
||||
const url = ref();
|
||||
|
||||
|
@ -52,10 +51,6 @@ const getEntryRedirectionFilter = (entry) => {
|
|||
to,
|
||||
});
|
||||
};
|
||||
|
||||
const showEntryReport = () => {
|
||||
openReport(`Entries/${route.params.id}/entry-order-pdf`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -68,14 +63,12 @@ const showEntryReport = () => {
|
|||
data-key="Entry"
|
||||
>
|
||||
<template #menu="{ entity }">
|
||||
<QItem v-ripple clickable @click="showEntryReport(entity)">
|
||||
<QItemSection>{{ t('Show entry report') }}</QItemSection>
|
||||
</QItem>
|
||||
<EntryDescriptorMenu :id="entity.id" />
|
||||
</template>
|
||||
<template #body="{ entity }">
|
||||
<VnLv :label="t('globals.agency')" :value="entity.travel?.agency?.name" />
|
||||
<VnLv :label="t('globals.shipped')" :value="toDate(entity.travel?.shipped)" />
|
||||
<VnLv :label="t('globals.landed')" :value="toDate(entity.travel?.landed)" />
|
||||
<VnLv :label="t('shipped')" :value="toDate(entity.travel?.shipped)" />
|
||||
<VnLv :label="t('landed')" :value="toDate(entity.travel?.landed)" />
|
||||
<VnLv
|
||||
:label="t('globals.warehouseOut')"
|
||||
:value="entity.travel?.warehouseOut?.name"
|
||||
|
@ -154,7 +147,6 @@ es:
|
|||
Supplier card: Ficha del proveedor
|
||||
All travels with current agency: Todos los envíos con la agencia actual
|
||||
All entries with current supplier: Todas las entradas con el proveedor actual
|
||||
Show entry report: Ver informe del pedido
|
||||
Go to module index: Ir al índice del modulo
|
||||
Inventory entry: Es inventario
|
||||
Virtual entry: Es una redada
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<script setup>
|
||||
import { usePrintService } from 'composables/usePrintService';
|
||||
|
||||
const { openReport } = usePrintService();
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
function showEntryReport() {
|
||||
openReport(`Entries/${$props.id}/entry-order-pdf`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QItem v-ripple clickable @click="showEntryReport">
|
||||
<QItemSection>{{ $t('entryList.list.showEntryReport') }}</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
|
@ -12,6 +12,9 @@ import { getUrl } from 'src/composables/getUrl';
|
|||
import axios from 'axios';
|
||||
import FetchedTags from 'src/components/ui/FetchedTags.vue';
|
||||
import VnToSummary from 'src/components/ui/VnToSummary.vue';
|
||||
import EntryDescriptorMenu from './EntryDescriptorMenu.vue';
|
||||
import VnRow from 'src/components/ui/VnRow.vue';
|
||||
import VnTitle from 'src/components/common/VnTitle.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
@ -147,9 +150,8 @@ async function setEntryData(data) {
|
|||
}
|
||||
|
||||
const fetchEntryBuys = async () => {
|
||||
const { data } = await axios.get(`Entries/${entry.value.id}/getBuys`);
|
||||
if (data) entryBuys.value = data;
|
||||
|
||||
const { data } = await axios.get(`Entries/${entry.value.id}/getBuys`);
|
||||
if (data) entryBuys.value = data;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -171,15 +173,15 @@ const fetchEntryBuys = async () => {
|
|||
<template #header>
|
||||
<span>{{ entry.id }} - {{ entry.supplier.nickname }}</span>
|
||||
</template>
|
||||
<template #menu="{ entity }">
|
||||
<EntryDescriptorMenu :id="entity.id" />
|
||||
</template>
|
||||
<template #body>
|
||||
<QCard class="vn-one">
|
||||
<router-link
|
||||
:to="{ name: 'EntryBasicData', params: { id: entityId } }"
|
||||
class="header header-link"
|
||||
>
|
||||
{{ t('globals.summary.basicData') }}
|
||||
<QIcon name="open_in_new" />
|
||||
</router-link>
|
||||
<VnTitle
|
||||
:url="`#/entry/${entityId}/basic-data`"
|
||||
:text="t('globals.summary.basicData')"
|
||||
/>
|
||||
<VnLv :label="t('entry.summary.commission')" :value="entry.commission" />
|
||||
<VnLv
|
||||
:label="t('entry.summary.currency')"
|
||||
|
@ -193,13 +195,10 @@ const fetchEntryBuys = async () => {
|
|||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<router-link
|
||||
:to="{ name: 'EntryBasicData', params: { id: entityId } }"
|
||||
class="header header-link"
|
||||
>
|
||||
{{ t('globals.summary.basicData') }}
|
||||
<QIcon name="open_in_new" />
|
||||
</router-link>
|
||||
<VnTitle
|
||||
:url="`#/entry/${entityId}/basic-data`"
|
||||
:text="t('globals.summary.basicData')"
|
||||
/>
|
||||
<VnLv :label="t('entry.summary.travelReference')">
|
||||
<template #value>
|
||||
<span class="link">
|
||||
|
@ -212,61 +211,45 @@ const fetchEntryBuys = async () => {
|
|||
:label="t('entry.summary.travelAgency')"
|
||||
:value="entry.travel.agency?.name"
|
||||
/>
|
||||
<VnLv :label="t('globals.shipped')" :value="toDate(entry.travel.shipped)" />
|
||||
<VnLv
|
||||
:label="t('globals.shipped')"
|
||||
:value="toDate(entry.travel.shipped)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('globals.warehouseOut')"
|
||||
:value="entry.travel.warehouseOut?.name"
|
||||
/>
|
||||
<QCheckbox
|
||||
<VnLv
|
||||
:label="t('entry.summary.travelDelivered')"
|
||||
v-model="entry.travel.isDelivered"
|
||||
:disable="true"
|
||||
:value="entry.travel.isDelivered"
|
||||
/>
|
||||
<VnLv :label="t('globals.landed')" :value="toDate(entry.travel.landed)" />
|
||||
<VnLv
|
||||
:label="t('globals.warehouseIn')"
|
||||
:value="entry.travel.warehouseIn?.name"
|
||||
/>
|
||||
<QCheckbox
|
||||
<VnLv
|
||||
:label="t('entry.summary.travelReceived')"
|
||||
v-model="entry.travel.isReceived"
|
||||
:disable="true"
|
||||
:value="entry.travel.isReceived"
|
||||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<router-link
|
||||
:to="{ name: 'TravelSummary', params: { id: entry.travel.id } }"
|
||||
class="header header-link"
|
||||
>
|
||||
{{ t('Travel data') }}
|
||||
<QIcon name="open_in_new" />
|
||||
</router-link>
|
||||
<QCheckbox
|
||||
:label="t('entry.summary.ordered')"
|
||||
v-model="entry.isOrdered"
|
||||
:disable="true"
|
||||
/>
|
||||
<QCheckbox
|
||||
:label="t('globals.confirmed')"
|
||||
v-model="entry.isConfirmed"
|
||||
:disable="true"
|
||||
/>
|
||||
<QCheckbox
|
||||
:label="t('entry.summary.booked')"
|
||||
v-model="entry.isBooked"
|
||||
:disable="true"
|
||||
/>
|
||||
<QCheckbox
|
||||
:label="t('entry.summary.excludedFromAvailable')"
|
||||
v-model="entry.isExcludedFromAvailable"
|
||||
:disable="true"
|
||||
/>
|
||||
<VnTitle :url="`#/travel/${entityId}/summary`" :text="t('Travel data')" />
|
||||
<VnRow class="block">
|
||||
<VnLv :label="t('entry.summary.ordered')" :value="entry.isOrdered" />
|
||||
<VnLv :label="t('globals.confirmed')" :value="entry.isConfirmed" />
|
||||
<VnLv :label="t('entry.summary.booked')" :value="entry.isBooked" />
|
||||
<VnLv
|
||||
:label="t('entry.summary.excludedFromAvailable')"
|
||||
:value="entry.isExcludedFromAvailable"
|
||||
/>
|
||||
</VnRow>
|
||||
</QCard>
|
||||
<QCard class="vn-two" style="min-width: 100%">
|
||||
<a class="header header-link">
|
||||
{{ t('entry.summary.buys') }}
|
||||
<QIcon name="open_in_new" />
|
||||
</a>
|
||||
<QCard class="vn-max">
|
||||
<VnTitle
|
||||
:url="`#/entry/${entityId}/buys`"
|
||||
:text="t('entry.summary.buys')"
|
||||
/>
|
||||
<QTable
|
||||
:rows="entryBuys"
|
||||
:columns="entriesTableColumns"
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { QBtn } from 'quasar';
|
||||
|
||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||
import { usePrintService } from 'composables/usePrintService';
|
||||
const { openReport } = usePrintService();
|
||||
|
||||
const { openReport } = usePrintService();
|
||||
const buyRows = ref([]);
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
const entityId = computed(() => $props.id || route.params.id);
|
||||
|
||||
const entriesTableColumns = computed(() => [
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -63,34 +63,39 @@ const entriesTableColumns = computed(() => [
|
|||
field: 'grouping',
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
function downloadCSV(rows) {
|
||||
const headers = ['id', 'itemFk', 'name', 'stickers', 'packing', 'comment'];
|
||||
|
||||
const csvRows = rows.map((row) => {
|
||||
const buy = row;
|
||||
const item = buy.item || {};
|
||||
|
||||
return [
|
||||
buy.id,
|
||||
buy.itemFk,
|
||||
item.name || '',
|
||||
buy.stickers,
|
||||
buy.packing,
|
||||
item.comment || '',
|
||||
].join(',');
|
||||
});
|
||||
|
||||
const csvContent = [headers.join(','), ...csvRows].join('\n');
|
||||
|
||||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.setAttribute('download', `${entityId.value}data.csv`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<QDialog ref="dialogRef">
|
||||
<QCard style="min-width: 800px">
|
||||
<QCardSection class="row items-center q-pb-none">
|
||||
<QAvatar
|
||||
:icon="icon"
|
||||
color="primary"
|
||||
text-color="white"
|
||||
size="xl"
|
||||
v-if="icon"
|
||||
/>
|
||||
<span class="text-h6 text-grey">{{ title }}</span>
|
||||
<QSpace />
|
||||
<QBtn icon="close" :disable="isLoading" flat round dense v-close-popup />
|
||||
</QCardSection>
|
||||
<QCardActions align="right">
|
||||
<QBtn
|
||||
:label="t('myEntries.printLabels')"
|
||||
color="primary"
|
||||
icon="print"
|
||||
:loading="isLoading"
|
||||
@click="openReport(`Entries/${entityId}/labelSupplier`)"
|
||||
unelevated
|
||||
autofocus
|
||||
/>
|
||||
</QCardActions>
|
||||
<QCardSection class="row items-center">
|
||||
<VnPaginate
|
||||
ref="entryBuysPaginateRef"
|
||||
|
@ -101,6 +106,7 @@ const entriesTableColumns = computed(() => [
|
|||
>
|
||||
<template #body="{ rows }">
|
||||
<QTable
|
||||
ref="buyRows"
|
||||
:rows="rows"
|
||||
:columns="entriesTableColumns"
|
||||
row-key="id"
|
||||
|
@ -110,6 +116,26 @@ const entriesTableColumns = computed(() => [
|
|||
:grid="$q.screen.lt.md"
|
||||
:no-data-label="t('globals.noResults')"
|
||||
>
|
||||
<template #top-left>
|
||||
<QBtn
|
||||
:label="t('myEntries.downloadCsv')"
|
||||
color="primary"
|
||||
icon="csv"
|
||||
@click="downloadCSV(rows)"
|
||||
unelevated
|
||||
/>
|
||||
</template>
|
||||
<template #top-right>
|
||||
<QBtn
|
||||
class="q-mr-lg"
|
||||
:label="t('myEntries.printLabels')"
|
||||
color="primary"
|
||||
icon="print"
|
||||
@click="
|
||||
openReport(`Entries/${entityId}/labelSupplier`)
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template #body="props">
|
||||
<QTr>
|
||||
<QTd v-for="col in props.cols" :key="col.name">
|
||||
|
@ -118,7 +144,6 @@ const entriesTableColumns = computed(() => [
|
|||
<QBtn
|
||||
icon="visibility"
|
||||
v-if="props.row.stickers > 0"
|
||||
:loading="isLoading"
|
||||
@click="
|
||||
openReport(
|
||||
`Entries/${props.row.id}/buy-label-supplier`
|
||||
|
|
|
@ -123,6 +123,7 @@ const companiesOptions = ref([]);
|
|||
option-value="id"
|
||||
option-label="name"
|
||||
:fields="['id', 'name', 'nickname']"
|
||||
:filter-options="['id', 'name', 'nickname']"
|
||||
sort-by="nickname"
|
||||
hide-selected
|
||||
dense
|
||||
|
@ -132,9 +133,12 @@ const companiesOptions = ref([]);
|
|||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
scope.opt?.name + ': ' + scope.opt?.nickname
|
||||
}}</QItemLabel>
|
||||
<QItemLabel>
|
||||
{{ scope.opt?.name}}
|
||||
</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ `#${scope.opt?.id } , ${ scope.opt?.nickname}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
|
|
|
@ -69,12 +69,14 @@ const tagValues = ref([]);
|
|||
use-input
|
||||
@update:model-value="searchFn()"
|
||||
>
|
||||
<template #option="{ itemProps, opt }">
|
||||
<QItem v-bind="itemProps">
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ opt.name }}</QItemLabel>
|
||||
<QItemLabel>
|
||||
{{ scope.opt?.name}}
|
||||
</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ opt.nickname }}
|
||||
{{ `#${scope.opt?.id } , ${ scope.opt?.nickname}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
<script setup>
|
||||
import { onMounted, ref, computed } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import EntryFilter from './EntryFilter.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import VnTable from 'components/VnTable/VnTable.vue';
|
||||
import RightMenu from 'src/components/common/RightMenu.vue';
|
||||
import { toDate } from 'src/filters';
|
||||
|
@ -12,7 +11,6 @@ import EntrySummary from './Card/EntrySummary.vue';
|
|||
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||
import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
const tableRef = ref();
|
||||
|
||||
|
@ -245,7 +243,6 @@ const columns = computed(() => [
|
|||
|
||||
<i18n>
|
||||
es:
|
||||
Inventory entry: Es inventario
|
||||
Virtual entry: Es una redada
|
||||
Search entries: Buscar entradas
|
||||
You can search by entry reference: Puedes buscar por referencia de la entrada
|
||||
|
|
|
@ -102,7 +102,7 @@ const columns = computed(() => [
|
|||
actions: [
|
||||
{
|
||||
title: t('myEntries.printLabels'),
|
||||
icon: 'print',
|
||||
icon: 'move_item',
|
||||
isPrimary: true,
|
||||
action: (row) => printBuys(row.id),
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
entryList:
|
||||
list:
|
||||
inventoryEntry: Inventory entry
|
||||
showEntryReport: Show entry report
|
||||
entryFilter:
|
||||
filter:
|
||||
search: General search
|
||||
|
@ -17,5 +18,6 @@ myEntries:
|
|||
warehouseInFk: Warehouse in
|
||||
daysOnward: Days onward
|
||||
daysAgo: Days ago
|
||||
downloadCsv: Download CSV
|
||||
wasteRecalc:
|
||||
recalcOk: The wastes were successfully recalculated
|
||||
|
|
|
@ -4,6 +4,7 @@ You can search by entry reference: Puedes buscar por referencia de la entrada
|
|||
entryList:
|
||||
list:
|
||||
inventoryEntry: Es inventario
|
||||
showEntryReport: Ver informe del pedido
|
||||
entryFilter:
|
||||
filter:
|
||||
search: Búsqueda general
|
||||
|
@ -20,5 +21,6 @@ myEntries:
|
|||
warehouseInFk: Alm. entrada
|
||||
daysOnward: Días adelante
|
||||
daysAgo: Días atras
|
||||
downloadCsv: Descargar CSV
|
||||
wasteRecalc:
|
||||
recalcOk: Se han recalculado las mermas correctamente
|
||||
|
|
|
@ -268,7 +268,7 @@ function deleteFile(dmsFk) {
|
|||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
:label="t('InvoiceIn.summary.sage')"
|
||||
:label="t('invoicein.summary.sage')"
|
||||
v-model="data.withholdingSageFk"
|
||||
:options="sageWithholdings"
|
||||
option-value="id"
|
||||
|
|
|
@ -1,60 +1,30 @@
|
|||
<script setup>
|
||||
import { ref, reactive, computed, onBeforeMount, capitalize } from 'vue';
|
||||
import { ref, reactive, computed, onBeforeMount } from 'vue';
|
||||
import { useRouter, onBeforeRouteUpdate } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
import axios from 'axios';
|
||||
import { toCurrency, toDate } from 'src/filters';
|
||||
import { useAcl } from 'src/composables/useAcl';
|
||||
import { downloadFile } from 'src/composables/downloadFile';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import { usePrintService } from 'composables/usePrintService';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import SendEmailDialog from 'components/common/SendEmailDialog.vue';
|
||||
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import { useCapitalize } from 'src/composables/useCapitalize';
|
||||
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||
import InvoiceInToBook from '../InvoiceInToBook.vue';
|
||||
import InvoiceInDescriptorMenu from './InvoiceInDescriptorMenu.vue';
|
||||
|
||||
const $props = defineProps({ id: { type: Number, default: null } });
|
||||
|
||||
const { push, currentRoute } = useRouter();
|
||||
|
||||
const quasar = useQuasar();
|
||||
const { hasAny } = useAcl();
|
||||
const { t } = useI18n();
|
||||
const { openReport, sendEmail } = usePrintService();
|
||||
const arrayData = useArrayData();
|
||||
|
||||
const invoiceIn = computed(() => arrayData.store.data);
|
||||
const cardDescriptorRef = ref();
|
||||
const correctionDialogRef = ref();
|
||||
const entityId = computed(() => $props.id || +currentRoute.value.params.id);
|
||||
const totalAmount = ref();
|
||||
const currentAction = ref();
|
||||
const config = ref();
|
||||
const cplusRectificationTypes = ref([]);
|
||||
const siiTypeInvoiceIns = ref([]);
|
||||
const invoiceCorrectionTypes = ref([]);
|
||||
const actions = {
|
||||
unbook: {
|
||||
title: t('assertAction', { action: t('unbook') }),
|
||||
action: toUnbook,
|
||||
},
|
||||
delete: {
|
||||
title: t('assertAction', { action: t('delete') }),
|
||||
action: deleteInvoice,
|
||||
},
|
||||
clone: {
|
||||
title: t('assertAction', { action: t('clone') }),
|
||||
action: cloneInvoice,
|
||||
},
|
||||
showPdf: { cb: showPdfInvoice },
|
||||
sendPdf: { cb: sendPdfInvoiceConfirmation },
|
||||
correct: { cb: () => correctionDialogRef.value.show() },
|
||||
};
|
||||
|
||||
const filter = {
|
||||
include: [
|
||||
{
|
||||
|
@ -90,7 +60,7 @@ const routes = reactive({
|
|||
return {
|
||||
name: 'InvoiceInList',
|
||||
query: {
|
||||
table: JSON.stringify({ supplierFk: id }),
|
||||
params: JSON.stringify({ supplierFk: id }),
|
||||
},
|
||||
};
|
||||
},
|
||||
|
@ -99,7 +69,7 @@ const routes = reactive({
|
|||
return {
|
||||
name: 'InvoiceInList',
|
||||
query: {
|
||||
table: JSON.stringify({ correctedFk: entityId.value }),
|
||||
params: JSON.stringify({ correctedFk: entityId.value }),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -118,21 +88,21 @@ const routes = reactive({
|
|||
const correctionFormData = reactive({
|
||||
invoiceReason: 2,
|
||||
invoiceType: 2,
|
||||
invoiceClass: 8,
|
||||
invoiceClass: 6,
|
||||
});
|
||||
const isNotFilled = computed(() => Object.values(correctionFormData).includes(null));
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await setInvoiceCorrection(entityId.value);
|
||||
const { data } = await axios.get(`InvoiceIns/${entityId.value}/getTotals`);
|
||||
totalAmount.value = data.totalTaxableBase;
|
||||
totalAmount.value = data.totalDueDay;
|
||||
});
|
||||
|
||||
onBeforeRouteUpdate(async (to, from) => {
|
||||
if (to.params.id !== from.params.id) {
|
||||
await setInvoiceCorrection(to.params.id);
|
||||
const { data } = await axios.get(`InvoiceIns/${to.params.id}/getTotals`);
|
||||
totalAmount.value = data.totalTaxableBase;
|
||||
totalAmount.value = data.totalDueDay;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -153,94 +123,6 @@ async function setInvoiceCorrection(id) {
|
|||
);
|
||||
}
|
||||
|
||||
function openDialog() {
|
||||
quasar.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t(currentAction.value.title),
|
||||
promise: currentAction.value.action,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function toUnbook() {
|
||||
const { data } = await axios.post(`InvoiceIns/${entityId.value}/toUnbook`);
|
||||
const { isLinked, bookEntry, accountingEntries } = data;
|
||||
|
||||
const type = isLinked ? 'warning' : 'positive';
|
||||
const message = isLinked
|
||||
? t('isLinked', { bookEntry, accountingEntries })
|
||||
: t('isNotLinked', { bookEntry });
|
||||
|
||||
quasar.notify({ type, message });
|
||||
if (!isLinked) arrayData.store.data.isBooked = false;
|
||||
}
|
||||
|
||||
async function deleteInvoice() {
|
||||
await axios.delete(`InvoiceIns/${entityId.value}`);
|
||||
quasar.notify({
|
||||
type: 'positive',
|
||||
message: t('Invoice deleted'),
|
||||
});
|
||||
push({ path: '/invoice-in' });
|
||||
}
|
||||
|
||||
async function cloneInvoice() {
|
||||
const { data } = await axios.post(`InvoiceIns/${entityId.value}/clone`);
|
||||
quasar.notify({
|
||||
type: 'positive',
|
||||
message: t('Invoice cloned'),
|
||||
});
|
||||
push({ path: `/invoice-in/${data.id}/summary` });
|
||||
}
|
||||
|
||||
const canEditProp = (props) =>
|
||||
hasAny([{ model: 'InvoiceIn', props, accessType: 'WRITE' }]);
|
||||
|
||||
const isAgricultural = () => {
|
||||
if (!config.value) return false;
|
||||
return (
|
||||
invoiceIn.value?.supplier?.sageFarmerWithholdingFk ===
|
||||
config?.value[0]?.sageWithholdingFk
|
||||
);
|
||||
};
|
||||
|
||||
function showPdfInvoice() {
|
||||
if (isAgricultural())
|
||||
openReport(`InvoiceIns/${entityId.value}/invoice-in-pdf`, null, '_blank');
|
||||
}
|
||||
|
||||
function sendPdfInvoiceConfirmation() {
|
||||
quasar.dialog({
|
||||
component: SendEmailDialog,
|
||||
componentProps: {
|
||||
data: {
|
||||
address: invoiceIn.value.supplier.contacts[0].email,
|
||||
},
|
||||
promise: sendPdfInvoice,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function sendPdfInvoice({ address }) {
|
||||
if (!address)
|
||||
quasar.notify({
|
||||
type: 'negative',
|
||||
message: t(`The email can't be empty`),
|
||||
});
|
||||
else
|
||||
return sendEmail(`InvoiceIns/${entityId.value}/invoice-in-email`, {
|
||||
recipientId: invoiceIn.value.supplier.id,
|
||||
recipient: address,
|
||||
});
|
||||
}
|
||||
|
||||
function triggerMenu(type) {
|
||||
currentAction.value = actions[type];
|
||||
if (currentAction.value.cb) currentAction.value.cb();
|
||||
else openDialog(type);
|
||||
}
|
||||
|
||||
const createInvoiceInCorrection = async () => {
|
||||
const { data: correctingId } = await axios.post(
|
||||
'InvoiceIns/corrective',
|
||||
|
@ -262,7 +144,7 @@ const createInvoiceInCorrection = async () => {
|
|||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
url="siiTypeInvoiceIns"
|
||||
url="SiiTypeInvoiceIns"
|
||||
:where="{ code: { like: 'R%' } }"
|
||||
@on-fetch="(data) => (siiTypeInvoiceIns = data)"
|
||||
auto-load
|
||||
|
@ -281,87 +163,13 @@ const createInvoiceInCorrection = async () => {
|
|||
title="supplierRef"
|
||||
>
|
||||
<template #menu="{ entity }">
|
||||
<InvoiceInToBook>
|
||||
<template #content="{ book }">
|
||||
<QItem
|
||||
v-if="!entity?.isBooked && canEditProp('toBook')"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="book(entityId)"
|
||||
>
|
||||
<QItemSection>{{ t('To book') }}</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</InvoiceInToBook>
|
||||
<QItem
|
||||
v-if="entity?.isBooked && canEditProp('toUnbook')"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="triggerMenu('unbook')"
|
||||
>
|
||||
<QItemSection>
|
||||
{{ t('To unbook') }}
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-if="canEditProp('deleteById')"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="triggerMenu('delete')"
|
||||
>
|
||||
<QItemSection>{{ t('Delete invoice') }}</QItemSection>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-if="canEditProp('clone')"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="triggerMenu('clone')"
|
||||
>
|
||||
<QItemSection>{{ t('Clone invoice') }}</QItemSection>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-if="isAgricultural()"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="triggerMenu('showPdf')"
|
||||
>
|
||||
<QItemSection>{{ t('Show agricultural receipt as PDF') }}</QItemSection>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-if="isAgricultural()"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="triggerMenu('sendPdf')"
|
||||
>
|
||||
<QItemSection
|
||||
>{{ t('Send agricultural receipt as PDF') }}...</QItemSection
|
||||
>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-if="!invoiceInCorrection.corrected"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="triggerMenu('correct')"
|
||||
>
|
||||
<QItemSection>{{ t('Create rectificative invoice') }}...</QItemSection>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-if="entity.dmsFk"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="downloadFile(entity.dmsFk)"
|
||||
>
|
||||
<QItemSection>{{ t('components.smartCard.downloadFile') }}</QItemSection>
|
||||
</QItem>
|
||||
<InvoiceInDescriptorMenu :invoice="entity" />
|
||||
</template>
|
||||
<template #body="{ entity }">
|
||||
<VnLv :label="t('InvoiceIn.list.issued')" :value="toDate(entity.issued)" />
|
||||
<VnLv
|
||||
:label="t('InvoiceIn.summary.bookedDate')"
|
||||
:value="toDate(entity.booked)"
|
||||
/>
|
||||
<VnLv :label="t('InvoiceIn.list.amount')" :value="toCurrency(totalAmount)" />
|
||||
<VnLv :label="t('InvoiceIn.list.supplier')">
|
||||
<VnLv :label="t('invoicein.list.issued')" :value="toDate(entity.issued)" />
|
||||
<VnLv :label="t('invoicein.summary.bookedDate')" :value="toDate(entity.booked)" />
|
||||
<VnLv :label="t('invoicein.list.amount')" :value="toCurrency(totalAmount)" />
|
||||
<VnLv :label="t('invoicein.list.supplier')">
|
||||
<template #value>
|
||||
<span class="link">
|
||||
{{ entity?.supplier?.nickname }}
|
||||
|
@ -378,7 +186,7 @@ const createInvoiceInCorrection = async () => {
|
|||
color="primary"
|
||||
:to="routes.getSupplier(entity.supplierFk)"
|
||||
>
|
||||
<QTooltip>{{ t('InvoiceIn.list.supplier') }}</QTooltip>
|
||||
<QTooltip>{{ t('invoicein.list.supplier') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
size="md"
|
||||
|
@ -394,7 +202,7 @@ const createInvoiceInCorrection = async () => {
|
|||
color="primary"
|
||||
:to="routes.getTickets(entity.supplierFk)"
|
||||
>
|
||||
<QTooltip>{{ t('InvoiceIn.descriptor.ticketList') }}</QTooltip>
|
||||
<QTooltip>{{ t('InvoiceOut.card.ticketList') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
v-if="
|
||||
|
@ -438,7 +246,7 @@ const createInvoiceInCorrection = async () => {
|
|||
readonly
|
||||
/>
|
||||
<VnSelect
|
||||
:label="`${capitalize(t('globals.class'))}`"
|
||||
:label="`${useCapitalize(t('globals.class'))}`"
|
||||
v-model="correctionFormData.invoiceClass"
|
||||
:options="siiTypeInvoiceIns"
|
||||
option-value="id"
|
||||
|
@ -448,27 +256,15 @@ const createInvoiceInCorrection = async () => {
|
|||
</QItemSection>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
:label="`${capitalize(t('globals.type'))}`"
|
||||
:label="`${useCapitalize(t('globals.type'))}`"
|
||||
v-model="correctionFormData.invoiceType"
|
||||
:options="cplusRectificationTypes"
|
||||
option-value="id"
|
||||
option-label="description"
|
||||
:required="true"
|
||||
>
|
||||
<template #option="{ opt }">
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<QItemLabel
|
||||
>{{ opt.code }} -
|
||||
{{ opt.description }}</QItemLabel
|
||||
>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<div></div>
|
||||
</template>
|
||||
</VnSelect>
|
||||
/>
|
||||
<VnSelect
|
||||
:label="`${capitalize(t('globals.reason'))}`"
|
||||
:label="`${useCapitalize(t('globals.reason'))}`"
|
||||
v-model="correctionFormData.invoiceReason"
|
||||
:options="invoiceCorrectionTypes"
|
||||
option-value="id"
|
||||
|
@ -512,31 +308,3 @@ const createInvoiceInCorrection = async () => {
|
|||
}
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
en:
|
||||
isNotLinked: The entry {bookEntry} has been deleted with {accountingEntries} entries
|
||||
isLinked: The entry {bookEntry} has been linked to Sage. Please contact administration for further information
|
||||
assertAction: Are you sure you want to {action} this invoice?
|
||||
es:
|
||||
book: asentar
|
||||
unbook: desasentar
|
||||
delete: eliminar
|
||||
clone: clonar
|
||||
To book: Contabilizar
|
||||
To unbook: Descontabilizar
|
||||
Delete invoice: Eliminar factura
|
||||
Invoice deleted: Factura eliminada
|
||||
Clone invoice: Clonar factura
|
||||
Invoice cloned: Factura clonada
|
||||
Show agricultural receipt as PDF: Ver recibo agrícola como PDF
|
||||
Send agricultural receipt as PDF: Enviar recibo agrícola como PDF
|
||||
Are you sure you want to send it?: Estás seguro que quieres enviarlo?
|
||||
Send PDF invoice: Enviar factura a PDF
|
||||
Create rectificative invoice: Crear factura rectificativa
|
||||
Rectificative invoice: Factura rectificativa
|
||||
Original invoice: Factura origen
|
||||
Entry: entrada
|
||||
isNotLinked: Se ha eliminado el asiento nº {bookEntry} con {accountingEntries} apuntes
|
||||
isLinked: El asiento {bookEntry} fue enlazado a Sage, por favor contacta con administración
|
||||
assertAction: Estas seguro de querer {action} esta factura?
|
||||
</i18n>
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
<script setup>
|
||||
import { ref, computed, toRefs, reactive } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
import axios from 'axios';
|
||||
import { useAcl } from 'src/composables/useAcl';
|
||||
import { downloadFile } from 'src/composables/downloadFile';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import { usePrintService } from 'composables/usePrintService';
|
||||
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||
import SendEmailDialog from 'components/common/SendEmailDialog.vue';
|
||||
import InvoiceInToBook from '../InvoiceInToBook.vue';
|
||||
|
||||
const { hasAny } = useAcl();
|
||||
const { t } = useI18n();
|
||||
const { openReport, sendEmail } = usePrintService();
|
||||
const { push, currentRoute } = useRouter();
|
||||
const $props = defineProps({
|
||||
invoice: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
const { invoice } = toRefs($props);
|
||||
const quasar = useQuasar();
|
||||
const arrayData = useArrayData();
|
||||
const currentAction = ref();
|
||||
const config = ref();
|
||||
const correctionDialogRef = ref();
|
||||
const invoiceInCorrection = reactive({ correcting: [], corrected: null });
|
||||
const entityId = computed(() => $props.invoice.id || +currentRoute.value.params.id);
|
||||
const invoiceIn = computed(() => arrayData.store.data);
|
||||
const actions = {
|
||||
unbook: {
|
||||
title: t('assertAction', { action: t('invoicein.descriptorMenu.unbook') }),
|
||||
action: toUnbook,
|
||||
},
|
||||
delete: {
|
||||
title: t('assertAction', { action: t('invoicein.descriptorMenu.delete') }),
|
||||
action: deleteInvoice,
|
||||
},
|
||||
clone: {
|
||||
title: t('assertAction', { action: t('invoicein.descriptorMenu.clone') }),
|
||||
action: cloneInvoice,
|
||||
},
|
||||
showPdf: { cb: showPdfInvoice },
|
||||
sendPdf: { cb: sendPdfInvoiceConfirmation },
|
||||
correct: { cb: () => correctionDialogRef.value.show() },
|
||||
};
|
||||
const canEditProp = (props) =>
|
||||
hasAny([{ model: 'InvoiceIn', props, accessType: 'WRITE' }]);
|
||||
|
||||
function triggerMenu(type) {
|
||||
currentAction.value = actions[type];
|
||||
if (currentAction.value.cb) currentAction.value.cb();
|
||||
else openDialog(type);
|
||||
}
|
||||
|
||||
function openDialog() {
|
||||
quasar.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t(currentAction.value.title),
|
||||
promise: currentAction.value.action,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function toUnbook() {
|
||||
const { data } = await axios.post(`InvoiceIns/${entityId.value}/toUnbook`);
|
||||
const { isLinked, bookEntry, accountingEntries } = data;
|
||||
|
||||
const type = isLinked ? 'warning' : 'positive';
|
||||
const message = isLinked
|
||||
? t('isLinked', { bookEntry })
|
||||
: t('isNotLinked', { bookEntry, accountingEntries });
|
||||
|
||||
quasar.notify({ type, message });
|
||||
if (!isLinked) arrayData.store.data.isBooked = false;
|
||||
}
|
||||
|
||||
async function deleteInvoice() {
|
||||
await axios.delete(`InvoiceIns/${entityId.value}`);
|
||||
quasar.notify({
|
||||
type: 'positive',
|
||||
message: t('invoicein.descriptorMenu.invoiceDeleted'),
|
||||
});
|
||||
push({ path: '/invoice-in' });
|
||||
}
|
||||
|
||||
async function cloneInvoice() {
|
||||
const { data } = await axios.post(`InvoiceIns/${entityId.value}/clone`);
|
||||
quasar.notify({
|
||||
type: 'positive',
|
||||
message: t('invoicein.descriptorMenu.invoiceCloned'),
|
||||
});
|
||||
push({ path: `/invoice-in/${data.id}/summary` });
|
||||
}
|
||||
|
||||
const isAgricultural = () => {
|
||||
if (!config.value) return false;
|
||||
return (
|
||||
invoiceIn.value?.supplier?.sageFarmerWithholdingFk ===
|
||||
config?.value[0]?.sageWithholdingFk
|
||||
);
|
||||
};
|
||||
function showPdfInvoice() {
|
||||
if (isAgricultural()) openReport(`InvoiceIns/${entityId.value}/invoice-in-pdf`);
|
||||
}
|
||||
|
||||
function sendPdfInvoiceConfirmation() {
|
||||
quasar.dialog({
|
||||
component: SendEmailDialog,
|
||||
componentProps: {
|
||||
data: {
|
||||
address: invoiceIn.value.supplier.contacts[0].email,
|
||||
},
|
||||
promise: sendPdfInvoice,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function sendPdfInvoice({ address }) {
|
||||
if (!address)
|
||||
quasar.notify({
|
||||
type: 'negative',
|
||||
message: t(`The email can't be empty`),
|
||||
});
|
||||
else
|
||||
return sendEmail(`InvoiceIns/${entityId.value}/invoice-in-email`, {
|
||||
recipientId: invoiceIn.value.supplier.id,
|
||||
recipient: address,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<InvoiceInToBook>
|
||||
<template #content="{ book }">
|
||||
<QItem
|
||||
v-if="!invoice?.isBooked && canEditProp('toBook')"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="book(entityId)"
|
||||
>
|
||||
<QItemSection>{{ t('invoicein.descriptorMenu.toBook') }}</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</InvoiceInToBook>
|
||||
<QItem
|
||||
v-if="invoice?.isBooked && canEditProp('toUnbook')"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="triggerMenu('unbook')"
|
||||
>
|
||||
<QItemSection>
|
||||
{{ t('invoicein.descriptorMenu.toUnbook') }}
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-if="canEditProp('deleteById')"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="triggerMenu('invoicein.descriptorMenu.delete')"
|
||||
>
|
||||
<QItemSection>{{ t('invoicein.descriptorMenu.deleteInvoice') }}</QItemSection>
|
||||
</QItem>
|
||||
<QItem v-if="canEditProp('clone')" v-ripple clickable @click="triggerMenu('clone')">
|
||||
<QItemSection>{{ t('invoicein.descriptorMenu.cloneInvoice') }}</QItemSection>
|
||||
</QItem>
|
||||
<QItem v-if="isAgricultural()" v-ripple clickable @click="triggerMenu('showPdf')">
|
||||
<QItemSection>{{
|
||||
t('invoicein.descriptorMenu.showAgriculturalPdf')
|
||||
}}</QItemSection>
|
||||
</QItem>
|
||||
<QItem v-if="isAgricultural()" v-ripple clickable @click="triggerMenu('sendPdf')">
|
||||
<QItemSection
|
||||
>{{ t('invoicein.descriptorMenu.sendAgriculturalPdf') }}...</QItemSection
|
||||
>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-if="!invoiceInCorrection.corrected"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="triggerMenu('correct')"
|
||||
>
|
||||
<QItemSection
|
||||
>{{ t('invoicein.descriptorMenu.createCorrective') }}...</QItemSection
|
||||
>
|
||||
</QItem>
|
||||
<QItem v-if="invoice.dmsFk" v-ripple clickable @click="downloadFile(invoice.dmsFk)">
|
||||
<QItemSection>{{ t('components.smartCard.downloadFile') }}</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
en:
|
||||
isNotLinked: The entry {bookEntry} has been deleted with {accountingEntries} entries
|
||||
isLinked: The entry has been linked to Sage. Please contact administration for further information
|
||||
assertAction: Are you sure you want to {action} this invoice?
|
||||
es:
|
||||
isNotLinked: Se ha eliminado el asiento nº {bookEntry} con {accountingEntries} apuntes
|
||||
isLinked: El asiento fue enlazado a Sage, por favor contacta con administración
|
||||
assertAction: Estas seguro de querer {action} esta factura?
|
||||
</i18n>
|
|
@ -10,6 +10,7 @@ import VnLv from 'src/components/ui/VnLv.vue';
|
|||
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||
import InvoiceIntoBook from '../InvoiceInToBook.vue';
|
||||
import VnTitle from 'src/components/common/VnTitle.vue';
|
||||
import InvoiceInDescriptorMenu from './InvoiceInDescriptorMenu.vue';
|
||||
|
||||
const props = defineProps({ id: { type: [Number, String], default: 0 } });
|
||||
const { t } = useI18n();
|
||||
|
@ -26,14 +27,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 +42,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 +52,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 +63,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 +71,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 +82,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 +104,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 +115,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 +124,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 +146,7 @@ const intrastatColumns = ref([
|
|||
},
|
||||
{
|
||||
name: 'landed',
|
||||
label: 'InvoiceIn.summary.country',
|
||||
label: 'invoicein.summary.country',
|
||||
field: (row) => row.country?.code,
|
||||
format: (value) => value,
|
||||
sortable: true,
|
||||
|
@ -200,6 +201,9 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
</template>
|
||||
</InvoiceIntoBook>
|
||||
</template>
|
||||
<template #menu="{ entity }">
|
||||
<InvoiceInDescriptorMenu :invoice="entity" />
|
||||
</template>
|
||||
<template #body="{ entity }">
|
||||
<!--Basic Data-->
|
||||
<QCard class="vn-one">
|
||||
|
@ -210,7 +214,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
/>
|
||||
</QCardSection>
|
||||
<VnLv
|
||||
:label="t('InvoiceIn.list.supplier')"
|
||||
:label="t('invoicein.list.supplier')"
|
||||
:value="entity.supplier?.name"
|
||||
>
|
||||
<template #value>
|
||||
|
@ -221,14 +225,14 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
</template>
|
||||
</VnLv>
|
||||
<VnLv
|
||||
:label="t('InvoiceIn.list.supplierRef')"
|
||||
:label="t('invoicein.list.supplierRef')"
|
||||
:value="entity.supplierRef"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('InvoiceIn.summary.currency')"
|
||||
:label="t('invoicein.summary.currency')"
|
||||
:value="entity.currency?.code"
|
||||
/>
|
||||
<VnLv :label="t('InvoiceIn.serial')" :value="`${entity.serial}`" />
|
||||
<VnLv :label="t('invoicein.serial')" :value="`${entity.serial}`" />
|
||||
<VnLv
|
||||
:label="t('globals.country')"
|
||||
:value="entity.supplier?.country?.code"
|
||||
|
@ -243,19 +247,19 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
</QCardSection>
|
||||
<VnLv
|
||||
:ellipsis-value="false"
|
||||
:label="t('InvoiceIn.summary.issued')"
|
||||
:label="t('invoicein.summary.issued')"
|
||||
:value="toDate(entity.issued)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('InvoiceIn.summary.operated')"
|
||||
:label="t('invoicein.summary.operated')"
|
||||
:value="toDate(entity.operated)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('InvoiceIn.summary.bookEntried')"
|
||||
:label="t('invoicein.summary.bookEntried')"
|
||||
:value="toDate(entity.bookEntried)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('InvoiceIn.summary.bookedDate')"
|
||||
:label="t('invoicein.summary.bookedDate')"
|
||||
:value="toDate(entity.booked)"
|
||||
/>
|
||||
<VnLv :label="t('globals.isVies')" :value="entity.supplier?.isVies" />
|
||||
|
@ -268,18 +272,18 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
/>
|
||||
</QCardSection>
|
||||
<VnLv
|
||||
:label="t('InvoiceIn.summary.sage')"
|
||||
:label="t('invoicein.summary.sage')"
|
||||
:value="entity.sageWithholding?.withholding"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('InvoiceIn.summary.vat')"
|
||||
:label="t('invoicein.summary.vat')"
|
||||
:value="entity.expenseDeductible?.name"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('InvoiceIn.card.company')"
|
||||
:label="t('invoicein.card.company')"
|
||||
:value="entity.company?.code"
|
||||
/>
|
||||
<VnLv :label="t('InvoiceIn.isBooked')" :value="invoiceIn?.isBooked" />
|
||||
<VnLv :label="t('invoicein.isBooked')" :value="invoiceIn?.isBooked" />
|
||||
</QCard>
|
||||
<QCard class="vn-one">
|
||||
<QCardSection class="q-pa-none">
|
||||
|
@ -290,11 +294,11 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
</QCardSection>
|
||||
<QCardSection class="q-pa-none">
|
||||
<VnLv
|
||||
:label="t('InvoiceIn.summary.taxableBase')"
|
||||
:label="t('invoicein.summary.taxableBase')"
|
||||
:value="toCurrency(entity.totals.totalTaxableBase)"
|
||||
/>
|
||||
<VnLv label="Total" :value="toCurrency(entity.totals.totalVat)" />
|
||||
<VnLv :label="t('InvoiceIn.summary.dueTotal')">
|
||||
<VnLv :label="t('invoicein.summary.dueTotal')">
|
||||
<template #value>
|
||||
<QChip
|
||||
dense
|
||||
|
@ -302,8 +306,8 @@ const getLink = (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) }}
|
||||
|
@ -314,7 +318,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
</QCard>
|
||||
<!--Vat-->
|
||||
<QCard v-if="entity.invoiceInTax.length" class="vat">
|
||||
<VnTitle :url="getLink('vat')" :text="t('InvoiceIn.card.vat')" />
|
||||
<VnTitle :url="getLink('vat')" :text="t('invoicein.card.vat')" />
|
||||
<QTable
|
||||
:columns="vatColumns"
|
||||
:rows="entity.invoiceInTax"
|
||||
|
@ -362,7 +366,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
</QCard>
|
||||
<!--Due Day-->
|
||||
<QCard v-if="entity.invoiceInDueDay.length" class="due-day">
|
||||
<VnTitle :url="getLink('due-day')" :text="t('InvoiceIn.card.dueDay')" />
|
||||
<VnTitle :url="getLink('due-day')" :text="t('invoicein.card.dueDay')" />
|
||||
<QTable :columns="dueDayColumns" :rows="entity.invoiceInDueDay" flat>
|
||||
<template #header="dueDayProps">
|
||||
<QTr :props="dueDayProps" class="bg">
|
||||
|
@ -400,7 +404,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
<QCard v-if="entity.invoiceInIntrastat.length">
|
||||
<VnTitle
|
||||
:url="getLink('intrastat')"
|
||||
:text="t('InvoiceIn.card.intrastat')"
|
||||
:text="t('invoicein.card.intrastat')"
|
||||
/>
|
||||
<QTable
|
||||
:columns="intrastatColumns"
|
||||
|
|
|
@ -83,7 +83,7 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
|
|||
</template>
|
||||
</VnSelect>
|
||||
<VnInput
|
||||
:label="t('InvoiceIn.list.supplierRef')"
|
||||
:label="t('invoicein.list.supplierRef')"
|
||||
v-model="data.supplierRef"
|
||||
/>
|
||||
</VnRow>
|
||||
|
@ -97,10 +97,10 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
|
|||
map-options
|
||||
hide-selected
|
||||
:required="true"
|
||||
:rules="validate('InvoiceIn.companyFk')"
|
||||
:rules="validate('invoicein.companyFk')"
|
||||
/>
|
||||
<VnInputDate
|
||||
:label="t('InvoiceIn.summary.issued')"
|
||||
:label="t('invoicein.summary.issued')"
|
||||
v-model="data.issued"
|
||||
/>
|
||||
</VnRow>
|
||||
|
|
|
@ -68,13 +68,26 @@ function handleDaysAgo(params, daysAgo) {
|
|||
<VnSelect
|
||||
v-model="params.supplierFk"
|
||||
url="Suppliers"
|
||||
:fields="['id', 'nickname']"
|
||||
:fields="['id', 'nickname', 'name']"
|
||||
:label="getLocale('supplierFk')"
|
||||
option-label="nickname"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
/>
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>
|
||||
{{ scope.opt?.name}}
|
||||
</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ `#${scope.opt?.id } , ${ scope.opt?.nickname}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
|
@ -151,7 +164,7 @@ function handleDaysAgo(params, daysAgo) {
|
|||
<QItem>
|
||||
<QItemSection>
|
||||
<QCheckbox
|
||||
:label="$t('InvoiceIn.isBooked')"
|
||||
:label="$t('invoicein.isBooked')"
|
||||
v-model="params.isBooked"
|
||||
@update:model-value="searchFn()"
|
||||
toggle-indeterminate
|
||||
|
|
|
@ -26,7 +26,7 @@ const cols = computed(() => [
|
|||
{
|
||||
align: 'left',
|
||||
name: 'isBooked',
|
||||
label: t('InvoiceIn.isBooked'),
|
||||
label: t('invoicein.isBooked'),
|
||||
columnFilter: false,
|
||||
},
|
||||
{
|
||||
|
@ -41,7 +41,7 @@ const cols = computed(() => [
|
|||
{
|
||||
align: 'left',
|
||||
name: 'supplierFk',
|
||||
label: t('InvoiceIn.list.supplier'),
|
||||
label: t('invoicein.list.supplier'),
|
||||
columnFilter: {
|
||||
component: 'select',
|
||||
attrs: {
|
||||
|
@ -55,16 +55,16 @@ const cols = computed(() => [
|
|||
{
|
||||
align: 'left',
|
||||
name: 'supplierRef',
|
||||
label: t('InvoiceIn.list.supplierRef'),
|
||||
label: t('invoicein.list.supplierRef'),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'serial',
|
||||
label: t('InvoiceIn.serial'),
|
||||
label: t('invoicein.serial'),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
label: t('InvoiceIn.list.issued'),
|
||||
label: t('invoicein.list.issued'),
|
||||
name: 'issued',
|
||||
component: null,
|
||||
columnFilter: {
|
||||
|
@ -74,7 +74,7 @@ const cols = computed(() => [
|
|||
},
|
||||
{
|
||||
align: 'left',
|
||||
label: t('InvoiceIn.list.dueDated'),
|
||||
label: t('invoicein.list.dueDated'),
|
||||
name: 'dueDated',
|
||||
component: null,
|
||||
columnFilter: {
|
||||
|
@ -86,12 +86,12 @@ const cols = computed(() => [
|
|||
{
|
||||
align: 'left',
|
||||
name: 'awbCode',
|
||||
label: t('InvoiceIn.list.awb'),
|
||||
label: t('invoicein.list.awb'),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'amount',
|
||||
label: t('InvoiceIn.list.amount'),
|
||||
label: t('invoicein.list.amount'),
|
||||
format: ({ amount }) => toCurrency(amount),
|
||||
cardVisible: true,
|
||||
},
|
||||
|
@ -165,24 +165,24 @@ const cols = computed(() => [
|
|||
<VnSelect
|
||||
v-model="data.supplierFk"
|
||||
url="Suppliers"
|
||||
:fields="['id', 'nickname']"
|
||||
:fields="['id', 'nickname', 'name']"
|
||||
:label="t('globals.supplier')"
|
||||
option-value="id"
|
||||
option-label="nickname"
|
||||
:filter-options="['id', 'name']"
|
||||
:filter-options="['id', 'name', 'nickname']"
|
||||
:required="true"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt?.nickname }}</QItemLabel>
|
||||
<QItemLabel caption> #{{ scope.opt?.id }} </QItemLabel>
|
||||
<QItemLabel caption> #{{ scope.opt?.id }}, {{ scope.opt?.name }} </QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<VnInput
|
||||
:label="t('InvoiceIn.list.supplierRef')"
|
||||
:label="t('invoicein.list.supplierRef')"
|
||||
v-model="data.supplierRef"
|
||||
/>
|
||||
<VnSelect
|
||||
|
@ -194,7 +194,7 @@ const cols = computed(() => [
|
|||
option-label="code"
|
||||
:required="true"
|
||||
/>
|
||||
<VnInputDate :label="t('InvoiceIn.summary.issued')" v-model="data.issued" />
|
||||
<VnInputDate :label="t('invoicein.summary.issued')" v-model="data.issued" />
|
||||
</template>
|
||||
</VnTable>
|
||||
</template>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
InvoiceIn:
|
||||
invoicein:
|
||||
serial: Serial
|
||||
isBooked: Is booked
|
||||
list:
|
||||
|
@ -12,6 +12,26 @@ InvoiceIn:
|
|||
amount: Amount
|
||||
descriptor:
|
||||
ticketList: Ticket list
|
||||
descriptorMenu:
|
||||
book: Book
|
||||
unbook: Unbook
|
||||
delete: Delete
|
||||
clone: Clone
|
||||
toBook: To book
|
||||
toUnbook: To unbook
|
||||
deleteInvoice: Delete invoice
|
||||
invoiceDeleted: invoice deleted
|
||||
cloneInvoice: Clone invoice
|
||||
invoiceCloned: Invoice cloned
|
||||
showAgriculturalPdf: Show agricultural receipt as PDF
|
||||
sendAgriculturalPdf: Send agricultural receipt as PDF
|
||||
checkSendInvoice: Are you sure you want to send it?
|
||||
sendPdfInvoice: Send PDF invoice
|
||||
createCorrective: Create rectificative invoice
|
||||
correctiveInvoice: Rectificative invoice
|
||||
originalInvoice: Original invoice
|
||||
entry: Entry
|
||||
emailEmpty: The email can't be empty
|
||||
card:
|
||||
client: Client
|
||||
company: Company
|
||||
|
@ -44,7 +64,8 @@ InvoiceIn:
|
|||
country: Country
|
||||
params:
|
||||
search: Id or supplier name
|
||||
account: Ledger account
|
||||
correctingFk: Rectificative
|
||||
correctedFk: Corrected
|
||||
isBooked: Is booked
|
||||
account: Ledger account
|
||||
correctingFk: Rectificative
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
InvoiceIn:
|
||||
invoicein:
|
||||
serial: Serie
|
||||
isBooked: Contabilizada
|
||||
list:
|
||||
|
@ -12,6 +12,26 @@ InvoiceIn:
|
|||
amount: Importe
|
||||
descriptor:
|
||||
ticketList: Listado de tickets
|
||||
descriptorMenu:
|
||||
book: Asentar
|
||||
unbook: Desasentar
|
||||
delete: Eliminar
|
||||
clone: Clonar
|
||||
toBook: Contabilizar
|
||||
toUnbook: Descontabilizar
|
||||
deleteInvoice: Eliminar factura
|
||||
invoiceDeleted: Factura eliminada
|
||||
cloneInvoice: Clonar factura
|
||||
invoiceCloned: Factura clonada
|
||||
showAgriculturalPdf: Ver recibo agrícola como PDF
|
||||
sendAgriculturalPdf: Enviar recibo agrícola como PDF
|
||||
checkSendInvoice: ¿Estás seguro que quieres enviarlo?
|
||||
sendPdfInvoice: Enviar factura a PDF
|
||||
createCorrective: Crear factura rectificativa
|
||||
correctiveInvoice: Factura rectificativa
|
||||
originalInvoice: Factura origen
|
||||
entry: Entrada
|
||||
emailEmpty: El email no puede estar vacío
|
||||
card:
|
||||
client: Cliente
|
||||
company: Empresa
|
||||
|
@ -42,6 +62,7 @@ InvoiceIn:
|
|||
country: País
|
||||
params:
|
||||
search: Id o nombre proveedor
|
||||
correctedFk: Rectificada
|
||||
account: Cuenta contable
|
||||
correctingFk: Rectificativa
|
||||
correctedFk: Rectificada
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import { getUrl } from 'src/composables/getUrl';
|
|||
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||
import VnTitle from 'src/components/common/VnTitle.vue';
|
||||
import InvoiceOutDescriptorMenu from './InvoiceOutDescriptorMenu.vue';
|
||||
|
||||
onMounted(async () => {
|
||||
fetch();
|
||||
|
@ -113,6 +114,9 @@ const ticketsColumns = ref([
|
|||
<template #header="{ entity: { invoiceOut } }">
|
||||
<div>{{ invoiceOut.ref }} - {{ invoiceOut.client?.socialName }}</div>
|
||||
</template>
|
||||
<template #menu="{ entity }">
|
||||
<InvoiceOutDescriptorMenu :invoice-out-data="entity.invoiceOut" />
|
||||
</template>
|
||||
<template #body="{ entity: { invoiceOut } }">
|
||||
<QCard class="vn-one">
|
||||
<VnTitle :text="t('globals.pageTitles.basicData')" />
|
||||
|
|
|
@ -47,6 +47,7 @@ const states = ref();
|
|||
:label="t('Amount')"
|
||||
v-model="params.amount"
|
||||
is-outlined
|
||||
data-cy="InvoiceOutFilterAmountBtn"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
|
|
|
@ -101,6 +101,7 @@ onMounted(async () => {
|
|||
dense
|
||||
outlined
|
||||
rounded
|
||||
data-cy="InvoiceOutGlobalClientSelect"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
|
@ -125,6 +126,7 @@ onMounted(async () => {
|
|||
dense
|
||||
outlined
|
||||
rounded
|
||||
data-cy="InvoiceOutGlobalSerialSelect"
|
||||
/>
|
||||
<VnInputDate
|
||||
v-model="formData.invoiceDate"
|
||||
|
@ -135,6 +137,7 @@ onMounted(async () => {
|
|||
v-model="formData.maxShipped"
|
||||
:label="t('maxShipped')"
|
||||
is-outlined
|
||||
data-cy="InvoiceOutGlobalMaxShippedDate"
|
||||
/>
|
||||
<VnSelect
|
||||
:label="t('company')"
|
||||
|
@ -144,6 +147,7 @@ onMounted(async () => {
|
|||
dense
|
||||
outlined
|
||||
rounded
|
||||
data-cy="InvoiceOutGlobalCompanySelect"
|
||||
/>
|
||||
<VnSelect
|
||||
:label="t('printer')"
|
||||
|
@ -152,6 +156,7 @@ onMounted(async () => {
|
|||
dense
|
||||
outlined
|
||||
rounded
|
||||
data-cy="InvoiceOutGlobalPrinterSelect"
|
||||
/>
|
||||
</div>
|
||||
<QBtn
|
||||
|
|
|
@ -126,6 +126,13 @@ const columns = computed(() => [
|
|||
columnField: { component: null },
|
||||
format: (row) => toDate(row.dued),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'customsAgentFk',
|
||||
label: t('invoiceOutList.tableVisibleColumns.customsAgent'),
|
||||
cardVisible: true,
|
||||
format: (row, dashIfEmpty) => dashIfEmpty(row.customsAgentName),
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
name: 'tableActions',
|
||||
|
@ -190,6 +197,7 @@ watchEffect(selectedRows);
|
|||
icon-right="cloud_download"
|
||||
@click="downloadPdf()"
|
||||
:disable="!hasSelectedCards"
|
||||
data-cy="InvoiceOutDownloadPdfBtn"
|
||||
>
|
||||
<QTooltip>{{ t('downloadPdf') }}</QTooltip>
|
||||
</QBtn>
|
||||
|
@ -238,6 +246,7 @@ watchEffect(selectedRows);
|
|||
v-model="data.ticketFk"
|
||||
:label="t('globals.ticket')"
|
||||
style="flex: 1"
|
||||
data-cy="InvoiceOutCreateTicketinput"
|
||||
/>
|
||||
|
||||
<div
|
||||
|
@ -346,12 +355,13 @@ watchEffect(selectedRows);
|
|||
<VnSelect
|
||||
url="InvoiceOutSerials"
|
||||
v-model="data.serial"
|
||||
:label="t('InvoiceIn.serial')"
|
||||
:label="t('invoicein.serial')"
|
||||
:options="invoiceOutSerialsOptions"
|
||||
option-label="description"
|
||||
option-value="code"
|
||||
option-filter
|
||||
:expr-builder="exprBuilder"
|
||||
data-cy="InvoiceOutCreateSerialSelect"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
|
|
|
@ -13,6 +13,7 @@ invoiceOutList:
|
|||
invoiceOutSerial: Serial
|
||||
ticket: Ticket
|
||||
taxArea: Tax area
|
||||
customsAgent: Custom Agent
|
||||
DownloadPdf: Download PDF
|
||||
InvoiceOutSummary: Summary
|
||||
negativeBases:
|
||||
|
@ -24,3 +25,15 @@ negativeBases:
|
|||
hasToInvoice: Has to invoice
|
||||
verifiedData: Verified data
|
||||
commercial: Commercial
|
||||
invoiceout:
|
||||
params:
|
||||
company: Company
|
||||
country: Country
|
||||
clientId: Client ID
|
||||
clientSocialName: Client
|
||||
taxableBase: Base
|
||||
ticketFk: Ticket
|
||||
isActive: Active
|
||||
hasToInvoice: Has to invoice
|
||||
hasVerifiedData: Verified data
|
||||
workerName: Worker
|
|
@ -15,6 +15,7 @@ invoiceOutList:
|
|||
invoiceOutSerial: Serial
|
||||
ticket: Ticket
|
||||
taxArea: Area
|
||||
customsAgent: Agente de aduanas
|
||||
DownloadPdf: Descargar PDF
|
||||
InvoiceOutSummary: Resumen
|
||||
negativeBases:
|
||||
|
@ -27,3 +28,15 @@ negativeBases:
|
|||
hasToInvoice: Debe facturar
|
||||
verifiedData: Datos verificados
|
||||
commercial: Comercial
|
||||
invoiceout:
|
||||
params:
|
||||
company: Empresa
|
||||
country: País
|
||||
clientId: ID del cliente
|
||||
clientSocialName: Cliente
|
||||
taxableBase: Base
|
||||
ticketFk: Ticket
|
||||
isActive: Activo
|
||||
hasToInvoice: Debe facturar
|
||||
hasVerifiedData: Datos verificados
|
||||
workerName: Comercial
|
|
@ -52,6 +52,7 @@ const entityId = computed(() => {
|
|||
:fields="['id', 'name']"
|
||||
sort-by="name ASC"
|
||||
hide-selected
|
||||
data-cy="AddGenusSelectDialog"
|
||||
>
|
||||
<template #form>
|
||||
<CreateGenusForm
|
||||
|
@ -68,6 +69,7 @@ const entityId = computed(() => {
|
|||
:fields="['id', 'name']"
|
||||
sort-by="name ASC"
|
||||
hide-selected
|
||||
data-cy="AddSpeciesSelectDialog"
|
||||
>
|
||||
<template #form>
|
||||
<CreateSpecieForm
|
||||
|
|
|
@ -6,17 +6,16 @@ import { useI18n } from 'vue-i18n';
|
|||
import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||
import RegularizeStockForm from 'components/RegularizeStockForm.vue';
|
||||
import ItemDescriptorImage from 'src/pages/Item/Card/ItemDescriptorImage.vue';
|
||||
import useCardDescription from 'src/composables/useCardDescription';
|
||||
import axios from 'axios';
|
||||
import { dashIfEmpty } from 'src/filters';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import { cloneItem } from 'src/pages/Item/composables/cloneItem';
|
||||
import ItemDescriptorMenu from './ItemDescriptorMenu.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: [Number, String],
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
|
@ -29,7 +28,7 @@ const $props = defineProps({
|
|||
default: null,
|
||||
},
|
||||
saleFk: {
|
||||
type: [Number, String],
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
warehouseFk: {
|
||||
|
@ -38,7 +37,6 @@ const $props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const { openCloneDialog } = cloneItem();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const warehouseConfig = ref(null);
|
||||
|
@ -46,7 +44,6 @@ const entityId = computed(() => {
|
|||
return $props.id || route.params.id;
|
||||
});
|
||||
|
||||
const regularizeStockFormDialog = ref(null);
|
||||
const mounted = ref();
|
||||
|
||||
const arrayDataStock = useArrayData('descriptorStock', {
|
||||
|
@ -61,14 +58,14 @@ onMounted(async () => {
|
|||
const data = ref(useCardDescription());
|
||||
const setData = async (entity) => {
|
||||
if (!entity) return;
|
||||
data.value = useCardDescription(entity?.name, entity?.id);
|
||||
data.value = useCardDescription(entity.name, entity.id);
|
||||
await updateStock();
|
||||
};
|
||||
|
||||
const getItemConfigs = async () => {
|
||||
const { data } = await axios.get('ItemConfigs/findOne');
|
||||
if (!data) return;
|
||||
return (warehouseConfig.value = data.warehouseFk);
|
||||
warehouseConfig.value = data.warehouseFk;
|
||||
};
|
||||
const updateStock = async () => {
|
||||
if (!mounted.value) return;
|
||||
|
@ -89,10 +86,6 @@ const updateStock = async () => {
|
|||
if (storeData?.itemFk == entityId.value) return;
|
||||
await stock.fetch({});
|
||||
};
|
||||
|
||||
const openRegularizeStockForm = () => {
|
||||
regularizeStockFormDialog.value.show();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -105,24 +98,12 @@ const openRegularizeStockForm = () => {
|
|||
:url="`Items/${entityId}/getCard`"
|
||||
@on-fetch="setData"
|
||||
>
|
||||
<template #menu="{}">
|
||||
<QItem v-ripple clickable @click="openRegularizeStockForm()">
|
||||
<QItemSection>
|
||||
{{ t('Regularize stock') }}
|
||||
<QDialog ref="regularizeStockFormDialog">
|
||||
<RegularizeStockForm
|
||||
:item-fk="entityId"
|
||||
:warehouse-fk="warehouseFk"
|
||||
@on-data-saved="updateStock()"
|
||||
/>
|
||||
</QDialog>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem v-ripple clickable @click="openCloneDialog(entityId)">
|
||||
<QItemSection>
|
||||
{{ t('globals.clone') }}
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<template #menu>
|
||||
<ItemDescriptorMenu
|
||||
:entity-id="entityId"
|
||||
:warehouse-fk="warehouseFk"
|
||||
@regularized="updateStock"
|
||||
/>
|
||||
</template>
|
||||
<template #before>
|
||||
<ItemDescriptorImage
|
||||
|
@ -191,7 +172,6 @@ const openRegularizeStockForm = () => {
|
|||
|
||||
<i18n>
|
||||
es:
|
||||
Regularize stock: Regularizar stock
|
||||
Inactive article: Artículo inactivo
|
||||
</i18n>
|
||||
|
||||
|
|
|
@ -131,7 +131,6 @@ const handlePhotoUpdated = (evt = false) => {
|
|||
|
||||
<i18n>
|
||||
es:
|
||||
Regularize stock: Regularizar stock
|
||||
All it's properties will be copied: Todas sus propiedades serán copiadas
|
||||
Do you want to clone this item?: ¿Desea clonar este artículo?
|
||||
warehouseText: Calculado sobre el almacén de { warehouseName }
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<script setup>
|
||||
import RegularizeStockForm from 'components/RegularizeStockForm.vue';
|
||||
import { cloneItem } from 'src/pages/Item/composables/cloneItem';
|
||||
|
||||
const { openCloneDialog } = cloneItem();
|
||||
|
||||
defineProps({
|
||||
entityId: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
warehouseFk: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
defineEmits(['regularized']);
|
||||
</script>
|
||||
<template>
|
||||
<QItem v-ripple clickable @click="$refs.regularizeStockFormDialog.show()">
|
||||
<QItemSection>
|
||||
{{ $t('item.regularizeStock') }}
|
||||
<QDialog ref="regularizeStockFormDialog">
|
||||
<RegularizeStockForm
|
||||
:item-fk="entityId"
|
||||
:warehouse-fk="warehouseFk"
|
||||
@on-data-saved="$emit('regularized')"
|
||||
/>
|
||||
</QDialog>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem v-ripple clickable @click="openCloneDialog(entityId)">
|
||||
<QItemSection>
|
||||
{{ $t('globals.clone') }}
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.weekdaysBtn {
|
||||
margin: 1%;
|
||||
}
|
||||
</style>
|
|
@ -8,6 +8,7 @@ import VnLv from 'src/components/ui/VnLv.vue';
|
|||
import ItemDescriptorImage from 'src/pages/Item/Card/ItemDescriptorImage.vue';
|
||||
import VnUserLink from 'src/components/ui/VnUserLink.vue';
|
||||
import VnTitle from 'src/components/common/VnTitle.vue';
|
||||
import ItemDescriptorMenu from './ItemDescriptorMenu.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
|
@ -43,10 +44,13 @@ const getUrl = (id, param) => `#/Item/${id}/${param}`;
|
|||
<template #header="{ entity: { item } }">
|
||||
{{ item.id }} - {{ item.name }}
|
||||
</template>
|
||||
<template #menu>
|
||||
<ItemDescriptorMenu :entity-id="entityId" :warehouse-fk="warehouseFk" />
|
||||
</template>
|
||||
<template #body="{ entity: { item, tags, visible, available, botanical } }">
|
||||
<QCard class="vn-one photo">
|
||||
<ItemDescriptorImage
|
||||
:entity-id="Number(entityId)"
|
||||
:entity-id="entityId"
|
||||
:visible="visible"
|
||||
:available="available"
|
||||
:show-edit-button="false"
|
||||
|
@ -89,7 +93,7 @@ const getUrl = (id, param) => `#/Item/${id}/${param}`;
|
|||
<QCard class="vn-one">
|
||||
<VnTitle
|
||||
:url="getUrl(entityId, 'basic-data')"
|
||||
:text="t('item.summary.basicData')"
|
||||
:text="t('item.summary.otherData')"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('item.summary.intrastatCode')"
|
||||
|
|
|
@ -107,7 +107,7 @@ const submitTags = async (data) => {
|
|||
@on-fetch="onItemTagsFetched"
|
||||
>
|
||||
<template #body="{ rows, validate }">
|
||||
<QCard class="q-px-lg q-pt-md q-pb-sm">
|
||||
<QCard class="q-px-lg q-pt-md q-pb-sm" data-cy="itemTags">
|
||||
<VnRow
|
||||
v-for="(row, index) in rows"
|
||||
:key="index"
|
||||
|
|
|
@ -35,6 +35,7 @@ const editTableCellDialogRef = ref(null);
|
|||
const user = state.getUser();
|
||||
const fixedPrices = ref([]);
|
||||
const warehousesOptions = ref([]);
|
||||
const hasSelectedRows = computed(() => rowsSelected.value.length > 0);
|
||||
const rowsSelected = ref([]);
|
||||
const itemFixedPriceFilterRef = ref();
|
||||
|
||||
|
@ -368,9 +369,9 @@ function handleOnDataSave({ CrudModelRef }) {
|
|||
</template>
|
||||
</RightMenu>
|
||||
<VnSubToolbar>
|
||||
<template #st-data>
|
||||
<template #st-actions>
|
||||
<QBtn
|
||||
v-if="rowsSelected.length"
|
||||
:disable="!hasSelectedRows"
|
||||
@click="openEditTableCellDialog()"
|
||||
color="primary"
|
||||
icon="edit"
|
||||
|
@ -380,13 +381,13 @@ function handleOnDataSave({ CrudModelRef }) {
|
|||
</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
:disable="!hasSelectedRows"
|
||||
:label="tMobile('globals.remove')"
|
||||
color="primary"
|
||||
icon="delete"
|
||||
flat
|
||||
@click="(row) => confirmRemove(row, true)"
|
||||
:title="t('globals.remove')"
|
||||
v-if="rowsSelected.length"
|
||||
/>
|
||||
</template>
|
||||
</VnSubToolbar>
|
||||
|
@ -396,6 +397,7 @@ function handleOnDataSave({ CrudModelRef }) {
|
|||
:default-save="false"
|
||||
data-key="ItemFixedPrices"
|
||||
url="FixedPrices/filter"
|
||||
:order="['name DESC', 'itemFk DESC']"
|
||||
save-url="FixedPrices/crud"
|
||||
ref="tableRef"
|
||||
dense
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { ref, computed, onBeforeMount } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
import VnImg from 'src/components/ui/VnImg.vue';
|
||||
|
@ -15,6 +15,9 @@ import ItemTypeDescriptorProxy from './ItemType/Card/ItemTypeDescriptorProxy.vue
|
|||
import { cloneItem } from 'src/pages/Item/composables/cloneItem';
|
||||
import RightMenu from 'src/components/common/RightMenu.vue';
|
||||
import ItemListFilter from './ItemListFilter.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import axios from 'axios';
|
||||
|
||||
const entityId = computed(() => route.params.id);
|
||||
const { openCloneDialog } = cloneItem();
|
||||
|
@ -22,7 +25,9 @@ const { viewSummary } = useSummaryDialog();
|
|||
const { t } = useI18n();
|
||||
const tableRef = ref();
|
||||
const route = useRoute();
|
||||
|
||||
const validPriorities = ref([]);
|
||||
const defaultTag = ref();
|
||||
const defaultPriority = ref();
|
||||
const itemFilter = {
|
||||
include: [
|
||||
{
|
||||
|
@ -90,7 +95,6 @@ const columns = computed(() => [
|
|||
label: t('globals.description'),
|
||||
name: 'description',
|
||||
align: 'left',
|
||||
create: true,
|
||||
columnFilter: {
|
||||
name: 'search',
|
||||
},
|
||||
|
@ -132,13 +136,23 @@ const columns = computed(() => [
|
|||
fields: ['id', 'name'],
|
||||
},
|
||||
},
|
||||
create: true,
|
||||
visible: false,
|
||||
},
|
||||
{
|
||||
label: t('item.list.typeName'),
|
||||
name: 'typeName',
|
||||
align: 'left',
|
||||
component: 'select',
|
||||
columnFilter: {
|
||||
name: 'typeFk',
|
||||
attrs: {
|
||||
url: 'ItemTypes',
|
||||
fields: ['id', 'name'],
|
||||
},
|
||||
},
|
||||
columnField: {
|
||||
component: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('item.list.category'),
|
||||
|
@ -161,6 +175,11 @@ const columns = computed(() => [
|
|||
name: 'intrastat',
|
||||
align: 'left',
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Intrastats',
|
||||
optionValue: 'description',
|
||||
optionLabel: 'description',
|
||||
},
|
||||
columnFilter: {
|
||||
name: 'intrastat',
|
||||
attrs: {
|
||||
|
@ -172,7 +191,6 @@ const columns = computed(() => [
|
|||
columnField: {
|
||||
component: null,
|
||||
},
|
||||
create: true,
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
|
@ -198,7 +216,6 @@ const columns = computed(() => [
|
|||
columnField: {
|
||||
component: null,
|
||||
},
|
||||
create: true,
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
|
@ -297,12 +314,21 @@ const columns = computed(() => [
|
|||
],
|
||||
},
|
||||
]);
|
||||
|
||||
onBeforeMount(async () => {
|
||||
const { data } = await axios.get('ItemConfigs');
|
||||
defaultTag.value = data[0].defaultTag;
|
||||
defaultPriority.value = data[0].defaultPriority;
|
||||
data.forEach((priority) => {
|
||||
validPriorities.value = priority.validPriorities;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<VnSearchbar
|
||||
data-key="ItemList"
|
||||
:label="t('item.searchbar.label')"
|
||||
:info="t('You can search by id')"
|
||||
:info="t('item.searchbar.info')"
|
||||
/>
|
||||
<RightMenu>
|
||||
<template #right-panel>
|
||||
|
@ -310,15 +336,18 @@ const columns = computed(() => [
|
|||
</template>
|
||||
</RightMenu>
|
||||
<VnTable
|
||||
v-if="defaultTag"
|
||||
ref="tableRef"
|
||||
data-key="ItemList"
|
||||
url="Items/filter"
|
||||
:create="{
|
||||
urlCreate: 'Items',
|
||||
title: t('Create Item'),
|
||||
onDataSaved: () => tableRef.redirect(),
|
||||
urlCreate: 'Items/new',
|
||||
title: t('item.list.newItem'),
|
||||
onDataSaved: ({ id }) => tableRef.redirect(`${id}/basic-data`),
|
||||
formInitialData: {
|
||||
editorFk: entityId,
|
||||
tag: defaultTag,
|
||||
priority: defaultPriority,
|
||||
},
|
||||
}"
|
||||
:order="['isActive DESC', 'name', 'id']"
|
||||
|
@ -364,6 +393,96 @@ const columns = computed(() => [
|
|||
</div>
|
||||
<FetchedTags :item="row" :columns="3" />
|
||||
</template>
|
||||
<template #more-create-dialog="{ data }">
|
||||
<VnInput
|
||||
v-model="data.provisionalName"
|
||||
:label="t('globals.description')"
|
||||
:is-required="true"
|
||||
/>
|
||||
<VnSelect
|
||||
url="Tags"
|
||||
v-model="data.tag"
|
||||
:label="t('globals.tag')"
|
||||
:fields="['id', 'name']"
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
:is-required="true"
|
||||
:sort-by="['name ASC']"
|
||||
>
|
||||
<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
|
||||
:options="validPriorities"
|
||||
v-model="data.priority"
|
||||
:label="t('item.create.priority')"
|
||||
:is-required="true"
|
||||
/>
|
||||
<VnSelect
|
||||
url="ItemTypes"
|
||||
v-model="data.typeFk"
|
||||
:label="t('item.list.typeName')"
|
||||
:fields="['id', 'code', 'name']"
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
:is-required="true"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ scope.opt?.code }} #{{ scope.opt?.id }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<VnSelect
|
||||
url="Intrastats"
|
||||
v-model="data.intrastatFk"
|
||||
:label="t('globals.intrastat')"
|
||||
:fields="['id', 'description']"
|
||||
option-label="description"
|
||||
option-value="id"
|
||||
:is-required="true"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt?.description }}</QItemLabel>
|
||||
<QItemLabel caption> #{{ scope.opt?.id }} </QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<VnSelect
|
||||
url="Origins"
|
||||
v-model="data.originFk"
|
||||
:label="t('globals.origin')"
|
||||
:fields="['id', 'code', 'name']"
|
||||
option-label="code"
|
||||
option-value="id"
|
||||
:is-required="true"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ scope.opt?.code }} #{{ scope.opt?.id }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</template>
|
||||
</VnTable>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
|
@ -379,5 +498,4 @@ es:
|
|||
Create Item: Crear artículo
|
||||
You can search by id: Puedes buscar por id
|
||||
Preview: Vista previa
|
||||
Regularize stock: Regularizar stock
|
||||
</i18n>
|
||||
|
|
|
@ -199,17 +199,7 @@ onMounted(async () => {
|
|||
dense
|
||||
outlined
|
||||
rounded
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
t(`params.${scope.opt?.name}`)
|
||||
}}</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
|
@ -265,6 +255,7 @@ onMounted(async () => {
|
|||
option-value="id"
|
||||
option-label="name"
|
||||
:fields="['id', 'name', 'nickname']"
|
||||
:filter-options="['id', 'name', 'nickname']"
|
||||
sort-by="name ASC"
|
||||
hide-selected
|
||||
dense
|
||||
|
@ -274,9 +265,12 @@ onMounted(async () => {
|
|||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{
|
||||
scope.opt?.name + ': ' + scope.opt?.nickname
|
||||
}}</QItemLabel>
|
||||
<QItemLabel>
|
||||
{{ scope.opt?.name}}
|
||||
</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ `#${scope.opt?.id } , ${ scope.opt?.nickname}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
|
@ -375,6 +369,7 @@ onMounted(async () => {
|
|||
:model-value="fieldFilter.selectedField"
|
||||
:options="moreFields"
|
||||
option-label="label"
|
||||
option-value="label"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
|
|
|
@ -172,24 +172,22 @@ const changeQuantity = async (request) => {
|
|||
};
|
||||
|
||||
await axios.patch(`Sales/${request.saleFk}`, params);
|
||||
notify(t('globals.dataSaved'), 'positive');
|
||||
confirmRequest(request);
|
||||
} else confirmRequest(request);
|
||||
}
|
||||
await confirmRequest(request);
|
||||
notify(t('globals.dataSaved'), 'positive');
|
||||
};
|
||||
|
||||
const confirmRequest = async (request) => {
|
||||
if (request.itemFk && request.saleQuantity) {
|
||||
const params = {
|
||||
itemFk: request.itemFk,
|
||||
quantity: request.saleQuantity,
|
||||
attenderFk: request.attenderFk,
|
||||
};
|
||||
if (!request.itemFk || !request.saleQuantity) return;
|
||||
const params = {
|
||||
itemFk: request.itemFk,
|
||||
quantity: request.saleQuantity,
|
||||
attenderFk: request.attenderFk,
|
||||
};
|
||||
|
||||
const { data } = await axios.post(`TicketRequests/${request.id}/confirm`, params);
|
||||
request.itemDescription = data.concept;
|
||||
request.isOk = true;
|
||||
notify(t('globals.dataSaved'), 'positive');
|
||||
}
|
||||
const { data } = await axios.post(`TicketRequests/${request.id}/confirm`, params);
|
||||
request.itemDescription = data.concept;
|
||||
request.isOk = true;
|
||||
};
|
||||
|
||||
const getState = (isOk) => {
|
||||
|
|
|
@ -149,7 +149,6 @@ onMounted(async () => {
|
|||
:label="t('params.requesterFk')"
|
||||
v-model="params.requesterFk"
|
||||
@update:model-value="searchFn()"
|
||||
:fields="['id', 'name']"
|
||||
:params="{ departmentCodes: ['VT'] }"
|
||||
hide-selected
|
||||
dense
|
||||
|
|
|
@ -107,6 +107,7 @@ item:
|
|||
scopeDays: Scope days
|
||||
searchbar:
|
||||
label: Search item
|
||||
info: You can search by id
|
||||
descriptor:
|
||||
item: Item
|
||||
buyer: Buyer
|
||||
|
@ -139,6 +140,7 @@ item:
|
|||
stemMultiplier: Multiplier
|
||||
producer: Producer
|
||||
landed: Landed
|
||||
newItem: New item
|
||||
basicData:
|
||||
type: Type
|
||||
reference: Reference
|
||||
|
@ -217,3 +219,4 @@ item:
|
|||
minSalesQuantity: 'Cantidad mínima de venta'
|
||||
genus: 'Genus'
|
||||
specie: 'Specie'
|
||||
regularizeStock: Regularize stock
|
||||
|
|
|
@ -109,6 +109,7 @@ item:
|
|||
scopeDays: Días en adelante
|
||||
searchbar:
|
||||
label: Buscar artículo
|
||||
info: Puedes buscar por id
|
||||
descriptor:
|
||||
item: Artículo
|
||||
buyer: Comprador
|
||||
|
@ -141,6 +142,7 @@ item:
|
|||
stemMultiplier: Multiplicador
|
||||
producer: Productor
|
||||
landed: F. entrega
|
||||
newItem: Nuevo artículo
|
||||
basicData:
|
||||
type: Tipo
|
||||
reference: Referencia
|
||||
|
@ -208,6 +210,7 @@ item:
|
|||
minSalesQuantity: 'Cantidad mínima de venta'
|
||||
genus: 'Genus'
|
||||
specie: 'Specie'
|
||||
regularizeStock: Regularizar stock
|
||||
buyRequest:
|
||||
ticketId: 'ID Ticket'
|
||||
shipped: 'F. envío'
|
||||
|
|
|
@ -3,7 +3,7 @@ import { ref } from 'vue';
|
|||
import { Notify } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import VnInputPassword from 'src/components/common/VnInputPassword.vue';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import { useLogin } from 'src/composables/useLogin';
|
||||
|
||||
|
@ -63,11 +63,10 @@ async function onSubmit() {
|
|||
:rules="[(val) => (val && val.length > 0) || t('login.fieldRequired')]"
|
||||
color="primary"
|
||||
/>
|
||||
<VnInput
|
||||
type="password"
|
||||
<VnInputPassword
|
||||
v-model="password"
|
||||
:label="t('login.password')"
|
||||
lazy-rules
|
||||
:toggle-visibility="true"
|
||||
:rules="[(val) => (val && val.length > 0) || t('login.fieldRequired')]"
|
||||
class="red"
|
||||
/>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
No hay que poner rutas concretas en un composable global.
Mañana lo miramos