refs #4700 tests back y empezando e2e
gitea/salix/pipeline/head There was a failure building this commit Details

This commit is contained in:
Alexandre Riera 2022-11-10 15:54:00 +01:00
parent ff00066138
commit 117e2c03f5
16 changed files with 739 additions and 148 deletions

View File

@ -4,6 +4,6 @@ DELIMITER $$
$$ $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canMerge`(vDated DATE, vScopeDays INT, vLitersMax INT, vLinesMax INT, vWarehouseFk INT) CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canMerge`(vDated DATE, vScopeDays INT, vLitersMax INT, vLinesMax INT, vWarehouseFk INT)
BEGIN BEGIN
CALL vn.ticket_canbePostponed(vDated,TIMESTAMPADD(DAY, vScopeDays, vDated),vLitersMax,vLinesMax,vWarehouseFk); CALL vn.ticket_canbePostponed(vDated,TIMESTAMPADD(DAY, vScopeDays, vDated),vLitersMax,vLinesMax,vWarehouseFk);
END $$ END $$
DELIMITER ; DELIMITER ;

View File

@ -3,7 +3,7 @@ DROP PROCEDURE IF EXISTS vn.ticket_canbePostponed;
DELIMITER $$ DELIMITER $$
$$ $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canbePostponed`(vOriginDated DATE, vFutureDated DATE, vLitersMax INT, vLinesMax INT, vWarehouseFk INT) CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canbePostponed`(vOriginDated DATE, vFutureDated DATE, vLitersMax INT, vLinesMax INT, vWarehouseFk INT)
BEGIN BEGIN
/** /**
* Devuelve un listado de tickets susceptibles de fusionarse con otros tickets en el futuro * Devuelve un listado de tickets susceptibles de fusionarse con otros tickets en el futuro
* *
@ -13,25 +13,27 @@ BEGIN
* @param vLinesMax Número máximo de lineas de los tickets a catapultar * @param vLinesMax Número máximo de lineas de los tickets a catapultar
* @param vWarehouseFk Identificador de vn.warehouse * @param vWarehouseFk Identificador de vn.warehouse
*/ */
DROP TEMPORARY TABLE IF EXISTS tmp.filter; DROP TEMPORARY TABLE IF EXISTS tmp.filter;
CREATE TEMPORARY TABLE tmp.filter CREATE TEMPORARY TABLE tmp.filter
(INDEX (id)) (INDEX (id))
SELECT sv.ticketFk id, SELECT sv.ticketFk id,
GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) ipt, GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) ipt,
CAST(sum(litros) AS DECIMAL(10,0)) liters, CAST(sum(litros) AS DECIMAL(10,0)) liters,
CAST(count(*) AS DECIMAL(10,0)) `lines`, CAST(count(*) AS DECIMAL(10,0)) `lines`,
st.name state, st.name state,
sub2.id ticketFuture, sub2.id ticketFuture,
t.landed originETD, t.landed originETD,
sub2.landed destETD, sub2.landed destETD,
sub2.iptd tfIpt, sub2.iptd tfIpt,
sub2.state tfState, sub2.state tfState,
t.clientFk, t.clientFk,
t.warehouseFk, t.warehouseFk,
ts.alertLevel, ts.alertLevel,
t.shipped, t.shipped,
sub2.shipped tfShipped, sub2.shipped tfShipped,
t.workerFk t.workerFk,
st.code code,
sub2.code tfCode
FROM vn.saleVolume sv FROM vn.saleVolume sv
JOIN vn.sale s ON s.id = sv.saleFk JOIN vn.sale s ON s.id = sv.saleFk
JOIN vn.item i ON i.id = s.itemFk JOIN vn.item i ON i.id = s.itemFk
@ -39,7 +41,7 @@ BEGIN
JOIN vn.address a ON a.id = t.addressFk JOIN vn.address a ON a.id = t.addressFk
JOIN vn.province p ON p.id = a.provinceFk JOIN vn.province p ON p.id = a.provinceFk
JOIN vn.country c ON c.id = p.countryFk JOIN vn.country c ON c.id = p.countryFk
JOIN vn.ticketState ts ON ts.ticketFk = t.id JOIN vn.ticketState ts ON ts.ticketFk = t.id
JOIN vn.state st ON st.id = ts.stateFk JOIN vn.state st ON st.id = ts.stateFk
JOIN vn.alertLevel al ON al.id = ts.alertLevel JOIN vn.alertLevel al ON al.id = ts.alertLevel
LEFT JOIN vn.ticketParking tp ON tp.ticketFk = t.id LEFT JOIN vn.ticketParking tp ON tp.ticketFk = t.id
@ -52,6 +54,7 @@ BEGIN
t.landed, t.landed,
t.shipped, t.shipped,
st.name state, st.name state,
st.code code,
GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) iptd GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) iptd
FROM vn.ticket t FROM vn.ticket t
JOIN vn.ticketState ts ON ts.ticketFk = t.id JOIN vn.ticketState ts ON ts.ticketFk = t.id
@ -67,7 +70,7 @@ BEGIN
) sub2 ON sub2.addressFk = t.addressFk AND t.id != sub2.id ) sub2 ON sub2.addressFk = t.addressFk AND t.id != sub2.id
WHERE t.shipped BETWEEN vOriginDated AND util.dayend(vOriginDated) WHERE t.shipped BETWEEN vOriginDated AND util.dayend(vOriginDated)
AND t.warehouseFk = vWarehouseFk AND t.warehouseFk = vWarehouseFk
AND al.code = 'FREE' AND al.code = 'FREE'
AND tp.ticketFk IS NULL AND tp.ticketFk IS NULL
GROUP BY sv.ticketFk GROUP BY sv.ticketFk
HAVING liters <= IFNULL(vLitersMax, 9999) AND `lines` <= IFNULL(vLinesMax, 9999) AND ticketFuture; HAVING liters <= IFNULL(vLitersMax, 9999) AND `lines` <= IFNULL(vLinesMax, 9999) AND ticketFuture;

View File

@ -712,6 +712,27 @@ export default {
saveImport: 'button[response="accept"]', saveImport: 'button[response="accept"]',
anyDocument: 'vn-ticket-dms-index > vn-data-viewer vn-tbody vn-tr' anyDocument: 'vn-ticket-dms-index > vn-data-viewer vn-tbody vn-tr'
}, },
ticketFuture: {
openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]',
originDated: 'vn-date-picker[label="Origin ETD"]',
futureDated: 'vn-date-picker[label="Destination ETD"]',
shipped: 'vn-date-picker[label="Origin date"]',
tfShipped: 'vn-date-picker[label="Destination date"]',
linesMax: 'vn-textfield[label="Max Lines"]',
litersMax: 'vn-textfield[label="Max Liters"]',
ipt: 'vn-autocomplete[label="Origin IPT"]',
tfIpt: 'vn-autocomplete[label="Destination IPT"]',
state: 'vn-autocomplete[label="Origin Grouped State"]',
tfState: 'vn-autocomplete[label="Destination Grouped State"]',
warehouseFk: 'vn-autocomplete[label="Warehouse"]',
problems: 'vn-check[label="With problems"]',
tableButtonSearch: 'vn-button[vn-tooltip="Search"]',
moveButton: 'vn-button[vn-tooltip="Future tickets"]',
tableId: 'vn-textfield[ng-model="searchProps[\'id\']"]',
tableTfId: 'vn-textfield[ng-model="searchProps[\'ticketFuture\']"]',
submit: 'vn-submit[label="Search"]',
table: 'tbody > tr:not(.empty-rows, #searchRow)',
},
createStateView: { createStateView: {
state: 'vn-autocomplete[ng-model="$ctrl.stateFk"]', state: 'vn-autocomplete[ng-model="$ctrl.stateFk"]',
worker: 'vn-autocomplete[ng-model="$ctrl.workerFk"]', worker: 'vn-autocomplete[ng-model="$ctrl.workerFk"]',

View File

@ -0,0 +1,126 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket Future path', () => {
let browser;
let page;
beforeAll(async () => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'ticket');
await page.accessToSection('ticket.future');
});
afterAll(async () => {
await browser.close();
});
const now = new Date();
const tomorrow = new Date(now.getDate() + 1);
it('should search with the required data', async () => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.pickDate(selectors.ticketFuture.originDated, now);
await page.pickDate(selectors.ticketFuture.futureDated, now);
await page.waitToClick(selectors.ticketFuture.linesMax);
await page.write(selectors.ticketFuture.linesMax, '9999');
await page.write(selectors.ticketFuture.litersMax, '9999');
await page.autocompleteSearch(selectors.ticketFuture.warehouseFk, 'Warehouse One');
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
});
it('should search with the origin shipped today', async () => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.pickDate(selectors.ticketFuture.shipped, now);
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
});
it('should search with the origin shipped tomorrow', async () => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.pickDate(selectors.ticketFuture.shipped, tomorrow);
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 0);
});
it('should search with the destination shipped today', async () => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.shipped);
await page.pickDate(selectors.ticketFuture.tfShipped, now);
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
});
it('should search with the destination shipped tomorrow', async () => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.pickDate(selectors.ticketFuture.tfShipped, tomorrow);
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 0);
});
it('should search with the origin IPT', async () => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.tfShipped);
await page.autocompleteSearch(selectors.ticketFuture.ipt, 'Horizontal');
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 0);
});
it('should search with the destination IPT', async () => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.ipt);
await page.autocompleteSearch(selectors.ticketFuture.tfIpt, 'Horizontal');
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 0);
});
it('should search with the origin grouped state', async () => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.tfIpt);
await page.autocompleteSearch(selectors.ticketFuture.state, 'Free');
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 3);
});
it('should search with the destination grouped state', async () => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.state);
await page.autocompleteSearch(selectors.ticketFuture.tfState, 'Free');
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 0);
});
it('should search with problems selected', async () => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.tfState);
await page.waitToClick(selectors.ticketFuture.problems);
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
});
it('should search with no problems selected', async () => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketFuture.problems);
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 0);
});
it('should search with an ID Origin', async () => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketFuture.problems);
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
await page.write(selectors.ticketFuture.tableId, "13");
await page.keyboard.press("Enter");
await page.waitForNumberOfElements(selectors.ticketFuture.table, 1);
});
it('should search with an ID Destination', async () => {
await page.clearInput(selectors.ticketFuture.tableId);
await page.write(selectors.ticketFuture.tableTfId, "12");
await page.waitForNumberOfElements(selectors.ticketFuture.table, 1);
});
// const message = await page.waitForSnackbar();
});

View File

@ -108,44 +108,15 @@ module.exports = Self => {
} }
}); });
Self.getTicketsFuture = async (ctx, filter, options) => { Self.getTicketsFuture = async (ctx, options) => {
const args = ctx.args;
const conn = Self.dataSource.connector;
const myOptions = {}; const myOptions = {};
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
const conn = Self.dataSource.connector;
const args = ctx.args;
const stmts = [];
let stmt;
stmt = new ParameterizedSQL( const where = buildFilter(ctx.args, (param, value) => {
`CALL vn.ticket_canbePostponed(?,?,?,?,?)`,
[args.originDated, args.futureDated, args.litersMax, args.linesMax, args.warehouseFk]);
stmts.push(stmt);
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.sale_getProblems');
stmt = new ParameterizedSQL(`
CREATE TEMPORARY TABLE tmp.sale_getProblems
(INDEX (ticketFk))
ENGINE = MEMORY
SELECT f.id ticketFk, f.clientFk, f.warehouseFk, f.shipped
FROM tmp.filter f
LEFT JOIN alertLevel al ON al.id = f.alertLevel
WHERE (al.code = 'FREE' OR f.alertLevel IS NULL)`);
stmts.push(stmt);
stmts.push('CALL ticket_getProblems(FALSE)');
stmt = new ParameterizedSQL(`
SELECT f.*, tp.*
FROM tmp.filter f
LEFT JOIN tmp.ticket_problems tp ON tp.ticketFk = f.id`);
if (args.problems != undefined && (!args.originDated && !args.futureDated))
throw new UserError('Choose a date range or days forward');
const searchBarWhere = buildFilter(args, (param, value) => {
switch (param) { switch (param) {
case 'id': case 'id':
return { 'f.id': value }; return { 'f.id': value };
@ -160,14 +131,43 @@ module.exports = Self => {
case 'tfIpt': case 'tfIpt':
return { 'f.tfIpt': value }; return { 'f.tfIpt': value };
case 'state': case 'state':
return { 'f.state': { like: `%${value}%` } }; return { 'f.code': { like: `%${value}%` } };
case 'tfState': case 'tfState':
return { 'f.tfState': { like: `%${value}%` } }; return { 'f.tfCode': { like: `%${value}%` } };
} }
}); });
filter = mergeFilters(args.filter, { where: searchBarWhere }); let filter = mergeFilters(ctx.args.filter, { where });
stmt.merge(conn.makeSuffix(filter)); const stmts = [];
let stmt;
stmt = new ParameterizedSQL(
`CALL vn.ticket_canbePostponed(?,?,?,?,?)`,
[args.originDated, args.futureDated, args.litersMax, args.linesMax, args.warehouseFk]);
stmts.push(stmt);
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.sale_getProblems');
stmt = new ParameterizedSQL(`
CREATE TEMPORARY TABLE tmp.sale_getProblems
(INDEX (ticketFk))
ENGINE = MEMORY
SELECT f.id ticketFk, f.clientFk, f.warehouseFk, f.shipped
FROM tmp.filter f
LEFT JOIN alertLevel al ON al.id = f.alertLevel
WHERE (al.code = 'FREE' OR f.alertLevel IS NULL)`);
stmts.push(stmt);
stmts.push('CALL ticket_getProblems(FALSE)');
stmt = new ParameterizedSQL(`
SELECT f.*, tp.*
FROM tmp.filter f
LEFT JOIN tmp.ticket_problems tp ON tp.ticketFk = f.id`);
if (args.problems != undefined && (!args.originDated && !args.futureDated))
throw new UserError('Choose a date range or days forward');
let condition; let condition;
let hasProblem; let hasProblem;
@ -196,13 +196,15 @@ module.exports = Self => {
{ 'tp.hasTicketRequest': hasProblem }, { 'tp.hasTicketRequest': hasProblem },
{ 'tp.itemShortage': range }, { 'tp.itemShortage': range },
{ 'tp.hasComponentLack': hasProblem }, { 'tp.hasComponentLack': hasProblem },
{ 'tp.isTaxDataChecked': !hasProblem },
{ 'tp.isTooLittle': hasProblem } { 'tp.isTooLittle': hasProblem }
] ]
}; };
if (hasWhere) if (hasWhere) {
stmt.merge(conn.makeWhere(problems)); filter = mergeFilters(filter, { where: problems });
}
stmt.merge(conn.makeWhere(filter.where));
const ticketsIndex = stmts.push(stmt) - 1; const ticketsIndex = stmts.push(stmt) - 1;
@ -212,7 +214,9 @@ module.exports = Self => {
tmp.ticket_problems`); tmp.ticket_problems`);
const sql = ParameterizedSQL.join(stmts, ';'); const sql = ParameterizedSQL.join(stmts, ';');
const result = await conn.executeStmt(sql, myOptions); const result = await conn.executeStmt(sql, myOptions);
return result[ticketsIndex]; return result[ticketsIndex];
}; };
}; };

