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)
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 $$
DELIMITER ;

View File

@ -3,7 +3,7 @@ DROP PROCEDURE IF EXISTS vn.ticket_canbePostponed;
DELIMITER $$
$$
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
*
@ -13,25 +13,27 @@ BEGIN
* @param vLinesMax Número máximo de lineas de los tickets a catapultar
* @param vWarehouseFk Identificador de vn.warehouse
*/
DROP TEMPORARY TABLE IF EXISTS tmp.filter;
CREATE TEMPORARY TABLE tmp.filter
DROP TEMPORARY TABLE IF EXISTS tmp.filter;
CREATE TEMPORARY TABLE tmp.filter
(INDEX (id))
SELECT sv.ticketFk id,
GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) ipt,
CAST(sum(litros) AS DECIMAL(10,0)) liters,
CAST(count(*) AS DECIMAL(10,0)) `lines`,
st.name state,
sub2.id ticketFuture,
CAST(count(*) AS DECIMAL(10,0)) `lines`,
st.name state,
sub2.id ticketFuture,
t.landed originETD,
sub2.landed destETD,
sub2.iptd tfIpt,
sub2.state tfState,
t.clientFk,
t.warehouseFk,
ts.alertLevel,
t.warehouseFk,
ts.alertLevel,
t.shipped,
sub2.shipped tfShipped,
t.workerFk
t.workerFk,
st.code code,
sub2.code tfCode
FROM vn.saleVolume sv
JOIN vn.sale s ON s.id = sv.saleFk
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.province p ON p.id = a.provinceFk
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.alertLevel al ON al.id = ts.alertLevel
LEFT JOIN vn.ticketParking tp ON tp.ticketFk = t.id
@ -52,6 +54,7 @@ BEGIN
t.landed,
t.shipped,
st.name state,
st.code code,
GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) iptd
FROM vn.ticket t
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
WHERE t.shipped BETWEEN vOriginDated AND util.dayend(vOriginDated)
AND t.warehouseFk = vWarehouseFk
AND al.code = 'FREE'
AND al.code = 'FREE'
AND tp.ticketFk IS NULL
GROUP BY sv.ticketFk
HAVING liters <= IFNULL(vLitersMax, 9999) AND `lines` <= IFNULL(vLinesMax, 9999) AND ticketFuture;

View File

