6745-2404_testToMaster #1950
|
@ -16,6 +16,7 @@
|
||||||
},
|
},
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"salix",
|
"salix",
|
||||||
"fdescribe"
|
"fdescribe",
|
||||||
|
"Loggable"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [2404.01] - 2024-01-25
|
||||||
|
|
||||||
|
### Added
|
||||||
|
### Changed
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
|
||||||
## [2402.01] - 2024-01-11
|
## [2402.01] - 2024-01-11
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -68,7 +68,7 @@ module.exports = Self => {
|
||||||
userToUpdate.hasGrant = hasGrant;
|
userToUpdate.hasGrant = hasGrant;
|
||||||
|
|
||||||
if (roleFk) {
|
if (roleFk) {
|
||||||
const role = await models.Role.findById(roleFk, {fields: ['name']}, myOptions);
|
const role = await models.VnRole.findById(roleFk, {fields: ['name']}, myOptions);
|
||||||
const hasRole = await Self.hasRole(userId, role.name, myOptions);
|
const hasRole = await Self.hasRole(userId, role.name, myOptions);
|
||||||
|
|
||||||
if (!hasRole)
|
if (!hasRole)
|
||||||
|
|
|
@ -70,7 +70,7 @@ describe('VnUser privileges()', () => {
|
||||||
const tx = await models.VnUser.beginTransaction({});
|
const tx = await models.VnUser.beginTransaction({});
|
||||||
|
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
const agency = await models.Role.findOne({
|
const agency = await models.VnRole.findOne({
|
||||||
where: {
|
where: {
|
||||||
name: 'agency'
|
name: 'agency'
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,9 +139,6 @@
|
||||||
"Warehouse": {
|
"Warehouse": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
"VnUser": {
|
|
||||||
"dataSource": "vn"
|
|
||||||
},
|
|
||||||
"OsTicket": {
|
"OsTicket": {
|
||||||
"dataSource": "osticket"
|
"dataSource": "osticket"
|
||||||
},
|
},
|
||||||
|
@ -156,6 +153,12 @@
|
||||||
},
|
},
|
||||||
"ViaexpressConfig": {
|
"ViaexpressConfig": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"VnUser": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"VnRole": {
|
||||||
|
"dataSource": "vn"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,12 +29,12 @@
|
||||||
"relations": {
|
"relations": {
|
||||||
"readRole": {
|
"readRole": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Role",
|
"model": "VnRole",
|
||||||
"foreignKey": "readRoleFk"
|
"foreignKey": "readRoleFk"
|
||||||
},
|
},
|
||||||
"writeRole": {
|
"writeRole": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Role",
|
"model": "VnRole",
|
||||||
"foreignKey": "writeRoleFk"
|
"foreignKey": "writeRoleFk"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -46,12 +46,12 @@
|
||||||
},
|
},
|
||||||
"readRole": {
|
"readRole": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Role",
|
"model": "VnRole",
|
||||||
"foreignKey": "readRoleFk"
|
"foreignKey": "readRoleFk"
|
||||||
},
|
},
|
||||||
"writeRole": {
|
"writeRole": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Role",
|
"model": "VnRole",
|
||||||
"foreignKey": "writeRoleFk"
|
"foreignKey": "writeRoleFk"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -64,4 +64,3 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,8 @@
|
||||||
},
|
},
|
||||||
"role": {
|
"role": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Role",
|
"model": "VnRole",
|
||||||
"foreignKey": "roleFk"
|
"foreignKey": "roleFk"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"name": "VnRole",
|
||||||
|
"base": "Role",
|
||||||
|
"validateUpsert": true,
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "account.role"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,9 @@
|
||||||
"table": "account.user"
|
"table": "account.user"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"resetPasswordTokenTTL": "604800",
|
"resetPasswordTokenTTL": "604800",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
|
@ -63,7 +66,7 @@
|
||||||
"relations": {
|
"relations": {
|
||||||
"role": {
|
"role": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Role",
|
"model": "VnRole",
|
||||||
"foreignKey": "roleFk"
|
"foreignKey": "roleFk"
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) VALUES
|
||||||
|
('VnRole','*','READ','ALLOW','ROLE','employee'),
|
||||||
|
('VnRole','*','WRITE','ALLOW','ROLE','it');
|
||||||
|
|
||||||
|
DELETE FROM`salix`.`ACL` WHERE model='Role';
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
ALTER TABLE `vn`.`invoiceCorrection` DROP FOREIGN KEY `cplusInvoiceTyoeFk`;
|
||||||
|
ALTER TABLE `vn`.`invoiceCorrection` DROP FOREIGN KEY `invoiceCorrectionType_Fk33`;
|
||||||
|
ALTER TABLE `vn`.`invoiceCorrection` DROP FOREIGN KEY `invoiceCorrection_ibfk_1`;
|
||||||
|
|
||||||
|
ALTER TABLE `vn`.`invoiceCorrection` ADD CONSTRAINT `siiTypeInvoiceOut_FK` FOREIGN KEY (`siiTypeInvoiceOutFk`) REFERENCES `vn`.`siiTypeInvoiceOut`(id) ON UPDATE CASCADE;
|
||||||
|
ALTER TABLE `vn`.`invoiceCorrection` ADD CONSTRAINT `invoiceCorrectionType_FK` FOREIGN KEY (`invoiceCorrectionTypeFk`) REFERENCES `vn`.`invoiceCorrectionType`(id) ON UPDATE CASCADE;
|
||||||
|
ALTER TABLE `vn`.`invoiceCorrection` ADD CONSTRAINT `cplusRectificationType_FK` FOREIGN KEY (`cplusRectificationTypeFk`) REFERENCES `vn`.`cplusRectificationType`(id) ON UPDATE CASCADE;
|
|
@ -0,0 +1,33 @@
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`getTaxBases`()
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Calcula y devuelve en número de bases imponibles postivas y negativas
|
||||||
|
* Requiere la tabla temporal tmp.ticketToInvoice(id)
|
||||||
|
*
|
||||||
|
* returns tmp.taxBases
|
||||||
|
*/
|
||||||
|
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.ticket
|
||||||
|
(KEY (ticketFk))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT id ticketFk
|
||||||
|
FROM tmp.ticketToInvoice;
|
||||||
|
|
||||||
|
CALL ticket_getTax(NULL);
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.taxBases;
|
||||||
|
CREATE TEMPORARY TABLE tmp.taxBases
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT
|
||||||
|
SUM(taxableBase > 0) as positive,
|
||||||
|
SUM(taxableBase < 0) as negative
|
||||||
|
FROM(
|
||||||
|
SELECT SUM(taxableBase) taxableBase
|
||||||
|
FROM tmp.ticketTax
|
||||||
|
GROUP BY pgcFk
|
||||||
|
) t;
|
||||||
|
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -0,0 +1,30 @@
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn`.`hasAnyPositiveBase`() RETURNS tinyint(1)
|
||||||
|
DETERMINISTIC
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calcula si existe alguna base imponible positiva
|
||||||
|
* Requiere la tabla temporal tmp.ticketToInvoice(id) para getTaxBases()
|
||||||
|
*
|
||||||
|
* returns BOOLEAN
|
||||||
|
*/
|
||||||
|
|
||||||
|
DECLARE hasAnyPositiveBase BOOLEAN;
|
||||||
|
|
||||||
|
CALL getTaxBases();
|
||||||
|
|
||||||
|
SELECT positive INTO hasAnyPositiveBase
|
||||||
|
FROM tmp.taxBases
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE
|
||||||
|
tmp.ticketTax,
|
||||||
|
tmp.ticket,
|
||||||
|
tmp.taxBases;
|
||||||
|
|
||||||
|
RETURN hasAnyPositiveBase;
|
||||||
|
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -0,0 +1,32 @@
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn`.`hasAnyNegativeBase`() RETURNS tinyint(1)
|
||||||
|
DETERMINISTIC
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calcula si existe alguna base imponible negativa
|
||||||
|
* Requiere la tabla temporal tmp.ticketToInvoice(id) para getTaxBases()
|
||||||
|
*
|
||||||
|
* returns BOOLEAN
|
||||||
|
*/
|
||||||
|
|
||||||
|
DECLARE hasAnyNegativeBase BOOLEAN;
|
||||||
|
|
||||||
|
CALL getTaxBases();
|
||||||
|
|
||||||
|
SELECT negative INTO hasAnyNegativeBase
|
||||||
|
FROM tmp.taxBases
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE
|
||||||
|
tmp.ticketTax,
|
||||||
|
tmp.ticket,
|
||||||
|
tmp.taxBases;
|
||||||
|
|
||||||
|
RETURN hasAnyNegativeBase;
|
||||||
|
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
||||||
|
|
||||||
|
|
|
@ -602,18 +602,19 @@ INSERT INTO `vn`.`taxArea` (`code`, `claveOperacionFactura`, `CodigoTransaccion`
|
||||||
|
|
||||||
INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaFk`, `isCEE`, `type`)
|
INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaFk`, `isCEE`, `type`)
|
||||||
VALUES
|
VALUES
|
||||||
('A', 'Global nacional', 1, 'NATIONAL', 0, 'global'),
|
('A', 'Global nacional', 1, 'NATIONAL', 0, 'global'),
|
||||||
('T', 'Española rapida', 1, 'NATIONAL', 0, 'quick'),
|
('T', 'Española rapida', 1, 'NATIONAL', 0, 'quick'),
|
||||||
('V', 'Intracomunitaria global', 0, 'CEE', 1, 'global'),
|
('V', 'Intracomunitaria global', 0, 'CEE', 1, 'global'),
|
||||||
('M', 'Múltiple nacional', 1, 'NATIONAL', 0, 'quick'),
|
('M', 'Múltiple nacional', 1, 'NATIONAL', 0, 'quick'),
|
||||||
('E', 'Exportación rápida', 0, 'WORLD', 0, 'quick');
|
('R', 'Rectificativa', 1, 'NATIONAL', 0, NULL),
|
||||||
|
('E', 'Exportación rápida', 0, 'WORLD', 0, 'quick');
|
||||||
|
|
||||||
INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `hasPdf`)
|
INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `hasPdf`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'T', 1026.24, util.VN_CURDATE(), 1101, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
(1, 'T', 1026.24, util.VN_CURDATE(), 1101, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
||||||
(2, 'T', 121.36, util.VN_CURDATE(), 1102, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
(2, 'T', 121.36, util.VN_CURDATE(), 1102, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
||||||
(3, 'T', 8.88, util.VN_CURDATE(), 1103, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
(3, 'T', 8.88, util.VN_CURDATE(), 1103, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
||||||
(4, 'T', 8.88, util.VN_CURDATE(), 1103, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
(4, 'T', 8.88, util.VN_CURDATE(), 1104, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
||||||
(5, 'A', 8.88, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1103, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 442, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1, 0);
|
(5, 'A', 8.88, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1103, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 442, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1, 0);
|
||||||
|
|
||||||
UPDATE `vn`.`invoiceOut` SET ref = 'T1111111' WHERE id = 1;
|
UPDATE `vn`.`invoiceOut` SET ref = 'T1111111' WHERE id = 1;
|
||||||
|
@ -719,7 +720,7 @@ INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agen
|
||||||
INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`, `weight`)
|
INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`, `weight`)
|
||||||
VALUES
|
VALUES
|
||||||
(1 , 3, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 121, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1),
|
(1 , 3, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 121, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1),
|
||||||
(2 , 1, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 2),
|
(2 , 1, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 1, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 2),
|
||||||
(3 , 1, 7, 1, 6, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), NULL),
|
(3 , 1, 7, 1, 6, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), NULL),
|
||||||
(4 , 3, 2, 1, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 9, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), NULL),
|
(4 , 3, 2, 1, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 9, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), NULL),
|
||||||
(5 , 3, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), NULL),
|
(5 , 3, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), NULL),
|
||||||
|
|
|
@ -35,7 +35,7 @@ describe('Ticket index payout path', () => {
|
||||||
await page.waitToClick(selectors.ticketsIndex.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketsIndex.openAdvancedSearchButton);
|
||||||
await page.write(selectors.ticketsIndex.advancedSearchClient, '1101');
|
await page.write(selectors.ticketsIndex.advancedSearchClient, '1101');
|
||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
await page.waitForNumberOfElements(selectors.ticketsIndex.anySearchResult, 9);
|
await page.waitForNumberOfElements(selectors.ticketsIndex.anySearchResult, 10);
|
||||||
await page.waitToClick(selectors.ticketsIndex.firstTicketCheckbox);
|
await page.waitToClick(selectors.ticketsIndex.firstTicketCheckbox);
|
||||||
await page.waitToClick(selectors.ticketsIndex.secondTicketCheckbox);
|
await page.waitToClick(selectors.ticketsIndex.secondTicketCheckbox);
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ describe('InvoiceOut summary path', () => {
|
||||||
|
|
||||||
it('should contain the tax breakdown', async() => {
|
it('should contain the tax breakdown', async() => {
|
||||||
const firstTax = await page.waitToGetProperty(selectors.invoiceOutSummary.taxOne, 'innerText');
|
const firstTax = await page.waitToGetProperty(selectors.invoiceOutSummary.taxOne, 'innerText');
|
||||||
|
|
||||||
const secondTax = await page.waitToGetProperty(selectors.invoiceOutSummary.taxTwo, 'innerText');
|
const secondTax = await page.waitToGetProperty(selectors.invoiceOutSummary.taxTwo, 'innerText');
|
||||||
|
|
||||||
expect(firstTax).toContain('10%');
|
expect(firstTax).toContain('10%');
|
||||||
|
@ -37,10 +36,9 @@ describe('InvoiceOut summary path', () => {
|
||||||
|
|
||||||
it('should contain the tickets info', async() => {
|
it('should contain the tickets info', async() => {
|
||||||
const firstTicket = await page.waitToGetProperty(selectors.invoiceOutSummary.ticketOne, 'innerText');
|
const firstTicket = await page.waitToGetProperty(selectors.invoiceOutSummary.ticketOne, 'innerText');
|
||||||
|
|
||||||
const secondTicket = await page.waitToGetProperty(selectors.invoiceOutSummary.ticketTwo, 'innerText');
|
const secondTicket = await page.waitToGetProperty(selectors.invoiceOutSummary.ticketTwo, 'innerText');
|
||||||
|
|
||||||
expect(firstTicket).toContain('Bat cave');
|
expect(firstTicket).toContain('Bat cave');
|
||||||
expect(secondTicket).toContain('Stark tower');
|
expect(secondTicket).toContain('Bat cave');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,7 +23,6 @@ export function directive($translate, $window) {
|
||||||
let rule = $attrs.rule.split('.');
|
let rule = $attrs.rule.split('.');
|
||||||
let modelName = rule.shift();
|
let modelName = rule.shift();
|
||||||
let fieldName = rule.shift();
|
let fieldName = rule.shift();
|
||||||
|
|
||||||
let split = $attrs.ngModel.split('.');
|
let split = $attrs.ngModel.split('.');
|
||||||
if (!fieldName) fieldName = split.pop() || null;
|
if (!fieldName) fieldName = split.pop() || null;
|
||||||
if (!modelName) modelName = firstUpper(split.pop() || '');
|
if (!modelName) modelName = firstUpper(split.pop() || '');
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
async function handleObserve(ctx) {
|
||||||
|
ctx.options.httpCtx = LoopBackContext.getCurrentContext();
|
||||||
|
}
|
||||||
|
module.exports = function(Self) {
|
||||||
|
let Mixin = {
|
||||||
|
'before save': handleObserve,
|
||||||
|
'before delete': handleObserve,
|
||||||
|
};
|
||||||
|
for (const [listener, handler] of Object.entries(Mixin))
|
||||||
|
Self.observe(listener, handler);
|
||||||
|
};
|
|
@ -1,15 +0,0 @@
|
||||||
const LoopBackContext = require('loopback-context');
|
|
||||||
|
|
||||||
module.exports = function(Self) {
|
|
||||||
Self.setup = function() {
|
|
||||||
Self.super_.setup.call(this);
|
|
||||||
};
|
|
||||||
|
|
||||||
Self.observe('before save', async function(ctx) {
|
|
||||||
ctx.options.httpCtx = LoopBackContext.getCurrentContext();
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.observe('before delete', async function(ctx) {
|
|
||||||
ctx.options.httpCtx = LoopBackContext.getCurrentContext();
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"name": "Loggable",
|
|
||||||
"base": "VnModel",
|
|
||||||
"validateUpsert": true
|
|
||||||
}
|
|
|
@ -45,6 +45,7 @@
|
||||||
"Extension format is invalid": "Extension format is invalid",
|
"Extension format is invalid": "Extension format is invalid",
|
||||||
"NO_ZONE_FOR_THIS_PARAMETERS": "NO_ZONE_FOR_THIS_PARAMETERS",
|
"NO_ZONE_FOR_THIS_PARAMETERS": "NO_ZONE_FOR_THIS_PARAMETERS",
|
||||||
"This client can't be invoiced": "This client can't be invoiced",
|
"This client can't be invoiced": "This client can't be invoiced",
|
||||||
|
"You must provide the correction information to generate a corrective invoice": "You must provide the correction information to generate a corrective invoice",
|
||||||
"The introduced hour already exists": "The introduced hour already exists",
|
"The introduced hour already exists": "The introduced hour already exists",
|
||||||
"Invalid parameters to create a new ticket": "Invalid parameters to create a new ticket",
|
"Invalid parameters to create a new ticket": "Invalid parameters to create a new ticket",
|
||||||
"Concept cannot be blank": "Concept cannot be blank",
|
"Concept cannot be blank": "Concept cannot be blank",
|
||||||
|
@ -178,7 +179,8 @@
|
||||||
"The renew period has not been exceeded": "The renew period has not been exceeded",
|
"The renew period has not been exceeded": "The renew period has not been exceeded",
|
||||||
"You can not use the same password": "You can not use the same password",
|
"You can not use the same password": "You can not use the same password",
|
||||||
"Valid priorities": "Valid priorities: %d",
|
"Valid priorities": "Valid priorities: %d",
|
||||||
"Negative basis of tickets": "Negative basis of tickets: {{ticketsIds}}",
|
"hasAnyNegativeBase": "Negative basis of tickets: {{ticketsIds}}",
|
||||||
|
"hasAnyPositiveBase": "Positive basis of tickets: {{ticketsIds}}",
|
||||||
"This ticket cannot be left empty.": "This ticket cannot be left empty. %s",
|
"This ticket cannot be left empty.": "This ticket cannot be left empty. %s",
|
||||||
"Social name should be uppercase": "Social name should be uppercase",
|
"Social name should be uppercase": "Social name should be uppercase",
|
||||||
"Street should be uppercase": "Street should be uppercase",
|
"Street should be uppercase": "Street should be uppercase",
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
"The secret can't be blank": "La contraseña no puede estar en blanco",
|
"The secret can't be blank": "La contraseña no puede estar en blanco",
|
||||||
"We weren't able to send this SMS": "No hemos podido enviar el SMS",
|
"We weren't able to send this SMS": "No hemos podido enviar el SMS",
|
||||||
"This client can't be invoiced": "Este cliente no puede ser facturado",
|
"This client can't be invoiced": "Este cliente no puede ser facturado",
|
||||||
|
"You must provide the correction information to generate a corrective invoice": "Debes informar la información de corrección para generar una factura rectificativa",
|
||||||
"This ticket can't be invoiced": "Este ticket no puede ser facturado",
|
"This ticket can't be invoiced": "Este ticket no puede ser facturado",
|
||||||
"You cannot add or modify services to an invoiced ticket": "No puedes añadir o modificar servicios a un ticket facturado",
|
"You cannot add or modify services to an invoiced ticket": "No puedes añadir o modificar servicios a un ticket facturado",
|
||||||
"This ticket can not be modified": "Este ticket no puede ser modificado",
|
"This ticket can not be modified": "Este ticket no puede ser modificado",
|
||||||
|
@ -306,7 +307,8 @@
|
||||||
"Mail not sent": "Se ha producido un fallo al enviar la factura al cliente [{{clientId}}]({{{clientUrl}}}), por favor revisa la dirección de correo electrónico",
|
"Mail not sent": "Se ha producido un fallo al enviar la factura al cliente [{{clientId}}]({{{clientUrl}}}), por favor revisa la dirección de correo electrónico",
|
||||||
"The renew period has not been exceeded": "El periodo de renovación no ha sido superado",
|
"The renew period has not been exceeded": "El periodo de renovación no ha sido superado",
|
||||||
"Valid priorities": "Prioridades válidas: %d",
|
"Valid priorities": "Prioridades válidas: %d",
|
||||||
"Negative basis of tickets": "Base negativa para los tickets: {{ticketsIds}}",
|
"hasAnyNegativeBase": "Base negativa para los tickets: {{ticketsIds}}",
|
||||||
|
"hasAnyPositiveBase": "Base positivas para los tickets: {{ticketsIds}}",
|
||||||
"You cannot assign an alias that you are not assigned to": "No puede asignar un alias que no tenga asignado",
|
"You cannot assign an alias that you are not assigned to": "No puede asignar un alias que no tenga asignado",
|
||||||
"This ticket cannot be left empty.": "Este ticket no se puede dejar vacío. %s",
|
"This ticket cannot be left empty.": "Este ticket no se puede dejar vacío. %s",
|
||||||
"The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias",
|
"The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias",
|
||||||
|
@ -334,5 +336,6 @@
|
||||||
"This user does not have an assigned tablet": "Este usuario no tiene tablet asignada",
|
"This user does not have an assigned tablet": "Este usuario no tiene tablet asignada",
|
||||||
"Incorrect pin": "Pin incorrecto.",
|
"Incorrect pin": "Pin incorrecto.",
|
||||||
"You already have the mailAlias": "Ya tienes este alias de correo",
|
"You already have the mailAlias": "Ya tienes este alias de correo",
|
||||||
"The alias cant be modified": "Este alias de correo no puede ser modificado"
|
"The alias cant be modified": "Este alias de correo no puede ser modificado",
|
||||||
}
|
"No tickets to invoice": "No hay tickets para facturar"
|
||||||
|
}
|
||||||
|
|
|
@ -25,20 +25,19 @@
|
||||||
"FieldAcl": {
|
"FieldAcl": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
"Role": {
|
|
||||||
"dataSource": "vn",
|
|
||||||
"options": {
|
|
||||||
"mysql": {
|
|
||||||
"table": "salix.Role"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"RoleMapping": {
|
"RoleMapping": {
|
||||||
"dataSource": "vn",
|
"dataSource": "vn",
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "salix.RoleMapping"
|
"table": "salix.RoleMapping"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"role": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "VnRole",
|
||||||
|
"foreignKey": "roleId"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Schema": {
|
"Schema": {
|
||||||
|
|
|
@ -1,49 +1,49 @@
|
||||||
{
|
{
|
||||||
"name": "Account",
|
"name": "Account",
|
||||||
"base": "VnModel",
|
"base": "VnModel",
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "account.account"
|
"table": "account.account"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
"id": true
|
"id": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
"user": {
|
"user": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "VnUser",
|
"model": "VnUser",
|
||||||
"foreignKey": "id"
|
"foreignKey": "id"
|
||||||
},
|
|
||||||
"aliases": {
|
|
||||||
"type": "hasMany",
|
|
||||||
"model": "MailAliasAccount",
|
|
||||||
"foreignKey": "account"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"acls": [
|
|
||||||
{
|
|
||||||
"property": "login",
|
|
||||||
"accessType": "EXECUTE",
|
|
||||||
"principalType": "ROLE",
|
|
||||||
"principalId": "$everyone",
|
|
||||||
"permission": "ALLOW"
|
|
||||||
},
|
},
|
||||||
{
|
"aliases": {
|
||||||
|
"type": "hasMany",
|
||||||
|
"model": "MailAliasAccount",
|
||||||
|
"foreignKey": "account"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"acls": [
|
||||||
|
{
|
||||||
|
"property": "login",
|
||||||
|
"accessType": "EXECUTE",
|
||||||
|
"principalType": "ROLE",
|
||||||
|
"principalId": "$everyone",
|
||||||
|
"permission": "ALLOW"
|
||||||
|
},
|
||||||
|
{
|
||||||
"property": "logout",
|
"property": "logout",
|
||||||
"accessType": "EXECUTE",
|
"accessType": "EXECUTE",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$authenticated",
|
"principalId": "$authenticated",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"property": "changePassword",
|
"property": "changePassword",
|
||||||
"accessType": "EXECUTE",
|
"accessType": "EXECUTE",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$everyone",
|
"principalId": "$everyone",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,7 +239,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
// Prepare data
|
// Prepare data
|
||||||
|
|
||||||
let roles = await $.Role.find({
|
let roles = await $.VnRole.find({
|
||||||
fields: ['id', 'name', 'description']
|
fields: ['id', 'name', 'description']
|
||||||
});
|
});
|
||||||
let roleRoles = await $.RoleRole.find({
|
let roleRoles = await $.RoleRole.find({
|
||||||
|
|
|
@ -15,12 +15,12 @@
|
||||||
"relations": {
|
"relations": {
|
||||||
"owner": {
|
"owner": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Role",
|
"model": "VnRole",
|
||||||
"foreignKey": "role"
|
"foreignKey": "role"
|
||||||
},
|
},
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Role",
|
"model": "VnRole",
|
||||||
"foreignKey": "inheritsFrom"
|
"foreignKey": "inheritsFrom"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,12 @@
|
||||||
"relations": {
|
"relations": {
|
||||||
"owner": {
|
"owner": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Role",
|
"model": "VnRole",
|
||||||
"foreignKey": "role"
|
"foreignKey": "role"
|
||||||
},
|
},
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Role",
|
"model": "VnRole",
|
||||||
"foreignKey": "inheritsFrom"
|
"foreignKey": "inheritsFrom"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
label="Role"
|
label="Role"
|
||||||
ng-model="$ctrl.acl.principalId"
|
ng-model="$ctrl.acl.principalId"
|
||||||
url="Roles"
|
url="VnRoles"
|
||||||
id-field="name"
|
id-field="name"
|
||||||
value-field="name"
|
value-field="name"
|
||||||
vn-focus>
|
vn-focus>
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="Property"
|
label="Property"
|
||||||
ng-model="$ctrl.acl.property"
|
ng-model="$ctrl.acl.property"
|
||||||
info="Use * to match all properties">
|
info="Use * to match all properties">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
label="Role"
|
label="Role"
|
||||||
ng-model="filter.principalId"
|
ng-model="filter.principalId"
|
||||||
url="Roles"
|
url="VnRoles"
|
||||||
value-field="name">
|
value-field="name">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
value-field="name">
|
value-field="name">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="Property"
|
label="Property"
|
||||||
ng-model="filter.property">
|
ng-model="filter.property">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
|
@ -36,4 +36,4 @@
|
||||||
</vn-submit>
|
</vn-submit>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
label="Role"
|
label="Role"
|
||||||
ng-model="$ctrl.user.roleFk"
|
ng-model="$ctrl.user.roleFk"
|
||||||
url="Roles"
|
url="VnRoles"
|
||||||
rule="VnUser">
|
rule="VnUser">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
label="Role"
|
label="Role"
|
||||||
ng-model="$ctrl.user.roleFk"
|
ng-model="$ctrl.user.roleFk"
|
||||||
url="Roles">
|
url="VnRoles">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
|
|
@ -1,25 +1,27 @@
|
||||||
<vn-watcher
|
<vn-watcher
|
||||||
vn-id="watcher"
|
vn-id="watcher"
|
||||||
url="Roles"
|
url="VnRoles"
|
||||||
data="$ctrl.role"
|
data="$ctrl.role"
|
||||||
form="form">
|
form="form">
|
||||||
</vn-watcher>
|
</vn-watcher>
|
||||||
<form
|
<form
|
||||||
name="form"
|
name="form"
|
||||||
ng-submit="watcher.submit()"
|
ng-submit="watcher.submit()"
|
||||||
|
|
||||||
class="vn-w-md">
|
class="vn-w-md">
|
||||||
<vn-card class="vn-pa-lg">
|
<vn-card class="vn-pa-lg">
|
||||||
<vn-vertical>
|
<vn-vertical>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="Name"
|
label="Name"
|
||||||
ng-model="$ctrl.role.name"
|
ng-model="$ctrl.role.name"
|
||||||
rule
|
rule="VnRole.name"
|
||||||
vn-focus>
|
vn-focus>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="Description"
|
label="Description"
|
||||||
ng-model="$ctrl.role.description"
|
ng-model="$ctrl.role.description"
|
||||||
rule>
|
rule="VnRole.description"
|
||||||
|
>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
@ -35,4 +37,4 @@
|
||||||
ng-click="watcher.loadOriginalData()">
|
ng-click="watcher.loadOriginalData()">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
</vn-button-bar>
|
</vn-button-bar>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import ModuleCard from 'salix/components/module-card';
|
||||||
|
|
||||||
class Controller extends ModuleCard {
|
class Controller extends ModuleCard {
|
||||||
reload() {
|
reload() {
|
||||||
this.$http.get(`Roles/${this.$params.id}`)
|
this.$http.get(`VnRoles/${this.$params.id}`)
|
||||||
.then(res => this.role = res.data);
|
.then(res => this.role = res.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import './index';
|
import './index';
|
||||||
|
|
||||||
describe('component vnRoleCard', () => {
|
fdescribe('component vnRoleCard', () => {
|
||||||
let controller;
|
let controller;
|
||||||
let $httpBackend;
|
let $httpBackend;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ describe('component vnRoleCard', () => {
|
||||||
it('should reload the controller data', () => {
|
it('should reload the controller data', () => {
|
||||||
controller.$params.id = 1;
|
controller.$params.id = 1;
|
||||||
|
|
||||||
$httpBackend.expectGET('Roles/1').respond('foo');
|
$httpBackend.expectGET('VnRoles/1').respond('foo');
|
||||||
controller.reload();
|
controller.reload();
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<vn-watcher
|
<vn-watcher
|
||||||
vn-id="watcher"
|
vn-id="watcher"
|
||||||
url="Roles"
|
url="VnRoles"
|
||||||
data="$ctrl.role"
|
data="$ctrl.role"
|
||||||
insert-mode="true"
|
insert-mode="true"
|
||||||
form="form">
|
form="form">
|
||||||
|
@ -12,15 +12,15 @@
|
||||||
<vn-card class="vn-pa-lg">
|
<vn-card class="vn-pa-lg">
|
||||||
<vn-vertical>
|
<vn-vertical>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="Name"
|
label="Name"
|
||||||
ng-model="$ctrl.role.name"
|
ng-model="$ctrl.role.name"
|
||||||
rule
|
rule="VnRole.name"
|
||||||
vn-focus>
|
vn-focus>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="Description"
|
label="Description"
|
||||||
ng-model="$ctrl.role.description"
|
ng-model="$ctrl.role.description"
|
||||||
rule>
|
rule="VnRole.description">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
|
|
@ -24,4 +24,4 @@
|
||||||
on-accept="$ctrl.onDelete()"
|
on-accept="$ctrl.onDelete()"
|
||||||
question="Are you sure you want to continue?"
|
question="Are you sure you want to continue?"
|
||||||
message="Role will be removed">
|
message="Role will be removed">
|
||||||
</vn-confirm>
|
</vn-confirm>
|
||||||
|
|
|
@ -11,7 +11,7 @@ class Controller extends Descriptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
onDelete() {
|
onDelete() {
|
||||||
return this.$http.delete(`Roles/${this.id}`)
|
return this.$http.delete(`VnRoles/${this.id}`)
|
||||||
.then(() => this.$state.go('account.role'))
|
.then(() => this.$state.go('account.role'))
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('Role removed')));
|
.then(() => this.vnApp.showSuccess(this.$t('Role removed')));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import './index';
|
import './index';
|
||||||
|
|
||||||
describe('component vnRoleDescriptor', () => {
|
fdescribe('component vnRoleDescriptor', () => {
|
||||||
let controller;
|
let controller;
|
||||||
let $httpBackend;
|
let $httpBackend;
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ describe('component vnRoleDescriptor', () => {
|
||||||
controller.$state.go = jest.fn();
|
controller.$state.go = jest.fn();
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
|
||||||
$httpBackend.expectDELETE('Roles/1').respond();
|
$httpBackend.expectDELETE('VnRoles/1').respond();
|
||||||
controller.onDelete();
|
controller.onDelete();
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
vn-id="model"
|
vn-id="model"
|
||||||
url="Roles"
|
url="VnRoles"
|
||||||
filter="::$ctrl.filter"
|
filter="::$ctrl.filter"
|
||||||
limit="20">
|
limit="20">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
|
@ -15,4 +15,4 @@
|
||||||
</vn-portal>
|
</vn-portal>
|
||||||
<ui-view>
|
<ui-view>
|
||||||
<vn-role-index></vn-role-index>
|
<vn-role-index></vn-role-index>
|
||||||
</ui-view>
|
</ui-view>
|
||||||
|
|
|
@ -33,14 +33,14 @@
|
||||||
ng-click="$ctrl.onAddClick()"
|
ng-click="$ctrl.onAddClick()"
|
||||||
fixed-bottom-right>
|
fixed-bottom-right>
|
||||||
</vn-float-button>
|
</vn-float-button>
|
||||||
<vn-dialog
|
<vn-dialog
|
||||||
vn-id="dialog"
|
vn-id="dialog"
|
||||||
on-accept="$ctrl.onAddSave()">
|
on-accept="$ctrl.onAddSave()">
|
||||||
<tpl-body>
|
<tpl-body>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
label="Role"
|
label="Role"
|
||||||
ng-model="$ctrl.addData.inheritsFrom"
|
ng-model="$ctrl.addData.inheritsFrom"
|
||||||
url="Roles"
|
url="VnRoles"
|
||||||
vn-focus>
|
vn-focus>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</tpl-body>
|
</tpl-body>
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
<button response="accept" translate>Save</button>
|
<button response="accept" translate>Save</button>
|
||||||
</tpl-buttons>
|
</tpl-buttons>
|
||||||
</vn-dialog>
|
</vn-dialog>
|
||||||
<vn-confirm
|
<vn-confirm
|
||||||
vn-id="remove-confirm"
|
vn-id="remove-confirm"
|
||||||
message="Role will be removed"
|
message="Role will be removed"
|
||||||
question="Are you sure you want to continue?"
|
question="Are you sure you want to continue?"
|
||||||
|
|
|
@ -6,7 +6,7 @@ class Controller extends Component {
|
||||||
this._role = value;
|
this._role = value;
|
||||||
this.$.summary = null;
|
this.$.summary = null;
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
this.$http.get(`Roles/${value.id}`)
|
this.$http.get(`VnRoles/${value.id}`)
|
||||||
.then(res => this.$.summary = res.data);
|
.then(res => this.$.summary = res.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
vn-one
|
vn-one
|
||||||
label="Role"
|
label="Role"
|
||||||
ng-model="filter.roleFk"
|
ng-model="filter.roleFk"
|
||||||
url="Roles"
|
url="VnRoles"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
show-field="name">
|
show-field="name">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
|
@ -28,4 +28,4 @@
|
||||||
<vn-submit label="Search"></vn-submit>
|
<vn-submit label="Search"></vn-submit>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ClaimBeginning",
|
"name": "ClaimBeginning",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "claimBeginning"
|
"table": "claimBeginning"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ClaimDevelopment",
|
"name": "ClaimDevelopment",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "claimDevelopment"
|
"table": "claimDevelopment"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ClaimDms",
|
"name": "ClaimDms",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "claimDms"
|
"table": "claimDms"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ClaimEnd",
|
"name": "ClaimEnd",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "claimEnd"
|
"table": "claimEnd"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ClaimObservation",
|
"name": "ClaimObservation",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "claimObservation"
|
"table": "claimObservation"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ClaimState",
|
"name": "ClaimState",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "claimState"
|
"table": "claimState"
|
||||||
|
@ -32,7 +35,7 @@
|
||||||
"relations": {
|
"relations": {
|
||||||
"writeRole": {
|
"writeRole": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Role",
|
"model": "VnRole",
|
||||||
"foreignKey": "roleFk"
|
"foreignKey": "roleFk"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "Claim",
|
"name": "Claim",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "claim"
|
"table": "claim"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const UserError = require('vn-loopback/util/user-error');
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = function(Self) {
|
module.exports = function(Self) {
|
||||||
Self.remoteMethodCtx('canBeInvoiced', {
|
Self.remoteMethod('canBeInvoiced', {
|
||||||
description: 'Change property isEqualizated in all client addresses',
|
description: 'Change property isEqualizated in all client addresses',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
accepts: [
|
accepts: [
|
||||||
|
|
|
@ -16,7 +16,7 @@ describe('client consumption() filter', () => {
|
||||||
};
|
};
|
||||||
const result = await models.Client.consumption(ctx, filter, options);
|
const result = await models.Client.consumption(ctx, filter, options);
|
||||||
|
|
||||||
expect(result.length).toEqual(10);
|
expect(result.length).toEqual(11);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -49,7 +49,7 @@ describe('client consumption() filter', () => {
|
||||||
const thirdRow = result[2];
|
const thirdRow = result[2];
|
||||||
|
|
||||||
expect(result.length).toEqual(3);
|
expect(result.length).toEqual(3);
|
||||||
expect(firstRow.quantity).toEqual(10);
|
expect(firstRow.quantity).toEqual(11);
|
||||||
expect(secondRow.quantity).toEqual(15);
|
expect(secondRow.quantity).toEqual(15);
|
||||||
expect(thirdRow.quantity).toEqual(20);
|
expect(thirdRow.quantity).toEqual(20);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "Address",
|
"name": "Address",
|
||||||
"description": "Client addresses",
|
"description": "Client addresses",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "address"
|
"table": "address"
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "ClientContact",
|
"name": "ClientContact",
|
||||||
"description": "Client phone contacts",
|
"description": "Client phone contacts",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "clientContact"
|
"table": "clientContact"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ClientDms",
|
"name": "ClientDms",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "clientDms"
|
"table": "clientDms"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ClientInforma",
|
"name": "ClientInforma",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"log": {
|
"log": {
|
||||||
"model":"ClientLog",
|
"model":"ClientLog",
|
||||||
"relation": "client",
|
"relation": "client",
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "ClientObservation",
|
"name": "ClientObservation",
|
||||||
"description": "Client notes",
|
"description": "Client notes",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "clientObservation"
|
"table": "clientObservation"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ClientSample",
|
"name": "ClientSample",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "clientSample"
|
"table": "clientSample"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "Client",
|
"name": "Client",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "client"
|
"table": "client"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "Greuge",
|
"name": "Greuge",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "greuge"
|
"table": "greuge"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "Recovery",
|
"name": "Recovery",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "recovery"
|
"table": "recovery"
|
||||||
|
|
|
@ -19,8 +19,8 @@
|
||||||
"relations": {
|
"relations": {
|
||||||
"role": {
|
"role": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Role",
|
"model": "VnRole",
|
||||||
"foreignKey": "roleFk"
|
"foreignKey": "roleFk"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "Buy",
|
"name": "Buy",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "buy"
|
"table": "buy"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "EntryObservation",
|
"name": "EntryObservation",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "entryObservation"
|
"table": "entryObservation"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "Entry",
|
"name": "Entry",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "entry"
|
"table": "entry"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "InvoiceInTax",
|
"name": "InvoiceInTax",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "invoiceInTax"
|
"table": "invoiceInTax"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "InvoiceIn",
|
"name": "InvoiceIn",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "invoiceIn"
|
"table": "invoiceIn"
|
||||||
|
|
|
@ -85,7 +85,7 @@ module.exports = Self => {
|
||||||
throw new UserError(`A ticket with an amount of zero can't be invoiced`);
|
throw new UserError(`A ticket with an amount of zero can't be invoiced`);
|
||||||
|
|
||||||
// Validates ticket nagative base
|
// Validates ticket nagative base
|
||||||
const hasNegativeBase = await getNegativeBase(ticketId, myOptions);
|
const hasNegativeBase = await getNegativeBase(maxShipped, clientId, companyId, myOptions);
|
||||||
if (hasNegativeBase && company.code == 'VNL')
|
if (hasNegativeBase && company.code == 'VNL')
|
||||||
throw new UserError(`A ticket with a negative base can't be invoiced`);
|
throw new UserError(`A ticket with a negative base can't be invoiced`);
|
||||||
} else {
|
} else {
|
||||||
|
@ -162,10 +162,13 @@ module.exports = Self => {
|
||||||
return result.invoiceable;
|
return result.invoiceable;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getNegativeBase(ticketId, options) {
|
async function getNegativeBase(maxShipped, clientId, companyId, options) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const query = 'SELECT vn.hasSomeNegativeBase(?) AS base';
|
await models.InvoiceOut.rawSql('CALL invoiceOut_exportationFromClient(?,?,?)',
|
||||||
const [result] = await models.InvoiceOut.rawSql(query, [ticketId], options);
|
[maxShipped, clientId, companyId], options
|
||||||
|
);
|
||||||
|
const query = 'SELECT vn.hasAnyNegativeBase() AS base';
|
||||||
|
const [result] = await models.InvoiceOut.rawSql(query, [], options);
|
||||||
|
|
||||||
return result.base;
|
return result.base;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,7 @@ module.exports = Self => {
|
||||||
invoiceType,
|
invoiceType,
|
||||||
args.companyFk,
|
args.companyFk,
|
||||||
args.invoiceDate,
|
args.invoiceDate,
|
||||||
|
null,
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
const LoopBackContext = require('loopback-context');
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('InvoiceOut tranferInvoice()', () => {
|
describe('InvoiceOut transferInvoice()', () => {
|
||||||
const activeCtx = {
|
const activeCtx = {
|
||||||
accessToken: {userId: 5},
|
accessToken: {userId: 5},
|
||||||
http: {
|
http: {
|
||||||
|
@ -23,20 +23,29 @@ describe('InvoiceOut tranferInvoice()', () => {
|
||||||
const tx = await models.InvoiceOut.beginTransaction({});
|
const tx = await models.InvoiceOut.beginTransaction({});
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
const args = {
|
const args = {
|
||||||
id: '1',
|
id: '4',
|
||||||
ref: 'T4444444',
|
refFk: 'T4444444',
|
||||||
newClientFk: 1,
|
newClientFk: 1,
|
||||||
cplusRectificationId: 1,
|
cplusRectificationTypeFk: 1,
|
||||||
siiTypeInvoiceOutId: 1,
|
siiTypeInvoiceOutFk: 1,
|
||||||
invoiceCorrectionTypeId: 1
|
invoiceCorrectionTypeFk: 1
|
||||||
};
|
};
|
||||||
ctx.args = args;
|
ctx.args = args;
|
||||||
try {
|
try {
|
||||||
|
const {clientFk: oldClient} = await models.InvoiceOut.findById(args.id, {fields: ['clientFk']});
|
||||||
|
const invoicesBefore = await models.InvoiceOut.find({}, options);
|
||||||
const result = await models.InvoiceOut.transferInvoice(
|
const result = await models.InvoiceOut.transferInvoice(
|
||||||
ctx,
|
ctx,
|
||||||
options);
|
options);
|
||||||
|
const invoicesAfter = await models.InvoiceOut.find({}, options);
|
||||||
|
const rectificativeInvoice = invoicesAfter[invoicesAfter.length - 2];
|
||||||
|
const newInvoice = invoicesAfter[invoicesAfter.length - 1];
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
|
expect(invoicesAfter.length - invoicesBefore.length).toEqual(2);
|
||||||
|
expect(rectificativeInvoice.clientFk).toEqual(oldClient);
|
||||||
|
expect(newInvoice.clientFk).toEqual(args.newClientFk);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
@ -49,20 +58,44 @@ 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,
|
cplusRectificationTypeFk: 1,
|
||||||
siiTypeInvoiceOutId: 1,
|
siiTypeInvoiceOutFk: 1,
|
||||||
invoiceCorrectionTypeId: 1
|
invoiceCorrectionTypeFk: 1
|
||||||
};
|
};
|
||||||
ctx.args = args;
|
ctx.args = args;
|
||||||
try {
|
try {
|
||||||
await models.InvoiceOut.transferInvoice(
|
await models.InvoiceOut.transferInvoice(
|
||||||
ctx,
|
ctx,
|
||||||
options);
|
options);
|
||||||
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(e.message).toBe(`Select a different client`);
|
expect(e.message).toBe(`Select a different client`);
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw an UserError when it is refund', async() => {
|
||||||
|
const tx = await models.InvoiceOut.beginTransaction({});
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const args = {
|
||||||
|
id: '1',
|
||||||
|
refFk: 'T1111111',
|
||||||
|
newClientFk: 1102,
|
||||||
|
cplusRectificationTypeFk: 1,
|
||||||
|
siiTypeInvoiceOutFk: 1,
|
||||||
|
invoiceCorrectionTypeFk: 1
|
||||||
|
};
|
||||||
|
ctx.args = args;
|
||||||
|
try {
|
||||||
|
await models.InvoiceOut.transferInvoice(
|
||||||
|
ctx,
|
||||||
|
options);
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.message).toContain(`This ticket is already a refund`);
|
||||||
|
await tx.rollback();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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: 'cplusRectificationTypeFk',
|
||||||
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, cplusRectificationTypeFk, 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, cplusRectificationTypeFk, 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, null, myOptions);
|
||||||
correctedFk: args.id,
|
|
||||||
cplusRectificationTypeFk: args.cplusRectificationId,
|
|
||||||
siiTypeInvoiceOutFk: args.siiTypeInvoiceOutId,
|
|
||||||
invoiceCorrectionTypeFk: args.invoiceCorrectionTypeId
|
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
if (tx) {
|
if (tx) {
|
||||||
await tx.commit();
|
await tx.commit();
|
||||||
|
|
|
@ -16,13 +16,43 @@
|
||||||
"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
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"correcting": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "InvoiceOut",
|
||||||
|
"foreignKey": "correctingFk"
|
||||||
|
},
|
||||||
|
"corrected": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "InvoiceOut",
|
||||||
|
"foreignKey": "correctedFk"
|
||||||
|
},
|
||||||
|
"cplusRectificationType": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "cplusRectificationType",
|
||||||
|
"foreignKey": "cplusRectificationTypeFk"
|
||||||
|
},
|
||||||
|
"siiTypeInvoiceOut": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "siiTypeInvoiceOut",
|
||||||
|
"foreignKey": "siiTypeInvoiceOutFk"
|
||||||
|
},
|
||||||
|
"invoiceCorrectionType": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "invoiceCorrectionType",
|
||||||
|
"foreignKey": "invoiceCorrectionTypeFk"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"description": "Identifier"
|
"description": "Identifier"
|
||||||
},
|
},
|
||||||
|
"code": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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="description"
|
||||||
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>
|
||||||
|
|
|
@ -129,15 +129,15 @@ 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,
|
cplusRectificationTypeFk: 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;
|
||||||
this.vnApp.showSuccess(this.$t('Invoice trasfered!'));
|
this.vnApp.showSuccess(this.$t('Transferred invoice'));
|
||||||
this.$state.go('invoiceOut.card.summary', {id: invoiceId});
|
this.$state.go('invoiceOut.card.summary', {id: invoiceId});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,4 +22,5 @@ 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
|
||||||
|
Transferred invoice: Factura transferida
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
auto-load="true"
|
auto-load="true"
|
||||||
url="InvoiceOutSerials"
|
url="InvoiceOutSerials"
|
||||||
data="invoiceOutSerials"
|
data="invoiceOutSerials"
|
||||||
|
where="{code: {neq: 'R'}}"
|
||||||
order="code">
|
order="code">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ItemBarcode",
|
"name": "ItemBarcode",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "itemBarcode"
|
"table": "itemBarcode"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ItemBotanical",
|
"name": "ItemBotanical",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "itemBotanical"
|
"table": "itemBotanical"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ItemShelving",
|
"name": "ItemShelving",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "itemShelving"
|
"table": "itemShelving"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ItemTag",
|
"name": "ItemTag",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "itemTag"
|
"table": "itemTag"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ItemTaxCountry",
|
"name": "ItemTaxCountry",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "itemTaxCountry"
|
"table": "itemTaxCountry"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "Item",
|
"name": "Item",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "item"
|
"table": "item"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "Route",
|
"name": "Route",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "route"
|
"table": "route"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "Shelving",
|
"name": "Shelving",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "shelving"
|
"table": "shelving"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "SupplierAccount",
|
"name": "SupplierAccount",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "supplierAccount"
|
"table": "supplierAccount"
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "SupplierAddress",
|
"name": "SupplierAddress",
|
||||||
"description": "Supplier addresses",
|
"description": "Supplier addresses",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "supplierAddress"
|
"table": "supplierAddress"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "SupplierContact",
|
"name": "SupplierContact",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "supplierContact"
|
"table": "supplierContact"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "Supplier",
|
"name": "Supplier",
|
||||||
"base": "Loggable",
|
"base": "VnModel",
|
||||||
|
"mixins": {
|
||||||
|
"Loggable": true
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "supplier"
|
"table": "supplier"
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -102,7 +102,7 @@ describe('sale canEdit()', () => {
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const role = await models.Role.findOne({
|
const role = await models.VnRole.findOne({
|
||||||
where: {
|
where: {
|
||||||
name: roleEnabled.principalId
|
name: roleEnabled.principalId
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ describe('sale canEdit()', () => {
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const role = await models.Role.findOne({
|
const role = await models.VnRole.findOne({
|
||||||
where: {
|
where: {
|
||||||
name: roleEnabled.principalId
|
name: roleEnabled.principalId
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,9 @@ describe('Sale refund()', () => {
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const refundedTicket = await models.Sale.refund(ctx, salesIds, servicesIds, withWarehouse, options);
|
const refundedTickets = await models.Sale.refund(ctx, salesIds, servicesIds, withWarehouse, options);
|
||||||
|
|
||||||
expect(refundedTicket).toBeDefined();
|
expect(refundedTickets).toBeDefined();
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -42,11 +42,11 @@ describe('Sale refund()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
const ticketsBefore = await models.Ticket.find({}, options);
|
const ticketsBefore = await models.Ticket.find({}, options);
|
||||||
|
|
||||||
const ticket = await models.Sale.refund(ctx, salesIds, servicesIds, withWarehouse, options);
|
const tickets = await models.Sale.refund(ctx, salesIds, servicesIds, withWarehouse, options);
|
||||||
|
|
||||||
const refundedTicket = await models.Ticket.findOne({
|
const refundedTicket = await models.Ticket.findOne({
|
||||||
where: {
|
where: {
|
||||||
id: ticket.id
|
id: tickets[0].id
|
||||||
},
|
},
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,20 +10,20 @@ module.exports = function(Self) {
|
||||||
description: 'The tickets id',
|
description: 'The tickets id',
|
||||||
type: ['number'],
|
type: ['number'],
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'isRectificative',
|
||||||
|
description: 'If it is rectificative',
|
||||||
|
type: 'boolean'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
|
||||||
arg: 'data',
|
|
||||||
type: 'boolean',
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
http: {
|
||||||
path: `/canBeInvoiced`,
|
path: `/canBeInvoiced`,
|
||||||
verb: 'get'
|
verb: 'get'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.canBeInvoiced = async(ctx, ticketsIds, options) => {
|
Self.canBeInvoiced = async(ctx, ticketsIds, isRectificative, options) => {
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
const $t = ctx.req.__; // $translate
|
const $t = ctx.req.__; // $translate
|
||||||
|
|
||||||
|
@ -34,26 +34,14 @@ module.exports = function(Self) {
|
||||||
where: {
|
where: {
|
||||||
id: {inq: ticketsIds}
|
id: {inq: ticketsIds}
|
||||||
},
|
},
|
||||||
fields: ['id', 'refFk', 'shipped', 'totalWithVat', 'companyFk']
|
fields: ['id', 'refFk', 'shipped', 'totalWithVat']
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
const [firstTicket] = tickets;
|
|
||||||
const companyFk = firstTicket.companyFk;
|
|
||||||
|
|
||||||
const query =
|
|
||||||
`SELECT COUNT(*) isSpanishCompany
|
|
||||||
FROM supplier s
|
|
||||||
JOIN country c ON c.id = s.countryFk
|
|
||||||
AND c.code = 'ES'
|
|
||||||
WHERE s.id = ?`;
|
|
||||||
const [supplierCompany] = await Self.rawSql(query, [companyFk], options);
|
|
||||||
|
|
||||||
const isSpanishCompany = supplierCompany?.isSpanishCompany;
|
|
||||||
|
|
||||||
const [result] = await Self.rawSql('SELECT hasAnyNegativeBase() AS base', null, options);
|
|
||||||
const hasAnyNegativeBase = result?.base && isSpanishCompany;
|
|
||||||
if (hasAnyNegativeBase)
|
|
||||||
throw new UserError($t('Negative basis of tickets', {ticketsIds: ticketsIds}));
|
|
||||||
|
|
||||||
|
const taxBaseFunction = isRectificative ? 'hasAnyPositiveBase' : 'hasAnyNegativeBase';
|
||||||
|
const [hasAnyIncorrectBase] =
|
||||||
|
await Self.rawSql(`SELECT ${taxBaseFunction}() AS hasBasesProblem`, null, options);
|
||||||
|
if (hasAnyIncorrectBase?.hasBasesProblem)
|
||||||
|
throw new UserError($t(taxBaseFunction, {ticketsIds: ticketsIds}));
|
||||||
const today = Date.vnNew();
|
const today = Date.vnNew();
|
||||||
|
|
||||||
tickets.some(ticket => {
|
tickets.some(ticket => {
|
||||||
|
@ -70,7 +58,5 @@ module.exports = function(Self) {
|
||||||
if (ticketsIds.length == 1 && priceZero)
|
if (ticketsIds.length == 1 && priceZero)
|
||||||
throw new UserError(`A ticket with an amount of zero can't be invoiced`);
|
throw new UserError(`A ticket with an amount of zero can't be invoiced`);
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -41,10 +47,10 @@ module.exports = function(Self) {
|
||||||
let invoicesIds = [];
|
let invoicesIds = [];
|
||||||
try {
|
try {
|
||||||
const tickets = await models.Ticket.find({
|
const tickets = await models.Ticket.find({
|
||||||
|
fields: ['id', 'clientFk', 'companyFk', 'addressFk'],
|
||||||
where: {
|
where: {
|
||||||
id: {inq: ticketsIds}
|
id: {inq: ticketsIds}
|
||||||
},
|
}
|
||||||
fields: ['id', 'clientFk', 'companyFk']
|
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
const [firstTicket] = tickets;
|
const [firstTicket] = tickets;
|
||||||
|
@ -55,22 +61,20 @@ module.exports = function(Self) {
|
||||||
if (!isSameClient)
|
if (!isSameClient)
|
||||||
throw new UserError(`You can't invoice tickets from multiple clients`);
|
throw new UserError(`You can't invoice tickets from multiple clients`);
|
||||||
|
|
||||||
const client = await models.Client.findById(clientId, {
|
const {hasToInvoiceByAddress} = await models.Client.findById(clientId, {
|
||||||
fields: ['id', 'hasToInvoiceByAddress']
|
fields: ['hasToInvoiceByAddress']
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
if (client.hasToInvoiceByAddress) {
|
let ticketsByAddress = hasToInvoiceByAddress
|
||||||
const query = `
|
? Object.values(tickets.reduce((group, {id, addressFk}) => {
|
||||||
SELECT DISTINCT addressFk
|
group[addressFk] = group[addressFk] ?? [];
|
||||||
FROM ticket t
|
group[addressFk].push(id);
|
||||||
WHERE id IN (?)`;
|
return group;
|
||||||
const result = await Self.rawSql(query, [ticketsIds], myOptions);
|
}, {}))
|
||||||
|
: [ticketsIds];
|
||||||
|
|
||||||
const addressIds = result.map(address => address.addressFk);
|
for (const ticketIds of ticketsByAddress)
|
||||||
for (const address of addressIds)
|
invoicesIds.push(await createInvoice(ctx, companyId, ticketIds, invoiceCorrection, myOptions));
|
||||||
await createInvoice(ctx, companyId, ticketsIds, address, invoicesIds, myOptions);
|
|
||||||
} else
|
|
||||||
await createInvoice(ctx, companyId, ticketsIds, null, invoicesIds, myOptions);
|
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -85,9 +89,8 @@ module.exports = function(Self) {
|
||||||
return invoicesIds;
|
return invoicesIds;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function createInvoice(ctx, companyId, ticketsIds, address, invoicesIds, myOptions) {
|
async function createInvoice(ctx, companyId, ticketsIds, invoiceCorrection, myOptions) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
|
||||||
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))
|
||||||
|
@ -95,11 +98,8 @@ module.exports = function(Self) {
|
||||||
SELECT id
|
SELECT id
|
||||||
FROM vn.ticket
|
FROM vn.ticket
|
||||||
WHERE id IN (?)
|
WHERE id IN (?)
|
||||||
${address ? `AND addressFk = ${address}` : ''}
|
|
||||||
`, [ticketsIds], myOptions);
|
`, [ticketsIds], myOptions);
|
||||||
|
return models.Ticket.makeInvoice(ctx, 'R', companyId, Date.vnNew(), invoiceCorrection, myOptions);
|
||||||
const invoiceId = await models.Ticket.makeInvoice(ctx, 'R', companyId, Date.vnNew(), myOptions);
|
|
||||||
invoicesIds.push(invoiceId);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
@ -62,20 +67,24 @@ module.exports = function(Self) {
|
||||||
fields: ['id', 'clientFk', 'addressFk']
|
fields: ['id', 'clientFk', 'addressFk']
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
await models.Ticket.canBeInvoiced(ctx, ticketsIds, myOptions);
|
await models.Ticket.canBeInvoiced(ctx, ticketsIds, !!invoiceCorrection, myOptions);
|
||||||
|
|
||||||
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, myOptions);
|
||||||
|
|
||||||
if (!clientCanBeInvoiced)
|
if (!clientCanBeInvoiced)
|
||||||
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 [{serial}] = invoiceCorrection ? [{serial: 'R'}] : await Self.rawSql(
|
||||||
const [{serial}] = await Self.rawSql(query, [
|
`SELECT vn.invoiceSerial(?, ?, ?) AS serial`,
|
||||||
clientId,
|
[
|
||||||
companyFk,
|
clientId,
|
||||||
invoiceType,
|
companyFk,
|
||||||
], myOptions);
|
invoiceType
|
||||||
|
],
|
||||||
|
myOptions);
|
||||||
|
|
||||||
const invoiceOutSerial = await models.InvoiceOutSerial.findById(serial);
|
const invoiceOutSerial = await models.InvoiceOutSerial.findById(serial);
|
||||||
if (invoiceOutSerial?.taxAreaFk == 'WORLD') {
|
if (invoiceOutSerial?.taxAreaFk == 'WORLD') {
|
||||||
|
@ -87,11 +96,17 @@ module.exports = function(Self) {
|
||||||
await Self.rawSql('CALL invoiceOut_new(?, ?, null, @invoiceId)', [serial, invoiceDate], myOptions);
|
await Self.rawSql('CALL invoiceOut_new(?, ?, null, @invoiceId)', [serial, invoiceDate], myOptions);
|
||||||
|
|
||||||
const [resultInvoice] = await Self.rawSql('SELECT @invoiceId id', [], myOptions);
|
const [resultInvoice] = await Self.rawSql('SELECT @invoiceId id', [], myOptions);
|
||||||
if (!resultInvoice)
|
if (!resultInvoice?.id)
|
||||||
throw new UserError('No tickets to invoice', 'notInvoiced');
|
throw new UserError('No tickets to invoice', 'notInvoiced');
|
||||||
|
|
||||||
if (serial != 'R' && resultInvoice.id)
|
if (invoiceCorrection) {
|
||||||
await Self.rawSql('CALL invoiceOutBooking(?)', [resultInvoice.id], myOptions);
|
await models.InvoiceCorrection.create(
|
||||||
|
Object.assign(invoiceCorrection, {correctingFk: resultInvoice.id}),
|
||||||
|
myOptions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Self.rawSql('CALL invoiceOutBooking(?)', [resultInvoice.id], myOptions);
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
type: ['number'],
|
type: ['object'],
|
||||||
root: true
|
root: true
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
|
|
|
@ -27,10 +27,7 @@ describe('ticket canBeInvoiced()', () => {
|
||||||
WHERE id IN (?)
|
WHERE id IN (?)
|
||||||
`, [ticketId], options);
|
`, [ticketId], options);
|
||||||
|
|
||||||
const canBeInvoiced = await models.Ticket.canBeInvoiced(ctx, [ticketId], options);
|
await models.Ticket.canBeInvoiced(ctx, [ticketId], false, options);
|
||||||
|
|
||||||
expect(canBeInvoiced).toEqual(false);
|
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
|
@ -59,10 +56,7 @@ describe('ticket canBeInvoiced()', () => {
|
||||||
WHERE id IN (?)
|
WHERE id IN (?)
|
||||||
`, [ticketId], options);
|
`, [ticketId], options);
|
||||||
|
|
||||||
const canBeInvoiced = await models.Ticket.canBeInvoiced(ctx, [ticketId], options);
|
await models.Ticket.canBeInvoiced(ctx, [ticketId], false, options);
|
||||||
|
|
||||||
expect(canBeInvoiced).toEqual(false);
|
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
|
@ -95,10 +89,7 @@ describe('ticket canBeInvoiced()', () => {
|
||||||
WHERE id IN (?)
|
WHERE id IN (?)
|
||||||
`, [ticketId], options);
|
`, [ticketId], options);
|
||||||
|
|
||||||
const canBeInvoiced = await models.Ticket.canBeInvoiced(ctx, [ticketId], options);
|
await models.Ticket.canBeInvoiced(ctx, [ticketId], false, options);
|
||||||
|
|
||||||
expect(canBeInvoiced).toEqual(false);
|
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
|
@ -123,14 +114,36 @@ describe('ticket canBeInvoiced()', () => {
|
||||||
WHERE id IN (?)
|
WHERE id IN (?)
|
||||||
`, [ticketId], options);
|
`, [ticketId], options);
|
||||||
|
|
||||||
const canBeInvoiced = await models.Ticket.canBeInvoiced(ctx, [ticketId], options);
|
await models.Ticket.canBeInvoiced(ctx, [ticketId], false, options);
|
||||||
|
|
||||||
expect(canBeInvoiced).toEqual(true);
|
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return falsy for a ticket has positiveBase', async() => {
|
||||||
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
await models.Ticket.rawSql(`
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketToInvoice
|
||||||
|
(PRIMARY KEY (id))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT id
|
||||||
|
FROM vn.ticket
|
||||||
|
WHERE id IN (?)
|
||||||
|
`, [ticketId], options);
|
||||||
|
|
||||||
|
await models.Ticket.canBeInvoiced(ctx, [ticketId], true, options);
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
await tx.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(`hasAnyPositiveBase`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,7 +31,7 @@ describe('ticket invoiceTickets()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const ticketsIds = [11, 16];
|
const ticketsIds = [11, 16];
|
||||||
await models.Ticket.invoiceTickets(ctx, ticketsIds, options);
|
await models.Ticket.invoiceTickets(ctx, ticketsIds, null, options);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -57,7 +57,7 @@ describe('ticket invoiceTickets()', () => {
|
||||||
await client.updateAttribute('isTaxDataChecked', false, options);
|
await client.updateAttribute('isTaxDataChecked', false, options);
|
||||||
|
|
||||||
const ticketsIds = [11];
|
const ticketsIds = [11];
|
||||||
await models.Ticket.invoiceTickets(ctx, ticketsIds, options);
|
await models.Ticket.invoiceTickets(ctx, ticketsIds, null, options);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -80,8 +80,8 @@ describe('ticket invoiceTickets()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const ticketsIds = [11];
|
const ticketsIds = [11];
|
||||||
await models.Ticket.invoiceTickets(ctx, ticketsIds, options);
|
await models.Ticket.invoiceTickets(ctx, ticketsIds, null, options);
|
||||||
await models.Ticket.invoiceTickets(ctx, ticketsIds, options);
|
await models.Ticket.invoiceTickets(ctx, ticketsIds, null, options);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -102,7 +102,7 @@ describe('ticket invoiceTickets()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const ticketsIds = [11];
|
const ticketsIds = [11];
|
||||||
const invoicesIds = await models.Ticket.invoiceTickets(ctx, ticketsIds, options);
|
const invoicesIds = await models.Ticket.invoiceTickets(ctx, ticketsIds, null, options);
|
||||||
|
|
||||||
expect(invoicesIds.length).toBeGreaterThan(0);
|
expect(invoicesIds.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue