Merge pull request '#4466 invoiceInCorrection' (!176) from 4666-invoiceInCorrection into dev
gitea/salix-front/pipeline/head This commit looks good Details

Reviewed-on: #176
Reviewed-by: Alex Moreno <alexm@verdnatura.es>
Reviewed-by: JAVIER SEGARRA MARTINEZ <jsegarra@verdnatura.es>
This commit is contained in:
Jorge Penadés 2024-02-09 07:46:15 +00:00
commit 18a84f2d61
25 changed files with 985 additions and 563 deletions

View File

@ -196,7 +196,6 @@ function getChanges() {
const creates = []; const creates = [];
const pk = $props.primaryKey; const pk = $props.primaryKey;
for (const [i, row] of formData.value.entries()) { for (const [i, row] of formData.value.entries()) {
if (!row[pk]) { if (!row[pk]) {
creates.push(row); creates.push(row);

View File

@ -7,7 +7,7 @@ import { date } from 'quasar';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { toRelativeDate } from 'src/filters'; import { toRelativeDate } from 'src/filters';
import { useColor } from 'src/composables/useColor'; import { useColor } from 'src/composables/useColor';
import { useFirstUpper } from 'src/composables/useFirstUpper'; import { useCapitalize } from 'src/composables/useCapitalize';
import { useValidator } from 'src/composables/useValidator'; import { useValidator } from 'src/composables/useValidator';
import VnAvatar from '../ui/VnAvatar.vue'; import VnAvatar from '../ui/VnAvatar.vue';
import VnJsonValue from '../common/VnJsonValue.vue'; import VnJsonValue from '../common/VnJsonValue.vue';
@ -140,7 +140,7 @@ function parseProps(propNames, locale, vals, olds) {
if (prop.endsWith('$')) continue; if (prop.endsWith('$')) continue;
props.push({ props.push({
name: prop, name: prop,
nameI18n: useFirstUpper(locale.columns?.[prop]) || prop, nameI18n: useCapitalize(locale.columns?.[prop]) || prop,
val: getVal(vals, prop), val: getVal(vals, prop),
old: olds && getVal(olds, prop), old: olds && getVal(olds, prop),
}); });
@ -202,7 +202,7 @@ function getLogTree(data) {
userLog.logs.push( userLog.logs.push(
(modelLog = { (modelLog = {
model: log.changedModel, model: log.changedModel,
modelI18n: useFirstUpper(locale.name) || log.changedModel, modelI18n: useCapitalize(locale.name) || log.changedModel,
id: log.changedModelId, id: log.changedModelId,
showValue: log.changedModelValue, showValue: log.changedModelValue,
logs: [], logs: [],
@ -395,7 +395,7 @@ setLogTree();
(data) => (data) =>
(actions = data.map((item) => { (actions = data.map((item) => {
return { return {
locale: useFirstUpper(validations[item.changedModel].locale.name), locale: useCapitalize(validations[item.changedModel].locale.name),
value: item.changedModel, value: item.changedModel,
}; };
})) }))
@ -409,7 +409,7 @@ setLogTree();
> >
<QItem class="origin-info items-center q-my-md" v-if="logTree.length > 1"> <QItem class="origin-info items-center q-my-md" v-if="logTree.length > 1">
<h6 class="origin-id text-grey"> <h6 class="origin-id text-grey">
{{ useFirstUpper(validations[props.model].locale.name) }} {{ useCapitalize(validations[props.model].locale.name) }}
#{{ originLog.originFk }} #{{ originLog.originFk }}
</h6> </h6>
<div class="line bg-grey"></div> <div class="line bg-grey"></div>

View File

@ -1,3 +1,3 @@
export function useFirstUpper(str) { export function useCapitalize(str) {
return str && str.charAt(0).toUpperCase() + str.substr(1); return str && str.charAt(0).toUpperCase() + str.substr(1);
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/css/fonts/icon.eot Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 162 KiB

BIN
src/css/fonts/icon.ttf Normal file

Binary file not shown.

BIN
src/css/fonts/icon.woff Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -1,19 +1,18 @@
@font-face { @font-face {
font-family: 'icomoon'; font-family: 'icon';
src: url('fonts/icomoon.eot?g6kvgn'); src: url('fonts/icon.eot?7zbcv0');
src: url('fonts/icomoon.eot?g6kvgn#iefix') format('embedded-opentype'), src: url('fonts/icon.eot?7zbcv0#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?g6kvgn') format('truetype'), url('fonts/icon.ttf?7zbcv0') format('truetype'),
url('fonts/icomoon.woff?g6kvgn') format('woff'), url('fonts/icon.woff?7zbcv0') format('woff'),
url('fonts/icomoon.svg?g6kvgn#icomoon') format('svg'); url('fonts/icon.svg?7zbcv0#icon') format('svg');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: block; font-display: block;
} }
[class^='icon-'], [class^="icon-"], [class*=" icon-"] {
[class*=' icon-'] {
/* use !important to prevent issues with browser extensions that change fonts */ /* use !important to prevent issues with browser extensions that change fonts */
font-family: 'icomoon' !important; font-family: 'icon' !important;
speak: never; speak: never;
font-style: normal; font-style: normal;
font-weight: normal; font-weight: normal;
@ -26,387 +25,363 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-frozen:before {
content: '\e900';
}
.icon-Person:before {
content: '\e901';
}
.icon-handmadeArtificial:before {
content: '\e902';
}
.icon-fruit:before {
content: '\e903';
}
.icon-funeral:before {
content: '\e904';
}
.icon-noPayMethod:before {
content: '\e905';
}
.icon-preserved:before {
content: '\e906';
}
.icon-greenery:before {
content: '\e907';
}
.icon-plant:before {
content: '\e908';
}
.icon-handmade:before {
content: '\e909';
}
.icon-accessory:before {
content: '\e90a';
}
.icon-artificial:before {
content: '\e90b';
}
.icon-flower:before {
content: '\e90c';
}
.icon-fixedPrice:before {
content: '\e90d';
}
.icon-addperson:before {
content: '\e90e';
}
.icon-supplierfalse:before {
content: '\e90f';
}
.icon-invoice-out:before {
content: '\e910';
}
.icon-invoice-in:before {
content: '\e911';
}
.icon-invoice-in-create:before {
content: '\e912';
}
.icon-basketadd:before {
content: '\e913';
}
.icon-basket:before {
content: '\e914';
}
.icon-uniE915:before {
content: '\e915';
}
.icon-uniE916:before {
content: '\e916';
}
.icon-uniE917:before {
content: '\e917';
}
.icon-uniE918:before {
content: '\e918';
}
.icon-uniE919:before {
content: '\e919';
}
.icon-uniE91A:before {
content: '\e91a';
}
.icon-isTooLittle:before {
content: '\e91b';
}
.icon-deliveryprices:before {
content: '\e91c';
}
.icon-onlinepayment:before {
content: '\e91d';
}
.icon-risk:before {
content: '\e91e';
}
.icon-noweb:before {
content: '\e91f';
}
.icon-no036:before {
content: '\e920';
}
.icon-disabled:before {
content: '\e921';
}
.icon-treatments:before {
content: '\e922';
}
.icon-invoice:before {
content: '\e923';
}
.icon-photo:before {
content: '\e924';
}
.icon-supplier:before {
content: '\e925';
}
.icon-languaje:before {
content: '\e926';
}
.icon-credit:before {
content: '\e927';
}
.icon-client:before {
content: '\e928';
}
.icon-shipment-01:before {
content: '\e929';
}
.icon-account:before {
content: '\e92a';
}
.icon-inventory:before {
content: '\e92b';
}
.icon-unavailable:before {
content: '\e92c';
}
.icon-wiki:before {
content: '\e92d';
}
.icon-attach:before {
content: '\e92e';
}
.icon-exit:before {
content: '\e92f';
}
.icon-anonymous:before {
content: '\e930';
}
.icon-net:before {
content: '\e931';
}
.icon-buyrequest:before {
content: '\e932';
}
.icon-thermometer:before {
content: '\e933';
}
.icon-entry:before {
content: '\e934';
}
.icon-deletedTicket:before {
content: '\e935';
}
.icon-logout:before {
content: '\e936';
}
.icon-catalog:before {
content: '\e937';
}
.icon-agency:before {
content: '\e938';
}
.icon-delivery:before {
content: '\e939';
}
.icon-wand:before {
content: '\e93a';
}
.icon-buscaman:before {
content: '\e93b';
}
.icon-pbx:before {
content: '\e93c';
}
.icon-calendar:before {
content: '\e93d';
}
.icon-splitline:before {
content: '\e93e';
}
.icon-consignatarios:before {
content: '\e93f';
}
.icon-tax:before {
content: '\e940';
}
.icon-notes:before {
content: '\e941';
}
.icon-lines:before {
content: '\e942';
}
.icon-zone:before {
content: '\e943';
}
.icon-greuge:before {
content: '\e944';
}
.icon-ticketAdd:before {
content: '\e945';
}
.icon-components:before {
content: '\e946';
}
.icon-pets:before {
content: '\e947';
}
.icon-linesprepaired:before {
content: '\e948';
}
.icon-control:before {
content: '\e949';
}
.icon-revision:before {
content: '\e94a';
}
.icon-deaulter:before {
content: '\e94b';
}
.icon-services:before {
content: '\e94c';
}
.icon-albaran:before {
content: '\e94d';
}
.icon-solunion:before {
content: '\e94e';
}
.icon-stowaway:before {
content: '\e94f';
}
.icon-apps:before {
content: '\e951';
}
.icon-info:before {
content: '\e952';
}
.icon-columndelete:before {
content: '\e953';
}
.icon-columnadd:before {
content: '\e954';
}
.icon-deleteline:before {
content: '\e955';
}
.icon-item:before {
content: '\e956';
}
.icon-worker:before {
content: '\e957';
}
.icon-headercol:before {
content: '\e958';
}
.icon-reserva:before {
content: '\e959';
}
.icon-100:before { .icon-100:before {
content: '\e95a'; content: "\e926";
}
.icon-sign:before {
content: '\e95d';
}
.icon-polizon:before {
content: '\e95e';
}
.icon-solclaim:before {
content: '\e95f';
}
.icon-actions:before {
content: '\e960';
}
.icon-details:before {
content: '\e961';
}
.icon-traceability:before {
content: '\e962';
}
.icon-claims:before {
content: '\e963';
}
.icon-regentry:before {
content: '\e964';
}
.icon-transaction:before {
content: '\e966';
} }
.icon-History:before { .icon-History:before {
content: '\e968'; content: "\e964";
} }
.icon-mana:before { .icon-Person:before {
content: '\e96a'; content: "\e984";
} }
.icon-ticket:before { .icon-accessory:before {
content: '\e96b'; content: "\e948";
} }
.icon-niche:before { .icon-account:before {
content: '\e96c'; content: "\e927";
} }
.icon-tags:before { .icon-actions:before {
content: '\e96d'; content: "\e928";
} }
.icon-volume:before { .icon-addperson:before {
content: '\e96e'; content: "\e929";
}
.icon-bin:before {
content: '\e96f';
}
.icon-splur:before {
content: '\e970';
}
.icon-barcode:before {
content: '\e971';
}
.icon-botanical:before {
content: '\e972';
}
.icon-clone:before {
content: '\e973';
}
.icon-sms:before {
content: '\e975';
}
.icon-eye:before {
content: '\e976';
}
.icon-doc:before {
content: '\e977';
}
.icon-package:before {
content: '\e978';
}
.icon-settings:before {
content: '\e979';
}
.icon-bucket:before {
content: '\e97a';
}
.icon-mandatory:before {
content: '\e97b';
}
.icon-recovery:before {
content: '\e97c';
}
.icon-payment:before {
content: '\e97e';
}
.icon-grid:before {
content: '\e980';
}
.icon-web:before {
content: '\e982';
}
.icon-dfiscales:before {
content: '\e984';
}
.icon-trolley:before {
content: '\e95c';
} }
.icon-agency-term:before { .icon-agency-term:before {
content: '\e950'; content: "\e92b";
} }
.icon-client-unpaid:before { .icon-anonymous:before {
content: '\e95b'; content: "\e92d";
}
.icon-apps:before {
content: "\e92e";
}
.icon-artificial:before {
content: "\e92f";
}
.icon-attach:before {
content: "\e930";
}
.icon-barcode:before {
content: "\e932";
}
.icon-basket:before {
content: "\e933";
}
.icon-basketadd:before {
content: "\e934";
}
.icon-bin:before {
content: "\e935";
}
.icon-botanical:before {
content: "\e936";
}
.icon-bucket:before {
content: "\e937";
}
.icon-buscaman:before {
content: "\e938";
}
.icon-buyrequest:before {
content: "\e939";
}
.icon-calc_volum:before {
content: "\e93a";
}
.icon-calendar:before {
content: "\e940";
}
.icon-catalog:before {
content: "\e941";
}
.icon-claims:before {
content: "\e942";
}
.icon-client:before {
content: "\e943";
}
.icon-clone:before {
content: "\e945";
}
.icon-columnadd:before {
content: "\e946";
}
.icon-columndelete:before {
content: "\e947";
}
.icon-components:before {
content: "\e949";
}
.icon-consignatarios:before {
content: "\e94b";
}
.icon-control:before {
content: "\e94c";
}
.icon-credit:before {
content: "\e94d";
}
.icon-deaulter:before {
content: "\e94e";
}
.icon-deletedTicket:before {
content: "\e94f";
}
.icon-deleteline:before {
content: "\e950";
}
.icon-delivery:before {
content: "\e951";
}
.icon-deliveryprices:before {
content: "\e952";
}
.icon-details:before {
content: "\e954";
}
.icon-dfiscales:before {
content: "\e955";
}
.icon-disabled:before {
content: "\e965";
}
.icon-doc:before {
content: "\e956";
}
.icon-entry:before {
content: "\e958";
}
.icon-exit:before {
content: "\e959";
}
.icon-eye:before {
content: "\e95a";
}
.icon-fixedPrice:before {
content: "\e95b";
}
.icon-flower:before {
content: "\e95c";
}
.icon-frozen:before {
content: "\e95d";
}
.icon-fruit:before {
content: "\e95e";
}
.icon-funeral:before {
content: "\e95f";
}
.icon-greenery:before {
content: "\e91e";
}
.icon-greuge:before {
content: "\e960";
}
.icon-grid:before {
content: "\e961";
}
.icon-handmade:before {
content: "\e94a";
}
.icon-handmadeArtificial:before {
content: "\e962";
}
.icon-headercol:before {
content: "\e963";
}
.icon-info:before {
content: "\e966";
}
.icon-inventory:before {
content: "\e967";
}
.icon-invoice:before {
content: "\e969";
}
.icon-invoice-in:before {
content: "\e96a";
}
.icon-invoice-in-create:before {
content: "\e96b";
}
.icon-invoice-out:before {
content: "\e96c";
}
.icon-isTooLittle:before {
content: "\e96e";
}
.icon-item:before {
content: "\e96f";
}
.icon-languaje:before {
content: "\e912";
}
.icon-lines:before {
content: "\e971";
}
.icon-linesprepaired:before {
content: "\e972";
}
.icon-link-to-corrected:before {
content: "\e900";
}
.icon-link-to-correcting:before {
content: "\e906";
}
.icon-logout:before {
content: "\e90a";
}
.icon-mana:before {
content: "\e974";
}
.icon-mandatory:before {
content: "\e975";
}
.icon-net:before {
content: "\e976";
}
.icon-newalbaran:before {
content: "\e977";
}
.icon-niche:before {
content: "\e979";
}
.icon-no036:before {
content: "\e97a";
}
.icon-noPayMethod:before {
content: "\e97b";
}
.icon-notes:before {
content: "\e97c";
}
.icon-noweb:before {
content: "\e97e";
}
.icon-onlinepayment:before {
content: "\e97f";
}
.icon-package:before {
content: "\e980";
}
.icon-payment:before {
content: "\e982";
}
.icon-pbx:before {
content: "\e983";
}
.icon-pets:before {
content: "\e985";
}
.icon-photo:before {
content: "\e986";
}
.icon-plant:before {
content: "\e987";
}
.icon-polizon:before {
content: "\e989";
}
.icon-preserved:before {
content: "\e98a";
}
.icon-recovery:before {
content: "\e98b";
}
.icon-regentry:before {
content: "\e901";
}
.icon-reserva:before {
content: "\e902";
}
.icon-revision:before {
content: "\e903";
}
.icon-risk:before {
content: "\e904";
}
.icon-services:before {
content: "\e905";
}
.icon-settings:before {
content: "\e907";
}
.icon-shipment:before {
content: "\e908";
}
.icon-sign:before {
content: "\e909";
}
.icon-sms:before {
content: "\e90b";
}
.icon-solclaim:before {
content: "\e90c";
}
.icon-solunion:before {
content: "\e90d";
}
.icon-splitline:before {
content: "\e90e";
}
.icon-splur:before {
content: "\e90f";
}
.icon-stowaway:before {
content: "\e910";
}
.icon-supplier:before {
content: "\e911";
}
.icon-supplierfalse:before {
content: "\e913";
}
.icon-tags:before {
content: "\e914";
}
.icon-tax:before {
content: "\e915";
}
.icon-thermometer:before {
content: "\e916";
}
.icon-ticket:before {
content: "\e917";
}
.icon-ticketAdd:before {
content: "\e918";
}
.icon-traceability:before {
content: "\e919";
}
.icon-treatments:before {
content: "\e91c";
} }
.icon-trolley:before { .icon-trolley:before {
content: '\e95c'; content: "\e91a";
}
.icon-grafana:before {
content: '\e965';
} }
.icon-troncales:before { .icon-troncales:before {
content: '\e967'; content: "\e91b";
}
.icon-unavailable:before {
content: "\e91d";
}
.icon-volume:before {
content: "\e91f";
}
.icon-wand:before {
content: "\e920";
}
.icon-web:before {
content: "\e921";
}
.icon-wiki:before {
content: "\e922";
}
.icon-worker:before {
content: "\e923";
}
.icon-zone:before {
content: "\e924";
} }

View File

@ -62,6 +62,10 @@ export default {
selectRows: 'Select all { numberRows } row(s)', selectRows: 'Select all { numberRows } row(s)',
allRows: 'All { numberRows } row(s)', allRows: 'All { numberRows } row(s)',
markAll: 'Mark all', markAll: 'Mark all',
requiredField: 'Required field',
class: 'clase',
type: 'type',
reason: 'reason',
noResults: 'No results', noResults: 'No results',
system: 'System', system: 'System',
}, },
@ -679,6 +683,7 @@ export default {
vat: 'VAT', vat: 'VAT',
dueDay: 'Due day', dueDay: 'Due day',
intrastat: 'Intrastat', intrastat: 'Intrastat',
corrective: 'Corrective',
log: 'Logs', log: 'Logs',
}, },
list: { list: {

View File

@ -62,6 +62,10 @@ export default {
selectRows: 'Seleccionar las { numberRows } filas(s)', selectRows: 'Seleccionar las { numberRows } filas(s)',
allRows: 'Todo { numberRows } filas(s)', allRows: 'Todo { numberRows } filas(s)',
markAll: 'Marcar todo', markAll: 'Marcar todo',
requiredField: 'Campo obligatorio',
class: 'clase',
type: 'tipo',
reason: 'motivo',
noResults: 'Sin resultados', noResults: 'Sin resultados',
system: 'Sistema', system: 'Sistema',
}, },
@ -738,6 +742,7 @@ export default {
vat: 'IVA', vat: 'IVA',
dueDay: 'Vencimiento', dueDay: 'Vencimiento',
intrastat: 'Intrastat', intrastat: 'Intrastat',
corrective: 'Rectificativa',
log: 'Registros de auditoría', log: 'Registros de auditoría',
}, },
list: { list: {

View File

@ -36,7 +36,7 @@ const inputFileRef = ref();
const editDmsRef = ref(); const editDmsRef = ref();
const createDmsRef = ref(); const createDmsRef = ref();
const requiredFieldRule = (val) => val || t('Required field'); const requiredFieldRule = (val) => val || t('globals.requiredField');
const dateMask = '####-##-##'; const dateMask = '####-##-##';
const fillMask = '_'; const fillMask = '_';
@ -684,7 +684,6 @@ async function upsert() {
Type: Tipo Type: Tipo
Description: Descripción Description: Descripción
Generate identifier for original file: Generar identificador para archivo original Generate identifier for original file: Generar identificador para archivo original
Required field: Campo obligatorio
File: Fichero File: Fichero
Create document: Crear documento Create document: Crear documento
Select a file: Seleccione un fichero Select a file: Seleccione un fichero

View File

@ -44,9 +44,8 @@ const arrayData = useArrayData('InvoiceIn', {
filter, filter,
}); });
onMounted(async () => { onMounted(async () => await arrayData.fetch({ append: false }));
await arrayData.fetch({ append: false }); watch(
watch(
() => route.params.id, () => route.params.id,
async (newId, oldId) => { async (newId, oldId) => {
if (newId) { if (newId) {
@ -54,8 +53,7 @@ onMounted(async () => {
await arrayData.fetch({ append: false }); await arrayData.fetch({ append: false });
} }
} }
); );
});
</script> </script>
<template> <template>
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()"> <Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
@ -83,6 +81,6 @@ onMounted(async () => {
<i18n> <i18n>
es: es:
Search invoice: Buscar factura emitida Search invoice: Buscar factura recibida
You can search by invoice reference: Puedes buscar por referencia de la factura You can search by invoice reference: Puedes buscar por referencia de la factura
</i18n> </i18n>

View File

@ -0,0 +1,164 @@
<script setup>
import { ref, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useArrayData } from 'src/composables/useArrayData';
import { useCapitalize } from 'src/composables/useCapitalize';
import CrudModel from 'src/components/CrudModel.vue';
import FetchData from 'src/components/FetchData.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
const router = useRouter();
const route = useRoute();
const { t } = useI18n();
const invoiceId = route.params.id;
const arrayData = useArrayData('InvoiceIn');
const invoiceIn = computed(() => arrayData.store.data);
const invoiceInCorrectionRef = ref();
const filter = {
include: { relation: 'invoiceIn' },
where: { correctingFk: invoiceId },
};
const columns = computed(() => [
{
name: 'origin',
label: t('Original invoice'),
field: (row) => row.correctedFk,
sortable: true,
tabIndex: 1,
align: 'left',
style: 'padding-bottom: 20px',
},
{
name: 'type',
label: useCapitalize(t('globals.type')),
field: (row) => row.cplusRectificationTypeFk,
options: cplusRectificationTypes.value,
model: 'cplusRectificationTypeFk',
optionValue: 'id',
optionLabel: 'description',
sortable: true,
tabIndex: 1,
align: 'left',
},
{
name: 'class',
label: useCapitalize(t('globals.class')),
field: (row) => row.siiTypeInvoiceOutFk,
options: siiTypeInvoiceOuts.value,
model: 'siiTypeInvoiceOutFk',
optionValue: 'id',
optionLabel: 'code',
sortable: true,
tabIndex: 1,
align: 'left',
},
{
name: 'reason',
label: useCapitalize(t('globals.reason')),
field: (row) => row.invoiceCorrectionTypeFk,
options: invoiceCorrectionTypes.value,
model: 'invoiceCorrectionTypeFk',
optionValue: 'id',
optionLabel: 'description',
sortable: true,
tabIndex: 1,
align: 'left',
},
]);
const cplusRectificationTypes = ref([]);
const siiTypeInvoiceOuts = ref([]);
const invoiceCorrectionTypes = ref([]);
const rowsSelected = ref([]);
const requiredFieldRule = (val) => val || t('globals.requiredField');
const onSave = (data) => data.deletes && router.push(`/invoice-in/${invoiceId}/summary`);
</script>
<template>
<FetchData
url="CplusRectificationTypes"
@on-fetch="(data) => (cplusRectificationTypes = data)"
auto-load
/>
<FetchData
url="SiiTypeInvoiceOuts"
:where="{ code: { like: 'R%' } }"
@on-fetch="(data) => (siiTypeInvoiceOuts = data)"
auto-load
/>
<FetchData
url="InvoiceCorrectionTypes"
@on-fetch="(data) => (invoiceCorrectionTypes = data)"
auto-load
/>
<CrudModel
ref="invoiceInCorrectionRef"
v-if="invoiceIn"
data-key="InvoiceInCorrection"
url="InvoiceInCorrections"
:filter="filter"
auto-load
v-model:selected="rowsSelected"
primary-key="correctingFk"
@save-changes="onSave"
>
<template #body="{ rows }">
<QTable
v-model:selected="rowsSelected"
:columns="columns"
:rows="rows"
row-key="$index"
selection="single"
hide-pagination
:grid="$q.screen.lt.sm"
:pagination="{ rowsPerPage: 0 }"
>
<template #body-cell-type="{ row, col }">
<QTd>
<VnSelectFilter
class="q-pb-md"
v-model="row[col.model]"
:options="col.options"
:option-value="col.optionValue"
:option-label="col.optionLabel"
:readonly="row.invoiceIn.isBooked"
/>
</QTd>
</template>
<template #body-cell-class="{ row, col }">
<QTd>
<VnSelectFilter
class="q-pb-md"
v-model="row[col.model]"
:options="col.options"
:option-value="col.optionValue"
:option-label="col.optionLabel"
:rules="[requiredFieldRule]"
:readonly="row.invoiceIn.isBooked"
/>
</QTd>
</template>
<template #body-cell-reason="{ row, col }">
<QTd>
<VnSelectFilter
class="q-pb-md"
v-model="row[col.model]"
:options="col.options"
:option-value="col.optionValue"
:option-label="col.optionLabel"
:rules="[requiredFieldRule]"
:readonly="row.invoiceIn.isBooked"
/>
</QTd>
</template>
</QTable>
</template>
</CrudModel>
</template>
<style lang="scss" scoped></style>
<i18n>
es:
Original invoice: Factura origen
</i18n>

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, reactive, computed, onBeforeMount, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
@ -15,6 +15,8 @@ import CardDescriptor from 'components/ui/CardDescriptor.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import SendEmailDialog from 'components/common/SendEmailDialog.vue'; import SendEmailDialog from 'components/common/SendEmailDialog.vue';
import VnConfirm from 'src/components/ui/VnConfirm.vue'; import VnConfirm from 'src/components/ui/VnConfirm.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import { useCapitalize } from 'src/composables/useCapitalize';
const $props = defineProps({ const $props = defineProps({
id: { id: {
@ -34,11 +36,14 @@ const arrayData = useArrayData('InvoiceIn');
const invoiceIn = computed(() => arrayData.store.data); const invoiceIn = computed(() => arrayData.store.data);
const cardDescriptorRef = ref(); const cardDescriptorRef = ref();
const entityId = computed(() => $props.id || route.params.id); const correctionDialogRef = ref();
const entityId = computed(() => $props.id || +route.params.id);
const totalAmount = ref(); const totalAmount = ref();
const currentAction = ref(); const currentAction = ref();
const config = ref(); const config = ref();
const cplusRectificationTypes = ref([]);
const siiTypeInvoiceOuts = ref([]);
const invoiceCorrectionTypes = ref([]);
const actions = { const actions = {
book: { book: {
title: 'Are you sure you want to book this invoice?', title: 'Are you sure you want to book this invoice?',
@ -59,6 +64,9 @@ const actions = {
sendPdf: { sendPdf: {
cb: sendPdfInvoiceConfirmation, cb: sendPdfInvoiceConfirmation,
}, },
correct: {
cb: () => correctionDialogRef.value.show(),
},
}; };
const filter = { const filter = {
include: [ include: [
@ -86,8 +94,90 @@ const filter = {
}, },
], ],
}; };
const data = ref(useCardDescription()); const data = ref(useCardDescription());
const invoiceInCorrection = reactive({
correcting: [],
corrected: null,
});
const routes = reactive({
getSupplier: (id) => {
return { name: 'SupplierCard', params: { id } };
},
getTickets: (id) => {
return {
name: 'InvoiceInList',
query: {
params: JSON.stringify({ supplierFk: id }),
},
};
},
getCorrection: (invoiceInCorrection) => {
if (invoiceInCorrection.correcting.length > 1) {
return {
name: 'InvoiceInList',
query: {
params: JSON.stringify({ correctedFk: entityId.value }),
},
};
}
return {
name: 'InvoiceInCard',
params: {
id: invoiceInCorrection.corrected ?? invoiceInCorrection.correcting[0],
},
};
},
getEntry: (id) => {
return { name: 'EntryCard', params: { id } };
},
});
const correctionFormData = reactive({
invoiceReason: 2,
invoiceType: 2,
invoiceClass: 6,
});
const isNotFilled = computed(() => Object.values(correctionFormData).includes(null));
onBeforeMount(async () => await setInvoiceCorrection(entityId.value));
watch(
() => route.params.id,
async (newId) => {
invoiceInCorrection.correcting.length = 0;
invoiceInCorrection.corrected = null;
if (newId) await setInvoiceCorrection(entityId.value);
}
);
async function setInvoiceCorrection(id) {
const [{ data: correctingData }, { data: correctedData }] = await Promise.all([
axios.get('InvoiceInCorrections', {
params: {
filter: {
where: {
correctingFk: id,
},
},
},
}),
axios.get('InvoiceInCorrections', {
params: {
filter: {
where: {
correctedFk: id,
},
},
},
}),
]);
if (correctingData[0]) invoiceInCorrection.corrected = correctingData[0].correctedFk;
invoiceInCorrection.correcting = correctedData.map(
(corrected) => corrected.correctingFk
);
}
async function setData(entity) { async function setData(entity) {
data.value = useCardDescription(entity.supplierRef, entity.id); data.value = useCardDescription(entity.supplierRef, entity.id);
@ -104,7 +194,7 @@ function openDialog() {
quasar.dialog({ quasar.dialog({
component: VnConfirm, component: VnConfirm,
componentProps: { componentProps: {
title: currentAction.value.title, title: t(currentAction.value.title),
promise: currentAction.value.action, promise: currentAction.value.action,
}, },
}); });
@ -135,7 +225,6 @@ async function checkToBook() {
async function toBook() { async function toBook() {
await axios.post(`InvoiceIns/${entityId.value}/toBook`); await axios.post(`InvoiceIns/${entityId.value}/toBook`);
// Pendiente de sincronizar todo con arrayData
quasar.notify({ quasar.notify({
type: 'positive', type: 'positive',
message: t('globals.dataSaved'), message: t('globals.dataSaved'),
@ -163,6 +252,8 @@ async function cloneInvoice() {
router.push({ path: `/invoice-in/${data.id}/summary` }); router.push({ path: `/invoice-in/${data.id}/summary` });
} }
const requiredFieldRule = (val) => val || t('globals.requiredField');
const isAdministrative = () => hasAny(['administrative']); const isAdministrative = () => hasAny(['administrative']);
const isAgricultural = () => const isAgricultural = () =>
@ -202,6 +293,14 @@ function triggerMenu(type) {
if (currentAction.value.cb) currentAction.value.cb(); if (currentAction.value.cb) currentAction.value.cb();
else openDialog(type); else openDialog(type);
} }
const createInvoiceInCorrection = async () => {
const { data: correctingId } = await axios.post(
'InvoiceIns/corrective',
Object.assign(correctionFormData, { id: entityId.value })
);
router.push({ path: `/invoice-in/${correctingId}/summary` });
};
</script> </script>
<template> <template>
@ -211,7 +310,22 @@ function triggerMenu(type) {
auto-load auto-load
@on-fetch="(data) => (config = data)" @on-fetch="(data) => (config = data)"
/> />
<!--Refactor para añadir en el arrayData--> <FetchData
url="CplusRectificationTypes"
@on-fetch="(data) => (cplusRectificationTypes = data)"
auto-load
/>
<FetchData
url="SiiTypeInvoiceOuts"
:where="{ code: { like: 'R%' } }"
@on-fetch="(data) => (siiTypeInvoiceOuts = data)"
auto-load
/>
<FetchData
url="InvoiceCorrectionTypes"
@on-fetch="(data) => (invoiceCorrectionTypes = data)"
auto-load
/>
<CardDescriptor <CardDescriptor
ref="cardDescriptorRef" ref="cardDescriptorRef"
module="InvoiceIn" module="InvoiceIn"
@ -265,6 +379,22 @@ function triggerMenu(type) {
>{{ t('Send agricultural receipt as PDF') }}...</QItemSection >{{ t('Send agricultural receipt as PDF') }}...</QItemSection
> >
</QItem> </QItem>
<QItem
v-if="!invoiceInCorrection.corrected"
v-ripple
clickable
@click="triggerMenu('correct')"
>
<QItemSection>{{ t('Create rectificative invoice') }}...</QItemSection>
</QItem>
<QItem
v-if="entity.dmsFk"
v-ripple
clickable
@click="downloadFile(entity.dmsFk)"
>
<QItemSection>{{ t('components.smartCard.downloadFile') }}</QItemSection>
</QItem>
<QItem <QItem
v-if="entity.dmsFk" v-if="entity.dmsFk"
v-ripple v-ripple
@ -285,29 +415,131 @@ function triggerMenu(type) {
</template> </template>
<template #actions="{ entity }"> <template #actions="{ entity }">
<QCardActions> <QCardActions>
<!--Sección proveedores no disponible--> <QBtn
<!--Sección entradas no disponible--> size="md"
icon="vn:supplier"
color="primary"
:to="routes.getSupplier(entity.supplierFk)"
>
<QTooltip>{{ t('invoiceIn.list.supplier') }}</QTooltip>
</QBtn>
<QBtn
size="md"
icon="vn:entry"
color="primary"
:to="routes.getEntry(entity.entryFk)"
>
<QTooltip>{{ t('Entry') }}</QTooltip>
</QBtn>
<QBtn <QBtn
size="md" size="md"
icon="vn:ticket" icon="vn:ticket"
color="primary" color="primary"
:to="{ :to="routes.getTickets(entity.supplierFk)"
name: 'InvoiceInList',
query: {
params: JSON.stringify({ supplierFk: entity.supplierFk }),
},
}"
> >
<QTooltip>{{ t('invoiceOut.card.ticketList') }}</QTooltip> <QTooltip>{{ t('invoiceOut.card.ticketList') }}</QTooltip>
</QBtn> </QBtn>
<QBtn
v-if="
invoiceInCorrection.corrected ||
invoiceInCorrection.correcting.length
"
size="md"
:icon="
invoiceInCorrection.corrected
? 'vn:link-to-corrected'
: 'vn:link-to-correcting'
"
color="primary"
:to="routes.getCorrection(invoiceInCorrection)"
>
<QTooltip>{{
invoiceInCorrection.corrected
? t('Original invoice')
: t('Rectificative invoice')
}}</QTooltip>
</QBtn>
</QCardActions> </QCardActions>
</template> </template>
</CardDescriptor> </CardDescriptor>
<QDialog ref="correctionDialogRef">
<QCard>
<QCardSection>
<QItem class="q-px-none">
<span class="text-primary text-h6 full-width">
{{ t('Create rectificative invoice') }}
</span>
<QBtn icon="close" flat round dense v-close-popup />
</QItem>
</QCardSection>
<QCardSection>
<QItem>
<QItemSection>
<QInput
:label="t('Original invoice')"
v-model="entityId"
readonly
/>
<VnSelectFilter
:label="`${useCapitalize(t('globals.class'))}*`"
v-model="correctionFormData.invoiceClass"
:options="siiTypeInvoiceOuts"
option-value="id"
option-label="code"
:rules="[requiredFieldRule]"
/>
</QItemSection>
<QItemSection>
<VnSelectFilter
:label="`${useCapitalize(t('globals.type'))}*`"
v-model="correctionFormData.invoiceType"
:options="cplusRectificationTypes"
option-value="id"
option-label="description"
:rules="[requiredFieldRule]"
/>
<VnSelectFilter
:label="`${useCapitalize(t('globals.reason'))}*`"
v-model="correctionFormData.invoiceReason"
:options="invoiceCorrectionTypes"
option-value="id"
option-label="description"
:rules="[requiredFieldRule]"
/>
</QItemSection>
</QItem>
</QCardSection>
<QCardActions class="justify-end q-mr-sm">
<QBtn flat :label="t('globals.close')" color="primary" v-close-popup />
<QBtn
:label="t('globals.save')"
color="primary"
v-close-popup
@click="createInvoiceInCorrection"
:disable="isNotFilled"
/>
</QCardActions>
</QCard>
</QDialog>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.q-dialog { .q-dialog {
.q-card { .q-card {
width: 35em; max-width: 45em;
.q-item__section > .q-input {
padding-bottom: 1.4em;
}
}
}
@media (max-width: $breakpoint-xs) {
.q-dialog {
.q-card__section:nth-child(2) {
.q-item,
.q-item__section {
flex-direction: column;
}
}
} }
} }
</style> </style>
@ -324,4 +556,8 @@ es:
Send agricultural receipt as PDF: Enviar recibo agrícola como PDF Send agricultural receipt as PDF: Enviar recibo agrícola como PDF
Are you sure you want to send it?: Estás seguro que quieres enviarlo? Are you sure you want to send it?: Estás seguro que quieres enviarlo?
Send PDF invoice: Enviar factura a PDF Send PDF invoice: Enviar factura a PDF
Create rectificative invoice: Crear factura rectificativa
Rectificative invoice: Factura rectificativa
Original invoice: Factura origen
Entry: entrada
</i18n> </i18n>

View File

@ -423,6 +423,6 @@ function getLink(param) {
</style> </style>
<i18n> <i18n>
es: es:
Search invoice: Buscar factura emitida Search invoice: Buscar factura recibida
You can search by invoice reference: Puedes buscar por referencia de la factura You can search by invoice reference: Puedes buscar por referencia de la factura
</i18n> </i18n>

View File

@ -7,6 +7,7 @@ import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import { useCapitalize } from 'src/composables/useCapitalize';
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps({ const props = defineProps({
@ -49,6 +50,19 @@ const suppliersRef = ref();
</VnInput> </VnInput>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem>
<QItemSection>
<VnInput
:label="useCapitalize(t('params.correctedFk'))"
v-model="params.correctedFk"
is-outlined
>
<template #prepend>
<QIcon name="attachment" size="sm" />
</template>
</VnInput>
</QItemSection>
</QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
@ -218,6 +232,7 @@ en:
serial: Serial serial: Serial
account: Account account: Account
isBooked: is booked isBooked: is booked
correctedFk: Rectificatives
es: es:
params: params:
search: Contiene search: Contiene
@ -234,6 +249,7 @@ es:
account: Cuenta account: Cuenta
created: Creada created: Creada
dued: Vencida dued: Vencida
correctedFk: Rectificativas
From: Desde From: Desde
To: Hasta To: Hasta
Amount: Importe Amount: Importe

View File

@ -67,6 +67,7 @@ function navigate(id) {
data-key="InvoiceInList" data-key="InvoiceInList"
url="InvoiceIns/filter" url="InvoiceIns/filter"
order="issued DESC, id DESC" order="issued DESC, id DESC"
auto-load
> >
<template #body="{ rows }"> <template #body="{ rows }">
<CardList <CardList
@ -149,7 +150,7 @@ function navigate(id) {
<i18n> <i18n>
es: es:
Search invoice: Buscar factura emitida Search invoice: Buscar factura recibida
You can search by invoice reference: Puedes buscar por referencia de la factura You can search by invoice reference: Puedes buscar por referencia de la factura
Download: Descargar Download: Descargar
</i18n> </i18n>

View File

@ -16,6 +16,7 @@ export default {
'InvoiceInVat', 'InvoiceInVat',
'InvoiceInDueDay', 'InvoiceInDueDay',
'InvoiceInIntrastat', 'InvoiceInIntrastat',
'InvoiceInCorrective',
'InvoiceInLog', 'InvoiceInLog',
], ],
}, },
@ -102,6 +103,16 @@ export default {
}, },
component: () => import('src/pages/InvoiceIn/Card/InvoiceInLog.vue'), component: () => import('src/pages/InvoiceIn/Card/InvoiceInLog.vue'),
}, },
{
name: 'InvoiceInCorrective',
path: 'corrective',
meta: {
title: 'corrective',
icon: 'attachment',
},
component: () =>
import('src/pages/InvoiceIn/Card/InvoiceInCorrective.vue'),
},
], ],
}, },
], ],

View File

@ -0,0 +1,21 @@
/// <reference types="cypress" />
describe('InvoiceInCorrective', () => {
const createRectificative = '.q-menu > .q-list > :nth-child(4) > .q-item__section';
const rectificativeSection = '.q-drawer-container .q-list > a:nth-child(6)';
const saveDialog = '.q-card > .q-card__actions > .q-btn--standard ';
it('should create a correcting invoice', () => {
cy.login('developer');
cy.visit(`/#/invoice-in/1/summary?limit=10`);
cy.openLeftMenu();
cy.openActionsDescriptor();
cy.get(createRectificative).click();
cy.get(saveDialog).click();
cy.openLeftMenu();
cy.get(rectificativeSection).click();
cy.get('tbody > tr:visible').should('have.length', 1);
});
});

View File

@ -8,8 +8,7 @@ describe('InvoiceInList', () => {
beforeEach(() => { beforeEach(() => {
cy.login('developer'); cy.login('developer');
cy.visit(`/#/invoice-in`); cy.visit(`/#/invoice-in/list`);
cy.clickFilterSearchBtn();
}); });
it('should redirect on clicking a invoice', () => { it('should redirect on clicking a invoice', () => {

View File

@ -184,7 +184,7 @@ Cypress.Commands.add('validateContent', (selector, expectedValue) => {
cy.get(selector).should('have.text', expectedValue); cy.get(selector).should('have.text', expectedValue);
}); });
Cypress.Commands.add('clickFilterSearchBtn', () => { Cypress.Commands.add('openActionsDescriptor', () => {
cy.get('.q-item__section > .q-btn > .q-btn__content > .q-icon').click(); cy.get('.descriptor > .header > .q-btn').click();
}); });
// registerCommands(); // registerCommands();