5934-clientSms #1675

Merged
alexm merged 5 commits from 5934-clientSms into dev 2023-07-20 07:01:02 +00:00
41 changed files with 375 additions and 261 deletions
Showing only changes of commit 6df1ef9d4c - Show all commits

View File

@ -1,68 +0,0 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethod('addAlias', {
description: 'Add an alias if the user has the grant',
accessType: 'WRITE',
accepts: [
{
arg: 'ctx',
type: 'Object',
http: {source: 'context'}
},
{
arg: 'id',
type: 'number',
required: true,
description: 'The user id',
http: {source: 'path'}
},
{
arg: 'mailAlias',
type: 'number',
description: 'The new alias for user',
required: true
}
],
http: {
path: `/:id/addAlias`,
verb: 'POST'
}
});
Self.addAlias = async function(ctx, id, mailAlias, options) {
const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const user = await Self.findById(userId, {fields: ['hasGrant']}, myOptions);
if (!user.hasGrant)
throw new UserError(`You don't have grant privilege`);
const account = await models.Account.findById(userId, {
fields: ['id'],
include: {
relation: 'aliases',
scope: {
fields: ['mailAlias']
}
}
}, myOptions);
const aliases = account.aliases().map(alias => alias.mailAlias);
const hasAlias = aliases.includes(mailAlias);
if (!hasAlias)
throw new UserError(`You cannot assign an alias that you are not assigned to`);
return models.MailAliasAccount.create({
mailAlias: mailAlias,
account: id
}, myOptions);
};
};

View File

@ -1,55 +0,0 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethod('removeAlias', {
description: 'Remove alias if the user has the grant',
accessType: 'WRITE',
accepts: [
{
arg: 'ctx',
type: 'Object',
http: {source: 'context'}
},
{
arg: 'id',
type: 'number',
required: true,
description: 'The user id',
http: {source: 'path'}
},
{
arg: 'mailAlias',
type: 'number',
description: 'The alias to delete',
required: true
}
],
http: {
path: `/:id/removeAlias`,
verb: 'POST'
}
});
Self.removeAlias = async function(ctx, id, mailAlias, options) {
const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const canRemoveAlias = await models.ACL.checkAccessAcl(ctx, 'VnUser', 'canRemoveAlias', 'WRITE');
if (userId != id && !canRemoveAlias) throw new UserError(`You don't have grant privilege`);
const mailAliasAccount = await models.MailAliasAccount.findOne({
where: {
mailAlias: mailAlias,
account: id
}
}, myOptions);
await mailAliasAccount.destroy(myOptions);
};
};

View File

@ -12,8 +12,6 @@ module.exports = function(Self) {
require('../methods/vn-user/privileges')(Self); require('../methods/vn-user/privileges')(Self);
require('../methods/vn-user/validate-auth')(Self); require('../methods/vn-user/validate-auth')(Self);
require('../methods/vn-user/renew-token')(Self); require('../methods/vn-user/renew-token')(Self);
require('../methods/vn-user/addAlias')(Self);
require('../methods/vn-user/removeAlias')(Self);
Self.definition.settings.acls = Self.definition.settings.acls.filter(acl => acl.property !== 'create'); Self.definition.settings.acls = Self.definition.settings.acls.filter(acl => acl.property !== 'create');

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalId`)
VALUES
('Vehicle','sorted','WRITE','ALLOW','employee');

View File

@ -1,11 +0,0 @@
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES
('VnUser', 'addAlias', 'WRITE', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES
('VnUser', 'removeAlias', 'WRITE', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES
('VnUser', 'canRemoveAlias', 'WRITE', 'ALLOW', 'ROLE', 'itManagement');

View File

@ -0,0 +1,8 @@
DELETE FROM `salix`.`ACL` WHERE model = 'MailAliasAccount';
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES
('MailAliasAccount', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
('MailAliasAccount', 'create', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
('MailAliasAccount', 'deleteById', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
('MailAliasAccount', 'canEditAlias', 'WRITE', 'ALLOW', 'ROLE', 'itManagement');

View File

@ -0,0 +1,4 @@
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES
('WorkerDisableExcluded', '*', 'READ', 'ALLOW', 'ROLE', 'itManagement'),
('WorkerDisableExcluded', '*', 'WRITE', 'ALLOW', 'ROLE', 'itManagement');

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalId`)
VALUES
('Vehicle','sorted','WRITE','ALLOW','employee');

View File

@ -0,0 +1,64 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`itemShelving_inventory`(vParkingFromFk VARCHAR(8), vParkingToFk VARCHAR(8))
BEGIN
/**
* Devuelve un listado de ubicaciones a revisar
*
* @param vParkingFromFk Parking de partida, identificador de parking
* @param vParkingToFk Parking de llegada, identificador de parking
*/
DECLARE vSectorFk INT;
DECLARE vPickingOrderFrom INT;
DECLARE vPickingOrderTo INT;
SELECT p.sectorFk, p.pickingOrder INTO vSectorFk, vPickingOrderFrom
FROM parking p
WHERE p.code = vParkingFromFk COLLATE 'utf8mb3_general_ci';
SELECT p.pickingOrder INTO vPickingOrderTo
FROM parking p
WHERE p.code = vParkingToFk COLLATE 'utf8mb3_general_ci';
CALL visible_getMisfit(vSectorFk);
SELECT ish.id,
p.pickingOrder,
p.code parking,
ish.shelvingFk,
ish.itemFk,
i.longName,
ish.visible,
p.sectorFk,
it.workerFk buyer,
CONCAT('http:',ic.url, '/catalog/1600x900/',i.image) urlImage,
ish.isChecked,
CASE
WHEN s.notPrepared > sm.parked THEN 0
WHEN sm.visible > sm.parked THEN 1
ELSE 2
END priority
FROM itemShelving ish
JOIN item i ON i.id = ish.itemFk
JOIN itemType it ON it.id = i.typeFk
JOIN tmp.stockMisfit sm ON sm.itemFk = ish.itemFk
JOIN shelving sh ON sh.code = ish.shelvingFk
JOIN parking p ON p.id = sh.parkingFk
JOIN (SELECT s.itemFk, sum(s.quantity) notPrepared
FROM sale s
JOIN ticket t ON t.id = s.ticketFk
JOIN warehouse w ON w.id = t.warehouseFk
JOIN config c ON c.mainWarehouseFk = w.id
WHERE t.shipped BETWEEN util.VN_CURDATE()
AND util.dayEnd(util.VN_CURDATE())
AND s.isPicked = FALSE
GROUP BY s.itemFk) s ON s.itemFk = i.id
JOIN hedera.imageConfig ic
WHERE p.pickingOrder BETWEEN vPickingOrderFrom AND vPickingOrderTo
AND p.sectorFk = vSectorFk
ORDER BY p.pickingOrder;
END$$
DELIMITER ;

View File

@ -37,7 +37,7 @@ ALTER TABLE `vn`.`ticket` AUTO_INCREMENT = 1;
INSERT INTO `salix`.`AccessToken` (`id`, `ttl`, `created`, `userId`) INSERT INTO `salix`.`AccessToken` (`id`, `ttl`, `created`, `userId`)
VALUES VALUES
('DEFAULT_TOKEN', '1209600', util.VN_CURDATE(), 66); ('DEFAULT_TOKEN', '1209600', CURDATE(), 66);
INSERT INTO `salix`.`printConfig` (`id`, `itRecipient`, `incidencesEmail`) INSERT INTO `salix`.`printConfig` (`id`, `itRecipient`, `incidencesEmail`)
VALUES VALUES
@ -2953,3 +2953,8 @@ INSERT INTO `vn`.`invoiceInSerial` (`code`, `description`, `cplusTerIdNifFk`, `t
('E', 'Midgard', 1, 'CEE'), ('E', 'Midgard', 1, 'CEE'),
('R', 'Jotunheim', 1, 'NATIONAL'), ('R', 'Jotunheim', 1, 'NATIONAL'),
('W', 'Vanaheim', 1, 'WORLD'); ('W', 'Vanaheim', 1, 'WORLD');
INSERT INTO `hedera`.`imageConfig` (`id`, `maxSize`, `useXsendfile`, `url`)
VALUES
(1, 0, 0, 'marvel.com');

View File

@ -305,5 +305,6 @@
"The renew period has not been exceeded": "El periodo de renovación no ha sido superado", "The renew period has not been exceeded": "El periodo de renovación no ha sido superado",
"Valid priorities": "Prioridades válidas: %d", "Valid priorities": "Prioridades válidas: %d",
"Negative basis of tickets": "Base negativa para los tickets: {{ticketsIds}}", "Negative basis of tickets": "Base negativa para los tickets: {{ticketsIds}}",
"You cannot assign an alias that you are not assigned to": "No puede asignar un alias que no tenga asignado" "You cannot assign/remove an alias that you are not assigned to": "No puede asignar/eliminar un alias que no tenga asignado",
"This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado"
} }

View File

@ -0,0 +1,55 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.observe('before save', async ctx => {
const changes = ctx.currentInstance || ctx.instance;
await Self.hasGrant(ctx, changes.mailAlias);
});
Self.observe('before delete', async ctx => {
const mailAliasAccount = await Self.findById(ctx.where.id);
await Self.hasGrant(ctx, mailAliasAccount.mailAlias);
});
/**
* Checks if current user has
* grant to add/remove alias
*
* @param {Object} ctx - Request context
* @param {Interger} mailAlias - mailAlias id
* @return {Boolean} True for user with grant
*/
Self.hasGrant = async function(ctx, mailAlias) {
const models = Self.app.models;
const accessToken = {req: {accessToken: ctx.options.accessToken}};
const userId = accessToken.req.accessToken.userId;
const canEditAlias = await models.ACL.checkAccessAcl(accessToken, 'MailAliasAccount', 'canEditAlias', 'WRITE');
if (canEditAlias) return true;
const user = await models.VnUser.findById(userId, {fields: ['hasGrant']});
if (!user.hasGrant)
throw new UserError(`You don't have grant privilege`);
const account = await models.Account.findById(userId, {
fields: ['id'],
include: {
relation: 'aliases',
scope: {
fields: ['mailAlias']
}
}
});
const aliases = account.aliases().map(alias => alias.mailAlias);
const hasAlias = aliases.includes(mailAlias);
if (!hasAlias)
throw new UserError(`You cannot assign/remove an alias that you are not assigned to`);
return true;
};
};

View File

@ -21,11 +21,12 @@ export default class Controller extends Section {
} }
onAddClick() { onAddClick() {
this.addData = {account: this.$params.id};
this.$.dialog.show(); this.$.dialog.show();
} }
onAddSave() { onAddSave() {
return this.$http.post(`VnUsers/${this.$params.id}/addAlias`, this.addData) return this.$http.post(`MailAliasAccounts`, this.addData)
.then(() => this.refresh()) .then(() => this.refresh())
.then(() => this.vnApp.showSuccess( .then(() => this.vnApp.showSuccess(
this.$t('Subscribed to alias!')) this.$t('Subscribed to alias!'))
@ -33,12 +34,11 @@ export default class Controller extends Section {
} }
onRemove(row) { onRemove(row) {
const params = { return this.$http.delete(`MailAliasAccounts/${row.id}`)
mailAlias: row.mailAlias .then(() => {
}; this.$.data.splice(this.$.data.indexOf(row), 1);
return this.$http.post(`VnUsers/${this.$params.id}/removeAlias`, params) this.vnApp.showSuccess(this.$t('Unsubscribed from alias!'));
.then(() => this.refresh()) });
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')));
} }
} }

View File

@ -25,9 +25,8 @@ describe('component vnUserAliases', () => {
describe('onAddSave()', () => { describe('onAddSave()', () => {
it('should add the new row', () => { it('should add the new row', () => {
controller.addData = {account: 1}; controller.addData = {account: 1};
controller.$params = {id: 1};
$httpBackend.expectPOST('VnUsers/1/addAlias').respond(); $httpBackend.expectPOST('MailAliasAccounts').respond();
$httpBackend.expectGET('MailAliasAccounts').respond('foo'); $httpBackend.expectGET('MailAliasAccounts').respond('foo');
controller.onAddSave(); controller.onAddSave();
$httpBackend.flush(); $httpBackend.flush();
@ -42,14 +41,12 @@ describe('component vnUserAliases', () => {
{id: 1, alias: 'foo'}, {id: 1, alias: 'foo'},
{id: 2, alias: 'bar'} {id: 2, alias: 'bar'}
]; ];
controller.$params = {id: 1};
$httpBackend.expectPOST('VnUsers/1/removeAlias').respond(); $httpBackend.expectDELETE('MailAliasAccounts/1').respond();
$httpBackend.expectGET('MailAliasAccounts').respond(controller.$.data[1]);
controller.onRemove(controller.$.data[0]); controller.onRemove(controller.$.data[0]);
$httpBackend.flush(); $httpBackend.flush();
expect(controller.$.data).toEqual({id: 2, alias: 'bar'}); expect(controller.$.data).toEqual([{id: 2, alias: 'bar'}]);
expect(controller.vnApp.showSuccess).toHaveBeenCalled(); expect(controller.vnApp.showSuccess).toHaveBeenCalled();
}); });
}); });

View File

@ -70,11 +70,12 @@ module.exports = Self => {
c.creditInsurance, c.creditInsurance,
d.defaulterSinced, d.defaulterSinced,
cn.country, cn.country,
c.countryFk,
pm.name payMethod pm.name payMethod
FROM vn.defaulter d FROM vn.defaulter d
JOIN vn.client c ON c.id = d.clientFk JOIN vn.client c ON c.id = d.clientFk
JOIN vn.country cn ON cn.id = c.countryFk JOIN vn.country cn ON cn.id = c.countryFk
JOIN vn.payMethod pm ON pm.id = c.payMethodFk JOIN vn.payMethod pm ON pm.id = c.payMethodFk
LEFT JOIN vn.clientObservation co ON co.clientFk = c.id LEFT JOIN vn.clientObservation co ON co.clientFk = c.id
LEFT JOIN account.user u ON u.id = c.salesPersonFk LEFT JOIN account.user u ON u.id = c.salesPersonFk
LEFT JOIN account.user uw ON uw.id = co.workerFk LEFT JOIN account.user uw ON uw.id = co.workerFk

View File

@ -33,7 +33,7 @@
"country": { "country": {
"type": "belongsTo", "type": "belongsTo",
"model": "Country", "model": "Country",
"foreignKey": "country" "foreignKey": "countryFk"
}, },
"payMethod": { "payMethod": {
"type": "belongsTo", "type": "belongsTo",
@ -41,4 +41,4 @@
"foreignKey": "payMethod" "foreignKey": "payMethod"
} }
} }
} }

View File

@ -60,7 +60,7 @@
<th field="salesPersonFk"> <th field="salesPersonFk">
<span translate>Comercial</span> <span translate>Comercial</span>
</th> </th>
<th field="country"> <th field="countryFk">
<span translate>Country</span> <span translate>Country</span>
</th> </th>
<th field="payMethod" <th field="payMethod"
@ -132,7 +132,7 @@
</td> </td>
<td> <td>
{{::defaulter.payMethod}} {{::defaulter.payMethod}}
</td> </td>
<td>{{::defaulter.amount | currency: 'EUR': 2}}</td> <td>{{::defaulter.amount | currency: 'EUR': 2}}</td>
<td> <td>
<span <span

View File

@ -31,10 +31,11 @@ export default class Controller extends Section {
valueField: 'id', valueField: 'id',
} }
}, { }, {
field: 'country', field: 'countryFk',
autocomplete: { autocomplete: {
url: 'Countries',
showField: 'country', showField: 'country',
valueField: 'country' valueField: 'id'
} }
}, { }, {
field: 'payMethodFk', field: 'payMethodFk',
@ -167,7 +168,7 @@ export default class Controller extends Section {
case 'amount': case 'amount':
case 'clientFk': case 'clientFk':
case 'workerFk': case 'workerFk':
case 'country': case 'countryFk':
case 'payMethod': case 'payMethod':
case 'salesPersonFk': case 'salesPersonFk':
return {[`d.${param}`]: value}; return {[`d.${param}`]: value};

View File

@ -1,3 +1,5 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
require('../methods/invoice-in/filter')(Self); require('../methods/invoice-in/filter')(Self);
require('../methods/invoice-in/summary')(Self); require('../methods/invoice-in/summary')(Self);
@ -7,4 +9,9 @@ module.exports = Self => {
require('../methods/invoice-in/invoiceInPdf')(Self); require('../methods/invoice-in/invoiceInPdf')(Self);
require('../methods/invoice-in/invoiceInEmail')(Self); require('../methods/invoice-in/invoiceInEmail')(Self);
require('../methods/invoice-in/getSerial')(Self); require('../methods/invoice-in/getSerial')(Self);
Self.rewriteDbError(function(err) {
if (err.code === 'ER_ROW_IS_REFERENCED_2' && err.sqlMessage.includes('vehicleInvoiceIn'))
return new UserError(`This invoice has a linked vehicle.`);
return err;
});
}; };

View File

@ -0,0 +1,37 @@
module.exports = Self => {
Self.remoteMethod('getInventory', {
description: 'Get list of itemShelving to review between two parking code',
accessType: 'WRITE',
accepts: [{
arg: 'parkingFrom',
type: 'string',
required: true,
description: 'Parking code from'
},
{
arg: 'parkingTo',
type: 'string',
required: true,
description: 'Parking code to'
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/getInventory`,
verb: 'POST'
}
});
Self.getInventory = async(parkingFrom, parkingTo, options) => {
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const [result] = await Self.rawSql(`CALL vn.itemShelving_inventory(?, ?)`, [parkingFrom, parkingTo], myOptions);
return result;
};
};

View File

@ -0,0 +1,24 @@
const models = require('vn-loopback/server/server').models;
describe('itemShelving getInventory()', () => {
it('should return a list of itemShelvings', async() => {
const tx = await models.ItemShelving.beginTransaction({});
let response;
try {
const options = {transaction: tx};
await models.ItemShelving.rawSql(`
UPDATE vn.config
SET mainWarehouseFk=1
WHERE id=1
`, null, options);
response = await models.ItemShelving.getInventory('100-01', 'LR-02-3', options);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
expect(response.length).toEqual(2);
});
});

View File

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

View File

@ -23,6 +23,9 @@
}, },
"isChecked": { "isChecked": {
"type": "boolean" "type": "boolean"
},
"visible": {
"type": "number"
} }
}, },
"relations": { "relations": {

View File

@ -85,7 +85,7 @@
show-field="id" show-field="id"
value-field="id" value-field="id"
search-function="$ctrl.itemSearchFunc($search)" search-function="$ctrl.itemSearchFunc($search)"
on-change="$ctrl.upsertPrice(price, true)" ng-change="$ctrl.upsertPrice(price, true)"
order="id DESC" order="id DESC"
tabindex="1"> tabindex="1">
<tpl-item> <tpl-item>

View File

@ -0,0 +1,27 @@
module.exports = Self => {
Self.remoteMethod('sorted', {
description: 'Sort the vehicles by warehouse',
accessType: 'WRITE',
accepts: [{
arg: 'warehouseFk',
type: 'number'
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/sorted`,
verb: `POST`
}
});
Self.sorted = async warehouseFk => {
return Self.rawSql(`
SELECT v.id, v.warehouseFk, v.numberPlate, w.name
FROM vehicle v
JOIN warehouse w ON w.id = v.warehouseFk
ORDER BY v.warehouseFk = ? DESC, w.id, v.numberPlate ASC;
`, [warehouseFk]);
};
};

View File

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

View File

@ -23,19 +23,21 @@
</tpl-item> </tpl-item>
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete <vn-autocomplete
label="Vehicle"
ng-model="$ctrl.route.vehicleFk" ng-model="$ctrl.route.vehicleFk"
url="Vehicles" data="$ctrl.vehicles"
show-field="numberPlate" show-field="numberPlate"
value-field="id" value-field="id"
label="Vehicle" order="false"
vn-name="vehicle"> vn-name="vehicle">
<tpl-item>{{::numberPlate}} - {{::name}}</tpl-item>
</vn-autocomplete> </vn-autocomplete>
</vn-horizontal> </vn-horizontal>
<vn-horizontal> <vn-horizontal>
<vn-date-picker <vn-date-picker
label="Created" label="Created"
ng-model="$ctrl.route.created" ng-model="$ctrl.route.created"
vn-name="created"> vn-name="created">
</vn-date-picker> </vn-date-picker>
<vn-autocomplete <vn-autocomplete
ng-model="$ctrl.route.agencyModeFk" ng-model="$ctrl.route.agencyModeFk"

View File

@ -2,6 +2,13 @@ import ngModule from '../module';
import Section from 'salix/components/section'; import Section from 'salix/components/section';
class Controller extends Section { class Controller extends Section {
$onInit() {
this.$http.post(`Vehicles/sorted`, {warehouseFk: this.vnConfig.warehouseFk})
.then(res => {
this.vehicles = res.data;
});
}
onSubmit() { onSubmit() {
this.$.watcher.submit().then(() => this.$.watcher.submit().then(() =>
this.card.reload() this.card.reload()

View File

@ -68,9 +68,9 @@ module.exports = Self => {
}; };
const country = await Self.app.models.Country.findOne(filter); const country = await Self.app.models.Country.findOne(filter);
const code = country ? country.code.toLowerCase() : null; const code = country ? country.code.toLowerCase() : null;
const countryCode = this.nif.toLowerCase().substring(0, 2); const countryCode = this.nif?.toLowerCase().substring(0, 2);
if (!this.nif || !validateTin(this.nif, code) || (this.isVies && countryCode == code)) if (!validateTin(this.nif, code) || (this.isVies && countryCode == code))
err(); err();
done(); done();
} }
@ -122,7 +122,7 @@ module.exports = Self => {
}); });
async function hasSupplierSameName(err, done) { async function hasSupplierSameName(err, done) {
if (!this.name || !this.countryFk) done(); if (!this.name || !this.countryFk) return done();
const supplier = await Self.app.models.Supplier.findOne( const supplier = await Self.app.models.Supplier.findOne(
{ {
where: { where: {

View File

@ -141,6 +141,7 @@ describe('ticket filter()', () => {
}); });
it('should return the tickets that are not pending', async() => { it('should return the tickets that are not pending', async() => {
pending('#6010 test intermitente');
const tx = await models.Ticket.beginTransaction({}); const tx = await models.Ticket.beginTransaction({});
try { try {

View File

@ -38,10 +38,12 @@ class Controller extends SearchPanel {
applyFilters(param) { applyFilters(param) {
if (typeof this.filter.scopeDays === 'number') { if (typeof this.filter.scopeDays === 'number') {
const shippedFrom = Date.vnNew(); const today = Date.vnNew();
const shippedFrom = new Date(today.getTime());
shippedFrom.setDate(today.getDate() - 30);
shippedFrom.setHours(0, 0, 0, 0); shippedFrom.setHours(0, 0, 0, 0);
const shippedTo = new Date(shippedFrom.getTime()); const shippedTo = new Date(today.getTime());
shippedTo.setDate(shippedTo.getDate() + this.filter.scopeDays); shippedTo.setDate(shippedTo.getDate() + this.filter.scopeDays);
shippedTo.setHours(23, 59, 59, 999); shippedTo.setHours(23, 59, 59, 999);
Object.assign(this.filter, {shippedFrom, shippedTo}); Object.assign(this.filter, {shippedFrom, shippedTo});

View File

@ -3,7 +3,7 @@
data="absenceTypes" data="absenceTypes"
auto-load="true"> auto-load="true">
</vn-crud-model> </vn-crud-model>
<div ng-if="$ctrl.worker.hasWorkCenter"> <div ng-if="$ctrl.card.hasWorkCenter">
<div class="vn-w-lg"> <div class="vn-w-lg">
<vn-card class="vn-pa-sm calendars"> <vn-card class="vn-pa-sm calendars">
<vn-icon ng-if="::$ctrl.isSubordinate" icon="info" color-marginal <vn-icon ng-if="::$ctrl.isSubordinate" icon="info" color-marginal
@ -23,7 +23,7 @@
</div> </div>
</div> </div>
<div <div
ng-if="!$ctrl.worker.hasWorkCenter" ng-if="!$ctrl.card.hasWorkCenter"
class="bg-title" class="bg-title"
translate> translate>
Autonomous worker Autonomous worker
@ -73,41 +73,39 @@
value-field="businessFk" value-field="businessFk"
order="businessFk DESC" order="businessFk DESC"
limit="5"> limit="5">
<tpl-item> <tpl-item>
<div>#{{businessFk}}</div> <div>#{{businessFk}}</div>
<div class="text-caption text-secondary"> <div class="text-caption text-secondary">
{{started | date: 'dd/MM/yyyy'}} - {{ended ? (ended | date: 'dd/MM/yyyy') : 'Indef.'}} {{started | date: 'dd/MM/yyyy'}} - {{ended ? (ended | date: 'dd/MM/yyyy') : 'Indef.'}}
</div> </div>
</tpl-item> </tpl-item>
</vn-autocomplete> </vn-autocomplete>
</div>
<div name="absenceTypes" class="input vn-py-md" style="overflow: hidden;">
<vn-chip ng-repeat="absenceType in absenceTypes" ng-class="::{'selectable': $ctrl.isSubordinate}"
ng-click="$ctrl.pick(absenceType)">
<vn-avatar
ng-style="{backgroundColor: absenceType.rgb}">
<vn-icon icon="check" ng-if="absenceType.id == $ctrl.absenceType.id"></vn-icon>
</vn-avatar>
{{absenceType.name}}
</vn-chip>
</div>
<div class="vn-py-md">
<vn-chip>
<vn-avatar class="festive">
</vn-avatar>
<span translate>Festive</span>
</vn-chip>
<vn-chip>
<vn-avatar class="today">
</vn-avatar>
<span translate>Current day</span>
</vn-chip>
</div>
</div> </div>
<div name="absenceTypes" class="input vn-py-md" style="overflow: hidden;">
<vn-chip ng-repeat="absenceType in absenceTypes" ng-class="::{'selectable': $ctrl.isSubordinate}" ng-click="$ctrl.pick(absenceType)">
<vn-avatar ng-style="{backgroundColor: absenceType.rgb}">
<vn-icon icon="check" ng-if="absenceType.id == $ctrl.absenceType.id"></vn-icon>
</vn-avatar>
{{absenceType.name}}
</vn-chip>
</div>
<div class="vn-py-md">
<vn-chip>
<vn-avatar class="festive">
</vn-avatar>
<span translate>Festive</span>
</vn-chip>
<vn-chip>
<vn-avatar class="today">
</vn-avatar>
<span translate>Current day</span>
</vn-chip>
</div>
</div>
</vn-side-menu> </vn-side-menu>
<vn-confirm <vn-confirm
vn-id="confirm" vn-id="confirm"
message="This item will be deleted" message="This item will be deleted"
question="Are you sure you want to continue?"> question="Are you sure you want to continue?">
</vn-confirm> </vn-confirm>

View File

@ -31,6 +31,8 @@ class Controller extends Section {
} }
set businessId(value) { set businessId(value) {
if (!this.card.hasWorkCenter) return;
this._businessId = value; this._businessId = value;
if (value) { if (value) {
this.refresh() this.refresh()
@ -64,7 +66,7 @@ class Controller extends Section {
set worker(value) { set worker(value) {
this._worker = value; this._worker = value;
if (value && value.hasWorkCenter) { if (value) {
this.getIsSubordinate(); this.getIsSubordinate();
this.getActiveContract(); this.getActiveContract();
} }
@ -293,5 +295,8 @@ ngModule.vnComponent('vnWorkerCalendar', {
controller: Controller, controller: Controller,
bindings: { bindings: {
worker: '<' worker: '<'
},
require: {
card: '^vnWorkerCard'
} }
}); });

View File

@ -20,6 +20,9 @@ describe('Worker', () => {
controller.absenceType = {id: 1, name: 'Holiday', code: 'holiday', rgb: 'red'}; controller.absenceType = {id: 1, name: 'Holiday', code: 'holiday', rgb: 'red'};
controller.$params.id = 1106; controller.$params.id = 1106;
controller._worker = {id: 1106}; controller._worker = {id: 1106};
controller.card = {
hasWorkCenter: true
};
})); }));
describe('year() getter', () => { describe('year() getter', () => {
@ -74,7 +77,7 @@ describe('Worker', () => {
let yesterday = new Date(today.getTime()); let yesterday = new Date(today.getTime());
yesterday.setDate(yesterday.getDate() - 1); yesterday.setDate(yesterday.getDate() - 1);
controller.worker = {id: 1107, hasWorkCenter: true}; controller.worker = {id: 1107};
expect(controller.getIsSubordinate).toHaveBeenCalledWith(); expect(controller.getIsSubordinate).toHaveBeenCalledWith();
expect(controller.getActiveContract).toHaveBeenCalledWith(); expect(controller.getActiveContract).toHaveBeenCalledWith();

View File

@ -3,7 +3,7 @@ import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard { class Controller extends ModuleCard {
reload() { reload() {
let filter = { const filter = {
include: [ include: [
{ {
relation: 'user', relation: 'user',
@ -32,13 +32,12 @@ class Controller extends ModuleCard {
] ]
}; };
this.$http.get(`Workers/${this.$params.id}`, {filter}) return Promise.all([
.then(res => this.worker = res.data) this.$http.get(`Workers/${this.$params.id}`, {filter})
.then(() => .then(res => this.worker = res.data),
this.$http.get(`Workers/${this.$params.id}/activeContract`) this.$http.get(`Workers/${this.$params.id}/activeContract`)
.then(res => { .then(res => this.hasWorkCenter = res.data.workCenterFk)
if (res.data) this.worker.hasWorkCenter = res.data.workCenterFk; ]);
}));
} }
} }

View File

@ -5,8 +5,8 @@
<slot-before> <slot-before>
<div class="photo" text-center> <div class="photo" text-center>
<img vn-id="photo" <img vn-id="photo"
ng-src="{{$root.imagePath('user', '520x520', $ctrl.worker.id)}}" ng-src="{{$root.imagePath('user', '520x520', $ctrl.worker.id)}}"
zoom-image="{{$root.imagePath('user', '1600x1600', $ctrl.worker.id)}}" zoom-image="{{$root.imagePath('user', '1600x1600', $ctrl.worker.id)}}"
on-error-src/> on-error-src/>
<vn-float-button ng-click="uploadPhoto.show('user', $ctrl.worker.id)" <vn-float-button ng-click="uploadPhoto.show('user', $ctrl.worker.id)"
icon="edit" icon="edit"
@ -15,39 +15,32 @@
</div> </div>
</slot-before> </slot-before>
<slot-menu> <slot-menu>
<vn-item <vn-item ng-click="$ctrl.handleExcluded()" translate>
ng-click="$ctrl.handleExcluded()" {{$ctrl.workerExcluded
translate ? 'Click to allow the user to be disabled'
ng-if="!$ctrl.excluded"> : 'Click to exclude the user from getting disabled'}}
Click to exclude the user from getting disabled </vn-item>
</vn-item>
<vn-item
ng-click="$ctrl.handleExcluded()"
translate
ng-if="$ctrl.excluded">
Click to allow the user to be disabled
</vn-item>
</slot-menu> </slot-menu>
<slot-body> <slot-body>
<div class="attributes"> <div class="attributes">
<vn-label-value <vn-label-value
label="User" label="User"
value="{{$ctrl.worker.user.name}}"> value="{{$ctrl.worker.user.name}}">
</vn-label-value> </vn-label-value>
<vn-label-value <vn-label-value
label="Email" label="Email"
value="{{$ctrl.worker.user.emailUser.email}}"> value="{{$ctrl.worker.user.emailUser.email}}">
</vn-label-value> </vn-label-value>
<vn-label-value <vn-label-value
label="Department" label="Department"
value="{{$ctrl.worker.department.department.name}}"> value="{{$ctrl.worker.department.department.name}}">
</vn-label-value> </vn-label-value>
<vn-label-value <vn-label-value
label="Phone" label="Phone"
value="{{$ctrl.worker.phone}}"> value="{{$ctrl.worker.phone}}">
</vn-label-value> </vn-label-value>
<vn-label-value <vn-label-value
label="Extension" label="Extension"
value="{{$ctrl.worker.sip.extension}}"> value="{{$ctrl.worker.sip.extension}}">
</vn-label-value> </vn-label-value>
</div> </div>
@ -84,7 +77,7 @@
</vn-popup> </vn-popup>
<!-- Upload photo dialog --> <!-- Upload photo dialog -->
<vn-upload-photo <vn-upload-photo
vn-id="uploadPhoto" vn-id="uploadPhoto"
on-response="$ctrl.onUploadResponse()"> on-response="$ctrl.onUploadResponse()">
</vn-upload-photo> </vn-upload-photo>

View File

@ -18,28 +18,19 @@ class Controller extends Descriptor {
this.getIsExcluded(); this.getIsExcluded();
} }
get excluded() {
return this.entity.excluded;
}
set excluded(value) {
this.entity.excluded = value;
}
getIsExcluded() { getIsExcluded() {
this.$http.get(`workerDisableExcludeds/${this.entity.id}/exists`).then(data => { this.$http.get(`WorkerDisableExcludeds/${this.entity.id}/exists`).then(data => {
this.excluded = data.data.exists; this.workerExcluded = data.data.exists;
}); });
} }
handleExcluded() { handleExcluded() {
if (this.excluded) { if (this.workerExcluded)
this.$http.delete(`workerDisableExcludeds/${this.entity.id}`); this.$http.delete(`WorkerDisableExcludeds/${this.entity.id}`);
this.excluded = false; else
} else { this.$http.post(`WorkerDisableExcludeds`, {workerFk: this.entity.id, dated: new Date});
this.$http.post(`workerDisableExcludeds`, {workerFk: this.entity.id, dated: new Date});
this.excluded = true; this.workerExcluded = !this.workerExcluded;
}
} }
loadData() { loadData() {

View File

@ -4,7 +4,7 @@
filter="::$ctrl.filter" filter="::$ctrl.filter"
data="$ctrl.hours"> data="$ctrl.hours">
</vn-crud-model> </vn-crud-model>
<div ng-if="$ctrl.worker.hasWorkCenter"> <div ng-if="$ctrl.card.hasWorkCenter">
<vn-card class="vn-pa-lg vn-w-lg"> <vn-card class="vn-pa-lg vn-w-lg">
<vn-table model="model" auto-load="false"> <vn-table model="model" auto-load="false">
<vn-thead> <vn-thead>
@ -107,7 +107,7 @@
</vn-button-bar> </vn-button-bar>
</div> </div>
<div <div
ng-if="!$ctrl.worker.hasWorkCenter" ng-if="!$ctrl.card.hasWorkCenter"
class="bg-title" class="bg-title"
translate> translate>
Autonomous worker Autonomous worker
@ -136,6 +136,7 @@
</vn-calendar> </vn-calendar>
</div> </div>
</vn-side-menu> </vn-side-menu>
<vn-dialog <vn-dialog
vn-id="addTimeDialog" vn-id="addTimeDialog"
on-accept="$ctrl.addTime()" on-accept="$ctrl.addTime()"

View File

@ -141,6 +141,8 @@ class Controller extends Section {
]} ]}
}; };
this.$.model.applyFilter(filter, params).then(() => { this.$.model.applyFilter(filter, params).then(() => {
if (!this.card.hasWorkCenter) return;
this.getWorkedHours(this.started, this.ended); this.getWorkedHours(this.started, this.ended);
this.getAbsences(); this.getAbsences();
}); });
@ -151,7 +153,6 @@ class Controller extends Section {
} }
getAbsences() { getAbsences() {
if (!this.worker.hasWorkerCenter) return;
const fullYear = this.started.getFullYear(); const fullYear = this.started.getFullYear();
let params = { let params = {
workerFk: this.$params.id, workerFk: this.$params.id,
@ -486,5 +487,8 @@ ngModule.vnComponent('vnWorkerTimeControl', {
controller: Controller, controller: Controller,
bindings: { bindings: {
worker: '<' worker: '<'
},
require: {
card: '^vnWorkerCard'
} }
}); });

View File

@ -16,9 +16,8 @@ describe('Component vnWorkerTimeControl', () => {
$scope = $rootScope.$new(); $scope = $rootScope.$new();
$element = angular.element('<vn-worker-time-control></vn-worker-time-control>'); $element = angular.element('<vn-worker-time-control></vn-worker-time-control>');
controller = $componentController('vnWorkerTimeControl', {$element, $scope}); controller = $componentController('vnWorkerTimeControl', {$element, $scope});
controller.worker = { controller.card = {
hasWorkerCenter: true hasWorkCenter: true
}; };
})); }));

View File

@ -10,16 +10,17 @@ module.exports = {
async send(options) { async send(options) {
options.from = `${config.app.senderName} <${config.app.senderEmail}>`; options.from = `${config.app.senderName} <${config.app.senderEmail}>`;
if (!process.env.NODE_ENV) const env = process.env.NODE_ENV;
options.to = config.app.senderEmail; const canSend = env === 'production' || !env || options.force;
if (process.env.NODE_ENV !== 'production' && !options.force) { if (!canSend || !config.smtp.auth.user) {
const notProductionError = {message: 'This not production, this email not sended'}; const notProductionError = {message: 'This not production, this email not sended'};
await this.mailLog(options, notProductionError); await this.mailLog(options, notProductionError);
return Promise.resolve(true);
} }
if (!config.smtp.auth.user) if (!env)
return Promise.resolve(true); options.to = config.app.senderEmail;
let res; let res;
let error; let error;