#6276 createNewWarehouse methods migrated from silex to salix #1850

Merged
jorgep merged 158 commits from 6276-createNewWarehouse into dev 2024-03-06 11:32:11 +00:00
59 changed files with 2479 additions and 48 deletions

View File

@ -0,0 +1,31 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('assign', {
description: 'Assign a collection',
accessType: 'WRITE',
http: {
path: `/assign`,
verb: 'POST'
},
returns: {
type: ['object'],
root: true
},
});
Self.assign = async(ctx, options) => {
const userId = ctx.req.accessToken.userId;
const myOptions = {userId};
if (typeof options == 'object')
Object.assign(myOptions, options);
const [,, {collectionFk}] = await Self.rawSql('CALL vn.collection_assign(?, @vCollectionFk); SELECT @vCollectionFk collectionFk',
jorgep marked this conversation as resolved Outdated

si no usas info y okPacket porque los recuperas?
No se pueden hacer dos llamadas?

si no usas info y okPacket porque los recuperas? No se pueden hacer dos llamadas?

@jgallego No funciona bien, no está abriendo y cerrando la conexión a bd si lo hago en 2 llamadas. Puedo probar a usar un new ParametizedSql como en cloneWithEntries. O hacer como en transferSales y acceder directamente a la posición del array.

@jgallego No funciona bien, no está abriendo y cerrando la conexión a bd si lo hago en 2 llamadas. Puedo probar a usar un new ParametizedSql como en cloneWithEntries. O hacer como en transferSales y acceder directamente a la posición del array.

const [, , {collectionFk}]

const [, , {collectionFk}]
[userId], myOptions);
if (!collectionFk) throw new UserError('There are not picking tickets');
await Self.rawSql('CALL vn.collection_printSticker(?, NULL)', [collectionFk], myOptions);
return collectionFk;
};
};

View File

@ -0,0 +1,157 @@
module.exports = Self => {
Self.remoteMethodCtx('getSales', {
description: 'Get sales from ticket or collection',
accessType: 'READ',
accepts: [
{
arg: 'collectionOrTicketFk',
type: 'number',
required: true
}, {
arg: 'print',
type: 'boolean',
required: true
}, {
arg: 'source',
type: 'string',
required: true
},
],
returns: {
type: 'Object',
root: true
},
http: {
path: `/getSales`,
verb: 'GET'
},
});
Self.getSales = async(ctx, collectionOrTicketFk, print, source, options) => {
const userId = ctx.req.accessToken.userId;
const myOptions = {userId};
const $t = ctx.req.__;
if (typeof options == 'object')
Object.assign(myOptions, options);
const [{id}] = await Self.rawSql('SELECT vn.ticket_get(?) as id',
[collectionOrTicketFk],
myOptions);
const [tickets] = await Self.rawSql('CALL vn.collection_getTickets(?)', [id], myOptions);
if (source) {
await Self.rawSql(
'CALL vn.ticketStateToday_setState(?,?)', [id, source], myOptions
);
}
jorgep marked this conversation as resolved Outdated

picker i checker no son el mateix..

picker i checker no son el mateix..
const [sales] = await Self.rawSql('CALL vn.sale_getFromTicketOrCollection(?)',
[id], myOptions);
const isPicker = source != 'CHECKER';
const [placements] = await Self.rawSql('CALL vn.collectionPlacement_get(?, ?)',
[id, isPicker], myOptions
);
if (print) await Self.rawSql('CALL vn.collection_printSticker(?,NULL)', [id], myOptions);
for (let ticket of tickets) {
let observations = ticket.observaciones.split(' ');
for (let observation of observations) {
const salesPerson = ticket.salesPersonFk;
if (observation.startsWith('#') || observation.startsWith('@')) {
jorgep marked this conversation as resolved Outdated

esto para que sirve? si todos los que envian tienen que hacerlo..conviene moverlo dentro del propio envio no?

esto para que sirve? si todos los que envian tienen que hacerlo..conviene moverlo dentro del propio envio no?

Pregunto a @alexm

Pregunto a @alexm
Outdated
Review

Si la función tuviese un test si que vería más correcto separarlo.
Si solo se usa una vez y no tiene test. Lo dejaria dentro tambien (como decia @jgallego)

Si la función tuviese un test si que vería más correcto separarlo. Si solo se usa una vez y no tiene test. Lo dejaria dentro tambien (como decia @jgallego)
await models.Chat.send(ctx,
observation,
$t('ticketCommercial', {ticket: ticket.ticketFk, salesPerson})
);
}
}
}
return getCollection(id, tickets, sales, placements, myOptions);
};
async function getCollection(id, tickets, sales, placements, options) {
const collection = {
collectionFk: id,
tickets: [],
};
for (let ticket of tickets) {
const {ticketFk} = ticket;
ticket.sales = [];
const barcodes = await getBarcodes(ticketFk, options);
await Self.rawSql(
'CALL util.log_add(?, ?, ?, ?, ?, ?, ?, ?)',
['vn', 'ticket', 'Ticket', ticketFk, ticketFk, 'select', null, null],
options
);
for (let sale of sales) {
if (sale.ticketFk == ticketFk) {
sale.placements = [];
for (const salePlacement of placements) {
if (salePlacement.saleFk == sale.saleFk && salePlacement.order) {
const placement = {
saleFk: salePlacement.saleFk,
itemFk: salePlacement.itemFk,
placement: salePlacement.placement,
shelving: salePlacement.shelving,
created: salePlacement.created,
visible: salePlacement.visible,
order: salePlacement.order,
grouping: salePlacement.grouping,
priority: salePlacement.priority,
saleOrder: salePlacement.saleOrder,
isPreviousPrepared: salePlacement.isPreviousPrepared,
itemShelvingSaleFk: salePlacement.itemShelvingSaleFk,
ticketFk: salePlacement.ticketFk,
id: salePlacement.id
};
sale.placements.push(placement);
}
}
sale.barcodes = [];
for (const barcode of barcodes) {
if (barcode.movementId == sale.saleFk) {
if (barcode.code) {
sale.barcodes.push(barcode.code);
sale.barcodes.push(`0 ${barcode.code}`);
}
if (barcode.id) {
sale.barcodes.push(barcode.id);
sale.barcodes.push(`0 ${barcode.id}`);
}
}
}
ticket.sales.push(sale);
}
}
collection.tickets.push(ticket);
}
return collection;
}
async function getBarcodes(ticketId, options) {
const query =
`SELECT s.id movementId,
b.code,
c.id
FROM vn.sale s
LEFT JOIN vn.itemBarcode b ON b.itemFk = s.itemFk
LEFT JOIN vn.buy c ON c.itemFk = s.itemFk
LEFT JOIN vn.entry e ON e.id = c.entryFk
LEFT JOIN vn.travel tr ON tr.id = e.travelFk
WHERE s.ticketFk = ?
AND tr.landed >= DATE_SUB(CURDATE(), INTERVAL 1 YEAR)`;
return Self.rawSql(query, [ticketId], options);
}
};
jorgep marked this conversation as resolved Outdated

@sergiodt ací no pots pasar desde front el codi correcte i evitem fer esta conversió?

@sergiodt ací no pots pasar desde front el codi correcte i evitem fer esta conversió?

Corregido, lo pasará desde el front.

Corregido, lo pasará desde el front.

View File

@ -0,0 +1,38 @@
const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('ticket assign()', () => {
let ctx;
let options;
let tx;
beforeEach(async() => {
ctx = {
req: {
accessToken: {userId: 1106},
headers: {origin: 'http://localhost'},
__: value => value
},
args: {}
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: ctx.req
});
options = {transaction: tx};
tx = await models.Sale.beginTransaction({});
options.transaction = tx;
});
afterEach(async() => {
await tx.rollback();
});
it('should throw an error when there is not picking tickets', async() => {
try {
await models.Collection.assign(ctx, options);
} catch (e) {
expect(e.message).toEqual('There are not picking tickets');
}
});
});

View File

@ -0,0 +1,62 @@
const {models} = require('vn-loopback/server/server');
describe('collection getSales()', () => {
const collectionOrTicketFk = 999999;
const print = true;
const source = 'CHECKER';
beforeAll(() => {
ctx = {
req: {
accessToken: {userId: 9},
headers: {origin: 'http://localhost'},
}
};
});
it('should return a collection with tickets, placements and barcodes settled correctly', async() => {
const tx = await models.Collection.beginTransaction({});
const options = {transaction: tx};
try {
const collection = await models.Collection.getSales(ctx,
collectionOrTicketFk, print, source, options);
const [firstTicket] = collection.tickets;
const [firstSale] = firstTicket.sales;
const [firstPlacement] = firstSale.placements;
expect(collection.tickets.length).toBeTruthy();
expect(collection.collectionFk).toEqual(firstTicket.ticketFk);
expect(firstSale.ticketFk).toEqual(firstTicket.ticketFk);
expect(firstSale.placements.length).toBeTruthy();
expect(firstSale.barcodes.length).toBeTruthy();
expect(firstSale.saleFk).toEqual(firstPlacement.saleFk);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should print a sticker', async() => {
const tx = await models.Collection.beginTransaction({});
const options = {transaction: tx};
const query = 'SELECT * FROM printQueue pq JOIN printQueueArgs pqa ON pqa.printQueueFk = pq.id';
try {
const printQueueBefore = await models.Collection.rawSql(
query, [], options);
await models.Collection.getSales(ctx,
collectionOrTicketFk, true, source, options);
const printQueueAfter = await models.Collection.rawSql(
query, [], options);
expect(printQueueAfter.length).toEqual(printQueueBefore.length + 1);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -0,0 +1,132 @@
const {models} = require('vn-loopback/server/server');
describe('machineWorker updateInTime()', () => {
const itBoss = 104;
const davidCharles = 1106;
beforeAll(async() => {
ctx = {
req: {
accessToken: {},
headers: {origin: 'http://localhost'},
__: value => value
}
};
});
it('should throw an error if the plate does not exist', async() => {
const tx = await models.MachineWorker.beginTransaction({});
const options = {transaction: tx};
const plate = 'RE-123';
ctx.req.accessToken.userId = 1106;
try {
await models.MachineWorker.updateInTime(ctx, plate, options);
await tx.rollback();
} catch (e) {
const error = e;
expect(error.message).toContain('the plate does not exist');
await tx.rollback();
}
});
it('should grab a machine where is not in use', async() => {
const tx = await models.MachineWorker.beginTransaction({});
const options = {transaction: tx};
const plate = 'RE-003';
ctx.req.accessToken.userId = 1107;
try {
const totalBefore = await models.MachineWorker.find(null, options);
await models.MachineWorker.updateInTime(ctx, plate, options);
const totalAfter = await models.MachineWorker.find(null, options);
expect(totalAfter.length).toEqual(totalBefore.length + 1);
await tx.rollback();
} catch (e) {
await tx.rollback();
}
});
describe('less than 12h', () => {
const plate = 'RE-001';
it('should trow an error if it is not himself', async() => {
const tx = await models.MachineWorker.beginTransaction({});
const options = {transaction: tx};
ctx.req.accessToken.userId = davidCharles;
try {
await models.MachineWorker.updateInTime(ctx, plate, options);
await tx.rollback();
} catch (e) {
const error = e;
expect(error.message).toContain('This machine is already in use');
await tx.rollback();
}
});
it('should throw an error if it is himself with a different machine', async() => {
const tx = await models.MachineWorker.beginTransaction({});
const options = {transaction: tx};
ctx.req.accessToken.userId = itBoss;
const plate = 'RE-003';
try {
await models.MachineWorker.updateInTime(ctx, plate, options);
await tx.rollback();
} catch (e) {
const error = e;
expect(error.message).toEqual('You are already using a machine');
await tx.rollback();
}
});
it('should set the out time if it is himself', async() => {
const tx = await models.MachineWorker.beginTransaction({});
const options = {transaction: tx};
ctx.req.accessToken.userId = itBoss;
try {
const isNotParked = await models.MachineWorker.findOne({
where: {workerFk: itBoss}
}, options);
await models.MachineWorker.updateInTime(ctx, plate, options);
const isParked = await models.MachineWorker.findOne({
where: {workerFk: itBoss}
}, options);
expect(isNotParked.outTime).toBeNull();
expect(isParked.outTime).toBeDefined();
await tx.rollback();
} catch (e) {
await tx.rollback();
}
});
});
describe('equal or more than 12h', () => {
const plate = 'RE-002';
it('should set the out time and grab the machine', async() => {
const tx = await models.MachineWorker.beginTransaction({});
const options = {transaction: tx};
ctx.req.accessToken.userId = davidCharles;
const filter = {
where: {workerFk: davidCharles, machineFk: 2}
};
try {
const isNotParked = await models.MachineWorker.findOne(filter, options);
const totalBefore = await models.MachineWorker.find(null, options);
await models.MachineWorker.updateInTime(ctx, plate, options);
const isParked = await models.MachineWorker.findOne(filter, options);
const totalAfter = await models.MachineWorker.find(null, options);
expect(isNotParked.outTime).toBeNull();
expect(isParked.outTime).toBeDefined();
expect(totalAfter.length).toEqual(totalBefore.length + 1);
await tx.rollback();
} catch (e) {
await tx.rollback();
}
});
});
});

View File

@ -0,0 +1,77 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('updateInTime', {
description: 'Updates the corresponding registry if the worker has been registered in the last few hours',
accessType: 'WRITE',
accepts: [
{
arg: 'plate',
type: 'string',
}
],
http: {
path: `/updateInTime`,
verb: 'POST'
}
});
Self.updateInTime = async(ctx, plate, options) => {
const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
const $t = ctx.req.__;
let tx;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const machine = await models.Machine.findOne({
fields: ['id', 'plate'],
where: {plate}
}, myOptions);
if (!machine)
throw new Error($t('the plate does not exist', {plate}));
const machineWorker = await Self.findOne({
where: {
or: [{machineFk: machine.id}, {workerFk: userId}],
outTime: null,
}
}, myOptions);
const {maxHours} = await models.MachineWorkerConfig.findOne({fields: ['maxHours']}, myOptions);
const hoursDifference = (Date.vnNow() - machineWorker.inTime.getTime()) / (60 * 60 * 1000);
if (machineWorker) {
jorgep marked this conversation as resolved Outdated

isHimself

isHimself
const isHimself = userId == machineWorker.workerFk;
const isSameMachine = machine.id == machineWorker.machineFk;
if (hoursDifference < maxHours && !isHimself)
throw new UserError($t('This machine is already in use.'));
if (hoursDifference < maxHours && isHimself && !isSameMachine)
throw new UserError($t('You are already using a machine'));
await machineWorker.updateAttributes({
outTime: Date.vnNew()
}, myOptions);
}
jorgep marked this conversation as resolved Outdated
Outdated
Review

Si en els dos casos fa el mateix, fica fora de les condicions

Si en els dos casos fa el mateix, fica fora de les condicions
if (!machineWorker || hoursDifference >= maxHours)
await models.MachineWorker.create({machineFk: machine.id, workerFk: userId}, myOptions);
if (tx) await tx.commit();
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,45 @@
module.exports = Self => {
Self.remoteMethodCtx('getVersion', {
description: 'gets app version data',
accessType: 'READ',
accepts: [{
arg: 'app',
type: 'string',
required: true
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/getVersion`,
verb: 'GET'
}
});
Self.getVersion = async(ctx, app) => {
const {models} = Self.app;
const userId = ctx.req.accessToken.userId;
const workerFk = await models.WorkerAppTester.findOne({
where: {
workerFk: userId
}
});
let fields = ['id', 'appName'];
if (workerFk)
jorgep marked this conversation as resolved
Review

Javascript tiene una forma muy sencilla de 'fusionar' arrays
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat

Javascript tiene una forma muy sencilla de 'fusionar' arrays https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
fields = fields.concat(['isVersionBetaCritical', 'versionBeta', 'urlBeta']);
else
fields = fields.concat(['isVersionCritical', 'version', 'urlProduction']);
const filter = {
where: {
appName: app
},
fields,
};
return Self.findOne(filter);
jorgep marked this conversation as resolved Outdated
Outdated
Review

en un return no hace falta poner 'await'

Bing:

En JavaScript, la mayoría de las veces, no hay diferencia observable entre return await myFunction() y return myFunction()12. Ambas versiones tienen el mismo comportamiento observable.
en un return no hace falta poner 'await' Bing: ``` En JavaScript, la mayoría de las veces, no hay diferencia observable entre return await myFunction() y return myFunction()12. Ambas versiones tienen el mismo comportamiento observable. ```
};
};

View File

@ -0,0 +1,29 @@
const {models} = require('vn-loopback/server/server');
describe('mobileAppVersionControl getVersion()', () => {
const appName = 'delivery';
beforeAll(async() => {
ctx = {
req: {
accessToken: {},
headers: {origin: 'http://localhost'},
}
};
});
it('should get the version app', async() => {
ctx.req.accessToken.userId = 9;
const {version, versionBeta} = await models.MobileAppVersionControl.getVersion(ctx, appName);
expect(version).toEqual('9.2');
expect(versionBeta).toBeUndefined();
});
it('should get the beta version app', async() => {
ctx.req.accessToken.userId = 66;
const {version, versionBeta} = await models.MobileAppVersionControl.getVersion(ctx, appName);
expect(versionBeta).toBeDefined();
expect(version).toBeUndefined();
});
});

View File

@ -79,9 +79,15 @@
"Language": { "Language": {
"dataSource": "vn" "dataSource": "vn"
}, },
"Machine": {
"dataSource": "vn"
},
"MachineWorker": { "MachineWorker": {
"dataSource": "vn" "dataSource": "vn"
}, },
"MachineWorkerConfig": {
"dataSource": "vn"
},
"MobileAppVersionControl": { "MobileAppVersionControl": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -3,4 +3,6 @@ module.exports = Self => {
require('../methods/collection/setSaleQuantity')(Self); require('../methods/collection/setSaleQuantity')(Self);
require('../methods/collection/previousLabel')(Self); require('../methods/collection/previousLabel')(Self);
require('../methods/collection/getTickets')(Self); require('../methods/collection/getTickets')(Self);
require('../methods/collection/assign')(Self);
require('../methods/collection/getSales')(Self);
}; };

View File

@ -0,0 +1,18 @@
{
"name": "MachineWorkerConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "vn.machineWorkerConfig"
}
},
"properties": {
"id": {
"type": "number",
"id": true
},
"maxHours": {
"type": "number"
}
}
}

View File

@ -0,0 +1,3 @@
module.exports = Self => {
require('../methods/machine-worker/updateInTime')(Self);
};

18
back/models/machine.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "Machine",
"base": "VnModel",
"options": {
"mysql": {
"table": "vn.machine"
}
},
"properties": {
"id": {
"type": "number",
"id": true
},
"plate": {
"type": "string"
}
}
}

View File

@ -0,0 +1,3 @@
module.exports = Self => {
require('../methods/mobile-app-version-control/getVersion')(Self);
};

View File

@ -0,0 +1,39 @@
{
"name": "MobileAppVersionControl",
"base": "VnModel",
"options": {
"mysql": {
"table": "vn.mobileAppVersionControl"
}
},
"properties": {
"id": {
"type": "number",
"id": true
},
"appName": {
"type": "string"
},
"version": {
"type": "string"
},
"isVersionCritical": {
"type": "boolean"
},
"urlProduction": {
"type": "string"
},
"urlBeta": {
"type": "string"
},
"versionBeta": {
"type": "string"
},
"isVersionBetaCritical": {
"type": "boolean"
}
}
}

View File

@ -1239,6 +1239,7 @@ INSERT INTO `vn`.`train`(`id`, `name`)
INSERT INTO `vn`.`operator` (`workerFk`, `numberOfWagons`, `trainFk`, `itemPackingTypeFk`, `warehouseFk`, `sectorFk`, `labelerFk`) INSERT INTO `vn`.`operator` (`workerFk`, `numberOfWagons`, `trainFk`, `itemPackingTypeFk`, `warehouseFk`, `sectorFk`, `labelerFk`)
VALUES VALUES
('1106', '1', '1', 'H', '1', '1', '1'), ('1106', '1', '1', 'H', '1', '1', '1'),
('9', '2', '1', 'H', '1', '1', '1'),
('1107', '1', '1', 'V', '1', '1', '1'); ('1107', '1', '1', 'V', '1', '1', '1');
INSERT INTO `vn`.`collection`(`id`, `workerFk`, `stateFk`, `created`, `trainFk`) INSERT INTO `vn`.`collection`(`id`, `workerFk`, `stateFk`, `created`, `trainFk`)
@ -2726,10 +2727,10 @@ INSERT INTO `vn`.`chat` (`senderFk`, `recipient`, `dated`, `checkUserStatus`, `m
(1101, '@PetterParker', util.VN_CURDATE(), 0, 'Second test message', 0, 'pending'); (1101, '@PetterParker', util.VN_CURDATE(), 0, 'Second test message', 0, 'pending');
INSERT INTO `vn`.`mobileAppVersionControl` (`appName`, `version`, `isVersionCritical`) INSERT INTO `vn`.`mobileAppVersionControl` (`appName`, `version`, `isVersionCritical`,`versionBeta`)
VALUES VALUES
('delivery', '9.2', 0), ('delivery', '9.2', 0,'9.7'),
('warehouse', '8.1', 0); ('warehouse', '8.1', 0,'8.3');
INSERT INTO `vn`.`machine` (`plate`, `maker`, `model`, `warehouseFk`, `departmentFk`, `type`, `use`, `productionYear`, `workerFk`, `companyFk`) INSERT INTO `vn`.`machine` (`plate`, `maker`, `model`, `warehouseFk`, `departmentFk`, `type`, `use`, `productionYear`, `workerFk`, `companyFk`)
VALUES VALUES
@ -3066,3 +3067,666 @@ INSERT INTO `vn`.`cmr` (id,truckPlate,observations,senderInstruccions,paymentIns
UPDATE vn.department UPDATE vn.department
SET workerFk = null; SET workerFk = null;
-- NEW WAREHOUSE
INSERT INTO vn.packaging
VALUES('--', 2745600.00, 100.00, 120.00, 220.00, 0.00, 1, '2001-01-01 00:00:00.000', NULL, NULL, NULL, 0.00, 16, 0.00, 0, NULL, 0.00, NULL, NULL, 0, NULL, 0, 0);
INSERT IGNORE INTO vn.intrastat
SET id = 44219999,
description = 'Manufacturas de madera',
taxClassFk = 1,
taxCodeFk = 1;
INSERT IGNORE INTO vn.warehouse
SET id = 999,
name = 'TestingWarehouse',
hasAvailable = TRUE,
isForTicket = TRUE,
isInventory = TRUE,
hasUbications = TRUE,
hasProduction = TRUE;
INSERT IGNORE INTO vn.sector
SET id = 9991,
description = 'NormalSector',
warehouseFk = 999,
code = 'NS',
isPackagingArea = FALSE,
sonFk = NULL,
isMain = TRUE,
itemPackingTypeFk = NULL;
INSERT IGNORE INTO vn.sector
SET id = 9992,
description = 'PreviousSector',
warehouseFk = 999,
code = 'PS',
isPackagingArea = FALSE,
sonFk = NULL,
isMain = TRUE,
itemPackingTypeFk = NULL;
INSERT IGNORE INTO vn.sector
SET id = 9993,
description = 'MezaninneSector',
warehouseFk = 999,
code = 'MS',
isPackagingArea = FALSE,
sonFk = 9991,
isMain = TRUE,
itemPackingTypeFk = NULL;
INSERT INTO vn.parking (id,sectorFk, code, pickingOrder)
VALUES (4,9991, 'A-01-1', 1),
(5,9991, 'A-02-2', 2),
(6,9991, 'A-03-3', 3),
(7,9991, 'A-04-4', 4),
(8,9991, 'A-05-5', 5),
Outdated
Review

Diria que es mejor enfoque cambiar el propio insert de vn.parking y de vn.shelving por lo que quieres.
Que hacer un insert que luegos vas a hacer un replaces.
O hacer el insert directamente
(Esto aplica a todos los repalces que haces)

Tampoco acabo de ver el adaptar las fixtures a ids tan altos, como lo ves @jgallego ?

Diria que es mejor enfoque cambiar el propio insert de vn.parking y de vn.shelving por lo que quieres. Que hacer un insert que luegos vas a hacer un replaces. O hacer el insert directamente (Esto aplica a todos los repalces que haces) Tampoco acabo de ver el adaptar las fixtures a ids tan altos, como lo ves @jgallego ?

cuantos menos ids mejor, si se puede cambiar directo en las fixtures mejor, sino intentar evitar el id

cuantos menos ids mejor, si se puede cambiar directo en las fixtures mejor, sino intentar evitar el id

replaces cambiados. Los ids en este caso son necesarios por lo hablado con @sergiodt .

replaces cambiados. Los ids en este caso son necesarios por lo hablado con @sergiodt .
(9,9992, 'P-01-1', 6),
(10,9992, 'P-02-2', 7),
(11,9992, 'P-03-3', 8),
(12,9993, 'M-01-1', 9),
(13,9993, 'M-02-2', 10),
(14,9993, 'M-03-3', 11);
INSERT INTO vn.shelving (code, parkingFk, priority)
VALUES ('NAA', 4, 1),
('NBB', 5, 1),
('NCC', 6, 1),
('NDD', 7, 1),
('NEE', 8, 1),
('PAA', 9, 1),
('PBB', 10, 1),
('PCC', 11, 1),
('MAA', 12, 1),
('MBB', 13, 1),
('MCC', 14, 1);
INSERT IGNORE INTO vn.itemType
SET id = 999,
code = 'WOO',
name = 'Wood Objects',
categoryFk = 3,
workerFk = 103,
isInventory = TRUE,
life = 10,
density = 250,
itemPackingTypeFk = NULL,
temperatureFk = 'warm';
INSERT IGNORE INTO vn.travel
SET id = 99,
shipped = CURDATE(),
landed = CURDATE(),
warehouseInFk = 999,
warehouseOutFk = 1,
isReceived = TRUE;
INSERT INTO vn.entry
SET id = 999,
supplierFk = 791,
isConfirmed = TRUE,
dated = CURDATE(),
travelFk = 99,
companyFk = 442;
INSERT INTO vn.ticket
SET id = 999999,
clientFk = 2,
warehouseFk = 999,
shipped = CURDATE(),
nickname = 'Cliente',
addressFk = 1,
companyFk = 442,
agencyModeFk = 10,
landed = CURDATE();
INSERT INTO vn.collection
SET id = 10101010,
workerFk = 9;
INSERT IGNORE INTO vn.ticketCollection
SET id = 10101010,
ticketFk = 999999,
collectionFk = 10101010;
INSERT INTO vn.item
SET id = 999991,
name = 'Palito para pinchos',
`size` = 25,
stems = NULL,
category = 'EXT',
typeFk = 999,
longName = 'Palito para pinchos',
itemPackingTypeFk = NULL,
originFk = 1,
weightByPiece = 6,
intrastatFk = 44219999;
INSERT INTO vn.buy
SET id = 9999991,
entryFk = 999,
itemFk = 999991,
quantity = 8,
buyingValue = 0.61,
stickers = 1,
packing = 20,
`grouping` = 1,
groupingMode = 1,
packageFk = 94,
price1 = 1,
price2 = 1,
price3 = 1,
minPrice = 1,
weight = 50;
INSERT INTO vn.sale
SET id = 99991,
itemFk = 999991,
ticketFk = 999999,
concept = 'Palito para pinchos',
quantity = 3,
price = 1,
discount = 0;
INSERT INTO vn.item
SET id = 999992,
name = 'Madera verde',
`size` = 10,
stems = NULL,
category = 'EXT',
typeFk = 999,
longName = 'Madera verde',
itemPackingTypeFk = NULL,
originFk = 1,
weightByPiece = 50,
intrastatFk = 44219999;
INSERT INTO vn.buy
SET id = 9999992,
entryFk = 999,
itemFk = 999992,
quantity = 40,
buyingValue = 0.62,
stickers = 1,
packing = 40,
`grouping` = 5,
groupingMode = 1,
packageFk = 94,
price1 = 1,
price2 = 1,
price3 = 1,
minPrice = 1,
weight = 25;
INSERT INTO vn.sale
SET id = 99992,
itemFk = 999992,
ticketFk = 999999,
concept = 'Madera Verde',
quantity = 10,
price = 1,
discount = 0;
INSERT INTO vn.item
SET id = 999993,
name = 'Madera Roja/Morada',
`size` = 12,
stems = 2,
category = 'EXT',
typeFk = 999,
longName = 'Madera Roja/Morada',
itemPackingTypeFk = NULL,
originFk = 1,
weightByPiece = 35,
intrastatFk = 44219999;
INSERT INTO vn.buy
SET id = 9999993,
entryFk = 999,
itemFk = 999993,
quantity = 20,
buyingValue = 0.63,
stickers = 2,
packing = 10,
`grouping` = 5,
groupingMode = 1,
packageFk = 94,
price1 = 1,
price2 = 1,
price3 = 1,
minPrice = 1,
weight = 25;
INSERT INTO vn.itemShelving
SET id = 9931,
itemFk = 999993,
shelvingFk = 'NCC',
visible = 10,
`grouping` = 5,
packing = 10;
INSERT INTO vn.sale
SET id = 99993,
itemFk = 999993,
ticketFk = 999999,
concept = 'Madera Roja/Morada',
quantity = 15,
price = 1,
discount = 0;
INSERT INTO vn.item
SET id = 999994,
name = 'Madera Naranja',
`size` = 18,
stems = 1,
category = 'EXT',
typeFk = 999,
longName = 'Madera Naranja',
itemPackingTypeFk = NULL,
originFk = 1,
weightByPiece = 160,
intrastatFk = 44219999;
INSERT INTO vn.buy
SET id = 9999994,
entryFk = 999,
itemFk = 999994,
quantity = 20,
buyingValue = 0.64,
stickers = 1,
packing = 20,
`grouping` = 4,
groupingMode = 1,
packageFk = 94,
price1 = 1,
price2 = 1,
price3 = 1,
minPrice = 1,
weight = 25;
INSERT INTO vn.sale
SET id = 99994,
itemFk = 999994,
ticketFk = 999999,
concept = 'Madera Naranja',
quantity = 4,
price = 1,
discount = 0;
INSERT INTO vn.item
SET id = 999995,
name = 'Madera Amarilla',
`size` = 11,
stems = 5,
category = 'EXT',
typeFk = 999,
longName = 'Madera Amarilla',
itemPackingTypeFk = NULL,
originFk = 1,
weightByPiece = 78,
intrastatFk = 44219999;
INSERT INTO vn.buy
SET id = 9999995,
entryFk = 999,
itemFk = 999995,
quantity = 4,
buyingValue = 0.65,
stickers = 1,
packing = 20,
`grouping` = 1,
groupingMode = 1,
packageFk = 94,
price1 = 1,
price2 = 1,
price3 = 1,
minPrice = 1,
weight = 35;
INSERT INTO vn.sale
SET id = 99995,
itemFk = 999995,
ticketFk = 999999,
concept = 'Madera Amarilla',
quantity = 5,
price = 1,
discount = 0;
-- Palito naranja
INSERT INTO vn.item
SET id = 999998,
name = 'Palito naranja',
`size` = 11,
stems = 1,
category = 'EXT',
typeFk = 999,
longName = 'Palito naranja',
itemPackingTypeFk = NULL,
originFk = 1,
weightByPiece = 78,
intrastatFk = 44219999;
INSERT INTO vn.buy
SET id = 9999998,
entryFk = 999,
itemFk = 999998,
quantity = 80,
buyingValue = 0.65,
stickers = 1,
packing = 200,
`grouping` = 30,
groupingMode = 1,
packageFk = 94,
price1 = 1,
price2 = 1,
price3 = 1,
minPrice = 1,
weight = 35;
INSERT INTO vn.sale
SET id = 99998,
itemFk = 999998,
ticketFk = 999999,
concept = 'Palito naranja',
quantity = 60,
price = 1,
discount = 0;
-- Palito amarillo
INSERT INTO vn.item
SET id = 999999,
name = 'Palito amarillo',
`size` = 11,
stems = 1,
category = 'EXT',
typeFk = 999,
longName = 'Palito amarillo',
itemPackingTypeFk = NULL,
originFk = 1,
weightByPiece = 78,
intrastatFk = 44219999;
INSERT INTO vn.buy
SET id = 9999999,
entryFk = 999,
itemFk = 999999,
quantity = 70,
buyingValue = 0.65,
stickers = 1,
packing = 500,
`grouping` = 10,
groupingMode = 1,
packageFk = 94,
price1 = 1,
price2 = 1,
price3 = 1,
minPrice = 1,
weight = 35;
INSERT INTO vn.sale
SET id = 99999,
itemFk = 999999,
ticketFk = 999999,
concept = 'Palito amarillo',
quantity = 50,
price = 1,
discount = 0;
-- Palito azul
INSERT INTO vn.item
SET id = 1000000,
name = 'Palito azul',
`size` = 10,
stems = 1,
category = 'EXT',
typeFk = 999,
longName = 'Palito azul',
itemPackingTypeFk = NULL,
originFk = 1,
weightByPiece = 78,
intrastatFk = 44219999;
INSERT INTO vn.buy
SET id = 10000000,
entryFk = 999,
itemFk = 1000000,
quantity = 75,
buyingValue = 0.65,
stickers = 2,
packing = 300,
`grouping` = 50,
groupingMode = 1,
packageFk = 94,
price1 = 1,
price2 = 1,
price3 = 1,
minPrice = 1,
weight = 35;
INSERT INTO vn.sale
SET id = 100000,
itemFk = 1000000,
ticketFk = 999999,
concept = 'Palito azul',
quantity = 50,
price = 1,
discount = 0;
-- Palito rojo
INSERT INTO vn.item
SET id = 1000001,
name = 'Palito rojo',
`size` = 10,
stems = NULL,
category = 'EXT',
typeFk = 999,
longName = 'Palito rojo',
itemPackingTypeFk = NULL,
originFk = 1,
weightByPiece = 78,
intrastatFk = 44219999;
INSERT INTO vn.buy
SET id = 10000001,
entryFk = 999,
itemFk = 1000001,
quantity = 12,
buyingValue = 0.65,
stickers = 2,
packing = 50,
`grouping` = 5,
groupingMode = 1,
packageFk = 94,
price1 = 1,
price2 = 1,
price3 = 1,
minPrice = 1,
weight = 35;
INSERT INTO vn.sale
SET id = 100001,
itemFk = 1000001,
ticketFk = 999999,
concept = 'Palito rojo',
quantity = 10,
price = 1,
discount = 0;
-- Previa
INSERT IGNORE INTO vn.item
SET id = 999996,
name = 'Bolas de madera',
`size` = 2,
stems = 4,
category = 'EXT',
typeFk = 999,
longName = 'Bolas de madera',
itemPackingTypeFk = NULL,
originFk = 1,
weightByPiece = 20,
intrastatFk = 44219999;
INSERT vn.buy
SET id = 9999996,
entryFk = 999,
itemFk = 999996,
quantity = 5,
buyingValue = 3,
stickers = 1,
packing = 5,
`grouping` = 2,
groupingMode = 1,
packageFk = 94,
price1 = 7,
price2 = 7,
price3 = 7,
minPrice = 7,
weight = 80;
INSERT vn.sale
SET id = 99996,
itemFk = 999996,
ticketFk = 999999,
concept = 'Bolas de madera',
quantity = 4,
price = 7,
discount = 0,
isPicked = TRUE;
INSERT IGNORE INTO vn.item
SET id = 999997,
name = 'Palitos de polo MIX',
`size` = 14,
stems = NULL,
category = 'EXT',
typeFk = 999,
longName = 'Palitos de polo MIX',
itemPackingTypeFk = NULL,
originFk = 1,
weightByPiece = 20,
intrastatFk = 44219999;
INSERT vn.buy
SET id = 9999997,
entryFk = 999,
itemFk = 999997,
quantity = 100,
buyingValue = 3.2,
stickers = 1,
packing = 100,
`grouping` = 5,
groupingMode = 1,
packageFk = 94,
price1 = 7,
price2 = 7,
price3 = 7,
minPrice = 7,
weight = 80;
INSERT vn.sale
SET id = 99997,
itemFk = 999997,
ticketFk = 999999,
concept = 'Palitos de polo MIX',
quantity = 5,
price = 7,
discount = 0;
USE vn;
DELETE ish.* FROM vn.itemShelving ish
JOIN vn.shelving sh ON sh.code = ish.shelvingFk
JOIN vn.parking p ON p.id = sh.parkingFk
JOIN vn.sector s ON s.id = p.sectorFk
JOIN vn.warehouse w ON w.id = s.warehouseFk
WHERE w.name = 'TestingWarehouse';
INSERT INTO vn.itemShelving
(itemFk, shelvingFk, visible, created, `grouping`, packing, packagingFk, userFk, isChecked)
VALUES
(999991, 'NAA', 8, '2023-09-20', 1, 20, NULL, 103, NULL),
(999998, 'NAA', 80, '2023-09-20', 10, 30, NULL, 103, NULL),
(1000001, 'NAA', 6, '2023-09-20', 3, 50, NULL, 103, NULL),
(1000000, 'NBB', 50, '2023-09-18', 25, 500, NULL, 103, NULL),
(999993, 'NBB', 25, '2023-09-18', NULL, 10, NULL, 103, NULL),
(999999, 'NBB', 30, '2023-09-18', 10, 500, NULL, 103, NULL),
(999993, 'NCC', 25, '2023-09-20', 5, 10, NULL, 103, NULL),
(999997, 'NCC', 10, '2023-09-20', NULL, 100, NULL, 103, NULL),
(999999, 'NCC', 40, '2023-09-20', 10, 500, NULL, 103, NULL),
(999995, 'NDD', 10, '2023-09-19', NULL, 20, NULL, 103, NULL),
(999994, 'NDD', 48, '2023-09-19', 4, 20, NULL, 103, NULL),
(1000001, 'NEE', 6, '2023-09-21', 3, 50, NULL, 103, NULL),
(999992, 'NEE', 50, '2023-09-21', NULL, 1, NULL, 103, NULL),
(1000000, 'NEE', 25, '2023-09-21', 25, 500, NULL, 103, NULL),
(999996, 'PAA', 5, '2023-09-27', 1, 5, NULL, 103, NULL),
(999997, 'PCC', 10, '2023-09-27', 5, 100, NULL, 103, NULL);
-- Previous for Bolas de madera
INSERT IGNORE INTO vn.sectorCollection
SET id = 99,
userFk = 1,
sectorFk = 9992;
INSERT IGNORE INTO vn.saleGroup
SET id = 4,
userFk = 1,
parkingFk = 9,
sectorFk = 9992;
INSERT IGNORE INTO vn.sectorCollectionSaleGroup
SET id = 9999,
sectorCollectionFk = 99,
saleGroupFk = 999;
INSERT vn.saleGroupDetail
SET id = 99991,
saleFk = 99996,
saleGroupFk = 999;
INSERT INTO vn.saleTracking
SET id = 7,
saleFk = 99996,
isChecked = TRUE,
workerFk = 103,
stateFk = 28;
INSERT IGNORE INTO vn.itemShelvingSale
SET id = 991,
itemShelvingFk = 9962,
saleFk = 99996,
quantity = 5,
userFk = 1;
UPDATE vn.ticket
SET zoneFk=1
WHERE id=999999;
UPDATE vn.collection
SET workerFk=9
WHERE id=10101010;
UPDATE vn.sale
SET isPicked =FALSE;
INSERT INTO vn.machineWorkerConfig(maxHours)
VALUES(12);
INSERT INTO vn.workerAppTester(workerFk) VALUES(66);
INSERT INTO `vn`.`machine` (`plate`, `maker`, `model`, `warehouseFk`, `departmentFk`, `type`, `use`, `productionYear`, `workerFk`, `companyFk`)
VALUES
('RE-003', 'IRON', 'JPH-24', 60, 23, 'ELECTRIC TOW', 'Drag cars', 2020, 103, 442);
INSERT INTO vn.machineWorker(workerFk,machineFk,inTimed) VALUES (104,1,'2001-01-01 10:00:00.00.000');
UPDATE vn.buy SET itemOriginalFk = 1 WHERE id = 1;
UPDATE vn.saleTracking SET stateFk = 26 WHERE id = 5;
INSERT INTO vn.report (name) VALUES ('LabelCollection');

View File

@ -0,0 +1,12 @@
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES
('Collection', 'assign', 'WRITE', 'ALLOW', 'ROLE', 'production'),
('ExpeditionPallet', 'getPallet', 'READ', 'ALLOW', 'ROLE', 'production'),
('MachineWorker','updateInTime','WRITE','ALLOW','ROLE','production'),
('MobileAppVersionControl','getVersion','READ','ALLOW','ROLE','production'),
('SaleTracking','delete','WRITE','ALLOW','ROLE','production'),
('SaleTracking','updateTracking','WRITE','ALLOW','ROLE','production'),
('SaleTracking','setPicked','WRITE','ALLOW','ROLE','production'),
('ExpeditionPallet', '*', 'READ', 'ALLOW', 'ROLE', 'production'),
('Sale', 'getFromSectorCollection', 'READ', 'ALLOW', 'ROLE', 'production'),
('ItemBarcode', 'delete', 'WRITE', 'ALLOW', 'ROLE', 'production');

View File

@ -1,6 +1,7 @@
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
async function handleObserve(ctx) { async function handleObserve(ctx) {
ctx.options.httpCtx = LoopBackContext.getCurrentContext(); const httpCtx = LoopBackContext.getCurrentContext();
ctx.options.userId = httpCtx?.active?.accessToken?.userId;
} }
module.exports = function(Self) { module.exports = function(Self) {
let Mixin = { let Mixin = {

View File

@ -209,5 +209,16 @@
"You cannot update these fields": "You cannot update these fields", "You cannot update these fields": "You cannot update these fields",
"CountryFK cannot be empty": "Country cannot be empty", "CountryFK cannot be empty": "Country cannot be empty",
"You are not allowed to modify the alias": "You are not allowed to modify the alias", "You are not allowed to modify the alias": "You are not allowed to modify the alias",
"You already have the mailAlias": "You already have the mailAlias" "You already have the mailAlias": "You already have the mailAlias",
"This machine is already in use.": "This machine is already in use.",
"the plate does not exist": "The plate {{plate}} does not exist",
"We do not have availability for the selected item": "We do not have availability for the selected item",
"You are already using a machine": "You are already using a machine",
"this state does not exist": "This state does not exist",
"The line could not be marked": "The line could not be marked",
"The sale cannot be tracked": "The sale cannot be tracked",
"Shelving not valid": "Shelving not valid",
"printerNotExists": "The printer does not exist",
"There are not picking tickets": "There are not picking tickets",
"ticketCommercial": "The ticket {{ ticket }} for the salesperson {{ salesMan }} is in preparation. (automatically generated message)"
} }

View File

@ -275,7 +275,7 @@ class VnMySQL extends MySQL {
} }
invokeMethod(method, args, model, ctx, opts, cb) { invokeMethod(method, args, model, ctx, opts, cb) {
if (!this.isLoggable(model)) if (!this.isLoggable(model) && !opts?.userId)
return super[method].apply(this, args); return super[method].apply(this, args);
this.invokeMethodP(method, [...args], model, ctx, opts) this.invokeMethodP(method, [...args], model, ctx, opts)
@ -287,11 +287,11 @@ class VnMySQL extends MySQL {
let tx; let tx;
if (!opts.transaction) { if (!opts.transaction) {
tx = await Transaction.begin(this, {}); tx = await Transaction.begin(this, {});
opts = Object.assign({transaction: tx, httpCtx: opts.httpCtx}, opts); opts = Object.assign({transaction: tx}, opts);
} }
try { try {
const userId = opts.httpCtx && opts.httpCtx.active?.accessToken?.userId; const {userId} = opts;
Review

I en ningun cas es opts.httpCtx.active?.accessToken?.userId ?

I en ningun cas es `opts.httpCtx.active?.accessToken?.userId` ?
Review

En el archivo loopback>common>mixins>loggable, se ha aplicado cambios para recoger directamente el userId. Cambio realizado por Juan.

En el archivo loopback>common>mixins>loggable, se ha aplicado cambios para recoger directamente el userId. Cambio realizado por Juan.
if (userId) { if (userId) {
const user = await Model.app.models.VnUser.findById(userId, {fields: ['name']}, opts); const user = await Model.app.models.VnUser.findById(userId, {fields: ['name']}, opts);
await this.executeP(`CALL account.myUser_loginWithName(?)`, [user.name], opts); await this.executeP(`CALL account.myUser_loginWithName(?)`, [user.name], opts);

View File

@ -37,7 +37,7 @@ describe('Entry filter()', () => {
const result = await models.Entry.filter(ctx, options); const result = await models.Entry.filter(ctx, options);
expect(result.length).toEqual(8); expect(result.length).toEqual(9);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -81,7 +81,7 @@ describe('Entry filter()', () => {
const result = await models.Entry.filter(ctx, options); const result = await models.Entry.filter(ctx, options);
expect(result.length).toEqual(7); expect(result.length).toEqual(8);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -0,0 +1,34 @@
module.exports = Self => {
jorgep marked this conversation as resolved
Review

aqui no se puede usar el nativo desde front y no cremo el archivo?

aqui no se puede usar el nativo desde front y no cremo el archivo?
Review

@jgallego solo permite eliminar por id.

@jgallego solo permite eliminar por id.
Self.remoteMethod('delete', {
description: 'Delete an ItemBarcode by itemFk and code',
accessType: 'WRITE',
jorgep marked this conversation as resolved Outdated
Outdated
Review

Seria WRITE

Seria WRITE
accepts: [
{
arg: 'barcode',
type: 'string',
required: true,
},
{
arg: 'itemFk',
type: 'number',
required: true,
}
],
http: {
path: `/delete`,
verb: 'DELETE'
}
});
Self.delete = async(barcode, itemFk, options) => {
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
await Self.destroyAll({
alexm marked this conversation as resolved
Review

No se pot fer directe contra el model?

DELETE .../ItemBarcodes?barcode=...&itemFk=...

No se pot fer directe contra el model? DELETE `.../ItemBarcodes?barcode=...&itemFk=...`
Review

Solo se puede por id

Solo se puede por id
code: barcode,
itemFk
}, myOptions);
};
};

View File

@ -0,0 +1,22 @@
const {models} = require('vn-loopback/server/server');
describe('itemBarcode delete()', () => {
it('should delete a record by itemFk and code', async() => {
const tx = await models.ItemBarcode.beginTransaction({});
const options = {transaction: tx};
const itemFk = 1;
const code = 1111111111;
try {
const itemsBarcodeBefore = await models.ItemBarcode.find({}, options);
await models.ItemBarcode.delete(code, itemFk, options);
const itemsBarcodeAfter = await models.ItemBarcode.find({}, options);
expect(itemsBarcodeBefore.length).toBeGreaterThan(itemsBarcodeAfter.length);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -0,0 +1,64 @@
module.exports = Self => {
Self.remoteMethod('getAlternative', {
description: 'Returns a list of items and possible alternative locations',
accessType: 'READ',
accepts: [{
arg: 'shelvingFk',
type: 'string',
required: true,
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/getAlternative`,
verb: 'GET'
}
});
Self.getAlternative = async(shelvingFk, options) => {
const models = Self.app.models;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const filterItemShelvings = {
fields: ['id', 'visible', 'itemFk', 'shelvingFk'],
where: {shelvingFk},
include: [
{
relation: 'item',
scope: {
fields: ['longName', 'name', 'size']
}
},
]
};
let itemShelvings = await models.ItemShelving.find(filterItemShelvings, myOptions);
if (itemShelvings) {
const [alternatives] = await models.ItemShelving.rawSql('CALL vn.itemShelving_getAlternatives(?)',
[shelvingFk], myOptions
);
return itemShelvings.map(itemShelving => {
const item = itemShelving.item();
const shelvings = alternatives.filter(alternative => alternative.itemFk == itemShelving.itemFk);
jorgep marked this conversation as resolved Outdated
Outdated
Review

Mejor variable en ingles

Mejor variable en ingles
return {
id: itemShelving.id,
itemFk: itemShelving.itemFk,
name: item.name,
jorgep marked this conversation as resolved Outdated

esto donde se muestra? mirar con @sergiodt ya que esto debería en todo caso hacerlo front, intentar mostrar siempre el longName si no lo hay no mostrar nada, siempre debe haberlo

esto donde se muestra? mirar con @sergiodt ya que esto debería en todo caso hacerlo front, intentar mostrar siempre el longName si no lo hay no mostrar nada, siempre debe haberlo

Vale, le envio por separado, como propiedades el name y el size y ya el muestra en el front longname o los otros. Hablado con @sergiodt .

Vale, le envio por separado, como propiedades el name y el size y ya el muestra en el front longname o los otros. Hablado con @sergiodt .
size: item.size,
longName: item.longName,
quantity: itemShelving.visible,
shelvings
};
});
}
};
};

View File

@ -0,0 +1,25 @@
const {models} = require('vn-loopback/server/server');
describe('itemShelving getAlternative()', () => {
beforeAll(async() => {
ctx = {
req: {
headers: {origin: 'http://localhost'},
}
};
});
it('should return a list of items without alternatives', async() => {
const shelvingFk = 'HEJ';
const itemShelvings = await models.ItemShelving.getAlternative(shelvingFk);
expect(itemShelvings[0].shelvings.length).toEqual(0);
});
it('should return an empty list', async() => {
const shelvingFk = 'ZZP';
const itemShelvings = await models.ItemShelving.getAlternative(shelvingFk);
expect(itemShelvings.length).toEqual(0);
});
});

View File

@ -0,0 +1,22 @@
const {models} = require('vn-loopback/server/server');
describe('itemShelving updateFromSale()', () => {
it('should update the quantity', async() => {
const tx = await models.ItemBarcode.beginTransaction({});
const options = {transaction: tx};
const saleFk = 2;
const filter = {where: {itemFk: 4, shelvingFk: 'HEJ'}
};
try {
const {visible: visibleBefore} = await models.ItemShelving.findOne(filter, options);
await models.ItemShelving.updateFromSale(saleFk, options);
const {visible: visibleAfter} = await models.ItemShelving.findOne(filter, options);
expect(visibleAfter).toEqual(visibleBefore + 5);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -0,0 +1,48 @@
module.exports = Self => {
Self.remoteMethod('updateFromSale', {
description: 'Update the visible items',
accessType: 'WRITE',
accepts: [{
arg: 'saleFk',
type: 'number',
required: true,
}],
http: {
path: `/updateFromSale`,
verb: 'POST'
}
});
Self.updateFromSale = async(saleFk, options) => {
const models = Self.app.models;
const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const itemShelvingSale = await models.ItemShelvingSale.findOne({
where: {saleFk},
include: {relation: 'itemShelving'}
}, myOptions);
const itemShelving = itemShelvingSale.itemShelving();
const quantity = itemShelving.visible + itemShelvingSale.quantity;
await itemShelving.updateAttributes(
{visible: quantity},
myOptions
);
if (tx) await tx.commit();
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,48 @@
module.exports = Self => {
Self.remoteMethod('get', {
description: 'Get the data from an item',
accessType: 'READ',
http: {
path: `/get`,
verb: 'GET'
},
accepts: [
{
arg: 'barcode',
type: 'number',
required: true,
},
{
arg: 'warehouseFk',
type: 'number',
required: true,
}
],
returns: {
type: ['object'],
root: true
},
});
Self.get = async(barcode, warehouseFk, options) => {
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const models = Self.app.models;
const [[itemInfo]] = await Self.rawSql('CALL vn.item_getInfo(?, ?)', [barcode, warehouseFk], myOptions);
if (itemInfo) {
itemInfo.barcodes = await models.ItemBarcode.find({
fields: ['code'],
where: {
itemFk: itemInfo.id
}
});
}
return itemInfo;
};
};

View File

@ -0,0 +1,12 @@
const {models} = require('vn-loopback/server/server');
describe('item get()', () => {
const barcode = 1;
const warehouseFk = 1;
it('should get an item with several barcodes', async() => {
const card = await models.Item.get(barcode, warehouseFk);
expect(card).toBeDefined();
expect(card.barcodes.length).toBeTruthy();
});
});

View File

@ -1,5 +1,6 @@
module.exports = Self => { module.exports = Self => {
require('../methods/item-barcode/toItem')(Self); require('../methods/item-barcode/toItem')(Self);
require('../methods/item-barcode/delete')(Self);
Self.validatesUniquenessOf('code', { Self.validatesUniquenessOf('code', {
message: `Barcode must be unique` message: `Barcode must be unique`

View File

@ -2,4 +2,6 @@ module.exports = Self => {
require('../methods/item-shelving/deleteItemShelvings')(Self); require('../methods/item-shelving/deleteItemShelvings')(Self);
require('../methods/item-shelving/upsertItem')(Self); require('../methods/item-shelving/upsertItem')(Self);
require('../methods/item-shelving/getInventory')(Self); require('../methods/item-shelving/getInventory')(Self);
require('../methods/item-shelving/getAlternative')(Self);
require('../methods/item-shelving/updateFromSale')(Self);
}; };

View File

@ -54,7 +54,8 @@
"shelving": { "shelving": {
"type": "belongsTo", "type": "belongsTo",
"model": "Shelving", "model": "Shelving",
"foreignKey": "shelvingFk" "foreignKey": "shelvingFk",
"primaryKey": "code"
} }
} }
} }

View File

@ -17,6 +17,7 @@ module.exports = Self => {
require('../methods/item/buyerWasteEmail')(Self); require('../methods/item/buyerWasteEmail')(Self);
require('../methods/item/labelPdf')(Self); require('../methods/item/labelPdf')(Self);
require('../methods/item/setVisibleDiscard')(Self); require('../methods/item/setVisibleDiscard')(Self);
require('../methods/item/get')(Self);
Self.validatesPresenceOf('originFk', {message: 'Cannot be blank'}); Self.validatesPresenceOf('originFk', {message: 'Cannot be blank'});

View File

@ -0,0 +1,56 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('addLog', {
description: 'Add a new log',
accessType: 'WRITE',
accepts: {
arg: 'code',
type: 'string',
required: true,
},
http: {
path: '/addLog',
verb: 'POST'
}
});
Self.addLog = async(ctx, code, options) => {
const userId = ctx.req.accessToken.userId;
const $t = ctx.req.__;
const models = Self.app.models;
const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const shelving = await Self.findOne({
where: {
code
}
}, myOptions);
if (!shelving) throw new UserError($t('Shelving not valid'));
await models.ShelvingLog.create({
jgallego marked this conversation as resolved
Review

esto porque no se hace automatizado como el resto de modelos? ejemplo ticket

esto porque no se hace automatizado como el resto de modelos? ejemplo ticket
Review

@jgallego @alexm Perquè necessitaria dos cridaes, una per traure el id i un altra per fer el insert amb el id.

@jgallego @alexm Perquè necessitaria dos cridaes, una per traure el id i un altra per fer el insert amb el id.
changedModel: 'Shelving',
originFk: shelving.id,
changedModelId: shelving.id,
action: 'select',
userFk: userId
}, myOptions);
if (tx) await tx.commit();
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,46 @@
const {models} = require('vn-loopback/server/server');
describe('shelving addLog()', () => {
beforeAll(async() => {
ctx = {
req: {
headers: {origin: 'http://localhost'},
accessToken: {userId: 66},
__: value => value
}
};
});
it('should add a log', async() => {
const tx = await models.SaleTracking.beginTransaction({});
const options = {transaction: tx};
const code = 'AA6';
try {
const shelvingLogsBefore = await models.ShelvingLog.find(null, options);
await models.Shelving.addLog(ctx, code, options);
const shelvingLogsAfter = await models.ShelvingLog.find(null, options);
expect(shelvingLogsAfter.length).toEqual(shelvingLogsBefore.length + 1);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should throw an error when the code does not exist', async() => {
const tx = await models.SaleTracking.beginTransaction({});
const options = {transaction: tx};
const code = 'DXI345';
try {
await models.Shelving.addLog(ctx, code, options);
await tx.rollback();
} catch (e) {
expect(e.message).toEqual('Shelving not valid');
await tx.rollback();
}
});
});

View File

@ -1,3 +1,4 @@
module.exports = Self => { module.exports = Self => {
require('../methods/shelving/getSummary')(Self); require('../methods/shelving/getSummary')(Self);
require('../methods/shelving/addLog')(Self);
}; };

View File

@ -2,7 +2,7 @@
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('delete', { Self.remoteMethod('delete', {
description: 'Delete sale trackings and item shelving sales', description: 'Delete sale trackings and item shelving sales',
accessType: 'READ', accessType: 'WRITE',
accepts: [ accepts: [
{ {
arg: 'saleFk', arg: 'saleFk',
@ -10,21 +10,17 @@ module.exports = Self => {
description: 'The sale id' description: 'The sale id'
}, },
{ {
arg: 'stateCode', arg: 'stateCodes',
type: 'string' type: ['string']
}
],
returns: {
type: ['object'],
root: true
}, },
],
http: { http: {
jorgep marked this conversation as resolved
Review

Si no lo requiere sergio especificamente, normalmente con terminar el procedimiento sobra, no hace falta devolver true

Si no lo requiere sergio especificamente, normalmente con terminar el procedimiento sobra, no hace falta devolver true
Review

Lo quito

Lo quito
path: `/delete`, path: `/delete`,
verb: 'POST' verb: 'POST'
} }
}); });
Self.delete = async(saleFk, stateCode, options) => { Self.delete = async(saleFk, stateCodes, options) => {
const models = Self.app.models; const models = Self.app.models;
const myOptions = {}; const myOptions = {};
let tx; let tx;
@ -38,20 +34,24 @@ module.exports = Self => {
} }
try { try {
if (stateCode === 'PREPARED') {
const itemShelvingSales = await models.ItemShelvingSale.find({where: {saleFk: saleFk}}, myOptions); const itemShelvingSales = await models.ItemShelvingSale.find({where: {saleFk: saleFk}}, myOptions);
for (let itemShelvingSale of itemShelvingSales) for (let itemShelvingSale of itemShelvingSales)
await itemShelvingSale.destroy(myOptions); await itemShelvingSale.destroy(myOptions);
}
const state = await models.State.findOne({ const states = await models.State.find({
where: {code: stateCode} fields: ['id'],
where: {
code: {inq: stateCodes}
}
}, myOptions); }, myOptions);
const stateIds = states.map(state => state.id);
const filter = { const filter = {
where: { where: {
saleFk: saleFk, saleFk: saleFk,
stateFk: state.id stateFk: {inq: stateIds}
} }
}; };
const saleTrackings = await models.SaleTracking.find(filter, myOptions); const saleTrackings = await models.SaleTracking.find(filter, myOptions);
@ -59,8 +59,6 @@ module.exports = Self => {
await saleTracking.destroy(myOptions); await saleTracking.destroy(myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();
return true;
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();
throw e; throw e;

View File

@ -0,0 +1,106 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('setPicked', {
description: 'Add the sales line of the item and set the tracking.',
accessType: 'WRITE',
accepts: [
{
arg: 'saleFk',
type: 'number',
required: true
},
{
arg: 'originalQuantity',
type: 'number',
required: true
},
{
arg: 'code',
type: 'string',
required: true
},
{
arg: 'isChecked',
type: 'boolean',
required: true
},
{
arg: 'buyFk',
type: 'number',
required: true
},
{
arg: 'isScanned',
type: 'boolean',
},
{
arg: 'quantity',
type: 'number',
required: true
},
{
arg: 'itemShelvingFk',
type: 'number',
required: true
}
],
http: {
path: `/setPicked`,
verb: 'POST'
}
});
Self.setPicked = async(ctx, saleFk, originalQuantity, code, isChecked, buyFk, isScanned, quantity, itemShelvingFk, options) => {
const userId = ctx.req.accessToken.userId;
const models = Self.app.models;
const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
await models.ItemShelvingSale.create({
itemShelvingFk,
saleFk,
quantity,
userFk: userId
}, myOptions);
const itemShelving = await models.ItemShelving.findById(itemShelvingFk, null, myOptions);
await itemShelving.updateAttributes({visible: itemShelving.visible - quantity}, myOptions);
await Self.updateAll(
{saleFk},
{isChecked: true},
myOptions
);
await Self.updateTracking(ctx, saleFk, originalQuantity, code, isChecked, null, isScanned, myOptions);
try {
const {itemOriginalFk} = await models.Buy.findById(buyFk, {fields: ['itemOriginalFk']}, myOptions);
jorgep marked this conversation as resolved Outdated
Outdated
Review

const {itemOriginalFk}

const {itemOriginalFk}
if (itemOriginalFk) await models.SaleBuy.create({saleFk, buyFk}, myOptions);
} catch (e) {
throw new UserError('The sale cannot be tracked');
}
if (tx) await tx.commit();
} catch (e) {
if (e.message == 'The sale cannot be tracked') {
if (tx) tx.commit();
throw e;
}
if (tx) await tx.rollback();
throw new UserError('The line could not be marked');
}
};
};

View File

@ -11,13 +11,12 @@ describe('sale-tracking delete()', () => {
const saleTrackingsBefore = await models.SaleTracking.find(null, options); const saleTrackingsBefore = await models.SaleTracking.find(null, options);
const saleFk = 1; const saleFk = 1;
const stateCode = 'PREPARED'; const stateCode = ['PREPARED'];
const result = await models.SaleTracking.delete(saleFk, stateCode, options); await models.SaleTracking.delete(saleFk, stateCode, options);
const itemShelvingsAfter = await models.ItemShelvingSale.find(null, options); const itemShelvingsAfter = await models.ItemShelvingSale.find(null, options);
const saleTrackingsAfter = await models.SaleTracking.find(null, options); const saleTrackingsAfter = await models.SaleTracking.find(null, options);
expect(result).toEqual(true);
expect(saleTrackingsAfter.length).toBeLessThan(saleTrackingsBefore.length); expect(saleTrackingsAfter.length).toBeLessThan(saleTrackingsBefore.length);
expect(itemShelvingsAfter.length).toBeLessThan(itemShelvingsBefore.length); expect(itemShelvingsAfter.length).toBeLessThan(itemShelvingsBefore.length);

View File

@ -0,0 +1,114 @@
const {models} = require('vn-loopback/server/server');
describe('saleTracking setPicked()', () => {
const saleFk = 1;
const originalQuantity = 10;
const code = 'PREPARED';
const isChecked = true;
const buyFk = 1;
const isScanned = false;
const quantity = 1;
const itemShelvingFk = 1;
beforeAll(async() => {
ctx = {
req: {
accessToken: {userId: 104},
headers: {origin: 'http://localhost'},
__: value => value
}
};
});
it('should throw an error if the line was not able to be marked', async() => {
const tx = await models.SaleTracking.beginTransaction({});
const options = {transaction: tx};
const code = 'FAKESTATE';
try {
await models.SaleTracking.setPicked(
ctx,
saleFk,
originalQuantity,
code,
isChecked,
buyFk,
isScanned,
quantity,
itemShelvingFk,
options
);
await tx.rollback();
} catch (e) {
const error = e;
expect(error.message).toEqual('The line could not be marked');
await tx.rollback();
}
});
it('should throw an error if there are duplicate salebuys', async() => {
const tx = await models.SaleTracking.beginTransaction({});
const options = {transaction: tx};
try {
await models.SaleTracking.setPicked(
ctx,
saleFk,
originalQuantity,
code,
isChecked,
buyFk,
isScanned,
quantity,
itemShelvingFk,
options
);
await models.SaleTracking.setPicked(
ctx,
saleFk,
originalQuantity,
code,
isChecked,
buyFk,
isScanned,
quantity,
itemShelvingFk,
options
);
await tx.rollback();
} catch (e) {
const error = e;
expect(error.message).toEqual('The sale cannot be tracked');
await tx.rollback();
}
});
it('should add an itemShelvingSale and Modify a saleTracking', async() => {
const tx = await models.SaleTracking.beginTransaction({});
const options = {transaction: tx};
try {
const itemShelvingSaleBefore = await models.ItemShelvingSale.find({}, options);
await models.SaleTracking.setPicked(
ctx,
saleFk,
originalQuantity,
code,
isChecked,
buyFk,
isScanned,
quantity,
itemShelvingFk,
options
);
const itemShelvingSaleAfter = await models.ItemShelvingSale.find({}, options);
const saleTracking = await models.SaleTracking.findOne({where: {saleFk, isChecked: false}}, options);
expect(itemShelvingSaleAfter.length).toEqual(itemShelvingSaleBefore.length + 1);
expect(saleTracking.isChecked).toBeFalse();
await tx.rollback();
} catch (e) {
await tx.rollback();
}
});
});

View File

@ -0,0 +1,104 @@
const {models} = require('vn-loopback/server/server');
describe('saleTracking updateTracking()', () => {
const saleFk = 1;
const originalQuantity = 10;
const code = 'PREPARED';
const isChecked = true;
const buyFk = 1;
const isScanned = false;
beforeAll(async() => {
ctx = {
req: {
accessToken: {userId: 104},
headers: {origin: 'http://localhost'},
__: value => value
}
};
});
it('should throw an error if the state does not exist', async() => {
const tx = await models.SaleTracking.beginTransaction({});
const options = {transaction: tx};
const code = 'FAKESTATE';
try {
await models.SaleTracking.updateTracking(
ctx,
saleFk,
originalQuantity,
code,
isChecked,
buyFk,
isScanned,
options
);
await tx.rollback();
} catch (e) {
const error = e;
expect(error.message).toEqual('this state does not exist');
await tx.rollback();
}
});
it('should add a new saleTracking and saleBuy', async() => {
const tx = await models.SaleTracking.beginTransaction({});
const options = {transaction: tx};
try {
const saleTrackingBefore = await models.SaleTracking.find(null, options);
const saleBuyBefore = await models.SaleBuy.find(null, options);
await models.SaleTracking.updateTracking(
ctx,
saleFk,
originalQuantity,
code,
isChecked,
buyFk,
isScanned,
options
);
const saleTrackingAfter = await models.SaleTracking.find(null, options);
const saleBuyAfter = await models.SaleBuy.find(null, options);
expect(saleTrackingAfter.length).toEqual(saleTrackingBefore.length + 1);
expect(saleBuyAfter.length).toEqual(saleBuyBefore.length + 1);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should only update a saleTracking', async() => {
const tx = await models.SaleTracking.beginTransaction({});
const options = {transaction: tx};
const saleFk = 2;
try {
const saleTrackingBefore = await models.SaleTracking.find(null, options);
await models.SaleTracking.updateTracking(
ctx,
saleFk,
originalQuantity,
code,
isChecked,
buyFk,
isScanned,
options
);
const saleTrackingAfter = await models.SaleTracking.find(null, options);
expect(saleTrackingAfter.length).toEqual(saleTrackingBefore.length + 1);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -0,0 +1,110 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('updateTracking', {
description: 'Modify a saleTracking record and, if applicable, add a corresponding record in saleBuy.',
accessType: 'WRITE',
accepts: [
{
arg: 'saleFk',
type: 'number',
required: true
},
{
arg: 'originalQuantity',
type: 'number',
required: true
},
{
arg: 'code',
type: 'string',
required: true
},
{
arg: 'isChecked',
type: 'boolean',
required: true
},
{
arg: 'buyFk',
type: 'number',
required: true
},
{
arg: 'isScanned',
type: 'boolean',
},
],
http: {
path: `/updateTracking`,
verb: 'POST'
}
});
Self.updateTracking = async(ctx, saleFk, originalQuantity, code, isChecked, buyFk, isScanned = null, options) => {
const userId = ctx.req.accessToken.userId;
const models = Self.app.models;
const myOptions = {userId};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const state = await models.State.findOne({
where: {code},
}, myOptions);
if (!state) throw new UserError('this state does not exist');
const uniqueAttributes = {
jorgep marked this conversation as resolved Outdated
Outdated
Review

No hace falta usar $t, UserError traduce por defecto.

No hace falta usar $t, UserError traduce por defecto.
saleFk,
workerFk: userId,
stateFk: state?.id,
};
const attributes = {
isChecked,
originalQuantity,
isScanned
};
jorgep marked this conversation as resolved Outdated
Outdated
Review

Tambien se puede hacer isScanned: !!isScanned y te ahorras el valor por defecto.
Pero no creo que haya una preferencia de hacerlo de una manera o de otra

Tambien se puede hacer `isScanned: !!isScanned` y te ahorras el valor por defecto. Pero no creo que haya una preferencia de hacerlo de una manera o de otra

En la tabla saleTracking -> el valor por defecto is NULL, no es solo true o false. Lo comenté con @sergiodt y quedamos así.

En la tabla saleTracking -> el valor por defecto is NULL, no es solo true o false. Lo comenté con @sergiodt y quedamos así.
const saleTracking = await models.SaleTracking.findOne({
where: uniqueAttributes,
}, myOptions);
if (!saleTracking) {
await models.SaleTracking.create({
...uniqueAttributes,
...attributes
}, myOptions);
} else {
await saleTracking.updateAttributes({
...attributes
}, myOptions);
}
let isBuy;
if (buyFk) {
isBuy = await models.Buy.findOne({
where: {
id: buyFk,
itemOriginalFk: {
neq: null
}
}
}, myOptions);
}
if (isBuy)
await models.SaleBuy.create({saleFk, buyFk}, myOptions);
if (tx) await tx.commit();
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,61 @@
module.exports = Self => {
Self.remoteMethodCtx('getFromSectorCollection', {
jgallego marked this conversation as resolved
Review

confirma con @sergiodt que esto pasa así a salix, o se separa..o hay alguna tarea para separarlo

confirma con @sergiodt que esto pasa así a salix, o se separa..o hay alguna tarea para separarlo
Review

@jgallego Separarlo en quin sentit ? És el back que trau les linies de previa amb les seues ubicacions.....

@jgallego Separarlo en quin sentit ? És el back que trau les linies de previa amb les seues ubicacions.....
Review

Crec que m'he confós tenia en ment allò de ticket or collection, tot clar.

Crec que m'he confós tenia en ment allò de ticket or collection, tot clar.
description: 'Get sales from sector collection',
accessType: 'READ',
accepts: [
{
arg: 'sectorCollectionFk',
type: 'number',
required: true,
},
{
arg: 'sectorFk',
type: 'number',
required: true
}
],
returns: {
type: ['object'],
root: true
},
http: {
path: `/getFromSectorCollection`,
verb: 'GET'
},
});
Self.getFromSectorCollection = async(ctx, sectorCollectionFk, sectorFk, options) => {
const userId = ctx.req.accessToken.userId;
const myOptions = {userId};
if (typeof options == 'object') Object.assign(myOptions, options);
const [sales] = await Self.rawSql('CALL sectorCollection_getSale(?)', [sectorCollectionFk], myOptions);
const itemShelvings = [];
for (let sale of sales) {
const [carros] = await Self.rawSql(
'CALL vn.itemPlacementSupplyStockGetTargetList(?, ?)',
[sale.itemFk, sectorFk],
myOptions
);
itemShelvings.push({
id: sale.ticketFk,
itemFk: sale.itemFk,
longName: sale.longName,
packingType: sale.itemPackingTypeFk,
subName: sale.subName,
quantity: sale.quantity,
saldo: sale.quantity,
trabajador: sale.workerCode,
idMovimiento: sale.saleFk,
salesPersonFk: sale.salesPersonFk,
picked: sale.pickedQuantity,
carros
});
}
return itemShelvings;
};
};

View File

@ -0,0 +1,23 @@
const {models} = require('vn-loopback/server/server');
describe('sale getFromSectorCollection()', () => {
const sectorCollectionFk = 1;
const sectorFk = 1;
beforeAll(async() => {
ctx = {
req: {
headers: {origin: 'http://localhost'},
accessToken: {userId: 40}
}
};
});
it('should find an item and a shelving', async() => {
const options = {};
const itemShelvings = await models.Sale.getFromSectorCollection(ctx, sectorCollectionFk, sectorFk, options);
expect(itemShelvings.length).toEqual(1);
expect(itemShelvings[0].carros.length).toEqual(1);
});
});

View File

@ -0,0 +1,56 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('addSaleByCode', {
description: 'Add a collection',
accessType: 'WRITE',
accepts: [
{
arg: 'barcode',
jorgep marked this conversation as resolved Outdated

barcode

barcode
type: 'string',
required: true
}, {
arg: 'quantity',
type: 'number',
required: true
}, {
arg: 'ticketFk',
type: 'number',
required: true
}, {
arg: 'warehouseFk',
type: 'number',
required: true
},
],
http: {
path: `/addSaleByCode`,
verb: 'POST'
},
});
Self.addSaleByCode = async(ctx, barcode, quantity, ticketFk, warehouseFk, options) => {
const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const [[item]] = await Self.rawSql('CALL vn.item_getInfo(?,?)', [barcode, warehouseFk], myOptions);
if (!item?.available) throw new UserError('We do not have availability for the selected item');
await Self.rawSql('CALL vn.collection_addItem(?, ?, ?)', [item.id, quantity, ticketFk], myOptions);
jorgep marked this conversation as resolved Outdated

crea redmine, aqui no deberia llamar a collection_addItem sino a remoteMethodCtx('addSale' para reutilizar codigo, y eliminar vn.collection_addItem

crea redmine, aqui no deberia llamar a collection_addItem sino a remoteMethodCtx('addSale' para reutilizar codigo, y eliminar vn.collection_addItem

@sergiodt poniendo addSale como está ahora te sirve?

@sergiodt poniendo addSale como está ahora te sirve?
if (tx) await tx.commit();
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,39 @@
const {models} = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('Ticket addSaleByCode()', () => {
const quantity = 3;
const ticketFk = 13;
const warehouseFk = 1;
beforeAll(async() => {
activeCtx = {
req: {
accessToken: {userId: 9},
headers: {origin: 'http://localhost'},
__: value => value
}
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
});
it('should add a new sale', async() => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const code = '1111111111';
const salesBefore = await models.Sale.find(null, options);
await models.Ticket.addSaleByCode(activeCtx, code, quantity, ticketFk, warehouseFk, options);
const salesAfter = await models.Sale.find(null, options);
expect(salesAfter.length).toEqual(salesBefore.length + 1);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -50,14 +50,17 @@ describe('ticket setDeleted()', () => {
return value; return value;
}; };
const ticketId = 23; const ticketId = 23;
const [sectorCollectionBefore] = await models.Ticket.rawSql(
await models.Ticket.setDeleted(ctx, ticketId, options);
const [sectorCollection] = await models.Ticket.rawSql(
`SELECT COUNT(*) numberRows `SELECT COUNT(*) numberRows
FROM vn.sectorCollection`, [], options); FROM vn.sectorCollection`, [], options);
expect(sectorCollection.numberRows).toEqual(0); await models.Ticket.setDeleted(ctx, ticketId, options);
const [sectorCollectionAfter] = await models.Ticket.rawSql(
`SELECT COUNT(*) numberRows
jorgep marked this conversation as resolved Outdated

ejecuta antes de borrar SELECT COUNT(*) numberRows
FROM vn.sectorCollection`, [], options);
FROM vn.sectorCollection
y así quitas el uno y el test lo haces dinamico

ejecuta antes de borrar SELECT COUNT(*) numberRows FROM vn.sectorCollection`, [], options); FROM vn.sectorCollection y así quitas el uno y el test lo haces dinamico
FROM vn.sectorCollection`, [], options);
expect(sectorCollectionAfter.numberRows).toEqual(sectorCollectionBefore.numberRows - 1);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -65,6 +65,9 @@
"SaleTracking": { "SaleTracking": {
"dataSource": "vn" "dataSource": "vn"
}, },
"SaleBuy": {
"dataSource": "vn"
},
"State": { "State": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -1,5 +1,6 @@
{ {
"name": "ExpeditionPallet", "name": "ExpeditionPallet",
"base": "VnModel",
"options": { "options": {
"mysql": { "mysql": {
"table": "expeditionPallet" "table": "expeditionPallet"
@ -10,13 +11,24 @@
"type": "number", "type": "number",
"id": true, "id": true,
"description": "Identifier" "description": "Identifier"
},
"built": {
jorgep marked this conversation as resolved
Review

al poner la relacion este bloque lo puedes quitar

al poner la relacion este bloque lo puedes quitar
"type": "date"
},
"position": {
"type": "number"
},
"isPrint": {
"type": "number"
} }
}, },
"acls": [{ "relations": {
"accessType": "WRITE", "expeditionTruck": {
"principalType": "ROLE", "type": "belongsTo",
"principalId": "production", "model": "ExpeditionTruck",
"permission": "ALLOW" "foreignKey": "truckFk"
}] }
}
} }

View File

@ -0,0 +1,34 @@
{
"name": "SaleBuy",
"base": "VnModel",
"options": {
"mysql": {
"table": "saleBuy"
}
},
"properties": {
"saleFk": {
"id": true,
"type": "number"
},
jorgep marked this conversation as resolved
Review

esto en que caso es necesario?

esto en que caso es necesario?
Review

@jgallego es necesario en 2 backs de saleTracking: mark y updateTracking. Lo dejo? O prefieres un rawSql?

@jgallego es necesario en 2 backs de saleTracking: mark y updateTracking. Lo dejo? O prefieres un rawSql?
Review

me refiero al parametro "forceId": false

me refiero al parametro "forceId": false
Review

Lo quito, creo que lo puse al principio se me olvido poner id: true

Lo quito, creo que lo puse al principio se me olvido poner id: true
"buyFk": {
"type": "number"
},
"created": {
"type": "date"
}
},
"relations": {
"sale": {
"type": "belongsTo",
"model": "Sale",
"foreignKey": "saleFk"
},
"worker": {
"type": "belongsTo",
"model": "Worker",
"foreignKey": "workerFk"
}
}
}

View File

@ -3,4 +3,6 @@ module.exports = Self => {
require('../methods/sale-tracking/listSaleTracking')(Self); require('../methods/sale-tracking/listSaleTracking')(Self);
require('../methods/sale-tracking/new')(Self); require('../methods/sale-tracking/new')(Self);
require('../methods/sale-tracking/delete')(Self); require('../methods/sale-tracking/delete')(Self);
require('../methods/sale-tracking/updateTracking')(Self);
require('../methods/sale-tracking/setPicked')(Self);
}; };

View File

@ -26,6 +26,9 @@
}, },
"originalQuantity": { "originalQuantity": {
"type": "number" "type": "number"
},
"isScanned": {
"type": "number"
} }
}, },
"relations": { "relations": {

View File

@ -12,6 +12,7 @@ module.exports = Self => {
require('../methods/sale/canEdit')(Self); require('../methods/sale/canEdit')(Self);
require('../methods/sale/usesMana')(Self); require('../methods/sale/usesMana')(Self);
require('../methods/sale/clone')(Self); require('../methods/sale/clone')(Self);
require('../methods/sale/getFromSectorCollection')(Self);
Self.validatesPresenceOf('concept', { Self.validatesPresenceOf('concept', {
message: `Concept cannot be blank` message: `Concept cannot be blank`

View File

@ -1,4 +1,5 @@
module.exports = Self => { module.exports = Self => {
require('./ticket-methods')(Self); require('./ticket-methods')(Self);
require('../methods/ticket/state')(Self); require('../methods/ticket/state')(Self);
require('../methods/ticket/addSaleByCode')(Self);
}; };

View File

@ -100,7 +100,7 @@ class Controller extends Section {
saleTrackingDel(sale, stateCode) { saleTrackingDel(sale, stateCode) {
const params = { const params = {
saleFk: sale.saleFk, saleFk: sale.saleFk,
stateCode: stateCode stateCodes: [stateCode]
}; };
this.$http.post(`SaleTrackings/delete`, params).then(() => { this.$http.post(`SaleTrackings/delete`, params).then(() => {
this.vnApp.showSuccess(this.$t('Data saved!')); this.vnApp.showSuccess(this.$t('Data saved!'));

View File

@ -53,6 +53,9 @@
"Time": { "Time": {
"dataSource": "vn" "dataSource": "vn"
}, },
"WorkerAppTester": {
"dataSource": "vn"
},
"WorkCenter": { "WorkCenter": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -1,4 +1,4 @@
module.exports = function(Self) { module.exports = Self => {
Self.observe('after save', async function(ctx) { Self.observe('after save', async function(ctx) {
const instance = ctx.data || ctx.instance; const instance = ctx.data || ctx.instance;
const models = Self.app.models; const models = Self.app.models;

View File

@ -43,6 +43,12 @@
"type": "belongsTo", "type": "belongsTo",
"model": "Printer", "model": "Printer",
"foreignKey": "labelerFk" "foreignKey": "labelerFk"
},
"itemPackingType": {
"type": "belongsTo",
"model": "ItemPackingType",
"foreignKey": "itemPackingTypeFk",
"primaryKey": "code"
} }
} }
} }

View File

@ -0,0 +1,22 @@
{
"name": "WorkerAppTester",
"base": "VnModel",
"options": {
"mysql": {
"table": "vn.workerAppTester"
}
},
"properties": {
"workerFk": {
"id": true,
"type": "number"
}
},
"relations": {
"worker": {
"type": "belongsTo",
"model": "Worker",
"foreignKey": "workerFk"
}
}
}