7953-devToTest_2438 #2942
|
@ -29,18 +29,8 @@ module.exports = Self => {
|
|||
return token;
|
||||
|
||||
// Schedule to remove current token
|
||||
setTimeout(async() => {
|
||||
let exists;
|
||||
try {
|
||||
exists = await models.AccessToken.findById(token.id);
|
||||
exists && await Self.logout(token.id);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
const body = {error: error.message, now: Date.now(), userId: token?.userId ?? null, exists};
|
||||
await handleError(body);
|
||||
throw new Error(error);
|
||||
}
|
||||
setTimeout(() => {
|
||||
Self.logout(token.id);
|
||||
}, courtesyTime * 1000);
|
||||
|
||||
// Get scopes
|
||||
|
@ -53,14 +43,20 @@ module.exports = Self => {
|
|||
|
||||
return {id: accessToken.id, ttl: accessToken.ttl};
|
||||
} catch (error) {
|
||||
const body = {error: error.message, now: Date.now(), userId: token?.userId ?? null, createTokenOptions, isNotExceeded};
|
||||
await handleError(body);
|
||||
const body = {
|
||||
error: error.message,
|
||||
userId: token?.userId ?? null,
|
||||
token: token?.id,
|
||||
scopes: token?.scopes,
|
||||
createTokenOptions,
|
||||
isNotExceeded
|
||||
};
|
||||
await handleError(JSON.stringify(body));
|
||||
throw new Error(error);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
async function handleError(body, tag = 'renewToken') {
|
||||
body = JSON.stringify(body);
|
||||
await models.Application.rawSql('CALL util.debugAdd(?,?);', [tag, body]);
|
||||
async function handleError(body) {
|
||||
await models.Application.rawSql('CALL util.debugAdd(?,?);', ['renewToken', body]);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1455,6 +1455,11 @@ INSERT IGNORE INTO `tables_priv` VALUES ('','vn','grafana','accountingType','gu
|
|||
INSERT IGNORE INTO `tables_priv` VALUES ('','vn','grafana','bankPolicyDetail','guillermo@db-proxy2.servers.dc.verdnatura.es','0000-00-00 00:00:00','Select','');
|
||||
INSERT IGNORE INTO `tables_priv` VALUES ('','vn','grafana','bankPolicyReview','guillermo@db-proxy2.servers.dc.verdnatura.es','0000-00-00 00:00:00','Select','');
|
||||
INSERT IGNORE INTO `tables_priv` VALUES ('','vn','grafana','bankPolicy','guillermo@db-proxy2.servers.dc.verdnatura.es','0000-00-00 00:00:00','Select','');
|
||||
INSERT IGNORE INTO `tables_priv` VALUES ('','edi','hedera-web','imapMultiConfig','guillermo@db-proxy2.servers.dc.verdnatura.es','0000-00-00 00:00:00','Select','');
|
||||
INSERT IGNORE INTO `tables_priv` VALUES ('','hedera','salesAssistant','orderConfig','root@localhost','0000-00-00 00:00:00','Select','');
|
||||
INSERT IGNORE INTO `tables_priv` VALUES ('','vn','grafana','itemEntryOut','guillermo@db-proxy2.servers.dc.verdnatura.es','0000-00-00 00:00:00','Select','');
|
||||
INSERT IGNORE INTO `tables_priv` VALUES ('','vn','grafana','itemEntryIn','guillermo@db-proxy2.servers.dc.verdnatura.es','0000-00-00 00:00:00','Select','');
|
||||
INSERT IGNORE INTO `tables_priv` VALUES ('','vn','grafana','itemShelvingSale','guillermo@db-proxy1.servers.dc.verdnatura.es','0000-00-00 00:00:00','Select','');
|
||||
/*!40000 ALTER TABLE `tables_priv` ENABLE KEYS */;
|
||||
|
||||
/*!40000 ALTER TABLE `columns_priv` DISABLE KEYS */;
|
||||
|
@ -2160,9 +2165,7 @@ INSERT IGNORE INTO `procs_priv` VALUES ('','vn','buyer','buy_recalcPricesByAwb'
|
|||
INSERT IGNORE INTO `procs_priv` VALUES ('','vn','buyer','buy_recalcPricesByEntry','PROCEDURE','jenkins@db-proxy2.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
INSERT IGNORE INTO `procs_priv` VALUES ('','util','hr','accountNumberToIban','FUNCTION','jenkins@db-proxy1.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
INSERT IGNORE INTO `procs_priv` VALUES ('','util','financial','accountNumberToIban','FUNCTION','jenkins@db-proxy1.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
INSERT IGNORE INTO `procs_priv` VALUES ('','vn','administrative','supplier_statement','PROCEDURE','jenkins@db-proxy1.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
INSERT IGNORE INTO `procs_priv` VALUES ('','vn','buyer','supplier_statement','PROCEDURE','jenkins@db-proxy1.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
INSERT IGNORE INTO `procs_priv` VALUES ('','vn','hrBoss','supplier_statement','PROCEDURE','jenkins@db-proxy1.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
INSERT IGNORE INTO `procs_priv` VALUES ('','vn','buyer','supplier_statementWithEntries','PROCEDURE','guillermo@db-proxy1.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
INSERT IGNORE INTO `procs_priv` VALUES ('','vn','adminBoss','XDiario_check','PROCEDURE','jenkins@db-proxy1.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
INSERT IGNORE INTO `procs_priv` VALUES ('','vn','buyer','travel_getDetailFromContinent','PROCEDURE','jenkins@db-proxy1.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
INSERT IGNORE INTO `procs_priv` VALUES ('','vn','claimManager','entry_getTransfer','PROCEDURE','jenkins@db-proxy1.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
|
@ -2193,6 +2196,8 @@ INSERT IGNORE INTO `procs_priv` VALUES ('','vn','production','itemShelvingSale_
|
|||
INSERT IGNORE INTO `procs_priv` VALUES ('','vn','production','sectorCollection_getMyPartial','PROCEDURE','carlosap@db-proxy1.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
INSERT IGNORE INTO `procs_priv` VALUES ('','vn','grafana-write','item_ValuateInventory','PROCEDURE','guillermo@db-proxy1.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
INSERT IGNORE INTO `procs_priv` VALUES ('','vn','guest','ticketCalculatePurge','PROCEDURE','jenkins@db-proxy2.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
INSERT IGNORE INTO `procs_priv` VALUES ('','vn','buyer','buy_getUltimate','PROCEDURE','guillermo@db-proxy2.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
INSERT IGNORE INTO `procs_priv` VALUES ('','vn','cooler','buy_getUltimate','PROCEDURE','guillermo@db-proxy2.servers.dc.verdnatura.es','Execute','0000-00-00 00:00:00');
|
||||
/*!40000 ALTER TABLE `procs_priv` ENABLE KEYS */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
|
@ -2237,7 +2242,7 @@ INSERT IGNORE INTO `global_priv` VALUES ('','grafana-write','{\"access\":0,\"ve
|
|||
INSERT IGNORE INTO `global_priv` VALUES ('','greenhouseBoss','{\"access\":0,\"version_id\":101106,\"is_role\":true}');
|
||||
INSERT IGNORE INTO `global_priv` VALUES ('','guest','{\"access\": 0, \"max_questions\": 40000, \"max_updates\": 1000, \"max_connections\": 150000, \"max_user_connections\": 200, \"max_statement_time\": 0.000000, \"is_role\": true, \"version_id\": 101106}');
|
||||
INSERT IGNORE INTO `global_priv` VALUES ('','handmadeBoss','{\"access\":0,\"version_id\":101106,\"is_role\":true}');
|
||||
INSERT IGNORE INTO `global_priv` VALUES ('','hedera-web','{\"access\":0,\"version_id\":100707,\"is_role\":true}');
|
||||
INSERT IGNORE INTO `global_priv` VALUES ('','hedera-web','{\"access\":0,\"version_id\":101106,\"is_role\":true}');
|
||||
INSERT IGNORE INTO `global_priv` VALUES ('','hr','{\"access\": 0, \"is_role\": true, \"version_id\": 101106}');
|
||||
INSERT IGNORE INTO `global_priv` VALUES ('','hrBoss','{\"access\":0,\"version_id\":101106,\"is_role\":true}');
|
||||
INSERT IGNORE INTO `global_priv` VALUES ('','invoicing','{\"access\":0,\"version_id\":100707,\"is_role\":true}');
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1423,6 +1423,70 @@ DELIMITER ;
|
|||
--
|
||||
|
||||
USE `salix`;
|
||||
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
|
||||
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
|
||||
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
|
||||
/*!50003 SET character_set_client = utf8mb4 */ ;
|
||||
/*!50003 SET character_set_results = utf8mb4 */ ;
|
||||
/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
|
||||
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
|
||||
/*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ;
|
||||
DELIMITER ;;
|
||||
/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `salix`.`ACL_beforeInsert`
|
||||
BEFORE INSERT ON `ACL`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
SET NEW.editorFk = account.myUser_getId();
|
||||
END */;;
|
||||
DELIMITER ;
|
||||
/*!50003 SET sql_mode = @saved_sql_mode */ ;
|
||||
/*!50003 SET character_set_client = @saved_cs_client */ ;
|
||||
/*!50003 SET character_set_results = @saved_cs_results */ ;
|
||||
/*!50003 SET collation_connection = @saved_col_connection */ ;
|
||||
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
|
||||
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
|
||||
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
|
||||
/*!50003 SET character_set_client = utf8mb4 */ ;
|
||||
/*!50003 SET character_set_results = utf8mb4 */ ;
|
||||
/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
|
||||
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
|
||||
/*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ;
|
||||
DELIMITER ;;
|
||||
/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `salix`.`ACL_beforeUpdate`
|
||||
BEFORE UPDATE ON `ACL`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
SET NEW.editorFk = account.myUser_getId();
|
||||
END */;;
|
||||
DELIMITER ;
|
||||
/*!50003 SET sql_mode = @saved_sql_mode */ ;
|
||||
/*!50003 SET character_set_client = @saved_cs_client */ ;
|
||||
/*!50003 SET character_set_results = @saved_cs_results */ ;
|
||||
/*!50003 SET collation_connection = @saved_col_connection */ ;
|
||||
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
|
||||
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
|
||||
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
|
||||
/*!50003 SET character_set_client = utf8mb4 */ ;
|
||||
/*!50003 SET character_set_results = utf8mb4 */ ;
|
||||
/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
|
||||
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
|
||||
/*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ;
|
||||
DELIMITER ;;
|
||||
/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `salix`.`ACL_afterDelete`
|
||||
AFTER DELETE ON `ACL`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO ACLLog
|
||||
SET `action` = 'delete',
|
||||
`changedModel` = 'Acl',
|
||||
`changedModelId` = OLD.id,
|
||||
`userFk` = account.myUser_getId();
|
||||
END */;;
|
||||
DELIMITER ;
|
||||
/*!50003 SET sql_mode = @saved_sql_mode */ ;
|
||||
/*!50003 SET character_set_client = @saved_cs_client */ ;
|
||||
/*!50003 SET character_set_results = @saved_cs_results */ ;
|
||||
/*!50003 SET collation_connection = @saved_col_connection */ ;
|
||||
|
||||
--
|
||||
-- Current Database: `srt`
|
||||
|
@ -9220,7 +9284,13 @@ BEGIN
|
|||
SET NEW.editorFk = account.myUser_getId();
|
||||
|
||||
IF NOT (NEW.routeFk <=> OLD.routeFk) THEN
|
||||
IF NEW.isSigned THEN
|
||||
IF NEW.isSigned AND NOT (
|
||||
SELECT (COUNT(s.id) = COUNT(cb.saleFk)
|
||||
AND SUM(s.quantity) = SUM(cb.quantity))
|
||||
FROM sale s
|
||||
LEFT JOIN claimBeginning cb ON cb.saleFk = s.id
|
||||
WHERE s.ticketFk = NEW.id
|
||||
) THEN
|
||||
CALL util.throw('A signed ticket cannot be rerouted');
|
||||
END IF;
|
||||
INSERT IGNORE INTO routeRecalc(routeFk)
|
||||
|
@ -11142,4 +11212,4 @@ USE `vn2008`;
|
|||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2024-08-06 6:03:19
|
||||
-- Dump completed on 2024-08-20 7:45:23
|
||||
|
|
|
@ -412,7 +412,7 @@ INSERT INTO `vn`.`clientManaCache`(`clientFk`, `mana`, `dated`)
|
|||
(1103, 0, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
|
||||
(1104, -30, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH));
|
||||
|
||||
INSERT INTO `vn`.`mandateType`(`id`, `name`)
|
||||
INSERT INTO `vn`.`mandateType`(`id`, `code`)
|
||||
VALUES
|
||||
(1, 'B2B'),
|
||||
(2, 'CORE'),
|
||||
|
@ -632,7 +632,7 @@ INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaF
|
|||
('A', 'Global nacional', 1, 'NATIONAL', 0, 'global'),
|
||||
('T', 'Española rapida', 1, 'NATIONAL', 0, 'quick'),
|
||||
('V', 'Intracomunitaria global', 0, 'CEE', 1, 'global'),
|
||||
('M', 'Múltiple nacional', 1, 'NATIONAL', 0, 'quick'),
|
||||
('M', 'Múltiple nacional', 1, 'NATIONAL', 0, 'multiple'),
|
||||
('R', 'Rectificativa', 1, 'NATIONAL', 0, NULL),
|
||||
('E', 'Exportación rápida', 0, 'WORLD', 0, 'quick');
|
||||
|
||||
|
@ -3945,11 +3945,11 @@ VALUES
|
|||
(35, 'ES12346B12345679', 3, 241);
|
||||
|
||||
INSERT INTO vn.accountDetailType
|
||||
(id, description)
|
||||
(id, description, code)
|
||||
VALUES
|
||||
(1, 'IBAN'),
|
||||
(2, 'SWIFT'),
|
||||
(3, 'Referencia Remesas'),
|
||||
(4, 'Referencia Transferencias'),
|
||||
(5, 'Referencia Nominas'),
|
||||
(6, 'ABA');
|
||||
(1, 'IBAN', 'iban'),
|
||||
(2, 'SWIFT', 'swift'),
|
||||
(3, 'Referencia Remesas', 'remRef'),
|
||||
(4, 'Referencia Transferencias', 'trnRef'),
|
||||
(5, 'Referencia Nominas', 'payRef'),
|
||||
(6, 'ABA', 'aba');
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` EVENT `srt`.`moving_clean`
|
||||
ON SCHEDULE EVERY 5 MINUTE
|
||||
ON SCHEDULE EVERY 15 MINUTE
|
||||
STARTS '2022-01-21 00:00:00.000'
|
||||
ON COMPLETION PRESERVE
|
||||
ENABLE
|
||||
COMMENT 'Llama a srt.moving_clean para que elimine y notifique de registr'
|
||||
DO BEGIN
|
||||
|
||||
CALL srt.moving_clean();
|
||||
|
||||
END$$
|
||||
DO CALL srt.moving_clean()$$
|
||||
DELIMITER ;
|
||||
|
|
|
@ -3,61 +3,69 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `srt`.`moving_clean`()
|
|||
BEGIN
|
||||
/**
|
||||
* Elimina movimientos por inactividad
|
||||
*
|
||||
*/
|
||||
DECLARE vExpeditionFk INT;
|
||||
DECLARE vBufferToFk INT;
|
||||
DECLARE vBufferFromFk INT;
|
||||
DECLARE done BOOL DEFAULT FALSE;
|
||||
|
||||
DECLARE cur CURSOR FOR
|
||||
SELECT m.expeditionFk, m.bufferToFk, m.bufferFromFk
|
||||
FROM srt.moving m
|
||||
JOIN srt.config c
|
||||
JOIN (SELECT bufferFk, SUM(isActive) hasBox
|
||||
FROM srt.photocell
|
||||
GROUP BY bufferFk) sub ON sub.bufferFk = m.bufferFromFk
|
||||
WHERE m.created < TIMESTAMPADD(MINUTE, - c.movingMaxLife , util.VN_NOW())
|
||||
DECLARE vStateOutFk INT
|
||||
DEFAULT (SELECT id FROM expeditionState WHERE `description` = 'OUT');
|
||||
DECLARE vDone BOOL;
|
||||
DECLARE vSorter CURSOR FOR
|
||||
SELECT m.expeditionFk, m.bufferFromFk
|
||||
FROM moving m
|
||||
JOIN (
|
||||
SELECT bufferFk, SUM(isActive) hasBox
|
||||
FROM photocell
|
||||
GROUP BY bufferFk
|
||||
) sub ON sub.bufferFk = m.bufferFromFk
|
||||
WHERE m.created < (util.VN_NOW() - INTERVAL (SELECT movingMaxLife FROM config) MINUTE)
|
||||
AND NOT sub.hasBox;
|
||||
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
|
||||
|
||||
OPEN cur;
|
||||
DECLARE EXIT HANDLER FOR SQLEXCEPTION
|
||||
BEGIN
|
||||
ROLLBACK;
|
||||
RESIGNAL;
|
||||
END;
|
||||
|
||||
bucle: LOOP
|
||||
OPEN vSorter;
|
||||
l: LOOP
|
||||
SET vDone = FALSE;
|
||||
FETCH vSorter INTO vExpeditionFk, vBufferFromFk;
|
||||
|
||||
FETCH cur INTO vExpeditionFk, vBufferToFk, vBufferFromFk;
|
||||
|
||||
IF done THEN
|
||||
LEAVE bucle;
|
||||
IF vDone THEN
|
||||
LEAVE l;
|
||||
END IF;
|
||||
|
||||
DELETE FROM srt.moving
|
||||
START TRANSACTION;
|
||||
|
||||
SELECT id
|
||||
FROM moving
|
||||
WHERE expeditionFk = vExpeditionFk
|
||||
FOR UPDATE;
|
||||
|
||||
DELETE FROM moving
|
||||
WHERE expeditionFk = vExpeditionFk;
|
||||
|
||||
UPDATE srt.expedition e
|
||||
JOIN srt.expeditionState es ON es.description = 'OUT'
|
||||
SET
|
||||
bufferFk = NULL,
|
||||
SELECT id
|
||||
FROM expedition
|
||||
WHERE id = vExpeditionFk
|
||||
OR (bufferFk = vBufferFromFk AND `position` > 0)
|
||||
FOR UPDATE;
|
||||
|
||||
UPDATE expedition
|
||||
SET bufferFk = NULL,
|
||||
`position` = NULL,
|
||||
stateFk = es.id
|
||||
WHERE e.id = vExpeditionFk;
|
||||
stateFk = vStateOutFk
|
||||
WHERE id = vExpeditionFk;
|
||||
|
||||
UPDATE srt.expedition e
|
||||
SET e.`position` = e.`position` - 1
|
||||
WHERE e.bufferFk = vBufferFromFk
|
||||
AND e.`position` > 0;
|
||||
|
||||
CALL vn.mail_insert(
|
||||
'pako@verdnatura.es, carles@verdnatura.es',
|
||||
NULL,
|
||||
CONCAT('Moving_clean. Expedition: ', vExpeditionFk, ' estaba parada'),
|
||||
CONCAT('Expedition: ', vExpeditionFk,' vBufferToFk: ', vBufferToFk)
|
||||
);
|
||||
|
||||
END LOOP bucle;
|
||||
|
||||
CLOSE cur;
|
||||
UPDATE expedition
|
||||
SET `position` = `position` - 1
|
||||
WHERE bufferFk = vBufferFromFk
|
||||
AND `position` > 0;
|
||||
|
||||
COMMIT;
|
||||
END LOOP l;
|
||||
CLOSE vSorter;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `srt`.`buffer_afterDelete`
|
||||
AFTER DELETE ON `buffer`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO buffer
|
||||
SET `action` = 'delete',
|
||||
`changedModel` = 'Buffer',
|
||||
`changedModelId` = OLD.id,
|
||||
`userFk` = account.myUser_getId();
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,8 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `srt`.`buffer_beforeInsert`
|
||||
BEFORE INSERT ON `buffer`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
SET NEW.editorFk = account.myUser_getId();
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,8 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `srt`.`buffer_beforeUpdate`
|
||||
BEFORE UPDATE ON `buffer`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
SET NEW.editorFk = account.myUser_getId();
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,12 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `srt`.`config_afterDelete`
|
||||
AFTER DELETE ON `config`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO config
|
||||
SET `action` = 'delete',
|
||||
`changedModel` = 'Config',
|
||||
`changedModelId` = OLD.id,
|
||||
`userFk` = account.myUser_getId();
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,8 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `srt`.`config_beforeInsert`
|
||||
BEFORE INSERT ON `config`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
SET NEW.editorFk = account.myUser_getId();
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,8 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `srt`.`config_beforeUpdate`
|
||||
BEFORE UPDATE ON `config`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
SET NEW.editorFk = account.myUser_getId();
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -1,26 +1,32 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn`.`invoiceSerial`(vClientFk INT, vCompanyFk INT, vType CHAR(1))
|
||||
RETURNS char(1) CHARSET utf8mb3 COLLATE utf8mb3_general_ci
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn`.`invoiceSerial`(vClientFk INT, vCompanyFk INT, vType CHAR(15))
|
||||
RETURNS char(2) CHARSET utf8mb3 COLLATE utf8mb3_general_ci
|
||||
DETERMINISTIC
|
||||
BEGIN
|
||||
/**
|
||||
* Obtiene la serie de de una factura
|
||||
* Obtiene la serie de una factura
|
||||
* dependiendo del area del cliente.
|
||||
*
|
||||
* @param vClientFk Id del cliente
|
||||
* @param vCompanyFk Id de la empresa
|
||||
* @param vType Tipo de factura ["R", "M", "G"]
|
||||
* @return Serie de la factura
|
||||
* @param vType Tipo de factura ['global','multiple','quick']
|
||||
* @return vSerie de la factura
|
||||
*/
|
||||
DECLARE vTaxArea VARCHAR(25);
|
||||
DECLARE vSerie CHAR(1);
|
||||
DECLARE vTaxArea VARCHAR(25) COLLATE utf8mb3_general_ci;
|
||||
DECLARE vSerie CHAR(2);
|
||||
|
||||
IF (SELECT hasInvoiceSimplified FROM client WHERE id = vClientFk) THEN
|
||||
RETURN 'S';
|
||||
END IF;
|
||||
|
||||
SELECT clientTaxArea(vClientFk, vCompanyFk) INTO vTaxArea;
|
||||
SELECT invoiceSerialArea(vType,vTaxArea) INTO vSerie;
|
||||
SELECT addressTaxArea(defaultAddressFk, vCompanyFk) INTO vTaxArea
|
||||
FROM client
|
||||
WHERE id = vClientFk;
|
||||
|
||||
SELECT code INTO vSerie
|
||||
FROM invoiceOutSerial
|
||||
WHERE `type` = vType AND taxAreaFk = vTaxArea;
|
||||
|
||||
RETURN vSerie;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn`.`invoiceSerialArea`(vType CHAR(1), vTaxArea VARCHAR(25))
|
||||
RETURNS char(1) CHARSET utf8mb3 COLLATE utf8mb3_unicode_ci
|
||||
DETERMINISTIC
|
||||
BEGIN
|
||||
DECLARE vSerie CHAR(1);
|
||||
|
||||
IF vType = 'R' THEN
|
||||
SELECT
|
||||
CASE vTaxArea
|
||||
WHEN 'CEE' THEN 'H'
|
||||
WHEN 'WORLD' THEN 'E'
|
||||
ELSE 'T'
|
||||
END INTO vSerie;
|
||||
-- Factura multiple
|
||||
ELSEIF vType = 'M' THEN
|
||||
SELECT
|
||||
CASE vTaxArea
|
||||
WHEN 'CEE' THEN 'H'
|
||||
WHEN 'WORLD' THEN 'E'
|
||||
ELSE 'M'
|
||||
END INTO vSerie;
|
||||
-- Factura global
|
||||
ELSEIF vType = 'G' THEN
|
||||
SELECT
|
||||
CASE vTaxArea
|
||||
WHEN 'CEE' THEN 'V'
|
||||
WHEN 'WORLD' THEN 'X'
|
||||
ELSE 'A'
|
||||
END INTO vSerie;
|
||||
END IF;
|
||||
RETURN vSerie;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -97,7 +97,7 @@ BEGIN
|
|||
AND (vCorrectingSerial = vSerial OR NOT hasAnyNegativeBase())
|
||||
THEN
|
||||
|
||||
-- el trigger añade el siguiente Id_Factura correspondiente a la vSerial
|
||||
-- el trigger añade el siguiente ref correspondiente a la vSerial
|
||||
INSERT INTO invoiceOut(
|
||||
ref,
|
||||
serial,
|
||||
|
|
|
@ -189,7 +189,7 @@ BEGIN
|
|||
SELECT * FROM sales
|
||||
UNION ALL
|
||||
SELECT * FROM orders
|
||||
ORDER BY shipped DESC,
|
||||
ORDER BY shipped,
|
||||
(inventorySupplierFk = entityId) DESC,
|
||||
alertLevel DESC,
|
||||
isTicket,
|
||||
|
|
|
@ -90,7 +90,7 @@ BEGIN
|
|||
IF vIsTaxDataChecked THEN
|
||||
CALL invoiceOut_newFromClient(
|
||||
vClientFk,
|
||||
(SELECT invoiceSerial(vClientFk, vCompanyFk, 'M')),
|
||||
(SELECT invoiceSerial(vClientFk, vCompanyFk, 'multiple')),
|
||||
vShipped,
|
||||
vCompanyFk,
|
||||
NULL,
|
||||
|
|
|
@ -14,16 +14,28 @@ BEGIN
|
|||
DECLARE vTicketFk INT;
|
||||
|
||||
DECLARE cTickets CURSOR FOR
|
||||
SELECT id FROM ticket
|
||||
WHERE refFk IS NULL
|
||||
AND ((vScope = 'client' AND clientFk = vId)
|
||||
OR (vScope = 'address' AND addressFk = vId));
|
||||
SELECT DISTINCT t.id
|
||||
FROM ticket t
|
||||
LEFT JOIN tItems ti ON ti.id = t.id
|
||||
WHERE t.refFk IS NULL
|
||||
AND ((vScope = 'client' AND t.clientFk = vId)
|
||||
OR (vScope = 'address' AND t.addressFk = vId)
|
||||
OR (vScope = 'item' AND ti.id)
|
||||
);
|
||||
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND
|
||||
SET vDone = TRUE;
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
|
||||
|
||||
CREATE OR REPLACE TEMPORARY TABLE tItems
|
||||
(PRIMARY KEY (id))
|
||||
ENGINE = MEMORY
|
||||
SELECT DISTINCT t.id
|
||||
FROM ticket t
|
||||
JOIN sale s ON s.ticketFk = t.id
|
||||
JOIN itemTaxCountry itc ON itc.itemFk = s.itemFk
|
||||
WHERE t.refFk IS NULL
|
||||
AND (vScope = 'item' AND itc.itemFk = vId);
|
||||
|
||||
OPEN cTickets;
|
||||
|
||||
myLoop: LOOP
|
||||
SET vDone = FALSE;
|
||||
FETCH cTickets INTO vTicketFk;
|
||||
|
@ -34,7 +46,8 @@ BEGIN
|
|||
|
||||
CALL ticket_recalc(vTicketFk, NULL);
|
||||
END LOOP;
|
||||
|
||||
CLOSE cTickets;
|
||||
|
||||
DROP TEMPORARY TABLE tItems;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`roadmap_beforeInsert`
|
||||
BEFORE INSERT ON `roadmap`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF NEW.driver1Fk IS NOT NULL THEN
|
||||
SET NEW.driverName = (SELECT firstName FROM worker WHERE id = NEW.driver1Fk);
|
||||
ELSE
|
||||
SET NEW.driverName = NULL;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,12 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`roadmap_beforeUpdate`
|
||||
BEFORE UPDATE ON `roadmap`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF NEW.driver1Fk IS NOT NULL THEN
|
||||
SET NEW.driverName = (SELECT firstName FROM worker WHERE id = NEW.driver1Fk);
|
||||
ELSE
|
||||
SET NEW.driverName = NULL;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -2,5 +2,5 @@ CREATE OR REPLACE DEFINER=`root`@`localhost`
|
|||
SQL SECURITY DEFINER
|
||||
VIEW `vn2008`.`mandato_tipo`
|
||||
AS SELECT `m`.`id` AS `idmandato_tipo`,
|
||||
`m`.`name` AS `Nombre`
|
||||
`m`.`code` AS `Nombre`
|
||||
FROM `vn`.`mandateType` `m`
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE srt.moving DROP INDEX moving_fk1_idx;
|
|
@ -0,0 +1,4 @@
|
|||
ALTER TABLE vn.invoiceOutSerial
|
||||
MODIFY COLUMN `type` enum('global','quick','multiple') CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL NULL;
|
||||
|
||||
CREATE UNIQUE INDEX invoiceOutSerial_taxAreaFk_IDX USING BTREE ON vn.invoiceOutSerial (taxAreaFk,`type`);
|
|
@ -0,0 +1,3 @@
|
|||
UPDATE vn.invoiceOutSerial
|
||||
SET `type`='multiple'
|
||||
WHERE `description` LIKE '%Múltiple%';
|
|
@ -0,0 +1,3 @@
|
|||
ALTER TABLE vn.mandateType
|
||||
CHANGE name code VARCHAR(45) NOT NULL,
|
||||
ADD UNIQUE (code);
|
|
@ -0,0 +1,3 @@
|
|||
ALTER TABLE vn.accountDetailType
|
||||
ADD COLUMN code VARCHAR(45),
|
||||
ADD UNIQUE (code);
|
|
@ -0,0 +1,9 @@
|
|||
UPDATE vn.accountDetailType
|
||||
SET code = CASE description
|
||||
WHEN 'IBAN' THEN 'iban'
|
||||
WHEN 'SWIFT' THEN 'swift'
|
||||
WHEN 'Referencia Remesas' THEN 'remRef'
|
||||
WHEN 'Referencia Transferencias' THEN 'trnRef'
|
||||
WHEN 'Referencia Nominas' THEN 'payRef'
|
||||
WHEN 'ABA' THEN 'aba'
|
||||
END;
|
|
@ -0,0 +1,3 @@
|
|||
ALTER TABLE hedera.tpvMerchantEnable
|
||||
MODIFY COLUMN companyFk int(10) unsigned NOT NULL,
|
||||
ADD CONSTRAINT tpvMerchantEnable_company_FK FOREIGN KEY (companyFk) REFERENCES vn.company(id) ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
@ -0,0 +1,19 @@
|
|||
CREATE OR REPLACE TABLE `srt`.`bufferLog` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`originFk` int(11) DEFAULT NULL,
|
||||
`userFk` int(10) unsigned DEFAULT NULL,
|
||||
`action` set('insert','update','delete','select') NOT NULL,
|
||||
`creationDate` timestamp NULL DEFAULT current_timestamp(),
|
||||
`description` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
|
||||
`changedModel` enum('Buffer', 'Config') NOT NULL DEFAULT 'Buffer',
|
||||
`oldInstance` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`oldInstance`)),
|
||||
`newInstance` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`newInstance`)),
|
||||
`changedModelId` int(11) NOT NULL,
|
||||
`changedModelValue` varchar(45) DEFAULT NULL,
|
||||
`summaryId` varchar(30) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `logBufferUserFk` (`userFk`),
|
||||
KEY `bufferLog_changedModel` (`changedModel`,`changedModelId`,`creationDate`),
|
||||
KEY `bufferLog_originFk` (`originFk`,`creationDate`),
|
||||
CONSTRAINT `bufferUserFk` FOREIGN KEY (`userFk`) REFERENCES `account`.`user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE srt.buffer ADD editorFk int(10) unsigned DEFAULT NULL NULL;
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE srt.config ADD editorFk int(10) unsigned DEFAULT NULL NULL;
|
|
@ -0,0 +1,9 @@
|
|||
CREATE TABLE vn.quadMindsApiConfig (
|
||||
id int(10) unsigned NULL PRIMARY KEY,
|
||||
`url` varchar(255) DEFAULT NULL NULL,
|
||||
`key` varchar(255) DEFAULT NULL NULL,
|
||||
CONSTRAINT quadMindsConfig_check CHECK (id = 1)
|
||||
)
|
||||
ENGINE=InnoDB
|
||||
DEFAULT CHARSET=utf8mb3
|
||||
COLLATE=utf8mb3_unicode_ci;
|
|
@ -0,0 +1,6 @@
|
|||
ALTER TABLE vn.roadmap
|
||||
ADD COLUMN m3 INT UNSIGNED NULL,
|
||||
ADD COLUMN driver2Fk INT UNSIGNED NULL,
|
||||
ADD COLUMN driver1Fk INT UNSIGNED NULL,
|
||||
ADD CONSTRAINT roadmap_worker_FK FOREIGN KEY (driver1Fk) REFERENCES vn.worker(id) ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||
ADD CONSTRAINT roadmap_worker_FK_2 FOREIGN KEY (driver2Fk) REFERENCES vn.worker(id) ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
@ -0,0 +1,24 @@
|
|||
DELETE FROM `salix`.`ACL`
|
||||
WHERE `model` = 'Ticket'
|
||||
AND `property` = 'refund'
|
||||
AND `accessType` = 'WRITE'
|
||||
AND `permission` = 'ALLOW'
|
||||
AND `principalType` = 'ROLE'
|
||||
AND `principalId` = 'salesAssistant';
|
||||
|
||||
UPDATE `salix`.`ACL`
|
||||
SET `property` = 'cloneAll'
|
||||
WHERE `model` = 'Ticket'
|
||||
AND `property` = 'refund'
|
||||
AND `accessType` = 'WRITE'
|
||||
AND `permission` = 'ALLOW'
|
||||
AND `principalType` = 'ROLE'
|
||||
AND `principalId` IN ('invoicing', 'claimManager', 'logistic');
|
||||
|
||||
DELETE FROM `salix`.`ACL`
|
||||
WHERE `model` = 'Ticket'
|
||||
AND `property` = 'clone'
|
||||
AND `accessType` = 'WRITE'
|
||||
AND `permission` = 'ALLOW'
|
||||
AND `principalType` = 'ROLE'
|
||||
AND `principalId` = 'administrative';
|
|
@ -108,7 +108,7 @@ module.exports = Self => {
|
|||
|
||||
async function notifyStateChange(ctx, workerId, claim, newState) {
|
||||
const models = Self.app.models;
|
||||
const url = await models.Url.getUrl();
|
||||
const url = await models.Url.getUrl('lilium');
|
||||
const $t = ctx.req.__;
|
||||
|
||||
const message = $t(`Claim state has changed to`, {
|
||||
|
@ -122,7 +122,7 @@ module.exports = Self => {
|
|||
|
||||
async function notifyPickUp(ctx, workerId, claim) {
|
||||
const models = Self.app.models;
|
||||
const url = await models.Url.getUrl();
|
||||
const url = await models.Url.getUrl('lilium');
|
||||
const $t = ctx.req.__; // $translate
|
||||
|
||||
const message = $t('Claim will be picked', {
|
||||
|
|
|
@ -27,7 +27,7 @@ module.exports = Self => {
|
|||
// Renew mandate
|
||||
if (mandate) {
|
||||
const mandateType = await models.MandateType.findOne({
|
||||
where: {name: mandate.type}
|
||||
where: {code: mandate.type}
|
||||
});
|
||||
|
||||
const oldMandate = await models.Mandate.findOne({
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"type": "number",
|
||||
"description": "Identifier"
|
||||
},
|
||||
"name": {
|
||||
"code": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<vn-tr ng-repeat="mandate in mandates">
|
||||
<vn-td number>{{::mandate.id}}</vn-td>
|
||||
<vn-td>{{::mandate.company.code}}</vn-td>
|
||||
<vn-td>{{::mandate.mandateType.name}}</vn-td>
|
||||
<vn-td>{{::mandate.mandateType.code}}</vn-td>
|
||||
<vn-td shrink-datetime>{{::mandate.created | date:'dd/MM/yyyy HH:mm' | dashIfEmpty}}</vn-td>
|
||||
<vn-td shrink-datetime>{{::mandate.finished | date:'dd/MM/yyyy HH:mm' | dashIfEmpty}}</vn-td>
|
||||
</vn-tr>
|
||||
|
|
|
@ -9,7 +9,7 @@ class Controller extends Section {
|
|||
{
|
||||
relation: 'mandateType',
|
||||
scope: {
|
||||
fields: ['id', 'name']
|
||||
fields: ['id', 'code']
|
||||
}
|
||||
}, {
|
||||
relation: 'company',
|
||||
|
|
|
@ -46,7 +46,7 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
filter = mergeFilters(args.filter, {where});
|
||||
const filter = mergeFilters(args.filter, {where});
|
||||
|
||||
const stmt = new ParameterizedSQL(
|
||||
`SELECT i.serial, SUM(IF(i.isBooked, 0,1)) pending, COUNT(*) total
|
||||
|
|
|
@ -75,7 +75,7 @@ module.exports = Self => {
|
|||
AND c.isTaxDataChecked
|
||||
AND c.isActive
|
||||
AND NOT t.isDeleted
|
||||
GROUP BY c.id, IF(c.hasToInvoiceByAddress, a.id, TRUE)
|
||||
GROUP BY IF(c.hasToInvoiceByAddress, a.id, c.id)
|
||||
HAVING SUM(t.totalWithVat) > 0;`;
|
||||
|
||||
const addresses = await Self.rawSql(query, [
|
||||
|
|
|
@ -28,6 +28,11 @@ module.exports = Self => {
|
|||
type: 'number',
|
||||
description: 'The company id to invoice',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'serialType',
|
||||
type: 'string',
|
||||
description: 'Invoice serial number type (see vn.invoiceOutSerial.type enum)',
|
||||
required: true
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
|
@ -39,12 +44,10 @@ module.exports = Self => {
|
|||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.invoiceClient = async(ctx, options) => {
|
||||
const args = ctx.args;
|
||||
const models = Self.app.models;
|
||||
options = typeof options == 'object'
|
||||
? Object.assign({}, options) : {};
|
||||
options = typeof options === 'object' ? {...options} : {};
|
||||
options.userId = ctx.req.accessToken.userId;
|
||||
|
||||
let tx;
|
||||
|
@ -74,10 +77,9 @@ module.exports = Self => {
|
|||
], options);
|
||||
}
|
||||
|
||||
const invoiceType = 'G';
|
||||
const invoiceId = await models.Ticket.makeInvoice(
|
||||
ctx,
|
||||
invoiceType,
|
||||
args.serialType,
|
||||
args.companyFk,
|
||||
args.invoiceDate,
|
||||
null,
|
||||
|
|
|
@ -43,7 +43,7 @@ module.exports = Self => {
|
|||
const tickets = await models.Ticket.find(filter, myOptions);
|
||||
|
||||
const ticketsIds = tickets.map(ticket => ticket.id);
|
||||
const refundedTickets = await models.Ticket.refund(ctx, ticketsIds, withWarehouse, myOptions);
|
||||
const refundedTickets = await models.Ticket.cloneAll(ctx, ticketsIds, withWarehouse, true, myOptions);
|
||||
|
||||
if (tx) await tx.commit();
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('InvoiceOut clientsToInvoice()', () => {
|
||||
const userId = 1;
|
||||
const clientId = 1101;
|
||||
const companyFk = 442;
|
||||
const maxShipped = new Date();
|
||||
maxShipped.setMonth(11);
|
||||
maxShipped.setDate(31);
|
||||
maxShipped.setHours(23, 59, 59, 999);
|
||||
const invoiceDate = new Date();
|
||||
const activeCtx = {
|
||||
getLocale: () => {
|
||||
return 'en';
|
||||
},
|
||||
accessToken: {userId: userId},
|
||||
__: value => {
|
||||
return value;
|
||||
},
|
||||
headers: {origin: 'http://localhost'}
|
||||
};
|
||||
const ctx = {req: activeCtx};
|
||||
|
||||
it('should return a list of clients to invoice', async() => {
|
||||
spyOn(models.InvoiceOut, 'rawSql').and.callFake(query => {
|
||||
if (query.includes('ticketPackaging_add'))
|
||||
return Promise.resolve(true);
|
||||
else if (query.includes('SELECT c.id clientId')) {
|
||||
return Promise.resolve([
|
||||
{
|
||||
clientId: clientId,
|
||||
clientName: 'Test Client',
|
||||
id: 1,
|
||||
nickname: 'Address 1'
|
||||
}
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
const tx = await models.InvoiceOut.beginTransaction({});
|
||||
const options = {transaction: tx};
|
||||
|
||||
try {
|
||||
const addresses = await models.InvoiceOut.clientsToInvoice(
|
||||
ctx, clientId, invoiceDate, maxShipped, companyFk, options);
|
||||
|
||||
expect(addresses.length).toBeGreaterThan(0);
|
||||
expect(addresses[0].clientId).toBe(clientId);
|
||||
expect(addresses[0].clientName).toBe('Test Client');
|
||||
expect(addresses[0].id).toBe(1);
|
||||
expect(addresses[0].nickname).toBe('Address 1');
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle errors and rollback transaction', async() => {
|
||||
spyOn(models.InvoiceOut, 'rawSql').and.callFake(() => {
|
||||
return Promise.reject(new Error('Test Error'));
|
||||
});
|
||||
|
||||
const tx = await models.InvoiceOut.beginTransaction({});
|
||||
const options = {transaction: tx};
|
||||
|
||||
try {
|
||||
await models.InvoiceOut.clientsToInvoice(ctx, clientId, invoiceDate, maxShipped, companyFk, options);
|
||||
} catch (e) {
|
||||
expect(e.message).toBe('Test Error');
|
||||
await tx.rollback();
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,16 +1,16 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
describe('InvoiceOut invoiceClient()', () => {
|
||||
const userId = 1;
|
||||
const clientId = 1101;
|
||||
const addressId = 121;
|
||||
const addressFk = 121;
|
||||
const companyFk = 442;
|
||||
const minShipped = Date.vnNew();
|
||||
minShipped.setFullYear(minShipped.getFullYear() - 1);
|
||||
minShipped.setMonth(1);
|
||||
minShipped.setDate(1);
|
||||
minShipped.setHours(0, 0, 0, 0);
|
||||
const invoiceSerial = 'A';
|
||||
const activeCtx = {
|
||||
getLocale: () => {
|
||||
return 'en';
|
||||
|
@ -23,9 +23,14 @@ describe('InvoiceOut invoiceClient()', () => {
|
|||
|
||||
};
|
||||
const ctx = {req: activeCtx};
|
||||
beforeAll(() => {
|
||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||
active: activeCtx
|
||||
});
|
||||
});
|
||||
|
||||
it('should make a global invoicing', async() => {
|
||||
spyOn(models.InvoiceOut, 'makePdf').and.returnValue(new Promise(resolve => resolve(true)));
|
||||
it('should make a global invoicing by address and verify billing status', async() => {
|
||||
spyOn(models.InvoiceOut, 'makePdf').and.returnValue(Promise.resolve(true));
|
||||
spyOn(models.InvoiceOut, 'invoiceEmail');
|
||||
|
||||
const tx = await models.InvoiceOut.beginTransaction({});
|
||||
|
@ -34,20 +39,96 @@ describe('InvoiceOut invoiceClient()', () => {
|
|||
try {
|
||||
ctx.args = {
|
||||
clientId: clientId,
|
||||
addressId: addressId,
|
||||
addressId: addressFk,
|
||||
invoiceDate: Date.vnNew(),
|
||||
maxShipped: Date.vnNew(),
|
||||
companyFk: companyFk,
|
||||
minShipped: minShipped
|
||||
serialType: 'global'
|
||||
};
|
||||
|
||||
const invoiceOutId = await models.InvoiceOut.invoiceClient(ctx, options);
|
||||
|
||||
const invoiceOut = await models.InvoiceOut.findById(invoiceOutId, null, options);
|
||||
const [firstTicket] = await models.Ticket.find({
|
||||
|
||||
expect(invoiceOutId).toBeGreaterThan(0);
|
||||
|
||||
const allClientTickets = await models.Ticket.find({
|
||||
where: {
|
||||
clientFk: clientId,
|
||||
or: [
|
||||
{refFk: null},
|
||||
{refFk: invoiceOut.ref}
|
||||
]
|
||||
}
|
||||
}, options);
|
||||
|
||||
const billedTickets = await models.Ticket.find({
|
||||
where: {refFk: invoiceOut.ref}
|
||||
}, options);
|
||||
|
||||
const allBilledTicketsHaveCorrectAddress = billedTickets.every(ticket => ticket.addressFk === addressFk);
|
||||
|
||||
expect(allBilledTicketsHaveCorrectAddress).toBe(true);
|
||||
|
||||
const addressTickets = allClientTickets.filter(ticket => ticket.addressFk === addressFk);
|
||||
|
||||
const allAddressTicketsBilled = addressTickets.every(ticket => ticket.refFk === invoiceOut.ref);
|
||||
|
||||
expect(allAddressTicketsBilled).toBe(true);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should invoice all tickets regardless of address when hasToInvoiceByAddress is false', async() => {
|
||||
spyOn(models.InvoiceOut, 'makePdf').and.returnValue(Promise.resolve(true));
|
||||
spyOn(models.InvoiceOut, 'invoiceEmail');
|
||||
|
||||
const tx = await models.InvoiceOut.beginTransaction({});
|
||||
const options = {transaction: tx};
|
||||
|
||||
try {
|
||||
const client = await models.Client.findById(clientId, null, options);
|
||||
await client.updateAttribute('hasToInvoiceByAddress', false, options);
|
||||
|
||||
ctx.args = {
|
||||
clientId: clientId,
|
||||
invoiceDate: Date.vnNew(),
|
||||
maxShipped: Date.vnNew(),
|
||||
companyFk: companyFk,
|
||||
serialType: 'global'
|
||||
};
|
||||
|
||||
const invoiceOutId = await models.InvoiceOut.invoiceClient(ctx, options);
|
||||
|
||||
const invoiceOut = await models.InvoiceOut.findById(invoiceOutId, null, options);
|
||||
|
||||
expect(invoiceOutId).toBeGreaterThan(0);
|
||||
expect(firstTicket.refFk).toContain(invoiceSerial);
|
||||
|
||||
const allClientTickets = await models.Ticket.find({
|
||||
where: {
|
||||
clientFk: clientId,
|
||||
or: [
|
||||
{refFk: null},
|
||||
{refFk: invoiceOut.ref}
|
||||
]
|
||||
}
|
||||
}, options);
|
||||
|
||||
const billedTickets = await models.Ticket.find({
|
||||
where: {refFk: invoiceOut.ref}
|
||||
}, options);
|
||||
|
||||
const allTicketsBilled = allClientTickets.every(ticket => ticket.refFk === invoiceOut.ref);
|
||||
|
||||
expect(allTicketsBilled).toBe(true);
|
||||
|
||||
const billedAddresses = new Set(billedTickets.map(ticket => ticket.addressFk));
|
||||
|
||||
expect(billedAddresses.size).toBeGreaterThan(1);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
|
|
|
@ -82,20 +82,12 @@ module.exports = Self => {
|
|||
myOptions.transaction = tx;
|
||||
}
|
||||
try {
|
||||
const filterRef = {where: {refFk: refFk}};
|
||||
const tickets = await models.Ticket.find(filterRef, myOptions);
|
||||
const tickets = await models.Ticket.find({where: {refFk}}, myOptions);
|
||||
const ticketsIds = tickets.map(ticket => ticket.id);
|
||||
const refundTickets = await models.Ticket.refund(ctx, ticketsIds, null, myOptions);
|
||||
const refundTickets = await models.Ticket.cloneAll(ctx, ticketsIds, false, true, myOptions);
|
||||
|
||||
const filterTicket = {where: {ticketFk: {inq: ticketsIds}}};
|
||||
const clonedTickets = await models.Ticket.cloneAll(ctx, ticketsIds, false, false, myOptions);
|
||||
|
||||
const services = await models.TicketService.find(filterTicket, myOptions);
|
||||
const servicesIds = services.map(service => service.id);
|
||||
|
||||
const sales = await models.Sale.find(filterTicket, myOptions);
|
||||
const salesIds = sales.map(sale => sale.id);
|
||||
|
||||
const clonedTickets = await models.Sale.clone(ctx, salesIds, servicesIds, null, false, myOptions);
|
||||
const clonedTicketIds = [];
|
||||
|
||||
for (const clonedTicket of clonedTickets) {
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
},
|
||||
"isCEE": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -88,28 +88,7 @@
|
|||
translate>
|
||||
Show CITES letter
|
||||
</vn-item>
|
||||
<vn-item class="dropdown"
|
||||
vn-click-stop="refundMenu.show($event, 'left')"
|
||||
vn-tooltip="Create a refund ticket for each ticket on the current invoice"
|
||||
vn-acl="invoicing, claimManager, salesAssistant"
|
||||
vn-acl-action="remove"
|
||||
translate>
|
||||
Refund...
|
||||
<vn-menu vn-id="refundMenu">
|
||||
<vn-list>
|
||||
<vn-item
|
||||
ng-click="$ctrl.refundInvoiceOut(true)"
|
||||
translate>
|
||||
with warehouse
|
||||
</vn-item>
|
||||
<vn-item
|
||||
ng-click="$ctrl.refundInvoiceOut(false)"
|
||||
translate>
|
||||
without warehouse
|
||||
</vn-item>
|
||||
</vn-list>
|
||||
</vn-menu>
|
||||
</vn-item>
|
||||
|
||||
</vn-list>
|
||||
</vn-menu>
|
||||
<vn-confirm
|
||||
|
|
|
@ -135,21 +135,6 @@ class Controller extends Section {
|
|||
});
|
||||
}
|
||||
|
||||
refundInvoiceOut(withWarehouse) {
|
||||
const query = 'InvoiceOuts/refund';
|
||||
const params = {ref: this.invoiceOut.ref, withWarehouse: withWarehouse};
|
||||
this.$http.post(query, params).then(res => {
|
||||
const tickets = res.data;
|
||||
const refundTickets = tickets.map(ticket => ticket.id);
|
||||
|
||||
this.vnApp.showSuccess(this.$t('The following refund tickets have been created', {
|
||||
ticketId: refundTickets.join(',')
|
||||
}));
|
||||
if (refundTickets.length == 1)
|
||||
this.$state.go('ticket.card.sale', {id: refundTickets[0]});
|
||||
});
|
||||
}
|
||||
|
||||
transferInvoice() {
|
||||
const params = {
|
||||
id: this.invoiceOut.id,
|
||||
|
|
|
@ -105,17 +105,4 @@ describe('vnInvoiceOutDescriptorMenu', () => {
|
|||
expect(controller.vnApp.showMessage).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('refundInvoiceOut()', () => {
|
||||
it('should make a query and show a success message', () => {
|
||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||
const params = {ref: controller.invoiceOut.ref};
|
||||
|
||||
$httpBackend.expectPOST(`InvoiceOuts/refund`, params).respond([{id: 1}, {id: 2}]);
|
||||
controller.refundInvoiceOut();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,40 +1,25 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('clone', {
|
||||
description: 'Clone sales and services provided',
|
||||
description: 'Clone sales, services, and ticket packaging provided',
|
||||
accessType: 'WRITE',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'salesIds',
|
||||
type: ['number'],
|
||||
}, {
|
||||
arg: 'servicesIds',
|
||||
type: ['number']
|
||||
}, {
|
||||
arg: 'withWarehouse',
|
||||
type: 'boolean',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'negative',
|
||||
type: 'boolean'
|
||||
}
|
||||
{arg: 'salesIds', type: ['number']},
|
||||
{arg: 'servicesIds', type: ['number']},
|
||||
{arg: 'ticketPackagingIds', type: ['number']},
|
||||
{arg: 'withWarehouse', type: 'boolean', required: true},
|
||||
{arg: 'negative', type: 'boolean'}
|
||||
],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/clone`,
|
||||
verb: 'POST'
|
||||
}
|
||||
returns: {type: ['object'], root: true},
|
||||
http: {path: `/clone`, verb: 'POST'}
|
||||
});
|
||||
Self.clone = async(ctx, salesIds, servicesIds, withWarehouse, negative, options) => {
|
||||
|
||||
Self.clone = async(ctx, salesIds, servicesIds, ticketPackagingIds, withWarehouse, negative, options) => {
|
||||
const models = Self.app.models;
|
||||
const myOptions = {};
|
||||
let tx;
|
||||
const newTickets = [];
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
if (typeof options === 'object') Object.assign(myOptions, options);
|
||||
|
||||
if (!myOptions.transaction) {
|
||||
tx = await Self.beginTransaction({});
|
||||
|
@ -44,8 +29,9 @@ module.exports = Self => {
|
|||
try {
|
||||
let sales;
|
||||
let services;
|
||||
let ticketPackaging;
|
||||
|
||||
if (salesIds && salesIds.length) {
|
||||
if (salesIds?.length) {
|
||||
sales = await models.Sale.find({
|
||||
where: {id: {inq: salesIds}},
|
||||
include: {
|
||||
|
@ -57,12 +43,18 @@ module.exports = Self => {
|
|||
}, myOptions);
|
||||
}
|
||||
|
||||
if (servicesIds && servicesIds.length) {
|
||||
if (servicesIds?.length) {
|
||||
services = await models.TicketService.find({
|
||||
where: {id: {inq: servicesIds}}
|
||||
}, myOptions);
|
||||
}
|
||||
|
||||
if (ticketPackagingIds?.length) {
|
||||
ticketPackaging = await models.TicketPackaging.find({
|
||||
where: {id: {inq: ticketPackagingIds}}
|
||||
}, myOptions);
|
||||
}
|
||||
|
||||
let ticketsIds = sales ?
|
||||
[...new Set(sales.map(sale => sale.ticketFk))] :
|
||||
[...new Set(services.map(service => service.ticketFk))];
|
||||
|
@ -74,12 +66,12 @@ module.exports = Self => {
|
|||
ctx,
|
||||
ticketId,
|
||||
withWarehouse,
|
||||
negative,
|
||||
myOptions
|
||||
);
|
||||
newTickets.push(newTicket);
|
||||
mappedTickets.set(ticketId, newTicket.id);
|
||||
}
|
||||
|
||||
if (sales) {
|
||||
for (const sale of sales) {
|
||||
const newTicketId = mappedTickets.get(sale.ticketFk);
|
||||
|
@ -107,7 +99,7 @@ module.exports = Self => {
|
|||
|
||||
await models.TicketService.create({
|
||||
description: service.description,
|
||||
quantity: negative ? - service.quantity : service.quantity,
|
||||
quantity: negative ? -service.quantity : service.quantity,
|
||||
price: service.price,
|
||||
taxClassFk: service.taxClassFk,
|
||||
ticketFk: newTicketId,
|
||||
|
@ -116,6 +108,18 @@ module.exports = Self => {
|
|||
}
|
||||
}
|
||||
|
||||
if (ticketPackaging) {
|
||||
for (const packaging of ticketPackaging) {
|
||||
const newTicketId = mappedTickets.get(packaging.ticketFk);
|
||||
|
||||
await models.TicketPackaging.create({
|
||||
ticketFk: newTicketId,
|
||||
packagingFk: packaging.packagingFk,
|
||||
quantity: negative ? -packaging.quantity : packaging.quantity
|
||||
}, myOptions);
|
||||
}
|
||||
}
|
||||
|
||||
if (tx) await tx.commit();
|
||||
|
||||
return newTickets;
|
||||
|
@ -124,13 +128,7 @@ module.exports = Self => {
|
|||
throw e;
|
||||
}
|
||||
|
||||
async function createTicket(
|
||||
ctx,
|
||||
ticketId,
|
||||
withWarehouse,
|
||||
negative,
|
||||
myOptions
|
||||
) {
|
||||
async function createTicket(ctx, ticketId, withWarehouse, myOptions) {
|
||||
const models = Self.app.models;
|
||||
const now = Date.vnNew();
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ describe('Ticket cloning - clone function', () => {
|
|||
const servicesIds = [];
|
||||
const withWarehouse = true;
|
||||
const negative = false;
|
||||
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, withWarehouse, negative, options);
|
||||
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, null, withWarehouse, negative, options);
|
||||
|
||||
expect(newTickets).toBeDefined();
|
||||
expect(newTickets.length).toBeGreaterThan(0);
|
||||
|
@ -30,7 +30,7 @@ describe('Ticket cloning - clone function', () => {
|
|||
const negative = true;
|
||||
const salesIds = [7, 8];
|
||||
const servicesIds = [];
|
||||
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, false, negative, options);
|
||||
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, null, false, negative, options);
|
||||
|
||||
for (const ticket of newTickets) {
|
||||
const sales = await models.Sale.find({where: {ticketFk: ticket.id}}, options);
|
||||
|
@ -43,7 +43,7 @@ describe('Ticket cloning - clone function', () => {
|
|||
it('should create new components and services for cloned tickets', async() => {
|
||||
const servicesIds = [2];
|
||||
const salesIds = [5];
|
||||
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, false, false, options);
|
||||
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, null, false, false, options);
|
||||
|
||||
for (const ticket of newTickets) {
|
||||
const sale = await models.Sale.findOne({where: {ticketFk: ticket.id}}, options);
|
||||
|
@ -58,7 +58,7 @@ describe('Ticket cloning - clone function', () => {
|
|||
it('should create a ticket without sales', async() => {
|
||||
const servicesIds = [4];
|
||||
|
||||
const tickets = await models.Sale.clone(ctx, null, servicesIds, false, false, options);
|
||||
const tickets = await models.Sale.clone(ctx, null, servicesIds, null, false, false, options);
|
||||
const refundedTicket = await getTicketRefund(tickets[0].id, options);
|
||||
|
||||
expect(refundedTicket).toBeDefined();
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('clone', {
|
||||
description: 'clone a ticket and return the new ticket id',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The ticket id',
|
||||
http: {source: 'path'}
|
||||
}, {
|
||||
arg: 'shipped',
|
||||
type: 'date',
|
||||
}, {
|
||||
arg: 'withWarehouse',
|
||||
type: 'boolean',
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'number',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/:id/clone`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.clone = async(ctx, id, shipped, withWarehouse, options) => {
|
||||
const myOptions = {userId: ctx.req.accessToken.userId};
|
||||
let tx;
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
if (!myOptions.transaction) {
|
||||
tx = await Self.beginTransaction({});
|
||||
myOptions.transaction = tx;
|
||||
}
|
||||
|
||||
try {
|
||||
const [, [{clonedTicketId}]] = await Self.rawSql(`
|
||||
CALL vn.ticket_cloneAll(?, ?, ?, @clonedTicketId);
|
||||
SELECT @clonedTicketId clonedTicketId;`,
|
||||
[id, shipped, withWarehouse], myOptions);
|
||||
|
||||
if (tx) await tx.commit();
|
||||
return clonedTicketId;
|
||||
} catch (e) {
|
||||
if (tx) await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -0,0 +1,77 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('cloneAll', {
|
||||
description: 'Clone tickets, sales, services and packages',
|
||||
accessType: 'WRITE',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'ticketsIds',
|
||||
type: ['number'],
|
||||
required: true,
|
||||
description: 'IDs of the tickets to clone'
|
||||
},
|
||||
{
|
||||
arg: 'withWarehouse',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
description: 'true: keep original warehouse; false: set to null'
|
||||
},
|
||||
{
|
||||
arg: 'negative',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
description: 'true: invert quantities; false: keep as is.'
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true,
|
||||
description: 'The cloned tickets with associated data'
|
||||
},
|
||||
http: {
|
||||
path: `/cloneAll`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.cloneAll = async(ctx, ticketsIds, withWarehouse, negative, options) => {
|
||||
const models = Self.app.models;
|
||||
const myOptions = typeof options == 'object' ? {...options} : {};
|
||||
let tx;
|
||||
|
||||
if (!myOptions.transaction) {
|
||||
tx = await Self.beginTransaction({});
|
||||
myOptions.transaction = tx;
|
||||
}
|
||||
|
||||
try {
|
||||
const filter = {where: {ticketFk: {inq: ticketsIds}}};
|
||||
|
||||
const [sales, services, ticketPackaging] = await Promise.all([
|
||||
models.Sale.find(filter, myOptions),
|
||||
models.TicketService.find(filter, myOptions),
|
||||
models.TicketPackaging.find(filter, myOptions)
|
||||
]);
|
||||
|
||||
const salesIds = sales.map(({id}) => id);
|
||||
const servicesIds = services.map(({id}) => id);
|
||||
const ticketPackagingIds = ticketPackaging.map(({id}) => id);
|
||||
|
||||
const clonedTickets = await models.Sale.clone(
|
||||
ctx,
|
||||
salesIds,
|
||||
servicesIds,
|
||||
ticketPackagingIds,
|
||||
withWarehouse,
|
||||
negative,
|
||||
myOptions
|
||||
);
|
||||
|
||||
if (tx) await tx.commit();
|
||||
|
||||
return clonedTickets;
|
||||
} catch (e) {
|
||||
if (tx) await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -114,7 +114,7 @@ module.exports = Self => {
|
|||
LEFT JOIN itemTaxCountry itc ON itc.itemFk = i.id
|
||||
AND itc.countryFk = su.countryFk
|
||||
LEFT JOIN vn.invoiceOutSerial ios ON ios.taxAreaFk = 'WORLD'
|
||||
AND ios.code = invoiceSerial(t.clientFk, t.companyFk, 'M')
|
||||
AND ios.code = invoiceSerial(t.clientFk, t.companyFk, 'multiple')
|
||||
WHERE (al.code = 'PACKED' OR (am.code = 'refund' AND al.code <> 'delivered'))
|
||||
AND DATE(t.shipped) BETWEEN ? - INTERVAL 2 DAY AND util.dayEnd(?)
|
||||
AND t.refFk IS NULL
|
||||
|
|
|
@ -95,7 +95,7 @@ module.exports = function(Self) {
|
|||
FROM vn.ticket
|
||||
WHERE id IN (?)
|
||||
`, [ticketsIds], myOptions);
|
||||
return models.Ticket.makeInvoice(ctx, 'R', companyId, Date.vnNew(), invoiceCorrection, myOptions);
|
||||
return models.Ticket.makeInvoice(ctx, 'quick', companyId, Date.vnNew(), invoiceCorrection, myOptions);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('refund', {
|
||||
description: 'Create refund tickets with all their sales and services',
|
||||
accessType: 'WRITE',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'ticketsIds',
|
||||
type: ['number'],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'withWarehouse',
|
||||
type: 'boolean',
|
||||
required: true
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/refund`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.refund = async(ctx, ticketsIds, withWarehouse, options) => {
|
||||
const models = Self.app.models;
|
||||
const myOptions = {};
|
||||
let tx;
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
if (!myOptions.transaction) {
|
||||
tx = await Self.beginTransaction({});
|
||||
myOptions.transaction = tx;
|
||||
}
|
||||
|
||||
try {
|
||||
const filter = {where: {ticketFk: {inq: ticketsIds}}};
|
||||
const sales = await models.Sale.find(filter, myOptions);
|
||||
const salesIds = sales.map(sale => sale.id);
|
||||
|
||||
const services = await models.TicketService.find(filter, myOptions);
|
||||
const servicesIds = services.map(service => service.id);
|
||||
|
||||
const refundedTickets = await models.Sale.clone(ctx, salesIds, servicesIds, withWarehouse, true, myOptions);
|
||||
|
||||
if (tx) await tx.commit();
|
||||
|
||||
return refundedTickets;
|
||||
} catch (e) {
|
||||
if (tx) await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -1,43 +0,0 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('Ticket cloning - clone function', () => {
|
||||
const ctx = beforeAll.getCtx();
|
||||
let options;
|
||||
let tx;
|
||||
const ticketId = 1;
|
||||
const shipped = Date.vnNew();
|
||||
|
||||
beforeEach(async() => {
|
||||
options = {transaction: tx};
|
||||
tx = await models.Ticket.beginTransaction({});
|
||||
options.transaction = tx;
|
||||
});
|
||||
|
||||
afterEach(async() => {
|
||||
await tx.rollback();
|
||||
});
|
||||
|
||||
it('should clone a new ticket without warehouse', async() => {
|
||||
const originalTicket = await models.Ticket.findById(ticketId, null, options);
|
||||
|
||||
const newTicketId = await models.Ticket.clone(ctx, ticketId, shipped, false, options);
|
||||
const newTicket = await models.Ticket.findById(newTicketId, null, options);
|
||||
|
||||
expect(newTicket.clientFk).toEqual(originalTicket.clientFk);
|
||||
expect(newTicket.companyFk).toEqual(originalTicket.companyFk);
|
||||
expect(newTicket.addressFk).toEqual(originalTicket.addressFk);
|
||||
expect(newTicket.warehouseFk).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should clone a new ticket with warehouse', async() => {
|
||||
const originalTicket = await models.Ticket.findById(ticketId, null, options);
|
||||
|
||||
const newTicketId = await models.Ticket.clone(ctx, ticketId, shipped, true, options);
|
||||
const newTicket = await models.Ticket.findById(newTicketId, null, options);
|
||||
|
||||
expect(newTicket.clientFk).toEqual(originalTicket.clientFk);
|
||||
expect(newTicket.companyFk).toEqual(originalTicket.companyFk);
|
||||
expect(newTicket.addressFk).toEqual(originalTicket.addressFk);
|
||||
expect(newTicket.warehouseFk).toEqual(originalTicket.warehouseFk);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,53 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
describe('Ticket cloning - cloneAll function', () => {
|
||||
const activeCtx = {
|
||||
accessToken: {userId: 1},
|
||||
http: {
|
||||
req: {
|
||||
headers: {origin: 'http://localhost'}
|
||||
}
|
||||
}
|
||||
};
|
||||
const ctx = {req: activeCtx};
|
||||
let options;
|
||||
let tx;
|
||||
const ticketIds = [1, 2];
|
||||
const withWarehouse = true;
|
||||
const negative = false;
|
||||
|
||||
beforeEach(async() => {
|
||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({active: ctx.req});
|
||||
tx = await models.Ticket.beginTransaction({});
|
||||
options = {transaction: tx};
|
||||
});
|
||||
|
||||
afterEach(async() => {
|
||||
if (tx)
|
||||
await tx.rollback();
|
||||
});
|
||||
|
||||
it('should clone all provided tickets with their associated sales, services, and packages', async() => {
|
||||
const originalTickets = await models.Ticket.find({where: {id: {inq: ticketIds}}}, options);
|
||||
const originalSales = await models.Sale.find({where: {ticketFk: {inq: ticketIds}}}, options);
|
||||
const originalServices = await models.TicketService.find({where: {ticketFk: {inq: ticketIds}}}, options);
|
||||
const originalTicketPackaging =
|
||||
await models.TicketPackaging.find({where: {ticketFk: {inq: ticketIds}}}, options);
|
||||
|
||||
// Pass the ctx correctly to the cloneAll function
|
||||
const clonedTickets = await models.Ticket.cloneAll(ctx, ticketIds, withWarehouse, negative, options);
|
||||
|
||||
expect(clonedTickets.length).toEqual(originalTickets.length);
|
||||
|
||||
const clonedSales = await models.Sale.find({where: {ticketFk: {inq: clonedTickets.map(t => t.id)}}}, options);
|
||||
const clonedServices =
|
||||
await models.TicketService.find({where: {ticketFk: {inq: clonedTickets.map(t => t.id)}}}, options);
|
||||
const clonedTicketPackaging =
|
||||
await models.TicketPackaging.find({where: {ticketFk: {inq: clonedTickets.map(t => t.id)}}}, options);
|
||||
|
||||
expect(clonedSales.length).toEqual(originalSales.length);
|
||||
expect(clonedServices.length).toEqual(originalServices.length);
|
||||
expect(clonedTicketPackaging.length).toEqual(originalTicketPackaging.length);
|
||||
});
|
||||
});
|
|
@ -3,7 +3,7 @@ const LoopBackContext = require('loopback-context');
|
|||
|
||||
describe('ticket makeInvoice()', () => {
|
||||
const userId = 19;
|
||||
const invoiceType = 'R';
|
||||
const invoiceType = 'quick';
|
||||
const companyFk = 442;
|
||||
const invoiceDate = Date.vnNew();
|
||||
const activeCtx = {
|
||||
|
|
|
@ -26,7 +26,7 @@ module.exports = function(Self) {
|
|||
require('../methods/ticket/isLocked')(Self);
|
||||
require('../methods/ticket/freightCost')(Self);
|
||||
require('../methods/ticket/getComponentsSum')(Self);
|
||||
require('../methods/ticket/refund')(Self);
|
||||
require('../methods/ticket/cloneAll')(Self);
|
||||
require('../methods/ticket/deliveryNotePdf')(Self);
|
||||
require('../methods/ticket/deliveryNoteEmail')(Self);
|
||||
require('../methods/ticket/deliveryNoteCsv')(Self);
|
||||
|
@ -46,5 +46,4 @@ module.exports = function(Self) {
|
|||
require('../methods/ticket/invoiceTicketsAndPdf')(Self);
|
||||
require('../methods/ticket/docuwareDownload')(Self);
|
||||
require('../methods/ticket/myLastModified')(Self);
|
||||
require('../methods/ticket/clone')(Self);
|
||||
};
|
||||
|
|
|
@ -287,15 +287,24 @@ class Controller extends Section {
|
|||
}
|
||||
|
||||
refund(withWarehouse) {
|
||||
const params = {ticketsIds: [this.id], withWarehouse: withWarehouse};
|
||||
const query = 'Tickets/refund';
|
||||
const params = {
|
||||
ticketsIds: [this.id],
|
||||
withWarehouse: withWarehouse,
|
||||
negative: true // Asumimos que queremos cantidades negativas para reembolsos
|
||||
};
|
||||
const query = 'Tickets/cloneAll';
|
||||
return this.$http.post(query, params)
|
||||
.then(res => {
|
||||
const [refundTicket] = res.data;
|
||||
this.vnApp.showSuccess(this.$t('The following refund ticket have been created', {
|
||||
this.vnApp.showSuccess(this.$t('The following refund ticket has been created', {
|
||||
ticketId: refundTicket.id
|
||||
}));
|
||||
this.$state.go('ticket.card.sale', {id: refundTicket.id});
|
||||
})
|
||||
.catch(error => {
|
||||
this.vnApp.showError(this.$t('Error creating refund ticket', {
|
||||
error: error.data?.error?.message || 'Unknown error'
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -217,24 +217,6 @@ describe('Ticket Component vnTicketDescriptorMenu', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('refund()', () => {
|
||||
it('should make a query and go to ticket.card.sale', () => {
|
||||
controller.$state.go = jest.fn();
|
||||
|
||||
controller._id = ticket.id;
|
||||
|
||||
const params = {
|
||||
ticketsIds: [16]
|
||||
};
|
||||
const response = {id: 99};
|
||||
$httpBackend.expectPOST('Tickets/refund', params).respond([response]);
|
||||
controller.refund();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.$state.go).toHaveBeenCalledWith('ticket.card.sale', response);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sendChangesSms()', () => {
|
||||
it('should make a query and open the sms dialog', () => {
|
||||
controller.$.sms = {open: () => {}};
|
||||
|
|
|
@ -22,7 +22,7 @@ labels AS (
|
|||
b.id,
|
||||
b.itemFk,
|
||||
p.name producer,
|
||||
IF(i2.id, i2.comment, i.comment) comment
|
||||
IFNULL(i2.comment, i.comment) comment
|
||||
FROM buy b
|
||||
JOIN item i ON i.id = b.itemFk
|
||||
LEFT JOIN producer p ON p.id = i.producerFk
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
|
||||
|
||||
h3 {
|
||||
font-weight: 100;
|
||||
color: #555
|
||||
}
|
||||
|
||||
.report-info {
|
||||
font-size: 20px
|
||||
}
|
||||
|
||||
.description strong {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.padding {
|
||||
padding: 16px;
|
||||
}
|
||||
.tags {
|
||||
font-size: 10px;
|
||||
margin: 0;
|
||||
}
|
|
@ -4,23 +4,23 @@
|
|||
</template>
|
||||
<div class="grid-row">
|
||||
<div class="grid-block">
|
||||
<div class="columns">
|
||||
<h1 class="title uppercase">{{$t('title')}}</h1>
|
||||
<div class="columns header-tables">
|
||||
<div class="size50">
|
||||
<div class="body">
|
||||
<h1 class="title uppercase">{{$t('title')}}</h1>
|
||||
<table class="row-oriented report-info">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="font gray uppercase">{{$t('entryId')}}</td>
|
||||
<td class="font gray uppercase padding nowrap">{{$t('entryId')}}</td>
|
||||
<th>{{entry.id}}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font gray uppercase">{{$t('date')}}</td>
|
||||
<td class="font gray uppercase padding nowrap">{{$t('date')}}</td>
|
||||
<th>{{formatDate(entry.landed,'%d-%m-%Y')}}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font gray uppercase">{{$t('ref')}}</td>
|
||||
<th>{{entry.invoiceNumber}}</th>
|
||||
<td class="font gray uppercase padding nowrap">{{$t('ref')}}</td>
|
||||
<th>{{entry.invoiceNumber | dashIfEmpty}}</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -38,42 +38,56 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="column-oriented vn-mt-ml">
|
||||
<table class="column-oriented vn-mt-ml border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="number">{{$t('boxes')}}</th>
|
||||
<th width="5%" class="number"></th>
|
||||
<th class="number">{{$t('packing')}}</th>
|
||||
<th width="50%">{{$t('concept')}}</th>
|
||||
<th width="40%">{{$t('concept')}}</th>
|
||||
<th width="10%">{{$t('reference')}}</th>
|
||||
<th width="10%">{{$t('tags')}}</th>
|
||||
<th width="10%" class="number">{{$t('quantity')}}</th>
|
||||
<th width="5%" class="number"></th>
|
||||
<th width="15%" class="number">{{$t('price')}}</th>
|
||||
<th width="5%" class="number"></th>
|
||||
<th width="15%" class="number">{{$t('amount')}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-for="buy in buys">
|
||||
<tr>
|
||||
<td class="number">{{buy.box}}</td>
|
||||
<td class="number">{{buy.stickers}}</td>
|
||||
<td width="5%" class="number">x</td>
|
||||
<td class="number">{{buy.packing}}</td>
|
||||
<td width="50%">{{buy.itemName}}</td>
|
||||
<td width="40%" class="nowrap">{{buy.name}}</td>
|
||||
<td width="10%">{{buy.comment}}</td>
|
||||
<td width="10%" class="font light-gray tags">
|
||||
<span v-if="buy.value5" class="nowrap"><strong>{{buy.tag5}} → </strong>{{buy.value5}} </span>
|
||||
<span v-if="buy.value6" class="nowrap"><strong>{{buy.tag6}} → </strong>{{buy.value6}} </span>
|
||||
<span v-if="buy.value7" class="nowrap"><strong>{{buy.tag7}} → </strong>{{buy.value7}} </span>
|
||||
</td>
|
||||
<td width="10%" class="number">{{buy.quantity | number($i18n.locale)}}</td>
|
||||
<td width="5%" class="number">x</td>
|
||||
<td width="15%" class="number">{{buy.buyingValue | currency('EUR', $i18n.locale)}}</td>
|
||||
<td width="5%" class="number">=</td>
|
||||
<td width="15%" class="number">
|
||||
{{buy.buyingValue * buy.quantity | currency('EUR', $i18n.locale)}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="description font light-gray">
|
||||
<td colspan="7">
|
||||
<span v-if="buy.value5"> <strong>{{buy.tag5}}</strong> {{buy.value5}} </span>
|
||||
<span v-if="buy.value6"> <strong>{{buy.tag6}}</strong> {{buy.value6}} </span>
|
||||
<span v-if="buy.value7"> <strong>{{buy.tag7}}</strong> {{buy.value7}} </span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="5" class="font bold">
|
||||
<span class="pull-right">{{$t('total')}}</span>
|
||||
</td>
|
||||
<td class="number">{{getTotal() | currency('EUR', $i18n.locale)}}</td>
|
||||
<tr class="font bold">
|
||||
<td class="number">{{getTotalBy('stickers')}}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td class="number">{{getTotalBy('quantity') | number($i18n.locale)}}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td class="number">{{getTotalBy('amount') | currency('EUR', $i18n.locale)}}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
|
|
@ -13,13 +13,17 @@ module.exports = {
|
|||
return {totalBalance: 0.00};
|
||||
},
|
||||
methods: {
|
||||
getTotal() {
|
||||
let total = 0.00;
|
||||
this.buys.forEach(buy => {
|
||||
total += buy.quantity * buy.buyingValue;
|
||||
});
|
||||
|
||||
return total;
|
||||
getTotalBy(property) {
|
||||
return this.buys.reduce((total, buy) => {
|
||||
switch (property) {
|
||||
case 'amount':
|
||||
return total + buy.quantity * buy.buyingValue;
|
||||
case 'quantity':
|
||||
return total + buy.quantity;
|
||||
case 'stickers':
|
||||
return total + buy.stickers;
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
props: {
|
||||
|
|
|
@ -2,7 +2,7 @@ reportName: pedido-de-entrada
|
|||
title: Pedido
|
||||
supplierName: Proveedor
|
||||
supplierStreet: Dirección
|
||||
entryId: Referencia interna
|
||||
entryId: Nº Entrada
|
||||
date: Fecha
|
||||
ref: Nº Factura
|
||||
boxes: Cajas
|
||||
|
@ -15,3 +15,5 @@ total: Total
|
|||
entry: Entrada {0}
|
||||
supplierData: Datos del proveedor
|
||||
notes: Notas
|
||||
reference: Referencia
|
||||
tags: Tags
|
|
@ -1,16 +1,17 @@
|
|||
SELECT
|
||||
b.itemFk,
|
||||
b.quantity,
|
||||
b.buyingValue,
|
||||
b.stickers box,
|
||||
b.packing,
|
||||
i.name itemName,
|
||||
i.tag5,
|
||||
i.value5,
|
||||
i.tag6,
|
||||
i.value6,
|
||||
i.tag7,
|
||||
i.value7
|
||||
FROM buy b
|
||||
JOIN item i ON i.id = b.itemFk
|
||||
WHERE b.entryFk = ?
|
||||
SELECT b.itemFk,
|
||||
b.quantity,
|
||||
b.buyingValue,
|
||||
b.stickers,
|
||||
b.packing,
|
||||
i.name,
|
||||
IFNULL(i2.comment, i.comment) comment,
|
||||
i.tag5,
|
||||
i.value5,
|
||||
i.tag6,
|
||||
i.value6,
|
||||
i.tag7,
|
||||
i.value7
|
||||
FROM buy b
|
||||
JOIN item i ON i.id = b.itemFk
|
||||
LEFT JOIN item i2 ON i2.id = b.itemOriginalFk
|
||||
WHERE b.entryFk = ?
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
SELECT
|
||||
e.id,
|
||||
e.invoiceNumber,
|
||||
c.code companyCode,
|
||||
t.landed
|
||||
FROM entry e
|
||||
JOIN travel t ON t.id = e.travelFk
|
||||
JOIN company c ON c.id = e.companyFk
|
||||
WHERE e.id = ?
|
||||
SELECT e.id,
|
||||
e.invoiceNumber,
|
||||
c.code companyCode,
|
||||
t.landed
|
||||
FROM entry e
|
||||
JOIN travel t ON t.id = e.travelFk
|
||||
JOIN company c ON c.id = e.companyFk
|
||||
WHERE e.id = ?
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
SELECT
|
||||
s.name,
|
||||
s.street,
|
||||
s.nif,
|
||||
s.postCode,
|
||||
s.city,
|
||||
p.name province
|
||||
FROM supplier s
|
||||
JOIN entry e ON e.supplierFk = s.id
|
||||
LEFT JOIN province p ON p.id = s.provinceFk
|
||||
WHERE e.id = ?
|
||||
SELECT s.name,
|
||||
s.street,
|
||||
s.nif,
|
||||
s.postCode,
|
||||
s.city,
|
||||
p.name province
|
||||
FROM supplier s
|
||||
JOIN entry e ON e.supplierFk = s.id
|
||||
LEFT JOIN province p ON p.id = s.provinceFk
|
||||
WHERE e.id = ?
|
||||
|
|
Loading…
Reference in New Issue