2883-invoiceIn-Booking #786

Merged
carlosjr merged 17 commits from 2883-invoiceIn-Booking into dev 2022-01-24 15:29:57 +00:00
17 changed files with 2331 additions and 38 deletions

View File

@ -0,0 +1,2 @@
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES('InvoiceInDueDay', '*', '*', 'ALLOW', 'ROLE', 'administrative');

View File

@ -0,0 +1,248 @@
DROP PROCEDURE IF EXISTS vn.invoiceInBookingMain;
DELIMITER $$
$$
CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`invoiceInBookingMain`(vInvoiceInId INT)
BEGIN
DECLARE vTotalAmount,vTotalAmountDivisa DECIMAL(10,2);
DECLARE vBookNumber,vSerialNumber INT;
DECLARE vRate DECIMAL(10,4);
CALL invoiceInBookingCommon(vInvoiceInId,vSerialNumber);
SELECT SUM(iit.taxableBase * IF( i.serial= 'R' AND ti.Iva <> 'HP DEVENGADO 21 ISP', 1 +(ti.PorcentajeIva/100),1)),
SUM(iit.foreignValue * IF( i.serial= 'R', 1 + (ti.PorcentajeIva/100),1)),
iit.taxableBase/iit.foreignValue
INTO vTotalAmount, vTotalAmountDivisa, vRate
FROM newInvoiceIn i
JOIN invoiceInTax iit ON iit.invoiceInFk = i.id
LEFT JOIN sage.TiposIva ti ON ti.CodigoIva = iit.taxTypeSageFk;
CALL vn.ledger_next(vBookNumber);
-- Apunte del proveedor
INSERT INTO XDiario(ASIEN,
FECHA,
SUBCTA,
EUROHABER,
CONCEPTO,
CAMBIO,
HABERME,
NFACTICK,
CLAVE,
empresa_id
)
SELECT
vBookNumber,
n.bookEntried,
s.supplierAccount,
vTotalAmount EUROHABER,
n.conceptWithSupplier,
vRate,
vTotalAmountDivisa,
n.invoicesCount,
vInvoiceInId,
n.companyFk
FROM newInvoiceIn n
JOIN newSupplier s;
-- Línea de Gastos
INSERT INTO XDiario ( ASIEN,
FECHA,
SUBCTA,
CONTRA,
EURODEBE,
EUROHABER,
CONCEPTO,
CAMBIO,
DEBEME,
HABERME,
NFACTICK,
empresa_id
)
SELECT vBookNumber ASIEN,
n.bookEntried FECHA,
IF(e.isWithheld , LPAD(RIGHT(s.supplierAccount,5),10,iit.expenceFk),iit.expenceFk) SUBCTA,
s.supplierAccount CONTRA,
IF(e.isWithheld AND iit.taxableBase < 0, NULL, ROUND(SUM(iit.taxableBase),2)) EURODEBE,
IF(e.isWithheld AND iit.taxableBase < 0,ROUND(SUM(-iit.taxableBase),2),NULL) EUROHABER,
n.conceptWithSupplier CONCEPTO,
vRate,
IF(e.isWithheld,NULL,ABS(ROUND(SUM(iit.foreignValue),2))) DEBEME,
IF(e.isWithheld,ABS(ROUND(SUM(iit.foreignValue),2)),NULL) HABERME,
n.invoicesCount NFACTICK,
n.companyFk empresa_id
FROM newInvoiceIn n
JOIN newSupplier s
JOIN invoiceInTax iit ON iit.invoiceInFk = n.id
JOIN (SELECT * FROM expence e GROUP BY e.id)e ON e.id = iit.expenceFk
WHERE e.name != 'Suplidos Transitarios nacionales'
GROUP BY iit.expenceFk;
-- Líneas de IVA
INSERT INTO XDiario( ASIEN,
FECHA,
SUBCTA,
CONTRA,
EURODEBE,
BASEEURO,
CONCEPTO,
FACTURA,
IVA,
AUXILIAR,
SERIE,
TIPOOPE,
FECHA_EX,
FECHA_OP,
NFACTICK,
FACTURAEX,
L340,
LRECT349,
TIPOCLAVE,
TIPOEXENCI,
TIPONOSUJE,
TIPOFACT,
TIPORECTIF,
TERIDNIF,
TERNIF,
TERNOM,
FECREGCON,
empresa_id
)
SELECT vBookNumber ASIEN,
n.bookEntried FECHA,
IF(n.expenceFkDeductible>0, n.expenceFkDeductible, ti.CuentaIvaSoportado) SUBCTA,
s.supplierAccount CONTRA,
SUM(ROUND(ti.PorcentajeIva * it.taxableBase / 100 /* + 0.0001*/ , 2)) EURODEBE,
SUM(it.taxableBase) BASEEURO,
GROUP_CONCAT(DISTINCT e.`name` SEPARATOR ', ') CONCEPTO,
vSerialNumber FACTURA,
ti.PorcentajeIva IVA,
IF(isUeeMember AND eWithheld.id IS NULL,'','*') AUXILIAR,
n.serial SERIE,
ttr.ClaveOperacionDefecto,
n.issued FECHA_EX,
n.operated FECHA_OP,
n.invoicesCount NFACTICK,
n.supplierRef FACTURAEX,
TRUE L340,
(isSameCountry OR NOT isUeeMember) LRECT349,
n.cplusTrascendency472Fk TIPOCLAVE,
n.cplusTaxBreakFk TIPOEXENCI,
n.cplusSubjectOpFk TIPONOSUJE,
n.cplusInvoiceType472Fk TIPOFACT,
n.cplusRectificationTypeFk TIPORECTIF,
iis.cplusTerIdNifFk TERIDNIF,
s.nif AS TERNIF,
s.name AS TERNOM,
n.booked FECREGCON,
n.companyFk
FROM newInvoiceIn n
JOIN newSupplier s
JOIN invoiceInTax it ON n.id = it.invoiceInFk
JOIN sage.TiposIva ti ON ti.CodigoIva = it.taxTypeSageFk
JOIN sage.TiposTransacciones ttr ON ttr.CodigoTransaccion = it.transactionTypeSageFk
JOIN invoiceInSerial iis ON iis.code = n.serial
JOIN (SELECT * FROM expence e GROUP BY e.id)e ON e.id = it.expenceFk
LEFT JOIN (
SELECT eWithheld.id
FROM invoiceInTax hold
JOIN expence eWithheld ON eWithheld.id = hold.expenceFk AND eWithheld.isWithheld
WHERE hold.invoiceInFk = vInvoiceInId LIMIT 1
) eWithheld ON TRUE
WHERE it.taxTypeSageFk IS NOT NULL
AND it.taxTypeSageFk NOT IN (22, 90)
GROUP BY ti.PorcentajeIva, e.id;
-- Línea iva inversor sujeto pasivo
INSERT INTO XDiario( ASIEN,
FECHA,
SUBCTA,
CONTRA,
EUROHABER,
BASEEURO,
CONCEPTO,
FACTURA,
IVA,
AUXILIAR,
SERIE,
TIPOOPE,
FECHA_EX,
FECHA_OP,
NFACTICK,
FACTURAEX,
L340,
LRECT349,
TIPOCLAVE,
TIPOEXENCI,
TIPONOSUJE,
TIPOFACT,
TIPORECTIF,
TERIDNIF,
TERNIF,
TERNOM,
empresa_id
)
SELECT vBookNumber ASIEN,
n.bookEntried FECHA,
ti.CuentaIvaRepercutido SUBCTA,
s.supplierAccount CONTRA,
SUM(ROUND(ti.PorcentajeIva * it.taxableBase / 100,2)) EUROHABER,
ROUND(SUM(it.taxableBase),2) BASEEURO,
GROUP_CONCAT(DISTINCT e.`name` SEPARATOR ', ') CONCEPTO,
vSerialNumber FACTURA,
ti.PorcentajeIva IVA,
'*' AUXILIAR,
n.serial SERIE,
ttr.ClaveOperacionDefecto,
n.issued FECHA_EX,
n.operated FECHA_OP,
n.invoicesCount NFACTICK,
n.supplierRef FACTURAEX,
FALSE L340,
(isSameCountry OR NOT isUeeMember) LRECT349,
1 TIPOCLAVE,
n.cplusTaxBreakFk TIPOEXENCI,
n.cplusSubjectOpFk TIPONOSUJE,
n.cplusInvoiceType472Fk TIPOFACT,
n.cplusRectificationTypeFk TIPORECTIF,
iis.cplusTerIdNifFk TERIDNIF,
s.nif AS TERNIF,
s.name AS TERNOM,
n.companyFk
FROM newInvoiceIn n
JOIN newSupplier s
JOIN invoiceInTax it ON n.id = it.invoiceInFk
JOIN sage.TiposIva ti ON ti.CodigoIva = it.taxTypeSageFk
JOIN sage.TiposTransacciones ttr ON ttr.CodigoTransaccion = it.transactionTypeSageFk
JOIN invoiceInSerial iis ON iis.code = n.serial
JOIN (SELECT * FROM expence e GROUP BY e.id)e ON e.id = it.expenceFk
WHERE ti.Iva = 'HP DEVENGADO 21 ISP' OR MID(s.account, 4, 1) = '1'
GROUP BY ti.PorcentajeIva, e.id;
-- Actualización del registro original
UPDATE invoiceIn ii
JOIN newInvoiceIn ni ON ii.id = ni.id
SET ii.serialNumber = vSerialNumber,
ii.isBooked = TRUE;
-- Problemas derivados de la precisión en los decimales al calcular los impuestos
UPDATE XDiario
SET EURODEBE = EURODEBE -
(SELECT IF(ABS(sub.difference) = 0.01, sub.difference, 0)
FROM(
SELECT SUM(IFNULL(ROUND(EURODEBE, 2),0)) - SUM(IFNULL(ROUND(EUROHABER, 2), 0)) difference
FROM XDiario
WHERE ASIEN = vBookNumber
)sub
)
WHERE ASIEN = vBookNumber
AND EURODEBE <> 0
ORDER BY id DESC
LIMIT 1;
END$$
DELIMITER ;

View File

@ -2353,7 +2353,7 @@ REPLACE INTO `vn`.`invoiceIn`(`id`, `serialNumber`,`serial`, `supplierFk`, `issu
INSERT INTO `vn`.`invoiceInDueDay`(`invoiceInFk`, `dueDated`, `bankFk`, `amount`) INSERT INTO `vn`.`invoiceInDueDay`(`invoiceInFk`, `dueDated`, `bankFk`, `amount`)
VALUES VALUES
(1, CURDATE(), 1, 237), (1, CURDATE(), 1, 336.99),
(1, CURDATE(), 1, 15.25), (1, CURDATE(), 1, 15.25),
(2, CURDATE(), 1, 168), (2, CURDATE(), 1, 168),
(2, CURDATE(), 1, 55.17), (2, CURDATE(), 1, 55.17),

133
front/package-lock.json generated Normal file
View File

@ -0,0 +1,133 @@
{
"name": "salix-front",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@uirouter/angularjs": {
"version": "1.0.29",
"resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.29.tgz",
"integrity": "sha512-RImWnBarNixkMto0o8stEaGwZmvhv5cnuOLXyMU2pY8MP2rgEF74ZNJTLeJCW14LR7XDUxVH8Mk8bPI6lxedmQ==",
"requires": {
"@uirouter/core": "6.0.7"
}
},
"@uirouter/core": {
"version": "6.0.7",
"resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.7.tgz",
"integrity": "sha512-KUTJxL+6q0PiBnFx4/Z+Hsyg0pSGiaW5yZQeJmUxknecjpTbnXkLU8H2EqRn9N2B+qDRa7Jg8RcgeNDPY72O1w=="
},
"angular": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/angular/-/angular-1.8.2.tgz",
"integrity": "sha512-IauMOej2xEe7/7Ennahkbb5qd/HFADiNuLSESz9Q27inmi32zB0lnAsFeLEWcox3Gd1F6YhNd1CP7/9IukJ0Gw=="
},
"angular-animate": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.8.2.tgz",
"integrity": "sha512-Jbr9+grNMs9Kj57xuBU3Ju3NOPAjS1+g2UAwwDv7su1lt0/PLDy+9zEwDiu8C8xJceoTbmBNKiWGPJGBdCQLlA=="
},
"angular-moment": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/angular-moment/-/angular-moment-1.3.0.tgz",
"integrity": "sha512-KG8rvO9MoaBLwtGnxTeUveSyNtrL+RNgGl1zqWN36+HDCCVGk2DGWOzqKWB6o+eTTbO3Opn4hupWKIElc8XETA==",
"requires": {
"moment": ">=2.8.0 <3.0.0"
}
},
"angular-translate": {
"version": "2.18.4",
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.4.tgz",
"integrity": "sha512-KohNrkH6J9PK+VW0L/nsRTcg5Fw70Ajwwe3Jbfm54Pf9u9Fd+wuingoKv+h45mKf38eT+Ouu51FPua8VmZNoCw==",
"requires": {
"angular": "^1.8.0"
}
},
"angular-translate-loader-partial": {
"version": "2.18.4",
"resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.18.4.tgz",
"integrity": "sha512-bsjR+FbB0sdA2528E/ugwKdlPPQhA1looxLxI3otayBTFXBpED33besfSZhYAISLgNMSL038vSssfRUen9qD8w==",
"requires": {
"angular-translate": "~2.18.4"
}
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": {
"sprintf-js": "~1.0.2"
}
},
"croppie": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/croppie/-/croppie-2.6.5.tgz",
"integrity": "sha512-IlChnVUGG5T3w2gRZIaQgBtlvyuYnlUWs2YZIXXR3H9KrlO1PtBT3j+ykxvy9eZIWhk+V5SpBmhCQz5UXKrEKQ=="
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"mg-crud": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/mg-crud/-/mg-crud-1.1.2.tgz",
"integrity": "sha1-p6AWGzWSPK7/8ZpIBpS2V1vDggw=",
"requires": {
"angular": "^1.6.1"
}
},
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
},
"oclazyload": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/oclazyload/-/oclazyload-0.6.3.tgz",
"integrity": "sha1-Kjirv/QJDAihEBZxkZRbWfLoJ5w="
},
"require-yaml": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/require-yaml/-/require-yaml-0.0.1.tgz",
"integrity": "sha1-LhsY2RPDuqcqWk03O28Tjd0sMr0=",
"requires": {
"js-yaml": "^4.1.0"
},
"dependencies": {
"argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"requires": {
"argparse": "^2.0.1"
}
}
}
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"validator": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz",
"integrity": "sha1-R84j7Y1Ord+p1LjvAHG2zxB418g="
}
}
}

View File

@ -210,6 +210,7 @@
"Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio", "Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio",
"You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito", "You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito",
"You can't change the credit set to zero from a manager": "No puedes cambiar el cŕedito establecido a cero por un gerente", "You can't change the credit set to zero from a manager": "No puedes cambiar el cŕedito establecido a cero por un gerente",
"Amounts do not match": "Las cantidades no coinciden",
"The PDF document does not exists": "El documento PDF no existe. Prueba a regenerarlo desde la opción 'Regenerar PDF factura'", "The PDF document does not exists": "El documento PDF no existe. Prueba a regenerarlo desde la opción 'Regenerar PDF factura'",
"The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos", "The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos",
"You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días", "You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días",

View File

@ -0,0 +1,43 @@
module.exports = Self => {
Self.remoteMethod('getTotals', {
description: 'Return totals for an invoiceIn',
accessType: 'READ',
accepts: {
arg: 'id',
type: 'number',
required: true,
description: 'invoiceIn id',
http: {source: 'path'}
},
returns: {
type: 'object',
root: true
},
http: {
path: '/:id/getTotals',
verb: 'GET'
}
});
Self.getTotals = async(id, options) => {
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const [result] = await Self.rawSql(`
SELECT iit.*,
jgallego marked this conversation as resolved
Review

creating transaction on a read methods is required.

creating transaction on a read methods is required.
Review

is not* required

is not* required
SUM(iidd.amount) totalDueDay
FROM vn.invoiceIn ii
LEFT JOIN (SELECT SUM(iit.taxableBase) totalTaxableBase,
SUM(iit.taxableBase * (1 + (ti.PorcentajeIva / 100))) totalVat
FROM vn.invoiceInTax iit
LEFT JOIN sage.TiposIva ti ON ti.CodigoIva = iit.taxTypeSageFk
jgallego marked this conversation as resolved
Review

It is easier for code maintenance to extract values to constants or variables

It is easier for code maintenance to extract values to constants or variables
WHERE iit.invoiceInFk = ?) iit ON TRUE
LEFT JOIN vn.invoiceInDueDay iidd ON iidd.invoiceInFk = ii.id
WHERE
ii.id = ?`, [id, id]);
return result;
};
};

View File

@ -0,0 +1,21 @@
const models = require('vn-loopback/server/server').models;
describe('invoiceIn getTotals()', () => {
it('should check that returns invoiceIn totals', async() => {
const invoiceInId = 1;
const tx = await models.InvoiceIn.beginTransaction({});
const options = {transaction: tx};
try {
const invoiceIntotals = await models.InvoiceIn.getTotals(invoiceInId, options);
expect(typeof invoiceIntotals.totalTaxableBase).toBe('number');
expect(invoiceIntotals.totalTaxableBase).toEqual(invoiceIntotals.totalDueDay);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -0,0 +1,34 @@
const models = require('vn-loopback/server/server').models;
describe('invoiceIn toBook()', () => {
it('should check that invoiceIn is booked', async() => {
const userId = 1;
const ctx = {
req: {
accessToken: {userId: userId},
headers: {origin: 'http://localhost:5000'},
}
};
const invoiceInId = 1;
const tx = await models.InvoiceIn.beginTransaction({});
const options = {transaction: tx};
try {
const invoiceInBefore = await models.InvoiceIn.findById(invoiceInId, null, options);
expect(invoiceInBefore.isBooked).toEqual(false);
await models.InvoiceIn.toBook(ctx, invoiceInId, options);
const invoiceIn = await models.InvoiceIn.findById(invoiceInId, null, options);
expect(invoiceIn.isBooked).toEqual(true);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -20,6 +20,7 @@ module.exports = Self => {
}); });
Self.summary = async(id, options) => { Self.summary = async(id, options) => {
const models = Self.app.models;
const myOptions = {}; const myOptions = {};
if (typeof options == 'object') if (typeof options == 'object')
@ -85,25 +86,9 @@ module.exports = Self => {
} }
] ]
}; };
let summaryObj = await models.InvoiceIn.findById(id, filter, myOptions);
let summaryObj = await Self.app.models.InvoiceIn.findById(id, filter, myOptions); summaryObj.totals = await models.InvoiceIn.getTotals(id, myOptions);
summaryObj.totals = await getTotals(id);
return summaryObj; return summaryObj;
}; };
async function getTotals(invoiceInFk) {
return (await Self.rawSql(`
SELECT iit.*,
SUM(iidd.amount) totalDueDay
FROM vn.invoiceIn ii
LEFT JOIN (SELECT SUM(iit.taxableBase) totalTaxableBase,
SUM(iit.taxableBase * (1 + (ti.PorcentajeIva / 100))) totalVat
FROM vn.invoiceInTax iit
LEFT JOIN sage.TiposIva ti ON ti.CodigoIva = iit.taxTypeSageFk
WHERE iit.invoiceInFk = ?) iit ON TRUE
LEFT JOIN vn.invoiceInDueDay iidd ON iidd.invoiceInFk = ii.id
WHERE
ii.id = ?`, [invoiceInFk, invoiceInFk]))[0];
}
}; };

View File

@ -0,0 +1,42 @@
module.exports = Self => {
Self.remoteMethodCtx('toBook', {
description: 'To book the invoiceIn',
accessType: 'WRITE',
accepts: {
arg: 'id',
type: 'number',
required: true,
description: 'The invoiceIn id',
http: {source: 'path'}
},
returns: {
type: 'object',
root: true
},
http: {
path: '/:id/toBook',
verb: 'POST'
}
});
Self.toBook = async(ctx, id, options) => {
let tx;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
await Self.rawSql(`CALL vn.invoiceInBookingMain(?)`, [id], myOptions);
if (tx) await tx.commit();
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -2,4 +2,6 @@ module.exports = Self => {
require('../methods/invoice-in/filter')(Self); require('../methods/invoice-in/filter')(Self);
require('../methods/invoice-in/summary')(Self); require('../methods/invoice-in/summary')(Self);
require('../methods/invoice-in/clone')(Self); require('../methods/invoice-in/clone')(Self);
require('../methods/invoice-in/toBook')(Self);
require('../methods/invoice-in/getTotals')(Self);
}; };

View File

@ -1,8 +1,16 @@
<vn-descriptor-content module="invoiceIn" description="$ctrl.invoiceIn.supplierRef"> <vn-descriptor-content module="invoiceIn" description="$ctrl.invoiceIn.supplierRef">
<slot-menu> <slot-menu>
<vn-item
ng-click="$ctrl.checkToBook()"
vn-acl="administrative"
ng-hide="$ctrl.invoiceIn.isBooked == true"
translate>
To book
</vn-item>
<vn-item <vn-item
ng-click="deleteConfirmation.show()" ng-click="deleteConfirmation.show()"
vn-acl="invoicing" vn-acl="administrative"
vn-acl-action="remove" vn-acl-action="remove"
name="deleteInvoice" name="deleteInvoice"
translate> translate>
@ -10,7 +18,7 @@
</vn-item> </vn-item>
<vn-item <vn-item
ng-click="cloneConfirmation.show()" ng-click="cloneConfirmation.show()"
vn-acl="invoicing" vn-acl="administrative"
name="cloneInvoice" name="cloneInvoice"
translate> translate>
Clone Invoice Clone Invoice
@ -46,7 +54,9 @@
icon="icon-invoice-in"> icon="icon-invoice-in">
</vn-quick-link> </vn-quick-link>
</div> </div>
</div> </div>
</slot-body> </slot-body>
</vn-descriptor-content> </vn-descriptor-content>
<vn-confirm <vn-confirm
@ -62,3 +72,8 @@
<vn-supplier-descriptor-popover <vn-supplier-descriptor-popover
vn-id="supplierDescriptor"> vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover> </vn-supplier-descriptor-popover>
<vn-confirm
vn-id="confirm-toBookAnyway"
message="Are you sure you want to book this invoice?"
on-accept="$ctrl.onAcceptToBook()">
</vn-confirm>

View File

@ -51,6 +51,43 @@ class Controller extends Descriptor {
return this.getData(`InvoiceIns/${this.id}`, {filter}) return this.getData(`InvoiceIns/${this.id}`, {filter})
.then(res => this.entity = res.data); .then(res => this.entity = res.data);
} }
checkToBook() {
let message = '';
const id = this.invoiceIn.id;
this.$q.all([
this.$http.get(`InvoiceIns/${this.id}/getTotals`)
.then(res => {
const taxableBaseNotEqualDueDay = res.data.totalDueDay != res.data.totalTaxableBase;
const vatNotEqualDueDay = res.data.totalDueDay != res.data.totalVat;
if (taxableBaseNotEqualDueDay && vatNotEqualDueDay)
message += 'amountsDoNotMatch';
}),
this.$http.get('InvoiceInDueDays/count', {
filter: {
where: {
invoiceInFk: id,
dueDated: {gte: new Date()}
}
}})
.then(res => {
if (res.data)
message += 'future payments';
})
]).finally(() => {
if (message.length)
this.$.confirmToBookAnyway.show();
else
this.onAcceptToBook();
});
}
onAcceptToBook() {
this.$http.post(`InvoiceIns/${this.id}/toBook`)
.then(() => this.$state.reload())
.then(() => this.vnApp.showSuccess(this.$t('InvoiceIn booked')));
}
} }
ngModule.vnComponent('vnInvoiceInDescriptor', { ngModule.vnComponent('vnInvoiceInDescriptor', {

View File

@ -8,19 +8,70 @@ describe('vnInvoiceInDescriptor', () => {
beforeEach(inject(($componentController, _$httpBackend_) => { beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
controller = $componentController('vnInvoiceInDescriptor', {$element: null}); const $element = angular.element('<vn-invoice-in-descriptor></vn-invoice-in-descriptor>');
controller = $componentController('vnInvoiceInDescriptor', {$element});
controller.invoiceIn = {id: 1};
$httpBackend.when('GET', `InvoiceIns/${controller.invoiceIn.id}`).respond({id: 1});
})); }));
describe('loadData()', () => { describe('loadData()', () => {
it(`should perform a get query to store the invoice in data into the controller`, () => { it(`should perform a get query to store the invoice in data into the controller`, () => {
const id = 1; expect(controller.invoiceIn).toEqual({id: 1});
const response = {id: 1}; });
});
$httpBackend.expectGET(`InvoiceIns/${id}`).respond(response); describe('onAcceptToBook()', () => {
controller.id = id; it(`should perform a post query to book the invoice`, () => {
jest.spyOn(controller.vnApp, 'showSuccess');
controller.$state.reload = jest.fn();
const id = 1;
$httpBackend.expectPOST(`InvoiceIns/${id}/toBook`).respond();
controller.onAcceptToBook();
$httpBackend.flush(); $httpBackend.flush();
expect(controller.invoiceIn).toEqual(response); expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('InvoiceIn booked');
});
});
describe('checkToBook()', () => {
it(`should show a warning before book`, () => {
controller.$.confirmToBookAnyway = {show: () => {}};
jest.spyOn(controller.$.confirmToBookAnyway, 'show');
const invoceInId = 1;
const data = {
totalDueDay: 'an amount',
totalTaxableBase: 'distinct amount'
};
$httpBackend.expectGET(`InvoiceIns/${invoceInId}/getTotals`).respond(data);
$httpBackend.expectGET(`InvoiceInDueDays/count`).respond();
controller.checkToBook();
$httpBackend.flush();
expect(controller.$.confirmToBookAnyway.show).toHaveBeenCalledWith();
});
it(`should call onAcceptToBook`, () => {
controller.onAcceptToBook = jest.fn();
const invoceInId = 1;
const data = {
totalDueDay: 'same amount',
totalTaxableBase: 'same amount'
};
$httpBackend.expectGET(`InvoiceIns/${invoceInId}/getTotals`).respond(data);
$httpBackend.expectGET(`InvoiceInDueDays/count`).respond();
controller.checkToBook();
$httpBackend.flush();
expect(controller.onAcceptToBook).toHaveBeenCalledWith();
}); });
}); });
}); });

View File

@ -1,13 +1,16 @@
InvoiceIn: Facturas recibidas
Search invoices in by reference: Buscar facturas recibidas por referencia
Entries list: Listado de entradas
InvoiceIn deleted: Factura eliminada
Remove tax: Quitar iva
Add tax: Añadir iva Add tax: Añadir iva
Amounts do not match: La BI no coincide con el vencimiento ni con el total
Due day: Vencimiento
Entries list: Listado de entradas
Foreign value: Divisa
InvoiceIn: Facturas recibidas
InvoiceIn cloned: Factura clonada
InvoiceIn deleted: Factura eliminada
Invoice list: Listado de facturas recibidas
InvoiceIn booked: Factura contabilizada
Remove tax: Quitar iva
Sage tax: Sage iva Sage tax: Sage iva
Sage transaction: Sage transaccion Sage transaction: Sage transaccion
Foreign value: Divisa Search invoices in by reference: Buscar facturas recibidas por referencia
Due day: Vencimiento To book: Contabilizar
Invoice list: Listado de facturas recibidas
InvoiceIn cloned: Factura clonada

1676
print/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff