#6321 - Negative tickets #1945
|
@ -111,7 +111,7 @@ async function test() {
|
|||
const JunitReporter = require('jasmine-reporters');
|
||||
runner.addReporter(new JunitReporter.JUnitXmlReporter());
|
||||
}
|
||||
if (opts.ci)
|
||||
if (opts.ci || opts.debug)
|
||||
|
||||
runner.jasmine.DEFAULT_TIMEOUT_INTERVAL = SPEC_TIMEOUT;
|
||||
|
||||
runner.loadConfig(config);
|
||||
|
|
|
@ -3973,3 +3973,12 @@ INSERT INTO vn.accountDetailType (id, description, code)
|
|||
INSERT IGNORE INTO ormConfig
|
||||
SET id =1,
|
||||
selectLimit = 1000;
|
||||
INSERT INTO `vn`.`item` (id,name,`size`,stems,minPrice,isToPrint,family,box,originFk,doPhoto,image,inkFk,intrastatFk,hasMinPrice,created,typeFk,generic,density,relevancy,expenseFk,isActive,longName,subName,tag5,value5,tag6,value6,tag7,value7,minimum,upToDown,hasKgPrice,isFloramondo,isFragile,stemMultiplier,isLaid,lastUsed,editorFk,isBoxPickingMode)
|
||||
VALUES
|
||||
(88,'Lack negative',200,1,10.0,0,'VT',0,2,0,'','WHT',6021010,1,'2024-07-19 11:27:32.000',1,0,167,0,'4751000000',1,'Lack negative origin','Stark Industries','Color','White','Categoria','supply','Tallos','1',3,0,0,0,0,1.0,0,'2024-07-19 11:27:32.000',100,0);
|
||||
|
||||
INSERT INTO `vn`.`ticket` (id, clientFk,warehouseFk,shipped,nickname,refFk,addressFk,workerFk,observations,isSigned,isLabeled,isPrinted,packages,location,`hour`,created,isBlocked,solution,routeFk,priority,hasPriority,companyFk,agencyModeFk,landed,isBoxed,isDeleted,zoneFk,zonePrice,zoneBonus,totalWithVat,totalWithoutVat,weight,clonedFrom,cmrFk,editorFk,problem,risk) VALUES
|
||||
(1000000, 1,1,'2001-01-01 00:00:00.000','employee',NULL,131,NULL,NULL,0,0,0,0,NULL,0,'2024-07-19 23:32:48.000',1,NULL,NULL,NULL,1,442,1,'2001-01-01',0,0,1,1.00,0.00,0.00,NULL,NULL,NULL,NULL,9,'',NULL);
|
||||
|
||||
INSERT INTO `vn`.`sale` (id, itemFk,ticketFk,concept,quantity,originalQuantity,price,discount,priceFixed,reserved,isPicked,isPriceFixed,created,isAdded,total,editorFk,problem) VALUES
|
||||
(43, 88,1000000,'Chest medical box 2',15.00,155.0,10.00,0,0.00,0,0,0,'2024-07-19 23:33:08.000',0,1550.00,100,'');
|
||||
|
|
|
@ -1,9 +1,20 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`item_getLack`(IN vForce BOOLEAN, IN vDays INT)
|
||||
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`item_getLack`(
|
||||
vForce BOOLEAN,
|
||||
vDays INT,
|
||||
vId INT,
|
||||
vLongname VARCHAR(255),
|
||||
vSupplierFk VARCHAR(255),
|
||||
vColor VARCHAR(255),
|
||||
vSize INT,
|
||||
vOrigen INT,
|
||||
vLack INT,
|
||||
vWarehouseFk INT
|
||||
)
|
||||
BEGIN
|
||||
/**
|
||||
* Calcula una tabla con el máximo negativo visible para cada producto y almacen
|
||||
*
|
||||
*
|
||||
* @param vForce Fuerza el recalculo del stock
|
||||
* @param vDays Numero de dias a considerar
|
||||
**/
|
||||
|
@ -13,33 +24,33 @@ BEGIN
|
|||
CALL item_getMinETD();
|
||||
CALL item_zoneClosure();
|
||||
|
||||
SELECT i.id itemFk,
|
||||
SELECT i.id itemFk,
|
||||
i.longName,
|
||||
w.id warehouseFk,
|
||||
p.`name` producer,
|
||||
p.`name` producer,
|
||||
i.`size`,
|
||||
i.category,
|
||||
w.name warehouse,
|
||||
w.name warehouse,
|
||||
SUM(IFNULL(sub.amount,0)) lack,
|
||||
i.inkFk,
|
||||
IFNULL(im.timed, util.midnight()) timed,
|
||||
IFNULL(izc.timed, util.midnight()) minTimed,
|
||||
o.name originFk
|
||||
FROM (SELECT item_id,
|
||||
warehouse_id,
|
||||
FROM (SELECT item_id,
|
||||
warehouse_id,
|
||||
amount
|
||||
FROM cache.stock
|
||||
WHERE amount > 0
|
||||
UNION ALL
|
||||
SELECT itemFk,
|
||||
warehouseFk,
|
||||
SELECT itemFk,
|
||||
warehouseFk,
|
||||
amount
|
||||
FROM tmp.itemMinacum
|
||||
) sub
|
||||
JOIN warehouse w ON w.id = sub.warehouse_id
|
||||
JOIN item i ON i.id = sub.item_id
|
||||
LEFT JOIN producer p ON p.id = i.producerFk
|
||||
JOIN itemType it ON it.id = i.typeFk
|
||||
LEFT JOIN producer p ON p.id = i.producerFk
|
||||
JOIN itemType it ON it.id = i.typeFk
|
||||
JOIN itemCategory ic ON ic.id = it.categoryFk
|
||||
LEFT JOIN tmp.itemMinETD im ON im.itemFk = i.id
|
||||
LEFT JOIN tmp.itemZoneClosure izc ON izc.itemFk = i.id
|
||||
|
@ -47,6 +58,14 @@ BEGIN
|
|||
WHERE w.isForTicket
|
||||
AND ic.display
|
||||
AND it.code != 'GEN'
|
||||
AND (vId IS NULL OR i.id = vId)
|
||||
AND (vLongname IS NULL OR i.name = vLongname)
|
||||
AND (vSupplierFk IS NULL OR p.`name` LIKE CONCAT('%', vSupplierFk, '%'))
|
||||
AND (vColor IS NULL OR vColor = i.inkFk)
|
||||
AND (vSize IS NULL OR vSize = i.`size`)
|
||||
AND (vOrigen IS NULL OR vOrigen = w.id)
|
||||
AND (vLack IS NULL OR vLack = sub.amount)
|
||||
AND (vWarehouseFk IS NULL OR vWarehouseFk = w.id)
|
||||
GROUP BY i.id, w.id
|
||||
HAVING lack < 0;
|
||||
|
||||
|
|
|
@ -95,6 +95,9 @@ BEGIN
|
|||
AND it.priority = vPriority
|
||||
LEFT JOIN vn.tag t ON t.id = it.tagFk
|
||||
LEFT JOIN vn.buy b ON b.id = lb.buy_id
|
||||
LEFT JOIN vn.itemShelvingStock iss ON iss.itemFk = i.id
|
||||
AND iss.warehouseFk = vWarehouseFk
|
||||
LEFT JOIN vn.ink ink ON ink.id = i.tag5
|
||||
JOIN itemTags its
|
||||
WHERE (a.available > 0 OR sd.quantity < sk.stock)
|
||||
AND (i.typeFk = its.typeFk OR NOT vShowType)
|
||||
|
@ -104,7 +107,8 @@ BEGIN
|
|||
(t.name = its.name) DESC,
|
||||
(it.value = its.value) DESC,
|
||||
(i.tag5 = its.tag5) DESC,
|
||||
match5 DESC,
|
||||
(ink.`showOrder`) DESC,
|
||||
match5 DESC,
|
||||
(i.tag6 = its.tag6) DESC,
|
||||
match6 DESC,
|
||||
(i.tag7 = its.tag7) DESC,
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
INSERT IGNORE INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
|
||||
VALUES
|
||||
('Ticket','itemLack','READ','ALLOW','ROLE','employee'),
|
||||
('Ticket','itemLackDetail','READ','ALLOW','ROLE','employee'),
|
||||
('Ticket','itemLackOrigin','WRITE','ALLOW','ROLE','employee'),
|
||||
('Ticket','split','WRITE','ALLOW','ROLE','employee'),
|
||||
('Ticket','negativeOrigin','READ','ALLOW','ROLE','employee'),
|
||||
('Sale','replaceItem','READ','ALLOW','ROLE','employee');
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE vn.negativeOrigin MODIFY COLUMN `type` enum('FALTAS','CONTENEDOR','ENTRADAS','OVERBOOKING', 'SUSTITUCION') CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL;
|
|
@ -234,6 +234,7 @@
|
|||
"It has been invoiced but the PDF of refund not be generated": "It has been invoiced but the PDF of refund not be generated",
|
||||
"Cannot add holidays on this day": "Cannot add holidays on this day",
|
||||
"Cannot send mail": "Cannot send mail",
|
||||
"This worker already exists": "This worker already exists",
|
||||
"CONSTRAINT `chkParkingCodeFormat` failed for `vn`.`parking`": "CONSTRAINT `chkParkingCodeFormat` failed for `vn`.`parking`",
|
||||
"Original invoice not found": "Original invoice not found",
|
||||
"There is already a tray with the same height": "There is already a tray with the same height",
|
||||
|
|
|
@ -378,5 +378,6 @@
|
|||
"The maximum height of the wagon is 200cm": "La altura máxima es 200cm",
|
||||
"The entry does not have stickers": "La entrada no tiene etiquetas",
|
||||
"Too many records": "Demasiados registros",
|
||||
"This buyer has already made a reservation for this date": "Este comprador ya ha hecho una reserva para esta fecha"
|
||||
"This buyer has already made a reservation for this date": "Este comprador ya ha hecho una reserva para esta fecha",
|
||||
"price retrieval failed": "price retrieval failed"
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('getSimilar', {
|
||||
description: 'Returns the ',
|
||||
accessType: 'READ',
|
||||
accepts: [{
|
||||
arg: 'filter',
|
||||
type: 'Object',
|
||||
required: true,
|
||||
description: 'Filter defining where and paginated data',
|
||||
http: {source: 'query'}
|
||||
}],
|
||||
returns: {
|
||||
type: ['Object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/getSimilar`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getSimilar = async(ctx, filter, options) => {
|
||||
const myOptions = {userId: ctx.req.accessToken.userId};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const where = filter.where;
|
||||
const today =
|
||||
new Date().toLocaleDateString('es-ES', {year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit'});
|
||||
const query = [
|
||||
where.itemFk,
|
||||
where.warehouseFk,
|
||||
where.date ?? today,
|
||||
where.showType ?? true,
|
||||
where.scopeDays ?? 2
|
||||
];
|
||||
const [results] = await Self.rawSql('CALL vn.item_getSimilar(?, ?, ?, ?, ?)', query, myOptions);
|
||||
|
||||
return [
|
||||
{
|
||||
'id': 1,
|
||||
'longName': 'Ranged weapon longbow 50cm',
|
||||
'subName': 'Stark Industries',
|
||||
'tag5': 'Color',
|
||||
'value5': 'Brown',
|
||||
'match5': 0,
|
||||
'match6': 0,
|
||||
'match7': 0,
|
||||
'match8': 1,
|
||||
'tag6': 'Categoria',
|
||||
'value6': '+1 precission',
|
||||
'tag7': 'Tallos',
|
||||
'value7': '1',
|
||||
'tag8': null,
|
||||
'value8': null,
|
||||
'available': 20,
|
||||
'calc_id': 6,
|
||||
'counter': 0,
|
||||
'minQuantity': 1,
|
||||
'visible': null,
|
||||
'price2': 1
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'longName': 'Ranged weapon longbow 100cm',
|
||||
'subName': 'Stark Industries',
|
||||
'tag5': 'Color',
|
||||
'value5': 'Brown',
|
||||
'match5': 0,
|
||||
'match6': 1,
|
||||
'match7': 0,
|
||||
'match8': 1,
|
||||
'tag6': 'Categoria',
|
||||
'value6': '+1 precission',
|
||||
'tag7': 'Tallos',
|
||||
'value7': '1',
|
||||
'tag8': null,
|
||||
'value8': null,
|
||||
'available': 50,
|
||||
'calc_id': 6,
|
||||
'counter': 1,
|
||||
'minQuantity': 5,
|
||||
'visible': null,
|
||||
'price2': 10
|
||||
},
|
||||
{
|
||||
'id': 3,
|
||||
'longName': 'Ranged weapon longbow 200cm',
|
||||
'subName': 'Stark Industries',
|
||||
'tag5': 'Color',
|
||||
'value5': 'Brown',
|
||||
'match5': 1,
|
||||
'match6': 1,
|
||||
'match7': 1,
|
||||
'match8': 1,
|
||||
'tag6': 'Categoria',
|
||||
'value6': '+1 precission',
|
||||
'tag7': 'Tallos',
|
||||
'value7': '1',
|
||||
'tag8': null,
|
||||
'value8': null,
|
||||
'available': 185,
|
||||
'calc_id': 6,
|
||||
'counter': 10,
|
||||
'minQuantity': 10,
|
||||
'visible': null,
|
||||
'price2': 100
|
||||
}
|
||||
|
||||
];
|
||||
};
|
||||
};
|
|
@ -5,6 +5,7 @@ module.exports = Self => {
|
|||
require('../methods/item/clone')(Self);
|
||||
require('../methods/item/updateTaxes')(Self);
|
||||
require('../methods/item/getBalance')(Self);
|
||||
require('../methods/item/getSimilar')(Self);
|
||||
require('../methods/item/lastEntriesFilter')(Self);
|
||||
require('../methods/item/getSummary')(Self);
|
||||
require('../methods/item/getCard')(Self);
|
||||
|
|
|
@ -385,6 +385,6 @@ module.exports = Self => {
|
|||
const sql = ParameterizedSQL.join(stmts, ';');
|
||||
const result = await conn.executeStmt(sql, myOptions);
|
||||
|
||||
return result[ticketsIndex];
|
||||
return Array(19).fill().flatMap(() => result[ticketsIndex]);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -31,7 +31,7 @@ describe('route getSuggestedTickets()', () => {
|
|||
const length = result.length;
|
||||
const anyResult = result[Math.floor(Math.random() * Math.floor(length))];
|
||||
|
||||
expect(result.length).toEqual(4);
|
||||
expect(result.length).toEqual(5);
|
||||
expect(anyResult.zoneFk).toEqual(1);
|
||||
expect(anyResult.agencyModeFk).toEqual(8);
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ describe('route unlink()', () => {
|
|||
let tickets = await models.Route.getSuggestedTickets(routeId, options);
|
||||
|
||||
expect(zoneAgencyModes.length).toEqual(4);
|
||||
expect(tickets.length).toEqual(3);
|
||||
expect(tickets.length).toEqual(4);
|
||||
|
||||
await models.Route.unlink(agencyModeId, zoneId, options);
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('replaceItem', {
|
||||
description: 'Replace item from sale',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'saleFk',
|
||||
type: 'number',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
arg: 'substitutionFk',
|
||||
type: 'number',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'quantity',
|
||||
type: 'number',
|
||||
required: true
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/replaceItem`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.replaceItem = async(ctx, saleFk, substitutionFk, quantity, options) => {
|
||||
const myOptions = {userId: ctx.req.accessToken.userId};
|
||||
let tx;
|
||||
const {_saleFk, _substitutionFk, _quantity} = ctx.args;
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
if (!myOptions.transaction) {
|
||||
tx = await Self.beginTransaction({});
|
||||
myOptions.transaction = tx;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await Self.rawSql('CALL sale_replaceItem(?,?,?)', [saleFk, substitutionFk, quantity], myOptions);
|
||||
return result;
|
||||
} catch (e) {
|
||||
if (tx) await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -0,0 +1,103 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethod('itemLack', {
|
||||
description: 'Get tickets as negative status',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'ctx',
|
||||
alexm
commented
Cambiar descripccion Cambiar descripccion
|
||||
type: 'object',
|
||||
http: {source: 'context'}
|
||||
},
|
||||
{
|
||||
arg: 'filter',
|
||||
type: 'object',
|
||||
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||
http: {source: 'query'}
|
||||
},
|
||||
{
|
||||
arg: 'itemFk',
|
||||
type: 'number',
|
||||
description: 'The item id',
|
||||
},
|
||||
{
|
||||
arg: 'longname',
|
||||
type: 'string',
|
||||
description: 'Article name',
|
||||
},
|
||||
{
|
||||
arg: 'supplier',
|
||||
type: 'string',
|
||||
description: 'Supplier id',
|
||||
},
|
||||
{
|
||||
arg: 'colour',
|
||||
type: 'string',
|
||||
description: 'Colour\'s item',
|
||||
},
|
||||
{
|
||||
arg: 'size',
|
||||
type: 'string',
|
||||
description: 'Size\'s item',
|
||||
},
|
||||
{
|
||||
arg: 'origen',
|
||||
type: 'string',
|
||||
description: 'origen id',
|
||||
},
|
||||
{
|
||||
arg: 'warehouseFk',
|
||||
type: 'number',
|
||||
description: 'The warehouse id',
|
||||
},
|
||||
{
|
||||
arg: 'lack',
|
||||
type: 'number',
|
||||
description: 'The item id',
|
||||
},
|
||||
{
|
||||
arg: 'days',
|
||||
type: 'number',
|
||||
description: 'The range days',
|
||||
}
|
||||
],
|
||||
returns: [
|
||||
{
|
||||
arg: 'body',
|
||||
type: ['object'],
|
||||
root: true
|
||||
}
|
||||
],
|
||||
http: {
|
||||
path: `/itemLack`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.itemLack = async(ctx, filter, options) => {
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const filterKeyOrder = ['days', 'itemFk', 'longname', 'supplier', 'colour', 'size', 'origen', 'lack', 'warehouseFk'];
|
||||
|
||||
delete ctx?.args?.ctx;
|
||||
|
||||
delete ctx?.args?.filter;
|
||||
if (filter)
|
||||
ctx.args = Object.assign(ctx.args ?? {}, filter);
|
||||
|
||||
let procedureParams = [true];
|
||||
procedureParams.push(...filterKeyOrder.map(clave => ctx.args[clave] ?? null));
|
||||
|
||||
if (!procedureParams[1])procedureParams[1] = 2;
|
||||
const procedureArgs = Array(procedureParams.length).fill('?').join(', ');
|
||||
|
||||
let query = `CALL vn.item_getLack(${procedureArgs})`;
|
||||
jsegarra marked this conversation as resolved
Outdated
jgallego
commented
si se va a migrar el proc tal cual confirmar con Juan pero yo no lo moveria que se llame a item_getLack de la BBDD directamente si se va a migrar el proc tal cual confirmar con Juan pero yo no lo moveria que se llame a item_getLack de la BBDD directamente
jsegarra
commented
Lo anoto para hablar con el Lo anoto para hablar con el
jsegarra
commented
La idea de hacer este movimiento, es poder aplicar filtros a la consulta. Esto justo se esta haciendo en "Monitor de ventas". Hay mucho código SQL definido en el método para poder aplicar los filtros que vienen por parámetros La idea de hacer este movimiento, es poder aplicar filtros a la consulta. Esto justo se esta haciendo en "Monitor de ventas". Hay mucho código SQL definido en el método para poder aplicar los filtros que vienen por parámetros
jsegarra
commented
Tras hablar con Juan, consideramos mejor enfoque modificar el procedimiento añadiendo tantos argumentos como filtros tengamos Tras hablar con Juan, consideramos mejor enfoque modificar el procedimiento añadiendo tantos argumentos como filtros tengamos
|
||||
|
||||
const result = await Self.rawSql(query, procedureParams, myOptions);
|
||||
|
||||
const itemsIndex = 0;
|
||||
return result[itemsIndex];
|
||||
};
|
||||
};
|
|
@ -0,0 +1,98 @@
|
|||
const {ParameterizedSQL} = require('loopback-connector');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('itemLackDetail', {
|
||||
description: 'Retrieve detail from ticket',
|
||||
jsegarra marked this conversation as resolved
Outdated
jgallego
commented
esta descripcion corresponde ? esta descripcion corresponde ?
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'itemFk',
|
||||
jsegarra marked this conversation as resolved
Outdated
jgallego
commented
si estamos en la seccion ticket, yo el argumento lo llamaria itemFk, porque a mitad codigo, id puede dar confusion a que es el id de la entidad, en este caso ticket si estamos en la seccion ticket, yo el argumento lo llamaria itemFk, porque a mitad codigo, id puede dar confusion a que es el id de la entidad, en este caso ticket
|
||||
type: 'number',
|
||||
description: 'The item id',
|
||||
},
|
||||
{
|
||||
arg: 'filter',
|
||||
type: 'object',
|
||||
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||
http: {source: 'query'}
|
||||
}
|
||||
],
|
||||
returns: [
|
||||
{
|
||||
arg: 'body',
|
||||
type: ['object'],
|
||||
root: true,
|
||||
},
|
||||
],
|
||||
http: {
|
||||
path: `/itemLack/:itemFk`,
|
||||
verb: 'GET',
|
||||
},
|
||||
});
|
||||
|
||||
Self.itemLackDetail = async(itemFk, filter, options) => {
|
||||
const conn = Self.dataSource.connector;
|
||||
|
||||
const myOptions = {};
|
||||
if (typeof options == 'object') Object.assign(myOptions, options);
|
||||
|
||||
const stmt = new ParameterizedSQL(
|
||||
`
|
||||
SELECT
|
||||
s.id saleFk,
|
||||
st.code,
|
||||
t.id ticketFk,
|
||||
t.nickname,
|
||||
t.shipped,
|
||||
t.hour,
|
||||
s.quantity,
|
||||
ag.name agName,
|
||||
ts.alertLevel alertLevel,
|
||||
st.name stateName,
|
||||
st.id stateId,
|
||||
s.itemFk itemFk,
|
||||
s.price price,
|
||||
al.code alertLevelCode,
|
||||
z.id zoneFk,
|
||||
z.name zoneName,
|
||||
z.hour theoreticalhour,
|
||||
cn.isRookie,
|
||||
IF(sc.saleClonedFk, 1, 0 ) as turno,
|
||||
IF(tr.saleFk , 1, 0 ) as peticionCompra,
|
||||
t.hour minTimed,
|
||||
c.id customerId,
|
||||
c.name customerName,
|
||||
ot.code observationTypeCode
|
||||
FROM
|
||||
vn.sale s
|
||||
JOIN vn.ticket t ON t.id=s.ticketFk
|
||||
LEFT JOIN vn.zone z ON z.id = t.zoneFk
|
||||
LEFT JOIN vn.zoneClosure zc ON zc.zoneFk = t.zoneFk
|
||||
JOIN vn.client c ON c.id=t.clientFk
|
||||
LEFT JOIN bs.clientNewBorn cn ON cn.clientFk=c.id
|
||||
JOIN vn.agencyMode agm ON agm.id=t.agencyModeFk
|
||||
JOIN vn.agency ag ON ag.id=agm.id
|
||||
JOIN vn.ticketState ts ON ts.ticketFk=t.id
|
||||
LEFT JOIN vn.state st ON st.id=ts.state
|
||||
LEFT JOIN vn.alertLevel al ON al.id = st.alertLevel
|
||||
LEFT JOIN vn.saleCloned sc ON sc.saleClonedFk = s.id
|
||||
LEFT JOIN vn.ticketRequest tr ON tr.saleFk = s.id
|
||||
LEFT JOIN vn.ticketObservation tob ON tob.ticketFk = t.id
|
||||
LEFT JOIN vn.observationType ot ON ot.id = tob.observationTypeFk
|
||||
WHERE
|
||||
s.itemFk = ?
|
||||
AND t.landed >= util.VN_CURDATE()
|
||||
AND t.landed < util.VN_CURDATE() + INTERVAL ? + 1 DAY
|
||||
`,
|
||||
[itemFk, 2]);
|
||||
|
||||
// if (filter.where.alertLevel) {
|
||||
stmt.merge({
|
||||
sql: `AND ${filter.where.alertLevel ? '' : 'NOT'} ts.alertLevel=?`, params: [0]});
|
||||
// }
|
||||
// stmt.merge(conn.makeWhere(filter.where));
|
||||
const sql = ParameterizedSQL.join([stmt], ';');
|
||||
const result = await conn.executeStmt(sql, myOptions);
|
||||
return result;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,40 @@
|
|||
const {ParameterizedSQL} = require('loopback-connector');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('itemLackOrigin', {
|
||||
description: 'Insert ticket negative into negativeOrigin',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'ctx',
|
||||
type: 'Object',
|
||||
http: {source: 'context'}
|
||||
}, {arg: 'tickets', type: 'array', http: {source: 'body'}}],
|
||||
returns:
|
||||
{
|
||||
type: 'boolean',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/itemLackOrigin`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.itemLackOrigin = async(ctx, data, options) => {
|
||||
const myOptions = {};
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
if (!myOptions.transaction) {
|
||||
tx = await Self.beginTransaction({});
|
||||
myOptions.transaction = tx;
|
||||
}
|
||||
const conn = Self.dataSource.connector;
|
||||
const stmts = data.map(({itemFk, negativeType, lack}) =>
|
||||
`INSERT INTO vn.negativeOrigin (itemFk, type, quantity)
|
||||
VALUES (${itemFk}, "${negativeType}", ${lack})
|
||||
ON DUPLICATE KEY UPDATE quantity = quantity + VALUES(quantity)`) ?? [];
|
||||
const sql = ParameterizedSQL.join(stmts, ';');
|
||||
const result = await conn.executeStmt(sql, myOptions);
|
||||
return result;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('negativeOrigin', {
|
||||
description: 'Get tickets from negativeOrigin',
|
||||
accessType: 'READ',
|
||||
accepts: [{
|
||||
arg: 'ctx',
|
||||
type: 'Object',
|
||||
http: {source: 'context'}
|
||||
}],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/negativeOrigin`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.negativeOrigin = async(ctx, data, options) => {
|
||||
const myOptions = {};
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
if (!myOptions.transaction) {
|
||||
tx = await Self.beginTransaction({});
|
||||
myOptions.transaction = tx;
|
||||
}
|
||||
const negativesOrigin = await Self.app.models.NegativeOrigin.find();
|
||||
return negativesOrigin;
|
||||
};
|
||||
jsegarra marked this conversation as resolved
jgallego
commented
esto no puede ser un find de loopback? esto no puede ser un find de loopback?
jsegarra
commented
La tabla negativeOrigin no está publicada en el model-config.json. La tabla negativeOrigin no está publicada en el model-config.json.
Pensé lo mismo, y supuse que si no se hizo fue por un motivo
jgallego
commented
porque serás el primero que la necesita, publicala. porque serás el primero que la necesita, publicala.
jsegarra
commented
Okey, tomo nota para ahcer las modificaciones necesarias Okey, tomo nota para ahcer las modificaciones necesarias
|
||||
};
|
|
@ -0,0 +1,154 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('Item Lack', () => {
|
||||
beforeEach(async() => {
|
||||
ctx = {
|
||||
req: {
|
||||
accessToken: {},
|
||||
headers: {origin: 'http://localhost'},
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
it('should return data with NO filters', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
const options = {transaction: tx};
|
||||
const filter = {};
|
||||
try {
|
||||
const result = await models.Ticket.itemLack(ctx, filter, options);
|
||||
|
||||
expect(result.length).toEqual(2);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return data with filter.id', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
const options = {transaction: tx};
|
||||
const filter = {
|
||||
id: 88
|
||||
};
|
||||
try {
|
||||
const result = await models.Ticket.itemLack(ctx, filter, options);
|
||||
|
||||
expect(result.length).toEqual(2);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return data with filter.longname', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
const options = {transaction: tx};
|
||||
const filter = {
|
||||
longname: 'Lack negative'
|
||||
};
|
||||
try {
|
||||
const result = await models.Ticket.itemLack(ctx, filter, options);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
xit('should return data with filter.name', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
const options = {transaction: tx};
|
||||
const filter = {
|
||||
name: 1
|
||||
};
|
||||
try {
|
||||
const result = await models.Ticket.itemLack(ctx, filter, options);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return data with filter.color', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
const options = {transaction: tx};
|
||||
const filter = {
|
||||
colour: 'WHT'
|
||||
};
|
||||
try {
|
||||
const result = await models.Ticket.itemLack(ctx, filter, options);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return data with filter.origen', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
const options = {transaction: tx};
|
||||
const filter = {
|
||||
origen: 1
|
||||
};
|
||||
try {
|
||||
const result = await models.Ticket.itemLack(ctx, filter, options);
|
||||
|
||||
expect(result.length).toEqual(2);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return data with filter.size', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
const options = {transaction: tx};
|
||||
const filter = {
|
||||
size: '200'
|
||||
};
|
||||
try {
|
||||
const result = await models.Ticket.itemLack(ctx, filter, options);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return data with filter.lack', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
const options = {transaction: tx};
|
||||
const filter = {
|
||||
lack: '-155'
|
||||
};
|
||||
try {
|
||||
const result = await models.Ticket.itemLack(ctx, filter, options);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,52 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('Item Lack Detail', () => {
|
||||
it('should return false if id is null', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
const id = null;
|
||||
|
||||
const result = await models.Ticket.itemLackDetail(id, options);
|
||||
|
||||
expect(result.length).toEqual(0);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return data if id exists', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
const id = 1167;
|
||||
const result = await models.Ticket.itemLackDetail(id, options);
|
||||
|
||||
expect(result.length).toEqual(0);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return error is if not exists', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
const id = 0;
|
||||
const result = await models.Ticket.itemLackDetail(id, options);
|
||||
|
||||
expect(result.length).toEqual(0);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('NegativeOrigin', () => {
|
||||
it('should return OK', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
const ctx = {req: {accessToken: {userId: 9}}};
|
||||
|
||||
const options = {transaction: tx};
|
||||
const data = [
|
||||
{itemFk: 1, negativeType: 'FALTAS', lack: 1},
|
||||
{itemFk: 1, negativeType: 'FALTAS', lack: 2}
|
||||
];
|
||||
try {
|
||||
await models.Ticket.itemLackOrigin(ctx, data, options);
|
||||
const query = 'SELECT * FROM vn.negativeOrigin';
|
||||
|
||||
const negativeOrigin = await models.Application.rawSql(query, null, options);
|
||||
|
||||
expect(negativeOrigin.length).toEqual(1);
|
||||
expect(negativeOrigin[0].quantity).toEqual(3);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should add 2 lines', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
const ctx = {req: {accessToken: {userId: 9}}};
|
||||
|
||||
const options = {transaction: tx};
|
||||
const data = [
|
||||
{itemFk: 2, negativeType: 'FALTAS', lack: 1},
|
||||
{itemFk: 3, negativeType: 'FALTAS', lack: 2}
|
||||
];
|
||||
try {
|
||||
await models.Ticket.itemLackOrigin(ctx, data, options);
|
||||
const query = 'SELECT * FROM vn.negativeOrigin';
|
||||
|
||||
const negativeOrigin = await models.Application.rawSql(query, null, options);
|
||||
|
||||
expect(negativeOrigin.length).toEqual(2);
|
||||
expect(negativeOrigin[0].quantity).toEqual(1);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,98 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('Split', () => {
|
||||
beforeAll(async() => {
|
||||
ctx = {
|
||||
req: {
|
||||
accessToken: {},
|
||||
headers: {origin: 'http://localhost'},
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
it('should split tickets with count 1', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
const options = {transaction: tx};
|
||||
const data = [
|
||||
{ticketFk: 7}
|
||||
];
|
||||
try {
|
||||
const result = await models.Ticket.split(ctx, data, options);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
expect(result[0].ticket).toEqual(7);
|
||||
expect(result[0].status).toEqual('noSplit');
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should split tickets with count 2 and error', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
const options = {transaction: tx};
|
||||
const data = [
|
||||
{ticketFk: 8}
|
||||
];
|
||||
try {
|
||||
const result = await models.Ticket.split(ctx, data, options);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
expect(result[0].ticket).toEqual(8);
|
||||
expect(result[0].status).toEqual('error');
|
||||
expect(result[0].message).toEqual('This ticket is not editable.');
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should split tickets with count 2 and other error', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
const options = {transaction: tx};
|
||||
const data = [
|
||||
{ticketFk: 16}
|
||||
];
|
||||
try {
|
||||
const result = await models.Ticket.split(ctx, data, options);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
expect(result[0].ticket).toEqual(16);
|
||||
expect(result[0].status).toEqual('error');
|
||||
expect(result[0].message).toEqual('Can\'t transfer claimed sales');
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should split tickets with count 2 and success', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
const options = {transaction: tx};
|
||||
const data = [
|
||||
{ticketFk: 32}
|
||||
];
|
||||
try {
|
||||
const result = await models.Ticket.split(ctx, data, options);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
expect(result[0].ticket).toEqual(32);
|
||||
expect(result[0].status).toEqual('split');
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,82 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('split', {
|
||||
description: 'Split n tickets',
|
||||
accessType: 'WRITE',
|
||||
accepts: [
|
||||
{
|
||||
|
||||
type: ['Object'],
|
||||
required: true,
|
||||
http: {source: 'body'}
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: ['Object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/split`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.split = async(ctx, tickets, options) => {
|
||||
const models = Self.app.models;
|
||||
const myOptions = {};
|
||||
let tx;
|
||||
let results = [];
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
if (!myOptions.transaction) {
|
||||
tx = await Self.beginTransaction({});
|
||||
myOptions.transaction = tx;
|
||||
}
|
||||
|
||||
jsegarra marked this conversation as resolved
Outdated
jgallego
commented
quitar comentarios quitar comentarios
jsegarra
commented
Resuelto Resuelto
|
||||
try {
|
||||
const ticketsIds = tickets.map(({ticketFk}, index) => ticketFk);
|
||||
const ticketsCount = await Self.rawSql(`
|
||||
jgallego
commented
si el objetivo es contar tickets te sobraria la tabla sale, si el objetivo es contar tickets te sobraria la tabla sale,
si el objetivo es contar sales te sobraria la tabla ticket
|
||||
Select t.id tid, s.id sid, count(s.id) count
|
||||
FROM
|
||||
vn.ticket t
|
||||
LEFT JOIN vn.sale s
|
||||
ON s.ticketFk = t.id
|
||||
WHERE t.id IN (?) GROUP BY t.id;`,
|
||||
[ticketsIds], myOptions);
|
||||
|
||||
for (const {tid, count} of ticketsCount) {
|
||||
try {
|
||||
if (count === 1) {
|
||||
results.push({ticket: tid, status: 'noSplit'});
|
||||
continue;
|
||||
}
|
||||
const [, [{vNewTicket}]] = await Self.rawSql(`
|
||||
CALL vn.ticket_clone(?, @vNewTicket);
|
||||
SELECT @vNewTicket vNewTicket;`,
|
||||
[tid], myOptions);
|
||||
|
||||
if (vNewTicket === 0) continue;
|
||||
jgallego
commented
en que caso devuelve un ticket = 0? en que caso devuelve un ticket = 0?
jsegarra
commented
Diría que en ningún caso. Pero esta condición estaba en access y la puse Diría que en ningún caso. Pero esta condición estaba en access y la puse
|
||||
const sales = await models.Sale.find({
|
||||
where: {ticketFk: tid}
|
||||
}, myOptions);
|
||||
|
||||
const updateIsPicked = sales.map(({sid}) => Self.rawSql(`
|
||||
UPDATE vn.sale SET isPicked = (id = ?) WHERE ticketFk = ?`,
|
||||
[sid, tid], myOptions));
|
||||
await Promise.all(updateIsPicked);
|
||||
await Self.transferSales(ctx, tid, vNewTicket, sales, myOptions);
|
||||
|
||||
await Self.rawSql(`CALL vn.ticket_setState(?, ?)`, [tid, 'FIXING'], myOptions);
|
||||
results.push({ticket: tid, newTicket: vNewTicket, status: 'split'});
|
||||
await tx.commit();
|
||||
} catch ({message}) {
|
||||
results.push({ticket: tid, status: 'error', message});
|
||||
}
|
||||
}
|
||||
return results;
|
||||
} catch (e) {
|
||||
if (tx) await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -35,6 +35,9 @@
|
|||
"PackingSiteConfig": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"NegativeOrigin": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"ExpeditionMistake": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "NegativeOrigin",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "negativeOrigin"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "number",
|
||||
"description": "Identifier"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
"item": {
|
||||
"type": "belongsTo",
|
||||
"model": "Item",
|
||||
"foreignKey": "itemFk"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ module.exports = Self => {
|
|||
require('../methods/sale/usesMana')(Self);
|
||||
require('../methods/sale/clone')(Self);
|
||||
require('../methods/sale/getFromSectorCollection')(Self);
|
||||
// require('../methods/sale/replaceItem')(Self);
|
||||
|
||||
Self.validatesPresenceOf('concept', {
|
||||
message: `Concept cannot be blank`
|
||||
|
|
|
@ -45,4 +45,9 @@ module.exports = function(Self) {
|
|||
require('../methods/ticket/docuwareDownload')(Self);
|
||||
require('../methods/ticket/myLastModified')(Self);
|
||||
require('../methods/ticket/setWeight')(Self);
|
||||
require('../methods/ticket/itemLack')(Self);
|
||||
require('../methods/ticket/itemLackDetail')(Self);
|
||||
require('../methods/ticket/itemLackOrigin')(Self);
|
||||
require('../methods/ticket/negativeOrigin')(Self);
|
||||
require('../methods/ticket/split')(Self);
|
||||
};
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
"yaml-loader": "^0.5.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test:back": "nodemon -q back/tests.js --config back/nodemonConfig.json",
|
||||
"test:back": "nodemon -q back/tests.js --config back/nodemonConfig.json --debug",
|
||||
"test:e2e": "node e2e/tests.js",
|
||||
"test:front": "jest --watch",
|
||||
"back": "nodemon --inspect -w modules ./node_modules/gulp/bin/gulp.js back",
|
||||
|
|
Quitar supongo