forked from verdnatura/salix-front
Merge branch 'dev' of https: refs #7404//gitea.verdnatura.es/verdnatura/salix-front into 7404-stockBought
This commit is contained in:
commit
2fb974af0f
|
@ -0,0 +1,33 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function getCurrentBranchName(p = process.cwd()) {
|
||||
if (!fs.existsSync(p)) return false;
|
||||
|
||||
const gitHeadPath = path.join(p, '.git', 'HEAD');
|
||||
|
||||
if (!fs.existsSync(gitHeadPath))
|
||||
return getCurrentBranchName(path.resolve(p, '..'));
|
||||
|
||||
const headContent = fs.readFileSync(gitHeadPath, 'utf-8');
|
||||
return headContent.trim().split('/')[2];
|
||||
}
|
||||
|
||||
const branchName = getCurrentBranchName();
|
||||
|
||||
if (branchName) {
|
||||
const msgPath = `.git/COMMIT_EDITMSG`;
|
||||
const msg = fs.readFileSync(msgPath, 'utf-8');
|
||||
const reference = branchName.match(/^\d+/);
|
||||
|
||||
const referenceTag = `refs #${reference}`;
|
||||
if (!msg.includes(referenceTag) && reference) {
|
||||
const splitedMsg = msg.split(':');
|
||||
|
||||
if (splitedMsg.length > 1) {
|
||||
const finalMsg = splitedMsg[0] + ': ' + referenceTag + splitedMsg.slice(1).join(':');
|
||||
fs.writeFileSync(msgPath, finalMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
echo "Running husky commit-msg hook"
|
||||
npx --no-install commitlint --edit
|
||||
echo "Adding reference tag to commit message"
|
||||
node .husky/addReferenceTag.js
|
||||
|
226
CHANGELOG.md
226
CHANGELOG.md
|
@ -1,3 +1,229 @@
|
|||
# Version 24.36 - 2024-08-27
|
||||
|
||||
### Added 🆕
|
||||
|
||||
- feat(FormModel): trim data by default by:alexm
|
||||
- feat(orderBasicData): add notes by:alexm
|
||||
- feat(orderList): correct create order by:alexm
|
||||
- feat(orderList): use orderFilter and fixed this by:alexm
|
||||
- feat: #7323 handle workerPhoto (origin/7323_workerPhoto, 7323_workerPhoto) by:Javier Segarra
|
||||
- feat: add recover password and reset password by:alexm
|
||||
- feat: refs #7346 add seriaType option by:jgallego
|
||||
- feat: refs #7346 elimino === by:jgallego
|
||||
- feat: refs #7346 formdata uses serialType by:jgallego
|
||||
- feat: refs #7346 refactor by:jgallego
|
||||
- feat: refs #7346 sonarLint warnings (origin/7346-invoiceOutMultilple, 7346-invoiceOutMultilple) by:jgallego
|
||||
- feat: refs #7710 uses cloneAll by:jgallego
|
||||
- fix: refs #7717 fix OrderList table filters' and summary table style by:Jon
|
||||
|
||||
### Changed 📦
|
||||
|
||||
- feat: refs #7346 refactor by:jgallego
|
||||
- perf: date fields (mindshore/feature/TicketFutureFilter, feature/TicketFutureFilter) by:Javier Segarra
|
||||
- perf: refs #7717 right menu filter by:Jon
|
||||
- perf: use ref at component start by:Javier Segarra
|
||||
- refactor: refs #7717 delete useless function and import by:Jon
|
||||
- refactor: refs #7717 deleted useless code by:Jon
|
||||
|
||||
### Fixed 🛠️
|
||||
|
||||
- feat(orderList): use orderFilter and fixed this by:alexm
|
||||
- fix(VnTable): orderBy v-model by:alexm
|
||||
- fix(account_card): redirection by:carlossa
|
||||
- fix(orderLines): reload when delete and redirect when confirm by:alexm
|
||||
- fix: #6336 ClaimListStates by:Javier Segarra
|
||||
- fix: account subsections cards by:carlossa
|
||||
- fix: duplicate key by:Jon
|
||||
- fix: order description to vnTable by:alexm
|
||||
- fix: orderCatalogFilter order by:alexm
|
||||
- fix: quasar build warnings (6336_claim_fix_states) by:Javier Segarra
|
||||
- fix: refs #7717 fix OrderList table filters' and summary table style by:Jon
|
||||
- fix: refs #7717 fix basic data form & minor errors by:Jon
|
||||
- fix: refs #7717 fix catalog filter, searchbar redirect and search by:Jon
|
||||
- fix: refs #7717 fix catalog searchbar and worker tests(refs #7323) by:Jon
|
||||
- fix: refs #7717 fix order sections by:Jon
|
||||
- fix: refs #7717 fix volume and lines redirect by:Jon
|
||||
- fix: refs #7717 fixed searchbar filter with rightmenu filters' applied by:Jon
|
||||
- fix: test by:alexm
|
||||
- fix: ticketDescriptorMenu by:Javier Segarra
|
||||
- refs #7355 account fixes by:carlossa
|
||||
|
||||
# Version 24.34 - 2024-08-20
|
||||
|
||||
### Added 🆕
|
||||
|
||||
- chore: #6900 order params by:jorgep
|
||||
- chore: refs #6900 drop console log by:jorgep
|
||||
- chore: refs #6900 drop vnCurrency by:jorgep
|
||||
- chore: refs #6900 fix e2e tests by:jorgep
|
||||
- chore: refs #6900 mv rectificative logic by:jorgep
|
||||
- chore: refs #6900 responsive code by:jorgep
|
||||
- chore: refs #7283 drop array types by:jorgep
|
||||
- chore: refs #7283 drop import by:jorgep
|
||||
- chore: refs #7283 fix e2e logout by:jorgep
|
||||
- chore: refs #7283 update VnAvatar title handling by:jorgep
|
||||
- chore: refs #7323 fix test by:jorgep
|
||||
- chore: refs #7323 remove unused import by:jorgep
|
||||
- chore: refs #7323drop commented code by:jorgep
|
||||
- feat(VnCard): use props searchbar by:alexm
|
||||
- feat(customer): improve basicData to balance by:alexm
|
||||
- feat(customer_balance): refs #6943 add functionality from salix by:alexm
|
||||
- feat(customer_balance): refs #6943 translations by:alexm
|
||||
- feat: refs #6130 husky commitLint config by:pablone
|
||||
- feat: refs #6130 husky hooks by:pablone
|
||||
- feat: refs #6900 add InvoiceInSerial by:jorgep
|
||||
- feat: refs #6900 add locale by:jorgep
|
||||
- feat: refs #6900 use VnTable & sort filter fields by:jorgep
|
||||
- feat: refs #7323 add flex-wrap by:jorgep
|
||||
- feat: refs #7323 add my account" btn & fix models log selectable by:jorgep
|
||||
- feat: refs #7323 improve test by:jorgep
|
||||
|
||||
### Changed 📦
|
||||
|
||||
- refactor(customer_log: use VnLog by:alexm
|
||||
- refactor(customer_recovery): to vnTable by:alexm
|
||||
- refactor(customer_webAccess): FormModel by:alexm
|
||||
- refactor: refs #7283 update avatar size and color by:jorgep
|
||||
|
||||
### Fixed 🛠️
|
||||
|
||||
- chore: refs #6900 fix e2e tests by:jorgep
|
||||
- chore: refs #7283 fix e2e logout by:jorgep
|
||||
- chore: refs #7323 fix test by:jorgep
|
||||
- feat: refs #7323 add my account" btn & fix models log selectable by:jorgep
|
||||
- fix #7355 fix acls list by:carlossa
|
||||
- fix(VnFilterPanel): emit userParams better by:alexm
|
||||
- fix(claim_summary): url links (HEAD -> 7864_testToMaster_2434, origin/test, origin/7864_testToMaster_2434, test) by:alexm
|
||||
- fix(customer_sms: fix reload by:alexm
|
||||
- fix(twoFactor): unify code login and twoFactor by:alexm
|
||||
- fix: VnCard VnSearchbar props by:alexm
|
||||
- fix: accountMailAlias by:alexm
|
||||
- fix: refs #6130 add commit lint modules by:pablone
|
||||
- fix: refs #6130 pnpm-lock.yml by:pablone
|
||||
- fix: refs #6900 improve loading by:jorgep
|
||||
- fix: refs #6900 improve logic (origin/6900-addSerial) by:jorgep
|
||||
- fix: refs #6900 improve logic by:jorgep
|
||||
- fix: refs #6900 rectificative btn reactivity by:jorgep
|
||||
- fix: refs #6900 use type number by:jorgep
|
||||
- fix: refs #6900 vat & dueday by:jorgep
|
||||
- fix: refs #6900 vat, dueday & intrastat by:jorgep
|
||||
- fix: refs #6989 show entity name & default time from config table by:jorgep
|
||||
- fix: refs #7283 basicData locale by:jorgep
|
||||
- fix: refs #7283 itemLastEntries filter by:jorgep
|
||||
- fix: refs #7283 itemTags & VnImg by:jorgep
|
||||
- fix: refs #7283 locale by:jorgep
|
||||
- fix: refs #7283 min-width vnImg by:jorgep
|
||||
- fix: refs #7283 use vnAvatar & add optional zoom by:jorgep
|
||||
- fix: refs #7283 userPanel pic by:jorgep
|
||||
- fix: refs #7323 add department popup by:jorgep
|
||||
- fix: refs #7323 add locale by:jorgep
|
||||
- fix: refs #7323 css righ menu by:jorgep
|
||||
- fix: refs #7323 data-key & add select by:jorgep
|
||||
- fix: refs #7323 load all opts by:jorgep
|
||||
- fix: refs #7323 righ menu bug by:jorgep
|
||||
- fix: refs #7323 use global locale by:jorgep
|
||||
- fix: refs #7323 use workerFilter (origin/7323-warmfix-fixErrors) by:jorgep
|
||||
- fix: refs #7323 vnsubtoolbar css by:jorgep
|
||||
- fix: refs #7323 wrong css by:jorgep
|
||||
- refs #7355 fix Rol, alias by:carlossa
|
||||
- refs #7355 fix accountAlias by:carlossa
|
||||
- refs #7355 fix alias summary by:carlossa
|
||||
- refs #7355 fix conflicts by:carlossa
|
||||
- refs #7355 fix create Rol by:carlossa
|
||||
- refs #7355 fix list by:carlossa
|
||||
- refs #7355 fix lists redirects summary by:carlossa
|
||||
- refs #7355 fix roles by:carlossa
|
||||
- refs #7355 fix search exprBuilder by:carlossa
|
||||
- refs #7355 fix vnTable by:carlossa
|
||||
|
||||
# Version 24.32 - 2024-08-06
|
||||
|
||||
### Added 🆕
|
||||
|
||||
- chore: refs #7197 drop space by:jorgep
|
||||
- chore: refs #7197 drop useless attr by:jorgep
|
||||
- chore: refs #7197 fix test by:jorgep
|
||||
- chore: refs #7197 fix tests by:jorgep
|
||||
- chore: refs #7197 fix unit tests by:jorgep
|
||||
- chore: refs #7197 idrop useless class by:jorgep
|
||||
- chore: refs #7197 improve form filling in Cypress tests by:jorgep
|
||||
- chore: refs #7197 remove unused import by:jorgep
|
||||
- feat: customerPayments card view by:alexm
|
||||
- feat: refs #6943 lock grid mode by:jorgep
|
||||
- feat: refs #6943 wip consumption filter by:jorgep
|
||||
- feat: refs #7197 add correcting filter by:jorgep
|
||||
- feat: refs #7197 add supplier activities filter option by:jorgep
|
||||
- feat: refs #7197 summary responsive by:jorgep
|
||||
- feat: refs #7323 fix descriptors, added VnTable and minor changes by:Jon
|
||||
- feat: refs #7323 fixed tests, changed calendar styles and fix workerCreate by:Jon
|
||||
- feat: refs #7356 list & weekly to VnTable and style fixes by:Jon
|
||||
- feat: refs #7401 add menu options by:pablone
|
||||
- feat: SalesClientTable by:Javier Segarra
|
||||
- feat: salesOrderTable by:Javier Segarra
|
||||
- feat: salesTicketTable by:Javier Segarra
|
||||
- feat: VnTable SalesTicketTable by:Javier Segarra
|
||||
- fix: columns style by:alexm
|
||||
|
||||
### Changed 📦
|
||||
|
||||
- perf: LeftMenu show/hide by:Javier Segarra
|
||||
- perf: refs #7356 TicketList state column by:Jon
|
||||
- perf: VnFilterPanel (origin/7323_WorkerMigration_End) by:Javier Segarra
|
||||
- perf: width SalesTicketsTable by:Javier Segarra
|
||||
- refactor: #6943 wip use vnTable CustomerCredits by:jorgep
|
||||
- refactor: CustomerNotifications use VnTable by:alexm
|
||||
- refactor: CustomerPayments use VnTable by:alexm
|
||||
- refactor: refs #7014 deleted main files and changed route files by:Jon
|
||||
- refactor: refs #7014 improved route.js & deleted RouteMain by:Jon
|
||||
- refactor: refs #7014 refactor <module>Main.vue by:Jon
|
||||
- refactor: refs #7014 refactor ZoneCard, deleted ZoneMain & created basic tests for functionality by:Jon
|
||||
- refactor: refs #7197 use invoiceInSearchbar & queryParams by:jorgep
|
||||
- refactor: refs #7323 hidden column filter proposal by:Jon
|
||||
- refactor: refs #7356 fixed VnTable filters by:Jon
|
||||
- refactor: refs #7356 requested changes by:Jon
|
||||
- refactor: wip use vnTable CustomerCredits by:jorgep
|
||||
|
||||
### Fixed 🛠️
|
||||
|
||||
- chore: refs #7197 fix test by:jorgep
|
||||
- chore: refs #7197 fix tests by:jorgep
|
||||
- chore: refs #7197 fix unit tests by:jorgep
|
||||
- feat: refs #7323 fix descriptors, added VnTable and minor changes by:Jon
|
||||
- feat: refs #7323 fixed tests, changed calendar styles and fix workerCreate by:Jon
|
||||
- feat: refs #7356 list & weekly to VnTable and style fixes by:Jon
|
||||
- fix(claim): small details (6336-claim-v6) by:alexm
|
||||
- fix: columns style by:alexm
|
||||
- fix: customer defaulter add amount order (6943-fixCustomer) by:alexm
|
||||
- fix: customerDefaulter correct functionality by:alexm
|
||||
- fix: customerNotifications filter by:alexm
|
||||
- fix: fix conflicts by:Jon
|
||||
- fix: refs #6101 fix TicketList by:Jon
|
||||
- fix: refs #6891 worker tests by:jorgep
|
||||
- fix: refs #6943 drop padding-left checkbox & create wrap mode vnRow by:jorgep
|
||||
- fix: refs #6943 prevent undefined by:jorgep
|
||||
- fix: refs #7014 fix tests by:Jon
|
||||
- fix: refs #7014 fix wagon module by:Jon
|
||||
- fix: refs #7197 add url InvoiceInSearchbar by:jorgep
|
||||
- fix: refs #7197 amount reactivity by:jorgep
|
||||
- fix: refs #7197 drop character by:jorgep
|
||||
- fix: refs #7197 reactivity invoiceCorrection by:jorgep
|
||||
- fix: refs #7197 responsive summary layout by:jorgep
|
||||
- fix: refs #7197 rollback by:jorgep
|
||||
- fix: refs #7197 rollback crudModel by:jorgep
|
||||
- fix: refs #7197 setInvoiceInCorrecition by:jorgep
|
||||
- fix: refs #7197 vat, intrastat, filter and list sections by:jorgep
|
||||
- fix: refs #7323 fix department & email table filter by:Jon
|
||||
- fix: refs #7323 fixed left filter by:Jon
|
||||
- fix: refs #7323 fix workerTimeControl form by:Jon
|
||||
- fix: refs #7401 fix routeForm by:pablone
|
||||
- fix: refs #7401 remove console.log by:pablone
|
||||
- fix: refs CAU 207504 fix itemDiary and logs by:Jon
|
||||
- fix: workerCreate form street field to be always upperCase by:Jon
|
||||
- hotfix: refs CAU #207614 fix sale.concept field by:Jon
|
||||
- refactor: refs #7356 fixed VnTable filters by:Jon
|
||||
- refs #6898 fix by:carlossa
|
||||
- Ticket expedition initial load fix by:wbuezas
|
||||
|
||||
# Version 24.28 - 2024-07-09
|
||||
|
||||
### Added 🆕
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
module.exports = { extends: ['@commitlint/config-conventional'] };
|
10
package.json
10
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "salix-front",
|
||||
"version": "24.34.0",
|
||||
"version": "24.36.0",
|
||||
"description": "Salix frontend",
|
||||
"productName": "Salix",
|
||||
"author": "Verdnatura",
|
||||
|
@ -13,7 +13,10 @@
|
|||
"test:e2e:ci": "cd ../salix && gulp docker && cd ../salix-front && cypress run",
|
||||
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
|
||||
"test:unit": "vitest",
|
||||
"test:unit:ci": "vitest run"
|
||||
"test:unit:ci": "vitest run",
|
||||
"commitlint": "commitlint --edit",
|
||||
"prepare": "npx husky install",
|
||||
"addReferenceTag": "node .husky/addReferenceTag.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@quasar/cli": "^2.3.0",
|
||||
|
@ -29,6 +32,8 @@
|
|||
"vue-router": "^4.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.2.1",
|
||||
"@commitlint/config-conventional": "^19.1.0",
|
||||
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
||||
"@pinia/testing": "^0.1.2",
|
||||
"@quasar/app-vite": "^1.7.3",
|
||||
|
@ -41,6 +46,7 @@
|
|||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-cypress": "^2.13.3",
|
||||
"eslint-plugin-vue": "^9.14.1",
|
||||
"husky": "^8.0.0",
|
||||
"postcss": "^8.4.23",
|
||||
"prettier": "^2.8.8",
|
||||
"vitest": "^0.31.1"
|
||||
|
|
5806
pnpm-lock.yaml
5806
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,23 @@
|
|||
import routes from 'src/router/modules';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
export default {
|
||||
mounted: function () {
|
||||
const router = useRouter();
|
||||
const keyBindingMap = routes
|
||||
.filter((route) => route.meta.keyBinding)
|
||||
.reduce((map, route) => {
|
||||
map[route.meta.keyBinding.toLowerCase()] = route.path;
|
||||
return map;
|
||||
}, {});
|
||||
const handleKeyDown = (event) => {
|
||||
const { ctrlKey, altKey, key } = event;
|
||||
event.preventDefault();
|
||||
if (ctrlKey && altKey && keyBindingMap[key]) {
|
||||
router.push(keyBindingMap[key]);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
},
|
||||
};
|
|
@ -1,6 +1,8 @@
|
|||
import { boot } from 'quasar/wrappers';
|
||||
import qFormMixin from './qformMixin';
|
||||
import mainShortcutMixin from './mainShortcutMixin';
|
||||
|
||||
export default boot(({ app }) => {
|
||||
app.mixin(qFormMixin);
|
||||
app.mixin(mainShortcutMixin);
|
||||
});
|
||||
|
|
|
@ -22,7 +22,7 @@ const { t } = useI18n();
|
|||
const { validate } = useValidator();
|
||||
const { notify } = useNotify();
|
||||
const route = useRoute();
|
||||
|
||||
const myForm = ref(null);
|
||||
const $props = defineProps({
|
||||
url: {
|
||||
type: String,
|
||||
|
@ -87,6 +87,10 @@ const $props = defineProps({
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
defaultTrim: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['onFetch', 'onDataSaved']);
|
||||
const modelValue = computed(
|
||||
|
@ -100,17 +104,19 @@ const isResetting = ref(false);
|
|||
const hasChanges = ref(!$props.observeFormChanges);
|
||||
const originalData = ref({});
|
||||
const formData = computed(() => state.get(modelValue));
|
||||
const formUrl = computed(() => $props.url);
|
||||
const defaultButtons = computed(() => ({
|
||||
save: {
|
||||
color: 'primary',
|
||||
icon: 'save',
|
||||
label: 'globals.save',
|
||||
click: () => myForm.value.submit(),
|
||||
type: 'submit',
|
||||
},
|
||||
reset: {
|
||||
color: 'primary',
|
||||
icon: 'restart_alt',
|
||||
label: 'globals.reset',
|
||||
click: () => reset(),
|
||||
},
|
||||
...$props.defaultButtons,
|
||||
}));
|
||||
|
@ -148,11 +154,14 @@ if (!$props.url)
|
|||
(val) => updateAndEmit('onFetch', val)
|
||||
);
|
||||
|
||||
watch(formUrl, async () => {
|
||||
watch(
|
||||
() => [$props.url, $props.filter],
|
||||
async () => {
|
||||
originalData.value = null;
|
||||
reset();
|
||||
await fetch();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
onBeforeRouteLeave((to, from, next) => {
|
||||
if (hasChanges.value && $props.observeFormChanges)
|
||||
|
@ -193,6 +202,7 @@ async function save() {
|
|||
|
||||
isLoading.value = true;
|
||||
try {
|
||||
formData.value = trimData(formData.value);
|
||||
const body = $props.mapper ? $props.mapper(formData.value) : formData.value;
|
||||
const method = $props.urlCreate ? 'post' : 'patch';
|
||||
const url =
|
||||
|
@ -251,6 +261,14 @@ function updateAndEmit(evt, val, res) {
|
|||
emit(evt, state.get(modelValue), res);
|
||||
}
|
||||
|
||||
function trimData(data) {
|
||||
if (!$props.defaultTrim) return data;
|
||||
for (const key in data) {
|
||||
if (typeof data[key] == 'string') data[key] = data[key].trim();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
save,
|
||||
isLoading,
|
||||
|
@ -261,7 +279,14 @@ defineExpose({
|
|||
</script>
|
||||
<template>
|
||||
<div class="column items-center full-width">
|
||||
<QForm @submit="save" @reset="reset" class="q-pa-md" id="formModel">
|
||||
<QForm
|
||||
ref="myForm"
|
||||
v-if="formData"
|
||||
@submit="save"
|
||||
@reset="reset"
|
||||
class="q-pa-md"
|
||||
id="formModel"
|
||||
>
|
||||
<QCard>
|
||||
<slot
|
||||
v-if="formData"
|
||||
|
@ -289,7 +314,7 @@ defineExpose({
|
|||
:color="defaultButtons.reset.color"
|
||||
:icon="defaultButtons.reset.icon"
|
||||
flat
|
||||
@click="reset"
|
||||
@click="defaultButtons.reset.click"
|
||||
:disable="!hasChanges"
|
||||
:title="t(defaultButtons.reset.label)"
|
||||
/>
|
||||
|
@ -329,7 +354,7 @@ defineExpose({
|
|||
:label="tMobile('globals.save')"
|
||||
color="primary"
|
||||
icon="save"
|
||||
@click="save"
|
||||
@click="defaultButtons.save.click"
|
||||
:disable="!hasChanges"
|
||||
:title="t(defaultButtons.save.label)"
|
||||
/>
|
||||
|
|
|
@ -7,7 +7,7 @@ import { useQuasar } from 'quasar';
|
|||
import PinnedModules from './PinnedModules.vue';
|
||||
import UserPanel from 'components/UserPanel.vue';
|
||||
import VnBreadcrumbs from './common/VnBreadcrumbs.vue';
|
||||
import VnImg from 'src/components/ui/VnImg.vue';
|
||||
import VnAvatar from './ui/VnAvatar.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const stateStore = useStateStore();
|
||||
|
@ -72,22 +72,13 @@ const pinnedModulesRef = ref();
|
|||
</QTooltip>
|
||||
<PinnedModules ref="pinnedModulesRef" />
|
||||
</QBtn>
|
||||
<QBtn
|
||||
:class="{ 'q-pa-none': quasar.platform.is.mobile }"
|
||||
rounded
|
||||
dense
|
||||
flat
|
||||
no-wrap
|
||||
id="user"
|
||||
>
|
||||
<QAvatar size="lg">
|
||||
<VnImg
|
||||
:id="user.id"
|
||||
collection="user"
|
||||
size="160x160"
|
||||
:zoom-size="null"
|
||||
<QBtn class="q-pa-none" rounded dense flat no-wrap id="user">
|
||||
<VnAvatar
|
||||
:worker-id="user.id"
|
||||
:title="user.name"
|
||||
size="lg"
|
||||
color="transparent"
|
||||
/>
|
||||
</QAvatar>
|
||||
<QTooltip bottom>
|
||||
{{ t('globals.userPanel') }}
|
||||
</QTooltip>
|
||||
|
|
|
@ -11,8 +11,8 @@ import VnSelect from 'src/components/common/VnSelect.vue';
|
|||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import { useClipboard } from 'src/composables/useClipboard';
|
||||
import VnImg from 'src/components/ui/VnImg.vue';
|
||||
import { useRole } from 'src/composables/useRole';
|
||||
import VnAvatar from './ui/VnAvatar.vue';
|
||||
|
||||
const state = useState();
|
||||
const session = useSession();
|
||||
|
@ -136,7 +136,7 @@ const isEmployee = computed(() => useRole().isEmployee());
|
|||
@update:model-value="saveLanguage"
|
||||
:label="t(`globals.lang['${userLocale}']`)"
|
||||
icon="public"
|
||||
color="orange"
|
||||
color="primary"
|
||||
false-value="es"
|
||||
true-value="en"
|
||||
/>
|
||||
|
@ -145,7 +145,7 @@ const isEmployee = computed(() => useRole().isEmployee());
|
|||
@update:model-value="saveDarkMode"
|
||||
:label="t(`globals.darkMode`)"
|
||||
checked-icon="dark_mode"
|
||||
color="orange"
|
||||
color="primary"
|
||||
unchecked-icon="light_mode"
|
||||
/>
|
||||
</div>
|
||||
|
@ -153,10 +153,20 @@ const isEmployee = computed(() => useRole().isEmployee());
|
|||
<QSeparator vertical inset class="q-mx-lg" />
|
||||
|
||||
<div class="col column items-center q-mb-sm">
|
||||
<QAvatar size="80px">
|
||||
<VnImg :id="user.id" collection="user" size="160x160" />
|
||||
</QAvatar>
|
||||
|
||||
<VnAvatar
|
||||
:worker-id="user.id"
|
||||
:title="user.name"
|
||||
size="xxl"
|
||||
color="transparent"
|
||||
/>
|
||||
<QBtn
|
||||
v-if="isEmployee"
|
||||
class="q-mt-sm q-px-md"
|
||||
:to="`/worker/${user.id}`"
|
||||
color="primary"
|
||||
:label="t('globals.myAccount')"
|
||||
dense
|
||||
/>
|
||||
<div class="text-subtitle1 q-mt-md">
|
||||
<strong>{{ user.nickname }}</strong>
|
||||
</div>
|
||||
|
@ -168,7 +178,7 @@ const isEmployee = computed(() => useRole().isEmployee());
|
|||
</div>
|
||||
<QBtn
|
||||
id="logout"
|
||||
color="orange"
|
||||
color="primary"
|
||||
flat
|
||||
:label="t('globals.logOut')"
|
||||
size="sm"
|
||||
|
|
|
@ -151,7 +151,7 @@ const col = computed(() => {
|
|||
};
|
||||
}
|
||||
if (
|
||||
(newColumn.name.startsWith('is') || newColumn.name.startsWith('has')) &&
|
||||
(/^is[A-Z]/.test(newColumn.name) || /^has[A-Z]/.test(newColumn.name)) &&
|
||||
newColumn.component == null
|
||||
)
|
||||
newColumn.component = 'checkbox';
|
||||
|
|
|
@ -108,7 +108,9 @@ const orders = ref(parseOrder(routeQuery.filter?.order));
|
|||
const CrudModelRef = ref({});
|
||||
const showForm = ref(false);
|
||||
const splittedColumns = ref({ columns: [] });
|
||||
const columnsVisibilitySkipped = ref();
|
||||
const columnsVisibilitySkippped = ref();
|
||||
const createForm = ref();
|
||||
|
||||
const tableModes = [
|
||||
{
|
||||
icon: 'view_column',
|
||||
|
@ -140,6 +142,14 @@ onMounted(() => {
|
|||
.map((c) => c.name),
|
||||
...['tableActions'],
|
||||
];
|
||||
createForm.value = $props.create;
|
||||
if ($props.create && route?.query?.createForm) {
|
||||
showForm.value = true;
|
||||
createForm.value = {
|
||||
...createForm.value,
|
||||
...{ formInitialData: JSON.parse(route?.query?.createForm) },
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
|
@ -155,24 +165,34 @@ watch(
|
|||
|
||||
const isTableMode = computed(() => mode.value == TABLE_MODE);
|
||||
|
||||
function setUserParams(watchedParams) {
|
||||
function setUserParams(watchedParams, watchedOrder) {
|
||||
if (!watchedParams) return;
|
||||
|
||||
if (typeof watchedParams == 'string') watchedParams = JSON.parse(watchedParams);
|
||||
const filter =
|
||||
typeof watchedParams?.filter == 'string'
|
||||
? JSON.parse(watchedParams?.filter)
|
||||
? JSON.parse(watchedParams?.filter ?? '{}')
|
||||
: watchedParams?.filter;
|
||||
const where = filter?.where;
|
||||
const order = filter?.order;
|
||||
const order = watchedOrder ?? filter?.order;
|
||||
|
||||
watchedParams = { ...watchedParams, ...where };
|
||||
delete watchedParams.filter;
|
||||
delete params.value?.filter;
|
||||
params.value = { ...params.value, ...watchedParams };
|
||||
params.value = { ...params.value, ...sanitizer(watchedParams) };
|
||||
orders.value = parseOrder(order);
|
||||
}
|
||||
|
||||
function sanitizer(params) {
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (typeof value == 'object') {
|
||||
const param = Object.values(value)[0];
|
||||
if (typeof param == 'string') params[key] = param.replaceAll('%', '');
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
function splitColumns(columns) {
|
||||
splittedColumns.value = {
|
||||
columns: [],
|
||||
|
@ -306,7 +326,7 @@ defineExpose({
|
|||
col?.columnFilter !== false &&
|
||||
col?.name !== 'tableActions'
|
||||
"
|
||||
v-model="orders[col.name]"
|
||||
v-model="orders[col.orderBy ?? col.name]"
|
||||
:name="col.orderBy ?? col.name"
|
||||
:data-key="$attrs['data-key']"
|
||||
:search-url="searchUrl"
|
||||
|
@ -323,23 +343,20 @@ defineExpose({
|
|||
</QScrollArea>
|
||||
</QDrawer>
|
||||
<!-- class in div to fix warn-->
|
||||
<div class="q-px-md">
|
||||
|
||||
<CrudModel
|
||||
v-bind="$attrs"
|
||||
:limit="$attrs.limit ?? 20"
|
||||
:class="$attrs['class'] ?? 'q-px-md'"
|
||||
:limit="20"
|
||||
ref="CrudModelRef"
|
||||
@on-fetch="(...args) => emit('onFetch', ...args)"
|
||||
:search-url="searchUrl"
|
||||
:disable-infinite-scroll="$attrs['disable-infinite-scroll'] ?? isTableMode"
|
||||
:disable-infinite-scroll="isTableMode"
|
||||
@save-changes="reload"
|
||||
:has-sub-toolbar="$attrs['hasSubToolbar'] ?? isEditable"
|
||||
:auto-load="hasParams || $attrs['auto-load']"
|
||||
>
|
||||
<template
|
||||
v-for="(_, slotName) in $slots"
|
||||
#[slotName]="slotData"
|
||||
:key="slotName"
|
||||
>
|
||||
<template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName">
|
||||
<slot :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
|
||||
</template>
|
||||
<template #body="{ rows }">
|
||||
|
@ -352,8 +369,9 @@ defineExpose({
|
|||
:grid="!isTableMode"
|
||||
table-header-class="bg-header"
|
||||
card-container-class="grid-three"
|
||||
flat
|
||||
:style="isTableMode && `max-height: ${tableHeight}`"
|
||||
:virtual-scroll="!isTableMode"
|
||||
virtual-scroll
|
||||
@virtual-scroll="
|
||||
(event) =>
|
||||
event.index > rows.length - 2 &&
|
||||
|
@ -361,7 +379,6 @@ defineExpose({
|
|||
"
|
||||
@row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
|
||||
@update:selected="emit('update:selected', $event)"
|
||||
flat
|
||||
>
|
||||
<template #top-left v-if="!$props.withoutHeader">
|
||||
<slot name="top-left"></slot>
|
||||
|
@ -378,12 +395,11 @@ defineExpose({
|
|||
toggle-color="primary"
|
||||
class="bg-vn-section-color"
|
||||
dense
|
||||
:options="tableModes"
|
||||
:options="tableModes.filter((mode) => !mode.disable)"
|
||||
/>
|
||||
<QBtn
|
||||
v-if="$props.rightSearch"
|
||||
icon="filter_alt"
|
||||
title="asd"
|
||||
class="bg-vn-section-color q-ml-md"
|
||||
dense
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
|
@ -396,12 +412,10 @@ defineExpose({
|
|||
:class="`text-${col?.align ?? 'left'}`"
|
||||
:style="$props.columnSearch ? 'height: 75px' : ''"
|
||||
>
|
||||
<div
|
||||
class="row items-center no-wrap"
|
||||
style="height: 30px"
|
||||
>
|
||||
<div class="row items-center no-wrap" style="height: 30px">
|
||||
<QTooltip v-if="col.toolTip">{{ col.toolTip }}</QTooltip>
|
||||
<VnTableOrder
|
||||
v-model="orders[col.name]"
|
||||
v-model="orders[col.orderBy ?? col.name]"
|
||||
:name="col.orderBy ?? col.name"
|
||||
:label="col?.label"
|
||||
:data-key="$attrs['data-key']"
|
||||
|
@ -425,10 +439,7 @@ defineExpose({
|
|||
</template>
|
||||
<template #body-cell-tableStatus="{ col, row }">
|
||||
<QTd auto-width :class="getColAlign(col)">
|
||||
<VnTableChip
|
||||
:columns="splittedColumns.columnChips"
|
||||
:row="row"
|
||||
>
|
||||
<VnTableChip :columns="splittedColumns.columnChips" :row="row">
|
||||
<template #afterChip>
|
||||
<slot name="afterChip" :row="row"></slot>
|
||||
</template>
|
||||
|
@ -444,8 +455,7 @@ defineExpose({
|
|||
v-if="col.visible ?? true"
|
||||
@click.ctrl="
|
||||
($event) =>
|
||||
rowCtrlClickFunction &&
|
||||
rowCtrlClickFunction($event, row)
|
||||
rowCtrlClickFunction && rowCtrlClickFunction($event, row)
|
||||
"
|
||||
>
|
||||
<slot
|
||||
|
@ -479,14 +489,10 @@ defineExpose({
|
|||
class="q-px-sm"
|
||||
flat
|
||||
:class="
|
||||
btn.isPrimary
|
||||
? 'text-primary-light'
|
||||
: 'color-vn-text '
|
||||
btn.isPrimary ? 'text-primary-light' : 'color-vn-text '
|
||||
"
|
||||
:style="`visibility: ${
|
||||
(btn.show && btn.show(row)) ?? true
|
||||
? 'visible'
|
||||
: 'hidden'
|
||||
(btn.show && btn.show(row)) ?? true ? 'visible' : 'hidden'
|
||||
}`"
|
||||
@click="btn.action(row)"
|
||||
/>
|
||||
|
@ -560,9 +566,7 @@ defineExpose({
|
|||
>
|
||||
<template #value>
|
||||
<span
|
||||
@click="
|
||||
stopEventPropagation($event)
|
||||
"
|
||||
@click="stopEventPropagation($event)"
|
||||
>
|
||||
<slot
|
||||
:name="`column-${col.name}`"
|
||||
|
@ -610,40 +614,20 @@ defineExpose({
|
|||
</QCard>
|
||||
</component>
|
||||
</template>
|
||||
<template #bottom-row="{ cols }">
|
||||
<QTr>
|
||||
<QTh
|
||||
v-for="col of cols.filter((cols) => cols.visible ?? true)"
|
||||
:key="col.id"
|
||||
class="text-center"
|
||||
>
|
||||
<span v-if="col.summation">
|
||||
{{
|
||||
rows.reduce(
|
||||
(sum, currentRow) =>
|
||||
sum + currentRow[col.name],
|
||||
0
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
</QTh>
|
||||
</QTr>
|
||||
</template>
|
||||
</QTable>
|
||||
</template>
|
||||
</CrudModel>
|
||||
</div>
|
||||
<QPageSticky v-if="create" :offset="[20, 20]" style="z-index: 2">
|
||||
<QBtn @click="showForm = !showForm" color="primary" fab icon="add" />
|
||||
<QTooltip>
|
||||
{{ create.title }}
|
||||
{{ createForm.title }}
|
||||
</QTooltip>
|
||||
</QPageSticky>
|
||||
<QDialog v-model="showForm" transition-show="scale" transition-hide="scale">
|
||||
<FormModelPopup
|
||||
v-bind="create"
|
||||
v-bind="createForm"
|
||||
:model="$attrs['data-key'] + 'Create'"
|
||||
@on-data-saved="(_, res) => create.onDataSaved(res)"
|
||||
@on-data-saved="(_, res) => createForm.onDataSaved(res)"
|
||||
>
|
||||
<template #form-inputs="{ data }">
|
||||
<div class="grid-create">
|
||||
|
|
|
@ -65,7 +65,7 @@ async function fetchViewConfigData() {
|
|||
const userConfig = await getConfig('UserConfigViews', {
|
||||
where: {
|
||||
...defaultFilter.where,
|
||||
...{ userFk: user.id },
|
||||
...{ userFk: user.value.id },
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ const stateStore = useStateStore();
|
|||
</div>
|
||||
</Teleport>
|
||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<QScrollArea class="fit text-grey-8">
|
||||
<QScrollArea class="fit">
|
||||
<div id="right-panel"></div>
|
||||
<slot v-if="!hasContent" name="right-panel" />
|
||||
</QScrollArea>
|
||||
|
|
|
@ -8,7 +8,6 @@ import VnSubToolbar from '../ui/VnSubToolbar.vue';
|
|||
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||
import LeftMenu from 'components/LeftMenu.vue';
|
||||
import RightMenu from 'components/common/RightMenu.vue';
|
||||
|
||||
const props = defineProps({
|
||||
dataKey: { type: String, required: true },
|
||||
baseUrl: { type: String, default: undefined },
|
||||
|
@ -17,12 +16,7 @@ const props = defineProps({
|
|||
descriptor: { type: Object, required: true },
|
||||
filterPanel: { type: Object, default: undefined },
|
||||
searchDataKey: { type: String, default: undefined },
|
||||
searchUrl: { type: String, default: undefined },
|
||||
searchbarLabel: { type: String, default: '' },
|
||||
searchbarInfo: { type: String, default: '' },
|
||||
searchCustomRouteRedirect: { type: String, default: undefined },
|
||||
searchRedirect: { type: Boolean, default: true },
|
||||
searchMakeFetch: { type: Boolean, default: true },
|
||||
searchbarProps: { type: Object, default: undefined },
|
||||
});
|
||||
|
||||
const stateStore = useStateStore();
|
||||
|
@ -31,7 +25,10 @@ const url = computed(() => {
|
|||
if (props.baseUrl) return `${props.baseUrl}/${route.params.id}`;
|
||||
return props.customUrl;
|
||||
});
|
||||
|
||||
const searchRightDataKey = computed(() => {
|
||||
if (!props.searchDataKey) return route.name;
|
||||
return props.searchDataKey;
|
||||
});
|
||||
const arrayData = useArrayData(props.dataKey, {
|
||||
url: url.value,
|
||||
filter: props.filter,
|
||||
|
@ -65,26 +62,18 @@ if (props.baseUrl) {
|
|||
</QScrollArea>
|
||||
</QDrawer>
|
||||
<slot name="searchbar" v-if="props.searchDataKey">
|
||||
<VnSearchbar
|
||||
:data-key="props.searchDataKey"
|
||||
:url="props.searchUrl"
|
||||
:label="props.searchbarLabel"
|
||||
:info="props.searchbarInfo"
|
||||
:custom-route-redirect-name="searchCustomRouteRedirect"
|
||||
:redirect="searchRedirect"
|
||||
/>
|
||||
<VnSearchbar :data-key="props.searchDataKey" v-bind="props.searchbarProps" />
|
||||
</slot>
|
||||
<slot v-else name="searchbar" />
|
||||
<RightMenu>
|
||||
<template #right-panel v-if="props.filterPanel">
|
||||
<component :is="props.filterPanel" :data-key="props.searchDataKey" />
|
||||
<component :is="props.filterPanel" :data-key="searchRightDataKey" />
|
||||
</template>
|
||||
</RightMenu>
|
||||
<QPageContainer>
|
||||
<QPage>
|
||||
<VnSubToolbar />
|
||||
<div :class="[useCardSize(), $attrs.class]">
|
||||
<RouterView />
|
||||
<RouterView :key="route.fullPath" />
|
||||
</div>
|
||||
</QPage>
|
||||
</QPageContainer>
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useCapitalize } from 'src/composables/useCapitalize';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: [String, Number], default: '' },
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const amount = computed({
|
||||
get() {
|
||||
return props.modelValue;
|
||||
},
|
||||
set(val) {
|
||||
emit('update:modelValue', val);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<VnInput
|
||||
v-model="amount"
|
||||
type="number"
|
||||
step="any"
|
||||
:label="useCapitalize(t('amount'))"
|
||||
/>
|
||||
</template>
|
||||
<i18n>
|
||||
es:
|
||||
amount: importe
|
||||
</i18n>
|
|
@ -1,6 +1,7 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useValidator } from 'src/composables/useValidator';
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:modelValue',
|
||||
|
@ -27,9 +28,11 @@ const $props = defineProps({
|
|||
default: true,
|
||||
},
|
||||
});
|
||||
const { validations } = useValidator();
|
||||
|
||||
const { t } = useI18n();
|
||||
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
|
||||
const requiredFieldRule = (val) => validations().required($attrs.required, val);
|
||||
|
||||
const vnInputRef = ref(null);
|
||||
const value = computed({
|
||||
get() {
|
||||
|
@ -57,21 +60,22 @@ const focus = () => {
|
|||
defineExpose({
|
||||
focus,
|
||||
});
|
||||
import { useAttrs } from 'vue';
|
||||
const $attrs = useAttrs();
|
||||
|
||||
const inputRules = [
|
||||
const mixinRules = [
|
||||
requiredFieldRule,
|
||||
...($attrs.rules ?? []),
|
||||
(val) => {
|
||||
const { min } = vnInputRef.value.$attrs;
|
||||
if (!min) return null;
|
||||
if (min >= 0) if (Math.floor(val) < min) return t('inputMin', { value: min });
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
@mouseover="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
:rules="$attrs.required ? [requiredFieldRule] : null"
|
||||
>
|
||||
<div @mouseover="hover = true" @mouseleave="hover = false">
|
||||
<QInput
|
||||
ref="vnInputRef"
|
||||
v-model="value"
|
||||
|
@ -80,7 +84,7 @@ const inputRules = [
|
|||
:class="{ required: $attrs.required }"
|
||||
@keyup.enter="emit('keyup.enter')"
|
||||
:clearable="false"
|
||||
:rules="inputRules"
|
||||
:rules="mixinRules"
|
||||
:lazy-rules="true"
|
||||
hide-bottom-space
|
||||
>
|
||||
|
|
|
@ -3,7 +3,7 @@ import { onMounted, watch, computed, ref } from 'vue';
|
|||
import { date } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const model = defineModel({ type: String });
|
||||
const model = defineModel({ type: [String, Date] });
|
||||
const $props = defineProps({
|
||||
isOutlined: {
|
||||
type: Boolean,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { watch, computed, ref, nextTick } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { date } from 'quasar';
|
||||
|
||||
|
@ -14,13 +14,13 @@ const props = defineProps({
|
|||
default: false,
|
||||
},
|
||||
});
|
||||
const initialDate = ref(model.value ?? Date.vnNew());
|
||||
const { t } = useI18n();
|
||||
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
|
||||
|
||||
const dateFormat = 'HH:mm';
|
||||
const isPopupOpen = ref();
|
||||
const hover = ref();
|
||||
const inputRef = ref();
|
||||
|
||||
const styleAttrs = computed(() => {
|
||||
return props.isOutlined
|
||||
|
@ -50,7 +50,8 @@ const formattedTime = computed({
|
|||
}
|
||||
if (!props.timeOnly) {
|
||||
const [hh, mm] = time.split(':');
|
||||
const date = model.value ?? Date.vnNew();
|
||||
|
||||
const date = new Date(model.value ? model.value : initialDate.value);
|
||||
date.setHours(hh, mm, 0);
|
||||
time = date?.toISOString();
|
||||
}
|
||||
|
@ -62,37 +63,10 @@ const formattedTime = computed({
|
|||
function dateToTime(newDate) {
|
||||
return date.formatDate(new Date(newDate), dateFormat);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => model.value,
|
||||
(val) => (formattedTime.value = val),
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => formattedTime.value,
|
||||
async (val) => {
|
||||
let position = 3;
|
||||
const input = inputRef.value?.getNativeElement();
|
||||
if (!val || !input) return;
|
||||
|
||||
let [hh, mm] = val.split(':');
|
||||
hh = parseInt(hh);
|
||||
if (hh >= 10 || mm != '00') return;
|
||||
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
if (!hh) position = 0;
|
||||
input.setSelectionRange(position, position);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div @mouseover="hover = true" @mouseleave="hover = false">
|
||||
<QInput
|
||||
ref="inputRef"
|
||||
class="vn-input-time"
|
||||
mask="##:##"
|
||||
placeholder="--:--"
|
||||
|
@ -102,7 +76,7 @@ watch(
|
|||
style="min-width: 100px"
|
||||
:rules="$attrs.required ? [requiredFieldRule] : null"
|
||||
@click="isPopupOpen = false"
|
||||
@focus="inputRef.getNativeElement().setSelectionRange(0, 0)"
|
||||
type="time"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon
|
||||
|
@ -149,8 +123,12 @@ watch(
|
|||
border-style: solid;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
:deep(input[type='time']::-webkit-calendar-picker-indicator) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
es:
|
||||
Open time: Abrir tiempo
|
||||
</i18n>
|
||||
, nextTick
|
||||
|
|
|
@ -214,7 +214,7 @@ function getLogTree(data) {
|
|||
}
|
||||
nLogs++;
|
||||
modelLog.logs.push(log);
|
||||
|
||||
modelLog.summaryId = modelLog.logs[0].summaryId;
|
||||
// Changes
|
||||
const notDelete = log.action != 'delete';
|
||||
const olds = (notDelete ? log.oldInstance : null) || {};
|
||||
|
@ -407,9 +407,12 @@ watch(
|
|||
@on-fetch="
|
||||
(data) =>
|
||||
(actions = data.map((item) => {
|
||||
const changedModel = item.changedModel;
|
||||
return {
|
||||
locale: useCapitalize(validations[item.changedModel].locale.name),
|
||||
value: item.changedModel,
|
||||
locale: useCapitalize(
|
||||
validations[changedModel]?.locale?.name ?? changedModel
|
||||
),
|
||||
value: changedModel,
|
||||
};
|
||||
}))
|
||||
"
|
||||
|
@ -472,12 +475,17 @@ watch(
|
|||
>
|
||||
{{ t(modelLog.modelI18n) }}
|
||||
</QChip>
|
||||
<span class="model-id" v-if="modelLog.summaryId"
|
||||
>#{{ modelLog.summaryId }}</span
|
||||
>
|
||||
<span class="model-value" :title="modelLog.showValue">
|
||||
{{ modelLog.showValue }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
class="model-id q-mr-xs"
|
||||
v-if="modelLog.summaryId"
|
||||
v-text="`#${modelLog.summaryId}`"
|
||||
/>
|
||||
<span
|
||||
class="model-value"
|
||||
:title="modelLog.showValue"
|
||||
v-text="modelLog.showValue"
|
||||
/>
|
||||
<QBtn
|
||||
flat
|
||||
round
|
||||
|
|
|
@ -73,6 +73,14 @@ const $props = defineProps({
|
|||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
params: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
noOne: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
@ -85,6 +93,11 @@ const myOptionsOriginal = ref([]);
|
|||
const vnSelectRef = ref();
|
||||
const dataRef = ref();
|
||||
const lastVal = ref();
|
||||
const noOneText = t('globals.noOne');
|
||||
const noOneOpt = ref({
|
||||
[optionValue.value]: false,
|
||||
[optionLabel.value]: noOneText,
|
||||
});
|
||||
|
||||
const value = computed({
|
||||
get() {
|
||||
|
@ -100,9 +113,11 @@ watch(options, (newValue) => {
|
|||
setOptions(newValue);
|
||||
});
|
||||
|
||||
watch(modelValue, (newValue) => {
|
||||
watch(modelValue, async (newValue) => {
|
||||
if (!myOptions.value.some((option) => option[optionValue.value] == newValue))
|
||||
fetchFilter(newValue);
|
||||
await fetchFilter(newValue);
|
||||
|
||||
if ($props.noOne) myOptions.value.unshift(noOneOpt.value);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -153,13 +168,19 @@ async function fetchFilter(val) {
|
|||
? optionValue.value
|
||||
: optionFilter.value ?? optionLabel.value);
|
||||
|
||||
const defaultWhere = $props.useLike
|
||||
? { [key]: { like: `%${val}%` } }
|
||||
: { [key]: val };
|
||||
let defaultWhere = {};
|
||||
if ($props.filterOptions.length) {
|
||||
defaultWhere = $props.filterOptions.reduce((obj, prop) => {
|
||||
if (!obj.or) obj.or = [];
|
||||
obj.or.push({ [prop]: getVal(val) });
|
||||
return obj;
|
||||
}, {});
|
||||
} else defaultWhere = { [key]: getVal(val) };
|
||||
const where = { ...(val ? defaultWhere : {}), ...$props.where };
|
||||
const fetchOptions = { where, include, limit };
|
||||
if (fields) fetchOptions.fields = fields;
|
||||
if (sortBy) fetchOptions.order = sortBy;
|
||||
|
||||
return dataRef.value.fetch(fetchOptions);
|
||||
}
|
||||
|
||||
|
@ -180,6 +201,9 @@ async function filterHandler(val, update) {
|
|||
} else newOptions = filter(val, myOptionsOriginal.value);
|
||||
update(
|
||||
() => {
|
||||
if ($props.noOne && noOneText.toLowerCase().includes(val.toLowerCase()))
|
||||
newOptions.unshift(noOneOpt.value);
|
||||
|
||||
myOptions.value = newOptions;
|
||||
},
|
||||
(ref) => {
|
||||
|
@ -194,6 +218,8 @@ async function filterHandler(val, update) {
|
|||
function nullishToTrue(value) {
|
||||
return value ?? true;
|
||||
}
|
||||
|
||||
const getVal = (val) => ($props.useLike ? { like: `%${val}%` } : val);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -205,6 +231,7 @@ function nullishToTrue(value) {
|
|||
:limit="limit"
|
||||
:sort-by="sortBy"
|
||||
:fields="fields"
|
||||
:params="params"
|
||||
/>
|
||||
<QSelect
|
||||
v-model="value"
|
||||
|
|
|
@ -31,7 +31,7 @@ const dialog = ref(null);
|
|||
<div class="container order-catalog-item overflow-hidden">
|
||||
<QCard class="card shadow-6">
|
||||
<div class="img-wrapper">
|
||||
<VnImg :id="item.id" zoom-size="lg" class="image" />
|
||||
<VnImg :id="item.id" class="image" />
|
||||
<div v-if="item.hex && isCatalog" class="item-color-container">
|
||||
<div
|
||||
class="item-color"
|
||||
|
|
|
@ -73,8 +73,7 @@ const containerClasses = computed(() => {
|
|||
.q-calendar-month__head--workweek,
|
||||
.q-calendar-month__head--weekday,
|
||||
// .q-calendar-month__workweek.q-past-day,
|
||||
.q-calendar-month__week :nth-child(6),
|
||||
:nth-child(7) {
|
||||
.q-calendar-month__week :nth-child(n+6):nth-child(-n+7) {
|
||||
color: var(--vn-label-color);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,45 +1,62 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import { useColor } from 'src/composables/useColor';
|
||||
import { getCssVar } from 'quasar';
|
||||
|
||||
const $props = defineProps({
|
||||
workerId: { type: Number, required: true },
|
||||
description: { type: String, default: null },
|
||||
size: { type: String, default: null },
|
||||
title: { type: String, default: null },
|
||||
color: { type: String, default: null },
|
||||
});
|
||||
|
||||
const { getTokenMultimedia } = useSession();
|
||||
const token = getTokenMultimedia();
|
||||
const { t } = useI18n();
|
||||
|
||||
const title = computed(() => $props.title ?? t('globals.system'));
|
||||
const src = computed(
|
||||
() => `/api/Images/user/160x160/${$props.workerId}/download?access_token=${token}`
|
||||
);
|
||||
const title = computed(() => $props.title?.toUpperCase() || t('globals.system'));
|
||||
const showLetter = ref(false);
|
||||
const backgroundColor = computed(() => {
|
||||
const color = $props.color || useColor(title.value);
|
||||
return getCssVar(color) || color;
|
||||
});
|
||||
|
||||
watch(src, () => (showLetter.value = false));
|
||||
</script>
|
||||
<template>
|
||||
<div class="avatar-picture column items-center">
|
||||
<div class="column items-center">
|
||||
<QAvatar
|
||||
:style="{
|
||||
backgroundColor: useColor(title),
|
||||
}"
|
||||
:size="$props.size"
|
||||
:title="title"
|
||||
:style="{ backgroundColor }"
|
||||
v-bind="$attrs"
|
||||
:title="title || t('globals.system')"
|
||||
>
|
||||
<template v-if="showLetter">{{ title.charAt(0) }}</template>
|
||||
<QImg
|
||||
v-else
|
||||
:src="`/api/Images/user/160x160/${$props.workerId}/download?access_token=${token}`"
|
||||
spinner-color="white"
|
||||
@error="showLetter = true"
|
||||
/>
|
||||
<template v-if="showLetter">
|
||||
{{ title.charAt(0) }}
|
||||
</template>
|
||||
<QImg v-else :src="src" spinner-color="white" @error="showLetter = true" />
|
||||
</QAvatar>
|
||||
<div class="description">
|
||||
<slot name="description" v-if="$props.description">
|
||||
<p>
|
||||
{{ $props.description }}
|
||||
</p>
|
||||
<slot name="description" v-if="description">
|
||||
<p v-text="description" />
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
[size='xxl'] {
|
||||
.q-avatar,
|
||||
.q-img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.q-img {
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -15,7 +15,7 @@ const props = defineProps({
|
|||
default: null,
|
||||
},
|
||||
message: {
|
||||
type: String,
|
||||
type: [String, Boolean],
|
||||
default: null,
|
||||
},
|
||||
data: {
|
||||
|
@ -35,7 +35,10 @@ defineEmits(['confirm', ...useDialogPluginComponent.emits]);
|
|||
const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
||||
|
||||
const title = props.title || t('Confirm');
|
||||
const message = props.message || t('Are you sure you want to continue?');
|
||||
const message =
|
||||
props.message ||
|
||||
(props.message !== false ? t('Are you sure you want to continue?') : false);
|
||||
|
||||
const isLoading = ref(false);
|
||||
|
||||
async function confirm() {
|
||||
|
@ -61,12 +64,12 @@ async function confirm() {
|
|||
size="xl"
|
||||
v-if="icon"
|
||||
/>
|
||||
<span class="text-h6 text-grey">{{ title }}</span>
|
||||
<span class="text-h6">{{ title }}</span>
|
||||
<QSpace />
|
||||
<QBtn icon="close" :disable="isLoading" flat round dense v-close-popup />
|
||||
</QCardSection>
|
||||
<QCardSection class="row items-center">
|
||||
<span v-html="message"></span>
|
||||
<span v-if="message !== false" v-html="message" />
|
||||
<slot name="customHTML"></slot>
|
||||
</QCardSection>
|
||||
<QCardActions align="right">
|
||||
|
|
|
@ -24,7 +24,7 @@ const $props = defineProps({
|
|||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
unRemovableParams: {
|
||||
unremovableParams: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [],
|
||||
|
@ -37,7 +37,7 @@ const $props = defineProps({
|
|||
},
|
||||
hiddenTags: {
|
||||
type: Array,
|
||||
default: () => ['filter'],
|
||||
default: () => ['filter', 'search', 'or', 'and'],
|
||||
},
|
||||
customTags: {
|
||||
type: Array,
|
||||
|
@ -82,23 +82,28 @@ onMounted(() => {
|
|||
});
|
||||
|
||||
function setUserParams(watchedParams) {
|
||||
if (!watchedParams) return;
|
||||
if (!watchedParams || Object.keys(watchedParams).length == 0) return;
|
||||
|
||||
if (typeof watchedParams == 'string') watchedParams = JSON.parse(watchedParams);
|
||||
if (typeof watchedParams?.filter == 'string')
|
||||
watchedParams.filter = JSON.parse(watchedParams.filter);
|
||||
|
||||
watchedParams = { ...watchedParams, ...watchedParams.filter?.where };
|
||||
const order = watchedParams.filter?.order;
|
||||
|
||||
delete watchedParams.filter;
|
||||
userParams.value = { ...userParams.value, ...watchedParams };
|
||||
emit('setUserParams', userParams.value);
|
||||
userParams.value = sanitizer(watchedParams);
|
||||
emit('setUserParams', userParams.value, order);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.query[$props.searchUrl],
|
||||
(val) => setUserParams(val)
|
||||
(val, oldValue) => (val || oldValue) && setUserParams(val)
|
||||
);
|
||||
|
||||
watch(
|
||||
() => arrayData.store.userParams,
|
||||
(val) => setUserParams(val)
|
||||
(val, oldValue) => (val || oldValue) && setUserParams(val)
|
||||
);
|
||||
|
||||
watch(
|
||||
|
@ -108,6 +113,7 @@ watch(
|
|||
|
||||
const isLoading = ref(false);
|
||||
async function search(evt) {
|
||||
try {
|
||||
if (evt && $props.disableSubmitEvent) return;
|
||||
|
||||
store.filter.where = {};
|
||||
|
@ -120,9 +126,10 @@ async function search(evt) {
|
|||
userParams.value = newParams;
|
||||
|
||||
if (!$props.showAll && !Object.values(filter).length) store.data = [];
|
||||
|
||||
isLoading.value = false;
|
||||
emit('search');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function reload() {
|
||||
|
@ -137,12 +144,13 @@ async function reload() {
|
|||
}
|
||||
|
||||
async function clearFilters() {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
store.userParamsChanged = true;
|
||||
arrayData.reset(['skip', 'filter.skip', 'page']);
|
||||
// Filtrar los params no removibles
|
||||
const removableFilters = Object.keys(userParams.value).filter((param) =>
|
||||
$props.unRemovableParams.includes(param)
|
||||
$props.unremovableParams.includes(param)
|
||||
);
|
||||
const newParams = {};
|
||||
// Conservar solo los params que no son removibles
|
||||
|
@ -156,10 +164,11 @@ async function clearFilters() {
|
|||
if (!$props.showAll) {
|
||||
store.data = [];
|
||||
}
|
||||
|
||||
isLoading.value = false;
|
||||
emit('clear');
|
||||
emit('update:modelValue', userParams.value);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const tagsList = computed(() => {
|
||||
|
@ -173,10 +182,10 @@ const tagsList = computed(() => {
|
|||
});
|
||||
|
||||
const tags = computed(() => {
|
||||
return tagsList.value.filter((tag) => !($props.customTags || []).includes(tag.key));
|
||||
return tagsList.value.filter((tag) => !($props.customTags || []).includes(tag.label));
|
||||
});
|
||||
const customTags = computed(() =>
|
||||
tagsList.value.filter((tag) => ($props.customTags || []).includes(tag.key))
|
||||
tagsList.value.filter((tag) => ($props.customTags || []).includes(tag.label))
|
||||
);
|
||||
|
||||
async function remove(key) {
|
||||
|
@ -195,8 +204,10 @@ function formatValue(value) {
|
|||
|
||||
function sanitizer(params) {
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (typeof value == 'object')
|
||||
params[key] = Object.values(value)[0].replaceAll('%', '');
|
||||
if (value && typeof value === 'object') {
|
||||
const param = Object.values(value)[0];
|
||||
if (typeof param == 'string') params[key] = param.replaceAll('%', '');
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
@ -259,7 +270,7 @@ function sanitizer(params) {
|
|||
<VnFilterPanelChip
|
||||
v-for="chip of tags"
|
||||
:key="chip.label"
|
||||
:removable="!unRemovableParams.includes(chip.label)"
|
||||
:removable="!unremovableParams?.includes(chip.label)"
|
||||
@remove="remove(chip.label)"
|
||||
>
|
||||
<slot name="tags" :tag="chip" :format-fn="formatValue">
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import noImage from '/no-user.png';
|
||||
import { useRole } from 'src/composables/useRole';
|
||||
|
||||
const $props = defineProps({
|
||||
storage: {
|
||||
|
@ -11,14 +13,17 @@ const $props = defineProps({
|
|||
type: String,
|
||||
default: 'catalog',
|
||||
},
|
||||
size: {
|
||||
resolution: {
|
||||
type: String,
|
||||
default: '200x200',
|
||||
},
|
||||
zoomSize: {
|
||||
zoomResolution: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'lg',
|
||||
default: null,
|
||||
},
|
||||
zoom: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
id: {
|
||||
type: Number,
|
||||
|
@ -28,14 +33,16 @@ const $props = defineProps({
|
|||
const show = ref(false);
|
||||
const token = useSession().getTokenMultimedia();
|
||||
const timeStamp = ref(`timestamp=${Date.now()}`);
|
||||
import noImage from '/no-user.png';
|
||||
import { useRole } from 'src/composables/useRole';
|
||||
const url = computed(() => {
|
||||
const isEmployee = useRole().isEmployee();
|
||||
const isEmployee = useRole().isEmployee();
|
||||
|
||||
const getUrl = (zoom = false) => {
|
||||
const curResolution = zoom
|
||||
? $props.zoomResolution || $props.resolution
|
||||
: $props.resolution;
|
||||
return isEmployee
|
||||
? `/api/${$props.storage}/${$props.collection}/${$props.size}/${$props.id}/download?access_token=${token}&${timeStamp.value}`
|
||||
? `/api/${$props.storage}/${$props.collection}/${curResolution}/${$props.id}/download?access_token=${token}&${timeStamp.value}`
|
||||
: noImage;
|
||||
});
|
||||
};
|
||||
const reload = () => {
|
||||
timeStamp.value = `timestamp=${Date.now()}`;
|
||||
};
|
||||
|
@ -45,23 +52,21 @@ defineExpose({
|
|||
</script>
|
||||
<template>
|
||||
<QImg
|
||||
:class="{ zoomIn: $props.zoomSize }"
|
||||
:src="url"
|
||||
:class="{ zoomIn: zoom }"
|
||||
:src="getUrl()"
|
||||
v-bind="$attrs"
|
||||
@click="show = !show"
|
||||
@click.stop="show = $props.zoom ? true : false"
|
||||
spinner-color="primary"
|
||||
/>
|
||||
<QDialog v-model="show" v-if="$props.zoomSize">
|
||||
<QDialog v-if="$props.zoom" v-model="show">
|
||||
<QImg
|
||||
:src="url"
|
||||
size="full"
|
||||
class="img_zoom"
|
||||
:src="getUrl(true)"
|
||||
v-bind="$attrs"
|
||||
spinner-color="primary"
|
||||
class="img_zoom"
|
||||
/>
|
||||
</QDialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.q-img {
|
||||
&.zoomIn {
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<script setup>
|
||||
const emit = defineEmits(['submit']);
|
||||
defineProps({
|
||||
icon: { type: String, required: false, default: 'phonelink_lock' },
|
||||
title: { type: String, required: true },
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<QForm @submit="emit('submit')" class="q-gutter-y-md q-pa-lg formCard">
|
||||
<div class="column items-center">
|
||||
<QIcon v-if="icon != false" :name="icon" size="xl" color="primary" />
|
||||
<h5 class="text-center q-my-md">
|
||||
{{ title }}
|
||||
</h5>
|
||||
</div>
|
||||
<slot></slot>
|
||||
<div class="q-mt-lg">
|
||||
<slot name="buttons"></slot>
|
||||
</div>
|
||||
</QForm>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.formCard {
|
||||
max-width: 350px;
|
||||
min-width: 300px;
|
||||
}
|
||||
@media (max-width: $breakpoint-xs-max) {
|
||||
.formCard {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -10,6 +10,10 @@ const props = defineProps({
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
class: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
autoLoad: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
|
@ -116,7 +120,7 @@ watch(
|
|||
|
||||
watch(
|
||||
() => [props.url, props.filter],
|
||||
([url, filter]) => fetch({ url, filter })
|
||||
([url, filter]) => mounted.value && fetch({ url, filter })
|
||||
);
|
||||
|
||||
const addFilter = async (filter, params) => {
|
||||
|
@ -215,13 +219,13 @@ defineExpose({ fetch, addFilter, paginate });
|
|||
v-if="store.data"
|
||||
@load="onLoad"
|
||||
:offset="offset"
|
||||
class="full-width"
|
||||
:class="['full-width', props.class]"
|
||||
:disable="disableInfiniteScroll || !store.hasMoreData"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<slot name="body" :rows="store.data"></slot>
|
||||
<div v-if="isLoading" class="info-row q-pa-md text-center">
|
||||
<QSpinner color="orange" size="md" />
|
||||
<QSpinner color="primary" size="md" />
|
||||
</div>
|
||||
</QInfiniteScroll>
|
||||
</template>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<script setup>
|
||||
defineProps({ wrap: { type: Boolean, default: false } });
|
||||
</script>
|
||||
<template>
|
||||
<div class="vn-row q-gutter-md q-mb-md" :class="{ wrap }">
|
||||
<slot></slot>
|
||||
<div class="vn-row q-gutter-md q-mb-md">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scopped>
|
||||
<style lang="scss" scoped>
|
||||
.vn-row {
|
||||
display: flex;
|
||||
> * {
|
||||
> :deep(*) {
|
||||
flex: 1;
|
||||
}
|
||||
&[wrap] {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
.vn-row {
|
||||
|
|
|
@ -63,13 +63,13 @@ const props = defineProps({
|
|||
type: String,
|
||||
default: '',
|
||||
},
|
||||
makeFetch: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
whereFilter: {
|
||||
type: Function,
|
||||
default: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const searchText = ref('');
|
||||
const searchText = ref();
|
||||
let arrayDataProps = { ...props };
|
||||
if (props.redirect)
|
||||
arrayDataProps = {
|
||||
|
@ -100,18 +100,23 @@ onMounted(() => {
|
|||
});
|
||||
|
||||
async function search() {
|
||||
const staticParams = Object.entries(store.userParams).filter(
|
||||
([key, value]) => value && (props.staticParams || []).includes(key)
|
||||
);
|
||||
const staticParams = Object.entries(store.userParams);
|
||||
arrayData.reset(['skip', 'page']);
|
||||
|
||||
if (props.makeFetch)
|
||||
await arrayData.applyFilter({
|
||||
const filter = {
|
||||
params: {
|
||||
...Object.fromEntries(staticParams),
|
||||
search: searchText.value,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (props.whereFilter) {
|
||||
filter.filter = {
|
||||
where: props.whereFilter(searchText.value),
|
||||
};
|
||||
delete filter.params.search;
|
||||
}
|
||||
await arrayData.applyFilter(filter);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
|
@ -119,7 +124,7 @@ async function search() {
|
|||
<QForm @submit="search" id="searchbarForm">
|
||||
<VnInput
|
||||
id="searchbar"
|
||||
v-model="searchText"
|
||||
v-model.trim="searchText"
|
||||
:placeholder="t(props.label)"
|
||||
dense
|
||||
standout
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { onBeforeMount } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import { date } from 'quasar';
|
||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||
import VnAvatar from '../ui/VnAvatar.vue';
|
||||
|
@ -10,7 +10,8 @@ const $props = defineProps({
|
|||
where: { type: Object, default: () => {} },
|
||||
});
|
||||
|
||||
const filter = {
|
||||
const filter = computed(() => {
|
||||
return {
|
||||
fields: ['smsFk'],
|
||||
include: {
|
||||
relation: 'sms',
|
||||
|
@ -32,9 +33,9 @@ const filter = {
|
|||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
onBeforeMount(() => (filter.where = $props.where));
|
||||
...{ where: $props.where },
|
||||
};
|
||||
});
|
||||
|
||||
function formatNumber(number) {
|
||||
if (number.length <= 10) return number;
|
||||
|
|
|
@ -43,20 +43,9 @@ onBeforeUnmount(() => stateStore.toggleSubToolbar());
|
|||
</slot>
|
||||
</QToolbar>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
.q-toolbar {
|
||||
background: var(--vn-section-color);
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.sticky {
|
||||
position: sticky;
|
||||
top: 61px;
|
||||
z-index: 1;
|
||||
}
|
||||
@media (max-width: $breakpoint-sm) {
|
||||
.sticky {
|
||||
top: 90px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -7,5 +7,5 @@ export function getDateQBadgeColor(date) {
|
|||
let comparation = today - timeTicket;
|
||||
|
||||
if (comparation == 0) return 'warning';
|
||||
if (comparation < 0) return 'negative';
|
||||
if (comparation < 0) return 'success';
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
|||
delete params.filter;
|
||||
store.userParams = { ...params, ...store.userParams };
|
||||
store.userFilter = { ...filter, ...store.userFilter };
|
||||
if (filter.order) store.order = filter.order;
|
||||
if (filter?.order) store.order = filter.order;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -3,12 +3,14 @@ import { useRole } from './useRole';
|
|||
import { useAcl } from './useAcl';
|
||||
import { useUserConfig } from './useUserConfig';
|
||||
import axios from 'axios';
|
||||
import { useRouter } from 'vue-router';
|
||||
import useNotify from './useNotify';
|
||||
import { useTokenConfig } from './useTokenConfig';
|
||||
const TOKEN_MULTIMEDIA = 'tokenMultimedia';
|
||||
const TOKEN = 'token';
|
||||
|
||||
export function useSession() {
|
||||
const router = useRouter();
|
||||
const { notify } = useNotify();
|
||||
let isCheckingToken = false;
|
||||
let intervalId = null;
|
||||
|
@ -102,6 +104,31 @@ export function useSession() {
|
|||
startInterval();
|
||||
}
|
||||
|
||||
async function setLogin(data) {
|
||||
const {
|
||||
data: { multimediaToken },
|
||||
} = await axios.get('VnUsers/ShareToken', {
|
||||
headers: { Authorization: data.token },
|
||||
});
|
||||
|
||||
if (!multimediaToken) return;
|
||||
|
||||
await login({
|
||||
...data,
|
||||
created: Date.now(),
|
||||
tokenMultimedia: multimediaToken.id,
|
||||
});
|
||||
|
||||
notify('login.loginSuccess', 'positive');
|
||||
|
||||
const currentRoute = router.currentRoute.value;
|
||||
if (currentRoute.query?.redirect) {
|
||||
router.push(currentRoute.query.redirect);
|
||||
} else {
|
||||
router.push({ name: 'Dashboard' });
|
||||
}
|
||||
}
|
||||
|
||||
function isLoggedIn() {
|
||||
const localToken = localStorage.getItem(TOKEN);
|
||||
const sessionToken = sessionStorage.getItem(TOKEN);
|
||||
|
@ -163,6 +190,7 @@ export function useSession() {
|
|||
setToken,
|
||||
destroy,
|
||||
login,
|
||||
setLogin,
|
||||
isLoggedIn,
|
||||
checkValidity,
|
||||
setSession,
|
||||
|
|
|
@ -28,7 +28,7 @@ export function useValidator() {
|
|||
}
|
||||
|
||||
const { t } = useI18n();
|
||||
const validations = function (validation) {
|
||||
const validations = function (validation = {}) {
|
||||
return {
|
||||
format: (value) => {
|
||||
const { allowNull, with: format, allowBlank } = validation;
|
||||
|
@ -40,12 +40,15 @@ export function useValidator() {
|
|||
if (!isValid) return message;
|
||||
},
|
||||
presence: (value) => {
|
||||
let message = `Value can't be empty`;
|
||||
let message = t(`globals.valueCantBeEmpty`);
|
||||
if (validation.message)
|
||||
message = t(validation.message) || validation.message;
|
||||
|
||||
return !validator.isEmpty(value ? String(value) : '') || message;
|
||||
},
|
||||
required: (required, value) => {
|
||||
return required ? !!value || t('globals.fieldRequired') : null;
|
||||
},
|
||||
length: (value) => {
|
||||
const options = {
|
||||
min: validation.min || validation.is,
|
||||
|
@ -71,12 +74,17 @@ export function useValidator() {
|
|||
return validator.isInt(value) || 'Value should be integer';
|
||||
return validator.isNumeric(value) || 'Value should be a number';
|
||||
},
|
||||
min: (value, min) => {
|
||||
if (min >= 0)
|
||||
if (Math.floor(value) < min) return t('inputMin', { value: min });
|
||||
},
|
||||
custom: (value) => validation.bindedFunction(value) || 'Invalid value',
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
validate,
|
||||
validations,
|
||||
models,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -103,10 +103,6 @@ select:-webkit-autofill {
|
|||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.card-width {
|
||||
width: 770px;
|
||||
}
|
||||
|
||||
.vn-card-list {
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
|
@ -190,6 +186,10 @@ select:-webkit-autofill {
|
|||
font-size: medium;
|
||||
}
|
||||
|
||||
.q-toolbar {
|
||||
background: var(--vn-section-color);
|
||||
}
|
||||
|
||||
.q-card__actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
@ -253,7 +253,6 @@ input::-webkit-inner-spin-button {
|
|||
}
|
||||
td {
|
||||
font-size: 11pt;
|
||||
border-top: 1px solid var(--vn-page-color);
|
||||
border-collapse: collapse;
|
||||
}
|
||||
}
|
||||
|
@ -264,3 +263,10 @@ input::-webkit-inner-spin-button {
|
|||
max-width: 400px;
|
||||
}
|
||||
}
|
||||
.edit-photo-btn {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
bottom: 12px;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -20,21 +20,21 @@ export function isValidDate(date) {
|
|||
* Converts a given date to a specific format.
|
||||
*
|
||||
* @param {number|string|Date} date - The date to be formatted.
|
||||
* @param {Object} opts - Optional parameters to customize the output format.
|
||||
* @returns {string} The formatted date as a string in 'dd/mm/yyyy' format. If the provided date is not valid, an empty string is returned.
|
||||
*
|
||||
* @example
|
||||
* // returns "02/12/2022"
|
||||
* toDateFormat(new Date(2022, 11, 2));
|
||||
*/
|
||||
export function toDateFormat(date, locale = 'es-ES') {
|
||||
if (!isValidDate(date)) {
|
||||
return '';
|
||||
}
|
||||
return new Date(date).toLocaleDateString(locale, {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
});
|
||||
export function toDateFormat(date, locale = 'es-ES', opts = {}) {
|
||||
if (!isValidDate(date)) return '';
|
||||
|
||||
const format = Object.assign(
|
||||
{ year: 'numeric', month: '2-digit', day: '2-digit' },
|
||||
opts
|
||||
);
|
||||
return new Date(date).toLocaleDateString(locale, format);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export default function dateRange(value) {
|
||||
const minHour = new Date(value);
|
||||
minHour.setHours(0, 0, 0, 0);
|
||||
const maxHour = new Date();
|
||||
const maxHour = new Date(value);
|
||||
maxHour.setHours(23, 59, 59, 59);
|
||||
|
||||
return [minHour, maxHour];
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
// parsing JSON safely
|
||||
function parseJSON(str, fallback) {
|
||||
try {
|
||||
return JSON.parse(str ?? '{}');
|
||||
} catch (e) {
|
||||
console.error('Error parsing JSON:', e);
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
export default function (route, param) {
|
||||
// catch route query params
|
||||
const params = parseJSON(route?.query?.params, {});
|
||||
|
||||
// extract and parse filter from params
|
||||
const { filter: filterStr = '{}' } = params;
|
||||
const where = parseJSON(filterStr, {})?.where;
|
||||
if (where && where[param] !== undefined) {
|
||||
return where[param];
|
||||
}
|
||||
return null;
|
||||
}
|
|
@ -11,6 +11,7 @@ import dashIfEmpty from './dashIfEmpty';
|
|||
import dateRange from './dateRange';
|
||||
import toHour from './toHour';
|
||||
import dashOrCurrency from './dashOrCurrency';
|
||||
import getParamWhere from './getParamWhere';
|
||||
|
||||
export {
|
||||
toLowerCase,
|
||||
|
@ -26,4 +27,5 @@ export {
|
|||
toPercentage,
|
||||
dashIfEmpty,
|
||||
dateRange,
|
||||
getParamWhere,
|
||||
};
|
||||
|
|
|
@ -67,6 +67,7 @@ globals:
|
|||
allRows: 'All { numberRows } row(s)'
|
||||
markAll: Mark all
|
||||
requiredField: Required field
|
||||
valueCantBeEmpty: Value cannot be empty
|
||||
class: clase
|
||||
type: Type
|
||||
reason: reason
|
||||
|
@ -90,6 +91,11 @@ globals:
|
|||
salesPerson: SalesPerson
|
||||
send: Send
|
||||
code: Code
|
||||
since: Since
|
||||
from: From
|
||||
to: To
|
||||
notes: Notes
|
||||
refresh: Refresh
|
||||
pageTitles:
|
||||
logIn: Login
|
||||
summary: Summary
|
||||
|
@ -249,6 +255,12 @@ globals:
|
|||
privileges: Privileges
|
||||
ldap: LDAP
|
||||
samba: Samba
|
||||
twoFactor: Two factor
|
||||
recoverPassword: Recover password
|
||||
resetPassword: Reset password
|
||||
ticketsMonitor: Tickets monitor
|
||||
clientsActionsMonitor: Clients and actions
|
||||
serial: Serial
|
||||
created: Created
|
||||
worker: Worker
|
||||
now: Now
|
||||
|
@ -260,6 +272,9 @@ globals:
|
|||
unsavedPopup:
|
||||
title: Unsaved changes will be lost
|
||||
subtitle: Are you sure exit without saving?
|
||||
createInvoiceIn: Create invoice in
|
||||
myAccount: My account
|
||||
noOne: No one
|
||||
errors:
|
||||
statusUnauthorized: Access denied
|
||||
statusInternalServerError: An internal server error has ocurred
|
||||
|
@ -285,14 +300,17 @@ twoFactor:
|
|||
explanation: >-
|
||||
Please, enter the verification code that we have sent to your email in the
|
||||
next 5 minutes
|
||||
pageTitles:
|
||||
twoFactor: Two-Factor
|
||||
verifyEmail:
|
||||
pageTitles:
|
||||
verifyEmail: Email verification
|
||||
dashboard:
|
||||
pageTitles:
|
||||
|
||||
recoverPassword:
|
||||
userOrEmail: User or recovery email
|
||||
explanation: >-
|
||||
We will sent you an email to recover your password
|
||||
resetPassword:
|
||||
repeatPassword: Repeat password
|
||||
passwordNotMatch: Passwords don't match
|
||||
passwordChanged: Password changed
|
||||
customer:
|
||||
list:
|
||||
phone: Phone
|
||||
|
@ -443,6 +461,7 @@ entry:
|
|||
travelFk: Travel
|
||||
isExcludedFromAvailable: Inventory
|
||||
isRaid: Raid
|
||||
invoiceAmount: Import
|
||||
summary:
|
||||
commission: Commission
|
||||
currency: Currency
|
||||
|
@ -679,6 +698,7 @@ invoiceOut:
|
|||
chooseValidClient: Choose a valid client
|
||||
chooseValidCompany: Choose a valid company
|
||||
chooseValidPrinter: Choose a valid printer
|
||||
chooseValidSerialType: Choose a serial type
|
||||
fillDates: Invoice date and the max date should be filled
|
||||
invoiceDateLessThanMaxDate: Invoice date can not be less than max date
|
||||
invoiceWithFutureDate: Exists an invoice with a future date
|
||||
|
@ -1110,9 +1130,12 @@ travel:
|
|||
agency: Agency
|
||||
shipped: Shipped
|
||||
landed: Landed
|
||||
shipHour: Shipment Hour
|
||||
landHour: Landing Hour
|
||||
warehouseIn: Warehouse in
|
||||
warehouseOut: Warehouse out
|
||||
totalEntries: Total entries
|
||||
totalEntriesTooltip: Total entries
|
||||
summary:
|
||||
confirmed: Confirmed
|
||||
entryId: Entry Id
|
||||
|
|
|
@ -76,6 +76,9 @@ globals:
|
|||
warehouse: Almacén
|
||||
company: Empresa
|
||||
fieldRequired: Campo requerido
|
||||
valueCantBeEmpty: El valor no puede estar vacío
|
||||
Value can't be blank: El valor no puede estar en blanco
|
||||
Value can't be null: El valor no puede ser nulo
|
||||
allowedFilesText: 'Tipos de archivo permitidos: { allowedContentTypes }'
|
||||
smsSent: SMS enviado
|
||||
confirmDeletion: Confirmar eliminación
|
||||
|
@ -90,6 +93,11 @@ globals:
|
|||
salesPerson: Comercial
|
||||
send: Enviar
|
||||
code: Código
|
||||
since: Desde
|
||||
from: Desde
|
||||
to: Hasta
|
||||
notes: Notas
|
||||
refresh: Actualizar
|
||||
pageTitles:
|
||||
logIn: Inicio de sesión
|
||||
summary: Resumen
|
||||
|
@ -232,7 +240,7 @@ globals:
|
|||
purchaseRequest: Petición de compra
|
||||
weeklyTickets: Tickets programados
|
||||
formation: Formación
|
||||
locations: Ubicaciones
|
||||
locations: Localizaciones
|
||||
warehouses: Almacenes
|
||||
roles: Roles
|
||||
connections: Conexiones
|
||||
|
@ -250,6 +258,12 @@ globals:
|
|||
packages: Bultos
|
||||
ldap: LDAP
|
||||
samba: Samba
|
||||
twoFactor: Doble factor
|
||||
recoverPassword: Recuperar contraseña
|
||||
resetPassword: Restablecer contraseña
|
||||
ticketsMonitor: Monitor de tickets
|
||||
clientsActionsMonitor: Clientes y acciones
|
||||
serial: Facturas por serie
|
||||
created: Fecha creación
|
||||
worker: Trabajador
|
||||
now: Ahora
|
||||
|
@ -261,6 +275,9 @@ globals:
|
|||
unsavedPopup:
|
||||
title: Los cambios que no haya guardado se perderán
|
||||
subtitle: ¿Seguro que quiere salir sin guardar?
|
||||
createInvoiceIn: Crear factura recibida
|
||||
myAccount: Mi cuenta
|
||||
noOne: Nadie
|
||||
errors:
|
||||
statusUnauthorized: Acceso denegado
|
||||
statusInternalServerError: Ha ocurrido un error interno del servidor
|
||||
|
@ -284,14 +301,17 @@ twoFactor:
|
|||
validate: Validar
|
||||
insert: Introduce el código de verificación
|
||||
explanation: Por favor introduce el código de verificación que te hemos enviado a tu email en los próximos 5 minutos
|
||||
pageTitles:
|
||||
twoFactor: Doble factor
|
||||
verifyEmail:
|
||||
pageTitles:
|
||||
verifyEmail: Verificación de correo
|
||||
dashboard:
|
||||
pageTitles:
|
||||
|
||||
recoverPassword:
|
||||
userOrEmail: Usuario o correo de recuperación
|
||||
explanation: >-
|
||||
Te enviaremos un correo para restablecer tu contraseña
|
||||
resetPassword:
|
||||
repeatPassword: Repetir contraseña
|
||||
passwordNotMatch: Las contraseñas no coinciden
|
||||
passwordChanged: Contraseña cambiada
|
||||
customer:
|
||||
list:
|
||||
phone: Teléfono
|
||||
|
@ -441,6 +461,7 @@ entry:
|
|||
travelFk: Envio
|
||||
isExcludedFromAvailable: Inventario
|
||||
isRaid: Redada
|
||||
invoiceAmount: Importe
|
||||
summary:
|
||||
commission: Comisión
|
||||
currency: Moneda
|
||||
|
@ -684,6 +705,7 @@ invoiceOut:
|
|||
chooseValidClient: Selecciona un cliente válido
|
||||
chooseValidCompany: Selecciona una empresa válida
|
||||
chooseValidPrinter: Selecciona una impresora válida
|
||||
chooseValidSerialType: Selecciona una tipo de serie válida
|
||||
fillDates: La fecha de la factura y la fecha máxima deben estar completas
|
||||
invoiceDateLessThanMaxDate: La fecha de la factura no puede ser menor que la fecha máxima
|
||||
invoiceWithFutureDate: Existe una factura con una fecha futura
|
||||
|
@ -697,8 +719,6 @@ invoiceOut:
|
|||
percentageText: '{getPercentage}% {getAddressNumber} de {getNAddresses}'
|
||||
pdfsNumberText: '{nPdfs} de {totalPdfs} PDFs'
|
||||
negativeBases:
|
||||
from: Desde
|
||||
to: Hasta
|
||||
company: Empresa
|
||||
country: País
|
||||
clientId: Id cliente
|
||||
|
@ -873,7 +893,7 @@ worker:
|
|||
card:
|
||||
workerId: ID Trabajador
|
||||
name: Nombre
|
||||
email: Email
|
||||
email: Correo personal
|
||||
phone: Teléfono
|
||||
mobile: Móvil
|
||||
active: Activo
|
||||
|
@ -1088,11 +1108,14 @@ travel:
|
|||
id: Id
|
||||
ref: Referencia
|
||||
agency: Agencia
|
||||
shipped: Enviado
|
||||
landed: Llegada
|
||||
warehouseIn: Almacén de salida
|
||||
warehouseOut: Almacén de entrada
|
||||
totalEntries: Total de entradas
|
||||
shipped: F.envío
|
||||
shipHour: Hora de envío
|
||||
landHour: Hora de llegada
|
||||
landed: F.entrega
|
||||
warehouseIn: Alm.salida
|
||||
warehouseOut: Alm.entrada
|
||||
totalEntries: ∑
|
||||
totalEntriesTooltip: Entradas totales
|
||||
summary:
|
||||
confirmed: Confirmado
|
||||
entryId: Id entrada
|
||||
|
@ -1240,8 +1263,6 @@ components:
|
|||
# LatestBuysFilter
|
||||
salesPersonFk: Comprador
|
||||
supplierFk: Proveedor
|
||||
from: Desde
|
||||
to: Hasta
|
||||
active: Activo
|
||||
visible: Visible
|
||||
floramondo: Floramondo
|
||||
|
@ -1259,6 +1280,7 @@ components:
|
|||
clone: Clonar
|
||||
openCard: Ficha
|
||||
openSummary: Detalles
|
||||
viewSummary: Vista previa
|
||||
cardDescriptor:
|
||||
mainList: Listado principal
|
||||
summary: Resumen
|
||||
|
|
|
@ -6,7 +6,6 @@ import axios from 'axios';
|
|||
import useNotify from 'src/composables/useNotify.js';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnTable from 'components/VnTable/VnTable.vue';
|
||||
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
|
@ -24,7 +23,6 @@ const stateStore = useStateStore();
|
|||
const quasar = useQuasar();
|
||||
|
||||
const tableRef = ref();
|
||||
const rolesOptions = ref([]);
|
||||
|
||||
const exprBuilder = (param, value) => {
|
||||
switch (param) {
|
||||
|
@ -41,14 +39,12 @@ const columns = computed(() => [
|
|||
name: 'id',
|
||||
label: t('id'),
|
||||
isId: true,
|
||||
field: 'id',
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'model',
|
||||
label: t('model'),
|
||||
field: 'model',
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
},
|
||||
|
@ -56,15 +52,19 @@ const columns = computed(() => [
|
|||
align: 'left',
|
||||
name: 'principalId',
|
||||
label: t('principalId'),
|
||||
field: 'principalId',
|
||||
cardVisible: true,
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'VnRoles',
|
||||
optionLabel: 'name',
|
||||
optionValue: 'name',
|
||||
},
|
||||
create: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'property',
|
||||
label: t('property'),
|
||||
field: 'property',
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
},
|
||||
|
@ -72,7 +72,10 @@ const columns = computed(() => [
|
|||
align: 'left',
|
||||
name: 'accessType',
|
||||
label: t('accessType'),
|
||||
field: 'accessType',
|
||||
component: 'select',
|
||||
attrs: {
|
||||
options: ['READ', 'WRITE', '*'],
|
||||
},
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
},
|
||||
|
@ -118,13 +121,6 @@ const deleteAcl = async ({ id }) => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
url="VnRoles"
|
||||
:filter="{ fields: ['name'], order: 'name ASC' }"
|
||||
@on-fetch="(data) => (rolesOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
|
||||
<VnSearchbar
|
||||
data-key="AccountAcls"
|
||||
url="ACLs"
|
||||
|
@ -145,9 +141,9 @@ const deleteAcl = async ({ id }) => {
|
|||
formInitialData: {},
|
||||
}"
|
||||
order="id DESC"
|
||||
:disable-option="{ card: true }"
|
||||
:columns="columns"
|
||||
default-mode="table"
|
||||
auto-load
|
||||
:right-search="true"
|
||||
:is-editable="true"
|
||||
:use-model="true"
|
||||
|
@ -162,4 +158,15 @@ es:
|
|||
Are you sure you want to continue?: ¿Seguro que quieres continuar?
|
||||
Remove ACL: Eliminar Acl
|
||||
Do you want to remove this ACL?: ¿Quieres eliminar este ACL?
|
||||
principalId: Rol
|
||||
model: Modelo
|
||||
en:
|
||||
New ACL: New ACL
|
||||
ACL removed: ACL removed
|
||||
ACL will be removed: ACL will be removed
|
||||
Are you sure you want to continue?: Are you sure you want to continue?
|
||||
Remove ACL: Remove ACL
|
||||
Do you want to remove this ACL?: Do you want to remove this ACL?
|
||||
principalId: Rol
|
||||
model: Models
|
||||
</i18n>
|
||||
|
|
|
@ -21,24 +21,21 @@ const columns = computed(() => [
|
|||
{
|
||||
align: 'left',
|
||||
name: 'id',
|
||||
label: t('id'),
|
||||
label: t('Id'),
|
||||
isId: true,
|
||||
field: 'id',
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'alias',
|
||||
label: t('alias'),
|
||||
field: 'alias',
|
||||
label: t('Alias'),
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'description',
|
||||
label: t('description'),
|
||||
field: 'description',
|
||||
label: t('Description'),
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
},
|
||||
|
@ -60,7 +57,7 @@ const columns = computed(() => [
|
|||
<VnTable
|
||||
ref="tableRef"
|
||||
data-key="AccountAliasList"
|
||||
:url="`MailAliases`"
|
||||
url="MailAliases"
|
||||
:create="{
|
||||
urlCreate: 'MailAliases',
|
||||
title: 'Create MailAlias',
|
||||
|
@ -69,10 +66,17 @@ const columns = computed(() => [
|
|||
}"
|
||||
order="id DESC"
|
||||
:columns="columns"
|
||||
:disable-option="{ card: true }"
|
||||
default-mode="table"
|
||||
auto-load
|
||||
redirect="account/alias"
|
||||
:is-editable="true"
|
||||
:use-model="true"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Id: Id
|
||||
Alias: Alias
|
||||
Description: Descripción
|
||||
</i18n>
|
||||
|
|
|
@ -14,15 +14,23 @@ const columns = computed(() => [
|
|||
{
|
||||
align: 'left',
|
||||
name: 'id',
|
||||
label: t('id'),
|
||||
label: t('Id'),
|
||||
isId: true,
|
||||
field: 'id',
|
||||
cardVisible: true,
|
||||
columnFilter: {
|
||||
component: 'select',
|
||||
name: 'search',
|
||||
attrs: {
|
||||
url: 'VnUsers/preview',
|
||||
fields: ['id', 'name'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'username',
|
||||
label: t('nickname'),
|
||||
label: t('Nickname'),
|
||||
isTitle: true,
|
||||
component: 'input',
|
||||
columnField: {
|
||||
|
@ -37,7 +45,7 @@ const columns = computed(() => [
|
|||
{
|
||||
align: 'left',
|
||||
name: 'name',
|
||||
label: t('name'),
|
||||
label: t('Name'),
|
||||
component: 'input',
|
||||
columnField: {
|
||||
component: null,
|
||||
|
@ -48,6 +56,14 @@ const columns = computed(() => [
|
|||
cardVisible: true,
|
||||
create: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'email',
|
||||
label: t('email'),
|
||||
component: 'input',
|
||||
create: true,
|
||||
visible: false,
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
label: '',
|
||||
|
@ -57,6 +73,7 @@ const columns = computed(() => [
|
|||
title: t('View Summary'),
|
||||
icon: 'preview',
|
||||
action: (row) => viewSummary(row.id, AccountSummary),
|
||||
isPrimary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -83,9 +100,9 @@ const exprBuilder = (param, value) => {
|
|||
|
||||
<template>
|
||||
<VnSearchbar
|
||||
:label="t('account.search')"
|
||||
data-key="AccountUsers"
|
||||
:expr-builder="exprBuilder"
|
||||
:label="t('account.search')"
|
||||
:info="t('account.searchInfo')"
|
||||
/>
|
||||
|
||||
|
@ -96,8 +113,14 @@ const exprBuilder = (param, value) => {
|
|||
order="id DESC"
|
||||
:columns="columns"
|
||||
default-mode="table"
|
||||
auto-load
|
||||
redirect="account"
|
||||
:use-model="true"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Id: Id
|
||||
Nickname: Nickname
|
||||
Name: Nombre
|
||||
</i18n>
|
||||
|
|
|
@ -1,22 +1,8 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { computed } from 'vue';
|
||||
|
||||
import VnCard from 'components/common/VnCard.vue';
|
||||
import AliasDescriptor from './AliasDescriptor.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const routeName = computed(() => route.name);
|
||||
const customRouteRedirectName = computed(() => {
|
||||
return routeName.value;
|
||||
});
|
||||
const searchBarDataKeys = {
|
||||
AliasBasicData: 'AliasBasicData',
|
||||
AliasUsers: 'AliasUsers',
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -24,10 +10,12 @@ const searchBarDataKeys = {
|
|||
data-key="Alias"
|
||||
base-url="MailAliases"
|
||||
:descriptor="AliasDescriptor"
|
||||
:search-data-key="searchBarDataKeys[routeName]"
|
||||
:search-custom-route-redirect="customRouteRedirectName"
|
||||
:search-redirect="!!customRouteRedirectName"
|
||||
:searchbar-label="t('mailAlias.search')"
|
||||
:searchbar-info="t('mailAlias.searchInfo')"
|
||||
search-data-key="AccountAliasList"
|
||||
:searchbar-props="{
|
||||
url: 'MailAliases',
|
||||
info: t('mailAlias.searchInfo'),
|
||||
label: t('mailAlias.search'),
|
||||
searchUrl: 'table',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -37,9 +37,11 @@ watch(
|
|||
<VnInput v-model="data.nickname" :label="t('account.card.alias')" />
|
||||
<VnInput v-model="data.email" :label="t('account.card.email')" />
|
||||
<VnSelect
|
||||
url="Languages"
|
||||
v-model="data.lang"
|
||||
:options="['es', 'en']"
|
||||
:label="t('account.card.lang')"
|
||||
option-value="code"
|
||||
option-label="code"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,34 +1,20 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import VnCard from 'components/common/VnCard.vue';
|
||||
import AccountDescriptor from './AccountDescriptor.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const routeName = computed(() => route.name);
|
||||
const customRouteRedirectName = computed(() => routeName.value);
|
||||
const searchBarDataKeys = {
|
||||
AccountSummary: 'AccountSummary',
|
||||
AccountInheritedRoles: 'AccountInheritedRoles',
|
||||
AccountMailForwarding: 'AccountMailForwarding',
|
||||
AccountMailAlias: 'AccountMailAlias',
|
||||
AccountPrivileges: 'AccountPrivileges',
|
||||
AccountLog: 'AccountLog',
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VnCard
|
||||
data-key="Account"
|
||||
:descriptor="AccountDescriptor"
|
||||
:search-data-key="searchBarDataKeys[routeName]"
|
||||
:search-custom-route-redirect="customRouteRedirectName"
|
||||
:search-redirect="!!customRouteRedirectName"
|
||||
:searchbar-label="t('account.search')"
|
||||
:searchbar-info="t('account.searchInfo')"
|
||||
search-data-key="AccountUsers"
|
||||
:searchbar-props="{
|
||||
url: 'VnUsers/preview',
|
||||
label: t('account.search'),
|
||||
info: t('account.searchInfo'),
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -54,7 +54,7 @@ const hasAccount = ref(false);
|
|||
</template>
|
||||
<template #before>
|
||||
<!-- falla id :id="entityId.value" collection="user" size="160x160" -->
|
||||
<VnImg :id="entityId" collection="user" size="160x160" class="photo">
|
||||
<VnImg :id="entityId" collection="user" resolution="160x160" class="photo">
|
||||
<template #error>
|
||||
<div
|
||||
class="absolute-full picture text-center q-pa-md flex flex-center"
|
||||
|
@ -72,7 +72,7 @@ const hasAccount = ref(false);
|
|||
</VnImg>
|
||||
</template>
|
||||
<template #body="{ entity }">
|
||||
<VnLv :label="t('account.card.nickname')" :value="entity.nickname" />
|
||||
<VnLv :label="t('account.card.nickname')" :value="entity.name" />
|
||||
<VnLv :label="t('account.card.role')" :value="entity.role.name" />
|
||||
</template>
|
||||
<template #actions="{ entity }">
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { computed, ref, toRefs } from 'vue';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useVnConfirm } from 'composables/useVnConfirm';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import CustomerChangePassword from 'src/pages/Customer/components/CustomerChangePassword.vue';
|
||||
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
const quasar = useQuasar();
|
||||
const $props = defineProps({
|
||||
hasAccount: {
|
||||
type: Boolean,
|
||||
|
@ -35,7 +32,7 @@ async function updateStatusAccount(active) {
|
|||
|
||||
account.value.hasAccount = active;
|
||||
const status = active ? 'enable' : 'disable';
|
||||
quasar.notify({
|
||||
notify({
|
||||
message: t(`account.card.${status}Account.success`),
|
||||
type: 'positive',
|
||||
});
|
||||
|
@ -44,19 +41,11 @@ async function updateStatusUser(active) {
|
|||
await axios.patch(`VnUsers/${entityId.value}`, { active });
|
||||
account.value.active = active;
|
||||
const status = active ? 'activate' : 'deactivate';
|
||||
quasar.notify({
|
||||
notify({
|
||||
message: t(`account.card.actions.${status}User.success`),
|
||||
type: 'positive',
|
||||
});
|
||||
}
|
||||
function setPassword() {
|
||||
quasar.dialog({
|
||||
component: CustomerChangePassword,
|
||||
componentProps: {
|
||||
id: entityId.value,
|
||||
},
|
||||
});
|
||||
}
|
||||
const showSyncDialog = ref(false);
|
||||
const syncPassword = ref(null);
|
||||
const shouldSyncPassword = ref(false);
|
||||
|
@ -66,22 +55,43 @@ async function sync() {
|
|||
await axios.patch(`Accounts/${account.value.name}/sync`, {
|
||||
params,
|
||||
});
|
||||
quasar.notify({
|
||||
notify({
|
||||
message: t('account.card.actions.sync.success'),
|
||||
type: 'positive',
|
||||
});
|
||||
}
|
||||
|
||||
const removeAccount = async () => {
|
||||
try {
|
||||
await axios.delete(`VnUsers/${account.value.id}`);
|
||||
notify(t('Account removed'), 'positive');
|
||||
} catch (error) {
|
||||
console.error('Error deleting user', error);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<VnConfirm
|
||||
v-model="showSyncDialog"
|
||||
:message="t('account.card.actions.sync.message')"
|
||||
:title="t('account.card.actions.sync.title')"
|
||||
:promise="sync"
|
||||
>
|
||||
<template #customHTML>
|
||||
{{ shouldSyncPassword }}
|
||||
<QCheckbox
|
||||
:label="t('account.card.actions.sync.checkbox')"
|
||||
v-model="shouldSyncPassword"
|
||||
class="full-width"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
>
|
||||
<QIcon style="padding-left: 10px" color="primary" name="info" size="sm">
|
||||
<QTooltip>{{ t('account.card.actions.sync.tooltip') }}</QTooltip>
|
||||
</QIcon></QCheckbox
|
||||
>
|
||||
<QInput
|
||||
v-if="shouldSyncPassword"
|
||||
:label="t('login.password')"
|
||||
v-model="syncPassword"
|
||||
class="full-width"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
type="password"
|
||||
/>
|
||||
</template>
|
||||
</VnConfirm>
|
||||
<QItem
|
||||
v-if="account.hasAccount"
|
||||
v-ripple
|
||||
|
@ -130,10 +140,4 @@ const removeAccount = async () => {
|
|||
</QItem>
|
||||
|
||||
<QSeparator />
|
||||
<!-- <QItem @click="removeAccount(id)" v-ripple clickable>
|
||||
<QItemSection avatar>
|
||||
<QIcon name="delete" />
|
||||
</QItemSection>
|
||||
<QItemSection>{{ t('account.card.actions.delete.name') }}</QItemSection>
|
||||
</QItem> -->
|
||||
</template>
|
||||
|
|
|
@ -85,7 +85,7 @@ const fetchMailAliases = async () => {
|
|||
paginateRef.value.fetch();
|
||||
};
|
||||
|
||||
const getAccountData = async () => {
|
||||
const getAccountData = async (reload = true) => {
|
||||
loading.value = true;
|
||||
hasAccount.value = await fetchAccountExistence();
|
||||
if (!hasAccount.value) {
|
||||
|
@ -93,7 +93,7 @@ const getAccountData = async () => {
|
|||
store.data = [];
|
||||
return;
|
||||
}
|
||||
await fetchMailAliases();
|
||||
reload && (await fetchMailAliases());
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
|
@ -102,13 +102,11 @@ const openCreateMailAliasForm = () => createMailAliasDialogRef.value.show();
|
|||
watch(
|
||||
() => route.params.id,
|
||||
() => {
|
||||
store.url = urlPath;
|
||||
store.filter = filter.value;
|
||||
getAccountData();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(async () => await getAccountData());
|
||||
onMounted(async () => await getAccountData(false));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
|
@ -12,22 +12,14 @@ const route = useRoute();
|
|||
|
||||
const rolesOptions = ref([]);
|
||||
const formModelRef = ref();
|
||||
watch(
|
||||
() => route.params.id,
|
||||
() => formModelRef.value.reset()
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
url="VnRoles"
|
||||
:filter="{ fields: ['id', 'name'], order: 'name ASC' }"
|
||||
auto-load
|
||||
@on-fetch="(data) => (rolesOptions = data)"
|
||||
/>
|
||||
<FormModel
|
||||
ref="formModelRef"
|
||||
model="AccountPrivileges"
|
||||
:url="`VnUsers/${route.params.id}`"
|
||||
:url-create="`VnUsers/${route.params.id}/privileges`"
|
||||
auto-load
|
||||
@on-data-saved="formModelRef.fetch()"
|
||||
>
|
||||
<FetchData url="VnRoles" auto-load @on-fetch="(data) => (rolesOptions = data)" />
|
||||
<FormModel ref="formModelRef" model="AccountPrivileges" url="VnUsers" auto-load>
|
||||
<template #form="{ data }">
|
||||
<div class="q-gutter-y-sm">
|
||||
<QCheckbox
|
||||
|
|
|
@ -48,7 +48,7 @@ const filter = {
|
|||
<QIcon name="open_in_new" />
|
||||
</router-link>
|
||||
</QCardSection>
|
||||
<VnLv :label="t('account.card.nickname')" :value="account.nickname" />
|
||||
<VnLv :label="t('account.card.nickname')" :value="account.name" />
|
||||
<VnLv :label="t('account.card.role')" :value="account.role.name" />
|
||||
</QCard>
|
||||
</template>
|
||||
|
|
|
@ -4,9 +4,9 @@ import { computed, ref } from 'vue';
|
|||
import VnTable from 'components/VnTable/VnTable.vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||
import RoleSummary from './Card/RoleSummary.vue';
|
||||
const route = useRoute();
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
|
@ -16,32 +16,51 @@ const $props = defineProps({
|
|||
});
|
||||
const tableRef = ref();
|
||||
const entityId = computed(() => $props.id || route.params.id);
|
||||
|
||||
const { viewSummary } = useSummaryDialog();
|
||||
const columns = computed(() => [
|
||||
{
|
||||
align: 'left',
|
||||
name: 'id',
|
||||
label: t('id'),
|
||||
label: t('Id'),
|
||||
isId: true,
|
||||
columnFilter: {
|
||||
inWhere: true,
|
||||
component: 'select',
|
||||
name: 'search',
|
||||
attrs: {
|
||||
url: 'VnRoles',
|
||||
fields: ['id', 'name'],
|
||||
},
|
||||
},
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'name',
|
||||
label: t('name'),
|
||||
label: t('Name'),
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'description',
|
||||
label: t('description'),
|
||||
label: t('Description'),
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
label: '',
|
||||
name: 'tableActions',
|
||||
actions: [
|
||||
{
|
||||
title: t('View Summary'),
|
||||
icon: 'preview',
|
||||
action: (row) => viewSummary(row.id, RoleSummary),
|
||||
isPrimary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
const exprBuilder = (param, value) => {
|
||||
switch (param) {
|
||||
|
@ -62,16 +81,12 @@ const exprBuilder = (param, value) => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport to="#searchbar">
|
||||
<VnSearchbar
|
||||
data-key="Roles"
|
||||
:expr-builder="exprBuilder"
|
||||
:label="t('role.searchRoles')"
|
||||
:info="t('role.searchInfo')"
|
||||
/>
|
||||
</Teleport>
|
||||
</template>
|
||||
<VnTable
|
||||
ref="tableRef"
|
||||
data-key="Roles"
|
||||
|
@ -85,10 +100,16 @@ const exprBuilder = (param, value) => {
|
|||
},
|
||||
}"
|
||||
order="id ASC"
|
||||
:disable-option="{ card: true }"
|
||||
:columns="columns"
|
||||
default-mode="table"
|
||||
auto-load
|
||||
redirect="account/role"
|
||||
:is-editable="true"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Id: Id
|
||||
Description: Descripción
|
||||
Name: Nombre
|
||||
</i18n>
|
||||
|
|
|
@ -23,11 +23,6 @@ const { t } = useI18n();
|
|||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<div class="col">
|
||||
<QCheckbox :label="t('mailAlias.isPublic')" v-model="data.isPublic" />
|
||||
</div>
|
||||
</VnRow>
|
||||
</template>
|
||||
</FormModel>
|
||||
</template>
|
||||
|
|
|
@ -1,31 +1,20 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
import VnCard from 'components/common/VnCard.vue';
|
||||
import RoleDescriptor from './RoleDescriptor.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const routeName = computed(() => route.name);
|
||||
const customRouteRedirectName = computed(() => routeName.value);
|
||||
const searchBarDataKeys = {
|
||||
RoleSummary: 'RoleSummary',
|
||||
RoleBasicData: 'RoleBasicData',
|
||||
SubRoles: 'SubRoles',
|
||||
InheritedRoles: 'InheritedRoles',
|
||||
RoleLog: 'RoleLog',
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<VnCard
|
||||
data-key="Role"
|
||||
:descriptor="RoleDescriptor"
|
||||
:search-data-key="searchBarDataKeys[routeName]"
|
||||
:search-custom-route-redirect="customRouteRedirectName"
|
||||
:search-redirect="!!customRouteRedirectName"
|
||||
:searchbar-label="t('role.searchRoles')"
|
||||
:searchbar-info="t('role.searchInfo')"
|
||||
search-data-key="AccountRoles"
|
||||
:searchbar-props="{
|
||||
url: 'VnRoles',
|
||||
label: t('role.searchRoles'),
|
||||
info: t('role.searchInfo'),
|
||||
searchUrl: 'table',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -10,13 +10,10 @@ import VnInput from 'src/components/common/VnInput.vue';
|
|||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
|
||||
import axios from 'axios';
|
||||
// import { useSession } from 'src/composables/useSession';
|
||||
import VnImg from 'src/components/ui/VnImg.vue';
|
||||
import VnAvatar from 'src/components/ui/VnAvatar.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
// const { getTokenMultimedia } = useSession();
|
||||
// const token = getTokenMultimedia();
|
||||
|
||||
const claimStates = ref([]);
|
||||
const claimStatesCopy = ref([]);
|
||||
|
@ -94,15 +91,14 @@ const statesFilter = {
|
|||
:rules="validate('claim.claimStateFk')"
|
||||
>
|
||||
<template #before>
|
||||
<QAvatar color="orange">
|
||||
<VnImg
|
||||
v-if="data.workerFk"
|
||||
:size="'160x160'"
|
||||
:id="data.workerFk"
|
||||
collection="user"
|
||||
spinner-color="white"
|
||||
<VnAvatar
|
||||
:worker-id="data.workerFk"
|
||||
size="md"
|
||||
:title="
|
||||
workersOptions.find(({ id }) => id == data.workerFk)?.name
|
||||
"
|
||||
color="primary"
|
||||
/>
|
||||
</QAvatar>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<QSelect
|
||||
|
|
|
@ -11,9 +11,11 @@ import filter from './ClaimFilter.js';
|
|||
:descriptor="ClaimDescriptor"
|
||||
:filter-panel="ClaimFilter"
|
||||
search-data-key="ClaimList"
|
||||
search-url="Claims/filter"
|
||||
searchbar-label="Search claim"
|
||||
searchbar-info="You can search by claim id or customer name"
|
||||
:filter="filter"
|
||||
:searchbar-props="{
|
||||
url: 'Claims/filter',
|
||||
label: 'Search claim',
|
||||
info: 'You can search by claim id or customer name',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -20,7 +20,8 @@ const workers = ref([]);
|
|||
const selected = ref([]);
|
||||
const saveButtonRef = ref();
|
||||
|
||||
const developmentsFilter = {
|
||||
const developmentsFilter = computed(() => {
|
||||
return {
|
||||
fields: [
|
||||
'id',
|
||||
'claimFk',
|
||||
|
@ -33,7 +34,8 @@ const developmentsFilter = {
|
|||
where: {
|
||||
claimFk: route.params.id,
|
||||
},
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
|
@ -142,9 +144,9 @@ const columns = computed(() => [
|
|||
ref="claimDevelopmentForm"
|
||||
:data-required="{ claimFk: route.params.id }"
|
||||
v-model:selected="selected"
|
||||
auto-load
|
||||
@save-changes="$router.push(`/claim/${route.params.id}/action`)"
|
||||
:default-save="false"
|
||||
auto-load
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<QTable
|
||||
|
|
|
@ -36,8 +36,6 @@ const $props = defineProps({
|
|||
|
||||
const entityId = computed(() => $props.id || route.params.id);
|
||||
const ClaimStates = ref([]);
|
||||
const claimUrl = ref();
|
||||
const salixUrl = ref();
|
||||
const claimDmsRef = ref();
|
||||
const claimDms = ref([]);
|
||||
const multimediaDialog = ref();
|
||||
|
@ -152,11 +150,6 @@ const developmentColumns = ref([
|
|||
},
|
||||
]);
|
||||
|
||||
onMounted(async () => {
|
||||
salixUrl.value = await getUrl('');
|
||||
claimUrl.value = salixUrl.value + `claim/${entityId.value}/`;
|
||||
});
|
||||
|
||||
async function getClaimDms() {
|
||||
claimDmsFilter.value.where = { claimFk: entityId.value };
|
||||
await claimDmsRef.value.fetch();
|
||||
|
@ -177,10 +170,15 @@ function openDialog(dmsId) {
|
|||
multimediaSlide.value = dmsId;
|
||||
multimediaDialog.value = true;
|
||||
}
|
||||
|
||||
async function changeState(value) {
|
||||
await axios.patch(`Claims/updateClaim/${entityId.value}`, { claimStateFk: value });
|
||||
router.go(route.fullPath);
|
||||
}
|
||||
|
||||
function claimUrl(section) {
|
||||
return '#/claim/' + entityId.value + '/' + section;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -234,7 +232,7 @@ async function changeState(value) {
|
|||
<template #body="{ entity: { claim, salesClaimed, developments } }">
|
||||
<QCard class="vn-one" v-if="$route.name != 'ClaimSummary'">
|
||||
<VnTitle
|
||||
:url="`#/claim/${entityId}/basic-data`"
|
||||
:url="claimUrl('basic-data')"
|
||||
:text="t('globals.pageTitles.basicData')"
|
||||
/>
|
||||
<VnLv :label="t('claim.created')" :value="toDate(claim.created)" />
|
||||
|
@ -275,7 +273,7 @@ async function changeState(value) {
|
|||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-two">
|
||||
<VnTitle :url="`#/claim/${entityId}/notes`" :text="t('claim.notes')" />
|
||||
<VnTitle :url="claimUrl('notes')" :text="t('claim.notes')" />
|
||||
<ClaimNotes
|
||||
:id="entityId"
|
||||
:add-note="false"
|
||||
|
@ -284,7 +282,7 @@ async function changeState(value) {
|
|||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-two" v-if="claimDms?.length">
|
||||
<VnTitle :url="`#/claim/${entityId}/photos`" :text="t('claim.photos')" />
|
||||
<VnTitle :url="claimUrl('photos')" :text="t('claim.photos')" />
|
||||
<div class="container max-container-height" style="overflow: auto">
|
||||
<div
|
||||
class="multimedia-container"
|
||||
|
@ -326,7 +324,7 @@ async function changeState(value) {
|
|||
</div>
|
||||
</QCard>
|
||||
<QCard class="vn-max" v-if="salesClaimed.length > 0">
|
||||
<VnTitle :url="`#/claim/${entityId}/lines`" :text="t('claim.details')" />
|
||||
<VnTitle :url="claimUrl('lines')" :text="t('claim.details')" />
|
||||
<QTable
|
||||
:columns="detailsColumns"
|
||||
:rows="salesClaimed"
|
||||
|
@ -365,7 +363,7 @@ async function changeState(value) {
|
|||
</QTable>
|
||||
</QCard>
|
||||
<QCard class="vn-max" v-if="developments.length > 0">
|
||||
<VnTitle :url="claimUrl + 'development'" :text="t('claim.development')" />
|
||||
<VnTitle :url="claimUrl('development')" :text="t('claim.development')" />
|
||||
<QTable
|
||||
:columns="developmentColumns"
|
||||
:rows="developments"
|
||||
|
@ -390,7 +388,7 @@ async function changeState(value) {
|
|||
</QTable>
|
||||
</QCard>
|
||||
<QCard class="vn-max">
|
||||
<VnTitle :url="claimUrl + 'action'" :text="t('claim.actions')" />
|
||||
<VnTitle :url="claimUrl('action')" :text="t('claim.actions')" />
|
||||
<div id="slider-container" class="q-px-xl q-py-md">
|
||||
<QSlider
|
||||
v-model="claim.responsibility"
|
||||
|
|
|
@ -50,7 +50,7 @@ const columns = computed(() => [
|
|||
align: 'left',
|
||||
label: t('claim.attendedBy'),
|
||||
name: 'attendedBy',
|
||||
cardVisible: true,
|
||||
orderBy: 'workerFk',
|
||||
columnFilter: {
|
||||
component: 'select',
|
||||
attrs: {
|
||||
|
@ -63,6 +63,7 @@ const columns = computed(() => [
|
|||
optionFilter: 'firstName',
|
||||
},
|
||||
},
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -77,6 +78,9 @@ const columns = computed(() => [
|
|||
{
|
||||
align: 'left',
|
||||
label: t('claim.state'),
|
||||
format: ({ stateCode }) =>
|
||||
claimFilterRef.value?.states.find(({ code }) => code === stateCode)
|
||||
?.description,
|
||||
name: 'stateCode',
|
||||
chip: {
|
||||
condition: () => true,
|
||||
|
@ -96,7 +100,7 @@ const columns = computed(() => [
|
|||
name: 'tableActions',
|
||||
actions: [
|
||||
{
|
||||
title: t('Client ticket list'),
|
||||
title: t('components.smartCard.viewSummary'),
|
||||
icon: 'preview',
|
||||
action: (row) => viewSummary(row.id, ClaimSummary),
|
||||
},
|
||||
|
|
|
@ -42,7 +42,7 @@ claim:
|
|||
pickup: Recoger
|
||||
null: No
|
||||
agency: Agencia
|
||||
delivery: Entrega
|
||||
delivery: Reparto
|
||||
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í
|
||||
|
|
|
@ -7,14 +7,15 @@ import FetchData from 'components/FetchData.vue';
|
|||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnImg from 'src/components/ui/VnImg.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnAvatar from 'src/components/ui/VnAvatar.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
const businessTypes = ref([]);
|
||||
const contactChannels = ref([]);
|
||||
const title = ref();
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
|
@ -95,16 +96,20 @@ const contactChannels = ref([]);
|
|||
:label="t('customer.basicData.salesPerson')"
|
||||
:rules="validate('client.salesPersonFk')"
|
||||
:use-like="false"
|
||||
:emit-value="false"
|
||||
@update:model-value="
|
||||
(val) => {
|
||||
title = val?.nickname;
|
||||
data.salesPersonFk = val?.id;
|
||||
}
|
||||
"
|
||||
>
|
||||
<template #prepend>
|
||||
<QAvatar color="orange">
|
||||
<VnImg
|
||||
v-if="data.salesPersonFk"
|
||||
:id="data.salesPersonFk"
|
||||
collection="user"
|
||||
spinner-color="white"
|
||||
<VnAvatar
|
||||
:worker-id="data.salesPersonFk"
|
||||
color="primary"
|
||||
:title="title"
|
||||
/>
|
||||
</QAvatar>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<QSelect
|
||||
|
|
|
@ -10,8 +10,10 @@ import CustomerFilter from '../CustomerFilter.vue';
|
|||
:descriptor="CustomerDescriptor"
|
||||
:filter-panel="CustomerFilter"
|
||||
search-data-key="CustomerList"
|
||||
search-url="Clients/extendedListFilter"
|
||||
searchbar-label="Search customer"
|
||||
searchbar-info="You can search by customer id or name"
|
||||
:searchbar-props="{
|
||||
url: 'Clients/extendedListFilter',
|
||||
label: 'Search customer',
|
||||
info: 'You can search by customer id or name',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import CustomerConsumptionFilter from './CustomerConsumptionFilter.vue';
|
||||
import { useStateStore } from 'src/stores/useStateStore';
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -139,7 +139,7 @@ const setData = (entity) => (data.value = useCardDescription(entity?.name, entit
|
|||
<QBtn
|
||||
:to="{
|
||||
name: 'TicketList',
|
||||
query: { params: JSON.stringify({ clientFk: entity.id }) },
|
||||
query: { table: JSON.stringify({ clientFk: entity.id }) },
|
||||
}"
|
||||
size="md"
|
||||
icon="vn:ticket"
|
||||
|
@ -150,7 +150,7 @@ const setData = (entity) => (data.value = useCardDescription(entity?.name, entit
|
|||
<QBtn
|
||||
:to="{
|
||||
name: 'InvoiceOutList',
|
||||
query: { params: JSON.stringify({ clientFk: entity.id }) },
|
||||
query: { table: JSON.stringify({ clientFk: entity.id }) },
|
||||
}"
|
||||
size="md"
|
||||
icon="vn:invoice-out"
|
||||
|
@ -161,7 +161,7 @@ const setData = (entity) => (data.value = useCardDescription(entity?.name, entit
|
|||
<QBtn
|
||||
:to="{
|
||||
name: 'OrderCreate',
|
||||
query: { clientFk: entity.id },
|
||||
query: { clientId: entity.id },
|
||||
}"
|
||||
size="md"
|
||||
icon="vn:basketadd"
|
||||
|
@ -169,8 +169,19 @@ const setData = (entity) => (data.value = useCardDescription(entity?.name, entit
|
|||
>
|
||||
<QTooltip>{{ t('New order') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn size="md" icon="face" color="primary">
|
||||
<!-- TODO:: Redirigir a la vista de usuario cuando exista -->
|
||||
<QBtn
|
||||
:to="{
|
||||
name: 'AccountList',
|
||||
query: {
|
||||
table: JSON.stringify({
|
||||
filter: { where: { id: entity.id } },
|
||||
}),
|
||||
},
|
||||
}"
|
||||
size="md"
|
||||
icon="face"
|
||||
color="primary"
|
||||
>
|
||||
<QTooltip>{{ t('Go to user') }}</QTooltip>
|
||||
</QBtn>
|
||||
</QCardActions>
|
||||
|
|
|
@ -1,262 +1,6 @@
|
|||
<script setup>
|
||||
import { onBeforeMount, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const stateStore = useStateStore();
|
||||
|
||||
const clientLogs = ref(null);
|
||||
const urlClientLogsEditors = ref(null);
|
||||
const urlClientLogsModels = ref(null);
|
||||
const clientLogsModelsOptions = ref([]);
|
||||
const clientLogsOptions = ref([]);
|
||||
const clientLogsEditorsOptions = ref([]);
|
||||
const radioButtonValue = ref('all');
|
||||
const insert = ref(false);
|
||||
const update = ref(false);
|
||||
const deletes = ref(false);
|
||||
const select = ref(false);
|
||||
const neq = ref(null);
|
||||
const inq = ref([]);
|
||||
|
||||
const filterClientLogs = {
|
||||
fields: [
|
||||
'id',
|
||||
'originFk',
|
||||
'userFk',
|
||||
'action',
|
||||
'changedModel',
|
||||
'oldInstance',
|
||||
'newInstance',
|
||||
'creationDate',
|
||||
'changedModel',
|
||||
'changedModelId',
|
||||
'changedModelValue',
|
||||
'description',
|
||||
],
|
||||
include: [
|
||||
{
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['nickname', 'name', 'image'],
|
||||
include: { relation: 'worker', scope: { fields: ['id'] } },
|
||||
},
|
||||
},
|
||||
],
|
||||
order: ['creationDate DESC', 'id DESC'],
|
||||
limit: 20,
|
||||
};
|
||||
const filterClientLogsEditors = {
|
||||
fields: ['id', 'nickname', 'name', 'image'],
|
||||
order: 'nickname',
|
||||
limit: 30,
|
||||
};
|
||||
const filterClientLogsModels = { order: ['changedModel'] };
|
||||
const urlBase = `ClientLogs/${route.params.id}`;
|
||||
|
||||
onBeforeMount(() => {
|
||||
stateStore.rightDrawer = true;
|
||||
filterClientLogs.where = {
|
||||
and: [
|
||||
{ originFk: `${route.params.id}` },
|
||||
{ userFk: { neq: radioButtonValue.value } },
|
||||
{ action: { inq: inq.value } },
|
||||
],
|
||||
};
|
||||
urlClientLogsEditors.value = `${urlBase}/editors`;
|
||||
urlClientLogsModels.value = `${urlBase}/models`;
|
||||
});
|
||||
|
||||
const getClientLogs = async (value, status) => {
|
||||
if (status === 'neq') {
|
||||
neq.value = value;
|
||||
} else {
|
||||
setInq(value, status);
|
||||
}
|
||||
filterClientLogs.where = {
|
||||
and: [
|
||||
{ originFk: `${route.params.id}` },
|
||||
{ userFk: { neq: neq.value } },
|
||||
{ action: { inq: inq.value } },
|
||||
],
|
||||
};
|
||||
clientLogs.value?.fetch();
|
||||
};
|
||||
|
||||
const setInq = (value, status) => {
|
||||
if (status) {
|
||||
if (!inq.value.includes(value)) {
|
||||
inq.value.push(value);
|
||||
}
|
||||
} else {
|
||||
inq.value = inq.value.filter((item) => item !== value);
|
||||
}
|
||||
};
|
||||
import VnLog from 'src/components/common/VnLog.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
:filter="filterClientLogs"
|
||||
@on-fetch="(data) => (clientLogsOptions = data)"
|
||||
auto-load
|
||||
url="ClientLogs"
|
||||
ref="clientLogs"
|
||||
/>
|
||||
<FetchData
|
||||
:filter="filterClientLogsEditors"
|
||||
@on-fetch="(data) => (clientLogsEditorsOptions = data)"
|
||||
auto-load
|
||||
:url="urlClientLogsEditors"
|
||||
/>
|
||||
<FetchData
|
||||
:filter="filterClientLogsModels"
|
||||
@on-fetch="(data) => (clientLogsModelsOptions = data)"
|
||||
auto-load
|
||||
:url="urlClientLogsModels"
|
||||
/>
|
||||
|
||||
<h5 class="flex justify-center color-vn-label">
|
||||
{{ t('globals.noResults') }}
|
||||
</h5>
|
||||
|
||||
<QDrawer :width="256" show-if-above side="right" v-model="stateStore.rightDrawer">
|
||||
<div class="q-mt-sm q-px-md">
|
||||
<VnInput :label="t('Search')" clearable>
|
||||
<template #append>
|
||||
<QIcon name="info" class="cursor-pointer">
|
||||
<QTooltip>
|
||||
{{ t('Search by id or concept') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</template>
|
||||
</VnInput>
|
||||
<VnSelect
|
||||
:label="t('Entity')"
|
||||
:options="[]"
|
||||
class="q-mt-md"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
/>
|
||||
|
||||
<div class="q-mt-lg">
|
||||
<QRadio
|
||||
:dark="true"
|
||||
:label="t('All')"
|
||||
@update:model-value="getClientLogs($event, 'neq')"
|
||||
dense
|
||||
v-model="radioButtonValue"
|
||||
val="all"
|
||||
/>
|
||||
</div>
|
||||
<div class="q-mt-md">
|
||||
<QRadio
|
||||
:dark="true"
|
||||
:label="t('User')"
|
||||
@update:model-value="getClientLogs($event, 'neq')"
|
||||
dense
|
||||
v-model="radioButtonValue"
|
||||
val="user"
|
||||
/>
|
||||
</div>
|
||||
<div class="q-mt-md">
|
||||
<QRadio
|
||||
:dark="true"
|
||||
:label="t('System')"
|
||||
@update:model-value="getClientLogs($event, 'neq')"
|
||||
dense
|
||||
v-model="radioButtonValue"
|
||||
val="system"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<VnSelect
|
||||
:label="t('User')"
|
||||
:options="[]"
|
||||
class="q-mt-sm"
|
||||
hide-selected
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
/>
|
||||
<VnInput :label="t('Changes')" clearable class="q-mt-sm">
|
||||
<template #append>
|
||||
<QIcon name="info" class="cursor-pointer">
|
||||
<QTooltip>
|
||||
{{ t('Search by changes') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</template>
|
||||
</VnInput>
|
||||
|
||||
<div class="q-mt-md">
|
||||
<QCheckbox
|
||||
:label="t('Creates')"
|
||||
@update:model-value="getClientLogs('insert', $event)"
|
||||
v-model="insert"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<QCheckbox
|
||||
:label="t('Edits')"
|
||||
@update:model-value="getClientLogs('update', $event)"
|
||||
v-model="update"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<QCheckbox
|
||||
:label="t('Deletes')"
|
||||
@update:model-value="getClientLogs('delete', $event)"
|
||||
v-model="deletes"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<QCheckbox
|
||||
:label="t('Accesses')"
|
||||
@update:model-value="getClientLogs('select', $event)"
|
||||
v-model="select"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<VnInputDate :label="t('Date')" class="q-mt-sm" />
|
||||
<VnInput :label="t('To')" clearable class="q-mt-md" />
|
||||
</div>
|
||||
</QDrawer>
|
||||
|
||||
<QPageSticky
|
||||
:offset="[18, 18]"
|
||||
v-if="radioButtonValue !== 'all' || insert || update || deletes || select"
|
||||
>
|
||||
<QBtn color="primary" fab icon="filter_alt_off" />
|
||||
<QTooltip>
|
||||
{{ t('Quit filter') }}
|
||||
</QTooltip>
|
||||
</QPageSticky>
|
||||
<VnLog model="Client" />
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Search: Buscar
|
||||
Search by id or concept: xxx
|
||||
Entity: Entidad
|
||||
All: Todo
|
||||
User: Usuario
|
||||
System: Sistema
|
||||
Changes: Cambios
|
||||
Search by changes: xxx
|
||||
Creates: Crea
|
||||
Edits: Modifica
|
||||
Deletes: Elimina
|
||||
Accesses: Accede
|
||||
Date: Fecha
|
||||
To: Hasta
|
||||
Quit filter: Quitar filtro
|
||||
</i18n>
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import VnNotes from 'src/components/ui/VnNotes.vue';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const noteFilter = {
|
||||
const noteFilter = computed(() => {
|
||||
return {
|
||||
order: 'created DESC',
|
||||
where: {
|
||||
clientFk: `${route.params.id}`,
|
||||
},
|
||||
};
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,146 +1,107 @@
|
|||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { ref, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { useRoute } from 'vue-router';
|
||||
import { toCurrency, toDate } from 'src/filters';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnTable from 'components/VnTable/VnTable.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const rows = ref([]);
|
||||
|
||||
const filter = {
|
||||
const tableRef = ref();
|
||||
const filter = computed(() => {
|
||||
return {
|
||||
where: { clientFk: route.params.id },
|
||||
order: ['started DESC'],
|
||||
limit: 20,
|
||||
};
|
||||
});
|
||||
const componentColumn = (type) => {
|
||||
return {
|
||||
columnFilter: {
|
||||
component: type,
|
||||
},
|
||||
columnCreate: {
|
||||
component: type,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const tableColumnComponents = {
|
||||
since: {
|
||||
component: 'span',
|
||||
props: () => {},
|
||||
event: () => {},
|
||||
},
|
||||
to: {
|
||||
component: 'span',
|
||||
props: () => {},
|
||||
event: () => {},
|
||||
},
|
||||
amount: {
|
||||
component: 'span',
|
||||
props: () => {},
|
||||
event: () => {},
|
||||
},
|
||||
period: {
|
||||
component: 'span',
|
||||
props: () => {},
|
||||
event: () => {},
|
||||
},
|
||||
};
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
align: 'left',
|
||||
field: 'started',
|
||||
label: t('Since'),
|
||||
name: 'since',
|
||||
format: (value) => toDate(value),
|
||||
label: t('globals.since'),
|
||||
name: 'started',
|
||||
format: ({ started }) => toDate(started),
|
||||
create: true,
|
||||
...componentColumn('date'),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
field: 'finished',
|
||||
label: t('To'),
|
||||
name: 'to',
|
||||
format: (value) => toDate(value),
|
||||
name: 'finished',
|
||||
label: t('globals.to'),
|
||||
format: ({ finished }) => toDate(finished),
|
||||
create: true,
|
||||
...componentColumn('date'),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
field: 'amount',
|
||||
label: t('Amount'),
|
||||
name: 'amount',
|
||||
format: (value) => toCurrency(value),
|
||||
label: t('globals.amount'),
|
||||
format: ({ amount }) => toCurrency(amount),
|
||||
create: true,
|
||||
...componentColumn('number'),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
field: 'period',
|
||||
label: t('Period'),
|
||||
name: 'period',
|
||||
label: t('Period'),
|
||||
create: true,
|
||||
...componentColumn('number'),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'tableActions',
|
||||
actions: [
|
||||
{
|
||||
title: t('Finish that recovery period'),
|
||||
icon: 'lock',
|
||||
show: (row) => !row.finished,
|
||||
action: ({ id }) => setFinished(id),
|
||||
isPrimary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const toCustomerRecoverieCreate = () => {
|
||||
router.push({ name: 'CustomerRecoverieCreate' });
|
||||
};
|
||||
function setFinished(id) {
|
||||
axios.patch(`Recoveries/${id}`, { finished: Date.vnNow() });
|
||||
tableRef.value.reload();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
:filter="filter"
|
||||
@on-fetch="(data) => (rows = data)"
|
||||
auto-load
|
||||
<VnTable
|
||||
ref="tableRef"
|
||||
data-key="Recoveries"
|
||||
url="Recoveries"
|
||||
/>
|
||||
|
||||
<div class="full-width flex justify-center">
|
||||
<QPage class="card-width q-pa-lg">
|
||||
<QTable
|
||||
search-url="recoveries"
|
||||
:filter="filter"
|
||||
order="started DESC"
|
||||
:columns="columns"
|
||||
:no-data-label="t('globals.noResults')"
|
||||
:pagination="{ rowsPerPage: 12 }"
|
||||
:rows="rows"
|
||||
class="full-width q-mt-md"
|
||||
row-key="id"
|
||||
>
|
||||
<template #body-cell="props">
|
||||
<QTd :props="props">
|
||||
<QTr :props="props" class="cursor-pointer">
|
||||
<component
|
||||
:is="tableColumnComponents[props.col.name].component"
|
||||
class="col-content"
|
||||
v-bind="
|
||||
tableColumnComponents[props.col.name].props(props)
|
||||
"
|
||||
@click="
|
||||
tableColumnComponents[props.col.name].event(props)
|
||||
"
|
||||
>
|
||||
{{ props.value }}
|
||||
</component>
|
||||
</QTr>
|
||||
</QTd>
|
||||
</template>
|
||||
</QTable>
|
||||
</QPage>
|
||||
</div>
|
||||
|
||||
<QPageSticky :offset="[18, 18]">
|
||||
<QBtn @click.stop="toCustomerRecoverieCreate()" color="primary" fab icon="add" />
|
||||
<QTooltip>
|
||||
{{ t('New recoverie') }}
|
||||
</QTooltip>
|
||||
</QPageSticky>
|
||||
:use-model="true"
|
||||
:right-search="false"
|
||||
:create="{
|
||||
urlCreate: 'Recoveries',
|
||||
title: 'New recovery',
|
||||
onDataSaved: () => tableRef.reload(),
|
||||
formInitialData: { clientFk: route.params.id, started: Date.vnNew() },
|
||||
}"
|
||||
auto-load
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.consignees-card {
|
||||
border: 2px solid var(--vn-accent-color);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.label-color {
|
||||
color: var(--vn-label-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Since: Desde
|
||||
To: Hasta
|
||||
Amount: Importe
|
||||
Period: Periodo
|
||||
New recoverie: Nuevo recobro
|
||||
New recovery: Nuevo recobro
|
||||
Finish that recovery period: Terminar recobro
|
||||
</i18n>
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import VnSms from 'src/components/ui/VnSms.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const id = route.params.id;
|
||||
|
||||
const where = {
|
||||
clientFk: id,
|
||||
const where = computed(() => {
|
||||
return {
|
||||
clientFk: route.params.id,
|
||||
ticketFk: null,
|
||||
};
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="column items-center">
|
||||
<VnSms url="clientSms" :where="where" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -2,164 +2,81 @@
|
|||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import axios from 'axios';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
import { useValidator } from 'src/composables/useValidator';
|
||||
import useNotify from 'src/composables/useNotify';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import CustomerChangePassword from 'src/pages/Customer/components/CustomerChangePassword.vue';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
|
||||
const { notify } = useNotify();
|
||||
const { t } = useI18n();
|
||||
const { validate } = useValidator();
|
||||
const quasar = useQuasar();
|
||||
const route = useRoute();
|
||||
const stateStore = useStateStore();
|
||||
|
||||
const active = ref(false);
|
||||
const canChangePassword = ref(0);
|
||||
const email = ref(null);
|
||||
const isLoading = ref(false);
|
||||
const name = ref(null);
|
||||
const usersPreviewRef = ref(null);
|
||||
const user = ref([]);
|
||||
|
||||
const dataChanges = computed(() => {
|
||||
return (
|
||||
user.value.active !== active.value ||
|
||||
user.value.email !== email.value ||
|
||||
user.value.name !== name.value
|
||||
);
|
||||
const filter = computed(() => {
|
||||
return {
|
||||
fields: ['active', 'email', 'name'],
|
||||
where: { id: route.params.id },
|
||||
};
|
||||
});
|
||||
|
||||
const filter = { where: { id: `${route.params.id}` } };
|
||||
|
||||
const showChangePasswordDialog = () => {
|
||||
quasar.dialog({
|
||||
component: CustomerChangePassword,
|
||||
componentProps: {
|
||||
id: route.params.id,
|
||||
promise: usersPreviewRef.value.fetch(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const setInitialData = () => {
|
||||
if (user.value.length) {
|
||||
active.value = user.value[0].active;
|
||||
email.value = user.value[0].email;
|
||||
name.value = user.value[0].name;
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
isLoading.value = true;
|
||||
|
||||
const payload = {
|
||||
active: active.value,
|
||||
email: email.value,
|
||||
name: name.value,
|
||||
};
|
||||
try {
|
||||
await axios.patch(`Clients/${route.params.id}/updateUser`, payload);
|
||||
notify('globals.dataSaved', 'positive');
|
||||
if (usersPreviewRef.value) usersPreviewRef.value.fetch();
|
||||
} catch (error) {
|
||||
notify('errors.create', 'negative');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
async function hasCustomerRole() {
|
||||
const { data } = await axios(`Clients/${route.params.id}/hasCustomerRole`);
|
||||
canChangePassword.value = data;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
<FormModel
|
||||
url="VnUsers/preview"
|
||||
:url-update="`Clients/${route.params.id}/updateUser`"
|
||||
:filter="filter"
|
||||
@on-fetch="
|
||||
(data) => {
|
||||
user = data;
|
||||
setInitialData();
|
||||
model="webAccess"
|
||||
:mapper="
|
||||
({ active, name, email }) => {
|
||||
return {
|
||||
active,
|
||||
name,
|
||||
email,
|
||||
};
|
||||
}
|
||||
"
|
||||
@on-fetch="hasCustomerRole()"
|
||||
auto-load
|
||||
ref="usersPreviewRef"
|
||||
url="VnUsers/preview"
|
||||
/>
|
||||
<FetchData
|
||||
:url="`Clients/${route.params.id}/hasCustomerRole`"
|
||||
@on-fetch="(data) => (canChangePassword = data)"
|
||||
auto-load
|
||||
/>
|
||||
|
||||
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
||||
<QBtnGroup push class="q-gutter-x-sm">
|
||||
<QBtn
|
||||
:disabled="isLoading"
|
||||
:label="t('globals.reset')"
|
||||
:loading="isLoading"
|
||||
@click="setInitialData"
|
||||
color="primary"
|
||||
flat
|
||||
icon="restart_alt"
|
||||
type="reset"
|
||||
/>
|
||||
<QBtn
|
||||
:disabled="isLoading"
|
||||
:label="t('Change password')"
|
||||
:loading="isLoading"
|
||||
@click.stop="showChangePasswordDialog()"
|
||||
color="primary"
|
||||
flat
|
||||
icon="edit"
|
||||
v-if="canChangePassword"
|
||||
/>
|
||||
<QBtn
|
||||
:disabled="isLoading || !dataChanges"
|
||||
:label="t('globals.save')"
|
||||
:loading="isLoading"
|
||||
@click="onSubmit"
|
||||
color="primary"
|
||||
icon="save"
|
||||
/>
|
||||
</QBtnGroup>
|
||||
</Teleport>
|
||||
|
||||
<div class="full-width flex justify-center">
|
||||
<QCard class="card-width q-pa-lg">
|
||||
<QCardSection>
|
||||
<QForm>
|
||||
<QCheckbox :label="t('Enable web access')" v-model="active" />
|
||||
|
||||
<div class="q-px-sm">
|
||||
<VnInput :label="t('User')" clearable v-model="name" />
|
||||
>
|
||||
<template #form="{ data, validate }">
|
||||
<QCheckbox :label="t('Enable web access')" v-model="data.active" />
|
||||
<VnInput :label="t('User')" clearable v-model="data.name" />
|
||||
<VnInput
|
||||
:label="t('Recovery email')"
|
||||
:rules="validate('client.email')"
|
||||
clearable
|
||||
type="email"
|
||||
v-model="email"
|
||||
v-model="data.email"
|
||||
class="q-mt-sm"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="info" class="cursor-pointer">
|
||||
<QTooltip>{{
|
||||
t(
|
||||
'This email is used for user to regain access their account'
|
||||
)
|
||||
}}</QTooltip>
|
||||
</QIcon>
|
||||
:info="t('This email is used for user to regain access their account')"
|
||||
/>
|
||||
</template>
|
||||
</VnInput>
|
||||
</div>
|
||||
</QForm>
|
||||
</QCardSection>
|
||||
</QCard>
|
||||
</div>
|
||||
<template #moreActions>
|
||||
<QBtn
|
||||
:label="t('Change password')"
|
||||
color="primary"
|
||||
icon="edit"
|
||||
:disable="!canChangePassword"
|
||||
@click="showChangePasswordDialog()"
|
||||
/>
|
||||
</template>
|
||||
</FormModel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
|
|
|
@ -357,7 +357,7 @@ const columns = computed(() => [
|
|||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
title: t('Client ticket list'),
|
||||
title: t('components.smartCard.viewSummary'),
|
||||
icon: 'preview',
|
||||
action: (row) => viewSummary(row.id, CustomerSummary),
|
||||
},
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useI18n } from 'vue-i18n';
|
|||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
import VnCurrency from 'src/components/common/VnCurrency.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
|
@ -47,7 +47,11 @@ const props = defineProps({
|
|||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnCurrency v-model="params.amount" is-outlined />
|
||||
<VnInputNumber
|
||||
:label="t('Amount')"
|
||||
v-model="params.amount"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
|
|
|
@ -19,8 +19,6 @@ const { t } = useI18n();
|
|||
const { hasAny } = useRole();
|
||||
const isAdministrative = () => hasAny(['administrative']);
|
||||
|
||||
const suppliersOptions = ref([]);
|
||||
const travelsOptions = ref([]);
|
||||
const companiesOptions = ref([]);
|
||||
const currenciesOptions = ref([]);
|
||||
|
||||
|
@ -29,20 +27,6 @@ const onFilterTravelSelected = (formData, id) => {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
url="Suppliers"
|
||||
:filter="{ fields: ['id', 'nickname'] }"
|
||||
order="nickname"
|
||||
@on-fetch="(data) => (suppliersOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
url="Travels/filter"
|
||||
:filter="{ fields: ['id', 'warehouseInName'] }"
|
||||
order="id"
|
||||
@on-fetch="(data) => (travelsOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
ref="companiesRef"
|
||||
url="Companies"
|
||||
|
@ -71,9 +55,10 @@ const onFilterTravelSelected = (formData, id) => {
|
|||
<VnSelect
|
||||
:label="t('entry.basicData.supplier')"
|
||||
v-model="data.supplierFk"
|
||||
:options="suppliersOptions"
|
||||
url="Suppliers"
|
||||
option-value="id"
|
||||
option-label="nickname"
|
||||
:fields="['id', 'nickname']"
|
||||
hide-selected
|
||||
:required="true"
|
||||
map-options
|
||||
|
@ -92,7 +77,8 @@ const onFilterTravelSelected = (formData, id) => {
|
|||
<VnSelectDialog
|
||||
:label="t('entry.basicData.travel')"
|
||||
v-model="data.travelFk"
|
||||
:options="travelsOptions"
|
||||
url="Travels/filter"
|
||||
:fields="['id', 'warehouseInName']"
|
||||
option-value="id"
|
||||
option-label="warehouseInName"
|
||||
map-options
|
||||
|
|
|
@ -10,8 +10,10 @@ import EntryFilter from '../EntryFilter.vue';
|
|||
:descriptor="EntryDescriptor"
|
||||
:filter-panel="EntryFilter"
|
||||
search-data-key="EntryList"
|
||||
search-url="Entries/filter"
|
||||
searchbar-label="Search entries"
|
||||
searchbar-info="You can search by entry reference"
|
||||
:searchbar-props="{
|
||||
url: 'Entries/filter',
|
||||
label: 'Search entries',
|
||||
info: 'You can search by entry reference',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -20,7 +20,6 @@ const props = defineProps({
|
|||
|
||||
const currenciesOptions = ref([]);
|
||||
const companiesOptions = ref([]);
|
||||
const suppliersOptions = ref([]);
|
||||
|
||||
const stateStore = useStateStore();
|
||||
onMounted(async () => {
|
||||
|
@ -45,14 +44,6 @@ onMounted(async () => {
|
|||
@on-fetch="(data) => (currenciesOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
url="Suppliers"
|
||||
:filter="{ fields: ['id', 'nickname', 'name'] }"
|
||||
order="nickname"
|
||||
@on-fetch="(data) => (suppliersOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
|
||||
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<div class="q-gutter-x-xs">
|
||||
|
@ -135,9 +126,11 @@ onMounted(async () => {
|
|||
:label="t('params.supplierFk')"
|
||||
v-model="params.supplierFk"
|
||||
@update:model-value="searchFn()"
|
||||
:options="suppliersOptions"
|
||||
url="Suppliers"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
:fields="['id', 'name', 'nickname']"
|
||||
sort-by="nickname"
|
||||
hide-selected
|
||||
dense
|
||||
outlined
|
||||
|
|
|
@ -7,11 +7,19 @@ 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';
|
||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||
import EntrySummary from './Card/EntrySummary.vue';
|
||||
import VnUserLink from 'components/ui/VnUserLink.vue';
|
||||
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||
import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.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();
|
||||
|
||||
const { viewSummary } = useSummaryDialog();
|
||||
const entryFilter = {
|
||||
include: [
|
||||
{
|
||||
|
@ -142,6 +150,12 @@ const columns = computed(() => [
|
|||
create: true,
|
||||
format: (row, dashIfEmpty) => dashIfEmpty(row.travelRef),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
label: t('entry.list.tableVisibleColumns.invoiceAmount'),
|
||||
name: 'invoiceAmount',
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
label: t('entry.list.tableVisibleColumns.isExcludedFromAvailable'),
|
||||
|
@ -168,6 +182,18 @@ const columns = computed(() => [
|
|||
inWhere: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
name: 'tableActions',
|
||||
actions: [
|
||||
{
|
||||
title: t('components.smartCard.viewSummary'),
|
||||
icon: 'preview',
|
||||
action: (row) => viewSummary(row.id, EntrySummary),
|
||||
isPrimary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
onMounted(async () => {
|
||||
stateStore.rightDrawer = true;
|
||||
|
@ -201,7 +227,20 @@ onMounted(async () => {
|
|||
redirect="entry"
|
||||
auto-load
|
||||
:right-search="false"
|
||||
/>
|
||||
>
|
||||
<template #column-supplierFk="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.supplierName }}
|
||||
<SupplierDescriptorProxy :id="row.supplierFk" />
|
||||
</span>
|
||||
</template>
|
||||
<template #column-travelFk="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.travelRef }}
|
||||
<TravelDescriptorProxy :id="row.travelFk" />
|
||||
</span>
|
||||
</template>
|
||||
</VnTable>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
|
|
|
@ -130,8 +130,6 @@ onBeforeMount(async () => {
|
|||
});
|
||||
|
||||
onBeforeRouteUpdate(async (to, from) => {
|
||||
invoiceInCorrection.correcting.length = 0;
|
||||
invoiceInCorrection.corrected = null;
|
||||
if (to.params.id !== from.params.id) {
|
||||
await setInvoiceCorrection(to.params.id);
|
||||
const { data } = await axios.get(`InvoiceIns/${to.params.id}/getTotals`);
|
||||
|
@ -140,6 +138,8 @@ onBeforeRouteUpdate(async (to, from) => {
|
|||
});
|
||||
|
||||
async function setInvoiceCorrection(id) {
|
||||
invoiceInCorrection.correcting.length = 0;
|
||||
invoiceInCorrection.corrected = null;
|
||||
const { data: correctingData } = await axios.get('InvoiceInCorrections', {
|
||||
params: { filter: { where: { correctingFk: id } } },
|
||||
});
|
||||
|
@ -198,7 +198,6 @@ async function cloneInvoice() {
|
|||
const isAdministrative = () => hasAny(['administrative']);
|
||||
|
||||
const isAgricultural = () => {
|
||||
console.error(config);
|
||||
if (!config.value) return false;
|
||||
return (
|
||||
invoiceIn.value?.supplier?.sageFarmerWithholdingFk ===
|
||||
|
|
|
@ -8,23 +8,22 @@ import { useArrayData } from 'src/composables/useArrayData';
|
|||
import CrudModel from 'src/components/CrudModel.vue';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnCurrency from 'src/components/common/VnCurrency.vue';
|
||||
import { toCurrency } from 'src/filters';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { notify } = useNotify();
|
||||
const { t } = useI18n();
|
||||
const arrayData = useArrayData();
|
||||
const invoiceIn = computed(() => arrayData.store.data);
|
||||
const currency = computed(() => invoiceIn.value?.currency?.code);
|
||||
|
||||
const rowsSelected = ref([]);
|
||||
const banks = ref([]);
|
||||
const invoiceInFormRef = ref();
|
||||
const invoiceId = +route.params.id;
|
||||
|
||||
const placeholder = 'yyyy/mm/dd';
|
||||
|
||||
const filter = { where: { invoiceInFk: invoiceId } };
|
||||
|
||||
const columns = computed(() => [
|
||||
|
@ -104,42 +103,7 @@ const getTotalAmount = (rows) => rows.reduce((acc, { amount }) => acc + +amount,
|
|||
>
|
||||
<template #body-cell-duedate="{ row }">
|
||||
<QTd>
|
||||
<QInput
|
||||
v-model="row.dueDated"
|
||||
mask="date"
|
||||
:placeholder="placeholder"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="event" class="cursor-pointer">
|
||||
<QPopupProxy
|
||||
cover
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
>
|
||||
<QDate v-model="row.dueDated" landscape>
|
||||
<div
|
||||
class="row items-center justify-end q-gutter-sm"
|
||||
>
|
||||
<QBtn
|
||||
:label="t('globals.cancel')"
|
||||
color="primary"
|
||||
flat
|
||||
v-close-popup
|
||||
/>
|
||||
<QBtn
|
||||
:label="t('globals.confirm')"
|
||||
color="primary"
|
||||
flat
|
||||
v-close-popup
|
||||
/>
|
||||
</div>
|
||||
</QDate>
|
||||
</QPopupProxy>
|
||||
</QIcon>
|
||||
</template>
|
||||
</QInput>
|
||||
<VnInputDate v-model="row.dueDated" />
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-bank="{ row, col }">
|
||||
|
@ -164,7 +128,7 @@ const getTotalAmount = (rows) => rows.reduce((acc, { amount }) => acc + +amount,
|
|||
</template>
|
||||
<template #body-cell-amount="{ row }">
|
||||
<QTd>
|
||||
<VnCurrency
|
||||
<VnInputNumber
|
||||
v-model="row.amount"
|
||||
:is-outlined="false"
|
||||
clearable
|
||||
|
@ -174,11 +138,11 @@ const getTotalAmount = (rows) => rows.reduce((acc, { amount }) => acc + +amount,
|
|||
</template>
|
||||
<template #body-cell-foreignvalue="{ row }">
|
||||
<QTd>
|
||||
<QInput
|
||||
<VnInputNumber
|
||||
:class="{
|
||||
'no-pointer-events': !isNotEuro(invoiceIn.currency.code),
|
||||
'no-pointer-events': !isNotEuro(currency),
|
||||
}"
|
||||
:disable="!isNotEuro(invoiceIn.currency.code)"
|
||||
:disable="!isNotEuro(currency)"
|
||||
v-model="row.foreignValue"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
|
@ -191,9 +155,7 @@ const getTotalAmount = (rows) => rows.reduce((acc, { amount }) => acc + +amount,
|
|||
<QTd />
|
||||
<QTd />
|
||||
<QTd>
|
||||
{{
|
||||
toCurrency(getTotalAmount(rows), invoiceIn.currency.code)
|
||||
}}
|
||||
{{ toCurrency(getTotalAmount(rows), currency) }}
|
||||
</QTd>
|
||||
<QTd />
|
||||
</QTr>
|
||||
|
@ -207,51 +169,11 @@ const getTotalAmount = (rows) => rows.reduce((acc, { amount }) => acc + +amount,
|
|||
<QSeparator />
|
||||
<QList>
|
||||
<QItem>
|
||||
<QInput
|
||||
<VnInputDate
|
||||
class="full-width"
|
||||
:label="t('Date')"
|
||||
v-model="props.row.dueDated"
|
||||
mask="date"
|
||||
:placeholder="placeholder"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="event" class="cursor-pointer">
|
||||
<QPopupProxy
|
||||
cover
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
>
|
||||
<QDate
|
||||
v-model="props.row.dueDated"
|
||||
landscape
|
||||
>
|
||||
<div
|
||||
class="row items-center justify-end q-gutter-sm"
|
||||
>
|
||||
<QBtn
|
||||
:label="
|
||||
t('globals.cancel')
|
||||
"
|
||||
color="primary"
|
||||
flat
|
||||
v-close-popup
|
||||
/>
|
||||
<QBtn
|
||||
:label="
|
||||
t('globals.confirm')
|
||||
"
|
||||
color="primary"
|
||||
flat
|
||||
v-close-popup
|
||||
/>
|
||||
</div>
|
||||
</QDate>
|
||||
</QPopupProxy>
|
||||
</QIcon>
|
||||
</template>
|
||||
</QInput>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<VnSelect
|
||||
|
@ -274,24 +196,20 @@ const getTotalAmount = (rows) => rows.reduce((acc, { amount }) => acc + +amount,
|
|||
</VnSelect>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QInput
|
||||
<VnInputNumber
|
||||
:label="t('Amount')"
|
||||
class="full-width"
|
||||
v-model="props.row.amount"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
/>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QInput
|
||||
<VnInputNumber
|
||||
:label="t('Foreign value')"
|
||||
class="full-width"
|
||||
:class="{
|
||||
'no-pointer-events': !isNotEuro(
|
||||
invoiceIn.currency.code
|
||||
),
|
||||
'no-pointer-events': !isNotEuro(currency),
|
||||
}"
|
||||
:disable="!isNotEuro(invoiceIn.currency.code)"
|
||||
:disable="!isNotEuro(currency)"
|
||||
v-model="props.row.foreignValue"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
|
|
|
@ -7,6 +7,7 @@ import CrudModel from 'src/components/CrudModel.vue';
|
|||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
@ -115,11 +116,7 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
>
|
||||
<template #body-cell="{ row, col }">
|
||||
<QTd>
|
||||
<QInput
|
||||
v-model="row[col.name]"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
/>
|
||||
<VnInputNumber v-model="row[col.name]" />
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-code="{ row, col }">
|
||||
|
@ -203,7 +200,7 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
]"
|
||||
:key="index"
|
||||
>
|
||||
<QInput
|
||||
<VnInputNumber
|
||||
:label="t(value)"
|
||||
class="full-width"
|
||||
v-model="props.row[value]"
|
||||
|
|
|
@ -120,7 +120,6 @@ const intrastatColumns = ref([
|
|||
},
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
style: 'width: 10px',
|
||||
},
|
||||
{
|
||||
name: 'amount',
|
||||
|
@ -128,7 +127,6 @@ const intrastatColumns = ref([
|
|||
field: (row) => toCurrency(row.amount, currency.value),
|
||||
sortable: true,
|
||||
align: 'left',
|
||||
style: 'width: 10px',
|
||||
},
|
||||
{
|
||||
name: 'net',
|
||||
|
@ -415,6 +413,11 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
</QTh>
|
||||
</QTr>
|
||||
</template>
|
||||
<template #body-cell-code="{ value: codeCell }">
|
||||
<QTd :title="codeCell" shrink>
|
||||
{{ codeCell }}
|
||||
</QTd>
|
||||
</template>
|
||||
<template #bottom-row>
|
||||
<QTr class="bg">
|
||||
<QTd></QTd>
|
||||
|
|
|
@ -9,7 +9,8 @@ import { toCurrency } from 'src/filters';
|
|||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import CrudModel from 'src/components/CrudModel.vue';
|
||||
import VnCurrency from 'src/components/common/VnCurrency.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const quasar = useQuasar();
|
||||
|
@ -205,7 +206,7 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
:grid="$q.screen.lt.sm"
|
||||
>
|
||||
<template #body-cell-expense="{ row, col }">
|
||||
<QTd auto-width>
|
||||
<QTd>
|
||||
<VnSelect
|
||||
v-model="row[col.model]"
|
||||
:options="col.options"
|
||||
|
@ -223,6 +224,7 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
name="close"
|
||||
@click.stop="value = null"
|
||||
class="cursor-pointer"
|
||||
size="xs"
|
||||
/>
|
||||
<QIcon
|
||||
@click.stop.prevent="newExpenseRef.show()"
|
||||
|
@ -240,11 +242,12 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
</template>
|
||||
<template #body-cell-taxablebase="{ row }">
|
||||
<QTd>
|
||||
<VnCurrency
|
||||
{{ currency }}
|
||||
<VnInputNumber
|
||||
:class="{
|
||||
'no-pointer-events': isNotEuro(invoiceIn.currency.code),
|
||||
'no-pointer-events': isNotEuro(currency),
|
||||
}"
|
||||
:disable="isNotEuro(invoiceIn.currency.code)"
|
||||
:disable="isNotEuro(currency)"
|
||||
label=""
|
||||
clear-icon="close"
|
||||
v-model="row.taxableBase"
|
||||
|
@ -308,11 +311,11 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
</template>
|
||||
<template #body-cell-foreignvalue="{ row }">
|
||||
<QTd>
|
||||
<QInput
|
||||
<VnInputNumber
|
||||
:class="{
|
||||
'no-pointer-events': !isNotEuro(invoiceIn.currency.code),
|
||||
'no-pointer-events': !isNotEuro(currency),
|
||||
}"
|
||||
:disable="!isNotEuro(invoiceIn.currency.code)"
|
||||
:disable="!isNotEuro(currency)"
|
||||
v-model="row.foreignValue"
|
||||
/>
|
||||
</QTd>
|
||||
|
@ -356,15 +359,13 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
</VnSelect>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<VnCurrency
|
||||
<VnInputNumber
|
||||
:label="t('Taxable base')"
|
||||
:class="{
|
||||
'no-pointer-events': isNotEuro(
|
||||
invoiceIn.currency.code
|
||||
),
|
||||
'no-pointer-events': isNotEuro(currency),
|
||||
}"
|
||||
class="full-width"
|
||||
:disable="isNotEuro(invoiceIn.currency.code)"
|
||||
:disable="isNotEuro(currency)"
|
||||
clear-icon="close"
|
||||
v-model="props.row.taxableBase"
|
||||
clearable
|
||||
|
@ -421,15 +422,13 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
{{ toCurrency(taxRate(props.row), currency) }}
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QInput
|
||||
<VnInputNumber
|
||||
:label="t('Foreign value')"
|
||||
class="full-width"
|
||||
:class="{
|
||||
'no-pointer-events': !isNotEuro(
|
||||
invoiceIn.currency.code
|
||||
),
|
||||
'no-pointer-events': !isNotEuro(currency),
|
||||
}"
|
||||
:disable="!isNotEuro(invoiceIn.currency.code)"
|
||||
:disable="!isNotEuro(currency)"
|
||||
v-model="props.row.foreignValue"
|
||||
/>
|
||||
</QItem>
|
||||
|
@ -453,7 +452,11 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
</QCardSection>
|
||||
<QCardSection class="q-pt-none">
|
||||
<QItem>
|
||||
<QInput :label="`${t('Code')}*`" v-model="newExpense.code" />
|
||||
<VnInput
|
||||
:label="`${t('Code')}*`"
|
||||
v-model="newExpense.code"
|
||||
:required="true"
|
||||
/>
|
||||
<QCheckbox
|
||||
dense
|
||||
size="sm"
|
||||
|
@ -462,7 +465,7 @@ const formatOpt = (row, { model, options }, prop) => {
|
|||
/>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QInput
|
||||
<VnInput
|
||||
:label="`${t('Descripction')}*`"
|
||||
v-model="newExpense.description"
|
||||
/>
|
||||
|
|
|
@ -26,8 +26,7 @@ const newInvoiceIn = reactive({
|
|||
companyFk: user.value.companyFk || null,
|
||||
issued: Date.vnNew(),
|
||||
});
|
||||
const suppliersOptions = ref([]);
|
||||
const companiesOptions = ref([]);
|
||||
const companies = ref([]);
|
||||
|
||||
const redirectToInvoiceInBasicData = (__, { id }) => {
|
||||
router.push({ name: 'InvoiceInBasicData', params: { id } });
|
||||
|
@ -35,19 +34,12 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
url="Suppliers"
|
||||
:filter="{ fields: ['id', 'nickname'] }"
|
||||
order="nickname"
|
||||
@on-fetch="(data) => (suppliersOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
ref="companiesRef"
|
||||
url="Companies"
|
||||
:filter="{ fields: ['id', 'code'] }"
|
||||
order="code"
|
||||
@on-fetch="(data) => (companiesOptions = data)"
|
||||
@on-fetch="(data) => (companies = data)"
|
||||
auto-load
|
||||
/>
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
|
@ -69,9 +61,10 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
|
|||
<template #form="{ data, validate }">
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
url="Suppliers"
|
||||
:fields="['id', 'nickname']"
|
||||
:label="t('Supplier')"
|
||||
v-model="data.supplierFk"
|
||||
:options="suppliersOptions"
|
||||
option-value="id"
|
||||
option-label="nickname"
|
||||
hide-selected
|
||||
|
@ -98,7 +91,7 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
|
|||
<VnSelect
|
||||
:label="t('Company')"
|
||||
v-model="data.companyFk"
|
||||
:options="companiesOptions"
|
||||
:options="companies"
|
||||
option-value="id"
|
||||
option-label="code"
|
||||
map-options
|
||||
|
|
|
@ -6,8 +6,8 @@ import VnSelect from 'components/common/VnSelect.vue';
|
|||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
import VnCurrency from 'src/components/common/VnCurrency.vue';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
defineProps({ dataKey: { type: String, required: true } });
|
||||
|
@ -28,6 +28,22 @@ const activities = ref([]);
|
|||
</div>
|
||||
</template>
|
||||
<template #body="{ params, searchFn }">
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
v-model="params.supplierFk"
|
||||
url="Suppliers"
|
||||
:fields="['id', 'nickname']"
|
||||
:label="t('params.supplierFk')"
|
||||
option-value="id"
|
||||
option-label="nickname"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
:filter-options="['id', 'name']"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
|
@ -50,17 +66,30 @@ const activities = ref([]);
|
|||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
v-model="params.supplierFk"
|
||||
url="Suppliers"
|
||||
:fields="['id', 'nickname']"
|
||||
:label="t('params.supplierFk')"
|
||||
option-value="id"
|
||||
option-label="nickname"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
:filter-options="['id', 'name']"
|
||||
<VnInput
|
||||
:label="t('params.serialNumber')"
|
||||
v-model="params.serialNumber"
|
||||
is-outlined
|
||||
lazy-rules
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
:label="t('params.serial')"
|
||||
v-model="params.serial"
|
||||
is-outlined
|
||||
lazy-rules
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
:label="t('Issued')"
|
||||
v-model="params.issued"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
|
@ -76,39 +105,20 @@ const activities = ref([]);
|
|||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnCurrency v-model="params.amount" is-outlined />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate :label="t('From')" v-model="params.from" is-outlined />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate :label="t('To')" v-model="params.to" is-outlined />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
:label="t('Issued')"
|
||||
v-model="params.issued"
|
||||
<VnInput
|
||||
:label="t('params.awb')"
|
||||
v-model="params.awbCode"
|
||||
is-outlined
|
||||
lazy-rules
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
:label="t('params.supplierActivityFk')"
|
||||
v-model="params.supplierActivityFk"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
option-value="code"
|
||||
option-label="name"
|
||||
:options="activities"
|
||||
<VnInputNumber
|
||||
:label="t('Amount')"
|
||||
v-model="params.amount"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
|
@ -133,32 +143,16 @@ const activities = ref([]);
|
|||
<QExpansionItem :label="t('More options')" expand-separator>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
:label="t('params.serialNumber')"
|
||||
v-model="params.serialNumber"
|
||||
<VnInputDate
|
||||
:label="t('From')"
|
||||
v-model="params.from"
|
||||
is-outlined
|
||||
lazy-rules
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
:label="t('params.serial')"
|
||||
v-model="params.serial"
|
||||
is-outlined
|
||||
lazy-rules
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
:label="t('params.awb')"
|
||||
v-model="params.awbCode"
|
||||
is-outlined
|
||||
lazy-rules
|
||||
/>
|
||||
<VnInputDate :label="t('To')" v-model="params.to" is-outlined />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QExpansionItem>
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
<script setup>
|
||||
import { onMounted, onUnmounted } from 'vue';
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { downloadFile } from 'src/composables/downloadFile';
|
||||
import { toDate, toCurrency } from 'src/filters/index';
|
||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||
import VnLv from 'src/components/ui/VnLv.vue';
|
||||
import CardList from 'src/components/ui/CardList.vue';
|
||||
import InvoiceInFilter from './InvoiceInFilter.vue';
|
||||
import InvoiceInSummary from './Card/InvoiceInSummary.vue';
|
||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||
import RightMenu from 'src/components/common/RightMenu.vue';
|
||||
import InvoiceInSearchbar from 'src/pages/InvoiceIn/InvoiceInSearchbar.vue';
|
||||
import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const { viewSummary } = useSummaryDialog();
|
||||
|
@ -20,8 +21,91 @@ const { t } = useI18n();
|
|||
|
||||
onMounted(async () => (stateStore.rightDrawer = true));
|
||||
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||
</script>
|
||||
|
||||
const tableRef = ref();
|
||||
const cols = computed(() => [
|
||||
{
|
||||
align: 'left',
|
||||
name: 'id',
|
||||
label: 'Id',
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'supplierFk',
|
||||
label: t('invoiceIn.list.supplier'),
|
||||
columnFilter: {
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Suppliers',
|
||||
fields: ['id', 'name'],
|
||||
},
|
||||
},
|
||||
columnClass: 'expand',
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'supplierRef',
|
||||
label: t('invoiceIn.list.supplierRef'),
|
||||
},
|
||||
|
||||
{
|
||||
align: 'left',
|
||||
name: 'serialNumber',
|
||||
label: t('invoiceIn.list.serialNumber'),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'serial',
|
||||
label: t('invoiceIn.list.serial'),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
label: t('invoiceIn.list.issued'),
|
||||
name: 'issued',
|
||||
component: null,
|
||||
columnFilter: {
|
||||
component: 'date',
|
||||
},
|
||||
format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.issued)),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'isBooked',
|
||||
label: t('invoiceIn.list.isBooked'),
|
||||
columnFilter: false,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'awbCode',
|
||||
label: t('invoiceIn.list.awb'),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'amount',
|
||||
label: t('invoiceIn.list.amount'),
|
||||
format: ({ amount }) => toCurrency(amount),
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
name: 'tableActions',
|
||||
actions: [
|
||||
{
|
||||
title: t('components.smartCard.openSummary'),
|
||||
icon: 'preview',
|
||||
type: 'submit',
|
||||
action: (row) => viewSummary(row.id, InvoiceInSummary),
|
||||
},
|
||||
{
|
||||
title: t('globals.download'),
|
||||
icon: 'download',
|
||||
type: 'submit',
|
||||
isPrimary: true,
|
||||
action: (row) => downloadFile(row.dmsFk),
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
<template>
|
||||
<InvoiceInSearchbar />
|
||||
<RightMenu>
|
||||
|
@ -29,92 +113,63 @@ onUnmounted(() => (stateStore.rightDrawer = false));
|
|||
<InvoiceInFilter data-key="InvoiceInList" />
|
||||
</template>
|
||||
</RightMenu>
|
||||
<QPage class="column items-center q-pa-md">
|
||||
<div class="vn-card-list">
|
||||
<VnPaginate
|
||||
<VnTable
|
||||
ref="tableRef"
|
||||
data-key="InvoiceInList"
|
||||
url="InvoiceIns/filter"
|
||||
order="issued DESC, id DESC"
|
||||
auto-load
|
||||
:order="['issued DESC', 'id DESC']"
|
||||
:create="{
|
||||
urlCreate: 'InvoiceIns',
|
||||
title: t('globals.createInvoiceIn'),
|
||||
onDataSaved: ({ id }) => tableRef.redirect(id),
|
||||
formInitialData: {},
|
||||
}"
|
||||
redirect="invoice-in"
|
||||
:columns="cols"
|
||||
:right-search="false"
|
||||
:disable-option="{ card: true }"
|
||||
:auto-load="!!$route.query.params"
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<CardList
|
||||
v-for="(row, index) of rows"
|
||||
:key="index"
|
||||
:title="row.supplierRef"
|
||||
@click="$router.push({ path: `/invoice-in/${row.id}` })"
|
||||
:id="row.id"
|
||||
>
|
||||
<template #list-items>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.list.supplierRef')"
|
||||
:value="row.supplierRef"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.list.supplier')"
|
||||
:value="row.supplierName"
|
||||
@click.stop
|
||||
>
|
||||
<template #value>
|
||||
<span class="link">
|
||||
<template #column-supplierFk="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.supplierName }}
|
||||
<SupplierDescriptorProxy :id="row.supplierFk" />
|
||||
</span>
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.list.serialNumber')"
|
||||
:value="row.serialNumber"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.list.serial')"
|
||||
:value="row.serial"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.list.issued')"
|
||||
:value="toDate(row.issued)"
|
||||
/>
|
||||
<VnLv :label="t('invoiceIn.list.awb')" :value="row.awbCode" />
|
||||
<VnLv
|
||||
:label="t('invoiceIn.list.amount')"
|
||||
:value="toCurrency(row.amount)"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('invoiceIn.list.isBooked')"
|
||||
:value="!!row.isBooked"
|
||||
/>
|
||||
<template #more-create-dialog="{ data }">
|
||||
<VnSelect
|
||||
v-model="data.supplierFk"
|
||||
url="Suppliers"
|
||||
:fields="['id', 'nickname']"
|
||||
:label="t('Supplier')"
|
||||
option-value="id"
|
||||
option-label="nickname"
|
||||
:filter-options="['id', 'name']"
|
||||
:required="true"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt?.nickname }}</QItemLabel>
|
||||
<QItemLabel caption> #{{ scope.opt?.id }} </QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
<template #actions>
|
||||
<QBtn
|
||||
:label="t('components.smartCard.openCard')"
|
||||
@click.stop="
|
||||
$router.push({ path: `/invoice-in/${row.id}` })
|
||||
"
|
||||
outline
|
||||
type="reset"
|
||||
</VnSelect>
|
||||
<VnInput
|
||||
:label="t('invoiceIn.summary.supplierRef')"
|
||||
v-model="data.supplierRef"
|
||||
/>
|
||||
|
||||
<QBtn
|
||||
:label="t('components.smartCard.openSummary')"
|
||||
@click.stop="viewSummary(row.id, InvoiceInSummary)"
|
||||
color="primary"
|
||||
type="submit"
|
||||
class="q-mt-sm"
|
||||
/>
|
||||
<QBtn
|
||||
:label="t('globals.download')"
|
||||
class="q-mt-sm"
|
||||
@click.stop="downloadFile(row.dmsFk)"
|
||||
type="submit"
|
||||
color="primary"
|
||||
<VnSelect
|
||||
url="Companies"
|
||||
:label="t('Company')"
|
||||
:fields="['id', 'code']"
|
||||
v-model="data.companyFk"
|
||||
option-value="id"
|
||||
option-label="code"
|
||||
:required="true"
|
||||
/>
|
||||
<VnInputDate :label="t('invoiceIn.summary.issued')" v-model="data.issued" />
|
||||
</template>
|
||||
</CardList>
|
||||
</template>
|
||||
</VnPaginate>
|
||||
</div>
|
||||
</QPage>
|
||||
<QPageSticky position="bottom-right" :offset="[20, 20]">
|
||||
<QBtn color="primary" icon="add" size="lg" round :href="`/#/invoice-in/create`" />
|
||||
</QPageSticky>
|
||||
</VnTable>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<script setup>
|
||||
import { ref, computed, onBeforeMount } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import axios from 'axios';
|
||||
import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||
import RightMenu from 'src/components/common/RightMenu.vue';
|
||||
import InvoiceInSerialFilter from './InvoiceInSerialFilter.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const cols = computed(() => [
|
||||
{
|
||||
align: 'left',
|
||||
name: 'serial',
|
||||
label: t('Serial'),
|
||||
columnFilter: false,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'pending',
|
||||
label: t('Pending'),
|
||||
columnFilter: false,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'total',
|
||||
label: 'Total',
|
||||
columnFilter: false,
|
||||
},
|
||||
]);
|
||||
|
||||
const daysAgo = ref();
|
||||
|
||||
onBeforeMount(async () => {
|
||||
const tableParam = useRoute().query.table;
|
||||
|
||||
if (tableParam) daysAgo.value = JSON.parse(tableParam).daysAgo;
|
||||
else
|
||||
daysAgo.value = (
|
||||
await axios.get('InvoiceInConfigs/findOne', {
|
||||
params: { filter: { fields: ['daysAgo'] } },
|
||||
})
|
||||
).data?.daysAgo;
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<RightMenu>
|
||||
<template #right-panel>
|
||||
<InvoiceInSerialFilter data-key="InvoiceInSerial" />
|
||||
</template>
|
||||
</RightMenu>
|
||||
<VnTable
|
||||
v-if="!isNaN(daysAgo)"
|
||||
data-key="InvoiceInSerial"
|
||||
url="InvoiceIns/getSerial"
|
||||
:columns="cols"
|
||||
:right-search="false"
|
||||
:user-params="{ daysAgo }"
|
||||
:disable-option="{ card: true }"
|
||||
auto-load
|
||||
/>
|
||||
</template>
|
||||
<i18n>
|
||||
es:
|
||||
Serial: Serie
|
||||
Pending: Pendiente
|
||||
</i18n>
|
|
@ -0,0 +1,53 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
defineProps({ dataKey: { type: String, required: true } });
|
||||
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<VnFilterPanel :data-key="dataKey" :search-button="true">
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<div class="q-gutter-x-xs">
|
||||
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||
<span>{{ formatFn(tag.value) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #body="{ params }">
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputNumber
|
||||
v-model="params.daysAgo"
|
||||
:label="t('params.daysAgo')"
|
||||
outlined
|
||||
rounded
|
||||
dense
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
v-model="params.serial"
|
||||
:label="t('params.serial')"
|
||||
outlined
|
||||
rounded
|
||||
dense
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnFilterPanel>
|
||||
</template>
|
||||
<i18n>
|
||||
en:
|
||||
params:
|
||||
daysAgo: Last days
|
||||
serial: serial
|
||||
es:
|
||||
params:
|
||||
daysAgo: Últimos días
|
||||
serial: serie
|
||||
</i18n>
|
|
@ -10,8 +10,10 @@ import InvoiceOutFilter from '../InvoiceOutFilter.vue';
|
|||
:descriptor="InvoiceOutDescriptor"
|
||||
:filter-panel="InvoiceOutFilter"
|
||||
search-data-key="InvoiceOutList"
|
||||
search-url="InvoiceOuts/filter"
|
||||
searchbar-label="Search invoice"
|
||||
searchbar-info="You can search by invoice reference"
|
||||
:searchbar-props="{
|
||||
url: 'InvoiceOuts/filter',
|
||||
label: 'Search invoice',
|
||||
info: 'You can search by invoice reference',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -100,7 +100,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity.
|
|||
color="primary"
|
||||
:to="{
|
||||
name: 'TicketList',
|
||||
query: { q: ticketFilter(entity) },
|
||||
query: { table: ticketFilter(entity) },
|
||||
}"
|
||||
>
|
||||
<QTooltip>{{ t('invoiceOut.card.ticketList') }}</QTooltip>
|
||||
|
|
|
@ -222,7 +222,7 @@ const showTransferInvoiceForm = async () => {
|
|||
<QItemSection>{{ t('Generate PDF invoice') }}</QItemSection>
|
||||
</QItem>
|
||||
<QItem v-ripple clickable>
|
||||
<QItemSection>{{ t('Refund...') }}</QItemSection>
|
||||
<QItemSection>{{ t('Refund') }}</QItemSection>
|
||||
<QItemSection side>
|
||||
<QIcon name="keyboard_arrow_right" />
|
||||
</QItemSection>
|
||||
|
@ -250,7 +250,7 @@ es:
|
|||
Delete invoice: Eliminar factura
|
||||
Book invoice: Asentar factura
|
||||
Generate PDF invoice: Generar PDF factura
|
||||
Refund...: Abono
|
||||
Refund: Abono
|
||||
As PDF: como PDF
|
||||
As CSV: como CSV
|
||||
Send PDF: Enviar PDF
|
||||
|
|
|
@ -6,7 +6,7 @@ import FetchData from 'components/FetchData.vue';
|
|||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
import VnCurrency from 'src/components/common/VnCurrency.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
|
@ -58,7 +58,7 @@ function setWorkers(data) {
|
|||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnCurrency
|
||||
<VnInputNumber
|
||||
:label="t('Amount')"
|
||||
v-model="params.amount"
|
||||
is-outlined
|
||||
|
|
|
@ -94,11 +94,13 @@ const selectCustomerId = (id) => {
|
|||
};
|
||||
|
||||
const statusText = computed(() => {
|
||||
return status.value === 'invoicing'
|
||||
? `${t(`status.${status.value}`)} ${
|
||||
addresses.value[getAddressNumber.value]?.clientId
|
||||
}`
|
||||
: t(`status.${status.value}`);
|
||||
const baseStatus = t(`status.${status.value}`);
|
||||
const clientId =
|
||||
status.value === 'invoicing'
|
||||
? addresses.value[getAddressNumber.value]?.clientId || ''
|
||||
: '';
|
||||
|
||||
return clientId ? `${baseStatus} ${clientId}`.trim() : baseStatus;
|
||||
});
|
||||
|
||||
onMounted(() => (stateStore.rightDrawer = true));
|
||||
|
|
|
@ -20,21 +20,25 @@ const { initialDataLoading, formInitialData, invoicing, status } =
|
|||
const { makeInvoice, setStatusValue } = invoiceOutGlobalStore;
|
||||
|
||||
const clientsToInvoice = ref('all');
|
||||
|
||||
const companiesOptions = ref([]);
|
||||
|
||||
const printersOptions = ref([]);
|
||||
const serialTypesOptions = ref([]);
|
||||
|
||||
const clientsOptions = ref([]);
|
||||
const handleInvoiceOutSerialsFetch = (data) => {
|
||||
serialTypesOptions.value = Array.from(
|
||||
new Set(data.map((item) => item.type).filter((type) => type))
|
||||
);
|
||||
};
|
||||
|
||||
const formData = ref({});
|
||||
|
||||
const optionsInitialData = computed(() => {
|
||||
return (
|
||||
companiesOptions.value.length > 0 &&
|
||||
printersOptions.value.length > 0 &&
|
||||
clientsOptions.value.length > 0
|
||||
);
|
||||
const optionsArrays = [
|
||||
companiesOptions.value,
|
||||
printersOptions.value,
|
||||
serialTypesOptions.value,
|
||||
];
|
||||
return optionsArrays.every((arr) => arr.length > 0);
|
||||
});
|
||||
|
||||
const getStatus = computed({
|
||||
|
@ -48,7 +52,7 @@ const getStatus = computed({
|
|||
|
||||
onMounted(async () => {
|
||||
await invoiceOutGlobalStore.init();
|
||||
formData.value = formInitialData.value.invoiceDate;
|
||||
formData.value = { invoiceDate: formInitialData.value.invoiceDate };
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -59,8 +63,11 @@ onMounted(async () => {
|
|||
auto-load
|
||||
/>
|
||||
<FetchData url="Printers" @on-fetch="(data) => (printersOptions = data)" auto-load />
|
||||
<FetchData url="Clients" @on-fetch="(data) => (clientsOptions = data)" auto-load />
|
||||
|
||||
<FetchData
|
||||
url="invoiceOutSerials"
|
||||
@on-fetch="handleInvoiceOutSerialsFetch"
|
||||
auto-load
|
||||
/>
|
||||
<QForm
|
||||
v-if="!initialDataLoading && optionsInitialData"
|
||||
@submit="makeInvoice(formData, clientsToInvoice)"
|
||||
|
@ -87,13 +94,34 @@ onMounted(async () => {
|
|||
v-if="clientsToInvoice === 'one'"
|
||||
:label="t('client')"
|
||||
v-model="formData.clientId"
|
||||
:options="clientsOptions"
|
||||
url="Clients"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
hide-selected
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>
|
||||
#{{ scope.opt?.id }} {{ scope.opt?.name }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<VnSelect
|
||||
:label="t('invoiceOutSerialType')"
|
||||
v-model="formData.serialType"
|
||||
:options="serialTypesOptions"
|
||||
option-value="type"
|
||||
option-label="type"
|
||||
hide-selected
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
/>
|
||||
<VnInputDate
|
||||
v-model="formData.invoiceDate"
|
||||
|
@ -109,9 +137,7 @@ onMounted(async () => {
|
|||
:label="t('company')"
|
||||
v-model="formData.companyFk"
|
||||
:options="companiesOptions"
|
||||
option-value="id"
|
||||
option-label="code"
|
||||
hide-selected
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
|
@ -120,9 +146,6 @@ onMounted(async () => {
|
|||
:label="t('printer')"
|
||||
v-model="formData.printer"
|
||||
:options="printersOptions"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
hide-selected
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
|
@ -168,6 +191,7 @@ en:
|
|||
printer: Printer
|
||||
invoiceOut: Invoice out
|
||||
client: Client
|
||||
invoiceOutSerialType: Serial Type
|
||||
stop: Stop
|
||||
|
||||
es:
|
||||
|
@ -179,5 +203,6 @@ es:
|
|||
printer: Impresora
|
||||
invoiceOut: Facturar
|
||||
client: Cliente
|
||||
invoiceOutSerialType: Tipo de Serie
|
||||
stop: Parar
|
||||
</i18n>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { onMounted, onUnmounted, ref, computed } from 'vue';
|
||||
import { onMounted, onUnmounted, ref, computed, watchEffect } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
|
@ -12,7 +12,7 @@ import InvoiceOutSummary from './Card/InvoiceOutSummary.vue';
|
|||
import { toCurrency, toDate } from 'src/filters/index';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { QBtn } from 'quasar';
|
||||
import { watchEffect } from 'vue';
|
||||
import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const stateStore = useStateStore();
|
||||
|
@ -63,7 +63,7 @@ const columns = computed(() => [
|
|||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'clientSocialName',
|
||||
name: 'clientFk',
|
||||
label: t('invoiceOutModule.customer'),
|
||||
cardVisible: true,
|
||||
component: 'select',
|
||||
|
@ -122,7 +122,7 @@ const columns = computed(() => [
|
|||
name: 'tableActions',
|
||||
actions: [
|
||||
{
|
||||
title: t('InvoiceOutSummary'),
|
||||
title: t('components.smartCard.viewSummary'),
|
||||
icon: 'preview',
|
||||
action: (row) => viewSummary(row.id, InvoiceOutSummary),
|
||||
},
|
||||
|
@ -198,7 +198,7 @@ watchEffect(selectedRows);
|
|||
:url="`${MODEL}/filter`"
|
||||
:create="{
|
||||
urlCreate: 'InvoiceOuts/createManualInvoice',
|
||||
title: t('Create Manual Invoice'),
|
||||
title: t('Create manual invoice'),
|
||||
onDataSaved: ({ id }) => tableRef.redirect(id),
|
||||
formInitialData: {
|
||||
active: true,
|
||||
|
@ -214,15 +214,32 @@ watchEffect(selectedRows);
|
|||
selection: 'multiple',
|
||||
}"
|
||||
>
|
||||
<template #column-clientFk="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.clientSocialName }}
|
||||
<CustomerDescriptorProxy :id="row.clientFk" />
|
||||
</span>
|
||||
</template>
|
||||
<template #more-create-dialog="{ data }">
|
||||
<div class="flex no-wrap flex-center">
|
||||
<VnSelect
|
||||
url="Tickets"
|
||||
v-model="data.ticketFk"
|
||||
:label="t('invoiceOutList.tableVisibleColumns.ticket')"
|
||||
:options="ticketsOptions"
|
||||
option-label="nickname"
|
||||
option-label="id"
|
||||
option-value="id"
|
||||
/>
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel> #{{ scope.opt?.id }} </QItemLabel>
|
||||
<QItemLabel caption>{{ scope.opt?.nickname }}</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<span class="q-ml-md">O</span>
|
||||
</div>
|
||||
<VnSelect
|
||||
url="Clients"
|
||||
v-model="data.clientFk"
|
||||
|
@ -231,21 +248,21 @@ watchEffect(selectedRows);
|
|||
option-label="name"
|
||||
option-value="id"
|
||||
/>
|
||||
<VnInputDate
|
||||
:label="t('invoiceOutList.tableVisibleColumns.dueDate')"
|
||||
v-model="data.maxShipped"
|
||||
/>
|
||||
<VnSelect
|
||||
url="InvoiceOutSerials"
|
||||
v-model="data.invoiceOutSerial"
|
||||
v-model="data.serial"
|
||||
:label="t('invoiceOutList.tableVisibleColumns.invoiceOutSerial')"
|
||||
:options="invoiceOutSerialsOptions"
|
||||
option-label="description"
|
||||
option-value="code"
|
||||
/>
|
||||
<VnInputDate
|
||||
:label="t('invoiceOutList.tableVisibleColumns.dueDate')"
|
||||
v-model="data.maxShipped"
|
||||
/>
|
||||
<VnSelect
|
||||
url="TaxAreas"
|
||||
v-model="data.area"
|
||||
v-model="data.taxArea"
|
||||
:label="t('invoiceOutList.tableVisibleColumns.taxArea')"
|
||||
:options="taxAreasOptions"
|
||||
option-label="code"
|
||||
|
@ -266,10 +283,12 @@ en:
|
|||
fileAllowed: Successful download of CSV file
|
||||
youCanSearchByInvoiceReference: You can search by invoice reference
|
||||
createInvoice: Make invoice
|
||||
Create manual invoice: Create manual invoice
|
||||
es:
|
||||
searchInvoice: Buscar factura emitida
|
||||
fileDenied: El navegador denegó la descarga de archivos...
|
||||
fileAllowed: Descarga exitosa de archivo CSV
|
||||
youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura
|
||||
createInvoice: Crear factura
|
||||
Create manual invoice: Crear factura manual
|
||||
</i18n>
|
||||
|
|
|
@ -6,6 +6,9 @@ import { toCurrency } from 'src/filters';
|
|||
import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||
import { useInvoiceOutGlobalStore } from 'src/stores/invoiceOutGlobal.js';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
|
||||
import TicketDescriptorProxy from '../Ticket/Card/TicketDescriptorProxy.vue';
|
||||
import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const tableRef = ref();
|
||||
|
@ -71,6 +74,9 @@ const columns = computed(() => [
|
|||
align: 'left',
|
||||
name: 'amount',
|
||||
label: t('invoiceOutModule.amount'),
|
||||
columnFilter: {
|
||||
type: 'number',
|
||||
},
|
||||
format: (row) => toCurrency(row.amount),
|
||||
cardVisible: true,
|
||||
},
|
||||
|
@ -168,6 +174,24 @@ const downloadCSV = async () => {
|
|||
:is-editable="false"
|
||||
:use-model="true"
|
||||
>
|
||||
<template #column-clientId="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.clientId }}
|
||||
<CustomerDescriptorProxy :id="row.clientId" />
|
||||
</span>
|
||||
</template>
|
||||
<template #column-ticketFk="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.ticketFk }}
|
||||
<TicketDescriptorProxy :id="row.ticketFk" />
|
||||
</span>
|
||||
</template>
|
||||
<template #column-workerName="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.workerName }}
|
||||
<WorkerDescriptorProxy :id="row.comercialId" />
|
||||
</span>
|
||||
</template>
|
||||
</VnTable>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
|
|||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
import VnCurrency from 'src/components/common/VnCurrency.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
|
@ -19,7 +19,7 @@ const props = defineProps({
|
|||
<VnFilterPanel
|
||||
:data-key="props.dataKey"
|
||||
:search-button="true"
|
||||
:unremovable-params="['from', 'to']"
|
||||
:un-removable-params="['from', 'to']"
|
||||
:hidden-tags="['from', 'to']"
|
||||
>
|
||||
<template #tags="{ tag, formatFn }">
|
||||
|
@ -86,7 +86,7 @@ const props = defineProps({
|
|||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnCurrency
|
||||
<VnInputNumber
|
||||
v-model="params.amount"
|
||||
:label="t('invoiceOut.negativeBases.amount')"
|
||||
is-outlined
|
||||
|
|
|
@ -16,7 +16,6 @@ const route = useRoute();
|
|||
const { t } = useI18n();
|
||||
|
||||
const itemTypesOptions = ref([]);
|
||||
const itemsWithNameOptions = ref([]);
|
||||
const intrastatsOptions = ref([]);
|
||||
const expensesOptions = ref([]);
|
||||
|
||||
|
@ -36,15 +35,6 @@ const onIntrastatCreated = (response, formData) => {
|
|||
@on-fetch="(data) => (itemTypesOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
url="Items/withName"
|
||||
:filter="{
|
||||
fields: ['id', 'name'],
|
||||
order: 'id DESC',
|
||||
}"
|
||||
@on-fetch="(data) => (itemsWithNameOptions = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
url="Intrastats"
|
||||
:filter="{
|
||||
|
@ -73,7 +63,7 @@ const onIntrastatCreated = (response, formData) => {
|
|||
<template #form="{ data }">
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
:label="t('basicData.type')"
|
||||
:label="t('itemBasicData.type')"
|
||||
v-model="data.typeFk"
|
||||
:options="itemTypesOptions"
|
||||
option-value="id"
|
||||
|
@ -92,19 +82,21 @@ const onIntrastatCreated = (response, formData) => {
|
|||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
<VnInput :label="t('basicData.reference')" v-model="data.comment" />
|
||||
<VnInput :label="t('basicData.relevancy')" v-model="data.relevancy" />
|
||||
<VnInput :label="t('itemBasicData.reference')" v-model="data.comment" />
|
||||
<VnInput :label="t('itemBasicData.relevancy')" v-model="data.relevancy" />
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnInput :label="t('basicData.stems')" v-model="data.stems" />
|
||||
<VnInput :label="t('itemBasicData.stems')" v-model="data.stems" />
|
||||
<VnInput
|
||||
:label="t('basicData.multiplier')"
|
||||
:label="t('itemBasicData.multiplier')"
|
||||
v-model="data.stemMultiplier"
|
||||
/>
|
||||
<VnSelectDialog
|
||||
:label="t('basicData.generic')"
|
||||
:label="t('itemBasicData.generic')"
|
||||
v-model="data.genericFk"
|
||||
:options="itemsWithNameOptions"
|
||||
url="Items/withName"
|
||||
:fields="['id', 'name']"
|
||||
sort-by="id DESC"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
map-options
|
||||
|
@ -129,7 +121,7 @@ const onIntrastatCreated = (response, formData) => {
|
|||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelectDialog
|
||||
:label="t('basicData.intrastat')"
|
||||
:label="t('itemBasicData.intrastat')"
|
||||
v-model="data.intrastatFk"
|
||||
:options="intrastatsOptions"
|
||||
option-value="id"
|
||||
|
@ -156,7 +148,7 @@ const onIntrastatCreated = (response, formData) => {
|
|||
</VnSelectDialog>
|
||||
<div class="col">
|
||||
<VnSelect
|
||||
:label="t('basicData.expense')"
|
||||
:label="t('itemBasicData.expense')"
|
||||
v-model="data.expenseFk"
|
||||
:options="expensesOptions"
|
||||
option-value="id"
|
||||
|
@ -168,61 +160,64 @@ const onIntrastatCreated = (response, formData) => {
|
|||
</VnRow>
|
||||
<VnRow>
|
||||
<VnInput
|
||||
:label="t('basicData.weightByPiece')"
|
||||
:label="t('itemBasicData.weightByPiece')"
|
||||
v-model.number="data.weightByPiece"
|
||||
:min="0"
|
||||
type="number"
|
||||
/>
|
||||
<VnInput
|
||||
:label="t('basicData.boxUnits')"
|
||||
:label="t('itemBasicData.boxUnits')"
|
||||
v-model.number="data.packingOut"
|
||||
:min="0"
|
||||
type="number"
|
||||
/>
|
||||
<VnInput
|
||||
:label="t('basicData.recycledPlastic')"
|
||||
:label="t('itemBasicData.recycledPlastic')"
|
||||
v-model.number="data.recycledPlastic"
|
||||
:min="0"
|
||||
type="number"
|
||||
/>
|
||||
<VnInput
|
||||
:label="t('basicData.nonRecycledPlastic')"
|
||||
:label="t('itemBasicData.nonRecycledPlastic')"
|
||||
v-model.number="data.nonRecycledPlastic"
|
||||
:min="0"
|
||||
type="number"
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<QCheckbox v-model="data.isActive" :label="t('basicData.isActive')" />
|
||||
<QCheckbox v-model="data.hasKgPrice" :label="t('basicData.hasKgPrice')" />
|
||||
<QCheckbox v-model="data.isActive" :label="t('itemBasicData.isActive')" />
|
||||
<QCheckbox
|
||||
v-model="data.hasKgPrice"
|
||||
:label="t('itemBasicData.hasKgPrice')"
|
||||
/>
|
||||
<div>
|
||||
<QCheckbox
|
||||
v-model="data.isFragile"
|
||||
:label="t('basicData.isFragile')"
|
||||
:label="t('itemBasicData.isFragile')"
|
||||
class="q-mr-sm"
|
||||
/>
|
||||
<QIcon name="info" class="cursor-pointer" size="xs">
|
||||
<QTooltip max-width="300px">
|
||||
{{ t('basicData.isFragileTooltip') }}
|
||||
{{ t('itemBasicData.isFragileTooltip') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</div>
|
||||
<div>
|
||||
<QCheckbox
|
||||
v-model="data.isPhotoRequested"
|
||||
:label="t('basicData.isPhotoRequested')"
|
||||
:label="t('itemBasicData.isPhotoRequested')"
|
||||
class="q-mr-sm"
|
||||
/>
|
||||
<QIcon name="info" class="cursor-pointer" size="xs">
|
||||
<QTooltip>
|
||||
{{ t('basicData.isPhotoRequestedTooltip') }}
|
||||
{{ t('itemBasicData.isPhotoRequestedTooltip') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnInput
|
||||
:label="t('basicData.description')"
|
||||
:label="t('itemBasicData.description')"
|
||||
type="textarea"
|
||||
v-model="data.description"
|
||||
fill-input
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue