refs #5914 fix: transferInvoice
gitea/salix/pipeline/head There was a failure building this commit Details

This commit is contained in:
Alex Moreno 2023-11-20 07:56:15 +01:00
parent 54e8e6a27d
commit 0ddfb28327
14 changed files with 155 additions and 104 deletions

View File

@ -280,7 +280,18 @@ UNLOCK TABLES;
LOCK TABLES `siiTypeInvoiceOut` WRITE; LOCK TABLES `siiTypeInvoiceOut` WRITE;
/*!40000 ALTER TABLE `siiTypeInvoiceOut` DISABLE KEYS */; /*!40000 ALTER TABLE `siiTypeInvoiceOut` DISABLE KEYS */;
INSERT INTO `siiTypeInvoiceOut` VALUES (1,'F1 - Factura'),(2,'F2 - Factura simplificada (ticket)'),(3,'F3 - Factura emitida en sustitución de facturas simplificadas facturadas y declaradas'),(4,'F4 - Asiento resumen de facturas'),(5,'R1 - Factura rectificativa (Art. 80.1, 80.2 y error fundado en derecho)'),(6,'R2 - Factura rectificativa (Art. 80.3)'),(7,'R3 - Factura rectificativa (Art. 80.4)'),(8,'R4 - Factura rectificativa (Resto)'),(9,'R5 - Factura rectificativa en facturas simplificadas');
INSERT INTO `siiTypeInvoiceOut` (id, code, description) VALUES
(1, 'F1', 'Factura'),
(2, 'F2', 'Factura simplificada (ticket)'),
(3, 'F3', 'Factura emitida en sustitución de facturas simplificadas facturadas y declaradas'),
(4, 'F4', 'Asiento resumen de facturas'),
(5, 'R1', 'Factura rectificativa (Art. 80.1, 80.2 y error fundado en derecho)'),
(6, 'R2', 'Factura rectificativa (Art. 80.3)'),
(7, 'R3', 'Factura rectificativa (Art. 80.4)'),
(8, 'R4', 'Factura rectificativa (Resto)'),
(9, 'R5', 'Factura rectificativa en facturas simplificadas');
/*!40000 ALTER TABLE `siiTypeInvoiceOut` ENABLE KEYS */; /*!40000 ALTER TABLE `siiTypeInvoiceOut` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;

View File

@ -26001,9 +26001,11 @@ DROP TABLE IF EXISTS `siiTypeInvoiceOut`;
/*!40101 SET character_set_client = utf8 */; /*!40101 SET character_set_client = utf8 */;
CREATE TABLE `siiTypeInvoiceOut` ( CREATE TABLE `siiTypeInvoiceOut` (
`id` int(10) unsigned NOT NULL, `id` int(10) unsigned NOT NULL,
`code` varchar(2) NOT NULL,
`description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, `description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci COMMENT='(*18) TIPO FACTURA (Asientos)REPERCUTIDO - DEVENGADO (477)'; UNIQUE KEY `code_UNIQUE` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci COMMENT='Tipo de Factura Emitidas en el suministro de inmediato';
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
-- --

View File

@ -326,5 +326,16 @@
"The notification subscription of this worker cant be modified": "La subscripción a la notificación de este trabajador no puede ser modificada", "The notification subscription of this worker cant be modified": "La subscripción a la notificación de este trabajador no puede ser modificada",
"User disabled": "Usuario desactivado", "User disabled": "Usuario desactivado",
"The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mínima", "The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mínima",
"quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mínima" "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mínima",
"Base negativa para los tickets: 65": "Base negativa para los tickets: 65",
"Base negativa para los tickets: 67": "Base negativa para los tickets: 67",
"Base negativa para los tickets: 70": "Base negativa para los tickets: 70",
"Base negativa para los tickets: 72": "Base negativa para los tickets: 72",
"Base negativa para los tickets: 74": "Base negativa para los tickets: 74",
"Base negativa para los tickets: 33": "Base negativa para los tickets: 33",
"Base negativa para los tickets: 35": "Base negativa para los tickets: 35",
"Base negativa para los tickets: 37": "Base negativa para los tickets: 37",
"Base negativa para los tickets: 39": "Base negativa para los tickets: 39",
"Base negativa para los tickets: 41": "Base negativa para los tickets: 41",
"Base negativa para los tickets: 43": "Base negativa para los tickets: 43"
} }

View File

@ -24,7 +24,7 @@ describe('InvoiceOut tranferInvoice()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const args = { const args = {
id: '1', id: '1',
ref: 'T4444444', refFk: 'T4444444',
newClientFk: 1, newClientFk: 1,
cplusRectificationId: 1, cplusRectificationId: 1,
siiTypeInvoiceOutId: 1, siiTypeInvoiceOutId: 1,
@ -49,7 +49,7 @@ describe('InvoiceOut tranferInvoice()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const args = { const args = {
id: '1', id: '1',
ref: 'T1111111', refFk: 'T1111111',
newClientFk: 1101, newClientFk: 1101,
cplusRectificationId: 1, cplusRectificationId: 1,
siiTypeInvoiceOutId: 1, siiTypeInvoiceOutId: 1,

View File

@ -12,7 +12,7 @@ module.exports = Self => {
description: 'Issued invoice id' description: 'Issued invoice id'
}, },
{ {
arg: 'ref', arg: 'refFk',
type: 'string', type: 'string',
required: true required: true
}, },
@ -22,17 +22,17 @@ module.exports = Self => {
required: true required: true
}, },
{ {
arg: 'cplusRectificationId', arg: 'cplusRectificationFk',
type: 'number', type: 'number',
required: true required: true
}, },
{ {
arg: 'siiTypeInvoiceOutId', arg: 'siiTypeInvoiceOutFk',
type: 'number', type: 'number',
required: true required: true
}, },
{ {
arg: 'invoiceCorrectionTypeId', arg: 'invoiceCorrectionTypeFk',
type: 'number', type: 'number',
required: true required: true
}, },
@ -50,14 +50,14 @@ module.exports = Self => {
Self.transferInvoice = async(ctx, options) => { Self.transferInvoice = async(ctx, options) => {
const models = Self.app.models; const models = Self.app.models;
const myOptions = {userId: ctx.req.accessToken.userId}; const myOptions = {userId: ctx.req.accessToken.userId};
const args = ctx.args; const {id, refFk, newClientFk, cplusRectificationFk, siiTypeInvoiceOutFk, invoiceCorrectionTypeFk} = ctx.args;
let tx; let tx;
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
const {clientFk} = await models.InvoiceOut.findById(args.id); const {clientFk} = await models.InvoiceOut.findById(id);
if (clientFk == args.newClientFk) if (clientFk == newClientFk)
throw new UserError(`Select a different client`); throw new UserError(`Select a different client`);
if (!myOptions.transaction) { if (!myOptions.transaction) {
@ -65,10 +65,10 @@ module.exports = Self => {
myOptions.transaction = tx; myOptions.transaction = tx;
} }
try { try {
const filterRef = {where: {refFk: args.ref}}; const filterRef = {where: {refFk: refFk}};
const tickets = await models.Ticket.find(filterRef, myOptions); const tickets = await models.Ticket.find(filterRef, myOptions);
const ticketsIds = tickets.map(ticket => ticket.id); const ticketsIds = tickets.map(ticket => ticket.id);
await models.Ticket.refund(ctx, ticketsIds, null, myOptions); const refundTickets = await models.Ticket.refund(ctx, ticketsIds, null, myOptions);
const filterTicket = {where: {ticketFk: {inq: ticketsIds}}}; const filterTicket = {where: {ticketFk: {inq: ticketsIds}}};
@ -82,20 +82,16 @@ module.exports = Self => {
const clonedTicketIds = []; const clonedTicketIds = [];
for (const clonedTicket of clonedTickets) { for (const clonedTicket of clonedTickets) {
await clonedTicket.updateAttribute('clientFk', args.newClientFk, myOptions); await clonedTicket.updateAttribute('clientFk', newClientFk, myOptions);
clonedTicketIds.push(clonedTicket.id); clonedTicketIds.push(clonedTicket.id);
} }
const invoiceIds = await models.Ticket.invoiceTickets(ctx, clonedTicketIds, myOptions); const invoiceCorrection =
const [invoiceId] = invoiceIds; {correctedFk: id, cplusRectificationFk, siiTypeInvoiceOutFk, invoiceCorrectionTypeFk};
const refundTicketIds = refundTickets.map(ticket => ticket.id);
await models.InvoiceCorrection.create({ await models.Ticket.invoiceTickets(ctx, refundTicketIds, invoiceCorrection, myOptions);
correctingFk: invoiceId, const [[invoiceId]] = await models.Ticket.invoiceTickets(ctx, clonedTicketIds, myOptions);
correctedFk: args.id,
cplusRectificationTypeFk: args.cplusRectificationId,
siiTypeInvoiceOutFk: args.siiTypeInvoiceOutId,
invoiceCorrectionTypeFk: args.invoiceCorrectionTypeId
}, myOptions);
if (tx) { if (tx) {
await tx.commit(); await tx.commit();

View File

@ -16,13 +16,16 @@
"type": "number" "type": "number"
}, },
"cplusRectificationTypeFk": { "cplusRectificationTypeFk": {
"type": "number" "type": "number",
"required": true
}, },
"siiTypeInvoiceOutFk": { "siiTypeInvoiceOutFk": {
"type": "number" "type": "number",
"required": true
}, },
"invoiceCorrectionTypeFk": { "invoiceCorrectionTypeFk": {
"type": "number" "type": "number",
"required": true
} }
} }
} }

View File

@ -12,6 +12,9 @@
"type": "number", "type": "number",
"description": "Identifier" "description": "Identifier"
}, },
"code": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
} }

View File

@ -7,7 +7,8 @@
<vn-crud-model <vn-crud-model
auto-load="true" auto-load="true"
url="SiiTypeInvoiceOuts" url="SiiTypeInvoiceOuts"
data="siiTypeInvoiceOut"> data="siiTypeInvoiceOuts"
where="{code: {like: 'R%'}}">
</vn-crud-model> </vn-crud-model>
<vn-crud-model <vn-crud-model
auto-load="true" auto-load="true"
@ -185,64 +186,69 @@
vn-id="transferInvoice" vn-id="transferInvoice"
title="transferInvoice" title="transferInvoice"
on-accept="$ctrl.transferInvoice()"> on-accept="$ctrl.transferInvoice()">
<tpl-title translate>
transferInvoice
</tpl-title>
<tpl-body> <tpl-body>
<section class="transferInvoice"> <section class="transferInvoice">
<vn-horizontal> <vn-horizontal>
<vn-autocomplete <vn-autocomplete
vn-one vn-one
vn-id="client" vn-id="client"
required="true" required="true"
url="Clients" url="Clients"
label="Client" label="Client"
show-field="name" show-field="name"
value-field="id" value-field="id"
search-function="{or: [{id: $search}, {name: {like: '%'+ $search +'%'}}]}" search-function="{or: [{id: $search}, {name: {like: '%'+ $search +'%'}}]}"
ng-model="$ctrl.invoiceOut.client.id" ng-model="$ctrl.clientId"
initial-data="$ctrl.invoiceOut.client.id" order="id">
order="id"> <tpl-item>
<tpl-item> #{{id}} - {{::name}}
#{{id}} - {{::name}} </tpl-item>
</tpl-item> </vn-autocomplete>
</vn-autocomplete> <vn-autocomplete
<vn-autocomplete vn-one
vn-one vn-id="cplusRectificationType"
vn-id="cplusRectificationType" required="true"
required="true" data="cplusRectificationTypes"
data="cplusRectificationTypes" show-field="description"
show-field="description" value-field="id"
value-field="id" ng-model="$ctrl.cplusRectificationType"
ng-model="$ctrl.cplusRectificationType" search-function="{or: [{id: $search}, {description: {like: '%'+ $search +'%'}}]}"
search-function="{or: [{id: $search}, {description: {like: '%'+ $search +'%'}}]}" label="Rectificative type">
label="Cplus Type"> <tpl-item>
<tpl-item> {{::description}}
{{::description}} </tpl-item>
</tpl-item> </vn-autocomplete>
</vn-autocomplete> </vn-horizontal>
</vn-horizontal> <vn-horizontal>
<vn-horizontal> <vn-autocomplete
<vn-autocomplete vn-one
vn-one vn-id="siiTypeInvoiceOut"
vn-id="cplusInvoiceType" data="siiTypeInvoiceOuts"
data="siiTypeInvoiceOut" show-field="code"
show-field="description" value-field="id"
value-field="id" fields="['id','code','description']"
required="true" required="true"
ng-model="$ctrl.siiTypeInvoiceOut" ng-model="$ctrl.siiTypeInvoiceOut"
search-function="{or: [{id: $search}, {description: {like: '%'+ $search +'%'}}]}" label="Class">
label="Class"> <tpl-item>
</vn-autocomplete> {{::code}} - {{::description}}
<vn-autocomplete </tpl-item>
vn-one </vn-autocomplete>
vn-id="invoiceCorrectionType" <vn-autocomplete
data="invoiceCorrectionTypes" vn-one
ng-model="$ctrl.invoiceCorrectionType" vn-id="invoiceCorrectionType"
show-field="description" data="invoiceCorrectionTypes"
value-field="id" ng-model="$ctrl.invoiceCorrectionType"
required="true" show-field="description"
label="Type"> value-field="id"
</vn-autocomplete> required="true"
</vn-horizontal> label="Type">
</section> </vn-autocomplete>
</vn-horizontal>
</section>
</tpl-body> </tpl-body>
<tpl-buttons> <tpl-buttons>
<button response="accept" translate>Transfer client</button> <button response="accept" translate>Transfer client</button>

