Merge branch 'dev' of https: refs #7031//gitea.verdnatura.es/verdnatura/salix into 7031-zonePrice
This commit is contained in:
commit
535b04e1df
|
@ -7,7 +7,8 @@ def RUN_BUILD
|
|||
|
||||
def BRANCH_ENV = [
|
||||
test: 'test',
|
||||
master: 'production'
|
||||
master: 'production',
|
||||
beta: 'production'
|
||||
]
|
||||
|
||||
node {
|
||||
|
@ -18,7 +19,8 @@ node {
|
|||
PROTECTED_BRANCH = [
|
||||
'dev',
|
||||
'test',
|
||||
'master'
|
||||
'master',
|
||||
'beta'
|
||||
].contains(env.BRANCH_NAME)
|
||||
|
||||
FROM_GIT = env.JOB_NAME.startsWith('gitea/')
|
||||
|
@ -62,6 +64,18 @@ pipeline {
|
|||
PROJECT_NAME = 'salix'
|
||||
}
|
||||
stages {
|
||||
stage('Version') {
|
||||
when {
|
||||
expression { RUN_BUILD }
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
def packageJson = readJSON file: 'package.json'
|
||||
def version = "${packageJson.version}-build${env.BUILD_ID}"
|
||||
writeFile(file: 'VERSION.txt', text: version)
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Install') {
|
||||
environment {
|
||||
NODE_ENV = ''
|
||||
|
@ -118,11 +132,10 @@ pipeline {
|
|||
when {
|
||||
expression { RUN_BUILD }
|
||||
}
|
||||
environment {
|
||||
VERSION = readFile 'VERSION.txt'
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
def packageJson = readJSON file: 'package.json'
|
||||
env.VERSION = "${packageJson.version}-build${env.BUILD_ID}"
|
||||
}
|
||||
sh 'docker-compose build back'
|
||||
}
|
||||
}
|
||||
|
@ -156,11 +169,10 @@ pipeline {
|
|||
when {
|
||||
expression { RUN_BUILD }
|
||||
}
|
||||
environment {
|
||||
VERSION = readFile 'VERSION.txt'
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
def packageJson = readJSON file: 'package.json'
|
||||
env.VERSION = "${packageJson.version}-build${env.BUILD_ID}"
|
||||
}
|
||||
sh 'gulp build'
|
||||
sh 'docker-compose build front'
|
||||
}
|
||||
|
@ -175,12 +187,9 @@ pipeline {
|
|||
}
|
||||
environment {
|
||||
CREDENTIALS = credentials('docker-registry')
|
||||
VERSION = readFile 'VERSION.txt'
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
def packageJson = readJSON file: 'package.json'
|
||||
env.VERSION = "${packageJson.version}-build${env.BUILD_ID}"
|
||||
}
|
||||
sh 'docker login --username $CREDENTIALS_USR --password $CREDENTIALS_PSW $REGISTRY'
|
||||
sh 'docker-compose push'
|
||||
}
|
||||
|
@ -207,11 +216,10 @@ pipeline {
|
|||
when {
|
||||
expression { FROM_GIT }
|
||||
}
|
||||
environment {
|
||||
VERSION = readFile 'VERSION.txt'
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
def packageJson = readJSON file: 'package.json'
|
||||
env.VERSION = "${packageJson.version}-build${env.BUILD_ID}"
|
||||
}
|
||||
withKubeConfig([
|
||||
serverUrl: "$KUBERNETES_API",
|
||||
credentialsId: 'kubernetes',
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
const axios = require('axios');
|
||||
const UserError = require('vn-loopback/util/user-error');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('sendOrders', {
|
||||
description: 'Sends a set of orders',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'tickets',
|
||||
type: ['number'],
|
||||
required: true
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'string',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/sendOrders`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
Self.sendOrders = async tickets => {
|
||||
const config = await Self.app.models.QuadmindsApiConfig.findOne();
|
||||
if (!config) throw new UserError('Config params not set');
|
||||
|
||||
if (tickets.length > config.maxObjects)
|
||||
throw new UserError(`Quadminds does not support more than ${config.maxObjects} tickets`);
|
||||
|
||||
let poisData = [];
|
||||
let isOk;
|
||||
for (let offset = 0; !isOk; offset = offset + config.limit) {
|
||||
const pois = await axios.get(`${config.url}pois/search?limit=${config.limit}&offset=${offset}`, {
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'X-Saas-Apikey': config.key
|
||||
}
|
||||
});
|
||||
pois.data.data.length ? poisData.push(...pois.data.data) : isOk = true;
|
||||
}
|
||||
|
||||
const poiMap = new Map(poisData.map(poi => [poi.code, poi._id]));
|
||||
|
||||
let orders = await Self.rawSql(`
|
||||
SELECT a.id poiCode,
|
||||
t.id code,
|
||||
t.shipped date,
|
||||
'PEDIDO' operation,
|
||||
t.totalWithVat totalAmount,
|
||||
t.totalWithoutVat totalAmountWithoutTaxes,
|
||||
SUM(sv.volume) volume
|
||||
FROM ticket t
|
||||
JOIN address a ON a.id = t.addressFk
|
||||
JOIN saleVolume sv ON sv.ticketFk = t.id
|
||||
WHERE t.id IN (?)
|
||||
GROUP BY t.id
|
||||
`, [tickets]);
|
||||
|
||||
// Transformo code en string ya que lo obtenermos como integer
|
||||
orders = orders.map(order => {
|
||||
return {
|
||||
...order,
|
||||
poiId: poiMap.get(order.poiCode.toString()) || undefined,
|
||||
code: order.code.toString(),
|
||||
date: moment(order.date).format('YYYY-MM-DD'),
|
||||
totalAmount: order.totalAmount || undefined,
|
||||
totalAmountWithoutTaxes: order.totalAmountWithoutTaxes || undefined,
|
||||
timeWindow: [{
|
||||
from: config.orderTimeFrom,
|
||||
to: config.orderTimeTo
|
||||
}],
|
||||
orderMeasures: [{
|
||||
constraintId: 3, // Volumen
|
||||
value: order.volume
|
||||
}]
|
||||
};
|
||||
});
|
||||
|
||||
await axios.post(`${config.url}orders`, orders, {
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'X-Saas-Apikey': config.key
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
|
@ -1,87 +0,0 @@
|
|||
const axios = require('axios');
|
||||
const UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('sendPois', {
|
||||
description: 'Sends a set of pois',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'tickets',
|
||||
type: ['number'],
|
||||
required: true
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'string',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/sendPois`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
Self.sendPois = async tickets => {
|
||||
const config = await Self.app.models.QuadmindsApiConfig.findOne();
|
||||
if (!config) throw new UserError('Config params not set');
|
||||
|
||||
if (tickets.length > config.maxObjects)
|
||||
throw new UserError(`Quadminds does not support more than ${config.maxObjects} tickets`);
|
||||
|
||||
let pois = await Self.rawSql(`
|
||||
WITH deliveryNotes AS (
|
||||
SELECT t.id, t.routeFk, tn.description
|
||||
FROM ticket t
|
||||
JOIN ticketObservation tn ON tn.ticketFk = t.id
|
||||
JOIN observationType ot ON ot.id = tn.observationTypeFk
|
||||
WHERE ot.code = 'delivery'
|
||||
)
|
||||
SELECT a.id code,
|
||||
c.socialName name,
|
||||
IF(ABS(a.latitude - ROUND(a.latitude)) < 0.000001, NULL, a.latitude) latitude,
|
||||
IF(ABS(a.longitude - ROUND(a.longitude)) < 0.000001, NULL, a.longitude) longitude,
|
||||
a.street,
|
||||
a.city locality,
|
||||
p.name state,
|
||||
co.name country,
|
||||
CONCAT_WS(', ', IFNULL(a.street, ''), IFNULL(a.city, ''), IFNULL(p.name, '')) longAddress,
|
||||
CONCAT(IFNULL(a.mobile, c.mobile)) phoneNumber,
|
||||
dn.description poiDeliveryComments,
|
||||
c.email email
|
||||
FROM ticket t
|
||||
JOIN address a ON a.id = t.addressFk
|
||||
JOIN province p ON p.id = a.provinceFk
|
||||
JOIN country co ON co.id = p.countryFk
|
||||
JOIN client c ON c.id = t.clientFk
|
||||
LEFT JOIN deliveryNotes dn ON dn.id = t.id
|
||||
WHERE t.id IN (?)
|
||||
GROUP BY t.id
|
||||
`, [tickets]);
|
||||
|
||||
// Transformo code en string ya que lo obtenermos como integer
|
||||
pois = pois.map(poi => {
|
||||
return {
|
||||
...poi,
|
||||
code: poi.code.toString(),
|
||||
latitude: poi.latitude || undefined,
|
||||
longitude: poi.longitude || undefined,
|
||||
address: {
|
||||
street: poi.street || undefined,
|
||||
locality: poi.locality || undefined,
|
||||
state: poi.state || undefined,
|
||||
country: poi.country || undefined
|
||||
},
|
||||
poiDeliveryComments: poi.poiDeliveryComments || undefined,
|
||||
phoneNumber: poi.phoneNumber || undefined,
|
||||
email: poi.email || undefined
|
||||
};
|
||||
});
|
||||
|
||||
await axios.post(`${config.url}pois`, pois, {
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'X-Saas-Apikey': config.key
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
|
@ -121,9 +121,6 @@
|
|||
"Province": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"QuadmindsApiConfig": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Autonomy": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
},
|
||||
"backupPrinterNotificationDelay": {
|
||||
"type": "string"
|
||||
},
|
||||
"itemOrderReviewHours": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
module.exports = Self => {
|
||||
require('../methods/quadminds-api-config/sendPois')(Self);
|
||||
require('../methods/quadminds-api-config/sendOrders')(Self);
|
||||
};
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
"name": "QuadmindsApiConfig",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "quadmindsApiConfig"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"id": true,
|
||||
"required": true
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"maxObjects": {
|
||||
"type": "number"
|
||||
},
|
||||
"limit": {
|
||||
"type": "number"
|
||||
},
|
||||
"orderTimeFrom": {
|
||||
"type": "string"
|
||||
},
|
||||
"orderTimeTo": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3772,7 +3772,8 @@ VALUES
|
|||
(999992, 18, 50, '2023-09-21', NULL, 1, NULL, 103, NULL),
|
||||
(1000000, 18, 25, '2023-09-21', 25, 500, NULL, 103, NULL),
|
||||
(999996, 19, 5, '2023-09-27', 1, 5, NULL, 103, NULL),
|
||||
(999997, 21, 10, '2023-09-27', 5, 100, NULL, 103, NULL);
|
||||
(999997, 21, 10, '2023-09-27', 5, 100, NULL, 103, NULL),
|
||||
(1000000, 16, 25, '2023-08-21',25, 500, NULL, NULL, NULL);
|
||||
|
||||
-- Previous for Bolas de madera
|
||||
INSERT IGNORE INTO vn.sectorCollection
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
CREATE OR REPLACE DEFINER=`root`@`localhost`
|
||||
SQL SECURITY DEFINER
|
||||
VIEW `bi`.`rotacion`
|
||||
AS SELECT `ic`.`itemFk` AS `Id_Article`,
|
||||
`ic`.`warehouseFk` AS `warehouse_id`,
|
||||
`ic`.`quantity` AS `total`,
|
||||
`ic`.`rotation` AS `rotacion`,
|
||||
`ic`.`cm3` AS `cm3`,
|
||||
`ic`.`storage` AS `almacenaje`,
|
||||
`ic`.`handling` AS `manipulacion`,
|
||||
`ic`.`extraCharge` AS `auxiliar`,
|
||||
`ic`.`wasted` AS `mermas`,
|
||||
`ic`.`cm3delivery` AS `cm3reparto`,
|
||||
`ic`.`grams` AS `grams`
|
||||
FROM `vn`.`itemCost` `ic`
|
|
@ -0,0 +1,2 @@
|
|||
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
|
||||
VALUES ('Worker','canCreateAbsenceInPast','WRITE','ALLOW','ROLE','hr');
|
|
@ -0,0 +1,5 @@
|
|||
-- Place your SQL code here
|
||||
|
||||
|
||||
ALTER TABLE vn.productionConfig ADD itemOrderReviewHours int(11) DEFAULT 24 NULL
|
||||
COMMENT 'Horas que no se tienen en cuenta para comprobar orden en el almacén, null para desactivar revisión';
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE vn.quadmindsApiConfig;
|
|
@ -246,7 +246,8 @@
|
|||
"ticketLostExpedition": "The ticket [{{ticketId}}]({{{ticketUrl}}}) has the following lost expedition:{{ expeditionId }}",
|
||||
"The raid information is not correct": "The raid information is not correct",
|
||||
"Payment method is required": "Payment method is required",
|
||||
"Sales already moved": "Sales already moved",
|
||||
"Sales already moved": "Sales already moved",
|
||||
"Holidays to past days not available": "Holidays to past days not available",
|
||||
"There are tickets to be invoiced": "There are tickets to be invoiced for this zone, please delete them first",
|
||||
"Price cannot be blank": "Price cannot be blank"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -391,5 +391,7 @@
|
|||
"Sales already moved": "Ya han sido transferidas",
|
||||
"The raid information is not correct": "La información de la redada no es correcta",
|
||||
"There are tickets to be invoiced": "Hay tickets para esta zona, borralos primero",
|
||||
"Price cannot be blank": "Price cannot be blank"
|
||||
}
|
||||
"Price cannot be blank": "Price cannot be blank",
|
||||
"An item type with the same code already exists": "Un tipo con el mismo código ya existe",
|
||||
"Holidays to past days not available": "Las vacaciones a días pasados no están disponibles"
|
||||
}
|
||||
|
|
|
@ -9,6 +9,10 @@ module.exports = Self => {
|
|||
required: true,
|
||||
description: 'The entry id',
|
||||
http: {source: 'path'}
|
||||
}, {
|
||||
arg: 'showEntryLines',
|
||||
type: 'boolean',
|
||||
required: false
|
||||
}
|
||||
],
|
||||
returns: [
|
||||
|
|
|
@ -47,8 +47,7 @@ module.exports = Self => {
|
|||
|
||||
for (const buy of buys) {
|
||||
if (buy.stickers < 1) continue;
|
||||
ctx.args.id = buy.id;
|
||||
ctx.args.copies = buy.stickers;
|
||||
ctx.args = {...ctx.args, id: buy.id, showEntryLines: true};
|
||||
const pdfBuffer = await models.Entry.buyLabelSupplier(ctx, myOptions);
|
||||
await merger.add(new Uint8Array(pdfBuffer[0]));
|
||||
}
|
||||
|
|
|
@ -14,6 +14,12 @@ module.exports = Self => {
|
|||
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||
http: {source: 'query'}
|
||||
},
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
description: 'The invoiceOut id',
|
||||
http: {source: 'query'}
|
||||
},
|
||||
{
|
||||
arg: 'search',
|
||||
type: 'string',
|
||||
|
@ -106,6 +112,8 @@ module.exports = Self => {
|
|||
return {'i.created': value};
|
||||
case 'clientFk':
|
||||
return {'i.clientFk': value};
|
||||
case 'id':
|
||||
return {'i.id': value};
|
||||
case 'fi':
|
||||
return {'c.fi': value};
|
||||
case 'amount':
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
name: Minimum Quantity
|
||||
columns:
|
||||
ended: Ended
|
||||
code: Code
|
||||
started: Started
|
|
@ -0,0 +1,5 @@
|
|||
name: Cantidad Mínima
|
||||
columns:
|
||||
ended: Finaliza
|
||||
quantity: Cantidad
|
||||
started: Comienza
|
|
@ -46,4 +46,4 @@ columns:
|
|||
itemFk: item
|
||||
density: density
|
||||
compression: compression
|
||||
|
||||
minQuantity: min quantity
|
||||
|
|
|
@ -46,4 +46,4 @@ columns:
|
|||
itemFk: artículo
|
||||
density: densidad
|
||||
compression: compresión
|
||||
|
||||
minQuantity: Cantidad mínima
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
module.exports = Self => {
|
||||
const models = require('vn-loopback/server/server').models;
|
||||
Self.remoteMethod('getItemsByReviewOrder', {
|
||||
description:
|
||||
'Get list items if they are ordered by pickingOrder and their created regarding where they will be parked',
|
||||
accessType: 'READ',
|
||||
accepts: [{
|
||||
arg: 'shelving',
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Shelving code'
|
||||
},
|
||||
{
|
||||
arg: 'parking',
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Parking code'
|
||||
},
|
||||
{
|
||||
arg: 'itemFk',
|
||||
type: 'number',
|
||||
description: 'Item id'
|
||||
},
|
||||
],
|
||||
returns: {
|
||||
type: 'Array',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/getItemsByReviewOrder`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getItemsByReviewOrder = async(shelving, parking, itemFk, options) => {
|
||||
const myOptions = {};
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const config = await models.ProductionConfig.findOne();
|
||||
const hoursToCompare = config['itemOrderReviewHours'];
|
||||
if (!hoursToCompare) return [];
|
||||
|
||||
const parkingItem = await models.Parking.findOne({where: {code: parking}}, myOptions);
|
||||
if (!parkingItem) return [];
|
||||
const pickingOrderToCompare = parkingItem['pickingOrder'];
|
||||
|
||||
const result = await Self.rawSql(`
|
||||
WITH currentItemShelving AS (
|
||||
SELECT is2.created, is2.itemFk, sh.code
|
||||
FROM vn.itemShelving is2
|
||||
JOIN vn.shelving sh ON sh.id = is2.shelvingFk
|
||||
LEFT JOIN vn.parking p ON p.id = sh.parkingFk
|
||||
LEFT JOIN vn.sector s ON s.id = p.sectorFk
|
||||
WHERE sh.code = ? AND (? IS NULL OR is2.itemFk = ?)
|
||||
),
|
||||
itemShelvings AS (
|
||||
SELECT is2.itemFk, is2.created, sh.code, p.pickingOrder, p.code AS parkingFk
|
||||
FROM vn.itemShelving is2
|
||||
JOIN currentItemShelving ai ON is2.itemFk = ai.itemFk
|
||||
JOIN vn.shelving sh ON sh.id = is2.shelvingFk AND ai.code <> sh.code
|
||||
JOIN vn.parking p ON p.id = sh.parkingFk
|
||||
JOIN vn.sector s ON s.id = p.sectorFk
|
||||
),
|
||||
parkingDestiny AS (
|
||||
SELECT ? AS pickingOrder
|
||||
)
|
||||
SELECT ish.*,
|
||||
CASE
|
||||
WHEN ish.pickingOrder < d.pickingOrder AND aish.created < ish.created
|
||||
AND ABS(TIMESTAMPDIFF(HOUR, aish.created, ish.created)) > ? THEN "old"
|
||||
WHEN ish.pickingOrder > d.pickingOrder AND aish.created > ish.created
|
||||
AND ABS(TIMESTAMPDIFF(HOUR, aish.created, ish.created)) > ? THEN "new"
|
||||
END AS itemCreated
|
||||
FROM itemShelvings ish
|
||||
JOIN parkingDestiny d ON d.pickingOrder IS NOT NULL
|
||||
JOIN currentItemShelving aish ON ish.itemFk = aish.itemFk
|
||||
WHERE ABS(TIMESTAMPDIFF(HOUR, aish.created, ish.created)) > ?
|
||||
AND (
|
||||
(ish.pickingOrder < d.pickingOrder AND aish.created < ish.created)
|
||||
OR
|
||||
(ish.pickingOrder > d.pickingOrder AND aish.created > ish.created)
|
||||
);
|
||||
`,
|
||||
[shelving, itemFk, itemFk, pickingOrderToCompare,
|
||||
hoursToCompare, hoursToCompare, hoursToCompare, hoursToCompare], myOptions);
|
||||
return result;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,123 @@
|
|||
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('itemShelving getItemsByReviewOrder()', () => {
|
||||
it('should return empty because hoursToReview = 0', async() => {
|
||||
const shelving = 'NBB';
|
||||
const parking = '700-01';
|
||||
|
||||
const tx = await models.Sector.beginTransaction({});
|
||||
const myOptions = {transaction: tx};
|
||||
|
||||
try {
|
||||
const config = await models.ProductionConfig.findOne();
|
||||
await config.updateAttributes({
|
||||
itemOrderReviewHours: null,
|
||||
});
|
||||
|
||||
const result = await models.ItemShelving.getItemsByReviewOrder(shelving, parking, myOptions);
|
||||
|
||||
expect(result.length).toEqual(0);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return an item because you are trying parking a shelving and there is an older item', async() => {
|
||||
const shelving = 'NBB';
|
||||
const parking = 'K-26-2';
|
||||
const itemFk = 1000000;
|
||||
|
||||
const tx = await models.Sector.beginTransaction({});
|
||||
const myOptions = {transaction: tx};
|
||||
|
||||
try {
|
||||
const config = await models.ProductionConfig.findOne();
|
||||
await config.updateAttributes({
|
||||
itemOrderReviewHours: 24,
|
||||
});
|
||||
|
||||
const result = await models.ItemShelving.getItemsByReviewOrder(shelving, parking, itemFk, myOptions);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return an item because you are trying parking a shelving and there is an newer item', async() => {
|
||||
const shelving = 'NBB';
|
||||
const parking = 'K-26-2';
|
||||
const itemFk = 1000000;
|
||||
|
||||
const tx = await models.Sector.beginTransaction({});
|
||||
const myOptions = {transaction: tx};
|
||||
|
||||
try {
|
||||
const config = await models.ProductionConfig.findOne();
|
||||
await config.updateAttributes({
|
||||
itemOrderReviewHours: 24,
|
||||
});
|
||||
|
||||
const result = await models.ItemShelving.getItemsByReviewOrder(shelving, parking, itemFk, myOptions);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return a item list because you are trying parking a shelving and there is an newer item', async() => {
|
||||
const shelving = 'NCC';
|
||||
const parking = 'K-26-2';
|
||||
const itemFk = 1000000;
|
||||
|
||||
const tx = await models.Sector.beginTransaction({});
|
||||
const myOptions = {transaction: tx};
|
||||
|
||||
try {
|
||||
const config = await models.ProductionConfig.findOne();
|
||||
await config.updateAttributes({
|
||||
itemOrderReviewHours: 24,
|
||||
});
|
||||
|
||||
const result = await models.ItemShelving.getItemsByReviewOrder(shelving, parking, itemFk, myOptions);
|
||||
|
||||
expect(result.length).toEqual(2);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return empty list because all order is correct', async() => {
|
||||
const shelving = 'NCC';
|
||||
const parking = 'A-01-1';
|
||||
const itemFk = 1000000;
|
||||
|
||||
const tx = await models.Sector.beginTransaction({});
|
||||
const myOptions = {transaction: tx};
|
||||
|
||||
try {
|
||||
const config = await models.ProductionConfig.findOne();
|
||||
await config.updateAttributes({
|
||||
itemOrderReviewHours: 24,
|
||||
});
|
||||
|
||||
const result = await models.ItemShelving.getItemsByReviewOrder(shelving, parking, itemFk, myOptions);
|
||||
|
||||
expect(result.length).toEqual(0);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -5,4 +5,5 @@ module.exports = Self => {
|
|||
require('../methods/item-shelving/getAlternative')(Self);
|
||||
require('../methods/item-shelving/updateFromSale')(Self);
|
||||
require('../methods/item-shelving/getListItemNewer')(Self);
|
||||
require('../methods/item-shelving/getItemsByReviewOrder')(Self);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
let UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.rewriteDbError(function(err) {
|
||||
if (err.code === 'ER_DUP_ENTRY')
|
||||
return new UserError(`An item type with the same code already exists`);
|
||||
return err;
|
||||
});
|
||||
};
|
|
@ -88,6 +88,11 @@ module.exports = Self => {
|
|||
arg: 'alertLevel',
|
||||
type: 'number',
|
||||
description: `The alert level of the tickets`
|
||||
},
|
||||
{
|
||||
arg: 'countryFk',
|
||||
type: 'number',
|
||||
description: 'The country id filter'
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
|
@ -182,6 +187,7 @@ module.exports = Self => {
|
|||
t.totalWithVat,
|
||||
io.id invoiceOutId,
|
||||
a.provinceFk,
|
||||
p.countryFk,
|
||||
p.name province,
|
||||
w.name warehouse,
|
||||
am.name agencyMode,
|
||||
|
@ -356,6 +362,7 @@ module.exports = Self => {
|
|||
}
|
||||
case 'agencyModeFk':
|
||||
case 'warehouseFk':
|
||||
case 'countryFk':
|
||||
param = `f.${param}`;
|
||||
return {[param]: value};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||
const buildFilter = require('vn-loopback/util/filter').buildFilter;
|
||||
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
|
||||
|
@ -87,7 +86,7 @@ module.exports = Self => {
|
|||
return /^\d+$/.test(value)
|
||||
? {'t.id': value}
|
||||
: {'t.ref': {like: `%${value}%`}};
|
||||
case 'ref':
|
||||
case 'reference':
|
||||
return {'t.ref': {like: `%${value}%`}};
|
||||
case 'shippedFrom':
|
||||
return {'t.shipped': {gte: value}};
|
||||
|
@ -115,42 +114,39 @@ module.exports = Self => {
|
|||
`CREATE TEMPORARY TABLE tmp.travel
|
||||
(INDEX (id))
|
||||
ENGINE = MEMORY
|
||||
SELECT
|
||||
t.id,
|
||||
SELECT t.id,
|
||||
t.ref,
|
||||
t.shipped,
|
||||
t.landed,
|
||||
t.kg,
|
||||
am.id AS agencyModeFk,
|
||||
am.name AS agencyModeName,
|
||||
wo.id AS warehouseOutFk,
|
||||
wo.name AS warehouseOutName,
|
||||
w.name AS warehouseInFk,
|
||||
w.name AS warehouseInName,
|
||||
SUM(b.stickers) AS stickers,
|
||||
s.id AS cargoSupplierFk,
|
||||
s.nickname AS cargoSupplierNickname,
|
||||
s.name AS supplierName,
|
||||
CAST(SUM(b.weight * b.stickers) as DECIMAL(10,0)) as loadedKg,
|
||||
am.id agencyModeFk,
|
||||
am.name agencyModeName,
|
||||
wo.id warehouseOutFk,
|
||||
wo.name warehouseOutName,
|
||||
w.name warehouseInFk,
|
||||
w.name warehouseInName,
|
||||
SUM(b.stickers) stickers,
|
||||
s.id cargoSupplierFk,
|
||||
s.nickname cargoSupplierNickname,
|
||||
s.name supplierName,
|
||||
CAST(SUM(b.weight * b.stickers) AS DECIMAL(10,0)) loadedKg,
|
||||
CAST(
|
||||
SUM(
|
||||
vc.aerealVolumetricDensity *
|
||||
b.stickers *
|
||||
IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000
|
||||
) as DECIMAL(10,0)
|
||||
) as volumeKg,
|
||||
GREATEST(
|
||||
CAST(SUM(b.weight * b.stickers) AS INT),
|
||||
CAST(
|
||||
) AS DECIMAL(10,0)
|
||||
) volumeKg,
|
||||
CAST(
|
||||
GREATEST(
|
||||
SUM(b.weight * b.stickers) ,
|
||||
SUM(vc.aerealVolumetricDensity *
|
||||
b.stickers *
|
||||
IF(pkg.volume,
|
||||
pkg.volume,
|
||||
pkg.width * pkg.depth * pkg.height
|
||||
) / 1000000
|
||||
) AS INT
|
||||
)
|
||||
/ t.kg * 100, 0) percentageKg
|
||||
IF(pkg.volume,
|
||||
pkg.volume,
|
||||
pkg.width * pkg.depth * pkg.height) / 1000000)
|
||||
) / t.kg * 100 AS INT
|
||||
) percentageKg
|
||||
FROM travel t
|
||||
LEFT JOIN supplier s ON s.id = t.cargoSupplierFk
|
||||
LEFT JOIN entry e ON e.travelFk = t.id
|
||||
|
|
|
@ -58,17 +58,24 @@ module.exports = Self => {
|
|||
if (!isSubordinate || (isSubordinate && userId == id && !isTeamBoss))
|
||||
throw new UserError(`You don't have enough privileges`);
|
||||
|
||||
const canCreateAbsenceInPast =
|
||||
await models.ACL.checkAccessAcl(ctx, 'Worker', 'canCreateAbsenceInPast', 'WRITE');
|
||||
const now = Date.vnNew();
|
||||
const newDate = new Date(args.dated).getTime();
|
||||
|
||||
if ((now.getTime() > newDate) && !canCreateAbsenceInPast)
|
||||
throw new UserError(`Holidays to past days not available`);
|
||||
|
||||
const labour = await models.WorkerLabour.findById(args.businessFk,
|
||||
{fields: ['started', 'ended', 'businessFk']}, myOptions);
|
||||
|
||||
if (args.dated < labour.started || (labour.ended != null && args.dated > labour.ended))
|
||||
throw new UserError(`The contract was not active during the selected date`);
|
||||
|
||||
query = `SELECT *
|
||||
FROM vn.workerTimeControl
|
||||
WHERE userFk = ? AND timed BETWEEN DATE(?) AND CONCAT(DATE(?), ' 23:59:59')
|
||||
LIMIT 1;`;
|
||||
const [hasHoursRecorded] = await Self.rawSql(query, [id, args.dated, args.dated]);
|
||||
const [hasHoursRecorded] = await Self.rawSql(`SELECT *
|
||||
FROM vn.workerTimeControl
|
||||
WHERE userFk = ? AND timed BETWEEN DATE(?) AND CONCAT(DATE(?), ' 23:59:59')
|
||||
LIMIT 1;`, [id, args.dated, args.dated]);
|
||||
|
||||
const absenceType = await models.AbsenceType.findById(args.absenceTypeId, null, myOptions);
|
||||
|
||||
|
@ -80,7 +87,6 @@ module.exports = Self => {
|
|||
throw new UserError(`The worker has hours recorded that day`);
|
||||
|
||||
const date = Date.vnNew();
|
||||
const now = Date.vnNew();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const [result] = await Self.rawSql(
|
||||
`SELECT COUNT(*) halfHolidayCounter
|
||||
|
|
|
@ -162,4 +162,33 @@ describe('Worker createAbsence()', () => {
|
|||
|
||||
expect(error.message).toEqual(`The worker has hours recorded that day`);
|
||||
});
|
||||
|
||||
it(`Should throw an error when adding a "Vacation" absence on a past day`, async() => {
|
||||
const ctx = {
|
||||
req: {accessToken: {userId: 19}},
|
||||
args: {
|
||||
id: 1110,
|
||||
businessFk: 1110,
|
||||
absenceTypeId: 1,
|
||||
dated: '2000-12-27T23:00:00.000Z',
|
||||
}
|
||||
};
|
||||
const workerId = 19;
|
||||
|
||||
const tx = await app.models.Calendar.beginTransaction({});
|
||||
|
||||
let error;
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
await app.models.Worker.createAbsence(ctx, workerId, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(`Holidays to past days not available`);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
<td>
|
||||
<div class="cell">
|
||||
<span class="lbl">{{$t('boxNum')}}</span>
|
||||
{{`${buy.labelNum} / ${buy.maxLabelNum}`}}
|
||||
{{getTotal(buy)}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -9,7 +9,7 @@ module.exports = {
|
|||
mixins: [vnReport],
|
||||
async serverPrefetch() {
|
||||
const buy = await models.Buy.findById(this.id, null);
|
||||
this.buys = await this.rawSqlFromDef('buy', [buy.entryFk, buy.entryFk, buy.entryFk, this.id]);
|
||||
this.buys = await this.rawSqlFromDef('buy', [buy.entryFk, buy.entryFk, buy.entryFk, this.id, this.id]);
|
||||
const date = new Date();
|
||||
this.weekNum = moment(date).isoWeek();
|
||||
this.dayNum = moment(date).day();
|
||||
|
@ -27,6 +27,11 @@ module.exports = {
|
|||
height: 115,
|
||||
});
|
||||
return new XMLSerializer().serializeToString(svgNode);
|
||||
},
|
||||
getTotal(buy) {
|
||||
return (this.showEntryLines) ?
|
||||
`${buy.entryLabelNum} / ${buy.entryLabels}` :
|
||||
`${buy.buyLabelNum} / ${buy.buyLabels}`;
|
||||
}
|
||||
},
|
||||
props: {
|
||||
|
@ -34,6 +39,10 @@ module.exports = {
|
|||
type: Number,
|
||||
required: true,
|
||||
description: 'The entry id'
|
||||
},
|
||||
showEntryLines: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ WITH RECURSIVE numbers AS (
|
|||
)
|
||||
),
|
||||
labels AS (
|
||||
SELECT ROW_NUMBER() OVER(ORDER BY b.id, num.n) labelNum,
|
||||
SELECT ROW_NUMBER() OVER(ORDER BY b.id, num.n) entryLabelNum,
|
||||
i.name,
|
||||
i.`size`,
|
||||
i.category,
|
||||
|
@ -33,6 +33,9 @@ labels AS (
|
|||
WHERE b.entryFk = ?
|
||||
AND num.n <= b.stickers
|
||||
)
|
||||
SELECT *, (SELECT SUM(stickers) FROM buy WHERE entryFk = ?) maxLabelNum
|
||||
SELECT *,
|
||||
ROW_NUMBER() OVER(ORDER BY entryLabelNum) buyLabelNum,
|
||||
(SELECT SUM(stickers) FROM buy WHERE entryFk = ?) entryLabels,
|
||||
(SELECT stickers FROM buy WHERE id = ?) buyLabels
|
||||
FROM labels
|
||||
WHERE id = ?
|
Loading…
Reference in New Issue