Merge branch 'dev' into 5137-aviso-impresora
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Alexandre Riera 2023-06-07 06:30:43 +00:00
commit bfd421c188
72 changed files with 12055 additions and 14043 deletions

View File

@ -24,13 +24,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- (Tickets -> Crear Factura) Al facturar se envia automáticamente el pdf al cliente
- (Artículos -> Histórico) Filtro para mostrar lo anterior al inventario
- (Trabajadores -> Nuevo trabajador) Permite elegir el método de pago
### Changed
- (Trabajadores -> Nuevo trabajador) Los clientes se crean sin 'TR' pero se añade tipo de negocio 'Trabajador'
### Fixed
- (Tickets -> Líneas) Se permite hacer split de líneas al mismo ticket
- (Tickets -> Cambiar estado) Ahora muestra la lista completa de todos los estados

View File

@ -1,9 +1,9 @@
LOAD DATA LOCAL INFILE ?
INTO TABLE `edi`.`item`
CHARACTER SET ascii
FIELDS TERMINATED BY ';'
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8, @col9, @col10, @col11, @col12)
SET
id = @col2,
SET id = @col2,
product_name = @col4,
name = @col5,
plant_id = @col7,
@ -11,3 +11,4 @@ LOAD DATA LOCAL INFILE ?
entry_date = STR_TO_DATE(@col10, '%Y%m%d'),
expiry_date = IFNULL(NULL,STR_TO_DATE(@col11, '%Y%m%d')),
change_date_time = STR_TO_DATE(@col12, '%Y%m%d%H%i')

View File

