#6147 - Refactor Invoice make PDF and notify #1933
|
@ -95,10 +95,7 @@ describe('image upload()', () => {
|
|||
spyOn(containerModel, 'upload');
|
||||
|
||||
const ctx = {req: {accessToken: {userId: hhrrId}},
|
||||
args: {
|
||||
id: itemId,
|
||||
collection: 'user'
|
||||
}
|
||||
args: {id: itemId, collection: 'user'}
|
||||
};
|
||||
|
||||
try {
|
||||
|
@ -109,7 +106,7 @@ describe('image upload()', () => {
|
|||
});
|
||||
|
||||
it('should try to upload a file for the collection "catalog" and throw a privilege error', async() => {
|
||||
const ctx = {req: {accessToken: {userId: hhrrId}},
|
||||
const ctx = {req: {accessToken: {userId: 1}},
|
||||
args: {
|
||||
id: workerId,
|
||||
collection: 'catalog'
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
const {ParameterizedSQL} = require('loopback-connector');
|
||||
const {buildFilter, mergeFilters} = require('vn-loopback/util/filter');
|
||||
// const {models} = require('vn-loopback/server/server');
|
||||
const {buildFilter} = require('vn-loopback/util/filter');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('filter', {
|
||||
description:
|
||||
'Find all postcodes of the model matched by postcode, town, province or country.',
|
||||
accessType: 'READ',
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true,
|
||||
},
|
||||
accepts: [
|
||||
{
|
||||
arg: 'filter',
|
||||
|
@ -25,6 +20,10 @@ module.exports = Self => {
|
|||
http: {source: 'query'}
|
||||
},
|
||||
],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true,
|
||||
},
|
||||
http: {
|
||||
path: `/filter`,
|
||||
verb: 'GET',
|
||||
|
@ -32,30 +31,34 @@ module.exports = Self => {
|
|||
});
|
||||
Self.filter = async(ctx, filter, options) => {
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
filter = ctx?.filter ?? {};
|
||||
|
||||
const conn = Self.dataSource.connector;
|
||||
const where = buildFilter(ctx.args, (param, value) => {
|
||||
const where = buildFilter(filter?.where, (param, value) => {
|
||||
switch (param) {
|
||||
case 'search':
|
||||
return {or: [
|
||||
{'pc.code': {like: `%${value}%`}},
|
||||
{'t.name': {like: `%${value}%`}},
|
||||
{'p.name': {like: `%${value}%`}},
|
||||
{'c.country': {like: `%${value}%`}}
|
||||
]
|
||||
return {
|
||||
or: [
|
||||
{'pc.code': {like: `%${value}%`}},
|
||||
{'t.name': {like: `%${value}%`}},
|
||||
{'p.name': {like: `%${value}%`}},
|
||||
{'c.country': {like: `%${value}%`}}
|
||||
]
|
||||
};
|
||||
}
|
||||
}) ?? {};
|
||||
|
||||
filter = mergeFilters(ctx.args?.filter ?? {}, {where});
|
||||
delete ctx.filter.where;
|
||||
|
||||
const stmts = [];
|
||||
let stmt;
|
||||
stmt = new ParameterizedSQL(`
|
||||
SELECT
|
||||
pc.townFk,
|
||||
t.provinceFk,
|
||||
p.countryFk,
|
||||
pc.code,
|
||||
t.name as town,
|
||||
p.name as province,
|
||||
|
@ -67,7 +70,7 @@ module.exports = Self => {
|
|||
JOIN country c on c.id = p.countryFk
|
||||
`);
|
||||
|
||||
stmt.merge(conn.makeSuffix(filter));
|
||||
stmt.merge(conn.makeSuffix({where, ...ctx}));
|
||||
const itemsIndex = stmts.push(stmt) - 1;
|
||||
|
||||
const sql = ParameterizedSQL.join(stmts, ';');
|
||||
|
|
|
@ -7,13 +7,13 @@ describe('Postcode filter()', () => {
|
|||
|
||||
try {
|
||||
const ctx = {
|
||||
args: {
|
||||
|
||||
filter: {
|
||||
},
|
||||
limit: 1
|
||||
};
|
||||
const results = await models.Postcode.filter(ctx, options);
|
||||
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
expect(results.length).toEqual(1);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
|
@ -27,8 +27,10 @@ describe('Postcode filter()', () => {
|
|||
|
||||
try {
|
||||
const ctx = {
|
||||
args: {
|
||||
search: 46,
|
||||
filter: {
|
||||
where: {
|
||||
search: 46,
|
||||
}
|
||||
},
|
||||
};
|
||||
const results = await models.Postcode.filter(ctx, options);
|
||||
|
@ -47,8 +49,10 @@ describe('Postcode filter()', () => {
|
|||
|
||||
try {
|
||||
const ctx = {
|
||||
args: {
|
||||
search: 'Alz',
|
||||
filter: {
|
||||
where: {
|
||||
search: 'Alz',
|
||||
}
|
||||
},
|
||||
};
|
||||
const results = await models.Postcode.filter(ctx, options);
|
||||
|
@ -67,8 +71,10 @@ describe('Postcode filter()', () => {
|
|||
|
||||
try {
|
||||
const ctx = {
|
||||
args: {
|
||||
search: 'one',
|
||||
filter: {
|
||||
where: {
|
||||
search: 'one',
|
||||
}
|
||||
},
|
||||
};
|
||||
const results = await models.Postcode.filter(ctx, options);
|
||||
|
@ -87,8 +93,10 @@ describe('Postcode filter()', () => {
|
|||
|
||||
try {
|
||||
const ctx = {
|
||||
args: {
|
||||
search: 'Ec',
|
||||
filter: {
|
||||
where: {
|
||||
search: 'Ec',
|
||||
}
|
||||
},
|
||||
};
|
||||
const results = await models.Postcode.filter(ctx, options);
|
||||
|
|
|
@ -79,10 +79,10 @@ module.exports = function(Self) {
|
|||
Self.getRoles = async(userId, options) => {
|
||||
const result = await Self.rawSql(
|
||||
`SELECT r.name
|
||||
FROM account.user u
|
||||
JOIN account.roleRole rr ON rr.role = u.role
|
||||
JOIN account.role r ON r.id = rr.inheritsFrom
|
||||
WHERE u.id = ?`, [userId], options);
|
||||
FROM account.user u
|
||||
JOIN account.roleRole rr ON rr.role = u.role
|
||||
JOIN account.role r ON r.id = rr.inheritsFrom
|
||||
WHERE u.id = ?`, [userId], options);
|
||||
|
||||
const roles = [];
|
||||
for (const role of result)
|
||||
|
@ -142,7 +142,7 @@ module.exports = function(Self) {
|
|||
ip: ctx.req.ip,
|
||||
owner: isOwner
|
||||
});
|
||||
throw new UserError('Try again');
|
||||
throw new UserError('Try again');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -585,5 +585,5 @@ END$$
|
|||
DELIMITER ;
|
||||
|
||||
TRUNCATE TABLE `vn`.`ticketUpdateAction`;
|
||||
INSERT INTO `vn`.`ticketUpdateAction` (id, description, code) VALUES(1, 'Cambiar los precios en el ticket', 'renewPrice');
|
||||
INSERT INTO `vn`.`ticketUpdateAction` (id, description, code) VALUES(1, 'Cambiar los precios en el ticket', 'renewPrices');
|
||||
INSERT INTO `vn`.`ticketUpdateAction` (id, description, code) VALUES(2, 'Convertir en maná', 'mana');
|
|
@ -0,0 +1,3 @@
|
|||
GRANT EXECUTE ON PROCEDURE util.tx_commit TO guest;
|
||||
GRANT EXECUTE ON PROCEDURE util.tx_rollback TO guest;
|
||||
GRANT EXECUTE ON PROCEDURE util.tx_start TO guest;
|
|
@ -0,0 +1,85 @@
|
|||
DROP PROCEDURE IF EXISTS vn.travel_cloneWithEntries;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`travel_cloneWithEntries`(
|
||||
IN vTravelFk INT,
|
||||
IN vDateStart DATE,
|
||||
IN vDateEnd DATE,
|
||||
IN vWarehouseOutFk INT,
|
||||
IN vWarehouseInFk INT,
|
||||
IN vRef VARCHAR(255),
|
||||
IN vAgencyModeFk INT,
|
||||
OUT vNewTravelFk INT)
|
||||
BEGIN
|
||||
/**
|
||||
* Clona un travel junto con sus entradas y compras
|
||||
* @param vTravelFk travel plantilla a clonar
|
||||
* @param vDateStart fecha del shipment del nuevo travel
|
||||
* @param vDateEnd fecha del landing del nuevo travel
|
||||
* @param vWarehouseOutFk warehouse del salida del nuevo travel
|
||||
* @param vWarehouseInFk warehouse de landing del nuevo travel
|
||||
* @param vRef referencia del nuevo travel
|
||||
* @param vAgencyModeFk del nuevo travel
|
||||
* @param vNewTravelFk id del nuevo travel
|
||||
*/
|
||||
DECLARE vNewEntryFk INT;
|
||||
DECLARE vEvaNotes VARCHAR(255);
|
||||
DECLARE vDone BOOL;
|
||||
DECLARE vAuxEntryFk INT;
|
||||
DECLARE vTx BOOLEAN DEFAULT !@@in_transaction;
|
||||
DECLARE vRsEntry CURSOR FOR
|
||||
SELECT e.id
|
||||
FROM entry e
|
||||
JOIN travel t ON t.id = e.travelFk
|
||||
WHERE e.travelFk = vTravelFk;
|
||||
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
|
||||
|
||||
DECLARE EXIT HANDLER FOR SQLEXCEPTION
|
||||
BEGIN
|
||||
CALL util.tx_rollback(vTx);
|
||||
RESIGNAL;
|
||||
END;
|
||||
|
||||
CALL util.tx_start(vTx);
|
||||
|
||||
INSERT INTO travel (shipped, landed, warehouseInFk, warehouseOutFk, agencyModeFk, `ref`, isDelivered, isReceived, m3, cargoSupplierFk, kg,clonedFrom)
|
||||
SELECT vDateStart, vDateEnd, vWarehouseInFk, vWarehouseOutFk, vAgencyModeFk, vRef, isDelivered, isReceived, m3,cargoSupplierFk, kg,vTravelFk
|
||||
FROM travel
|
||||
WHERE id = vTravelFk;
|
||||
|
||||
SET vNewTravelFk = LAST_INSERT_ID();
|
||||
|
||||
SET vDone = FALSE;
|
||||
SET @isModeInventory = TRUE;
|
||||
|
||||
OPEN vRsEntry;
|
||||
|
||||
l: LOOP
|
||||
SET vDone = FALSE;
|
||||
FETCH vRsEntry INTO vAuxEntryFk;
|
||||
|
||||
IF vDone THEN
|
||||
LEAVE l;
|
||||
END IF;
|
||||
|
||||
CALL entry_cloneHeader(vAuxEntryFk, vNewEntryFk, vNewTravelFk);
|
||||
CALL entry_copyBuys(vAuxEntryFk, vNewEntryFk);
|
||||
|
||||
SELECT evaNotes INTO vEvaNotes
|
||||
FROM entry
|
||||
WHERE id = vAuxEntryFk;
|
||||
|
||||
UPDATE entry
|
||||
SET evaNotes = vEvaNotes
|
||||
WHERE id = vNewEntryFk;
|
||||
|
||||
END LOOP;
|
||||
|
||||
SET @isModeInventory = FALSE;
|
||||
CLOSE vRsEntry;
|
||||
|
||||
CALL util.tx_commit(vTx);
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,15 @@
|
|||
DELIMITER $$
|
||||
$$
|
||||
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `util`.`tx_commit`(IN tx BOOL)
|
||||
BEGIN
|
||||
/**
|
||||
* Procedimiento para confirmar los cambios asociados a una transacción
|
||||
*
|
||||
* @param tx BOOL es true si existe transacción asociada
|
||||
*/
|
||||
IF tx THEN
|
||||
COMMIT;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,15 @@
|
|||
DELIMITER $$
|
||||
$$
|
||||
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `util`.`tx_rollback`(tx BOOL)
|
||||
BEGIN
|
||||
/**
|
||||
* Procedimiento para deshacer los cambios asociados a una transacción
|
||||
*
|
||||
* @param tx BOOL es true si existe transacción asociada
|
||||
*/
|
||||
IF tx THEN
|
||||
ROLLBACK;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `util`.`tx_start`(tx BOOL)
|
||||
BEGIN
|
||||
/**
|
||||
* Procedimiento para iniciar una transacción
|
||||
*
|
||||
* @param tx BOOL es true si existe transacción asociada
|
||||
*/
|
||||
IF tx THEN
|
||||
START TRANSACTION;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
File diff suppressed because one or more lines are too long
|
@ -180,11 +180,13 @@ INSERT INTO `vn`.`warehouse`(`id`, `name`, `code`, `isComparative`, `isInventory
|
|||
(13, 'Inventory', 'inv', 1, 1, 1, 0, 0, 0, 2, 1, 0),
|
||||
(60, 'Algemesi', NULL, 1, 1, 1, 0, 0, 0, 2, 1, 0);
|
||||
|
||||
INSERT INTO `vn`.`sectorType` (id,description)
|
||||
VALUES (1,'First type');
|
||||
|
||||
INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `isPreviousPreparedByPacking`, `code`)
|
||||
INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `code`, `typeFk`)
|
||||
VALUES
|
||||
(1, 'First sector', 1, 1, 'FIRST'),
|
||||
(2, 'Second sector', 2, 0, 'SECOND');
|
||||
(1, 'First sector', 1, 'FIRST', 1),
|
||||
(2, 'Second sector', 2, 'SECOND',1);
|
||||
|
||||
INSERT INTO `vn`.`printer` (`id`, `name`, `path`, `isLabeler`, `sectorFk`, `ipAddress`)
|
||||
VALUES
|
||||
|
@ -194,6 +196,7 @@ INSERT INTO `vn`.`printer` (`id`, `name`, `path`, `isLabeler`, `sectorFk`, `ipAd
|
|||
|
||||
UPDATE `vn`.`sector` SET mainPrinterFk = 1 WHERE id = 1;
|
||||
|
||||
|
||||
INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`,`bossFk`, `phone`)
|
||||
VALUES
|
||||
(1106, 'LGN', 'David Charles', 'Haller', 19, 432978106),
|
||||
|
@ -659,19 +662,20 @@ INSERT INTO `vn`.`invoiceOutExpense`(`id`, `invoiceOutFk`, `amount`, `expenseFk`
|
|||
|
||||
INSERT INTO `vn`.`zone` (`id`, `name`, `hour`, `agencyModeFk`, `travelingDays`, `price`, `bonus`, `itemMaxSize`)
|
||||
VALUES
|
||||
(1, 'Zone pickup A', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 1, 0, 0, 0, 100),
|
||||
(2, 'Zone pickup B', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 1, 0, 0, 0, 100),
|
||||
(1, 'Zone pickup A', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 1, 0, 1, 0, 100),
|
||||
(2, 'Zone pickup B', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 1, 0, 1, 0, 100),
|
||||
(3, 'Zone 247 A', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 7, 1, 2, 0, 100),
|
||||
(4, 'Zone 247 B', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 7, 1, 2, 0, 100),
|
||||
(5, 'Zone expensive A', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 8, 1, 1000, 0, 100),
|
||||
(6, 'Zone expensive B', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 8, 1, 1000, 0, 100),
|
||||
(7, 'Zone refund', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 23, 0, 0, 0, 100),
|
||||
(8, 'Zone others', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 10, 0, 0, 0, 100),
|
||||
(9, 'Zone superMan', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 2, 0, 0, 0, 100),
|
||||
(10, 'Zone teleportation', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 3, 0, 0, 0, 100),
|
||||
(11, 'Zone pickup C', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 1, 0, 0, 0, 100),
|
||||
(12, 'Zone entanglement', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 4, 0, 0, 0, 100),
|
||||
(13, 'Zone quantum break', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 5, 0, 0, 0, 100);
|
||||
(7, 'Zone refund', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 23, 0, 1, 0, 100),
|
||||
(8, 'Zone others', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 10, 0, 1, 0, 100),
|
||||
(9, 'Zone superMan', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 2, 0, 1, 0, 100),
|
||||
(10, 'Zone teleportation', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 3, 0, 1, 0, 100),
|
||||
(11, 'Zone pickup C', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 1, 0, 1, 0, 100),
|
||||
(12, 'Zone entanglement', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 4, 0, 1, 0, 100),
|
||||
(13, 'Zone quantum break', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 5, 0, 1, 0, 100);
|
||||
|
||||
|
||||
INSERT INTO `vn`.`zoneWarehouse` (`id`, `zoneFk`, `warehouseFk`)
|
||||
VALUES
|
||||
|
@ -1426,16 +1430,29 @@ INSERT INTO `vn`.`ticketWeekly`(`ticketFk`, `weekDay`)
|
|||
(5, 6),
|
||||
(15, 6);
|
||||
|
||||
INSERT INTO `vn`.`travel`(`id`,`shipped`, `landed`, `warehouseInFk`, `warehouseOutFk`, `agencyModeFk`, `m3`, `kg`,`ref`, `totalEntries`, `cargoSupplierFk`)
|
||||
INSERT INTO `vn`.`awb` (id, code, package, weight, created, amount, transitoryFk, taxFk)
|
||||
VALUES
|
||||
(1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), 1, 2, 1, 100.00, 1000, 'first travel', 1, 1),
|
||||
(2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1, 2, 1, 150, 2000, 'second travel', 2, 2),
|
||||
(3, util.VN_CURDATE(), util.VN_CURDATE(), 1, 2, 1, 0.00, 0.00, 'third travel', 1, 1),
|
||||
(4, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1, 3, 1, 50.00, 500, 'fourth travel', 0, 2),
|
||||
(5, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 3, 3, 1, 50.00, 500, 'fifth travel', 1, 1),
|
||||
(6, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 4, 4, 1, 50.00, 500, 'sixth travel', 1, 2),
|
||||
(7, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 5, 4, 1, 50.00, 500, 'seventh travel', 2, 1),
|
||||
(8, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 5, 1, 1, 50.00, 500, 'eight travel', 1, 2);
|
||||
(1, '07546501420', 67, 671, util.VN_CURDATE(), 1761, 1, 1),
|
||||
(2, '07546491421', 252, 2769, util.VN_CURDATE(), 5231, 1, 1),
|
||||
(3, '07546500823', 102, 1495, util.VN_CURDATE(), 3221, 1, 1),
|
||||
(4, '99610288821', 252, 2777, util.VN_CURDATE(), 3641, 1, 1),
|
||||
(5, '07546500834', 229, 3292, util.VN_CURDATE(), 6601, 2, 1),
|
||||
(6, '22101929561', 37, 458, util.VN_CURDATE(), 441, 2, 1),
|
||||
(7, '07546491432', 258, 3034, util.VN_CURDATE(), 6441, 2, 1),
|
||||
(8, '99610288644', 476, 4461, util.VN_CURDATE(), 5751, 442, 1),
|
||||
(9, '99610289193', 302, 2972, util.VN_CURDATE(), 3871, 442, 1),
|
||||
(10, '07546500856', 185, 2364, util.VN_CURDATE(), 5321, 442, 1);
|
||||
|
||||
INSERT INTO `vn`.`travel`(`id`,`shipped`, `landed`, `warehouseInFk`, `warehouseOutFk`, `agencyModeFk`, `m3`, `kg`,`ref`, `totalEntries`, `cargoSupplierFk`, `awbFK`)
|
||||
VALUES
|
||||
(1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), 1, 2, 1, 100.00, 1000, 'first travel', 1, 1, 1),
|
||||
(2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1, 2, 1, 150, 2000, 'second travel', 2, 2, 2),
|
||||
(3, util.VN_CURDATE(), util.VN_CURDATE(), 1, 2, 1, 0.00, 0.00, 'third travel', 1, 1, 3),
|
||||
(4, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1, 3, 1, 50.00, 500, 'fourth travel', 0, 2, 4),
|
||||
(5, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 3, 3, 1, 50.00, 500, 'fifth travel', 1, 1, 5),
|
||||
(6, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 4, 4, 1, 50.00, 500, 'sixth travel', 1, 2, 6),
|
||||
(7, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 5, 4, 1, 50.00, 500, 'seventh travel', 2, 1, 7),
|
||||
(8, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 5, 1, 1, 50.00, 500, 'eight travel', 1, 2, 10);
|
||||
|
||||
INSERT INTO `vn`.`entry`(`id`, `supplierFk`, `created`, `travelFk`, `isConfirmed`, `companyFk`, `invoiceNumber`, `reference`, `isExcludedFromAvailable`, `isRaid`, `evaNotes`)
|
||||
VALUES
|
||||
|
@ -2499,20 +2516,7 @@ INSERT INTO `vn`.`rate`(`dated`, `warehouseFk`, `rate0`, `rate1`, `rate2`, `rate
|
|||
(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR), 1, 10, 15, 20, 25),
|
||||
(util.VN_CURDATE(), 1, 12, 17, 22, 27);
|
||||
|
||||
INSERT INTO `vn`.`awb` (id, code, package, weight, created, amount, transitoryFk, taxFk)
|
||||
VALUES
|
||||
(1, '07546501420', 67, 671, util.VN_CURDATE(), 1761, 1, 1),
|
||||
(2, '07546491421', 252, 2769, util.VN_CURDATE(), 5231, 1, 1),
|
||||
(3, '07546500823', 102, 1495, util.VN_CURDATE(), 3221, 1, 1),
|
||||
(4, '99610288821', 252, 2777, util.VN_CURDATE(), 3641, 1, 1),
|
||||
(5, '07546500834', 229, 3292, util.VN_CURDATE(), 6601, 2, 1),
|
||||
(6, '22101929561', 37, 458, util.VN_CURDATE(), 441, 2, 1),
|
||||
(7, '07546491432', 258, 3034, util.VN_CURDATE(), 6441, 2, 1),
|
||||
(8, '99610288644', 476, 4461, util.VN_CURDATE(), 5751, 442, 1),
|
||||
(9, '99610289193', 302, 2972, util.VN_CURDATE(), 3871, 442, 1),
|
||||
(10, '07546500856', 185, 2364, util.VN_CURDATE(), 5321, 442, 1);
|
||||
|
||||
INSERT INTO `vn`.`dua` (id, code, awbFk, issued, operated, booked, bookEntried, gestdocFk, customsValue, companyFk)
|
||||
INSERT INTO `vn`.`dua` (id, code, awbFk__, issued, operated, booked, bookEntried, gestdocFk, customsValue, companyFk)
|
||||
VALUES
|
||||
(1, '19ES0028013A481523', 1, util.VN_CURDATE(), util.VN_CURDATE(), util.VN_CURDATE(), util.VN_CURDATE(), 1, 11276.95, 442),
|
||||
(2, '21ES00280136115760', 2, util.VN_CURDATE(), util.VN_CURDATE(), util.VN_CURDATE(), util.VN_CURDATE(), 2, 1376.20, 442),
|
||||
|
@ -2525,6 +2529,17 @@ INSERT INTO `vn`.`dua` (id, code, awbFk, issued, operated, booked, bookEntried,
|
|||
(9, '19ES00280132025491', 9, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), util.VN_CURDATE(), util.VN_CURDATE(), util.VN_CURDATE(), 2, 7126.23, 442),
|
||||
(10, '19ES00280132025492', 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), util.VN_CURDATE(), util.VN_CURDATE(), util.VN_CURDATE(), 2, 4631.45, 442);
|
||||
|
||||
INSERT INTO `vn`.`duaEntry` (`duaFk`, `entryFk`, `value`, `customsValue`, `euroValue`)
|
||||
VALUES
|
||||
(1, 1, 1.00, 1.00, 1.00),
|
||||
(2, 2, 1.00, 1.00, 1.00),
|
||||
(3, 3, 1.00, 1.00, 1.00),
|
||||
(4, 4, 1.00, 1.00, 1.00),
|
||||
(5, 5, 1.00, 1.00, 1.00),
|
||||
(6, 6, 1.00, 1.00, 1.00),
|
||||
(7, 7, 1.00, 1.00, 1.00),
|
||||
(8, 8, 1.00, 1.00, 1.00);
|
||||
|
||||
REPLACE INTO `vn`.`invoiceIn`(`id`, `serialNumber`,`serial`, `supplierFk`, `issued`, `created`, `supplierRef`, `isBooked`, `companyFk`, `docFk`)
|
||||
VALUES
|
||||
(1, 1001, 'R', 1, util.VN_CURDATE(), util.VN_CURDATE(), 1234, 0, 442, 1),
|
||||
|
@ -2911,7 +2926,7 @@ INSERT INTO `vn`.`workerConfig` (`id`, `businessUpdated`, `roleFk`, `payMethodFk
|
|||
|
||||
INSERT INTO `vn`.`ticketRefund`(`refundTicketFk`, `originalTicketFk`)
|
||||
VALUES
|
||||
(24, 7);
|
||||
(24, 8);
|
||||
|
||||
INSERT INTO `vn`.`deviceProductionModels` (`code`)
|
||||
VALUES
|
||||
|
|
13975
db/dump/structure.sql
13975
db/dump/structure.sql
File diff suppressed because it is too large
Load Diff
|
@ -49,6 +49,20 @@ async function test() {
|
|||
random: false,
|
||||
});
|
||||
|
||||
const SpecReporter = require('jasmine-spec-reporter').SpecReporter;
|
||||
jasmine.addReporter(new SpecReporter({
|
||||
spec: {
|
||||
displaySuccessful: false,
|
||||
displayPending: false,
|
||||
displayDuration: false,
|
||||
displayFailed: true,
|
||||
displayErrorMessages: true,
|
||||
},
|
||||
summary: {
|
||||
displayPending: false,
|
||||
}
|
||||
}));
|
||||
|
||||
await backendStatus();
|
||||
|
||||
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
|
||||
|
|
|
@ -225,7 +225,8 @@ describe('Ticket Edit sale path', () => {
|
|||
});
|
||||
|
||||
it('should show error trying to delete a ticket with a refund', async() => {
|
||||
await page.accessToSearchResult('7');
|
||||
await page.loginAndModule('production', 'ticket');
|
||||
await page.accessToSearchResult('8');
|
||||
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
|
||||
await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteTicket);
|
||||
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||
|
|
|
@ -22,17 +22,6 @@ describe('Travel basic data path', () => {
|
|||
await page.waitForState('travel.card.basicData');
|
||||
});
|
||||
|
||||
it('should throw error if try move a travel with entries', async() => {
|
||||
const lastMonth = Date.vnNew();
|
||||
lastMonth.setMonth(lastMonth.getMonth() - 1);
|
||||
|
||||
await page.pickDate(selectors.travelBasicData.deliveryDate, lastMonth);
|
||||
await page.waitToClick(selectors.travelBasicData.save);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Cannot past travels with entries');
|
||||
});
|
||||
|
||||
it('should set a wrong delivery date then receive an error on submit', async() => {
|
||||
await page.loginAndModule('buyer', 'travel');
|
||||
await page.write(selectors.travelIndex.generalSearchFilter, '4');
|
||||
|
|
|
@ -271,7 +271,7 @@ class VnMySQL extends MySQL {
|
|||
isLoggable(model) {
|
||||
const Model = this.getModelDefinition(model).model;
|
||||
const {settings} = Model.definition;
|
||||
return settings?.mixins?.Loggable;
|
||||
return settings.mixins?.Loggable;
|
||||
}
|
||||
|
||||
invokeMethod(method, args, model, ctx, opts, cb) {
|
||||
|
|
|
@ -127,7 +127,7 @@ module.exports = Self => {
|
|||
case 'isBooked':
|
||||
return {[`ii.${param}`]: value};
|
||||
case 'awbCode':
|
||||
return {'awb.code': value};
|
||||
return {'sub.code': value};
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -143,20 +143,27 @@ module.exports = Self => {
|
|||
ii.issued,
|
||||
ii.isBooked,
|
||||
ii.supplierRef,
|
||||
ii.docFk AS dmsFk,
|
||||
ii.docFk dmsFk,
|
||||
dm.file,
|
||||
ii.supplierFk,
|
||||
ii.expenseFkDeductible deductibleExpenseFk,
|
||||
s.name AS supplierName,
|
||||
s.name supplierName,
|
||||
s.account,
|
||||
SUM(iid.amount) AS amount,
|
||||
awb.code AS awbCode
|
||||
SUM(iid.amount) amount,
|
||||
sub.code awbCode
|
||||
FROM invoiceIn ii
|
||||
JOIN supplier s ON s.id = ii.supplierFk
|
||||
LEFT JOIN invoiceInDueDay iid ON iid.invoiceInFk = ii.id
|
||||
LEFT JOIN duaInvoiceIn dii ON dii.invoiceInFk = ii.id
|
||||
LEFT JOIN dua d ON d.id = dii.duaFk
|
||||
LEFT JOIN awb ON awb.id = d.awbFk
|
||||
LEFT JOIN (
|
||||
SELECT awb.code, de.duaFk
|
||||
FROM duaEntry de
|
||||
JOIN entry e ON e.id = de.entryFk
|
||||
JOIN travel t ON t.id = e.travelFk
|
||||
JOIN awb ON awb.id = t.awbFk
|
||||
GROUP BY de.duaFk
|
||||
) sub ON sub.duaFk = d.id
|
||||
LEFT JOIN company co ON co.id = ii.companyFk
|
||||
LEFT JOIN dms dm ON dm.id = ii.docFk`
|
||||
);
|
||||
|
|
|
@ -98,9 +98,10 @@ describe('InvoiceIn filter()', () => {
|
|||
const options = {transaction: tx};
|
||||
|
||||
try {
|
||||
const awbExpected = '07546501420';
|
||||
const ctx = {
|
||||
args: {
|
||||
awbCode: '07546500856',
|
||||
awbCode: awbExpected,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -108,8 +109,8 @@ describe('InvoiceIn filter()', () => {
|
|||
const firstRow = result[0];
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
expect(firstRow.id).toEqual(10);
|
||||
expect(firstRow.awbCode).toEqual('07546500856');
|
||||
expect(firstRow.id).toEqual(1);
|
||||
expect(firstRow.awbCode).toEqual(awbExpected);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
|
|
|
@ -117,7 +117,7 @@
|
|||
url="Expenses"
|
||||
label="Expense"
|
||||
ng-model="$ctrl.item.expenseFk"
|
||||
vn-name="expence"
|
||||
vn-name="expense"
|
||||
initial-data="$ctrl.item.expense">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
|
|
|
@ -40,7 +40,7 @@ module.exports = Self => {
|
|||
currentDate.setHours(0, 0, 0, 0);
|
||||
const nextDay = Date.vnNew();
|
||||
nextDay.setDate(currentDate.getDate() + 1);
|
||||
|
||||
nextDay.setHours(0, 0, 0, 0);
|
||||
const filter = {
|
||||
where: {
|
||||
and: [
|
||||
|
|
|
@ -74,7 +74,6 @@ describe('ticket componentUpdate()', () => {
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
await models.Ticket.componentUpdate(ctx, options);
|
||||
|
||||
[componentValue] = await models.SaleComponent.rawSql(componentOfSaleSeven, null, options);
|
||||
|
|
|
@ -117,7 +117,7 @@ describe('ticket setDeleted()', () => {
|
|||
return value;
|
||||
};
|
||||
|
||||
const ticketId = 7;
|
||||
const ticketId = 8;
|
||||
await models.Ticket.setDeleted(ctx, ticketId, options);
|
||||
|
||||
await tx.rollback();
|
||||
|
|
|
@ -28,7 +28,6 @@ module.exports = Self => {
|
|||
|
||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||
ctx.req = loopBackContext.active;
|
||||
if (await models.ACL.checkAccessAcl(ctx, 'Sale', 'canForceQuantity', 'WRITE')) return;
|
||||
|
||||
const ticketId = changes?.ticketFk || instance?.ticketFk;
|
||||
const itemId = changes?.itemFk || instance?.itemFk;
|
||||
|
|
|
@ -25,7 +25,7 @@ class Controller extends Component {
|
|||
|
||||
loadDefaultTicketAction() {
|
||||
const isSalesAssistant = this.aclService.hasAny(['salesAssistant']);
|
||||
this.ticket.option = isSalesAssistant ? 'mana' : 'buyerDiscount';
|
||||
this.ticket.option = isSalesAssistant ? 'mana' : 'renewPrices';
|
||||
}
|
||||
|
||||
onStepChange() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||
const UserError = require('vn-loopback/util/user-error');
|
||||
const loggable = require('vn-loopback/util/log');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('cloneWithEntries', {
|
||||
|
@ -11,8 +10,9 @@ module.exports = Self => {
|
|||
type: 'number',
|
||||
required: true,
|
||||
description: 'The original travel id',
|
||||
http: {source: 'path'}
|
||||
}],
|
||||
http: {source: 'path'},
|
||||
},
|
||||
],
|
||||
returns: {
|
||||
type: 'object',
|
||||
description: 'The new cloned travel id',
|
||||
|
@ -24,61 +24,75 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.cloneWithEntries = async(ctx, id) => {
|
||||
Self.cloneWithEntries = async(ctx, id, options) => {
|
||||
const conn = Self.dataSource.connector;
|
||||
const travel = await Self.findById(id, {
|
||||
fields: [
|
||||
'id',
|
||||
'shipped',
|
||||
'landed',
|
||||
'warehouseInFk',
|
||||
'warehouseOutFk',
|
||||
'agencyModeFk',
|
||||
'ref'
|
||||
]
|
||||
});
|
||||
const started = Date.vnNew();
|
||||
const ended = Date.vnNew();
|
||||
const myOptions = {};
|
||||
let tx = options?.transaction;
|
||||
|
||||
if (!travel)
|
||||
throw new UserError('Travel not found');
|
||||
try {
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
let stmts = [];
|
||||
let stmt;
|
||||
if (!myOptions.transaction) {
|
||||
tx = await Self.beginTransaction({});
|
||||
myOptions.transaction = tx;
|
||||
}
|
||||
|
||||
stmt = new ParameterizedSQL(
|
||||
`CALL travel_cloneWithEntries(?, ?, ?, ?, ?, ?, ?, @vTravelFk)`, [
|
||||
id,
|
||||
started,
|
||||
ended,
|
||||
travel.warehouseOutFk,
|
||||
travel.warehouseInFk,
|
||||
travel.ref,
|
||||
travel.agencyModeFk
|
||||
]
|
||||
);
|
||||
stmts.push(stmt);
|
||||
const newTravelIndex = stmts.push('SELECT @vTravelFk AS id') - 1;
|
||||
const travel = await Self.findById(id, {
|
||||
fields: [
|
||||
'id',
|
||||
'shipped',
|
||||
'landed',
|
||||
'warehouseInFk',
|
||||
'warehouseOutFk',
|
||||
'agencyModeFk',
|
||||
'ref'
|
||||
]
|
||||
});
|
||||
const started = Date.vnNew();
|
||||
const ended = Date.vnNew();
|
||||
|
||||
const sql = ParameterizedSQL.join(stmts, ';');
|
||||
const result = await conn.executeStmt(sql);
|
||||
const [lastInsert] = result[newTravelIndex];
|
||||
if (!travel)
|
||||
throw new UserError('Travel not found');
|
||||
|
||||
if (!lastInsert.id)
|
||||
throw new UserError('Unable to clone this travel');
|
||||
let stmts = [];
|
||||
let stmt;
|
||||
stmt = new ParameterizedSQL(
|
||||
`CALL travel_cloneWithEntries(?, ?, ?, ?, ?, ?, ?, @vTravelFk)`, [
|
||||
id,
|
||||
started,
|
||||
ended,
|
||||
travel.warehouseOutFk,
|
||||
travel.warehouseInFk,
|
||||
travel.ref,
|
||||
travel.agencyModeFk
|
||||
]
|
||||
);
|
||||
stmts.push(stmt);
|
||||
const newTravelIndex = stmts.push('SELECT @vTravelFk AS id') - 1;
|
||||
|
||||
const newTravel = await Self.findById(lastInsert.id, {
|
||||
fields: [
|
||||
'id',
|
||||
'shipped',
|
||||
'landed',
|
||||
'warehouseInFk',
|
||||
'warehouseOutFk',
|
||||
'agencyModeFk',
|
||||
'ref'
|
||||
]
|
||||
});
|
||||
const sql = ParameterizedSQL.join(stmts, ';');
|
||||
const result = await conn.executeStmt(sql, myOptions);
|
||||
const [lastInsert] = result[newTravelIndex];
|
||||
|
||||
return newTravel.id;
|
||||
if (!lastInsert.id)
|
||||
throw new UserError('Unable to clone this travel');
|
||||
|
||||
const newTravel = await Self.findById(lastInsert.id, {
|
||||
fields: [
|
||||
'id',
|
||||
'shipped',
|
||||
'landed',
|
||||
'warehouseInFk',
|
||||
'warehouseOutFk',
|
||||
'agencyModeFk',
|
||||
'ref'
|
||||
]
|
||||
}, myOptions);
|
||||
return newTravel.id;
|
||||
} catch (e) {
|
||||
if (tx) await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,73 +5,36 @@ describe('Travel cloneWithEntries()', () => {
|
|||
const travelId = 5;
|
||||
const currentUserId = 1102;
|
||||
const ctx = {req: {accessToken: {userId: currentUserId}}};
|
||||
let travelBefore;
|
||||
let newTravelId;
|
||||
|
||||
// afterAll(async() => {
|
||||
// try {
|
||||
// const entries = await models.Entry.find({
|
||||
// where: {
|
||||
// travelFk: newTravelId
|
||||
// }
|
||||
// });
|
||||
// const entriesId = entries.map(entry => entry.id);
|
||||
|
||||
// // Destroy all entries buys
|
||||
// await models.Buy.destroyAll({
|
||||
// where: {
|
||||
// entryFk: {inq: entriesId}
|
||||
// }
|
||||
// });
|
||||
|
||||
// // Destroy travel entries
|
||||
// await models.Entry.destroyAll({
|
||||
// where: {
|
||||
// travelFk: newTravelId
|
||||
// }
|
||||
// });
|
||||
|
||||
// // Destroy new travel
|
||||
// await models.Travel.destroyById(newTravelId);
|
||||
|
||||
// // Restore original travel shipped & landed
|
||||
// const travel = await models.Travel.findById(travelId);
|
||||
// await travel.updateAttributes({
|
||||
// shipped: travelBefore.shipped,
|
||||
// landed: travelBefore.landed
|
||||
// });
|
||||
// } catch (error) {
|
||||
// console.error(error);
|
||||
// }
|
||||
// });
|
||||
|
||||
it(`should clone the travel and the containing entries`, async() => {
|
||||
pending('#2687 - Cannot make a data rollback because of the triggers');
|
||||
const tx = await models.Travel.beginTransaction({
|
||||
});
|
||||
const warehouseThree = 3;
|
||||
const agencyModeOne = 1;
|
||||
const yesterday = Date.vnNew();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
newTravelId = await models.Travel.cloneWithEntries(ctx, travelId, options);
|
||||
const travelEntries = await models.Entry.find({
|
||||
where: {
|
||||
travelFk: newTravelId
|
||||
}
|
||||
}, options);
|
||||
const newTravel = await models.Travel.findById(travelId);
|
||||
|
||||
travelBefore = await models.Travel.findById(travelId);
|
||||
await travelBefore.updateAttributes({
|
||||
shipped: yesterday,
|
||||
landed: yesterday
|
||||
});
|
||||
expect(newTravelId).not.toEqual(travelId);
|
||||
expect(newTravel.ref).toEqual('fifth travel');
|
||||
expect(newTravel.warehouseInFk).toEqual(warehouseThree);
|
||||
expect(newTravel.warehouseOutFk).toEqual(warehouseThree);
|
||||
expect(newTravel.agencyModeFk).toEqual(agencyModeOne);
|
||||
expect(travelEntries.length).toBeGreaterThan(0);
|
||||
|
||||
newTravelId = await models.Travel.cloneWithEntries(ctx, travelId);
|
||||
const travelEntries = await models.Entry.find({
|
||||
where: {
|
||||
travelFk: newTravelId
|
||||
}
|
||||
});
|
||||
await tx.rollback();
|
||||
const travelRemoved = await models.Travel.findById(newTravelId, options);
|
||||
|
||||
const newTravel = await models.Travel.findById(travelId);
|
||||
|
||||
expect(newTravelId).not.toEqual(travelId);
|
||||
expect(newTravel.ref).toEqual('fifth travel');
|
||||
expect(newTravel.warehouseInFk).toEqual(warehouseThree);
|
||||
expect(newTravel.warehouseOutFk).toEqual(warehouseThree);
|
||||
expect(newTravel.agencyModeFk).toEqual(agencyModeOne);
|
||||
expect(travelEntries.length).toBeGreaterThan(0);
|
||||
expect(travelRemoved).toBeNull();
|
||||
} catch (e) {
|
||||
if (tx) await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -247,6 +247,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
|
|||
|
||||
const start = new Date(dated - 1);
|
||||
start.setHours(0, 0, 0);
|
||||
|
||||
await models.WorkerTimeControl.rawSql('CALL vn.timeControl_calculateByUser(?, ?, ?)', [
|
||||
hankPymId,
|
||||
start,
|
||||
|
@ -255,7 +256,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
|
|||
|
||||
let [timeControlCalculateTable] = await models.WorkerTimeControl.rawSql('SELECT * FROM tmp.timeControlCalculate', null, options);
|
||||
|
||||
expect(timeControlCalculateTable.timeWorkSeconds).toEqual(26400);
|
||||
expect(timeControlCalculateTable.timeWorkSeconds).toEqual(25200);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
|
|
Loading…
Reference in New Issue