View File

@ -129,11 +129,11 @@ class Controller extends Section {
transferInvoice() { transferInvoice() {
const params = { const params = {
id: this.invoiceOut.id, id: this.invoiceOut.id,
ref: this.invoiceOut.ref, refFk: this.invoiceOut.ref,
newClientFk: this.invoiceOut.client.id, newClientFk: this.clientId,
cplusRectificationId: this.cplusRectificationType, cplusRectificationFk: this.cplusRectificationType,
siiTypeInvoiceOutId: this.siiTypeInvoiceOut, siiTypeInvoiceOutFk: this.siiTypeInvoiceOut,
invoiceCorrectionTypeId: this.invoiceCorrectionType invoiceCorrectionTypeFk: this.invoiceCorrectionType
}; };
this.$http.post(`InvoiceOuts/transferInvoice`, params).then(res => { this.$http.post(`InvoiceOuts/transferInvoice`, params).then(res => {
const invoiceId = res.data; const invoiceId = res.data;

View File

@ -22,4 +22,4 @@ The email can't be empty: El correo no puede estar vacío
The following refund tickets have been created: "Se han creado los siguientes tickets de abono: {{ticketIds}}" The following refund tickets have been created: "Se han creado los siguientes tickets de abono: {{ticketIds}}"
Refund...: Abono... Refund...: Abono...
Transfer invoice to...: Transferir factura a... Transfer invoice to...: Transferir factura a...
Cplus Type: Cplus Tipo Rectificative type: Tipo rectificativa

View File

@ -19,7 +19,7 @@ module.exports = Self => {
} }
], ],
returns: { returns: {
type: ['number'], type: ['object'],
root: true root: true
}, },
http: { http: {
@ -54,7 +54,7 @@ module.exports = Self => {
if (tx) await tx.commit(); if (tx) await tx.commit();
return refundsTicket[0]; return refundsTicket;
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();
throw e; throw e;

View File

@ -10,7 +10,13 @@ module.exports = function(Self) {
description: 'The tickets id', description: 'The tickets id',
type: ['number'], type: ['number'],
required: true required: true
},
{
arg: 'invoiceCorrection',
description: 'The invoice correction',
type: 'object',
} }
], ],
returns: { returns: {
type: ['object'], type: ['object'],
@ -22,7 +28,7 @@ module.exports = function(Self) {
} }
}); });
Self.invoiceTickets = async(ctx, ticketsIds, options) => { Self.invoiceTickets = async(ctx, ticketsIds, invoiceCorrection, options) => {
const models = Self.app.models; const models = Self.app.models;
const date = Date.vnNew(); const date = Date.vnNew();
date.setHours(0, 0, 0, 0); date.setHours(0, 0, 0, 0);
@ -68,9 +74,9 @@ module.exports = function(Self) {
const addressIds = result.map(address => address.addressFk); const addressIds = result.map(address => address.addressFk);
for (const address of addressIds) for (const address of addressIds)
await createInvoice(ctx, companyId, ticketsIds, address, invoicesIds, myOptions); await createInvoice(ctx, companyId, ticketsIds, address, invoicesIds, invoiceCorrection, myOptions);
} else } else
await createInvoice(ctx, companyId, ticketsIds, null, invoicesIds, myOptions); await createInvoice(ctx, companyId, ticketsIds, null, invoicesIds, invoiceCorrection, myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();
} catch (e) { } catch (e) {
@ -85,9 +91,9 @@ module.exports = function(Self) {
return invoicesIds; return invoicesIds;
}; };
async function createInvoice(ctx, companyId, ticketsIds, address, invoicesIds, myOptions) { async function createInvoice(ctx, companyId, ticketsIds, address, invoicesIds, invoiceCorrection, myOptions) {
const models = Self.app.models; const models = Self.app.models;
console.log(ticketsIds, address);
await models.Ticket.rawSql(` await models.Ticket.rawSql(`
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketToInvoice CREATE OR REPLACE TEMPORARY TABLE tmp.ticketToInvoice
(PRIMARY KEY (id)) (PRIMARY KEY (id))
@ -98,7 +104,8 @@ module.exports = function(Self) {
${address ? `AND addressFk = ${address}` : ''} ${address ? `AND addressFk = ${address}` : ''}
`, [ticketsIds], myOptions); `, [ticketsIds], myOptions);
const invoiceId = await models.Ticket.makeInvoice(ctx, 'R', companyId, Date.vnNew(), myOptions); const invoiceId =
await models.Ticket.makeInvoice(ctx, 'R', companyId, Date.vnNew(), invoiceCorrection, myOptions);
invoicesIds.push(invoiceId); invoicesIds.push(invoiceId);
} }
}; };

View File

@ -22,6 +22,11 @@ module.exports = function(Self) {
description: 'The invoice date', description: 'The invoice date',
type: 'date', type: 'date',
required: true required: true
},
{
arg: 'invoiceCorrection',
description: 'The invoice correction',
type: 'object',
} }
], ],
returns: { returns: {
@ -34,7 +39,7 @@ module.exports = function(Self) {
} }
}); });
Self.makeInvoice = async(ctx, invoiceType, companyFk, invoiceDate, options) => { Self.makeInvoice = async(ctx, invoiceType, companyFk, invoiceDate, invoiceCorrection, options) => {
const models = Self.app.models; const models = Self.app.models;
invoiceDate.setHours(0, 0, 0, 0); invoiceDate.setHours(0, 0, 0, 0);
@ -67,8 +72,8 @@ module.exports = function(Self) {
const [firstTicket] = tickets; const [firstTicket] = tickets;
const clientId = firstTicket.clientFk; const clientId = firstTicket.clientFk;
const clientCanBeInvoiced = await models.Client.canBeInvoiced(clientId, companyFk, myOptions); const clientCanBeInvoiced = await models.Client.canBeInvoiced(clientId, companyFk, invoiceCorrection, myOptions);
if (!clientCanBeInvoiced) if (!clientCanBeInvoiced && !invoiceCorrection)
throw new UserError(`This client can't be invoiced`); throw new UserError(`This client can't be invoiced`);
const query = `SELECT vn.invoiceSerial(?, ?, ?) AS serial`; const query = `SELECT vn.invoiceSerial(?, ?, ?) AS serial`;
@ -85,6 +90,13 @@ module.exports = function(Self) {
if (!resultInvoice) if (!resultInvoice)
throw new UserError('No tickets to invoice', 'notInvoiced'); throw new UserError('No tickets to invoice', 'notInvoiced');
if (invoiceCorrection) {
await models.InvoiceCorrection.create(
Object.assign(invoiceCorrection, {correctingFk: resultInvoice.id}),
myOptions
);
}
if (serial != 'R' && resultInvoice.id) if (serial != 'R' && resultInvoice.id)
await Self.rawSql('CALL invoiceOutBooking(?)', [resultInvoice.id], myOptions); await Self.rawSql('CALL invoiceOutBooking(?)', [resultInvoice.id], myOptions);

View File

@ -15,7 +15,7 @@ module.exports = Self => {
} }
], ],
returns: { returns: {
type: ['number'], type: ['object'],
root: true root: true
}, },
http: { http: {