@ -121,7 +121,8 @@ module.exports = Self => {
host: ftpConfig.host,
username: ftpConfig.user,
password: ftpConfig.password,
procotol: 'ftp'
procotol: 'ftp',
additionalLftpCommands: 'set ssl:verify-certificate no'
});
}
@ -132,7 +133,7 @@ module.exports = Self => {
const ftpClient = await getFtpClient();
console.debug(`Checking checksum for file ${file.name}...`);
ftpClient.cat(`codes/${file.name}.txt`);
ftpClient.cat(`codes/${file.name}.TXT`);
const response = await new Promise((resolve, reject) => {
ftpClient.exec((err, response) => {

View File

@ -56,8 +56,6 @@ CREATE TABLE `vn`.`collectionWagonTicket` (
ALTER TABLE `vn`.`wagon` ADD `typeFk` int(11) unsigned NOT NULL;
ALTER TABLE `vn`.`wagon` ADD `label` int(11) unsigned NOT NULL;
ALTER TABLE `vn`.`wagon` ADD CONSTRAINT `wagon_type` FOREIGN KEY (`typeFk`) REFERENCES `wagonType` (`id`) ON UPDATE CASCADE;
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('WagonType', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
@ -70,3 +68,4 @@ INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `pri
('WagonType', 'createWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi'),
('WagonType', 'deleteWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi'),
('WagonType', 'editWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi');

View File

@ -0,0 +1 @@
ALTER TABLE `vn`.`wagon` ADD CONSTRAINT `wagon_type` FOREIGN KEY (`typeFk`) REFERENCES `wagonType` (`id`) ON UPDATE CASCADE;

View File

@ -1,7 +1,7 @@
ALTER TABLE `vn`.`workerConfig` ADD payMethodFk tinyint(3) unsigned NULL;
ALTER TABLE `vn`.`workerConfig` ADD CONSTRAINT workerConfig_FK FOREIGN KEY (roleFk) REFERENCES account.`role`(id) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `vn`.`workerConfig` ADD CONSTRAINT workerConfig_FK_1 FOREIGN KEY (payMethodFk) REFERENCES `vn`.`payMethod`(id) ON DELETE SET NULL ON UPDATE CASCADE;
-- Cuando se apruebe el PR quitar y poner en redmine para hacerse manualmente
UPDATE `vn`.`workerConfig`
SET payMethodFk = 4
WHERE id=1;

View File

@ -0,0 +1,28 @@
CREATE TABLE `vn`.`buyConfig` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`monthsAgo` int(11) NOT NULL DEFAULT 6 COMMENT 'Meses desde la última compra',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
CREATE TABLE `vn`.`travelConfig` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`warehouseInFk` smallint(6) unsigned NOT NULL DEFAULT 8 COMMENT 'Warehouse de origen',
`warehouseOutFk` smallint(6) unsigned NOT NULL DEFAULT 60 COMMENT 'Warehouse destino',
`agencyFk` int(11) NOT NULL DEFAULT 1378 COMMENT 'Agencia por defecto',
`companyFk` smallint(5) unsigned NOT NULL DEFAULT 442 COMMENT 'Compañía por defecto',
PRIMARY KEY (`id`),
KEY `travelConfig_FK` (`warehouseInFk`),
KEY `travelConfig_FK_1` (`warehouseOutFk`),
KEY `travelConfig_FK_2` (`agencyFk`),
KEY `travelConfig_FK_3` (`companyFk`),
CONSTRAINT `travelConfig_FK` FOREIGN KEY (`warehouseInFk`) REFERENCES `warehouse` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `travelConfig_FK_1` FOREIGN KEY (`warehouseOutFk`) REFERENCES `warehouse` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `travelConfig_FK_2` FOREIGN KEY (`agencyFk`) REFERENCES `agencyMode` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `travelConfig_FK_3` FOREIGN KEY (`companyFk`) REFERENCES `company` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('Entry', 'addFromPackaging', 'WRITE', 'ALLOW', 'ROLE', 'production'),
('Entry', 'addFromBuy', 'WRITE', 'ALLOW', 'ROLE', 'production'),
('Supplier', 'getItemsPackaging', 'READ', 'ALLOW', 'ROLE', 'production');

View File

@ -907,7 +907,7 @@ INSERT INTO `vn`.`itemFamily`(`code`, `description`)
INSERT INTO `vn`.`item`(`id`, `typeFk`, `size`, `inkFk`, `stems`, `originFk`, `description`, `producerFk`, `intrastatFk`, `expenceFk`,
`comment`, `relevancy`, `image`, `subName`, `minPrice`, `stars`, `family`, `isFloramondo`, `genericFk`, `itemPackingTypeFk`, `hasMinPrice`, `packingShelve`, `weightByPiece`)
VALUES
(1, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 1, 'VT', 0, NULL, 'V', 0, 15,3),
(1, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 1, 'EMB', 0, NULL, 'V', 0, 15,3),
(2, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 2, 'VT', 0, NULL, 'H', 0, 10,2),
(3, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 5, 'VT', 0, NULL, NULL, 0, 5,5),
(4, 1, 60, 'YEL', 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL,NULL),
@ -2889,6 +2889,10 @@ INSERT INTO `vn`.`wagonTypeTray` (`id`, `typeFk`, `height`, `colorFk`)
(2, 1, 50, 2),
(3, 1, 0, 3);
INSERT INTO `vn`.`travelConfig` (`id`, `warehouseInFk`, `warehouseOutFk`, `agencyFk`, `companyFk`)
VALUES
(1, 1, 1, 1, 442);
INSERT INTO `vn`.`buyConfig` (`id`, `monthsAgo`)
VALUES
(1, 6);

View File

@ -61950,7 +61950,6 @@ BEGIN
* @vWarehouseFk almacen donde buscar
* @vDate Si la fecha es null, muestra el histórico desde el inventario. Si la fecha no es null, muestra histórico desde la fecha pasada.
*/
DECLARE vDateInventory DATETIME;
DECLARE vInvCalculated INT;

View File

@ -43,7 +43,7 @@ services:
- node.role == worker
resources:
limits:
memory: 4G
memory: 8G
configs:
datasources:
external: true

View File

@ -22,7 +22,8 @@ export async function getBrowser() {
env.E2E_SHOW = true;
}
const headless = !env.E2E_SHOW;
const headless = env.E2E_SHOW ? false : 'new';
const browser = await Puppeteer.launch({
args,
defaultViewport: null,

View File

@ -64,6 +64,6 @@ describe('SmartTable SearchBar integration', () => {
await page.reload({
waitUntil: 'networkidle2'
});
await page.waitForTextInField(selectors.itemFixedPrice.firstItemID, '13');
await page.waitForTextInField(selectors.itemFixedPrice.firstItemID, '3');
});
});

View File

@ -88,7 +88,8 @@ describe('Item fixed prices path', () => {
it('should reload the section and check the created price has the expected ID', async() => {
await page.goto(`http://localhost:5000/#!/item/fixed-price`);
await page.autocompleteSearch($.warehouseFilter, 'Warehouse one');
await page.click($.chip);
const result = await page.waitToGetProperty($.fourthItemID, 'value');
expect(result).toContain('13');

View File

@ -150,10 +150,10 @@ export default class Controller extends Section {
if (value == null || value == '') return null;
switch (prop) {
case 'search':
const or = [{changedModelId: value}];
if (!/^[0-9]+$/.test(value))
or.push({changedModelValue: {like: `%${value}%`}});
return {or};
if (/^[0-9]+$/.test(value))
return {changedModelId: value};
else
return {changedModelValue: {like: `%${value}%`}};
case 'changes':
return {or: [
{oldInstance: {like: `%${value}%`}},

View File

@ -20,8 +20,6 @@ class Controller {
name: config.languages[code] ? config.languages[code] : code
});
}
vnConfig.initialize();
}
set lang(value) {

View File

@ -10,6 +10,9 @@ function config($stateProvider, $urlRouterProvider) {
.state('layout', {
abstract: true,
template: '<vn-layout></vn-layout>',
resolve: {
config: ['vnConfig', vnConfig => vnConfig.initialize()]
}
})
.state('outLayout', {
abstract: true,

View File

@ -1,6 +1,8 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const UserError = require('vn-loopback/util/user-error');
const utils = require('loopback/lib/utils');
const {util} = require('webpack');
module.exports = function(Self) {
Self.ParameterizedSQL = ParameterizedSQL;
@ -164,23 +166,21 @@ module.exports = function(Self) {
function rewriteMethod(methodName) {
const realMethod = this[methodName];
return async(data, options, cb) => {
if (options instanceof Function) {
cb = options;
options = null;
}
return function(...args) {
let cb;
const lastArg = args[args.length - 1];
if (lastArg instanceof Function) {
cb = lastArg;
args.pop();
} else
cb = utils.createPromiseCallback();
try {
const result = await realMethod.call(this, data, options);
if (cb) cb(null, result);
else return result;
} catch (err) {
let myErr = replaceErr(err, replaceErrFunc);
if (cb) cb(myErr);
else
throw myErr;
}
args.push(function(err, res) {
if (err) err = replaceErr(err, replaceErrFunc);
cb(err, res);
});
realMethod.apply(this, args);
return cb.promise;
};
}

View File

@ -15,3 +15,7 @@ columns:
image: image
hasGrant: has grant
userFk: user
recoverPass: recover password
role: role
sync: pending sync
lastPassChange: password changed

View File

@ -15,3 +15,7 @@ columns:
image: imagen
hasGrant: puede delegar
userFk: usuario
recoverPass: recuperar contraseña
role: rol
sync: Pendiente de sincronizar
lastPassChange: contraseña modificada

View File

@ -59,6 +59,12 @@ module.exports = function(Self) {
fields: ['id', 'name']
}
},
{
relation: 'businessType',
scope: {
fields: ['description']
}
},
{
relation: 'account',
scope: {

View File

@ -43,6 +43,10 @@
{{$ctrl.client.salesPersonUser.name}}
</span>
</vn-label-value>
<vn-label-value
label="Business type"
value="{{$ctrl.client.businessType.description}}">
</vn-label-value>
</div>
<div class="icons">
<vn-icon

View File

@ -8,3 +8,4 @@ Client invoices list: Listado de facturas del cliente
Pay method: Forma de pago
Unpaid Dated: "Fecha: {{dated | date:'dd/MM/yyyy'}}"
Unpaid Amount: "Importe: {{amount | currency: 'EUR':2}}"
Business type: Tipo de negocio

View File

@ -34,7 +34,7 @@
<vn-autocomplete vn-one required="true"
label="Warehouse"
ng-model="$ctrl.dms.warehouseId"
data="Warehouses"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>

View File

@ -0,0 +1,107 @@
module.exports = Self => {
Self.remoteMethodCtx('addFromBuy', {
description: 'Modify a field of a buy or creates a new one with default values',
accessType: 'WRITE',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'The entry id',
http: {source: 'path'}
}, {
arg: 'item',
type: 'number',
required: true,
description: 'The item id',
}, {
arg: 'printedStickers',
type: 'number',
required: true,
description: 'The field to modify',
}],
returns: {
type: 'object',
root: true
},
http: {
path: `/:id/addFromBuy`,
verb: 'POST'
}
});
Self.addFromBuy = async(ctx, options) => {
const args = ctx.args;
const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
const myOptions = {userId};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
let buy = await models.Buy.findOne({where: {entryFk: args.id}}, myOptions);
if (buy)
await buy.updateAttribute('printedStickers', args.printedStickers, myOptions);
else {
const userConfig = await models.UserConfig.findById(userId, {fields: ['warehouseFk']}, myOptions);
await Self.rawSql(
'CALL vn.buyUltimate(?,?)',
[userConfig.warehouseFk, null],
myOptions
);
let buyUltimate = await Self.rawSql(
`SELECT buyFk
FROM tmp.buyUltimate
WHERE itemFk = ?`,
[args.item],
myOptions
);
buyUltimate = await models.Buy.findById(buyUltimate[0].buyFk, null, myOptions);
buy = await models.Buy.create({
entryFk: args.id,
itemFk: args.item,
quantity: 0,
dispatched: buyUltimate.dispatched,
buyingValue: buyUltimate.buyingValue,
freightValue: buyUltimate.freightValue,
isIgnored: buyUltimate.isIgnored,
stickers: buyUltimate.stickers,
packing: buyUltimate.packing,
grouping: buyUltimate.grouping,
groupingMode: buyUltimate.groupingMode,
containerFk: buyUltimate.containerFk,
comissionValue: buyUltimate.comissionValue,
packageValue: buyUltimate.packageValue,
location: buyUltimate.location,
packageFk: buyUltimate.packageFk,
price1: buyUltimate.price1,
price2: buyUltimate.price2,
price3: buyUltimate.price3,
minPrice: buyUltimate.minPrice,
printedStickers: args.printedStickers,
workerFk: buyUltimate.workerFk,
isChecked: buyUltimate.isChecked,
isPickedOff: buyUltimate.isPickedOff,
created: buyUltimate.created,
ektFk: buyUltimate.ektFk,
weight: buyUltimate.weight,
deliveryFk: buyUltimate.deliveryFk,
itemOriginalFk: buyUltimate.itemOriginalFk
}, myOptions);
}
if (tx) await tx.commit();
return buy;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,72 @@
module.exports = Self => {
Self.remoteMethodCtx('addFromPackaging', {
description: 'Create a receipt or return entry for a supplier with a specific travel',
accessType: 'WRITE',
accepts: [{
arg: 'supplier',
type: 'number',
required: true,
description: 'The supplier id',
},
{
arg: 'isTravelReception',
type: 'boolean',
required: true,
description: 'Indicates if the travel associated with the entry is a return or receipt travel'
}],
returns: {
type: 'object',
root: true
},
http: {
path: `/addFromPackaging`,
verb: 'POST'
}
});
Self.addFromPackaging = async(ctx, options) => {
const args = ctx.args;
const models = Self.app.models;
let tx;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const travelConfig = await models.TravelConfig.findOne({}, myOptions);
const today = new Date();
const yesterday = new Date(today);
yesterday.setDate(today.getDate() - 1);
const tomorrow = new Date(today);
tomorrow.setDate(today.getDate() + 1);
const travel = await models.Travel.create({
shipped: args.isTravelReception ? yesterday : today,
landed: args.isTravelReception ? today : tomorrow,
agencyModeFk: travelConfig.agencyFk,
warehouseInFk: travelConfig.warehouseOutFk,
warehouseOutFk: travelConfig.warehouseInFk
}, myOptions);
const entry = await models.Entry.create({
supplierFk: args.supplier,
travelFk: travel.id,
companyFk: travelConfig.companyFk
}, myOptions);
if (tx) await tx.commit();
return entry;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,51 @@
const models = require('vn-loopback/server/server').models;
describe('entry addFromBuy()', () => {
const ctx = {req: {accessToken: {userId: 18}}};
it('should change the printedStickers of an existent buy', async() => {
const id = 1;
const item = 1;
const buy = 1;
const tx = await models.Entry.beginTransaction({});
const options = {transaction: tx};
try {
const currentBuy = await models.Buy.findById(buy, {fields: ['printedStickers']}, options);
const printedStickers = currentBuy.printedStickers + 10;
ctx.args = {id, item, printedStickers};
const newBuy = await models.Entry.addFromBuy(ctx, options);
expect(newBuy.printedStickers).toEqual(printedStickers);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should create for an entry without a concrete item a new buy', async() => {
const id = 8;
const item = 1;
const printedStickers = 10;
const tx = await models.Entry.beginTransaction({});
const options = {transaction: tx};
try {
const emptyBuy = await models.Buy.findOne({where: {entryFk: id}}, options);
ctx.args = {id, item, printedStickers};
const newBuy = await models.Entry.addFromBuy(ctx, options);
expect(emptyBuy).toEqual(null);
expect(newBuy.entryFk).toEqual(id);
expect(newBuy.printedStickers).toEqual(printedStickers);
expect(newBuy.itemFk).toEqual(item);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -0,0 +1,49 @@
const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('entry addFromPackaging()', () => {
const supplier = 442;
const today = new Date();
const yesterday = new Date(today);
yesterday.setDate(today.getDate() - 1);
beforeAll(async() => {
const activeCtx = {
accessToken: {userId: 49},
http: {
req: {
headers: {origin: 'http://localhost'},
},
},
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx,
});
});
it('should create an incoming travel', async() => {
const ctx = {args: {isTravelReception: true, supplier}};
const tx = await models.Entry.beginTransaction({});
const options = {transaction: tx};
try {
const entry = await models.Entry.addFromPackaging(ctx, options);
const travelConfig = await models.TravelConfig.findOne({}, options);
const travel = await models.Travel.findOne({order: 'id DESC'}, options);
expect(new Date(travel.shipped).getDate()).toEqual(yesterday.getDate());
expect(new Date(travel.landed).getDate()).toEqual(today.getDate());
expect(travel.agencyModeFk).toEqual(travelConfig.agencyFk);
expect(travel.warehouseInFk).toEqual(travelConfig.warehouseOutFk);
expect(travel.warehouseOutFk).toEqual(travelConfig.warehouseInFk);
expect(entry.supplierFk).toEqual(supplier);
expect(entry.travelFk).toEqual(travel.id);
expect(entry.companyFk).toEqual(travelConfig.companyFk);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -5,6 +5,9 @@
"Buy": {
"dataSource": "vn"
},
"BuyConfig": {
"dataSource": "vn"
},
"ItemMatchProperties": {
"dataSource": "vn"
},

View File

@ -0,0 +1,18 @@
{
"name": "BuyConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "buyConfig"
}
},
"properties": {
"id": {
"type": "number",
"id": true
},
"showLastBuy": {
"type": "number"
}
}
}

View File

@ -39,6 +39,9 @@
"packageValue": {
"type": "number"
},
"price1": {
"type": "number"
},
"price2": {
"type": "number"
},
@ -47,7 +50,44 @@
},
"weight": {
"type": "number"
},
"printedStickers": {
"type": "number"
},
"dispatched": {
"type": "number"
},
"isIgnored": {
"type": "boolean"
},
"containerFk": {
"type": "number"
},
"location": {
"type": "number"
},
"minPrice": {
"type": "number"
},
"isChecked": {
"type": "boolean"
},
"isPickedOff": {
"type": "boolean"
},
"created": {
"type": "date"
},
"ektFk": {
"type": "number"
},
"itemOriginalFk": {
"type": "number"
},
"editorFk": {
"type": "number"
}
},
"relations": {
"entry": {
@ -64,6 +104,16 @@
"type": "belongsTo",
"model": "Packaging",
"foreignKey": "packageFk"
},
"worker": {
"type": "belongsTo",
"model": "Worker",
"foreignKey": "workerFk"
},
"delivery": {
"type": "belongsTo",
"model": "Delivery",
"foreignKey": "deliveryFk"
}
}
}

View File

@ -8,6 +8,8 @@ module.exports = Self => {
require('../methods/entry/importBuysPreview')(Self);
require('../methods/entry/lastItemBuys')(Self);
require('../methods/entry/entryOrderPdf')(Self);
require('../methods/entry/addFromPackaging')(Self);
require('../methods/entry/addFromBuy')(Self);
Self.observe('before save', async function(ctx, options) {
if (ctx.isNewInstance) return;

View File

@ -157,14 +157,14 @@
<vn-autocomplete
label="Warehouse Out"
ng-model="$ctrl.travelFilterParams.warehouseOutFk"
data="Warehouses"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete
label="Warehouse In"
ng-model="$ctrl.travelFilterParams.warehouseInFk"
data="Warehouses"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>

View File

@ -1,4 +1,4 @@
name: botanical
name: botanical data
columns:
itemFk: item
genusFk: genus

View File

@ -1,4 +1,4 @@
name: botánico
name: datos botánicos
columns:
itemFk: artículo
genusFk: género

View File

@ -35,7 +35,7 @@ columns:
packingOut: packing out
hasMinPrice: has min price
isFragile: fragile
isFloramondo: is floramondo
isFloramondo: floramondo
packingShelve: packing shelve
isLaid: laid
inkFk: ink

View File

@ -35,7 +35,7 @@ columns:
packingOut: empaquetar
hasMinPrice: tiene precio mínimo
isFragile: frágil
isFloramondo: es floramondo
isFloramondo: floramondo
packingShelve: estantería embalaje
isLaid: puesto
inkFk: tinta

View File

@ -88,7 +88,7 @@
vn-one
label="Warehouse"
ng-model="filter.warehouseFk"
data="Warehouses">
data="warehouses">
</vn-autocomplete>
<vn-autocomplete
vn-one

View File

@ -22,7 +22,7 @@
ng-model="filter.agencyModeFk">
</vn-autocomplete>
<vn-autocomplete vn-one
url="Agencies"
data="agencies"
label="Agency Agreement"
show-field="name"
value-field="id"

View File

@ -84,7 +84,7 @@
vn-one
label="Warehouse"
ng-model="filter.warehouseFk"
data="Warehouses"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>

View File

@ -0,0 +1,11 @@
name: shelving
columns:
id: id
code: code
parkingFk: parking
isPrinted: printed
priority: priority
parked: parked
userFk: user
isSpam: SPAM
isRecyclable: recyclable

View File

@ -0,0 +1,11 @@
name: estantería
columns:
id: id
code: código
parkingFk: parking
isPrinted: impreso
priority: prioridad
parked: aparcado
userFk: usuario
isSpam: SPAM
isRecyclable: reciclable

View File

@ -0,0 +1,50 @@
module.exports = Self => {
Self.remoteMethod('getItemsPackaging', {
description: 'Returns the list of items from the supplier of type packing',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'The supplier id',
http: {source: 'path'}
}, {
arg: 'entry',
type: 'number',
required: true,
description: 'The entry id',
}],
returns: {
type: 'object',
root: true
},
http: {
path: `/:id/getItemsPackaging`,
verb: 'GET'
}
});
Self.getItemsPackaging = async(id, entry) => {
return Self.rawSql(`
WITH entryTmp AS (
SELECT i.id, SUM(b.quantity) quantity
FROM vn.entry e
JOIN vn.buy b ON b.entryFk = e.id
JOIN vn.supplier s ON s.id = e.supplierFk
JOIN vn.item i ON i.id = b.itemFk
WHERE e.id = ? AND e.supplierFk = ?
GROUP BY i.id
) SELECT i.id, i.name, et.quantity, SUM(b.quantity) quantityTotal
FROM vn.buy b
JOIN vn.item i ON i.id = b.itemFk
JOIN vn.entry e ON e.id = b.entryFk
JOIN vn.supplier s ON s.id = e.supplierFk
JOIN vn.buyConfig bc ON bc.monthsAgo
JOIN vn.travel t ON t.id = e.travelFk
LEFT JOIN entryTmp et ON et.id = i.id
WHERE e.supplierFk = ?
AND i.family IN ('EMB', 'CONT')
AND b.created > (util.VN_CURDATE() - INTERVAL bc.monthsAgo MONTH)
GROUP BY b.itemFk
ORDER BY et.quantity DESC, quantityTotal DESC`, [entry, id, id]);
};
};

View File

@ -0,0 +1,12 @@
const app = require('vn-loopback/server/server');
describe('Supplier getItemsPackaging()', () => {
it('should return a summary of the list of items from a specific supplier', async() => {
const [item] = await app.models.Supplier.getItemsPackaging(1, 1);
expect(item.id).toEqual(1);
expect(item.name).toEqual('Ranged weapon longbow 2m');
expect(item.quantity).toEqual(5000);
expect(item.quantityTotal).toEqual(5100);
});
});

View File

@ -11,6 +11,7 @@ module.exports = Self => {
require('../methods/supplier/campaignMetricsPdf')(Self);
require('../methods/supplier/campaignMetricsEmail')(Self);
require('../methods/supplier/newSupplier')(Self);
require('../methods/supplier/getItemsPackaging')(Self);
Self.validatesPresenceOf('name', {
message: 'The social name cannot be empty'

View File

@ -12,3 +12,5 @@ columns:
hostFk: PC
isBox: box
itemPackingTypeFk: packing type
externalId: external id
stateTypeFk: status

View File

@ -12,3 +12,5 @@ columns:
hostFk: PC
isBox: caja
itemPackingTypeFk: tipo empaquetado
externalId: id externo
stateTypeFk: estado

View File

@ -30,7 +30,7 @@
</vn-autocomplete>
<vn-autocomplete vn-one
required="true"
data="Warehouses"
data="warehouses"
label="Warehouse"
show-field="name"
value-field="id"

View File

@ -34,7 +34,7 @@
<vn-autocomplete vn-one required="true"
label="Warehouse"
ng-model="$ctrl.dms.warehouseId"
data="Warehouses"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>

View File

@ -107,7 +107,7 @@
vn-one
label="Warehouse"
ng-model="filter.warehouseFk"
data="Warehouses">
data="warehouses">
</vn-autocomplete>
<vn-autocomplete
vn-one

View File

@ -14,6 +14,9 @@
"TravelThermograph": {
"dataSource": "vn"
},
"TravelConfig": {
"dataSource": "vn"
},
"Temperature": {
"dataSource": "vn"
}

View File

@ -0,0 +1,49 @@
{
"name": "TravelConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "travelConfig"
}
},
"properties": {
"id": {
"type": "number",
"id": true
},
"warehouseInFk": {
"type": "number"
},
"warehouseOutFk": {
"type": "number"
},
"agencyFk": {
"type": "number"
},
"companyFk": {
"type": "number"
}
},
"relations": {
"warehouseIn": {
"type": "belongsTo",
"model": "Warehouse",
"foreignKey": "warehouseInFk"
},
"warehouseOut": {
"type": "belongsTo",
"model": "Warehouse",
"foreignKey": "warehouseOutFk"
},
"agency": {
"type": "belongsTo",
"model": "AgencyMode",
"foreignKey": "agencyFk"
},
"company": {
"type": "belongsTo",
"model": "Company",
"foreignKey": "companyFk"
}
}
}

View File

@ -57,14 +57,14 @@
<vn-autocomplete vn-one
label="Warehouse Out"
ng-model="filter.warehouseOutFk"
data="Warehouses"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete vn-one
label="Warehouse In"
ng-model="filter.warehouseInFk"
data="Warehouses"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>

View File

@ -4,9 +4,20 @@ columns:
companyCodeFk: company
started: started
ended: ended
workerBusiness: business
reasonEndFk: ending reason
payedHolidays: payed holidays
occupationCodeFk: occupation
workerFk: worker
notes: notes
departmentFk: department
workerBusinessProfessionalCategoryFk: professional category
calendarTypeFk: calendar type
isHourlyLabor: hourly labor
workcenterFk: workcenter
rate: rate
workerBusinessCategoryFk: category
workerBusinessTypeFk: type
amount: amount
workerBusinessAgreementFk: agreement
basicSalary: salary

View File

@ -4,9 +4,20 @@ columns:
companyCodeFk: empresa
started: iniciado
ended: finalizado
workerBusiness: negocio
reasonEndFk: motivo finalización
payedHolidays: vacaciones pagadas
occupationCodeFk: ocupación
workerFk: trabajador
notes: notas
departmentFk: departamento
workerBusinessProfessionalCategoryFk: categoría profesional
calendarTypeFk: tipo calendario
isHourlyLabor: horario laboral
workcenterFk: centro de trabajo
rate: tarifa
workerBusinessCategoryFk: categoría
workerBusinessTypeFk: tipo
amount: salario
workerBusinessAgreementFk: acuerdo
basicSalary: salario base

View File

@ -1,6 +1,6 @@
name: document
columns:
id: id
dmsFk: dms
workerFk: worker
worker: worker
document: document
isReadableByWorker: readable by worker

View File

@ -1,6 +1,6 @@
name: documento
columns:
id: id
dmsFk: dms
workerFk: trabajador
isReadableByWorker: legible por trabajador
worker: trabajador
document: documento
isReadableByWorker: accesible por trabajador

View File

@ -1,20 +1,32 @@
name: worker
columns:
id: id
code: code
firstName: first name
lastName: last name
sub: buyer number
photo: photo
phone: phone
mobileExtension: mobile extension
userFk: user
bossFk: boss
fiDueDate: FI due date
hasMachineryAuthorized: machinery authorized
seniority: seniority
isTodayRelative: today relative
isF11Allowed: F11 allowed
sectorFk: sector
maritalStatus: marital status
labelerFk: labeler
originCountryFk: origin country
educationLevelFk: education level
SSN: SSN
labelerFk: labeler
mobileExtension: mobile extension
code: code
fi: fiscal identifier
birth: birth date
isDisable: disabled
isFreelance: freelance
isSsDiscounted: SS discounted
sex: sex
businessFk: current contract
balance: balance
locker: locker
workerFk: worker
sectorFk: sector

View File

@ -1,20 +1,32 @@
name: trabajador
columns:
id: id
code: código
firstName: nombre
lastName: apellido
sub: número comprador
photo: foto
phone: teléfono
mobileExtension: extensión móvil
userFk: usuario
bossFk: jefe
fiDueDate: caducidad DNI
hasMachineryAuthorized: maquinaria autorizada
seniority: antigüedad
isTodayRelative: relativo hoy
isF11Allowed: F11 autorizado
sectorFk: sector
maritalStatus: estado civil
labelerFk: etiquetadora
originCountryFk: país origen
educationLevelFk: nivel educativo
SSN: SSN
labelerFk: etiquetadora
mobileExtension: extensión móvil
code: código
locker: casillero
workerFk: trabajador
sectorFk: sector
fi: identificador fiscal
birth: nacimiento
isDisable: deshabilitado
isFreelance: autónomo
isSsDiscounted: descuento SS
sex: sexo
businessFk: contrato actual
balance: saldo
locker: taquilla

View File

@ -230,21 +230,21 @@ module.exports = Self => {
}, myOptions);
} else {
const weekDay = day.dated.getDay();
const journeys = await models.Journey.find({
const journeys = await models.BusinessSchedule.find({
where: {
business_id: day.businessFk,
day_id: weekDay
businessFk: day.businessFk,
weekday: weekDay
}
}, myOptions);
let timeTableDecimalInSeconds = 0;
for (let journey of journeys) {
const start = Date.vnNew();
const [startHours, startMinutes, startSeconds] = getTime(journey.start);
const [startHours, startMinutes, startSeconds] = getTime(journey.started);
start.setHours(startHours, startMinutes, startSeconds, 0);
const end = Date.vnNew();
const [endHours, endMinutes, endSeconds] = getTime(journey.end);
const [endHours, endMinutes, endSeconds] = getTime(journey.ended);
end.setHours(endHours, endMinutes, endSeconds, 0);
const result = (end - start) / 1000;
@ -255,7 +255,7 @@ module.exports = Self => {
const timeTableDecimal = timeTableDecimalInSeconds / 3600;
if (day.timeWorkDecimal == timeTableDecimal) {
const timed = new Date(day.dated);
const [startHours, startMinutes, startSeconds] = getTime(journey.start);
const [startHours, startMinutes, startSeconds] = getTime(journey.started);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
timed: timed.setHours(startHours, startMinutes, startSeconds),
@ -263,7 +263,7 @@ module.exports = Self => {
isSendMail: true
}, myOptions);
const [endHours, endMinutes, endSeconds] = getTime(journey.end);
const [endHours, endMinutes, endSeconds] = getTime(journey.ended);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
timed: timed.setHours(endHours, endMinutes, endSeconds),
@ -276,7 +276,7 @@ module.exports = Self => {
});
if (journey == minStart) {
const timed = new Date(day.dated);
const [startHours, startMinutes, startSeconds] = getTime(journey.start);
const [startHours, startMinutes, startSeconds] = getTime(journey.started);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
timed: timed.setHours(startHours, startMinutes, startSeconds),
@ -304,7 +304,7 @@ module.exports = Self => {
});
if (journey == minStart) {
const timed = new Date(day.dated);
const [startHours, startMinutes, startSeconds] = getTime(journey.start);
const [startHours, startMinutes, startSeconds] = getTime(journey.started);
await models.WorkerTimeControl.create({
userFk: day.workerFk,
timed: timed.setHours(startHours + 1, startMinutes, startSeconds),

View File

@ -2,6 +2,9 @@
"AbsenceType": {
"dataSource": "vn"
},
"BusinessSchedule": {
"dataSource": "vn"
},
"Calendar": {
"dataSource": "vn"
},
@ -38,9 +41,6 @@
"EducationLevel": {
"dataSource": "vn"
},
"Journey": {
"dataSource": "vn"
},
"ProfileType":{
"dataSource": "vn"
},

View File

@ -1,26 +1,26 @@
{
"name": "Journey",
"name": "BusinessSchedule",
"base": "VnModel",
"options": {
"mysql": {
"table": "postgresql.journey"
"table": "businessSchedule"
}
},
"properties": {
"journey_id": {
"id": {
"id": true,
"type": "number"
},
"day_id": {
"weekday": {
"type": "number"
},
"start": {
"started": {
"type": "date"
},
"end": {
"ended": {
"type": "date"
},
"business_id": {
"businessFk": {
"type": "number"
}
}

View File

@ -35,7 +35,7 @@
<vn-autocomplete vn-one
label="Warehouse"
ng-model="$ctrl.dms.warehouseId"
url="Warehouses"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>

View File

@ -34,7 +34,7 @@
<vn-autocomplete vn-one required="true"
label="Warehouse"
ng-model="$ctrl.dms.warehouseId"
data="Warehouses"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>

View File

@ -72,7 +72,7 @@
"state": "worker.card.workerLog",
"component": "vn-worker-log",
"description": "Log",
"acl": ["salesAssistant"]
"acl": ["hr"]
}, {
"url": "/note",
"state": "worker.card.note",

24765
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@
"url": "https://gitea.verdnatura.es/verdnatura/salix"
},
"engines": {
"node": ">=14"
"node": ">=20"
},
"dependencies": {
"axios": "^1.2.2",
@ -33,19 +33,20 @@
"loopback-boot": "3.3.1",
"loopback-component-explorer": "^6.5.0",
"loopback-component-storage": "3.6.1",
"loopback-connector-mysql": "^5.4.3",
"loopback-connector-mysql": "^6.2.0",
"loopback-connector-remote": "^3.4.1",
"loopback-context": "^3.4.0",
"loopback-context": "^3.5.2",
"mailparser": "^2.8.0",
"md5": "^2.2.1",
"node-ssh": "^11.0.0",
"object-diff": "0.0.4",
"object.pick": "^1.3.0",
"puppeteer": "^18.0.5",
"puppeteer": "^20.3.0",
"read-chunk": "^3.2.0",
"require-yaml": "0.0.1",
"smbhash": "0.0.1",
"strong-error-handler": "^2.3.2",
"url-loader": "^4.1.1",
"uuid": "^3.3.3",
"vn-loopback": "file:./loopback",
"vn-print": "file:./print",
@ -58,15 +59,15 @@
"@babel/register": "^7.7.7",
"angular-mocks": "^1.7.9",
"babel-jest": "^26.0.1",
"babel-loader": "^8.0.6",
"core-js": "^3.9.1",
"css-loader": "^2.1.0",
"babel-loader": "^8.2.4",
"core-js": "^3.30.1",
"css-loader": "^6.7.4",
"del": "^2.2.2",
"eslint": "^7.11.0",
"eslint-config-google": "^0.11.0",
"eslint-plugin-jasmine": "^2.10.1",
"fancy-log": "^1.3.2",
"file-loader": "^1.1.11",
"file-loader": "^6.2.0",
"gulp": "^4.0.2",
"gulp-concat": "^2.6.1",
"gulp-env": "^0.4.0",
@ -79,9 +80,9 @@
"gulp-yaml": "^1.0.1",
"html-loader": "^0.4.5",
"html-loader-jest": "^0.2.1",
"html-webpack-plugin": "^4.0.0-beta.11",
"html-webpack-plugin": "^5.5.1",
"identity-obj-proxy": "^3.0.0",
"jasmine": "^4.5.0",
"jasmine": "^5.0.0",
"jasmine-reporters": "^2.4.0",
"jasmine-spec-reporter": "^7.0.0",
"jest": "^26.0.1",
@ -90,15 +91,16 @@
"merge-stream": "^1.0.1",
"minimist": "^1.2.5",
"mysql2": "^1.7.0",
"node-sass": "^4.14.1",
"node-sass": "^9.0.0",
"nodemon": "^2.0.16",
"plugin-error": "^1.0.1",
"raw-loader": "^1.0.0",
"raw-loader": "^4.0.2",
"regenerator-runtime": "^0.13.7",
"sass-loader": "^7.3.1",
"style-loader": "^0.23.1",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10",
"sass": "^1.62.1",
"sass-loader": "^13.3.0",
"style-loader": "^3.3.3",
"webpack": "^5.83.1",
"webpack-cli": "^5.1.1",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^4.2.2",
"yaml-loader": "^0.5.0"

View File

@ -9,7 +9,7 @@ module.exports = {
concurrency: Cluster.CONCURRENCY_CONTEXT,
maxConcurrency: cpus().length,
puppeteerOptions: {
headless: true,
headless: 'new',
args: [
'--no-sandbox',
'--disable-setuid-sandbox',

View File

@ -2,7 +2,6 @@
"name": "vn-print",
"version": "2.0.0",
"description": "Print service",
"main": "server/server.js",
"scripts": {
"start": "node server/server.js",
"test": "echo \"Error: no test specified\" && exit 1"

View File

@ -20,13 +20,13 @@ let baseConfig = {
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-syntax-dynamic-import']
}
}, {
test: /\.yml$/,
loader: 'json-loader!yaml-loader'
use: ['json-loader!yaml-loader']
}, {
test: /\.html$/,
loader: 'html-loader',
@ -38,39 +38,34 @@ let baseConfig = {
}
}, {
test: /\.css$/,
use: [
{
loader: 'style-loader'
}, {
loader: 'css-loader'
}
]
use: ['style-loader', 'css-loader']
}, {
test: /\.scss$/,
use: [
{
loader: 'style-loader'
}, {
loader: 'css-loader'
}, {
'style-loader', 'css-loader', {
loader: 'sass-loader',
options: {
// XXX: Don't work in Firefox
// https://github.com/webpack-contrib/style-loader/issues/303
// sourceMap: true,
sassOptions: {
includePaths: [
path.resolve(__dirname, 'front/core/styles')
path.resolve(__dirname, 'front/core/styles/')
]
}
}
}
]
}, {
test: /\.(svg|png|ttf|woff|woff2)$/,
loader: 'file-loader'
test: /\.(woff(2)?|ttf|eot|svg|png)(\?v=\d+\.\d+\.\d+)?$/,
type: 'asset/resource',
}, {
test: /manifest\.json$/,
type: 'javascript/auto',
loader: 'file-loader'
loader: 'file-loader',
options: {
esModule: false,
}
}
]
},
@ -105,7 +100,7 @@ let baseConfig = {
chunks: ['salix']
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
'process.env.NODE_ENV': JSON.stringify(env)
})
],
devtool: 'source-map',
@ -124,7 +119,7 @@ let prodConfig = {
chunkFilename: '[id].[chunkhash].js'
},
plugins: [
new webpack.HashedModuleIdsPlugin()
new webpack.ids.HashedModuleIdsPlugin()
],
performance: {
maxEntrypointSize: 2000000,