Merge branch 'dev' into 3258-import_buys
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
78507240ee
|
@ -0,0 +1,2 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||||
|
VALUES('InvoiceInDueDay', '*', '*', 'ALLOW', 'ROLE', 'administrative');
|
|
@ -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 ;
|
|
@ -137,7 +137,8 @@ module.exports = class Docker {
|
||||||
user: this.dbConf.username,
|
user: this.dbConf.username,
|
||||||
password: this.dbConf.password,
|
password: this.dbConf.password,
|
||||||
host: this.dbConf.host,
|
host: this.dbConf.host,
|
||||||
port: this.dbConf.port
|
port: this.dbConf.port,
|
||||||
|
connectTimeout: maxInterval
|
||||||
};
|
};
|
||||||
|
|
||||||
log('Waiting for MySQL init process...');
|
log('Waiting for MySQL init process...');
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -206,6 +206,12 @@ describe('Ticket Edit sale path', () => {
|
||||||
expect(message.text).toContain('Data saved!');
|
expect(message.text).toContain('Data saved!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should log in as salesAssistant and navigate to ticket sales', async() => {
|
||||||
|
await page.loginAndModule('salesAssistant', 'ticket');
|
||||||
|
await page.accessToSearchResult('16');
|
||||||
|
await page.accessToSection('ticket.card.sale');
|
||||||
|
});
|
||||||
|
|
||||||
it('should select the third sale and create a pay back', async() => {
|
it('should select the third sale and create a pay back', async() => {
|
||||||
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
|
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
|
||||||
await page.waitToClick(selectors.ticketSales.moreMenu);
|
await page.waitToClick(selectors.ticketSales.moreMenu);
|
||||||
|
|
|
@ -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="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -210,11 +210,13 @@
|
||||||
"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",
|
||||||
"The worker has hours recorded that day": "El trabajador tiene horas fichadas ese día",
|
"The worker has hours recorded that day": "El trabajador tiene horas fichadas ese día",
|
||||||
"The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día",
|
"The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día",
|
||||||
"You can not modify is pay method checked": "No se puede modificar el campo método de pago validado",
|
"You can not modify is pay method checked": "No se puede modificar el campo método de pago validado",
|
||||||
"Can't transfer claimed sales": "No puedes transferir lineas reclamadas"
|
"Can't transfer claimed sales": "No puedes transferir lineas reclamadas",
|
||||||
}
|
"You don't have privileges to create pay back": "No tienes permisos para crear un abono"
|
||||||
|
}
|
|
@ -20,6 +20,9 @@
|
||||||
"MailForward": {
|
"MailForward": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"RoleConfig": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"RoleInherit": {
|
"RoleInherit": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -114,17 +114,22 @@ module.exports = Self => {
|
||||||
'bcryptPassword',
|
'bcryptPassword',
|
||||||
'updated'
|
'updated'
|
||||||
],
|
],
|
||||||
include: {
|
include: [
|
||||||
relation: 'roles',
|
{
|
||||||
scope: {
|
relation: 'roles',
|
||||||
include: {
|
scope: {
|
||||||
relation: 'inherits',
|
include: {
|
||||||
scope: {
|
relation: 'inherits',
|
||||||
fields: ['name']
|
scope: {
|
||||||
|
fields: ['name']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
relation: 'role',
|
||||||
|
fields: ['name']
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
let info = {
|
let info = {
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.getSynchronizer = async function() {
|
||||||
|
return await Self.findOne({fields: ['id']});
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(Self.prototype, {
|
||||||
|
async init() {
|
||||||
|
const [row] = await Self.rawSql('SELECT VERSION() AS `version`');
|
||||||
|
if (row.version.includes('MariaDB'))
|
||||||
|
this.dbType = 'MariaDB';
|
||||||
|
else
|
||||||
|
this.dbType = 'MySQL';
|
||||||
|
},
|
||||||
|
|
||||||
|
async syncUser(userName, info, password) {
|
||||||
|
const mysqlHost = '%';
|
||||||
|
|
||||||
|
let mysqlUser = userName;
|
||||||
|
if (this.dbType == 'MySQL') mysqlUser = `!${mysqlUser}`;
|
||||||
|
|
||||||
|
const [row] = await Self.rawSql(
|
||||||
|
`SELECT COUNT(*) AS nRows
|
||||||
|
FROM mysql.user
|
||||||
|
WHERE User = ?
|
||||||
|
AND Host = ?`,
|
||||||
|
[mysqlUser, mysqlHost]
|
||||||
|
);
|
||||||
|
let userExists = row.nRows > 0;
|
||||||
|
|
||||||
|
let isUpdatable = true;
|
||||||
|
if (this.dbType == 'MariaDB') {
|
||||||
|
const [row] = await Self.rawSql(
|
||||||
|
`SELECT Priv AS priv
|
||||||
|
FROM mysql.global_priv
|
||||||
|
WHERE User = ?
|
||||||
|
AND Host = ?`,
|
||||||
|
[mysqlUser, mysqlHost]
|
||||||
|
);
|
||||||
|
const priv = row && JSON.parse(row.priv);
|
||||||
|
const role = priv && priv.default_role;
|
||||||
|
isUpdatable = !row || (role && role.startsWith('z-'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isUpdatable) {
|
||||||
|
console.warn(`RoleConfig.syncUser(): User '${userName}' cannot be updated, not managed by me`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.hasAccount) {
|
||||||
|
if (password) {
|
||||||
|
if (!userExists) {
|
||||||
|
await Self.rawSql('CREATE USER ?@? IDENTIFIED BY ?',
|
||||||
|
[mysqlUser, mysqlHost, password]
|
||||||
|
);
|
||||||
|
userExists = true;
|
||||||
|
} else {
|
||||||
|
switch (this.dbType) {
|
||||||
|
case 'MariaDB':
|
||||||
|
await Self.rawSql('ALTER USER ?@? IDENTIFIED BY ?',
|
||||||
|
[mysqlUser, mysqlHost, password]
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
await Self.rawSql('SET PASSWORD FOR ?@? = PASSWORD(?)',
|
||||||
|
[mysqlUser, mysqlHost, password]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userExists && this.dbType == 'MariaDB') {
|
||||||
|
let role = `z-${info.user.role().name}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Self.rawSql('REVOKE ALL, GRANT OPTION FROM ?@?',
|
||||||
|
[mysqlUser, mysqlHost]
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code == 'ER_REVOKE_GRANTS')
|
||||||
|
console.warn(`${err.code}: ${err.sqlMessage}: ${err.sql}`);
|
||||||
|
else
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
await Self.rawSql('GRANT ? TO ?@?',
|
||||||
|
[role, mysqlUser, mysqlHost]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (role) {
|
||||||
|
await Self.rawSql('SET DEFAULT ROLE ? FOR ?@?',
|
||||||
|
[role, mysqlUser, mysqlHost]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await Self.rawSql('SET DEFAULT ROLE NONE FOR ?@?',
|
||||||
|
[mysqlUser, mysqlHost]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (userExists)
|
||||||
|
await Self.rawSql('DROP USER ?@?', [mysqlUser, mysqlHost]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "RoleConfig",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "account.roleConfig"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mixins": {
|
||||||
|
"AccountSynchronizer": {}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"mysqlPassword": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.*,
|
||||||
|
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 = ?`, [id, id]);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
};
|
|
@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -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];
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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>
|
|
@ -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', {
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('payBack', {
|
Self.remoteMethodCtx('payBack', {
|
||||||
description: 'Create ticket with the selected lines changing the sign to the quantites',
|
description: 'Create ticket with the selected lines changing the sign to the quantites',
|
||||||
|
@ -39,6 +41,15 @@ module.exports = Self => {
|
||||||
try {
|
try {
|
||||||
const salesIds = [];
|
const salesIds = [];
|
||||||
const params = [];
|
const params = [];
|
||||||
|
const userId = ctx.req.accessToken.userId;
|
||||||
|
|
||||||
|
const isClaimManager = await Self.app.models.Account.hasRole(userId, 'claimManager');
|
||||||
|
const isSalesAssistant = await Self.app.models.Account.hasRole(userId, 'salesAssistant');
|
||||||
|
const hasValidRole = isClaimManager || isSalesAssistant;
|
||||||
|
|
||||||
|
if (!hasValidRole)
|
||||||
|
throw new UserError(`You don't have privileges to create pay back`);
|
||||||
|
|
||||||
sales.forEach(sale => {
|
sales.forEach(sale => {
|
||||||
salesIds.push(sale.id);
|
salesIds.push(sale.id);
|
||||||
params.push('?');
|
params.push('?');
|
||||||
|
|
|
@ -3,15 +3,17 @@ const models = require('vn-loopback/server/server').models;
|
||||||
describe('sale payBack()', () => {
|
describe('sale payBack()', () => {
|
||||||
it('should create ticket with the selected lines changing the sign to the quantites', async() => {
|
it('should create ticket with the selected lines changing the sign to the quantites', async() => {
|
||||||
const tx = await models.Sale.beginTransaction({});
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
const ctx = {req: {accessToken: {userId: 9}}};
|
||||||
|
|
||||||
const ticketId = 11;
|
const ticketId = 11;
|
||||||
const sales = [
|
const sales = [
|
||||||
{id: 7, ticketFk: 11},
|
{id: 7, ticketFk: 11},
|
||||||
{id: 8, ticketFk: 11}
|
{id: 8, ticketFk: 11}
|
||||||
];
|
];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const ctx = {req: {accessToken: {userId: 9}}};
|
|
||||||
const response = await models.Sale.payBack(ctx, sales, ticketId, options);
|
const response = await models.Sale.payBack(ctx, sales, ticketId, options);
|
||||||
const [newTicketId] = await models.Sale.rawSql('SELECT MAX(t.id) id FROM vn.ticket t;', null, options);
|
const [newTicketId] = await models.Sale.rawSql('SELECT MAX(t.id) id FROM vn.ticket t;', null, options);
|
||||||
|
|
||||||
|
@ -23,4 +25,30 @@ describe('sale payBack()', () => {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(`should throw an error if the user doesn't have privileges to create a pay back`, async() => {
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
const ctx = {req: {accessToken: {userId: 1}}};
|
||||||
|
|
||||||
|
const ticketId = 11;
|
||||||
|
const sales = [
|
||||||
|
{id: 7, ticketFk: 11}
|
||||||
|
];
|
||||||
|
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
await models.Sale.payBack(ctx, sales, ticketId, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toEqual(`You don't have privileges to create pay back`);
|
||||||
|
});
|
||||||
});
|
});
|
|
@ -492,7 +492,9 @@
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item translate
|
<vn-item translate
|
||||||
name="payBack"
|
name="payBack"
|
||||||
ng-click="$ctrl.createPayBack()">
|
ng-click="$ctrl.createPayBack()"
|
||||||
|
vn-acl="claimManager, salesAssistant"
|
||||||
|
vn-acl-action="remove">
|
||||||
Pay Back
|
Pay Back
|
||||||
</vn-item>
|
</vn-item>
|
||||||
</vn-menu>
|
</vn-menu>
|
|
@ -10,64 +10,68 @@ module.exports = Self => {
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
arg: 'filter',
|
arg: 'filter',
|
||||||
type: 'Object',
|
type: 'object',
|
||||||
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
}, {
|
||||||
arg: 'search',
|
arg: 'search',
|
||||||
type: 'String',
|
type: 'string',
|
||||||
description: 'Searchs the travel by id',
|
description: 'Searchs the travel by id',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
}, {
|
||||||
arg: 'id',
|
arg: 'id',
|
||||||
type: 'Integer',
|
type: 'integer',
|
||||||
description: 'The travel id',
|
description: 'The travel id',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
}, {
|
||||||
arg: 'shippedFrom',
|
arg: 'shippedFrom',
|
||||||
type: 'Date',
|
type: 'date',
|
||||||
description: 'The shipped from date filter',
|
description: 'The shipped from date filter',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
}, {
|
||||||
arg: 'shippedTo',
|
arg: 'shippedTo',
|
||||||
type: 'Date',
|
type: 'date',
|
||||||
description: 'The shipped to date filter',
|
description: 'The shipped to date filter',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
}, {
|
||||||
arg: 'landedFrom',
|
arg: 'landedFrom',
|
||||||
type: 'Date',
|
type: 'date',
|
||||||
description: 'The landed from date filter',
|
description: 'The landed from date filter',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
}, {
|
||||||
arg: 'landedTo',
|
arg: 'landedTo',
|
||||||
type: 'Date',
|
type: 'date',
|
||||||
description: 'The landed to date filter',
|
description: 'The landed to date filter',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
}, {
|
||||||
arg: 'agencyFk',
|
arg: 'agencyFk',
|
||||||
type: 'Number',
|
type: 'number',
|
||||||
description: 'The agencyModeFk id',
|
description: 'The agencyModeFk id',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
}, {
|
||||||
arg: 'warehouseOutFk',
|
arg: 'warehouseOutFk',
|
||||||
type: 'Number',
|
type: 'number',
|
||||||
description: 'The warehouseOutFk filter',
|
description: 'The warehouseOutFk filter',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
}, {
|
||||||
arg: 'warehouseInFk',
|
arg: 'warehouseInFk',
|
||||||
type: 'Number',
|
type: 'number',
|
||||||
description: 'The warehouseInFk filter',
|
description: 'The warehouseInFk filter',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
}, {
|
||||||
arg: 'totalEntries',
|
arg: 'totalEntries',
|
||||||
type: 'Number',
|
type: 'number',
|
||||||
description: 'The totalEntries filter',
|
description: 'The totalEntries filter',
|
||||||
http: {source: 'query'}
|
http: {source: 'query'}
|
||||||
}, {
|
}, {
|
||||||
arg: 'ref',
|
arg: 'ref',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The reference'
|
description: 'The reference'
|
||||||
}
|
}, {
|
||||||
|
arg: 'continent',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The continent code'
|
||||||
|
},
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
type: ['Object'],
|
type: ['Object'],
|
||||||
|
@ -102,6 +106,7 @@ module.exports = Self => {
|
||||||
case 'warehouseOutFk':
|
case 'warehouseOutFk':
|
||||||
case 'warehouseInFk':
|
case 'warehouseInFk':
|
||||||
case 'totalEntries':
|
case 'totalEntries':
|
||||||
|
case 'continent':
|
||||||
param = `t.${param}`;
|
param = `t.${param}`;
|
||||||
return {[param]: value};
|
return {[param]: value};
|
||||||
}
|
}
|
||||||
|
@ -129,11 +134,15 @@ module.exports = Self => {
|
||||||
t.totalEntries,
|
t.totalEntries,
|
||||||
am.name agencyModeName,
|
am.name agencyModeName,
|
||||||
win.name warehouseInName,
|
win.name warehouseInName,
|
||||||
wout.name warehouseOutName
|
wout.name warehouseOutName,
|
||||||
|
cnt.code continent
|
||||||
FROM vn.travel t
|
FROM vn.travel t
|
||||||
JOIN vn.agencyMode am ON am.id = t.agencyFk
|
JOIN vn.agencyMode am ON am.id = t.agencyFk
|
||||||
JOIN vn.warehouse win ON win.id = t.warehouseInFk
|
JOIN vn.warehouse win ON win.id = t.warehouseInFk
|
||||||
JOIN vn.warehouse wout ON wout.id = t.warehouseOutFk) AS t`
|
JOIN vn.warehouse wout ON wout.id = t.warehouseOutFk
|
||||||
|
JOIN warehouse wo ON wo.id = t.warehouseOutFk
|
||||||
|
JOIN country c ON c.id = wo.countryFk
|
||||||
|
LEFT JOIN continent cnt ON cnt.id = c.continentFk) AS t`
|
||||||
);
|
);
|
||||||
|
|
||||||
stmt.merge(conn.makeSuffix(filter));
|
stmt.merge(conn.makeSuffix(filter));
|
||||||
|
|
|
@ -70,4 +70,16 @@ describe('Travel filter()', () => {
|
||||||
|
|
||||||
expect(result.length).toEqual(1);
|
expect(result.length).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return the travel matching "continent"', async() => {
|
||||||
|
const ctx = {
|
||||||
|
args: {
|
||||||
|
continent: 'EU',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await app.models.Travel.filter(ctx);
|
||||||
|
|
||||||
|
expect(result.length).toEqual(5);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -93,6 +93,15 @@
|
||||||
value-field="id">
|
value-field="id">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
<vn-horizontal class="vn-px-lg">
|
||||||
|
<vn-autocomplete vn-one
|
||||||
|
label="Continent Out"
|
||||||
|
ng-model="filter.continent"
|
||||||
|
url="Continents"
|
||||||
|
show-field="name"
|
||||||
|
value-field="code">
|
||||||
|
</vn-autocomplete>
|
||||||
|
</vn-horizontal>
|
||||||
<vn-horizontal class="vn-px-lg vn-pb-lg vn-mt-lg">
|
<vn-horizontal class="vn-px-lg vn-pb-lg vn-mt-lg">
|
||||||
<vn-submit label="Search"></vn-submit>
|
<vn-submit label="Search"></vn-submit>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Delivered"
|
label="Delivered"
|
||||||
value="{{$ctrl.travelData.isDelivered}}"
|
ng-model="$ctrl.travelData.isDelivered"
|
||||||
disabled="true">
|
disabled="true">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Received"
|
label="Received"
|
||||||
value="{{$ctrl.travelData.isReceived}}"
|
ng-model="$ctrl.travelData.isReceived"
|
||||||
disabled="true">
|
disabled="true">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue