WIP: 6897-entryBuyListRefactor #860

Draft
pablone wants to merge 16 commits from 6897-entryBuyListRefactor into dev
135 changed files with 3731 additions and 1338 deletions
Showing only changes of commit 6b9f9188a2 - Show all commits

4
.gitignore vendored
View File

@ -31,3 +31,7 @@ yarn-error.log*
# Cypress directories and files
/test/cypress/videos
/test/cypress/screenshots
# VitePress directories and files
/docs/.vitepress/cache
/docs/.vuepress

View File

@ -1,3 +1,157 @@
# Version 25.04 - 2025-01-28
### Added 🆕
- chore: add task comment by:jorgep
- chore: refs #8198 rollback by:jorgep
- chore: refs #8322 unnecessary prop by:alexm
- feat: refs #7055 added new test case by:provira
- feat: refs #7055 created FilterItemForm test by:provira
- feat: refs #7077 created test for VnInputTime by:provira
- feat: refs #7078 created test for VnJsonValue by:provira
- feat: refs #7087 added more test cases by:provira
- feat: refs #7087 added new test by:provira
- feat: refs #7087 created CardSummary test by:provira
- feat: refs #7088 created test for FetchedTags by:provira
- feat: refs #7202 added new field by:Jon
- feat: refs #7882 Added coords to create a address by:guillermo
- feat: refs #7957 add tooltip and i18n support for search link in VnSearchbar component by:jorgep
- feat: refs #7957 enhance search functionality and improve data filtering logic by:jorgep
- feat: refs #7957 open in new tab by:jorgep
- feat: refs #7957 simplify fn to by:jorgep
- feat: refs #7957 update VnSearchbar component with improved search URL handling and styling enhancements by:jorgep
- feat: refs #8117 filters and values added as needed by:jtubau
- feat: refs #8197 useHasContent and use in VnSection and RightMenu by:alexm
- feat: refs #8219 added invoice out e2e tests by:Jon
- feat: refs #8219 global invoicing e2e by:Jon
- feat: refs #8220 added barcodes e2e test by:Jon
- feat: refs #8220 created items e2e by:Jon
- feat: refs #8220 modified create item form and added respective e2e by:Jon
- feat: refs #8225 added account and invoiceOut modules by:Jon
- feat: refs #8225 added entry module and fixed translations by:Jon
- feat: refs #8225 added invoiceIn and travel module by:Jon
- feat: refs #8225 added moreOptions and use it in customer and ticket summary by:Jon
- feat: refs #8225 added route and shelving module by:Jon
- feat: refs #8225 added worker and zone modules by:Jon
- feat: refs #8225 use it in claim, item and order modules by:Jon
- feat: refs #8258 added button to pass to uppercase by:provira
- feat: refs #8258 added uppercase option to VnInput by:provira
- feat: refs #8258 added uppercase validation on supplier create by:provira
- feat: refs #8298 add price optimum input and update translations for bonus and price optimum by:jgallego
- feat: refs #8316 add entryFilter prop to VnTable component in EntryList by:jtubau
- feat: refs #8322 added department changes by:provira
- feat: refs #8372 workerPBX by:robert
- feat: refs #8381 add initial and final temperature fields to entry forms and summaries by:jgallego
- feat: refs #8381 add initial and final temperature labels in English and Spanish locales by:jgallego
- feat: refs #8381 add toCelsius filter and update temperature fields in entry forms and summaries by:jgallego
- feat: skip tests by:jorgep
- style: refs #7957 update VnSearchbar padding for improved layout by:jorgep
### Changed 📦
- perf: refs #8219 #8219 minor change by:Javier Segarra
- perf: refs #8220 on-fetch and added missing translations by:Jon
- perf: refs #8220 on-fetch by:Jon
- perf: refs #8220 translations by:Jon
- perf: refs #8220 use searchbar selector in e2e tests by:Jon
- perf: remove warning default value by:Javier Segarra
- refactor: redirect using params by:Jon
- refactor: refs #7077 removed some comments by:provira
- refactor: refs #7087 removed unused imports by:provira
- refactor: refs #7100 added const mockData by:jtubau
- refactor: refs #7100 delete unnecesary set prop by:jtubau
- refactor: refs #7100 refactorized with methods by:jtubau
- refactor: refs #7957 remove blank by:jorgep
- refactor: refs #8198 simplify data fetching and filtering logic by:jorgep
- refactor: refs #8198 simplify state management and data fetching in ItemDiary component by:jorgep
- refactor: refs #8219 modified e2e tests and fixed some translations by:Jon
- refactor: refs #8219 modified list test, created cypress download folder and added to gitignore by:Jon
- refactor: refs #8219 requested changes by:Jon
- refactor: refs #8219 use checkNotification command by:Jon
- refactor: refs #8220 added data-cy for e2e tests by:Jon
- refactor: refs #8220 requested changes by:Jon
- refactor: refs #8220 skip failling test and modifed tag test by:Jon
- refactor: refs #8225 requested changes by:Jon
- refactor: refs #8247 use new acl for sysadmin by:Jon
- refactor: refs #8316 added claimFilter by:jtubau
- refactor: refs #8316 added entryFilter by:jtubau
- refactor: refs #8316 add new localization keys and update existing ones for entry components by:jtubau
- refactor: refs #8316 moved localizations to local locale by:jtubau
- refactor: refs #8316 move order localization by:jtubau
- refactor: refs #8316 remove unused OrderSearchbar component by:jtubau
- refactor: refs #8316 update EntryCard to use user-filter prop and remove exprBuilder from EntryList by:jtubau
- refactor: refs #8316 used VnSection and VnCardBeta by:jtubau
- refactor: refs #8322 changed translations by:provira
- refactor: refs #8322 changed Worker component to use VnSection/VnCardBeta by:provira
- refactor: refs #8322 set department inside worker by:alexm
- refactor: skip intermitent failing test by:Jon
### Fixed 🛠️
- feat: refs #8225 added entry module and fixed translations by:Jon
- fix: added missing translations in InvoiceIn by:provira
- fix: changed invoiceIn for InvoiceIn by:provira
- fix: changed translations to only use "invoicein" by:provira
- fix: department descriptor link by:Jon
- fix: e2e tests by:Jon
- fix: entry summary view and build warnings by:Jon
- fix: fixed InvoiceIn filter translations by:provira
- fix: modified setData in customerDescriptor to show the icons by:Jon
- fix: redirect to TicketSale from OrderLines (origin/Fix-RedirectToTicketSale) by:Jon
- fix: redirect when confirming lines by:Jon
- fix: refs #7055 #7055 #7055 fixed some tests by:provira
- fix: refs #7077 removed unused imports by:provira
- fix: refs #7078 added missing case with array by:provira
- fix: refs #7087 fixed some tests by:provira
- fix: refs #7088 changed "vm.vm" to "vm" by:provira
- fix: refs #7088 changed wrapper to vm by:provira
- fix: refs #7699 add icons and hint by:carlossa
- fix: refs #7699 add pwd vnInput by:carlossa
- fix: refs #7699 fix component by:carlossa
- fix: refs #7699 fix password visibility by:carlossa
- fix: refs #7699 fix tfront clean code by:carlossa
- fix: refs #7699 fix vnChangePassword, clean VnInput by:carlossa
- fix: refs #7699 fix vnInputPassword by:carlossa
- fix: refs #7957 add missing closing brace by:jorgep
- fix: refs #7957 css by:jorgep
- fix: refs #7957 rollback by:jorgep
- fix: refs #7957 update data-cy by:jorgep
- fix: refs #7957 update visibility handling for clear icon in VnInput component by:jorgep
- fix: refs #7957 vn-searchbar test by:jorgep
- fix: refs #8117 update salesPersonFk filter options and URL for improved data retrieval by:jtubau
- fix: refs #8197 not use yet by:alexm
- fix: refs #8198 update query param by:jorgep
- fix: refs #8219 fixed e2e tests by:Jon
- fix: refs #8219 fixed summary and global tests by:Jon
- fix: refs #8219 forgotten dataCy by:Jon
- fix: refs #8219 global e2e by:Jon
- fix: refs #8219 requested changes by:Jon
- fix: refs #8220 itemTag test by:Javier Segarra
- fix: refs #8225 invoice in translations by:Jon
- fix: refs #8243 fixed SkeletonSummary by:provira
- fix: refs #8247 conflicts by:Jon
- fix: refs #8247 fixed acls and added lost options by:Jon
- fix: refs #8316 ref="claimFilterRef" by:alexm
- fix: refs #8316 userFilter by:alexm
- fix: refs #8316 use rightMenu by:alexm
- fix: refs #8316 use section-searchbar by:alexm
- fix: refs #8317 disable action buttons when no rows are selected in ItemFixedPrice by:jtubau
- fix: refs #8322 unnecessary section by:alexm
- fix: refs #8338 fixed VnTable translations by:provira
- fix: refs #8338 removed chipLocale property/added more translations by:provira
- fix: refs #8448 e2e by:Jon
- fix: refs #8448 not use croppie by:alexm
- fix: remove departmentCode by:Javier Segarra
- fix: removed unused searchbar by:PAU ROVIRA ROSALENY
- fix: skip failling e2e by:Jon
- fix: sort by name in description by:Jon
- fix: translations by:Jon
- fix: use entryFilter by:alexm
- fix(VnCardBeta): add userFilter by:alexm
- refactor: refs #8219 modified e2e tests and fixed some translations by:Jon
- revert: revert header by:alexm
- test: fix expedition e2e by:alexm
# Version 25.00 - 2025-01-14
### Added 🆕

View File

@ -30,9 +30,11 @@ export default defineConfig({
testFiles: '**/*.spec.js',
supportFile: 'test/cypress/support/unit.js',
},
setupNodeEvents(on, config) {
import('cypress-mochawesome-reporter/plugin').then((plugin) => plugin.default(on));
// implement node event listeners here
setupNodeEvents: async (on, config) => {
const plugin = await import('cypress-mochawesome-reporter/plugin');
plugin.default(on);
return config;
},
viewportWidth: 1280,
viewportHeight: 720,

38
docs/.vitepress/config.js Normal file
View File

@ -0,0 +1,38 @@
import { defineConfig } from 'vitepress';
// https://vitepress.dev/reference/site-config
export default defineConfig({
title: 'Lilium',
description: 'Lilium docs',
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
nav: [
{ text: 'Home', link: '/' },
{ text: 'Components', link: '/components/vnInput' },
{ text: 'Composables', link: '/composables/useArrayData' },
],
sidebar: [
{
items: [
{
text: 'Components',
collapsible: true,
collapsed: true,
items: [{ text: 'VnInput', link: '/components/vnInput' }],
},
{
text: 'Composables',
collapsible: true,
collapsed: true,
items: [
{ text: 'useArrayData', link: '/composables/useArrayData' },
],
},
],
},
],
socialLinks: [{ icon: 'github', link: 'https://github.com/vuejs/vitepress' }],
},
});

136
docs/components/vnInput.md Normal file
View File

@ -0,0 +1,136 @@
# VnInput
`VnInput` is a custom input component that provides various useful features such as validation, input clearing, and more.
## Props
### `modelValue`
- **Type:** `String | Number`
- **Default:** `null`
- **Description:** The value of the model bound to the component.
### `isOutlined`
- **Type:** `Boolean`
- **Default:** `false`
- **Description:** If `true`, the component is rendered with an outlined style.
### `info`
- **Type:** `String`
- **Default:** `''`
- **Description:** Additional information displayed alongside the component.
### `clearable`
- **Type:** `Boolean`
- **Default:** `true`
- **Description:** If `true`, the component shows a button to clear the input.
### `emptyToNull`
- **Type:** `Boolean`
- **Default:** `true`
- **Description:** If `true`, converts empty inputs to `null`.
### `insertable`
- **Type:** `Boolean`
- **Default:** `false`
- **Description:** If `true`, allows the insertion of new values.
### `maxlength`
- **Type:** `Number`
- **Default:** `null`
- **Description:** The maximum number of characters allowed in the input.
### `uppercase`
- **Type:** `Boolean`
- **Default:** `false`
- **Description:** If `true`, converts the input text to uppercase.
## Emits
### `update:modelValue`
- **Description:** Emits the updated model value.
- **Behavior:** This event is emitted whenever the input value changes. It is used to update the model value bound to the component.
### `update:options`
- **Description:** Emits the updated options.
- **Behavior:** This event is emitted when the component's options change. It is useful for components with dynamic options.
### `keyup.enter`
- **Description:** Emits an event when the Enter key is pressed.
- **Behavior:** This event is emitted whenever the Enter key is pressed while the input is focused. It can be used to handle specific actions when the input is confirmed.
### `remove`
- **Description:** Emits an event to remove the current value.
- **Behavior:** This event is emitted when the clear button (close icon) is clicked. It is used to handle the removal of the current input value.
## Functions
### `focus`
- **Description:** Focuses the input.
- **Behavior:** This function is exposed so it can be called from outside the component. It uses `vnInputRef.value.focus()` to focus the input.
### `handleKeydown`
- **Description:** Handles the `keydown` event of the input.
- **Behavior:** This function is called whenever a key is pressed while the input is focused. If the pressed key is `Backspace`, it does nothing. If `insertable` is `true` and the pressed key is a number, it calls `handleInsertMode`.
### `handleInsertMode`
- **Description:** Handles the insertion mode of values.
- **Behavior:** This function is called when `insertable` is `true` and a numeric key is pressed. It inserts the value at the cursor position and updates the input value. Then, it moves the cursor to the correct position.
### `handleUppercase`
- **Description:** Converts the input value to uppercase.
- **Behavior:** This function is called when the uppercase icon is clicked. It converts the current input value to uppercase.
## Usage
```vue
<template>
<VnInput
v-model="inputValue"
:isOutlined="true"
info="Additional information"
:clearable="true"
:emptyToNull="true"
:insertable="false"
:maxlength="50"
:uppercase="true"
@update:modelValue="handleUpdate"
@keyup.enter="handleEnter"
@remove="handleRemove"
/>
</template>
<script setup>
import { ref } from 'vue';
import VnInput from 'src/components/common/VnInput.vue';
const inputValue = ref('');
const handleUpdate = (value) => {
console.log('Updated value:', value);
};
const handleEnter = () => {
console.log('Enter pressed');
};
const handleRemove = () => {
console.log('Value removed');
};
</script>
```

View File

@ -0,0 +1,215 @@
# useArrayData
`useArrayData` is a composable function that provides a set of utilities for managing array data in a Vue component. It leverages Pinia for state management and provides various methods for fetching, filtering, and manipulating data.
## Usage
```javascript
import { useArrayData } from 'src/composables/useArrayData';
const {
fetch,
applyFilter,
addFilter,
getCurrentFilter,
setCurrentFilter,
addFilterWhere,
addOrder,
deleteOrder,
refresh,
destroy,
loadMore,
store,
totalRows,
updateStateParams,
isLoading,
deleteOption,
reset,
resetPagination,
} = useArrayData('myKey', userOptions);
```
## Parameters
### `key`
- **Type:** `String`
- **Description:** A unique key to identify the data store.
### `userOptions`
- **Type:** `Object`
- **Description:** An object containing user-defined options for configuring the data store.
## Methods
### `fetch`
Fetches data from the server.
#### Parameters
- **`options`** : An object with the following properties:
- `append` (Boolean): Whether to append the fetched data to the existing data.
- `updateRouter` (Boolean): Whether to update the router with the current filter.
#### Returns
- **`Promise`** : A promise that resolves with the fetched data.
### `applyFilter`
Applies a filter to the data.
#### Parameters
- **`filter`** : An object containing the filter criteria.
- **`params`** : Additional parameters for the filter.
- **`fetchOptions`** : Options for the fetch method.
#### Returns
- **`Promise`** : A promise that resolves with the filtered data.
### `addFilter`
Adds a filter to the existing filters.
#### Parameters
- **`filter`** : An object containing the filter criteria.
- **`params`** : Additional parameters for the filter.
#### Returns
- **`Promise`** : A promise that resolves with the updated filter and parameters.
### `getCurrentFilter`
Gets the current filter applied to the data.
#### Returns
- **`Object`** : The current filter and parameters.
### `setCurrentFilter`
Sets the current filter for the data.
#### Returns
- **`Object`** : The current filter and parameters.
### `addFilterWhere`
Adds a `where` clause to the existing filters.
#### Parameters
- **`where`** : An object containing the `where` clause.
#### Returns
- **`Promise`** : A promise that resolves when the filter is applied.
### `addOrder`
Adds an order to the existing orders.
#### Parameters
- **`field`** : The field to order by.
- **`direction`** : The direction of the order (`ASC` or `DESC`).
#### Returns
- **`Promise`** : A promise that resolves with the updated order.
### `deleteOrder`
Deletes an order from the existing orders.
#### Parameters
- **`field`** : The field to delete the order for.
#### Returns
- **`Promise`** : A promise that resolves when the order is deleted.
### `refresh`
Refreshes the data by re-fetching it from the server.
#### Returns
- **`Promise`** : A promise that resolves with the refreshed data.
### `destroy`
Destroys the data store for the given key.
### `loadMore`
Loads more data by incrementing the pagination.
#### Returns
- **`Promise`** : A promise that resolves with the additional data.
### `updateStateParams`
Updates the state parameters with the given data.
#### Parameters
- **`data`** : The data to update the state parameters with.
### `deleteOption`
Deletes an option from the store.
#### Parameters
- **`option`** : The option to delete.
### `reset`
Resets the store to its default state.
#### Parameters
- **`opts`** : An array of options to reset.
### `resetPagination`
Resets the pagination for the store.
## Computed Properties
### `totalRows`
- **Description:** The total number of rows in the data.
- **Type:** `Number`
### `isLoading`
- **Description:** Whether the data is currently being loaded.
- **Type:** `Boolean`
```vue
<script setup>
import { useArrayData } from 'src/composables/useArrayData';
const userOptions = {
url: '/api/data',
limit: 10,
};
const arrayData = useArrayData('myKey', userOptions);
</script>
```
```
```

13
docs/index.md Normal file
View File

@ -0,0 +1,13 @@
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home
hero:
name: 'Lilium'
text: 'Lilium docs'
tagline: Powered by Verdnatura
actions:
- theme: brand
text: Docs
link: /components/vnInput
---

View File

@ -1,70 +1,74 @@
{
"name": "salix-front",
"version": "25.06.0",
"description": "Salix frontend",
"productName": "Salix",
"author": "Verdnatura",
"private": true,
"packageManager": "pnpm@8.15.1",
"type": "module",
"scripts": {
"resetDatabase": "cd ../salix && gulp docker",
"lint": "eslint --ext .js,.vue ./",
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
"test:e2e": "cypress open",
"test:e2e:ci": "npm run resetDatabase && cd ../salix-front && cypress run",
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
"test:unit": "vitest",
"test:unit:ci": "vitest run",
"commitlint": "commitlint --edit",
"prepare": "npx husky install",
"addReferenceTag": "node .husky/addReferenceTag.js"
},
"dependencies": {
"@quasar/cli": "^2.4.1",
"@quasar/extras": "^1.16.16",
"axios": "^1.4.0",
"chromium": "^3.0.3",
"croppie": "^2.6.5",
"moment": "^2.30.1",
"pinia": "^2.1.3",
"quasar": "^2.17.7",
"validator": "^13.9.0",
"vue": "^3.5.13",
"vue-i18n": "^9.3.0",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@commitlint/cli": "^19.2.1",
"@commitlint/config-conventional": "^19.1.0",
"@intlify/unplugin-vue-i18n": "^0.8.2",
"@pinia/testing": "^0.1.2",
"@quasar/app-vite": "^2.0.8",
"@quasar/quasar-app-extension-qcalendar": "^4.0.2",
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.4.0",
"@vue/test-utils": "^2.4.4",
"autoprefixer": "^10.4.14",
"cypress": "^13.6.6",
"cypress-mochawesome-reporter": "^3.8.2",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-cypress": "^4.1.0",
"eslint-plugin-vue": "^9.32.0",
"husky": "^8.0.0",
"postcss": "^8.4.23",
"prettier": "^3.4.2",
"sass": "^1.83.4",
"vitest": "^0.34.0"
},
"engines": {
"node": "^20 || ^18 || ^16",
"npm": ">= 8.1.2",
"yarn": ">= 1.21.1",
"bun": ">= 1.0.25"
},
"overrides": {
"@vitejs/plugin-vue": "^5.2.1",
"vite": "^6.0.11",
"vitest": "^0.31.1"
}
}
"name": "salix-front",
"version": "25.06.0",
"description": "Salix frontend",
"productName": "Salix",
"author": "Verdnatura",
"private": true,
"packageManager": "pnpm@8.15.1",
"type": "module",
"scripts": {
"resetDatabase": "cd ../salix && gulp docker",
"lint": "eslint --ext .js,.vue ./",
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
"test:e2e": "cypress open",
"test:e2e:ci": "npm run resetDatabase && cd ../salix-front && cypress run",
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
"test:unit": "vitest",
"test:unit:ci": "vitest run",
"commitlint": "commitlint --edit",
"prepare": "npx husky install",
"addReferenceTag": "node .husky/addReferenceTag.js",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
},
"dependencies": {
"@quasar/cli": "^2.4.1",
"@quasar/extras": "^1.16.16",
"axios": "^1.4.0",
"chromium": "^3.0.3",
"croppie": "^2.6.5",
"moment": "^2.30.1",
"pinia": "^2.1.3",
"quasar": "^2.17.7",
"validator": "^13.9.0",
"vue": "^3.5.13",
"vue-i18n": "^9.3.0",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@commitlint/cli": "^19.2.1",
"@commitlint/config-conventional": "^19.1.0",
"@intlify/unplugin-vue-i18n": "^0.8.2",
"@pinia/testing": "^0.1.2",
"@quasar/app-vite": "^2.0.8",
"@quasar/quasar-app-extension-qcalendar": "^4.0.2",
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.4.0",
"@vue/test-utils": "^2.4.4",
"autoprefixer": "^10.4.14",
"cypress": "^13.6.6",
"cypress-mochawesome-reporter": "^3.8.2",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-cypress": "^4.1.0",
"eslint-plugin-vue": "^9.32.0",
"husky": "^8.0.0",
"postcss": "^8.4.23",
"prettier": "^3.4.2",
"sass": "^1.83.4",
"vitepress": "^1.6.3",
"vitest": "^0.34.0"
},
"engines": {
"node": "^20 || ^18 || ^16",
"npm": ">= 8.1.2",
"yarn": ">= 1.21.1",
"bun": ">= 1.0.25"
},
"overrides": {
"@vitejs/plugin-vue": "^5.2.1",
"vite": "^6.0.11",
"vitest": "^0.31.1"
}
}

File diff suppressed because it is too large Load Diff

6
proxy-serve.js Normal file
View File

@ -0,0 +1,6 @@
export default [
{
path: '/api',
rule: { target: 'http://0.0.0.0:3000' },
},
];

View File

@ -179,7 +179,6 @@ export default configure(function (/* ctx */) {
'render', // keep this as last one
],
},
// https://v2.quasar.dev/quasar-cli/developing-pwa/configuring-pwa
pwa: {
workboxMode: 'generateSW', // or 'injectManifest'

View File

@ -31,7 +31,6 @@ const props = defineProps({
const route = useRoute();
const itemTypesOptions = ref([]);
const suppliersOptions = ref([]);
const tagOptions = ref([]);
const tagValues = ref([]);
const categoryList = ref(null);
@ -40,13 +39,13 @@ const selectedTypeFk = ref(getParamWhere(route.query.table, 'typeFk', false));
const selectedCategory = computed(() => {
return (categoryList.value || []).find(
(category) => category?.id === selectedCategoryFk.value
(category) => category?.id === selectedCategoryFk.value,
);
});
const selectedType = computed(() => {
return (itemTypesOptions.value || []).find(
(type) => type?.id === selectedTypeFk.value
(type) => type?.id === selectedTypeFk.value,
);
});
@ -134,13 +133,6 @@ const setCategoryList = (data) => {
<template>
<FetchData url="ItemCategories" limit="30" auto-load @on-fetch="setCategoryList" />
<FetchData
url="Suppliers"
limit="30"
auto-load
:filter="{ fields: ['id', 'name', 'nickname'], order: 'name ASC', limit: 30 }"
@on-fetch="(data) => (suppliersOptions = data)"
/>
<FetchData
url="Tags"
:filter="{ fields: ['id', 'name', 'isFree'] }"
@ -349,4 +341,11 @@ es:
floramondo: Floramondo
salesPersonFk: Comprador
categoryFk: Categoría
Plant: Planta natural
Flower: Flor fresca
Handmade: Hecho a mano
Artificial: Artificial
Green: Verdes frescos
Accessories: Complementos florales
Fruit: Fruta
</i18n>

View File

@ -10,12 +10,13 @@ import routes from 'src/router/modules';
import LeftMenuItem from './LeftMenuItem.vue';
import LeftMenuItemGroup from './LeftMenuItemGroup.vue';
import VnInput from './common/VnInput.vue';
import { useRouter } from 'vue-router';
const { t } = useI18n();
const route = useRoute();
const quasar = useQuasar();
const navigation = useNavigationStore();
const router = useRouter();
const props = defineProps({
source: {
type: String,
@ -174,6 +175,10 @@ function normalize(text) {
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase();
}
const searchModule = () => {
const [item] = filteredItems.value;
if (item) router.push({ name: item.name });
};
</script>
<template>
@ -188,10 +193,11 @@ function normalize(text) {
filled
dense
autofocus
@keyup.enter.stop="searchModule()"
/>
</QItem>
<QSeparator />
<template v-if="filteredPinnedModules.size">
<template v-if="filteredPinnedModules.size && !search">
<LeftMenuItem
v-for="[key, pinnedModule] of filteredPinnedModules"
:key="key"
@ -215,11 +221,11 @@ function normalize(text) {
</LeftMenuItem>
<QSeparator />
</template>
<template v-for="item in filteredItems" :key="item.name">
<template v-for="(item, index) in filteredItems" :key="item.name">
<template
v-if="item.children && !filteredPinnedModules.has(item.name)"
v-if="search ||item.children && !filteredPinnedModules.has(item.name)"
>
<LeftMenuItem :item="item" group="modules">
<LeftMenuItem :item="item" group="modules" :class="search && index === 0 ? 'searched' : ''">
<template #side>
<QBtn
v-if="item.isPinned === true"
@ -336,6 +342,9 @@ function normalize(text) {
.header {
color: var(--vn-label-color);
}
.searched{
background-color: var(--vn-section-hover-color);
}
</style>
<i18n>
es:

View File

@ -20,6 +20,7 @@ const appName = 'Lilium';
const pinnedModulesRef = ref();
onMounted(() => stateStore.setMounted());
const refresh = () => window.location.reload();
</script>
<template>
<QHeader color="white" elevated>
@ -61,10 +62,16 @@ onMounted(() => stateStore.setMounted());
/>
<QSpace />
<div id="searchbar" class="searchbar"></div>
<div id="searchbar-after"></div>
<QSpace />
<div class="q-pl-sm q-gutter-sm row items-center no-wrap">
<div id="actions-prepend"></div>
<QIcon
name="refresh"
size="md"
color="red"
v-if="state.get('error')"
@click="refresh"
/>
<QBtn
:class="{ 'q-pa-none': quasar.platform.is.mobile }"
id="pinnedModules"

View File

@ -757,7 +757,7 @@ function formatColumnValue(col, row, dashIfEmpty) {
<QCard
bordered
flat
class="row no-wrap justify-between cursor-pointer"
class="row no-wrap justify-between cursor-pointer q-pa-sm"
@click="
(_, row) => {
$props.rowClick && $props.rowClick(row);
@ -838,7 +838,6 @@ function formatColumnValue(col, row, dashIfEmpty) {
<!-- Actions -->
<QCardSection
v-if="colsMap.tableActions"
class="column flex-center w-10 no-margin q-pa-xs q-gutter-y-xs"
@click="stopEventPropagation($event)"
>
<QBtn
@ -1126,12 +1125,15 @@ es:
.grid-two {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, max-content));
max-width: 100%;
margin: 0 auto;
overflow: scroll;
white-space: wrap;
width: 100%;
grid-template-columns: 2fr 2fr;
.vn-label-value {
flex-direction: column;
white-space: nowrap;
.fields {
display: flex;
}
}
white-space: nowrap;
}
.w-80 {

View File

@ -0,0 +1,12 @@
export default function (initialFooter, data) {
const footer = data.reduce(
(acc, row) => {
Object.entries(initialFooter).forEach(([key, initialValue]) => {
acc[key] += row?.[key] !== undefined ? row[key] : initialValue;
});
return acc;
},
{ ...initialFooter }
);
return footer;
}

View File

@ -20,7 +20,7 @@ watch(
(val) => {
if (stateStore) stateStore.rightAdvancedDrawer = val;
},
{ immediate: true }
{ immediate: true },
);
</script>
<template>
@ -30,8 +30,8 @@ watch(
flat
@click="stateStore.toggleRightAdvancedDrawer()"
round
dense
icon="tune"
color="white"
>
<QTooltip bottom anchor="bottom right">
{{ t('globals.advancedMenu') }}

View File

@ -26,6 +26,7 @@ onMounted(() => {
round
dense
icon="dock_to_left"
data-cy="toggle-right-drawer"
>
<QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }}

View File

@ -202,7 +202,7 @@ const columns = computed(() => [
prop.row.id,
$props.downloadModel,
undefined,
prop.row.download
prop.row.download,
),
},
{
@ -374,7 +374,7 @@ defineExpose({
v-if="
shouldRenderButton(
button.name,
props.row.isDocuware
props.row.isDocuware,
)
"
:is="button.component"

View File

@ -74,6 +74,11 @@ const focus = () => {
vnInputRef.value.focus();
};
defineExpose({
focus,
vnInputRef,
});
const mixinRules = [
requiredFieldRule,
...($attrs.rules ?? []),
@ -172,7 +177,11 @@ const handleUppercase = () => {
v-if="!$attrs.disabled && !$attrs.readonly && $props.uppercase"
@click="handleUppercase"
class="uppercase-icon"
/>
>
<QTooltip>
{{ t('Convert to uppercase') }}
</QTooltip>
</QIcon>
<slot name="append" v-if="$slots.append && !$attrs.disabled" />
<QIcon v-if="info" name="info">
@ -185,13 +194,28 @@ const handleUppercase = () => {
</div>
</template>
<style>
.uppercase-icon {
transition:
color 0.3s,
transform 0.2s;
cursor: pointer;
}
.uppercase-icon:hover {
color: #ed9937;
transform: scale(1.2);
}
</style>
<i18n>
en:
inputMin: Must be more than {value}
maxLength: The value exceeds {value} characters
inputMax: Must be less than {value}
es:
inputMin: Debe ser mayor a {value}
maxLength: El valor excede los {value} carácteres
inputMax: Debe ser menor a {value}
Convert to uppercase: Convertir a mayúsculas
</i18n>

View File

@ -804,7 +804,7 @@ watch(
<QItem class="q-mt-sm">
<QInput
class="full-width"
:label="t('to')"
:label="t('globals.to')"
@click="dateToDialog = true"
@focus="(evt) => evt.target.blur()"
@clear="selectFilter('date', 'from')"
@ -1059,9 +1059,9 @@ en:
Deletes: Deletes
Accesses: Accesses
Users:
User: Usuario
All: Todo
System: Sistema
User: User
All: All
System: System
properties:
id: ID
claimFk: Claim ID

View File

@ -75,6 +75,10 @@ onMounted(() => {
onUnmounted(unsubscribe);
});
onUnmounted(() => {
if (arrayData) arrayData.destroy();
});
function checkIsMain() {
isMainSection.value = sectionValue.value == route.name;
if (!isMainSection.value && arrayData) {

View File

@ -0,0 +1,35 @@
<script setup>
import { computed } from 'vue';
import VnSelect from 'components/common/VnSelect.vue';
const model = defineModel({ type: [String, Number, Object] });
const url = 'Suppliers';
</script>
<template>
<VnSelect
:label="$t('globals.supplier')"
v-bind="$attrs"
v-model="model"
:url="url"
option-value="id"
option-label="nickname"
:fields="['id', 'name', 'nickname', 'nif']"
sort-by="name ASC"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.name }}
</QItemLabel>
<QItemLabel caption>
{{
`#${scope.opt?.id} , ${scope.opt?.nickname} (${scope.opt?.nif})`
}}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</template>

View File

@ -9,9 +9,9 @@ const $props = defineProps({
type: Boolean,
default: false,
},
hasInfo: {
type: Boolean,
default: false,
info: {
type: String,
default: undefined,
},
modelValue: {
type: [String, Number, Object],
@ -53,13 +53,14 @@ const url = computed(() => {
:fields="['id', 'name', 'nickname', 'code']"
:filter-options="['id', 'name', 'nickname', 'code']"
sort-by="nickname ASC"
data-cy="vnWorkerSelect"
>
<template #prepend v-if="$props.hasAvatar">
<VnAvatar :worker-id="value" color="primary" v-bind="$attrs" />
</template>
<template #append v-if="$props.hasInfo">
<template #append v-if="$props.info">
<QIcon name="info" class="cursor-pointer">
<QTooltip>{{ $t($props.hasInfo) }}</QTooltip>
<QTooltip>{{ $t($props.info) }}</QTooltip>
</QIcon>
</template>
<template #option="scope">

View File

@ -10,6 +10,10 @@ defineProps({
type: Object,
required: true,
},
width: {
type: String,
default: 'md-width',
},
});
defineEmits([...useDialogPluginComponent.emits]);
@ -17,7 +21,19 @@ defineEmits([...useDialogPluginComponent.emits]);
const { dialogRef, onDialogHide } = useDialogPluginComponent();
</script>
<template>
<QDialog ref="dialogRef" @hide="onDialogHide" full-width>
<component :is="summary" :id="id" />
<QDialog ref="dialogRef" @hide="onDialogHide">
<component :is="summary" :id="id" :class="width" />
</QDialog>
</template>
<style lang="scss" scoped>
.md-width {
max-width: $width-md;
}
.lg-width {
max-width: $width-lg;
}
.xlg-width {
max-width: $width-xl;
}
</style>

View File

@ -37,6 +37,10 @@ const $props = defineProps({
type: Object,
default: null,
},
width: {
type: String,
default: 'md-width',
},
});
const state = useState();
@ -128,9 +132,8 @@ const toModule = computed(() =>
</QTooltip>
</QBtn></slot
>
<QBtn
@click.stop="viewSummary(entity.id, $props.summary)"
@click.stop="viewSummary(entity.id, $props.summary, $props.width)"
round
flat
dense

View File

@ -123,7 +123,7 @@ watch(
() => props.data,
() => {
store.data = props.data;
}
},
);
watch(
@ -132,12 +132,12 @@ watch(
if (!mounted.value) return;
emit('onChange', data);
},
{ immediate: true }
{ immediate: true },
);
watch(
() => [props.url, props.filter],
([url, filter]) => mounted.value && fetch({ url, filter })
([url, filter]) => mounted.value && fetch({ url, filter }),
);
const addFilter = async (filter, params) => {
await arrayData.addFilter({ filter, params });
@ -170,7 +170,7 @@ function emitStoreData() {
async function paginate() {
const { page, rowsPerPage, sortBy, descending } = pagination.value;
if (!props.url) return;
if (!arrayData.store.url) return;
isLoading.value = true;
await arrayData.loadMore();
@ -198,7 +198,7 @@ function endPagination() {
async function onLoad(index, done) {
if (!store.data || !mounted.value) return done();
if (store.data.length === 0 || !props.url) return done(false);
if (store.data.length === 0 || !arrayData.store.url) return done(false);
pagination.value.page = pagination.value.page + 1;

View File

@ -102,7 +102,7 @@ watch(
(val) => {
arrayData = useArrayData(val, { ...props });
store = arrayData.store;
}
},
);
onMounted(() => {
@ -176,6 +176,7 @@ async function search() {
>
<QTooltip>{{ t(props.info) }}</QTooltip>
</QIcon>
<div id="searchbar-after"></div>
</template>
</VnInput>
</QForm>

View File

@ -4,10 +4,10 @@ import { useQuasar } from 'quasar';
export function useSummaryDialog() {
const quasar = useQuasar();
function viewSummary(id, summary) {
function viewSummary(id, summary, width) {
quasar.dialog({
component: VnSummaryDialog,
componentProps: { id, summary },
componentProps: { id, summary, width },
});
}

Binary file not shown.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 186 KiB

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,10 @@
@font-face {
font-family: 'icon';
src: url('fonts/icon.eot?y0x93o');
src: url('fonts/icon.eot?y0x93o#iefix') format('embedded-opentype'),
url('fonts/icon.ttf?y0x93o') format('truetype'),
url('fonts/icon.woff?y0x93o') format('woff'),
url('fonts/icon.svg?y0x93o#icon') format('svg');
src: url('fonts/icon.eot?uocffs');
src: url('fonts/icon.eot?uocffs#iefix') format('embedded-opentype'),
url('fonts/icon.ttf?uocffs') format('truetype'),
url('fonts/icon.woff?uocffs') format('woff'),
url('fonts/icon.svg?uocffs#icon') format('svg');
font-weight: normal;
font-style: normal;
font-display: block;
@ -25,8 +25,17 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-entry_lastbuys:before {
content: "\e91a";
.icon-inactive-car:before {
content: "\e978";
}
.icon-hasItemLost:before {
content: "\e957";
}
.icon-hasItemDelay:before {
content: "\e96d";
}
.icon-add_entries:before {
content: "\e953";
}
.icon-100:before {
content: "\e901";
@ -52,6 +61,9 @@
.icon-addperson:before {
content: "\e908";
}
.icon-agencia_tributaria:before {
content: "\e948";
}
.icon-agency:before {
content: "\e92a";
}
@ -189,6 +201,9 @@
.icon-entry:before {
content: "\e937";
}
.icon-entry_lastbuys:before {
content: "\e91a";
}
.icon-exit:before {
content: "\e938";
}

View File

@ -33,6 +33,11 @@ $dark-shadow-color: black;
$layout-shadow-dark: 0 0 10px 2px #00000033, 0 0px 10px #0000003d;
$spacing-md: 16px;
$color-font-secondary: #777;
$width-xs: 400px;
$width-sm: 544px;
$width-md: 800px;
$width-lg: 1280px;
$width-xl: 1600px;
.bg-success {
background-color: $positive;
}

View File

@ -327,6 +327,7 @@ globals:
ticketsMonitor: Tickets monitor
clientsActionsMonitor: Clients and actions
serial: Serial
business: Business
medical: Mutual
pit: IRPF
wasteRecalc: Waste recaclulate
@ -378,7 +379,7 @@ login:
loginError: Invalid username or password
fieldRequired: This field is required
twoFactorRequired: Two-factor verification required
twoFactorRequired:
twoFactor:
validate: Validate
insert: Enter the verification code
explanation: >-
@ -555,50 +556,9 @@ ticket:
service: Service
attender: Attender
ok: Ok
consigneeStreet: Street
create:
address: Address
invoiceOut:
card:
issued: Issued
customerCard: Customer card
ticketList: Ticket List
summary:
issued: Issued
dued: Due
booked: Booked
taxBreakdown: Tax breakdown
taxableBase: Taxable base
rate: Rate
fee: Fee
tickets: Tickets
totalWithVat: Amount
globalInvoices:
errors:
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
noTicketsToInvoice: There are not tickets to invoice
criticalInvoiceError: 'Critical invoicing error, process stopped'
invalidSerialTypeForAll: The serial type must be global when invoicing all clients
table:
addressId: Address id
streetAddress: Street
statusCard:
percentageText: '{getPercentage}% {getAddressNumber} of {getNAddresses}'
pdfsNumberText: '{nPdfs} of {totalPdfs} PDFs'
negativeBases:
clientId: Client Id
base: Base
active: Active
hasToInvoice: Has to Invoice
verifiedData: Verified Data
comercial: Comercial
errors:
downloadCsvFailed: CSV download failed
department:
chat: Chat
bossDepartment: Boss Department
@ -610,6 +570,24 @@ department:
hasToSendMail: Send check-ins by email
departmentRemoved: Department removed
worker:
pageTitles:
workers: Workers
list: List
basicData: Basic data
summary: Summary
notifications: Notifications
workerCreate: New worker
department: Department
pda: PDA
notes: Notas
dms: My documentation
pbx: Private Branch Exchange
log: Log
calendar: Calendar
timeControl: Time control
locker: Locker
balance: Balance
medical: Medical
list:
department: Department
schedule: Schedule
@ -625,15 +603,24 @@ worker:
role: Role
sipExtension: Extension
locker: Locker
fiDueDate: FI due date
fiDueDate: DNI expiration date
sex: Sex
seniority: Seniority
seniority: Antiquity
fi: DNI/NIE/NIF
birth: Birth
isFreelance: Freelance
isSsDiscounted: SS Bonification
hasMachineryAuthorized: Machinery authorized
isDisable: Disable
business: Business
started: Started
ended: Ended
reasonEnd: Reason End
department: Departament
workerBusinessCategory: Worker Business Category
notes: Notes
workCenter: Center
professionalCategory: Professional Category
notificationsManager:
activeNotifications: Active notifications
availableNotifications: Available notifications
@ -692,6 +679,23 @@ worker:
sizeLimit: Size limit
isOnReservationMode: Reservation mode
machine: Machine
business:
tableVisibleColumns:
started: Start Date
ended: End Date
company: Company
reasonEnd: Reason for Termination
department: Department
professionalCategory: Professional Category
calendarType: Work Calendar
workCenter: Work Center
payrollCategories: Contract Category
occupationCode: Contribution Code
rate: Rate
businessType: Contract Type
amount: Salary
basicSalary: Transport Workers Salary
notes: Notes
wagon:
type:
submit: Submit
@ -823,6 +827,7 @@ travel:
destination: Destination
thermograph: Thermograph
travelFileDescription: 'Travel id { travelId }'
carrier: Carrier
components:
topbar: {}
itemsFilterPanel:

View File

@ -327,6 +327,7 @@ globals:
ticketsMonitor: Monitor de tickets
clientsActionsMonitor: Clientes y acciones
serial: Facturas por serie
business: Contratos
medical: Mutua
pit: IRPF
wasteRecalc: Recalcular mermas
@ -530,6 +531,7 @@ ticket:
purchaseRequest: Petición de compra
service: Servicio
attender: Consignatario
consigneeStreet: Dirección
create:
address: Dirección
invoiceOut:
@ -623,6 +625,26 @@ department:
hasToSendMail: Enviar fichadas por mail
departmentRemoved: Departamento eliminado
worker:
pageTitles:
workers: Trabajadores
list: Listado
basicData: Datos básicos
summary: Resumen
notifications: Notificaciones
workerCreate: Nuevo trabajador
department: Departamentos
pda: PDA
notes: Notas
dms: Mi documentación
pbx: Centralita
log: Historial
calendar: Calendario
timeControl: Control de horario
locker: Taquilla
balance: Balance
business: Contrato
formation: Formación
medical: Mutua
list:
department: Departamento
schedule: Horario
@ -647,6 +669,15 @@ worker:
isSsDiscounted: Bonificación SS
hasMachineryAuthorized: Autorizado para maquinaria
isDisable: Deshabilitado
business: Contrato
started: Antigüedad
ended: Fin
reasonEnd: Motivo finalización
department: Departamento
workerBusinessCategory: Categoria profesional
notes: Notas
workCenter: Centro
professionalCategory: Categoria profesional
notificationsManager:
activeNotifications: Notificaciones activas
availableNotifications: Notificaciones disponibles
@ -693,6 +724,23 @@ worker:
debit: Debe
credit: Haber
concept: Concepto
business:
tableVisibleColumns:
started: Fecha inicio
ended: Fecha fin
company: Empresa
reasonEnd: Motivo finalización
department: Departamento
professionalCategory: Categoria profesional
calendarType: Calendario laboral
workCenter: Centro
payrollCategories: Categoria contrato
occupationCode: Cotización
rate: Tarifa
businessType: Contrato
amount: Salario
basicSalary: Salario transportistas
notes: Notas
operator:
numberOfWagons: Número de vagones
train: tren
@ -705,7 +753,6 @@ worker:
sizeLimit: Tamaño límite
isOnReservationMode: Modo de reserva
machine: Máquina
wagon:
type:
submit: Guardar
@ -834,6 +881,7 @@ travel:
destination: Destino
thermograph: Termógrafo
travelFileDescription: 'Id envío { travelId }'
carrier: Transportista
components:
topbar: {}
itemsFilterPanel:

View File

@ -53,6 +53,7 @@ const debtWarning = computed(() => {
@on-fetch="setData"
:summary="$props.summary"
data-key="customer"
width="lg-width"
>
<template #menu="{ entity }">
<CustomerDescriptorMenu :customer="entity" />

View File

@ -1,3 +1,4 @@
<script setup>
import { useI18n } from 'vue-i18n';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
@ -169,9 +170,16 @@ en:
fi: FI
salesPersonFk: Salesperson
provinceFk: Province
isActive: Is active
city: City
phone: Phone
email: Email
isToBeMailed: Mailed
isEqualizated: Equailized
businessTypeFk: Business type
sageTaxTypeFk: Sage Tax Type
sageTransactionTypeFk: Sage Tax Type
payMethodFk: Billing data
zoneFk: Zone
socialName : Social name
name: Name
@ -180,6 +188,13 @@ es:
params:
search: Contiene
fi: NIF
isActive: Activo
isToBeMailed: A enviar
isEqualizated: Recargo de equivalencia
businessTypeFk: Tipo de negocio
sageTaxTypeFk: Tipo de impuesto Sage
sageTransactionTypeFk: Tipo de impuesto Sage
payMethodFk: Forma de pago
salesPersonFk: Comercial
provinceFk: Provincia
city: Ciudad

View File

@ -38,8 +38,8 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('globals.name'),
name: 'name',
label: t('globals.name'),
isTitle: true,
create: true,
columnClass: 'expand',
@ -51,25 +51,30 @@ const columns = computed(() => [
isTitle: true,
create: true,
columnClass: 'expand',
attrs: {
uppercase: true,
},
columnFilter: {
component: 'select',
attrs: {
url: 'Clients',
fields: ['socialName'],
optionLabel: 'socialName',
optionValue: 'socialName',
uppercase: false,
},
},
attrs: {
uppercase: true,
},
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.fi'),
name: 'fi',
label: t('customer.extendedList.tableVisibleColumns.fi'),
create: true,
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.salesPersonFk'),
name: 'salesPersonFk',
label: t('customer.extendedList.tableVisibleColumns.salesPersonFk'),
component: 'select',
attrs: {
url: 'Workers/activeWithInheritedRole',
@ -85,8 +90,8 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('customer.summary.credit'),
name: 'credit',
label: t('customer.summary.credit'),
columnFilter: {
component: 'number',
inWhere: true,
@ -94,8 +99,8 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.creditInsurance'),
name: 'creditInsurance',
label: t('customer.extendedList.tableVisibleColumns.creditInsurance'),
columnFilter: {
component: 'number',
inWhere: true,
@ -103,8 +108,8 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.phone'),
name: 'phone',
label: t('customer.extendedList.tableVisibleColumns.phone'),
cardVisible: true,
columnFilter: {
component: 'number',
@ -112,8 +117,8 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('customer.summary.mobile'),
name: 'mobile',
label: t('customer.summary.mobile'),
cardVisible: true,
columnFilter: {
component: 'number',
@ -122,8 +127,8 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.street'),
name: 'street',
label: t('customer.extendedList.tableVisibleColumns.street'),
create: true,
columnFilter: {
inWhere: true,
@ -132,8 +137,8 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.countryFk'),
name: 'countryFk',
label: t('customer.extendedList.tableVisibleColumns.countryFk'),
columnFilter: {
component: 'select',
inWhere: true,
@ -146,8 +151,8 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.provinceFk'),
name: 'provinceFk',
label: t('customer.extendedList.tableVisibleColumns.provinceFk'),
component: 'select',
attrs: {
url: 'Provinces',
@ -159,24 +164,24 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('customer.summary.city'),
name: 'city',
label: t('customer.summary.city'),
},
{
align: 'left',
label: t('customer.summary.postcode'),
name: 'postcode',
label: t('customer.summary.postcode'),
},
{
align: 'left',
label: t('globals.params.email'),
name: 'email',
label: t('globals.params.email'),
cardVisible: true,
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.created'),
name: 'created',
label: t('customer.extendedList.tableVisibleColumns.created'),
format: ({ created }) => toDate(created),
columnFilter: {
component: 'date',
@ -186,10 +191,13 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.businessTypeFk'),
name: 'businessTypeFk',
label: t('customer.extendedList.tableVisibleColumns.businessTypeFk'),
create: true,
component: 'select',
columnFilter: {
inWhere: true,
},
attrs: {
url: 'BusinessTypes',
fields: ['code', 'description'],
@ -204,8 +212,8 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('customer.summary.payMethodFk'),
name: 'payMethodFk',
label: t('customer.summary.payMethodFk'),
columnFilter: {
component: 'select',
attrs: {
@ -225,8 +233,6 @@ const columns = computed(() => [
optionLabel: 'vat',
url: 'SageTaxTypes',
},
alias: 'sti',
inWhere: true,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.sageTaxType),
},
@ -240,15 +246,13 @@ const columns = computed(() => [
optionLabel: 'transaction',
url: 'SageTransactionTypes',
},
alias: 'stt',
inWhere: true,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.sageTransactionType),
},
{
align: 'left',
label: t('customer.summary.isActive'),
name: 'isActive',
label: t('customer.summary.isActive'),
chip: {
color: null,
condition: (value) => !value,
@ -260,24 +264,24 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('globals.isVies'),
name: 'isVies',
label: t('globals.isVies'),
columnFilter: {
inWhere: true,
},
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.isTaxDataChecked'),
name: 'isTaxDataChecked',
label: t('customer.extendedList.tableVisibleColumns.isTaxDataChecked'),
columnFilter: {
inWhere: true,
},
},
{
align: 'left',
label: t('customer.summary.isEqualizated'),
name: 'isEqualizated',
label: t('customer.summary.isEqualizated'),
create: true,
columnFilter: {
inWhere: true,
@ -285,8 +289,8 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.isFreezed'),
name: 'isFreezed',
label: t('customer.extendedList.tableVisibleColumns.isFreezed'),
chip: {
color: null,
condition: (value) => value,
@ -298,48 +302,48 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.hasToInvoice'),
name: 'hasToInvoice',
label: t('customer.extendedList.tableVisibleColumns.hasToInvoice'),
columnFilter: {
inWhere: true,
},
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.hasToInvoiceByAddress'),
name: 'hasToInvoiceByAddress',
label: t('customer.extendedList.tableVisibleColumns.hasToInvoiceByAddress'),
columnFilter: {
inWhere: true,
},
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.isToBeMailed'),
name: 'isToBeMailed',
label: t('customer.extendedList.tableVisibleColumns.isToBeMailed'),
columnFilter: {
inWhere: true,
},
},
{
align: 'left',
label: t('customer.summary.hasLcr'),
name: 'hasLcr',
label: t('customer.summary.hasLcr'),
columnFilter: {
inWhere: true,
},
},
{
align: 'left',
label: t('customer.summary.hasCoreVnl'),
name: 'hasCoreVnl',
label: t('customer.summary.hasCoreVnl'),
columnFilter: {
inWhere: true,
},
},
{
align: 'left',
label: t('customer.extendedList.tableVisibleColumns.hasSepaVnl'),
name: 'hasSepaVnl',
label: t('customer.extendedList.tableVisibleColumns.hasSepaVnl'),
columnFilter: {
inWhere: true,
},

View File

@ -114,7 +114,7 @@ const columns = computed(() => [
action: ({ id }) =>
window.open(
router.resolve({ params: { id }, name: 'TicketSale' }).href,
'_blank'
'_blank',
),
isPrimary: true,
},
@ -122,7 +122,7 @@ const columns = computed(() => [
title: t('components.smartCard.viewSummary'),
icon: 'preview',
isPrimary: true,
action: (row) => viewSummary(row.id, TicketSummary),
action: (row) => viewSummary(row.id, TicketSummary, 'lg-width'),
},
],
},

View File

@ -1,8 +1,9 @@
import axios from 'axios';
export async function getAddresses(clientId) {
export async function getAddresses(clientId, _filter = {}) {
if (!clientId) return;
const filter = {
..._filter,
fields: ['nickname', 'street', 'city', 'id'],
where: { isActive: true },
order: 'nickname ASC',

View File

@ -1,7 +1,8 @@
import axios from 'axios';
export async function getClient(clientId) {
export async function getClient(clientId, _filter = {}) {
const filter = {
..._filter,
include: {
relation: 'defaultAddress',
scope: {

View File

@ -10,4 +10,4 @@ import DepartmentDescriptor from 'pages/Department/Card/DepartmentDescriptor.vue
base-url="Departments"
:descriptor="DepartmentDescriptor"
/>
</template>
</template>

View File

@ -61,10 +61,6 @@ onMounted(() => {
<VnSelect
:label="t('globals.supplier')"
v-model="data.supplierFk"
url="Suppliers"
option-value="id"
option-label="nickname"
:fields="['id', 'nickname']"
hide-selected
:required="true"
map-options

View File

@ -27,7 +27,42 @@ const route = useRoute();
const { t } = useI18n();
const entryDescriptorRef = ref(null);
const url = ref();
const entryFilter = {
include: [
{
relation: 'travel',
scope: {
fields: ['id', 'landed', 'shipped', 'agencyModeFk', 'warehouseOutFk'],
include: [
{
relation: 'agency',
scope: {
fields: ['name'],
},
},
{
relation: 'warehouseOut',
scope: {
fields: ['name'],
},
},
{
relation: 'warehouseIn',
scope: {
fields: ['name'],
},
},
],
},
},
{
relation: 'supplier',
scope: {
fields: ['id', 'nickname'],
},
},
],
};
const entityId = computed(() => {
return $props.id || route.params.id;
});
@ -86,9 +121,10 @@ async function deleteEntry() {
ref="entryDescriptorRef"
module="Entry"
:url="`Entries/${entityId}`"
:filter="filter"
:userFilter="entryFilter"
title="supplier.nickname"
data-key="Entry"
width="lg-width"
>
<template #menu="{ entity }">
<QItem v-ripple clickable @click="showEntryReport(entity)">

View File

@ -1,5 +1,11 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { useQuasar } from 'quasar';
import axios from 'axios';
import { usePrintService } from 'composables/usePrintService';
import useNotify from 'src/composables/useNotify.js';
import VnConfirm from 'src/components/ui/VnConfirm.vue';
const { openReport } = usePrintService();
@ -9,14 +15,47 @@ const $props = defineProps({
required: true,
},
});
const { t } = useI18n();
const { notify } = useNotify();
const quasar = useQuasar();
const route = useRoute();
function showEntryReport() {
openReport(`Entries/${$props.id}/entry-order-pdf`);
}
const openDialog = () => {
quasar.dialog({
component: VnConfirm,
componentProps: {
title: t('transferEntryDialog'),
promise: transferEntry,
},
});
};
const transferEntry = async () => {
const { data } = await axios.post(`Entries/${route.params.id}/transfer`);
notify('globals.dataSaved', 'positive');
const url = `#/entry/${data.newEntryFk.newEntryFk}/summary`;
window.open(url, '_blank');
};
</script>
<template>
<QItem v-ripple clickable @click="showEntryReport">
<QItemSection>{{ $t('entryList.list.showEntryReport') }}</QItemSection>
<QItemSection>{{ $t('entry.descriptorMenu.showEntryReport') }}</QItemSection>
</QItem>
<QItem v-ripple clickable @click="openDialog">
<QItemSection>{{ t('transferEntry') }}</QItemSection>
</QItem>
</template>
<i18n>
en:
transferEntryDialog: The entries will be transferred to the next day
transferEntry: Transfer Entry
es:
transferEntryDialog: Se van a transferir las compras al dia siguiente
transferEntry: Transferir Entrada
</i18n>

View File

@ -13,6 +13,7 @@ import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import { useStateStore } from 'stores/useStateStore';
import { useState } from 'src/composables/useState';
import { toDate } from 'src/filters';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const state = useState();
const { t } = useI18n();
@ -26,7 +27,6 @@ const newEntryForm = reactive({
travelFk: Number(route.query?.travelFk) || null,
companyFk: user.value.companyFk || null,
});
const suppliersOptions = ref([]);
const travelsOptions = ref([]);
const companiesOptions = ref([]);
@ -36,13 +36,6 @@ const redirectToEntryBasicData = (_, { 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'] }"
@ -79,28 +72,13 @@ const redirectToEntryBasicData = (_, { id }) => {
>
<template #form="{ data, validate }">
<VnRow>
<VnSelect
:label="t('Supplier')"
<VnSelectSupplier
class="full-width"
v-model="data.supplierFk"
:options="suppliersOptions"
option-value="id"
option-label="nickname"
hide-selected
:required="true"
:rules="validate('entry.supplierFk')"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.nickname }}</QItemLabel>
<QItemLabel caption>
#{{ scope.opt?.id }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
/>
</VnRow>
<VnRow>
<VnSelect
@ -152,7 +130,6 @@ const redirectToEntryBasicData = (_, { id }) => {
<i18n>
es:
Supplier: Proveedor
Travel: Envío
Company: Empresa
</i18n>

View File

@ -7,6 +7,7 @@ import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue';
import FetchData from 'components/FetchData.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n();
const props = defineProps({
@ -128,16 +129,79 @@ const entryFilterPanel = ref();
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('entryFilter.params.supplierFk')"
<VnSelectSupplier
v-model="params.supplierFk"
@update:model-value="searchFn()"
url="Suppliers"
option-value="id"
option-label="name"
:fields="['id', 'name', 'nickname']"
:filter-options="['id', 'name', 'nickname']"
sort-by="nickname"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
<QItemSection>
<VnInput
v-model="params.invoiceNumber"
:label="t('params.invoiceNumber')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
v-model="params.reference"
:label="t('entry.list.tableVisibleColumns.reference')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('params.agencyModeId')"
v-model="params.agencyModeId"
@update:model-value="searchFn()"
url="AgencyModes"
:fields="['id', 'name']"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
v-model="params.evaNotes"
:label="t('params.evaNotes')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('params.warehouseOutFk')"
v-model="params.warehouseOutFk"
@update:model-value="searchFn()"
url="Warehouses"
:fields="['id', 'name']"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('params.warehouseInFk')"
v-model="params.warehouseInFk"
@update:model-value="searchFn()"
url="Warehouses"
:fields="['id', 'name']"
hide-selected
dense
outlined
@ -169,9 +233,10 @@ const entryFilterPanel = ref();
</QItem>
<QItem>
<QItemSection>
<VnInput
v-model="params.reference"
:label="t('entry.list.tableVisibleColumns.reference')"
<VnSelect
:label="t('params.entryTypeCode')"
v-model="params.entryTypeCode"
@update:model-value="searchFn()"
is-outlined
/>
</QItemSection>

View File

@ -7,6 +7,7 @@ import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue';
import VnSelect from 'components/common/VnSelect.vue';
import ItemsFilterPanel from 'src/components/ItemsFilterPanel.vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n();
@ -18,7 +19,6 @@ defineProps({
});
const itemTypeWorkersOptions = ref([]);
const suppliersOptions = ref([]);
const tagValues = ref([]);
</script>
@ -30,13 +30,6 @@ const tagValues = ref([]);
:filter="{ fields: ['id', 'nickname'], order: 'nickname ASC', limit: 30 }"
@on-fetch="(data) => (itemTypeWorkersOptions = data)"
/>
<FetchData
url="Suppliers"
limit="30"
auto-load
:filter="{ fields: ['id', 'name', 'nickname'], order: 'name ASC', limit: 30 }"
@on-fetch="(data) => (suppliersOptions = data)"
/>
<ItemsFilterPanel :data-key="dataKey" :custom-tags="['tags']">
<template #body="{ params, searchFn }">
<QItem class="q-my-md">
@ -57,31 +50,14 @@ const tagValues = ref([]);
</QItem>
<QItem class="q-my-md">
<QItemSection>
<VnSelect
:label="t('globals.params.supplierFk')"
<VnSelectSupplier
v-model="params.supplierFk"
:options="suppliersOptions"
option-value="id"
option-label="name"
@update:model-value="searchFn()"
hide-selected
dense
outlined
rounded
use-input
@update:model-value="searchFn()"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.name}}
</QItemLabel>
<QItemLabel caption>
{{ `#${scope.opt?.id } , ${ scope.opt?.nickname}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
/>
</QItemSection>
</QItem>
<QItem class="q-my-md">

View File

@ -128,6 +128,8 @@ entry:
workerFk: Worker id
search: Search entries
searchInfo: You can search by entry reference
descriptorMenu:
showEntryReport: Show entry report
entryFilter:
params:
invoiceNumber: Invoice number

View File

@ -13,6 +13,7 @@ import VnInput from 'src/components/common/VnInput.vue';
import VnDms from 'src/components/common/VnDms.vue';
import VnConfirm from 'src/components/ui/VnConfirm.vue';
import axios from 'axios';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n();
@ -115,27 +116,12 @@ function deleteFile(dmsFk) {
>
<template #form="{ data }">
<VnRow>
<VnSelect
:required="true"
:label="t('supplierFk')"
<VnSelectSupplier
v-model="data.supplierFk"
option-value="id"
option-label="nickname"
url="Suppliers"
:fields="['id', 'nickname']"
sort-by="nickname"
hide-selected
:is-clearable="false"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{
`${scope.opt.id} - ${scope.opt.nickname}`
}}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
:required="true"
/>
<VnInput
clearable
clear-icon="close"
@ -268,7 +254,7 @@ function deleteFile(dmsFk) {
</VnRow>
<VnRow>
<VnSelect
:label="t('invoicein.summary.sage')"
:label="t('invoiceIn.summary.sage')"
v-model="data.withholdingSageFk"
:options="sageWithholdings"
option-value="id"

View File

@ -1,10 +1,6 @@
<script setup>
import VnCard from 'components/common/VnCard.vue';
import VnCardBeta from 'components/common/VnCardBeta.vue';
import InvoiceInDescriptor from './InvoiceInDescriptor.vue';
import InvoiceInFilter from '../InvoiceInFilter.vue';
import InvoiceInSearchbar from '../InvoiceInSearchbar.vue';
import { onBeforeRouteUpdate } from 'vue-router';
import { setRectificative } from '../composables/setRectificative';
const filter = {
include: [
@ -39,20 +35,13 @@ const filter = {
},
],
};
onBeforeRouteUpdate(async (to) => await setRectificative(to));
</script>
<template>
<VnCard
<VnCardBeta
data-key="InvoiceIn"
base-url="InvoiceIns"
:filter="filter"
:descriptor="InvoiceInDescriptor"
:filter-panel="InvoiceInFilter"
search-data-key="InvoiceInList"
>
<template #searchbar>
<InvoiceInSearchbar />
</template>
</VnCard>
:user-filter="filter"
/>
</template>

View File

@ -105,7 +105,7 @@ async function setInvoiceCorrection(id) {
if (correctingData[0]) invoiceInCorrection.corrected = correctingData[0].correctedFk;
invoiceInCorrection.correcting = correctedData.map(
(corrected) => corrected.correctingFk
(corrected) => corrected.correctingFk,
);
}
</script>
@ -117,18 +117,19 @@ async function setInvoiceCorrection(id) {
:url="`InvoiceIns/${entityId}`"
:filter="filter"
title="supplierRef"
width="xlg-width"
>
<template #menu="{ entity }">
<InvoiceInDescriptorMenu :invoice="entity" />
</template>
<template #body="{ entity }">
<VnLv :label="t('invoicein.list.issued')" :value="toDate(entity.issued)" />
<VnLv :label="t('invoiceIn.list.issued')" :value="toDate(entity.issued)" />
<VnLv
:label="t('invoicein.summary.bookedDate')"
:label="t('invoiceIn.summary.bookedDate')"
:value="toDate(entity.booked)"
/>
<VnLv :label="t('invoicein.list.amount')" :value="toCurrency(totalAmount)" />
<VnLv :label="t('invoicein.list.supplier')">
<VnLv :label="t('invoiceIn.list.amount')" :value="toCurrency(totalAmount)" />
<VnLv :label="t('invoiceIn.list.supplier')">
<template #value>
<span class="link">
{{ entity?.supplier?.nickname }}
@ -145,7 +146,7 @@ async function setInvoiceCorrection(id) {
color="primary"
:to="routes.getSupplier(entity.supplierFk)"
>
<QTooltip>{{ t('invoicein.list.supplier') }}</QTooltip>
<QTooltip>{{ t('globals.supplier') }}</QTooltip>
</QBtn>
<QBtn
size="md"
@ -153,7 +154,7 @@ async function setInvoiceCorrection(id) {
color="primary"
:to="routes.getEntry(entity.entryFk)"
>
<QTooltip>{{ t('Entry') }}</QTooltip>
<QTooltip>{{ t('globals.entry') }}</QTooltip>
</QBtn>
<QBtn
size="md"
@ -161,7 +162,7 @@ async function setInvoiceCorrection(id) {
color="primary"
:to="routes.getTickets(entity.supplierFk)"
>
<QTooltip>{{ t('InvoiceOut.card.ticketList') }}</QTooltip>
<QTooltip>{{ t('globals.ticketList') }}</QTooltip>
</QBtn>
<QBtn
v-if="

View File

@ -40,15 +40,15 @@ const cplusRectificationTypes = ref([]);
const siiTypeInvoiceIns = ref([]);
const actions = {
unbook: {
title: t('assertAction', { action: t('invoicein.descriptorMenu.unbook') }),
title: t('assertAction', { action: t('invoiceIn.descriptorMenu.unbook') }),
action: toUnbook,
},
delete: {
title: t('assertAction', { action: t('invoicein.descriptorMenu.delete') }),
title: t('assertAction', { action: t('invoiceIn.descriptorMenu.delete') }),
action: deleteInvoice,
},
clone: {
title: t('assertAction', { action: t('invoicein.descriptorMenu.clone') }),
title: t('assertAction', { action: t('invoiceIn.descriptorMenu.clone') }),
action: cloneInvoice,
},
showPdf: { cb: showPdfInvoice },
@ -96,7 +96,7 @@ async function deleteInvoice() {
await axios.delete(`InvoiceIns/${entityId.value}`);
quasar.notify({
type: 'positive',
message: t('invoicein.descriptorMenu.invoiceDeleted'),
message: t('invoiceIn.descriptorMenu.invoiceDeleted'),
});
push({ path: '/invoice-in' });
}
@ -105,7 +105,7 @@ async function cloneInvoice() {
const { data } = await axios.post(`InvoiceIns/${entityId.value}/clone`);
quasar.notify({
type: 'positive',
message: t('invoicein.descriptorMenu.invoiceCloned'),
message: t('invoiceIn.descriptorMenu.invoiceCloned'),
});
push({ path: `/invoice-in/${data.id}/summary` });
}
@ -149,7 +149,7 @@ function sendPdfInvoice({ address }) {
const createInvoiceInCorrection = async () => {
const { data: correctingId } = await axios.post(
'InvoiceIns/corrective',
Object.assign(correctionFormData, { id: entityId.value })
Object.assign(correctionFormData, { id: entityId.value }),
);
push({ path: `/invoice-in/${correctingId}/summary` });
};
@ -186,7 +186,7 @@ const createInvoiceInCorrection = async () => {
clickable
@click="book(entityId)"
>
<QItemSection>{{ t('invoicein.descriptorMenu.toBook') }}</QItemSection>
<QItemSection>{{ t('invoiceIn.descriptorMenu.toBook') }}</QItemSection>
</QItem>
</template>
</InvoiceInToBook>
@ -197,7 +197,7 @@ const createInvoiceInCorrection = async () => {
@click="triggerMenu('unbook')"
>
<QItemSection>
{{ t('invoicein.descriptorMenu.toUnbook') }}
{{ t('invoiceIn.descriptorMenu.toUnbook') }}
</QItemSection>
</QItem>
<QItem
@ -206,19 +206,19 @@ const createInvoiceInCorrection = async () => {
clickable
@click="triggerMenu('delete')"
>
<QItemSection>{{ t('invoicein.descriptorMenu.deleteInvoice') }}</QItemSection>
<QItemSection>{{ t('invoiceIn.descriptorMenu.deleteInvoice') }}</QItemSection>
</QItem>
<QItem v-if="canEditProp('clone')" v-ripple clickable @click="triggerMenu('clone')">
<QItemSection>{{ t('invoicein.descriptorMenu.cloneInvoice') }}</QItemSection>
<QItemSection>{{ t('invoiceIn.descriptorMenu.cloneInvoice') }}</QItemSection>
</QItem>
<QItem v-if="isAgricultural()" v-ripple clickable @click="triggerMenu('showPdf')">
<QItemSection>{{
t('invoicein.descriptorMenu.showAgriculturalPdf')
t('invoiceIn.descriptorMenu.showAgriculturalPdf')
}}</QItemSection>
</QItem>
<QItem v-if="isAgricultural()" v-ripple clickable @click="triggerMenu('sendPdf')">
<QItemSection
>{{ t('invoicein.descriptorMenu.sendAgriculturalPdf') }}...</QItemSection
>{{ t('invoiceIn.descriptorMenu.sendAgriculturalPdf') }}...</QItemSection
>
</QItem>
<QItem
@ -226,9 +226,10 @@ const createInvoiceInCorrection = async () => {
v-ripple
clickable
@click="triggerMenu('correct')"
data-cy="createCorrectiveItem"
>
<QItemSection
>{{ t('invoicein.descriptorMenu.createCorrective') }}...</QItemSection
>{{ t('invoiceIn.descriptorMenu.createCorrective') }}...</QItemSection
>
</QItem>
<QItem v-if="invoice.dmsFk" v-ripple clickable @click="downloadFile(invoice.dmsFk)">
@ -310,11 +311,11 @@ const createInvoiceInCorrection = async () => {
<i18n>
en:
isNotLinked: The entry {bookEntry} has been deleted with {accountingEntries} entries
isLinked: The entry has been linked to Sage. Please contact administration for further information
isNotLinked: The entry {bookEntry} has been deleted with {accountingEntries} entries
isLinked: The entry has been linked to Sage. Please contact administration for further information
assertAction: Are you sure you want to {action} this invoice?
es:
isNotLinked: Se ha eliminado el asiento {bookEntry} con {accountingEntries} apuntes
isLinked: El asiento fue enlazado a Sage, por favor contacta con administración
isNotLinked: Se ha eliminado el asiento {bookEntry} con {accountingEntries} apuntes
isLinked: El asiento fue enlazado a Sage, por favor contacta con administración
assertAction: Estas seguro de querer {action} esta factura?
</i18n>

View File

@ -27,14 +27,14 @@ const intrastatTotals = ref({ amount: 0, net: 0, stems: 0 });
const vatColumns = ref([
{
name: 'expense',
label: 'invoicein.summary.expense',
label: 'invoiceIn.summary.expense',
field: (row) => row.expenseFk,
sortable: true,
align: 'left',
},
{
name: 'landed',
label: 'invoicein.summary.taxableBase',
label: 'invoiceIn.summary.taxableBase',
field: (row) => row.taxableBase,
format: (value) => toCurrency(value),
sortable: true,
@ -42,7 +42,7 @@ const vatColumns = ref([
},
{
name: 'vat',
label: 'invoicein.summary.sageVat',
label: 'invoiceIn.summary.sageVat',
field: (row) => {
if (row.taxTypeSage) return `#${row.taxTypeSage.id} : ${row.taxTypeSage.vat}`;
},
@ -52,7 +52,7 @@ const vatColumns = ref([
},
{
name: 'transaction',
label: 'invoicein.summary.sageTransaction',
label: 'invoiceIn.summary.sageTransaction',
field: (row) => {
if (row.transactionTypeSage)
return `#${row.transactionTypeSage.id} : ${row.transactionTypeSage?.transaction}`;
@ -63,7 +63,7 @@ const vatColumns = ref([
},
{
name: 'rate',
label: 'invoicein.summary.rate',
label: 'invoiceIn.summary.rate',
field: (row) => taxRate(row.taxableBase, row.taxTypeSage?.rate),
format: (value) => toCurrency(value),
sortable: true,
@ -71,7 +71,7 @@ const vatColumns = ref([
},
{
name: 'currency',
label: 'invoicein.summary.currency',
label: 'invoiceIn.summary.currency',
field: (row) => row.foreignValue,
format: (val) => val && toCurrency(val, currency.value),
sortable: true,
@ -82,21 +82,21 @@ const vatColumns = ref([
const dueDayColumns = ref([
{
name: 'date',
label: 'invoicein.summary.dueDay',
label: 'invoiceIn.summary.dueDay',
field: (row) => toDate(row.dueDated),
sortable: true,
align: 'left',
},
{
name: 'bank',
label: 'invoicein.summary.bank',
label: 'invoiceIn.summary.bank',
field: (row) => row.bank.bank,
sortable: true,
align: 'left',
},
{
name: 'amount',
label: 'invoicein.list.amount',
label: 'invoiceIn.list.amount',
field: (row) => row.amount,
format: (value) => toCurrency(value),
sortable: true,
@ -104,7 +104,7 @@ const dueDayColumns = ref([
},
{
name: 'landed',
label: 'invoicein.summary.foreignValue',
label: 'invoiceIn.summary.foreignValue',
field: (row) => row.foreignValue,
format: (val) => val && toCurrency(val, currency.value),
sortable: true,
@ -115,7 +115,7 @@ const dueDayColumns = ref([
const intrastatColumns = ref([
{
name: 'code',
label: 'invoicein.summary.code',
label: 'invoiceIn.summary.code',
field: (row) => {
return `${row.intrastat.id}: ${row.intrastat?.description}`;
},
@ -124,21 +124,21 @@ const intrastatColumns = ref([
},
{
name: 'amount',
label: 'invoicein.list.amount',
label: 'invoiceIn.list.amount',
field: (row) => toCurrency(row.amount),
sortable: true,
align: 'left',
},
{
name: 'net',
label: 'invoicein.summary.net',
label: 'invoiceIn.summary.net',
field: (row) => row.net,
sortable: true,
align: 'left',
},
{
name: 'stems',
label: 'invoicein.summary.stems',
label: 'invoiceIn.summary.stems',
field: (row) => row.stems,
format: (value) => value,
sortable: true,
@ -146,7 +146,7 @@ const intrastatColumns = ref([
},
{
name: 'landed',
label: 'invoicein.summary.country',
label: 'invoiceIn.summary.country',
field: (row) => row.country?.code,
format: (value) => value,
sortable: true,
@ -214,7 +214,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
/>
</QCardSection>
<VnLv
:label="t('invoicein.list.supplier')"
:label="t('invoiceIn.list.supplier')"
:value="entity.supplier?.name"
>
<template #value>
@ -225,14 +225,14 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</template>
</VnLv>
<VnLv
:label="t('invoicein.list.supplierRef')"
:label="t('invoiceIn.list.supplierRef')"
:value="entity.supplierRef"
/>
<VnLv
:label="t('invoicein.summary.currency')"
:label="t('invoiceIn.summary.currency')"
:value="entity.currency?.code"
/>
<VnLv :label="t('invoicein.serial')" :value="`${entity.serial}`" />
<VnLv :label="t('invoiceIn.serial')" :value="`${entity.serial}`" />
<VnLv
:label="t('globals.country')"
:value="entity.supplier?.country?.code"
@ -247,19 +247,19 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</QCardSection>
<VnLv
:ellipsis-value="false"
:label="t('invoicein.summary.issued')"
:label="t('invoiceIn.summary.issued')"
:value="toDate(entity.issued)"
/>
<VnLv
:label="t('invoicein.summary.operated')"
:label="t('invoiceIn.summary.operated')"
:value="toDate(entity.operated)"
/>
<VnLv
:label="t('invoicein.summary.bookEntried')"
:label="t('invoiceIn.summary.bookEntried')"
:value="toDate(entity.bookEntried)"
/>
<VnLv
:label="t('invoicein.summary.bookedDate')"
:label="t('invoiceIn.summary.bookedDate')"
:value="toDate(entity.booked)"
/>
<VnLv :label="t('globals.isVies')" :value="entity.supplier?.isVies" />
@ -272,18 +272,18 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
/>
</QCardSection>
<VnLv
:label="t('invoicein.summary.sage')"
:label="t('invoiceIn.summary.sage')"
:value="entity.sageWithholding?.withholding"
/>
<VnLv
:label="t('invoicein.summary.vat')"
:label="t('invoiceIn.summary.vat')"
:value="entity.expenseDeductible?.name"
/>
<VnLv
:label="t('invoicein.card.company')"
:label="t('invoiceIn.card.company')"
:value="entity.company?.code"
/>
<VnLv :label="t('invoicein.isBooked')" :value="invoiceIn?.isBooked" />
<VnLv :label="t('invoiceIn.isBooked')" :value="invoiceIn?.isBooked" />
</QCard>
<QCard class="vn-one">
<QCardSection class="q-pa-none">
@ -294,11 +294,11 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</QCardSection>
<QCardSection class="q-pa-none">
<VnLv
:label="t('invoicein.summary.taxableBase')"
:label="t('invoiceIn.summary.taxableBase')"
:value="toCurrency(entity.totals.totalTaxableBase)"
/>
<VnLv label="Total" :value="toCurrency(entity.totals.totalVat)" />
<VnLv :label="t('invoicein.summary.dueTotal')">
<VnLv :label="t('invoiceIn.summary.dueTotal')">
<template #value>
<QChip
dense
@ -306,8 +306,8 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
:color="amountsNotMatch ? 'negative' : 'transparent'"
:title="
amountsNotMatch
? t('invoicein.summary.noMatch')
: t('invoicein.summary.dueTotal')
? t('invoiceIn.summary.noMatch')
: t('invoiceIn.summary.dueTotal')
"
>
{{ toCurrency(entity.totals.totalDueDay) }}
@ -318,7 +318,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</QCard>
<!--Vat-->
<QCard v-if="entity.invoiceInTax.length" class="vat">
<VnTitle :url="getLink('vat')" :text="t('invoicein.card.vat')" />
<VnTitle :url="getLink('vat')" :text="t('invoiceIn.card.vat')" />
<QTable
:columns="vatColumns"
:rows="entity.invoiceInTax"
@ -366,7 +366,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</QCard>
<!--Due Day-->
<QCard v-if="entity.invoiceInDueDay.length" class="due-day">
<VnTitle :url="getLink('due-day')" :text="t('invoicein.card.dueDay')" />
<VnTitle :url="getLink('due-day')" :text="t('invoiceIn.card.dueDay')" />
<QTable :columns="dueDayColumns" :rows="entity.invoiceInDueDay" flat>
<template #header="dueDayProps">
<QTr :props="dueDayProps" class="bg">
@ -404,7 +404,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
<QCard v-if="entity.invoiceInIntrastat.length">
<VnTitle
:url="getLink('intrastat')"
:text="t('invoicein.card.intrastat')"
:text="t('invoiceIn.card.intrastat')"
/>
<QTable
:columns="intrastatColumns"

View File

@ -12,6 +12,7 @@ import { useStateStore } from 'stores/useStateStore';
import { useState } from 'src/composables/useState';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const state = useState();
const { t } = useI18n();
@ -60,30 +61,14 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
>
<template #form="{ data, validate }">
<VnRow>
<VnSelect
url="Suppliers"
:fields="['id', 'nickname']"
:label="t('Supplier')"
<VnSelectSupplier
v-model="data.supplierFk"
option-value="id"
option-label="nickname"
hide-selected
:required="true"
:rules="validate('entry.supplierFk')"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.nickname }}</QItemLabel>
<QItemLabel caption>
#{{ scope.opt?.id }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
/>
<VnInput
:label="t('invoicein.list.supplierRef')"
:label="t('invoiceIn.list.supplierRef')"
v-model="data.supplierRef"
/>
</VnRow>
@ -97,10 +82,10 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
map-options
hide-selected
:required="true"
:rules="validate('invoicein.companyFk')"
:rules="validate('invoiceIn.companyFk')"
/>
<VnInputDate
:label="t('invoicein.summary.issued')"
:label="t('invoiceIn.summary.issued')"
v-model="data.issued"
/>
</VnRow>
@ -111,7 +96,6 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
<i18n>
es:
Supplier: Proveedor
Travel: Envío
Company: Empresa
</i18n>

View File

@ -6,6 +6,7 @@ import VnInputDate from 'components/common/VnInputDate.vue';
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
import { dateRange } from 'src/filters';
import { date } from 'quasar';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
defineProps({ dataKey: { type: String, required: true } });
const dateFormat = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
@ -65,29 +66,12 @@ function handleDaysAgo(params, daysAgo) {
</QItem>
<QItem>
<QItemSection>
<VnSelect
<VnSelectSupplier
v-model="params.supplierFk"
url="Suppliers"
:fields="['id', 'nickname', 'name']"
:label="getLocale('supplierFk')"
option-label="nickname"
dense
outlined
rounded
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.name}}
</QItemLabel>
<QItemLabel caption>
{{ `#${scope.opt?.id } , ${ scope.opt?.nickname}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
/>
</QItemSection>
</QItem>
<QItem>
@ -164,7 +148,7 @@ function handleDaysAgo(params, daysAgo) {
<QItem>
<QItemSection>
<QCheckbox
:label="$t('invoicein.isBooked')"
:label="$t('invoiceIn.isBooked')"
v-model="params.isBooked"
@update:model-value="searchFn()"
toggle-indeterminate

View File

@ -8,17 +8,18 @@ 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';
import FetchData from 'src/components/FetchData.vue';
import VnSection from 'src/components/common/VnSection.vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const user = useState().getUser();
const { viewSummary } = useSummaryDialog();
const { t } = useI18n();
const dataKey = 'InvoiceInList';
const tableRef = ref();
const companies = ref([]);
@ -26,7 +27,7 @@ const cols = computed(() => [
{
align: 'left',
name: 'isBooked',
label: t('invoicein.isBooked'),
label: t('invoiceIn.isBooked'),
columnFilter: false,
component: 'checkbox',
},
@ -42,7 +43,7 @@ const cols = computed(() => [
{
align: 'left',
name: 'supplierFk',
label: t('invoicein.list.supplier'),
label: t('invoiceIn.list.supplier'),
columnFilter: {
component: 'select',
attrs: {
@ -56,16 +57,16 @@ const cols = computed(() => [
{
align: 'left',
name: 'supplierRef',
label: t('invoicein.list.supplierRef'),
label: t('invoiceIn.list.supplierRef'),
},
{
align: 'left',
name: 'serial',
label: t('invoicein.serial'),
label: t('invoiceIn.serial'),
},
{
align: 'left',
label: t('invoicein.list.issued'),
label: t('invoiceIn.list.issued'),
name: 'issued',
component: null,
columnFilter: {
@ -75,7 +76,7 @@ const cols = computed(() => [
},
{
align: 'left',
label: t('invoicein.list.dueDated'),
label: t('invoiceIn.list.dueDated'),
name: 'dueDated',
component: null,
columnFilter: {
@ -87,12 +88,12 @@ const cols = computed(() => [
{
align: 'left',
name: 'awbCode',
label: t('invoicein.list.awb'),
label: t('invoiceIn.list.awb'),
},
{
align: 'left',
name: 'amount',
label: t('invoicein.list.amount'),
label: t('invoiceIn.list.amount'),
format: ({ amount }) => toCurrency(amount),
cardVisible: true,
},
@ -131,73 +132,70 @@ const cols = computed(() => [
},
]);
</script>
<template>
<FetchData url="Companies" @on-fetch="(data) => (companies = data)" auto-load />
<InvoiceInSearchbar />
<RightMenu>
<template #right-panel>
<InvoiceInFilter data-key="InvoiceInList" />
</template>
</RightMenu>
<VnTable
ref="tableRef"
data-key="InvoiceInList"
url="InvoiceIns/filter"
:order="['issued DESC', 'id DESC']"
:create="{
urlCreate: 'InvoiceIns',
title: t('globals.createInvoiceIn'),
onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: { companyFk: user.companyFk, issued: Date.vnNew() },
}"
redirect="invoice-in"
<VnSection
:data-key
:columns="cols"
:right-search="false"
:disable-option="{ card: true }"
:auto-load="!!$route.query.table"
prefix="invoiceIn"
:array-data-props="{
url: 'InvoiceIns/filter',
order: ['issued DESC', 'id DESC'],
}"
>
<template #column-supplierFk="{ row }">
<span class="link" @click.stop>
{{ row.supplierName }}
<SupplierDescriptorProxy :id="row.supplierFk" />
</span>
<template #advanced-menu>
<InvoiceInFilter :data-key />
</template>
<template #more-create-dialog="{ data }">
<VnSelect
v-model="data.supplierFk"
url="Suppliers"
:fields="['id', 'nickname', 'name']"
:label="t('globals.supplier')"
option-value="id"
option-label="nickname"
:filter-options="['id', 'name', 'nickname']"
:required="true"
<template #body>
<VnTable
ref="tableRef"
data-key="InvoiceInList"
url="InvoiceIns/filter"
:order="['issued DESC', 'id DESC']"
:create="{
urlCreate: 'InvoiceIns',
title: t('globals.createInvoiceIn'),
onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: { companyFk: user.companyFk, issued: Date.vnNew() },
}"
redirect="invoice-in"
:columns="cols"
:right-search="false"
:disable-option="{ card: true }"
:auto-load="!!$route.query.table"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.nickname }}</QItemLabel>
<QItemLabel caption>
#{{ scope.opt?.id }}, {{ scope.opt?.name }}
</QItemLabel>
</QItemSection>
</QItem>
<template #column-supplierFk="{ row }">
<span class="link" @click.stop>
{{ row.supplierName }}
<SupplierDescriptorProxy :id="row.supplierFk" />
</span>
</template>
</VnSelect>
<VnInput
:label="t('invoicein.list.supplierRef')"
v-model="data.supplierRef"
/>
<VnSelect
url="Companies"
:label="t('globals.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 #more-create-dialog="{ data }">
<VnSelectSupplier
v-model="data.supplierFk"
hide-selected
:required="true"
/>
<VnInput
:label="t('invoicein.list.supplierRef')"
v-model="data.supplierRef"
/>
<VnSelect
url="Companies"
:label="t('globals.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>
</VnTable>
</template>
</VnTable>
</VnSection>
</template>

View File

@ -1,18 +0,0 @@
<script setup>
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>
<template>
<VnSearchbar
data-key="InvoiceInList"
:label="t('Search invoice')"
:info="t('Search invoices in by id or supplier fiscal name')"
url="InvoiceIns/filter"
/>
</template>
<i18n>
es:
Search invoice: Buscar factura recibida
Search invoices in by id or supplier fiscal name: Buscar facturas recibidas por id o por nombre fiscal del proveedor
</i18n>

View File

@ -1,4 +1,6 @@
invoicein:
invoiceIn:
search: Search invoice
searchInfo: Search incoming invoices by ID or supplier fiscal name
serial: Serial
isBooked: Is booked
list:

View File

@ -1,4 +1,6 @@
invoicein:
invoiceIn:
search: Buscar factura recibida
searchInfo: Buscar facturas recibidas por ID o nombre fiscal del proveedor
serial: Serie
isBooked: Contabilizada
list:
@ -63,6 +65,7 @@ invoicein:
params:
search: Id o nombre proveedor
correctedFk: Rectificada
isBooked: Contabilizada
account: Cuenta contable
correctingFk: Rectificativa

View File

@ -62,6 +62,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity.
:subtitle="data.subtitle"
@on-fetch="setData"
data-key="invoiceOutData"
width="lg-width"
>
<template #menu="{ entity, menuRef }">
<InvoiceOutDescriptorMenu :invoice-out-data="entity" :menu-ref="menuRef" />

View File

@ -368,7 +368,7 @@ watchEffect(selectedRows);
<VnSelect
url="InvoiceOutSerials"
v-model="data.serial"
:label="t('invoicein.serial')"
:label="t('invoiceOutModule.serial')"
:options="invoiceOutSerialsOptions"
option-label="description"
option-value="code"

View File

@ -60,6 +60,7 @@ invoiceOutModule:
amount: Amount
company: Company
address: Address
serial: Serial
invoiceOutList:
tableVisibleColumns:
id: ID
@ -70,4 +71,4 @@ invoiceOutList:
invoiceOutSerial: Serial
ticket: Ticket
taxArea: Tax area
customsAgent: Custom Agent
customsAgent: Custom Agent

View File

@ -11,7 +11,7 @@ invoiceOut:
isActive: Activo
hasToInvoice: Debe facturar
hasVerifiedData: Datos verificados
workerName: Comercial
workerName: Comercial
card:
issued: Fecha emisión
customerCard: Ficha del cliente
@ -54,12 +54,13 @@ invoiceOut:
verifiedData: Datos comprobados
comercial: Comercial
errors:
downloadCsvFailed: Error al descargar CSV
downloadCsvFailed: Error al descargar CSV
invoiceOutModule:
customer: Cliente
amount: Importe
company: Empresa
address: Consignatario
serial: Serie
invoiceOutList:
tableVisibleColumns:
id: ID
@ -70,4 +71,4 @@ invoiceOutList:
invoiceOutSerial: Serial
ticket: Ticket
taxArea: Area
customsAgent: Agente de aduanas
customsAgent: Agente de aduanas

View File

@ -13,10 +13,9 @@ import axios from 'axios';
const route = useRoute();
const { t } = useI18n();
const itemTagsRef = ref(null);
const itemTagsRef = ref();
const tagOptions = ref([]);
const valueOptionsMap = ref(new Map());
const getSelectedTagValues = async (tag) => {
if (!tag.tagFk && tag.tag.isFree) return;
const filter = {
@ -59,10 +58,6 @@ const insertTag = (rows) => {
itemTagsRef.value.formData[itemTagsRef.value.formData.length - 1].priority =
getHighestPriority(rows);
};
const submitTags = async (data) => {
itemTagsRef.value.onSubmit(data);
};
</script>
<template>
@ -149,7 +144,7 @@ const submitTags = async (data) => {
v-model="row.value"
:label="t('itemTags.value')"
:is-clearable="false"
@keyup.enter.stop="submitTags(row)"
@keyup.enter.stop="(data) => itemTagsRef.onSubmit(data)"
/>
<VnInput
:label="t('itemBasicData.relevancy')"
@ -157,7 +152,7 @@ const submitTags = async (data) => {
v-model="row.priority"
:required="true"
:rules="validate('itemTag.priority')"
@keyup.enter.stop="submitTags(row)"
@keyup.enter.stop="(data) => itemTagsRef.onSubmit(data)"
/>
<div class="row justify-center" style="flex: 0">
<QIcon
@ -197,4 +192,5 @@ const submitTags = async (data) => {
<i18n>
es:
Tags can not be repeated: Las etiquetas no pueden repetirse
The value must be a number or a range of numbers: El valor debe ser un número o un rango de números
</i18n>

View File

@ -422,6 +422,7 @@ function handleOnDataSave({ CrudModelRef }) {
onDataSaved: handleOnDataSave,
}"
:disable-option="{ card: true }"
:has-sub-toolbar="false"
>
<template #header-selection="scope">
<QCheckbox v-model="scope.selected" />

View File

@ -12,6 +12,7 @@ import { QCheckbox } from 'quasar';
import { useArrayData } from 'composables/useArrayData';
import { useValidator } from 'src/composables/useValidator';
import axios from 'axios';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n();
@ -246,34 +247,14 @@ onMounted(async () => {
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('params.supplierFk')"
<VnSelectSupplier
v-model="params.supplierFk"
@update:model-value="searchFn()"
url="Suppliers"
option-value="id"
option-label="name"
:fields="['id', 'name', 'nickname']"
:filter-options="['id', 'name', 'nickname']"
sort-by="name ASC"
hide-selected
dense
outlined
rounded
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.name }}
</QItemLabel>
<QItemLabel caption>
{{ `#${scope.opt?.id} , ${scope.opt?.nickname}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
/>
</QItemSection>
</QItem>
<!-- Tags filter -->

View File

@ -272,11 +272,12 @@ const onDenyAccept = (_, responseData) => {
<template #column-achieved="{ row }">
<span>
<VnInput
ref="achievedRef"
type="number"
v-model.number="row.saleQuantity"
:disable="!row.itemFk || row.isOk != null"
@blur="changeQuantity(row)"
@keyup.enter="changeQuantity(row)"
@keyup.enter="$refs.achievedRef.vnInputRef.blur()"
dense
/>
</span>

View File

@ -48,14 +48,14 @@ const itemPackingTypesOptions = ref([]);
>
<template #form="{ data }">
<VnRow>
<VnInput v-model="data.code" :label="t('shared.code')" />
<VnInput v-model="data.name" :label="t('shared.name')" />
<VnInput v-model="data.code" :label="t('itemType.shared.code')" />
<VnInput v-model="data.name" :label="t('itemType.shared.name')" />
</VnRow>
<VnRow>
<VnSelect
url="Workers/search"
v-model="data.workerFk"
:label="t('shared.worker')"
:label="t('itemType.shared.worker')"
sort-by="nickname ASC"
:fields="['id', 'nickname']"
option-label="nickname"
@ -83,7 +83,7 @@ const itemPackingTypesOptions = ref([]);
>
<VnSelect
v-model="data.categoryFk"
:label="t('shared.category')"
:label="t('itemType.shared.category')"
:options="categoriesOptions"
option-value="id"
option-label="name"
@ -93,27 +93,30 @@ const itemPackingTypesOptions = ref([]);
<VnRow>
<VnSelect
v-model="data.temperatureFk"
:label="t('shared.temperature')"
:label="t('itemType.shared.temperature')"
:options="temperaturesOptions"
option-value="code"
option-label="name"
hide-selected
/>
<VnInput v-model="data.life" :label="t('shared.life')" />
<VnInput v-model="data.life" :label="t('itemType.summary.life')" />
</VnRow>
<VnRow>
<VnSelect
v-model="data.itemPackingTypeFk"
:label="t('shared.itemPackingType')"
:label="t('itemType.shared.itemPackingType')"
:options="itemPackingTypesOptions"
option-value="code"
option-label="description"
hide-selected
/>
<VnInput v-model="data.maxRefs" :label="t('shared.maxRefs')" />
<VnInput v-model="data.maxRefs" :label="t('itemType.shared.maxRefs')" />
</VnRow>
<VnRow>
<QCheckbox v-model="data.isFragile" :label="t('shared.fragile')" />
<QCheckbox
v-model="data.isFragile"
:label="t('itemType.shared.fragile')"
/>
</VnRow>
</template>
</FormModel>

View File

@ -50,15 +50,15 @@ const setData = (entity) => (data.value = useCardDescription(entity.code, entity
@on-fetch="setData"
>
<template #body="{ entity }">
<VnLv :label="t('shared.code')" :value="entity.code" />
<VnLv :label="t('shared.name')" :value="entity.name" />
<VnLv :label="t('shared.worker')">
<VnLv :label="t('itemType.shared.code')" :value="entity.code" />
<VnLv :label="t('itemType.shared.name')" :value="entity.name" />
<VnLv :label="t('itemType.shared.worker')">
<template #value>
<span class="link">{{ entity.worker?.firstName }}</span>
<WorkerDescriptorProxy :id="entity.worker?.id" />
</template>
</VnLv>
<VnLv :label="t('shared.category')" :value="entity.category?.name" />
<VnLv :label="t('itemType.shared.category')" :value="entity.category?.name" />
</template>
</CardDescriptor>
</template>

View File

@ -5,6 +5,9 @@ import VnTable from 'components/VnTable/VnTable.vue';
import FetchData from 'components/FetchData.vue';
import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue';
import VnSection from 'src/components/common/VnSection.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
const { t } = useI18n();
const tableRef = ref();
@ -60,20 +63,17 @@ const columns = computed(() => [
label: t('code'),
isTitle: true,
cardVisible: true,
create: true,
},
{
align: 'left',
name: 'name',
label: t('globals.name'),
cardVisible: true,
create: true,
},
{
align: 'left',
label: t('worker'),
name: 'workerFk',
create: true,
component: 'select',
attrs: {
url: 'Workers/search',
@ -100,7 +100,6 @@ const columns = computed(() => [
align: 'left',
name: 'categoryFk',
label: t('ItemCategory'),
create: true,
component: 'select',
attrs: {
options: itemCategoriesOptions.value,
@ -112,7 +111,6 @@ const columns = computed(() => [
align: 'left',
name: 'Temperature',
label: t('Temperature'),
create: true,
component: 'select',
attrs: {
options: temperatureOptions.value,
@ -180,6 +178,29 @@ const columns = computed(() => [
<WorkerDescriptorProxy :id="row.workerFk" />
</span>
</template>
<template #more-create-dialog="{ data }">
<VnInput v-model="data.code" :label="t('code')" data-cy="codeInput" />
<VnInput
v-model="data.name"
:label="t('globals.name')"
data-cy="nameInput"
/>
<VnSelectWorker v-model="data.workerFk" />
<VnSelect
:label="t('ItemCategory')"
v-model="data.categoryFk"
:options="itemCategoriesOptions"
hide-selected
data-cy="itemCategorySelect"
/>
<VnSelect
:label="t('Temperature')"
v-model="data.temperatureFk"
:options="temperatureOptions"
hide-selected
data-cy="temperatureSelect"
/>
</template>
</VnTable>
</template>
</VnSection>

View File

@ -76,13 +76,6 @@ itemTags:
searchbar:
label: Search item
info: Search by item id
itemType:
shared:
code: Code
name: Name
worker: Worker
category: Category
temperature: Temperature
item:
params:
daysOnward: Days onward

View File

@ -80,6 +80,9 @@ itemType:
worker: Trabajador
category: Reino
temperature: Temperatura
searchbar:
label: Buscar artículo
info: Buscar por id de artículo
params:
state: asfsdf
item:
@ -217,6 +220,6 @@ item:
item: 'Artículo'
achieved: 'Conseguido'
concept: 'Concepto'
state: 'Estado'
state: 'Estado'
search: 'Buscar artículo'
searchInfo: 'Puedes buscar por id'
searchInfo: 'Puedes buscar por id'

View File

@ -22,7 +22,6 @@ const catalogParams = {
};
const arrayData = useArrayData(dataKey, {
url: 'Orders/CatalogFilter',
limit: 50,
userParams: catalogParams,
});
const store = arrayData.store;
@ -66,7 +65,7 @@ function extractValueTags(items) {
.filter((k) => /^value\d+$/.test(k))
.map((v) => x[v])
.filter((v) => v)
.sort()
.sort(),
);
tagValue.value = resultValueTags;
}
@ -76,7 +75,7 @@ watch(
(val) => {
extractTags(val);
},
{ immediate: true }
{ immediate: true },
);
</script>

View File

@ -2,11 +2,12 @@
import { computed } from 'vue';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import VnTable from 'components/VnTable/VnTable.vue';
import VnSection from 'src/components/common/VnSection.vue';
const { t } = useI18n();
const router = useRouter();
const dataKey = 'AgencyList';
function navigate(id) {
router.push({ path: `/agency/${id}` });
}
@ -66,27 +67,29 @@ const columns = computed(() => [
]);
</script>
<template>
<VnSearchbar
:info="t('You can search by name')"
:label="t('Search agency')"
data-key="AgencyList"
:expr-builder="exprBuilder"
/>
<div class="list-container">
<div class="list">
<VnSection
:data-key
:columns="columns"
prefix="agency"
:right-filter="false"
:array-data-props="{
url: 'Agencies',
order: 'name',
exprBuilder,
}"
>
<template #body>
<VnTable
data-key="AgencyList"
url="Agencies"
order="name"
:data-key
:columns="columns"
is-editable="false"
:right-search="true"
:use-model="true"
redirect="agency"
redirect="route/agency"
default-mode="card"
/>
</div>
</div>
</template>
</VnSection>
</template>
<style lang="scss" scoped>
.list {
@ -104,8 +107,6 @@ const columns = computed(() => [
es:
isOwn: Tiene propietario
isAnyVolumeAllowed: Permite cualquier volumen
Search agency: Buscar agencia
You can search by name: Puedes buscar por nombre
en:
isOwn: Has owner
isAnyVolumeAllowed: Allows any volume

View File

@ -1,17 +1,7 @@
<script setup>
import AgencyDescriptor from 'pages/Route/Agency/Card/AgencyDescriptor.vue';
import VnCard from 'components/common/VnCard.vue';
import VnCardBeta from 'src/components/common/VnCardBeta.vue';
</script>
<template>
<VnCard
data-key="Agency"
base-url="Agencies"
:descriptor="AgencyDescriptor"
search-data-key="AgencyList"
:searchbar-props="{
url: 'Agencies',
label: 'agency.searchBar.label',
info: 'agency.searchBar.info',
}"
/>
<VnCardBeta data-key="Agency" base-url="Agencies" :descriptor="AgencyDescriptor" />
</template>

View File

@ -3,14 +3,17 @@ import axios from 'axios';
import { getAgencies } from 'src/pages/Route/Agency/composables/getAgencies';
vi.mock('axios');
const response = { data: [{ agencyModeFk: 'Agency1' }, { agencyModeFk: 'Agency2' }] };
axios.get.mockResolvedValue(response);
describe('getAgencies', () => {
afterEach(() => {
vi.clearAllMocks();
});
const generateParams = (formData) => ({
const generateParams = (formData, filter = {}) => ({
params: {
filter: JSON.stringify(filter),
warehouseFk: formData.warehouseId,
addressFk: formData.addressId,
landed: formData.landed,
@ -23,10 +26,15 @@ describe('getAgencies', () => {
addressId: '456',
landed: 'true',
};
const filter = {
fields: ['nickname', 'street', 'city', 'id'],
where: { isActive: true },
order: 'nickname ASC',
};
await getAgencies(formData);
await getAgencies(formData, null, filter);
expect(axios.get).toHaveBeenCalledWith('Agencies/getAgenciesWithWarehouse', generateParams(formData));
expect(axios.get).toHaveBeenCalledWith('Agencies/getAgenciesWithWarehouse', generateParams(formData, filter));
});
it('should not call API when formData is missing required landed field', async () => {
@ -52,4 +60,23 @@ describe('getAgencies', () => {
expect(axios.get).not.toHaveBeenCalled();
});
it('should return options and agency when default agency is found', async () => {
const formData = { warehouseId: '123', addressId: '456', landed: 'true' };
const client = { defaultAddress: { agencyModeFk: 'Agency1' } };
const { options, agency } = await getAgencies(formData, client);
expect(options).toEqual(response.data);
expect(agency).toEqual(response.data[0]);
});
it('should return options and agency when client is not provided', async () => {
const formData = { warehouseId: '123', addressId: '456', landed: 'true' };
const { options, agency } = await getAgencies(formData);
expect(options).toEqual(response.data);
expect(agency).toBeNull();
});
});

View File

@ -1,12 +1,26 @@
import axios from 'axios';
import agency from 'src/router/modules/agency';
export async function getAgencies(formData) {
export async function getAgencies(formData, client, _filter = {}) {
if (!formData.warehouseId || !formData.addressId || !formData.landed) return;
const filter = {
..._filter
};
let defaultAgency = null;
let params = {
filter: JSON.stringify(filter),
warehouseFk: formData.warehouseId,
addressFk: formData.addressId,
landed: formData.landed,
};
return await axios.get('Agencies/getAgenciesWithWarehouse', { params });
const { data } = await axios.get('Agencies/getAgenciesWithWarehouse', { params });
if(data && client) {
defaultAgency = data.find((agency) => agency.agencyModeFk === client.defaultAddress.agencyModeFk );
};
return {options: data, agency: defaultAgency}
}

View File

@ -1,4 +1,6 @@
agency:
search: Search agency
searchInfo: You can search by name
isOwn: Own
isAnyVolumeAllowed: Any volume allowed
notification:

View File

@ -1,4 +1,6 @@
agency:
search: Buscar agencia
searchInfo: Puedes buscar por nombre
isOwn: Propio
isAnyVolumeAllowed: Cualquier volumen
removeItem: Agencia eliminada correctamente

View File

@ -6,6 +6,7 @@ import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
import VnSelect from 'components/common/VnSelect.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n();
const props = defineProps({
@ -19,7 +20,6 @@ const emit = defineEmits(['search']);
const agencyList = ref([]);
const agencyAgreementList = ref([]);
const supplierList = ref([]);
const exprBuilder = (param, value) => {
switch (param) {
@ -58,14 +58,6 @@ const exprBuilder = (param, value) => {
@on-fetch="(data) => (agencyAgreementList = data)"
auto-load
/>
<FetchData
url="Suppliers"
:filter="{ fields: ['name'] }"
sort-by="name ASC"
limit="30"
@on-fetch="(data) => (supplierList = data)"
auto-load
/>
<VnFilterPanel
:data-key="props.dataKey"
:expr-builder="exprBuilder"
@ -123,14 +115,12 @@ const exprBuilder = (param, value) => {
/>
</QItemSection>
</QItem>
<QItem class="q-my-sm" v-if="supplierList">
<QItem class="q-my-sm">
<QItemSection>
<VnSelect
<VnSelectSupplier
:label="t('Autonomous')"
v-model="params.supplierFk"
:options="supplierList"
option-value="name"
option-label="name"
hide-selected
dense
outlined
rounded

View File

@ -1,19 +1,12 @@
<script setup>
import VnCard from 'components/common/VnCard.vue';
import RouteDescriptor from 'pages/Route/Card/RouteDescriptor.vue';
import RouteFilter from './RouteFilter.vue';
import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue';
import VnCardBeta from 'src/components/common/VnCardBeta.vue';
</script>
<template>
<VnCard
<VnCardBeta
data-key="Route"
base-url="Routes"
custom-url="Routes/filter"
:descriptor="RouteDescriptor"
:filter-panel="RouteFilter"
search-data-key="RouteList"
>
<template #searchbar>
<RouteSearchbar />
</template>
</VnCard>
/>
</template>

View File

@ -76,6 +76,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.code, entity
:subtitle="data.subtitle"
data-key="routeData"
@on-fetch="setData"
width="lg-width"
>
<template #body="{ entity }">
<VnLv :label="t('Date')" :value="toDate(entity?.dated)" />

View File

@ -6,29 +6,18 @@ import FormModel from 'components/FormModel.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue';
import VnInputTime from 'components/common/VnInputTime.vue';
import VnSelect from 'components/common/VnSelect.vue';
import FetchData from 'components/FetchData.vue';
import { ref } from 'vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n();
const router = useRouter();
const route = useRoute();
const supplierList = ref([]);
const filter = { include: [{ relation: 'supplier' }] };
const onSave = (data, response) => {
router.push({ name: 'RoadmapSummary', params: { id: response?.id } });
};
</script>
<template>
<FetchData
url="Suppliers"
auto-load
:filter="{ fields: ['id', 'nickname'] }"
sort-by="nickname"
limit="30"
@on-fetch="(data) => (supplierList = data)"
/>
<FormModel
:url="`Roadmaps/${route.params?.id}`"
observe-form-changes
@ -56,27 +45,14 @@ const onSave = (data, response) => {
/>
</VnRow>
<VnRow>
<VnSelect
<VnSelectSupplier
:label="t('Carrier')"
v-model="data.supplierFk"
:options="supplierList"
option-value="id"
option-label="nickname"
emit-value
map-options
use-input
:input-debounce="0"
>
<template #option="{ itemProps, opt }">
<QItem v-bind="itemProps">
<QItemSection>
<QItemLabel
>{{ opt.id }} - {{ opt.nickname }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
/>
<VnInput
v-model="data.price"
:label="t('Price')"

View File

@ -1,19 +1,7 @@
<script setup>
import VnCard from 'components/common/VnCard.vue';
import VnCardBeta from 'components/common/VnCardBeta.vue';
import RoadmapDescriptor from 'pages/Route/Roadmap/RoadmapDescriptor.vue';
import RoadmapFilter from 'pages/Route/Roadmap/RoadmapFilter.vue';
</script>
<template>
<VnCard
data-key="Roadmap"
base-url="Roadmaps"
:descriptor="RoadmapDescriptor"
:filter-panel="RoadmapFilter"
search-data-key="RoadmapList"
:searchbar-props="{
url: 'Roadmaps',
label: 'Search roadmap',
info: 'You can search by roadmap id or customer name',
}"
/>
<VnCardBeta data-key="Roadmap" base-url="Roadmaps" :descriptor="RoadmapDescriptor" />
</template>

View File

@ -3,9 +3,9 @@ import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
import VnSelect from 'components/common/VnSelect.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n();
const props = defineProps({
@ -16,39 +16,12 @@ const props = defineProps({
});
const emit = defineEmits(['search']);
const supplierList = ref([]);
const exprBuilder = (param, value) => {
switch (param) {
case 'tractorPlate':
case 'trailerPlate':
case 'driverName':
case 'phone':
return { [param]: { like: `%${value}%` } };
case 'supplierFk':
case 'price':
return { [param]: value };
case 'from':
return { etd: { gte: value } };
case 'to':
return { etd: { lte: value } };
}
};
</script>
<template>
<FetchData
url="Suppliers"
:filter="{ fields: ['id', 'nickname'] }"
sort-by="nickname"
limit="30"
@on-fetch="(data) => (supplierList = data)"
auto-load
/>
<VnFilterPanel
:data-key="props.dataKey"
:search-button="true"
:expr-builder="exprBuilder"
@search="emit('search')"
>
<template #tags="{ tag, formatFn }">
@ -88,14 +61,11 @@ const exprBuilder = (param, value) => {
/>
</QItemSection>
</QItem>
<QItem v-if="supplierList" class="q-my-sm">
<QItem class="q-my-sm">
<QItemSection>
<VnSelect
<VnSelectSupplier
:label="t('Carrier')"
v-model="params.supplierFk"
:options="supplierList"
option-value="id"
option-label="nickname"
dense
outlined
rounded
@ -103,17 +73,7 @@ const exprBuilder = (param, value) => {
map-options
use-input
:input-debounce="0"
>
<template #option="{ itemProps, opt }">
<QItem v-bind="itemProps">
<QItemSection>
<QItemLabel
>{{ opt.id }} - {{ opt.nickname }}</QItemLabel
>
</QItemSection>
</QItem>
</template>
</VnSelect>
/>
</QItemSection>
</QItem>
<QItem class="q-my-sm">

View File

@ -3,17 +3,16 @@ import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import { toHour } from 'src/filters';
import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue';
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
import VnTable from 'components/VnTable/VnTable.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import VnSection from 'src/components/common/VnSection.vue';
const { t } = useI18n();
const { viewSummary } = useSummaryDialog();
const tableRef = ref([]);
const dataKey = 'RouteList';
const routeFilter = {
include: [
{
@ -110,32 +109,39 @@ const columns = computed(() => [
]);
</script>
<template>
<RouteSearchbar />
<RightMenu>
<template #right-panel>
<RouteFilter data-key="RouteList" />
</template>
</RightMenu>
<VnTable
data-key="RouteList"
url="Routes/filter"
<VnSection
:data-key
:columns="columns"
:right-search="false"
:filter="routeFilter"
redirect="route"
:create="{
urlCreate: 'Routes',
title: t('route.createRoute'),
onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: {},
prefix="route"
:array-data-props="{
url: 'Routes/filter',
userFilter: routeFilter,
}"
table-height="85vh"
>
<template #column-workerFk="{ row }">
<span class="link" @click.stop>
{{ row?.workerUserName }}
<WorkerDescriptorProxy :id="row?.workerFk" v-if="row?.workerFk" />
</span>
<template #advanced-menu>
<RouteFilter :data-key />
</template>
</VnTable>
<template #body>
<VnTable
:data-key
:columns="columns"
:right-search="false"
redirect="route"
:create="{
urlCreate: 'Routes',
title: t('route.createRoute'),
onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: {},
}"
table-height="85vh"
>
<template #column-workerFk="{ row }">
<span class="link" @click.stop>
{{ row?.workerUserName }}
<WorkerDescriptorProxy :id="row?.workerFk" v-if="row?.workerFk" />
</span>
</template>
</VnTable>
</template>
</VnSection>
</template>

View File

@ -15,11 +15,14 @@ import RoadmapSummary from 'pages/Route/Roadmap/RoadmapSummary.vue';
import VnConfirm from 'components/ui/VnConfirm.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInputTime from 'src/components/common/VnInputTime.vue';
import VnSection from 'src/components/common/VnSection.vue';
import RoadmapFilter from './Roadmap/RoadmapFilter.vue';
const { viewSummary } = useSummaryDialog();
const { t } = useI18n();
const quasar = useQuasar();
const selectedRows = ref([]);
const dataKey = 'RoadmapList';
const columns = computed(() => [
{
align: 'left',
@ -128,14 +131,28 @@ function confirmRemove() {
})
.onOk(() => tableRef.value++);
}
function exprBuilder(param, value) {
switch (param) {
case 'search':
return /^\d+$/.test(value) ? { id: value } : { name: { like: `%${value}%` } };
case 'tractorPlate':
case 'trailerPlate':
case 'driverName':
case 'phone':
return { [param]: { like: `%${value}%` } };
case 'supplierFk':
case 'price':
return { [param]: value };
case 'from':
return { etd: { gte: value } };
case 'to':
return { etd: { lte: value } };
}
}
</script>
<template>
<VnSearchbar
data-key="RoadmapList"
:label="t('Search roadmaps')"
:info="t('You can search by roadmap reference')"
/>
<QDialog v-model="isCloneDialogOpen">
<QCard style="min-width: 350px">
<QCardSection>
@ -177,39 +194,53 @@ function confirmRemove() {
</QBtn>
</template>
</VnSubToolbar>
<VnTable
ref="tableRef"
data-key="RoadmapList"
url="roadmaps"
<VnSection
:data-key
:columns="columns"
:right-search="true"
:use-model="true"
default-mode="table"
v-model:selected="selectedRows"
table-height="85vh"
:table="{
selection: 'multiple',
prefix="route.roadmap"
:array-data-props="{
url: 'roadmaps',
exprBuilder,
}"
redirect="route/roadmap"
:create="{
urlCreate: 'Roadmaps',
title: t('Create routemap'),
onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: {},
}"
:disable-option="{ card: true }"
>
<template #column-etd="{ row }">
{{ toDateHourMin(row.etd) }}
<template #advanced-menu>
<RoadmapFilter :dataKey />
</template>
<template #column-supplierFk="{ row }">
{{ row.supplierFk }}
<template #body>
<VnTable
ref="tableRef"
:data-key
:columns="columns"
:right-search="false"
:use-model="true"
default-mode="table"
v-model:selected="selectedRows"
table-height="85vh"
:table="{
selection: 'multiple',
}"
redirect="route/roadmap"
:create="{
urlCreate: 'Roadmaps',
title: t('Create routemap'),
onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: {},
}"
:disable-option="{ card: true }"
>
<template #column-etd="{ row }">
{{ toDateHourMin(row.etd) }}
</template>
<template #column-supplierFk="{ row }">
{{ row.supplierFk }}
</template>
<template #more-create-dialog="{ data }">
<VnInputDate v-model="data.etd" />
<VnInputTime v-model="data.etd" />
</template>
</VnTable>
</template>
<template #more-create-dialog="{ data }">
<VnInputDate v-model="data.etd" />
<VnInputTime v-model="data.etd" />
</template>
</VnTable>
</VnSection>
</template>
<style lang="scss" scoped>

View File

@ -1,4 +1,7 @@
route:
roadmap:
search: Search roadmap
searchInfo: You can search by roadmap reference
params:
etd: ETD
tractorPlate: Plate
@ -36,6 +39,8 @@ route:
Summary: Summary
Route is closed: Route is closed
Route is not served: Route is not served
search: Search route
searchInfo: You can search by route reference
cmr:
list:
results: results
@ -49,4 +54,4 @@ route:
clientFk: Client id
shipped: Preparation date
viewCmr: View CMR
downloadCmrs: Download CMRs
downloadCmrs: Download CMRs

View File

@ -1,4 +1,7 @@
route:
roadmap:
search: Buscar troncales
searchInfo: Puedes buscar por referencia del troncal
params:
agencyModeName: Agencia Ruta
agencyAgreement: Agencia Acuerdo
@ -37,6 +40,8 @@ route:
Summary: Resumen
Route is closed: La ruta está cerrada
Route is not served: La ruta no está servida
search: Buscar rutas
searchInfo: Puedes buscar por referencia de la ruta
cmr:
list:
results: resultados
@ -50,4 +55,4 @@ route:
clientFk: Id cliente
shipped: Fecha preparación
viewCmr: Ver CMR
downloadCmrs: Descargar CMRs
downloadCmrs: Descargar CMRs

View File

@ -0,0 +1,191 @@
<script setup>
import { computed, onBeforeMount, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import tableFooter from 'src/components/VnTable/filters/tableFooter';
import { dashIfEmpty, toCurrency, toDateHourMin } from 'src/filters';
import { useState } from 'src/composables/useState';
import { useStateStore } from 'src/stores/useStateStore';
import VnTable from 'src/components/VnTable/VnTable.vue';
import InvoiceInDescriptorProxy from 'src/pages/InvoiceIn/Card/InvoiceInDescriptorProxy.vue';
import SupplierBalanceFilter from './SupplierBalanceFilter.vue';
import { onMounted } from 'vue';
import FetchData from 'src/components/FetchData.vue';
const { t } = useI18n();
const route = useRoute();
const state = useState();
const stateStore = useStateStore();
const user = state.getUser();
const tableRef = ref();
const companyId = ref();
const companyUser = ref(user.value.companyFk);
const balances = ref([]);
const userParams = ref({
supplierId: route.params.id,
companyId: companyId.value ?? companyUser.value,
isBooked: false,
});
const columns = computed(() => [
{
align: 'left',
name: 'dated',
label: t('Creation date'),
format: ({ dated }) => toDateHourMin(dated),
cardVisible: true,
},
{
align: 'left',
name: 'sref',
label: t('Reference'),
isTitle: true,
class: 'extend',
format: ({ sref }) => dashIfEmpty(sref),
},
{
align: 'left',
name: 'bank',
label: t('Bank'),
cardVisible: true,
},
{
align: 'left',
name: 'invoiceEuros',
label: t('Debit'),
format: ({ invoiceEuros }) => toCurrency(invoiceEuros),
isId: true,
},
{
align: 'left',
name: 'paymentEuros',
label: t('Havings'),
format: ({ paymentEuros }) => toCurrency(paymentEuros),
cardVisible: true,
},
{
align: 'left',
name: 'euroBalance',
label: t('Balance'),
format: ({ euroBalance }) => toCurrency(euroBalance),
cardVisible: true,
},
{
align: 'left',
name: 'isBooked',
label: t('Conciliated'),
cardVisible: true,
},
]);
onBeforeMount(() => {
stateStore.rightDrawer = true;
companyId.value = companyUser.value;
});
onMounted(async () => {
Object.assign(userParams, {
supplierId: route.params.id,
companyId: companyId.value ?? companyUser.value,
isConciliated: false,
});
});
function setFooter(data) {
const initialFooter = {
invoiceEuros: 0,
paymentEuros: 0,
euroBalance: 0,
};
tableRef.value.footer = tableFooter(initialFooter, data);
}
async function onFetch(data) {
setFooter(data);
return;
}
function round(value) {
return Math.round(value * 100) / 100;
}
const onFetchCurrencies = ([currency]) => {
userParams.value.currencyFk = currency?.id;
};
</script>
<template>
<QDrawer side="right" :width="265" v-model="stateStore.rightDrawer">
<SupplierBalanceFilter data-key="SupplierBalance" />
</QDrawer>
<FetchData
url="Currencies"
:filter="{ fields: ['id', 'code'], where: { code: 'EUR' } }"
sort-by="code"
@on-fetch="onFetchCurrencies"
auto-load
/>
<VnTable
v-if="userParams.currencyFk"
ref="tableRef"
data-key="SupplierBalance"
url="Suppliers/receipts"
search-url="balance"
:user-params="userParams"
:columns="columns"
:right-search="false"
:is-editable="false"
:column-search="false"
@on-fetch="onFetch"
:disable-option="{ card: true }"
:footer="true"
:order="['dated ASC']"
data-cy="supplierBalanceTable"
auto-load
:map-key="false"
>
<template #column-balance="{ rowIndex }">
{{ toCurrency(balances[rowIndex]?.balance) }}
</template>
<template #column-sref="{ row }">
<span class="link" v-if="row.statementType === 'invoiceIn'">
{{ dashIfEmpty(row.sref) }}
<InvoiceInDescriptorProxy :id="row.id" />
</span>
<span v-else> {{ dashIfEmpty(row.sref) }}</span>
</template>
<template #column-footer-invoiceEuros>
<span>
{{ toCurrency(round(tableRef.footer.invoiceEuros)) }}
</span>
</template>
<template #column-footer-paymentEuros>
<span>
{{ toCurrency(round(tableRef.footer.paymentEuros)) }}
</span>
</template>
<template #column-footer-euroBalance>
<span>
{{ toCurrency(round(tableRef.footer.euroBalance)) }}
</span>
</template>
</VnTable>
</template>
<i18n>
es:
Company: Empresa
Total by company: Total por empresa
Date: Fecha
Creation date: Fecha de creación
Reference: Referencia
Bank: Caja
Debit: Debe
Havings: Haber
Balance: Balance
Conciliated: Conciliado
</i18n>

View File

@ -0,0 +1,121 @@
<script setup>
import { useI18n } from 'vue-i18n';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
const { t } = useI18n();
defineProps({
dataKey: {
type: String,
required: true,
},
});
</script>
<template>
<VnFilterPanel
:data-key="dataKey"
:search-button="true"
:redirect="false"
:unremovable-params="['supplierId', 'companyId']"
>
<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, searchFn }">
<QItem>
<QItemSection>
<VnInputDate
:label="t('params.from')"
v-model="params.from"
@update:model-value="searchFn()"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('params.bankFk')"
v-model="params.bankFk"
url="Accountings"
option-label="bank"
:include="{ relation: 'accountingType' }"
sort-by="id"
dense
outlined
rounded
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt.id }} {{ scope.opt.bank }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('params.currencyFk')"
url="Currencies"
:filter="{ fields: ['id', 'name'] }"
order="code"
v-model="params.currencyFk"
option-value="id"
option-label="name"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QCheckbox
v-model="params.isConciliated"
:label="t('params.isConciliated')"
/></QItemSection>
</QItem>
</template>
</VnFilterPanel>
</template>
<i18n>
en:
params:
search: General search
supplierId: Supplier
categoryId: Category
from: From
to: To
isConciliated: Is conciliated
currencyFk: Currency
bankFk: Bank
companyId: Comapany
isBooked: Is booked
es:
params:
supplierId: Proveedor
isConciliated: Conciliado
currencyFk: Moneda
New payment: Añadir pago
Date: Fecha
from: Desde
to: Hasta
companyId: Empresa
isBooked: Contabilizado
bankFk: Caja
Amount: Importe
Reference: Referencia
Cash: Efectivo
</i18n>

View File

@ -36,7 +36,7 @@ const companySizes = [
/>
<VnSelectWorker
v-model="data.workerFk"
has-info="Responsible for approving invoices"
info="Responsible for approving invoices"
:rules="validate('supplier.workerFk')"
/>
<VnSelect

View File

@ -94,7 +94,7 @@ function handleLocation(data, location) {
<VnInput
v-model="data.name"
:label="t('supplier.fiscalData.name')"
uppercase="true"
:uppercase="true"
clearable
/>
<VnInput

View File

@ -16,6 +16,10 @@ const $props = defineProps({
required: false,
default: null,
},
summary: {
type: Object,
default: null,
},
});
const route = useRoute();
@ -102,6 +106,10 @@ const data = ref(useCardDescription());
function ticketFilter(ticket) {
return JSON.stringify({ clientFk: ticket.clientFk });
}
const setData = (entity) => {
data.value = useCardDescription(entity.ref, entity.id);
};
</script>
<template>
@ -111,7 +119,10 @@ function ticketFilter(ticket) {
:filter="filter"
:title="data.title"
:subtitle="data.subtitle"
@on-fetch="setData"
:summary="$props.summary"
data-key="ticketData"
width="lg-width"
>
<template #menu="{ entity }">
<TicketDescriptorMenu :ticket="entity" />
@ -157,7 +168,7 @@ function ticketFilter(ticket) {
<VnLv :label="t('globals.alias')" :value="entity.nickname" />
</template>
<template #icons="{ entity }">
<QCardActions class="q-gutter-x-xs">
<QCardActions class="q-gutter-x-md">
<QIcon
v-if="entity.client.isActive == false"
name="vn:disabled"
@ -201,7 +212,7 @@ function ticketFilter(ticket) {
</QCardActions>
</template>
<template #actions="{ entity }">
<QCardActions>
<QCardActions class="flex justify-center" style="padding-inline: 0">
<QBtn
size="md"
icon="vn:client"

View File

@ -69,14 +69,16 @@ const onAddressSelected = (addressId) => {
}
const fetchClient = async () => {
const { data } = await getClient(client.value)
const [retrievedClient] = data;
const response = await getClient(client.value)
if (!response) return;
const [retrievedClient] = response.data;
selectedClient.value = retrievedClient;
};
const fetchAddresses = async () => {
const { data } = await getAddresses(client.value);
addressesOptions.value = data;
const response = await getAddresses(client.value);
if (!response) return;
addressesOptions.value = response.data;
const { defaultAddress } = selectedClient.value;
address.value = defaultAddress.id;

View File

@ -6,7 +6,6 @@ import { useI18n } from 'vue-i18n';
import axios from 'axios';
import { dashIfEmpty, toDate, toCurrency } from 'src/filters';
import CardSummary from 'components/ui/CardSummary.vue';
import FetchData from 'components/FetchData.vue';
import FetchedTags from 'components/ui/FetchedTags.vue';
import InvoiceOutDescriptorProxy from 'pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue';
import VnLv from 'src/components/ui/VnLv.vue';
@ -29,15 +28,14 @@ const { t } = useI18n();
const $props = defineProps({
id: {
type: Number,
required: false,
default: null,
},
});
const entityId = computed(() => $props.id || route.params.id);
const summaryRef = ref();
const ticket = computed(() => summaryRef.value?.entity);
const summary = ref();
const ticket = computed(() => summary.value?.entity);
const editableStates = ref([]);
const ticketUrl = ref();
const grafanaUrl = 'https://grafana.verdnatura.es';
@ -48,16 +46,6 @@ onMounted(async () => {
ticketUrl.value = (await getUrl('ticket/')) + entityId.value + '/';
});
function formattedAddress() {
if (!ticket.value) return '';
const address = ticket.value.address;
const postcode = address.postalCode;
const province = address.province ? `(${address.province.name})` : '';
return `${address.street} - ${postcode} - ${address.city} ${province}`;
}
function isEditable() {
try {
return !ticket.value.ticketState?.state?.alertLevel;
@ -76,26 +64,27 @@ async function changeState(value) {
};
await axios.post(`Tickets/state`, formData);
notify('globals.dataSaved', 'positive');
summaryRef.value?.fetch();
summary.value?.fetch();
descriptorData.fetch({});
}
function toTicketUrl(section) {
return '#/ticket/' + entityId.value + '/' + section;
}
onMounted(async () => {
const filter = { fields: ['code', 'name', 'id', 'alertLevel'] };
const params = { filter: JSON.stringify(filter) };
editableStates.value = (await axios.get('States/editableStates', { params }))?.data;
});
</script>
<template>
<FetchData
url="States/editableStates"
:filter="{ fields: ['code', 'name', 'id', 'alertLevel'], order: 'name ASC' }"
auto-load
@on-fetch="(data) => (editableStates = data)"
/>
<CardSummary
ref="summaryRef"
ref="summary"
:url="`Tickets/${entityId}/summary`"
data-key="TicketSummary"
v-bind="$attrs.width"
>
<template #header-left>
<VnToSummary
@ -207,7 +196,7 @@ function toTicketUrl(section) {
</VnLv>
<VnLv :label="t('globals.weight')" :value="dashIfEmpty(entity.weight)" />
</QCard>
<QCard class="vn-one" style="flex: 2 1">
<QCard class="vn-one">
<VnTitle
:url="toTicketUrl('basic-data')"
:text="t('globals.summary.basicData')"
@ -244,7 +233,11 @@ function toTicketUrl(section) {
</VnLv>
<VnLv
:label="t('ticket.summary.consignee')"
:value="formattedAddress()"
:value="`${entity.address?.nickname} #${entity.address?.id}`"
/>
<VnLv
:label="t('ticket.summary.consigneeStreet')"
:value="entity.address?.street"
/>
</QCard>
<QCard class="vn-one" v-if="entity.notes.length">
@ -454,7 +447,7 @@ function toTicketUrl(section) {
toCurrency(
props.row.quantity *
props.row.price *
((100 - props.row.discount) / 100)
((100 - props.row.discount) / 100),
)
}}
</QTd>

Some files were not shown because too many files have changed in this diff Show More