@ -712,6 +712,27 @@ export default {
saveImport: 'button[response="accept"]',
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: {
state: 'vn-autocomplete[ng-model="$ctrl.stateFk"]',
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 = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const conn = Self.dataSource.connector;
const args = ctx.args;
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');
const searchBarWhere = buildFilter(args, (param, value) => {
const where = buildFilter(ctx.args, (param, value) => {
switch (param) {
case 'id':
return { 'f.id': value };
@ -160,14 +131,43 @@ module.exports = Self => {
case 'tfIpt':
return { 'f.tfIpt': value };
case 'state':
return { 'f.state': { like: `%${value}%` } };
return { 'f.code': { like: `%${value}%` } };
case 'tfState':
return { 'f.tfState': { like: `%${value}%` } };
return { 'f.tfCode': { like: `%${value}%` } };
}
});
filter = mergeFilters(args.filter, { where: searchBarWhere });
stmt.merge(conn.makeSuffix(filter));
let filter = mergeFilters(ctx.args.filter, { where });
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 hasProblem;
@ -196,13 +196,15 @@ module.exports = Self => {
{ 'tp.hasTicketRequest': hasProblem },
{ 'tp.itemShortage': range },
{ 'tp.hasComponentLack': hasProblem },
{ 'tp.isTaxDataChecked': !hasProblem },
{ 'tp.isTooLittle': hasProblem }
]
};
if (hasWhere)
stmt.merge(conn.makeWhere(problems));
if (hasWhere) {
filter = mergeFilters(filter, { where: problems });
}
stmt.merge(conn.makeWhere(filter.where));
const ticketsIndex = stmts.push(stmt) - 1;
@ -212,7 +214,9 @@ module.exports = Self => {
tmp.ticket_problems`);
const sql = ParameterizedSQL.join(stmts, ';');
const result = await conn.executeStmt(sql, myOptions);
return result[ticketsIndex];
};
};

View File

@ -1,8 +1,9 @@
const models = require('vn-loopback/server/server').models;
fdescribe('TicketFuture getTicketsFuture()', () => {
describe('TicketFuture getTicketsFuture()', () => {
const today = new Date();
today.setHours(0, 0, 0, 0);
const tomorrow = new Date(today.getDate() + 1);
it('should return the tickets passing the required data', async () => {
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({});
try {
const options = {transaction: tx};
const options = { transaction: tx };
const args = {
originDated: today,
@ -98,7 +99,7 @@ fdescribe('TicketFuture getTicketsFuture()', () => {
problems: null
};
const ctx = {req: {accessToken: {userId: 9}}, args};
const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(4);
@ -110,12 +111,11 @@ fdescribe('TicketFuture getTicketsFuture()', () => {
}
});
it('should return the tickets matching the correct origin shipped', async() => {
let tomorrow = new Date(today.getDate() + 1);
it('should return the tickets matching the correct origin shipped', async () => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const options = { transaction: tx };
const args = {
originDated: today,
@ -126,7 +126,7 @@ fdescribe('TicketFuture getTicketsFuture()', () => {
shipped: today
};
const ctx = {req: {accessToken: {userId: 9}}, args};
const ctx = { req: { accessToken: { userId: 9 } }, args };
const result = await models.Ticket.getTicketsFuture(ctx, options);
expect(result.length).toEqual(4);
@ -136,30 +136,303 @@ fdescribe('TicketFuture getTicketsFuture()', () => {
await tx.rollback();
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 => {
Self.remoteMethodCtx('merge', {
description: 'Merge one ticket into another',
@ -23,8 +21,9 @@ module.exports = Self => {
});
Self.merge = async (ctx, tickets, options) => {
const loopBackContext = LoopBackContext.getCurrentContext();
const httpCtx = {req: loopBackContext.active};
const httpRequest = ctx.req;
const $t = httpRequest.__;
const origin = httpRequest.headers.origin;
const models = Self.app.models;
const myOptions = {};
let tx;
@ -39,24 +38,21 @@ module.exports = Self => {
for (let ticket of tickets) {
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;
await models.Sale.updateAll({ticketFk: ticket.id}, {ticketFk: ticket.ticketFuture}, myOptions);
await models.Ticket.setDeleted(ctx, ticket.id, myOptions);
await models.Chat.sendCheckingPresence(ctx, ticket.workerFk, message);
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 models.Chat.sendCheckingPresence(httpCtx, ticket.workerFk, message);
}
} catch (e) {
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
label="Origin ETD"
ng-model="filter.originDated"
required="true">
required="true"
info="ETD">
</vn-date-picker>
<vn-date-picker
vn-one
label="Destination ETD"
ng-model="filter.futureDated"
required="true">
required="true"
info="ETD">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-textfield
vn-one
label="Max Lines Origin"
label="Max Lines"
ng-model="filter.linesMax"
required="true">
</vn-textfield>
<vn-textfield
vn-one
label="Max Liters Origin"
label="Max Liters"
ng-model="filter.litersMax"
required="true">
</vn-textfield>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-textfield
vn-one
<vn-autocomplete vn-one
data="$ctrl.itemPackingTypes"
label="Origin IPT"
ng-model="filter.ipt">
</vn-textfield>
<vn-textfield
vn-one
value-field="code"
show-field="name"
ng-model="filter.ipt"
info="IPT">
<tpl-item>
{{name}}
</tpl-item>
</vn-autocomplete>
<vn-autocomplete vn-one
data="$ctrl.itemPackingTypes"
label="Destination IPT"
ng-model="filter.tfIpt">
</vn-textfield>
value-field="code"
show-field="name"
ng-model="filter.tfIpt"
info="IPT">
<tpl-item>
{{name}}
</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-textfield
vn-one
label="Origin Agrupated State"
<vn-autocomplete vn-one
data="$ctrl.groupedStates"
label="Origin Grouped State"
value-field="code"
show-field="name"
ng-model="filter.state">
</vn-textfield>
<vn-textfield
vn-one
label="Destination Agrupated State"
<tpl-item>
{{name}}
</tpl-item>
</vn-autocomplete>
<vn-autocomplete vn-one
data="$ctrl.groupedStates"
label="Destination Grouped State"
value-field="code"
show-field="name"
ng-model="filter.tfState">
</vn-textfield>
<tpl-item>
{{name}}
</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-check
@ -72,12 +96,13 @@
ng-model="filter.problems"
triple-state="true">
</vn-check>
<vn-textfield
<vn-autocomplete
vn-one
label="Warehouse"
ng-model="filter.warehouseFk"
url="Warehouses"
required="true">
</vn-textfield>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="vn-px-lg vn-pb-lg vn-mt-lg">
<vn-submit label="Search"></vn-submit>

View File

@ -5,6 +5,36 @@ class Controller extends SearchPanel {
constructor($, $element) {
super($, $element);
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
Origin ETD: ETD origen
Destination ETD: ETD destino
Max Lines Origin: Líneas máx. origen
Max Liters Origin: Litros máx. origen
Max Lines: Líneas máx.
Max Liters: Litros máx.
Origin IPT: IPT origen
Destination IPT: IPT destino
With problems: Con problemas
Warehouse: Almacén
Origin Agrupated State: Estado agrupado origen
Destination Agrupated State: Estado agrupado destino
Origin Grouped State: Estado agrupado origen
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-id="model"
url="Tickets/getTicketsFuture"
filter="::$ctrl.filter"
limit="20">
</vn-crud-model>
<vn-portal slot="topbar">
@ -11,14 +10,14 @@
placeholder="Search tickets"
info="Search future tickets by date"
auto-state="false"
model="model"
filter="::$ctrl.filter">
model="model">
</vn-searchbar>
</vn-portal>
<vn-card>
<smart-table
model="model"
options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)"
>
<slot-actions>
<vn-button disabled="$ctrl.checked.length === 0"
@ -53,10 +52,10 @@
<th field="ipt">
<span>IPT</span>
</th>
<th field="liters">
<th field="litersMax">
<span translate>Liters</span>
</th>
<th field="lines">
<th field="linesMax">
<span translate>Available Lines</span>
</th>
<th field="ticketFuture">

View File

@ -6,23 +6,9 @@ export default class Controller extends Section {
super($element, $);
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 = {
activeButtons: {
search: true
search: true,
},
columns: [{
field: 'problems',
@ -35,7 +21,32 @@ export default class Controller extends Section {
{
field: 'destETD',
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) {
if (state === 'OK')
return 'success';
else if (state === 'FREE')
else if (state === 'Libre')
return 'notice';
}
@ -96,6 +107,23 @@ export default class Controller extends Section {
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'];

View File

@ -1 +1,5 @@
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 confirmation: ¿Desea mover {{checked}} tickets hacia el futuro?
Success: Tickets movidos correctamente
ETD: Tiempo estimado de entrega
IPT: Encajado
FREE: Libre
DELIVERED: Servido
ON_PREPARATION: En preparacion
PACKED: Encajado