View File

@ -1,8 +1,9 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
fdescribe('TicketFuture getTicketsFuture()', () => { describe('TicketFuture getTicketsFuture()', () => {
const today = new Date(); const today = new Date();
today.setHours(0, 0, 0, 0); today.setHours(0, 0, 0, 0);
const tomorrow = new Date(today.getDate() + 1);
it('should return the tickets passing the required data', async () => { it('should return the tickets passing the required data', async () => {
const tx = await models.Ticket.beginTransaction({}); const tx = await models.Ticket.beginTransaction({});
@ -83,11 +84,11 @@ fdescribe('TicketFuture getTicketsFuture()', () => {
} }
}); });
it('should return the tickets matching the problems on null', async() => { it('should return the tickets matching the problems on null', async () => {
const tx = await models.Ticket.beginTransaction({}); const tx = await models.Ticket.beginTransaction({});
try { try {
const options = {transaction: tx}; const options = { transaction: tx };
const args = { const args = {
originDated: today, originDated: today,
@ -98,7 +99,7 @@ fdescribe('TicketFuture getTicketsFuture()', () => {
problems: null problems: null
}; };
const ctx = {req: {accessToken: {userId: 9}}, args}; const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options); const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(4); expect(result.length).toEqual(4);
@ -110,12 +111,11 @@ fdescribe('TicketFuture getTicketsFuture()', () => {
} }
}); });
it('should return the tickets matching the correct origin shipped', async() => { it('should return the tickets matching the correct origin shipped', async () => {
let tomorrow = new Date(today.getDate() + 1);
const tx = await models.Ticket.beginTransaction({}); const tx = await models.Ticket.beginTransaction({});
try { try {
const options = {transaction: tx}; const options = { transaction: tx };
const args = { const args = {
originDated: today, originDated: today,
@ -126,7 +126,7 @@ fdescribe('TicketFuture getTicketsFuture()', () => {
shipped: today shipped: today
}; };
const ctx = {req: {accessToken: {userId: 9}}, args}; const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options); const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(4); expect(result.length).toEqual(4);
@ -136,30 +136,303 @@ fdescribe('TicketFuture getTicketsFuture()', () => {
await tx.rollback(); await tx.rollback();
throw e; throw e;
} }
// try {
// const options = {transaction: tx};
// const args = {
// originDated: today,
// futureDated: today,
// litersMax: 9999,
// linesMax: 9999,
// warehouseFk: 1,
// shipped: tomorrow
// };
// const ctx = {req: {accessToken: {userId: 9}}, args};
// const result = await models.Ticket.getTicketsFuture(ctx, options);
// expect(result.length).toEqual(0);
// await tx.rollback();
// } catch (e) {
// await tx.rollback();
// throw e;
// }
}); });
it('should return the tickets matching the an incorrect origin shipped', async () => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = { transaction: tx };
const args = {
originDated: today,
futureDated: today,
litersMax: 9999,
linesMax: 9999,
warehouseFk: 1,
shipped: tomorrow
};
const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(0);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the tickets matching the correct destination shipped', async () => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = { transaction: tx };
const args = {
originDated: today,
futureDated: today,
litersMax: 9999,
linesMax: 9999,
warehouseFk: 1,
tfShipped: today
};
const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the tickets matching the an incorrect destination shipped', async () => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = { transaction: tx };
const args = {
originDated: today,
futureDated: today,
litersMax: 9999,
linesMax: 9999,
warehouseFk: 1,
tfShipped: tomorrow
};
const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(0);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the tickets matching the OK State in origin date', async () => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = { transaction: tx };
const args = {
originDated: today,
futureDated: today,
litersMax: 9999,
linesMax: 9999,
warehouseFk: 1,
state: "OK"
};
const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(1);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the tickets matching the OK State in destination date', async () => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = { transaction: tx };
const args = {
originDated: today,
futureDated: today,
litersMax: 9999,
linesMax: 9999,
warehouseFk: 1,
tfState: "OK"
};
const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the tickets matching the correct IPT in origin date', async () => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = { transaction: tx };
const args = {
originDated: today,
futureDated: today,
litersMax: 9999,
linesMax: 9999,
warehouseFk: 1,
ipt: null
};
const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the tickets matching the incorrect IPT in origin date', async () => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = { transaction: tx };
const args = {
originDated: today,
futureDated: today,
litersMax: 9999,
linesMax: 9999,
warehouseFk: 1,
ipt: 0
};
const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(0);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the tickets matching the correct IPT in destination date', async () => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = { transaction: tx };
const args = {
originDated: today,
futureDated: today,
litersMax: 9999,
linesMax: 9999,
warehouseFk: 1,
tfIpt: null
};
const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the tickets matching the incorrect IPT in destination date', async () => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = { transaction: tx };
const args = {
originDated: today,
futureDated: today,
litersMax: 9999,
linesMax: 9999,
warehouseFk: 1,
tfIpt: 0
};
const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(0);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the tickets matching the ID in origin date', async () => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = { transaction: tx };
const args = {
originDated: today,
futureDated: today,
litersMax: 9999,
linesMax: 9999,
warehouseFk: 1,
id: 13
};
const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(1);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the tickets matching the ID in destination date', async () => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = { transaction: tx };
const args = {
originDated: today,
futureDated: today,
litersMax: 9999,
linesMax: 9999,
warehouseFk: 1,
tfId: 12
};
const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
}); });

View File

@ -1,5 +1,3 @@
const LoopBackContext = require('loopback-context');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('merge', { Self.remoteMethodCtx('merge', {
description: 'Merge one ticket into another', description: 'Merge one ticket into another',
@ -23,8 +21,9 @@ module.exports = Self => {
}); });
Self.merge = async (ctx, tickets, options) => { Self.merge = async (ctx, tickets, options) => {
const loopBackContext = LoopBackContext.getCurrentContext(); const httpRequest = ctx.req;
const httpCtx = {req: loopBackContext.active}; const $t = httpRequest.__;
const origin = httpRequest.headers.origin;
const models = Self.app.models; const models = Self.app.models;
const myOptions = {}; const myOptions = {};
let tx; let tx;
@ -39,24 +38,21 @@ module.exports = Self => {
for (let ticket of tickets) { for (let ticket of tickets) {
try { try {
const fullPath = `${origin}/#!/ticket/${ticket.ticketFuture}/summary`;
const message = $t('MOVE_TICKET_CONFIRMATION', {
originDated: new Date(ticket.originETD).toLocaleDateString('es-ES'),
futureDated: new Date(ticket.destETD).toLocaleDateString('es-ES'),
id: ticket.id,
tfId: ticket.ticketFuture,
fullPath
});
if(!ticket.id || !ticket.ticketFuture) continue; if(!ticket.id || !ticket.ticketFuture) continue;
await models.Sale.updateAll({ticketFk: ticket.id}, {ticketFk: ticket.ticketFuture}, myOptions); await models.Sale.updateAll({ticketFk: ticket.id}, {ticketFk: ticket.ticketFuture}, myOptions);
await models.Ticket.setDeleted(ctx, ticket.id, myOptions); await models.Ticket.setDeleted(ctx, ticket.id, myOptions);
await models.Chat.sendCheckingPresence(ctx, ticket.workerFk, message);
if (tx) if (tx)
{ {
const httpRequest = httpCtx.req.http.req;
const $t = httpRequest.__;
const origin = httpRequest.headers.origin;
const fullPath = `${origin}/#!/ticket/${ticket.ticketFuture}/summary`;
const message = $t('MOVE_TICKET_CONFIRMATION', {
originDated: new Date(ticket.originETD).toLocaleDateString('es-ES'),
futureDated: new Date(ticket.destETD).toLocaleDateString('es-ES'),
id: ticket.id,
tfId: ticket.ticketFuture,
fullPath
});
await tx.commit(); await tx.commit();
await models.Chat.sendCheckingPresence(httpCtx, ticket.workerFk, message);
} }
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();

View File

@ -0,0 +1,58 @@
const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('ticket merge()', () => {
const tickets = [{
id: 13,
ticketFuture: 12,
workerFk: 1,
originETD: new Date(),
destETD: new Date()
}];
const activeCtx = {
accessToken: { userId: 9 },
};
beforeEach(() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
});
const ctx = {
req: {
accessToken: { userId: 9 },
headers: { origin: 'http://localhost:5000' },
}
};
ctx.req.__ = value => {
return value;
};
it('should merge two tickets', async () => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = { transaction: tx };
const chatNotificationBeforeMerge = await models.Chat.find();
await models.Ticket.merge(ctx, tickets, options);
const createdTicketLog = await models.TicketLog.find({ where: { originFk: tickets[0].id } }, options);
const deletedTicket = await models.Ticket.findOne({ where: { id: tickets[0].id } }, options);
const salesTicketFuture = await models.Sale.find({ where: { ticketFk: tickets[0].ticketFuture } }, options);
const chatNotificationAfterMerge = await models.Chat.find();
expect(createdTicketLog.length).toEqual(1);
expect(deletedTicket.isDeleted).toEqual(true);
expect(salesTicketFuture.length).toEqual(2);
expect(chatNotificationBeforeMerge.length).toEqual(chatNotificationAfterMerge.length - 2);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -18,52 +18,76 @@
vn-one vn-one
label="Origin ETD" label="Origin ETD"
ng-model="filter.originDated" ng-model="filter.originDated"
required="true"> required="true"
info="ETD">
</vn-date-picker> </vn-date-picker>
<vn-date-picker <vn-date-picker
vn-one vn-one
label="Destination ETD" label="Destination ETD"
ng-model="filter.futureDated" ng-model="filter.futureDated"
required="true"> required="true"
info="ETD">
</vn-date-picker> </vn-date-picker>
</vn-horizontal> </vn-horizontal>
<vn-horizontal class="vn-px-lg"> <vn-horizontal class="vn-px-lg">
<vn-textfield <vn-textfield
vn-one vn-one
label="Max Lines Origin" label="Max Lines"
ng-model="filter.linesMax" ng-model="filter.linesMax"
required="true"> required="true">
</vn-textfield> </vn-textfield>
<vn-textfield <vn-textfield
vn-one vn-one
label="Max Liters Origin" label="Max Liters"
ng-model="filter.litersMax" ng-model="filter.litersMax"
required="true"> required="true">
</vn-textfield> </vn-textfield>
</vn-horizontal> </vn-horizontal>
<vn-horizontal class="vn-px-lg"> <vn-horizontal class="vn-px-lg">
<vn-textfield <vn-autocomplete vn-one
vn-one data="$ctrl.itemPackingTypes"
label="Origin IPT" label="Origin IPT"
ng-model="filter.ipt"> value-field="code"
</vn-textfield> show-field="name"
<vn-textfield ng-model="filter.ipt"
vn-one info="IPT">
<tpl-item>
{{name}}
</tpl-item>
</vn-autocomplete>
<vn-autocomplete vn-one
data="$ctrl.itemPackingTypes"
label="Destination IPT" label="Destination IPT"
ng-model="filter.tfIpt"> value-field="code"
</vn-textfield> show-field="name"
ng-model="filter.tfIpt"
info="IPT">
<tpl-item>
{{name}}
</tpl-item>
</vn-autocomplete>
</vn-horizontal> </vn-horizontal>
<vn-horizontal class="vn-px-lg"> <vn-horizontal class="vn-px-lg">
<vn-textfield <vn-autocomplete vn-one
vn-one data="$ctrl.groupedStates"
label="Origin Agrupated State" label="Origin Grouped State"
value-field="code"
show-field="name"
ng-model="filter.state"> ng-model="filter.state">
</vn-textfield> <tpl-item>
<vn-textfield {{name}}
vn-one </tpl-item>
label="Destination Agrupated State" </vn-autocomplete>
<vn-autocomplete vn-one
data="$ctrl.groupedStates"
label="Destination Grouped State"
value-field="code"
show-field="name"
ng-model="filter.tfState"> ng-model="filter.tfState">
</vn-textfield> <tpl-item>
{{name}}
</tpl-item>
</vn-autocomplete>
</vn-horizontal> </vn-horizontal>
<vn-horizontal class="vn-px-lg"> <vn-horizontal class="vn-px-lg">
<vn-check <vn-check
@ -72,12 +96,13 @@
ng-model="filter.problems" ng-model="filter.problems"
triple-state="true"> triple-state="true">
</vn-check> </vn-check>
<vn-textfield <vn-autocomplete
vn-one vn-one
label="Warehouse" label="Warehouse"
ng-model="filter.warehouseFk" ng-model="filter.warehouseFk"
url="Warehouses"
required="true"> required="true">
</vn-textfield> </vn-autocomplete>
</vn-horizontal> </vn-horizontal>
<vn-horizontal class="vn-px-lg vn-pb-lg vn-mt-lg"> <vn-horizontal class="vn-px-lg vn-pb-lg vn-mt-lg">
<vn-submit label="Search"></vn-submit> <vn-submit label="Search"></vn-submit>

View File

@ -5,6 +5,36 @@ class Controller extends SearchPanel {
constructor($, $element) { constructor($, $element) {
super($, $element); super($, $element);
this.filter = this.$.filter; this.filter = this.$.filter;
this.getGroupedStates();
this.getItemPackingTypes();
}
getGroupedStates() {
let groupedStates = [];
this.$http.get('AlertLevels').then(res => {
for (let state of res.data) {
groupedStates.push({
id: state.id,
code: state.code,
name: this.$t(state.code)
});
}
this.groupedStates = groupedStates;
});
}
getItemPackingTypes() {
let itemPackingTypes = [];
this.$http.get('ItemPackingTypes').then(res => {
for (let ipt of res.data) {
itemPackingTypes.push({
id: ipt.id,
code: ipt.code,
name: this.$t(ipt.code)
});
}
this.itemPackingTypes = itemPackingTypes;
});
} }
} }

View File

@ -1 +1,9 @@
Future tickets: Tickets a futuro Future tickets: Tickets a futuro
FREE: Free
DELIVERED: Delivered
ON_PREPARATION: On preparation
PACKED: Packed
F: Fruits and vegetables
V: Vertical
H: Horizontal
P: Feed

View File

@ -3,11 +3,21 @@ Origin date: Fecha origen
Destination date: Fecha destino Destination date: Fecha destino
Origin ETD: ETD origen Origin ETD: ETD origen
Destination ETD: ETD destino Destination ETD: ETD destino
Max Lines Origin: Líneas máx. origen Max Lines: Líneas máx.
Max Liters Origin: Litros máx. origen Max Liters: Litros máx.
Origin IPT: IPT origen Origin IPT: IPT origen
Destination IPT: IPT destino Destination IPT: IPT destino
With problems: Con problemas With problems: Con problemas
Warehouse: Almacén Warehouse: Almacén
Origin Agrupated State: Estado agrupado origen Origin Grouped State: Estado agrupado origen
Destination Agrupated State: Estado agrupado destino Destination Grouped State: Estado agrupado destino
FREE: Libre
DELIVERED: Servido
ON_PREPARATION: En preparacion
PACKED: Encajado
F: Frutas y verduras
V: Vertical
H: Horizontal
P: Pienso
ETD: Tiempo estimado de entrega
IPT: Encajado

View File

@ -1,7 +1,6 @@
<vn-crud-model <vn-crud-model
vn-id="model" vn-id="model"
url="Tickets/getTicketsFuture" url="Tickets/getTicketsFuture"
filter="::$ctrl.filter"
limit="20"> limit="20">
</vn-crud-model> </vn-crud-model>
<vn-portal slot="topbar"> <vn-portal slot="topbar">
@ -11,14 +10,14 @@
placeholder="Search tickets" placeholder="Search tickets"
info="Search future tickets by date" info="Search future tickets by date"
auto-state="false" auto-state="false"
model="model" model="model">
filter="::$ctrl.filter">
</vn-searchbar> </vn-searchbar>
</vn-portal> </vn-portal>
<vn-card> <vn-card>
<smart-table <smart-table
model="model" model="model"
options="$ctrl.smartTableOptions" options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)"
> >
<slot-actions> <slot-actions>
<vn-button disabled="$ctrl.checked.length === 0" <vn-button disabled="$ctrl.checked.length === 0"
@ -53,10 +52,10 @@
<th field="ipt"> <th field="ipt">
<span>IPT</span> <span>IPT</span>
</th> </th>
<th field="liters"> <th field="litersMax">
<span translate>Liters</span> <span translate>Liters</span>
</th> </th>
<th field="lines"> <th field="linesMax">
<span translate>Available Lines</span> <span translate>Available Lines</span>
</th> </th>
<th field="ticketFuture"> <th field="ticketFuture">

View File

@ -6,23 +6,9 @@ export default class Controller extends Section {
super($element, $); super($element, $);
this.$checkAll = false; this.$checkAll = false;
const originDated = new Date();
const futureDated = new Date();
const warehouseFk = 1;
const litersMax = 9999;
const linesMax = 9999;
this.defaultFilter = {
originDated,
futureDated,
warehouseFk,
litersMax,
linesMax
};
this.smartTableOptions = { this.smartTableOptions = {
activeButtons: { activeButtons: {
search: true search: true,
}, },
columns: [{ columns: [{
field: 'problems', field: 'problems',
@ -35,7 +21,32 @@ export default class Controller extends Section {
{ {
field: 'destETD', field: 'destETD',
searchable: false searchable: false
}] },
{
field: 'state',
searchable: false
},
{
field: 'tfState',
searchable: false
},
{
field: 'ipt',
autocomplete: {
url: 'ItemPackingTypes',
showField: 'description',
valueField: 'code'
}
},
{
field: 'tfIpt',
autocomplete: {
url: 'ItemPackingTypes',
showField: 'description',
valueField: 'code'
}
},
]
}; };
} }
@ -67,7 +78,7 @@ export default class Controller extends Section {
stateColor(state) { stateColor(state) {
if (state === 'OK') if (state === 'OK')
return 'success'; return 'success';
else if (state === 'FREE') else if (state === 'Libre')
return 'notice'; return 'notice';
} }
@ -96,6 +107,23 @@ export default class Controller extends Section {
this.vnApp.showSuccess(this.$t('Success')); this.vnApp.showSuccess(this.$t('Success'));
}); });
} }
exprBuilder(param, value) {
switch (param) {
case 'id':
return { 'id': value };
case 'ticketFuture':
return { 'ticketFuture': value };
case 'litersMax':
return { 'liters': value };
case 'linesMax':
return { 'lines': value };
case 'ipt':
return { 'ipt': value };
case 'tfIpt':
return { 'tfIpt': value };
}
}
} }
Controller.$inject = ['$element', '$scope']; Controller.$inject = ['$element', '$scope'];

View File

@ -1 +1,5 @@
Move confirmation: Do you want to move {{checked}} tickets to the future? Move confirmation: Do you want to move {{checked}} tickets to the future?
FREE: Free
DELIVERED: Delivered
ON_PREPARATION: On preparation
PACKED: Packed

View File

@ -14,3 +14,9 @@ Origin ETD: ETD Origen
Move tickets: Mover tickets Move tickets: Mover tickets
Move confirmation: ¿Desea mover {{checked}} tickets hacia el futuro? Move confirmation: ¿Desea mover {{checked}} tickets hacia el futuro?
Success: Tickets movidos correctamente Success: Tickets movidos correctamente
ETD: Tiempo estimado de entrega
IPT: Encajado
FREE: Libre
DELIVERED: Servido
ON_PREPARATION: En preparacion
PACKED: Encajado