Merge branch 'dev' of http://git.verdnatura.es/salix into dev

This commit is contained in:
Carlos Jimenez 2018-10-08 11:00:49 +02:00
commit fa385cd9d6
49 changed files with 993 additions and 63 deletions

View File

@ -88,7 +88,12 @@
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit label="Regularizar" vn-acl="administrative, salesAssistant"></vn-submit>
{{$ctrl.resolvedStateText}}
<vn-button
label="Regularize"
ng-click="$ctrl.regularize()"
disabled="$ctrl.claim.claimStateFk == $ctrl.resolvedState">
</vn-button>
</vn-button-bar>
<!-- WIP
<a ng-click="$ctrl.openAddSalesDialog()" vn-tooltip="New item" vn-bind="+" fixed-bottom-right>

View File

@ -22,6 +22,7 @@ class Controller {
{relation: 'claimBeggining'}
]
};
this.resolvedState = 3;
}
openAddSalesDialog() {
@ -118,6 +119,15 @@ class Controller {
this.$.model.refresh();
});
}
regularize() {
let data = {claimFk: this.$stateParams.id};
let query = `/claim/api/Claims/regularizeClaim`;
this.$http.post(query, data).then(() => {
this.card.reload();
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
});
}
}
Controller.$inject = ['$stateParams', '$scope', '$http', '$translate', 'vnApp'];
@ -127,5 +137,8 @@ ngModule.component('vnClaimAction', {
controller: Controller,
bindings: {
claim: '<'
},
require: {
card: '^vnClaimCard'
}
});

View File

@ -31,6 +31,7 @@ describe('claim', () => {
hide: () => {},
show: () => {}
};
controller.card = {reload: () => {}};
}));
describe('openAddSalesDialog()', () => {
@ -135,7 +136,7 @@ describe('claim', () => {
});
describe('importTicketLines()', () => {
it('should perform a post quer', () => {
it('should perform a post query', () => {
spyOn(controller.$.model, 'refresh');
spyOn(controller.vnApp, 'showSuccess');
spyOn(controller.$.lastTicketsPopover, 'hide');
@ -149,5 +150,20 @@ describe('claim', () => {
expect(controller.$.lastTicketsPopover.hide).toHaveBeenCalledWith();
});
});
describe('regularize()', () => {
it('should perform a post query and reload the claim card', () => {
spyOn(controller.card, 'reload');
spyOn(controller.vnApp, 'showSuccess');
let data = {claimFk: $state.params.id};
$httpBackend.expect('POST', `/claim/api/Claims/regularizeClaim`, data).respond({});
controller.regularize();
$httpBackend.flush();
expect(controller.card.reload).toHaveBeenCalledWith();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
});
});
});
});

View File

@ -4,4 +4,5 @@ Total claimed: Total Reclamado
Import claim: Importar reclamacion
Imports claim details: Importa detalles de la reclamacion
Import ticket: Importar ticket
Imports ticket lines: Importa las lineas de un ticket
Imports ticket lines: Importa las lineas de un ticket
Regularize: Regularizar

View File

@ -2,12 +2,9 @@ import ngModule from '../module';
import './style.scss';
class Controller {
constructor($state, $scope, $http, $translate, vnApp) {
constructor($state, $scope) {
this.$state = $state;
this.$ = $scope;
this.$http = $http;
this.$translate = $translate;
this.vnApp = vnApp;
}
onSubmit() {
@ -19,7 +16,7 @@ class Controller {
}
}
Controller.$inject = ['$state', '$scope', '$http', '$translate', 'vnApp'];
Controller.$inject = ['$state', '$scope'];
ngModule.component('vnClaimDevelopment', {
template: require('./index.html'),

View File

@ -5,4 +5,5 @@ Before: Antes
After: Despues
update: Actualizar
Create: Crear
History: Historial
History: Historial
insert: Crear

View File

@ -126,7 +126,7 @@
}
},
{
"url" : "/diary?warehouseFk",
"url" : "/diary?warehouseFk&ticketFk",
"state": "item.card.diary",
"component": "vn-item-diary",
"description": "Diary",

View File

@ -16,15 +16,14 @@
value-field="id"
initial-data="$ctrl.warehouseFk"
field="$ctrl.warehouseFk"
label="Select warehouse"
on-change="$ctrl.onChange(value)">
label="Select warehouse">
</vn-autocomplete>
</vn-horizontal>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th>Date</vn-th>
<vn-th number>Id</vn-th>
<vn-th number order="DESC">Id</vn-th>
<vn-th>State</vn-th>
<vn-th>Reference</vn-th>
<vn-th>Client</vn-th>
@ -34,9 +33,9 @@
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-class="::{'warning': $ctrl.isToday(sale.date)}"
ng-repeat="sale in sales" vn-repeat-last on-last="$ctrl.scrollToActive()">
<vn-td>{{::sale.date | date:'dd/MM/yyyy HH:mm' }}</vn-td>
<vn-tr ng-class="::{'warning': $ctrl.isToday(sale.date), 'isIn': sale.in, 'balanceNegative': sale.balance < 0}"
ng-repeat="sale in sales" vn-repeat-last on-last="$ctrl.scrollToLine()">
<vn-td>{{::sale.date | date:'dd/MM/yyyy' }}</vn-td>
<vn-td number>
<span ng-class="::{'link pointer': sale.isTicket}"
ng-click="$ctrl.showDescriptor($event, sale)">
@ -45,16 +44,17 @@
</vn-td>
<vn-td>{{::sale.stateName | dashIfEmpty}}</vn-td>
<vn-td>{{::sale.reference | dashIfEmpty}}</vn-td>
<vn-td>
<span ng-class="::{'link pointer': sale.isTicket}"
<vn-td class="truncate">
<span
ng-class="::{'link pointer': sale.isTicket}"
ng-click="$ctrl.showClientDescriptor($event, sale)">
{{::sale.name | dashIfEmpty}}
</span>
</vn-td>
<vn-td number>{{::sale.in | dashIfEmpty}}</vn-td>
<vn-td number class="in">{{::sale.in | dashIfEmpty}}</vn-td>
<vn-td number>{{::sale.out | dashIfEmpty}}</vn-td>
<vn-td number>
<span class="balance">
<vn-td number class="balance">
<span class="balanceSpan">
{{::sale.balance | dashIfEmpty}}
</span>
</vn-td>

View File

@ -27,6 +27,9 @@ class Controller {
this.warehouseFk = this.$stateParams.warehouseFk;
else if (value)
this.warehouseFk = value.itemType.warehouseFk;
if (this.$stateParams.ticketFk)
this.ticketFk = this.$stateParams.ticketFk;
});
}
@ -74,16 +77,33 @@ class Controller {
}
}
scrollToActive() {
get givenTicketIndex() {
let lines = this.$scope.model.data;
for (let i = lines.length - 1; i > 0; i--) {
let line = lines[i];
if (line.origin == this.ticketFk)
return i;
}
}
scrollToLine() {
let body = this.$window.document.body;
let lineIndex = this.onPreparationLineIndex;
let lineIndex;
if (this.ticketFk)
lineIndex = this.givenTicketIndex;
else
lineIndex = this.onPreparationLineIndex;
let lines = body.querySelector('vn-tbody').children;
if (!lineIndex || !lines.length) return;
let onPreparationLine = lines[lineIndex];
let balance = onPreparationLine.querySelector('.balance');
let balance = onPreparationLine.querySelector('.balanceSpan');
balance.classList.add('counter');
balance.title = this.$translate.instant('Visible quantity');
@ -92,6 +112,7 @@ class Controller {
let offsetTop = onPreparationLine.offsetTop - headerHeight;
body.querySelector('ui-view').scrollTop = offsetTop;
this.ticketFk = null;
}
/**

View File

@ -98,6 +98,22 @@ describe('Item', () => {
expect(controller.item.id).toEqual(1);
});
});
describe('givenTicketIndex() setter', () => {
it(`should return the index position of the line of a given ticket fk`, () => {
controller.$scope.model = {data: [
{name: 'My item 1', origin: 1, alertLevel: 3, isPicked: true, date: '2018-05-02'},
{name: 'My item 3', origin: 2, alertLevel: 1, isPicked: true, date: '2018-05-03'},
{name: 'My item 4', origin: 3, alertLevel: 1, isPicked: false, date: '2018-05-03'}]
};
controller.ticketFk = 2;
let index = controller.givenTicketIndex;
expect(controller.$scope.model.data[index].origin).toEqual(2);
});
});
});
});

View File

@ -1,8 +1,34 @@
@import 'colors';
vn-item-diary {
& > vn-vertical {
display: block;
}
vn-card {
margin: auto;
max-width: 880px;
}
& vn-horizontal {
justify-content: center;
}
& vn-autocomplete > div{
width: 400px;
}
.balanceNegative .balance {
background-color: $main-01;
color: white;
}
.isIn .in {
background-color: $main-02;
color: white;
}
.truncate {
max-width: 250px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}

View File

@ -4,3 +4,4 @@ import './main-menu/main-menu';
import './left-menu/left-menu';
import './left-menu/menu-item';
import './topbar/topbar';
import './user-configuration-popover'

View File

@ -1,6 +1,8 @@
<div style="position: fixed; top: 0; right: 0; padding: .8em 1.5em; z-index: 10;">
<div
id="user">
ng-click="$ctrl.openUserConfiguration($event)"
id="user"
class="unselectable">
<h6>{{currentUserName}}</h6>
</div>
<a href="/salix/version-notes.html"
@ -47,6 +49,7 @@
translate-attr="{title: 'Logout'}"
ng-click="$ctrl.onLogoutClick()">
</vn-icon>
<!--
TODO: Keep it commented until they are functional
@ -54,3 +57,8 @@
<vn-icon icon="account_circle" translate-attr="{title: 'Profile'}"></vn-icon>
-->
</div>
<vn-popover vn-id="popover">
<vn-user-configuration-popover>
</vn-user-configuration-popover>
</vn-popover>

View File

@ -34,6 +34,11 @@ export default class MainMenu {
});
}
openUserConfiguration(event) {
this.$.popover.parent = event.target;
this.$.popover.show();
}
onLogoutClick() {
this.$window.location = '/logout';
}

View File

@ -0,0 +1,79 @@
<vn-crud-model
url="/api/Banks"
data="banks"
order="bank">
</vn-crud-model>
<vn-crud-model
url="/api/Warehouses"
data="warehouses"
order="name">
</vn-crud-model>
<vn-crud-model
url="/api/Companies"
data="companies"
order="code">
</vn-crud-model>
<vn-vertical class="body">
<form name="form" ng-submit="$ctrl.onSubmit()">
<vn-horizontal>
<vn-autocomplete
vn-one
label="Local warehouse"
id="localWarehouse"
field="$ctrl.localWarehouse"
data="warehouses"
select-fields="['id','name']"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
label="Local bank"
id="localBank"
field="$ctrl.localBank"
data="banks"
select-fields="['id','bank']"
show-field="bank"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
label="Local company"
id="localCompany"
field="$ctrl.localCompany"
data="companies"
select-fields="['id','code']"
show-field="code"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
label="User warehouse"
id="userWarehouse"
field="$ctrl.warehouseFk"
data="warehouses"
select-fields="['id', 'name']"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
label="User company"
id="userCompany"
field="$ctrl.companyFk"
data="companies"
select-fields="['id', 'code']"
show-field="code"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
</form>
</vn-vertical>

View File

@ -0,0 +1,90 @@
import ngModule from '../../module';
import './style.scss';
class Controller {
constructor($scope, $http, $state, vnApp, $translate) {
this.$scope = $scope;
this.$http = $http;
this.$state = $state;
this.vnApp = vnApp;
this.$translate = $translate;
this.getUserConfig();
}
set localBank(value) {
window.localStorage.localBank = value;
this.showOk();
}
get localBank() {
return parseInt(window.localStorage.localBank);
}
set localWarehouse(value) {
window.localStorage.localWarehouse = value;
this.showOk();
}
get localWarehouse() {
return parseInt(window.localStorage.localWarehouse);
}
set localCompany(value) {
window.localStorage.localCompany = value;
this.showOk();
}
get localCompany() {
return parseInt(window.localStorage.localCompany);
}
set warehouseFk(value) {
this.warehouse = value;
this.setUserConfig('warehouseFk');
}
get warehouseFk() {
return this.warehouse;
}
set companyFk(value) {
this.company = value;
this.setUserConfig('companyFk');
}
get companyFk() {
return this.company;
}
showOk() {
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
}
getUserConfig() {
this.$http.get('/api/UserConfigs/getUserConfig')
.then(res => {
if (res.data && res.data.warehouseFk)
this.warehouse = res.data.warehouseFk;
if (res.data && res.data.companyFk)
this.company = res.data.companyFk;
});
}
setUserConfig(property) {
let params = {};
params[property] = this[property];
this.$http.post('/api/UserConfigs/setUserConfig', params)
.then(() => {
this.showOk();
});
}
}
Controller.$inject = ['$scope', '$http', '$state', 'vnApp', '$translate'];
ngModule.component('vnUserConfigurationPopover', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,97 @@
import './index.js';
describe('Salix', () => {
describe('Component vnUserConfigurationPopover', () => {
let $componentController;
let controller;
let $httpBackend;
let $scope;
beforeEach(() => {
angular.mock.module('salix');
});
beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_, $rootScope) => {
$componentController = _$componentController_;
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
$scope = $rootScope.$new();
controller = $componentController('vnUserConfigurationPopover', {$scope: $scope, $translate: null});
}));
describe('localBank() setter', () => {
it('should set window.localStorage.localBank and call showOk', () => {
spyOn(controller, 'showOk')
controller.localBank = 4;
expect(window.localStorage.localBank).toBe('4');
expect(controller.showOk).toHaveBeenCalledWith();
});
});
describe('localWarehouse() setter', () => {
it('should set window.localStorage.localWarehouse and call showOk', () => {
spyOn(controller, 'showOk')
controller.localWarehouse = 4;
expect(window.localStorage.localWarehouse).toBe('4');
expect(controller.showOk).toHaveBeenCalledWith();
});
});
describe('localCompany() setter', () => {
it('should set window.localStorage.localCompany and call showOk', () => {
spyOn(controller, 'showOk')
controller.localCompany = 4;
expect(window.localStorage.localCompany).toBe('4');
expect(controller.showOk).toHaveBeenCalledWith();
});
});
describe('warehouseFk() setter', () => {
it('should set warehouse and call setUserConfig', () => {
spyOn(controller, 'setUserConfig')
controller.warehouseFk = 4;
expect(controller.warehouse).toBe(4);
expect(controller.setUserConfig).toHaveBeenCalledWith('warehouseFk');
});
});
describe('companyFk() setter', () => {
it('should set company and call setUserConfig', () => {
spyOn(controller, 'setUserConfig')
controller.companyFk = 4;
expect(controller.company).toBe(4);
expect(controller.setUserConfig).toHaveBeenCalledWith('companyFk');
});
});
describe('getUserConfig()', () => {
it('should make a query, set company and not set warehouse if its not in the response', () => {
$httpBackend.when('GET', `/api/UserConfigs/getUserConfig`).respond({response: {companyFk: 2}});
$httpBackend.expect('GET', `/api/UserConfigs/getUserConfig`);
controller.getUserConfig();
$httpBackend.flush();
expect(controller.warehouse).toBeUndefined();
expect(controller.company).toEqual(2);
});
});
describe('setUserConfig()', () => {
it('should make a query with the property given and call showOk', () => {
spyOn(controller, 'showOk');
controller.company = 1;
$httpBackend.when('POST', `/api/UserConfigs/setUserConfig`, {companyFk: 1}).respond(200);
$httpBackend.expect('POST', `/api/UserConfigs/setUserConfig`, {companyFk: 1});
controller.setUserConfig('companyFk');
$httpBackend.flush();
expect(controller.showOk).toHaveBeenCalledWith();
});
});
});
});

View File

@ -0,0 +1,12 @@
@import 'colors';
vn-user-configuration-popover {
color: $main-font-color;
& > vn-vertical {
min-width: 250px;
}
.body {
padding: 16px 16px 6px 16px;
}
}

View File

@ -93,7 +93,8 @@ class Controller {
icon: 'icon-transaction',
state: `item.card.diary({
id: ${itemFk},
q: '{"warehouseFk": ${this.ticket.warehouseFk}}'
warehouseFk: ${this.ticket.warehouseFk},
ticketFk: ${this.ticket.id}
})`,
tooltip: 'Item diary'
}

View File

@ -36,7 +36,7 @@
<span
ng-class="{'link pointer':expedition.itemFk}"
ng-click="$ctrl.showDescriptor($event, expedition.itemFk)">
{{expedition.itemFk}}
{{("000000"+expedition.itemFk).slice(-6)}}
</span>
</vn-td>
<vn-td>{{::expedition.namePackage}}</vn-td>

View File

@ -27,7 +27,8 @@ class Controller {
icon: 'icon-transaction',
state: `item.card.diary({
id: ${itemFk},
q: '{"warehouseFk": ${this.ticket.warehouseFk}}'
warehouseFk: ${this.ticket.warehouseFk},
ticketFk: ${this.ticket.id}
})`,
tooltip: 'Item diary'
}

View File

@ -73,4 +73,4 @@ Tracking: Revisión
Sale checked: Control clientes
Components: Componentes
Sale tracking: Líneas preparadas
Pictures: Imágenes
Pictures: Fotos

View File

@ -39,7 +39,8 @@ class Controller {
icon: 'icon-transaction',
state: `item.card.diary({
id: ${itemFk},
q: '{"warehouseFk": ${this.ticket.warehouseFk}}'
warehouseFk: ${this.ticket.warehouseFk},
ticketFk: ${this.ticket.id}
})`,
tooltip: 'Item diary'
}

View File

@ -12,7 +12,8 @@ class Controller {
icon: 'icon-transaction',
state: `item.card.diary({
id: ${itemFk},
q: '{"warehouseFk": ${this.ticket.warehouseFk}}'
warehouseFk: ${this.ticket.warehouseFk},
ticketFk: ${this.ticket.id}
})`,
tooltip: 'Item diary'
}

View File

@ -243,7 +243,8 @@ class Controller {
icon: 'icon-transaction',
state: `item.card.diary({
id: ${itemFk},
q: '{"warehouseFk": ${this.ticket.warehouseFk}}'
warehouseFk: ${this.ticket.warehouseFk},
ticketFk: ${this.ticket.id}
})`,
tooltip: 'Item diary'
}

View File

@ -37,7 +37,8 @@ class Controller {
icon: 'icon-transaction',
state: `item.card.diary({
id: ${itemFk},
q: '{"warehouseFk": ${this.ticket.warehouseFk}}'
warehouseFk: ${this.ticket.warehouseFk},
ticketFk: ${this.ticket.id}
})`,
tooltip: 'Item diary'
}

View File

@ -52,7 +52,8 @@ class Controller {
icon: 'icon-transaction',
state: `item.card.diary({
id: ${itemFk},
q: '{"warehouseFk": ${this.ticket.warehouseFk}}'
warehouseFk: ${this.ticket.warehouseFk},
ticketFk: ${this.ticket.id}
})`,
tooltip: 'Item diary'
}

View File

@ -60,7 +60,6 @@ describe('Client lock verified data path', () => {
return nightmare
.wait(selectors.clientFiscalData.verifiedDataCheckboxInput)
.evaluate(selector => {
console.log(document.querySelector(selector));
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.verifiedDataCheckbox)
.then(result => {

View File

@ -0,0 +1,156 @@
module.exports = Self => {
Self.remoteMethodCtx('regularizeClaim', {
description: 'Imports lines from claimBeginning to a new ticket with specific shipped, landed dates, agency and company',
accessType: 'WRITE',
accepts: [{
arg: 'params',
type: 'object',
http: {source: 'body'}
}],
returns: {
type: ['Object'],
root: true
},
http: {
path: `/regularizeClaim`,
verb: 'POST'
}
});
Self.regularizeClaim = async (ctx, params) => {
const userId = ctx.req.accessToken.userId;
const models = Self.app.models;
const resolvedState = 3;
const claimEnds = await models.ClaimEnd.find({
include: {
relation: 'claimDestination',
fields: ['addressFk']
},
where: {claimFk: params.claimFk}
});
let transaction = await Self.beginTransaction({});
try {
for (let i = 0; i < claimEnds.length; i++) {
const claimEnd = claimEnds[i];
const destination = claimEnd.claimDestination();
const addressFk = destination && destination.addressFk;
if (!addressFk)
continue;
let sale = await getSale(claimEnd.saleFk);
let ticketFk = await getTicketId({
addressFk: addressFk,
companyFk: sale.ticket().companyFk,
warehouseFk: sale.ticket().warehouseFk
}, transaction);
let address = await models.Address.findOne({
where: {id: addressFk}
});
if (!ticketFk) {
ticketFk = await createTicket({
clientFk: address.clientFk,
addressFk: addressFk,
warehouseFk: sale.ticket().warehouseFk,
companyFk: sale.ticket().companyFk,
userId: userId
}, transaction);
}
await models.Sale.create({
ticketFk: ticketFk,
itemFk: sale.itemFk,
concept: sale.concept,
quantity: -sale.quantity,
price: sale.price,
discount: 100
}, {transaction: transaction});
await sendMessage(ctx, {
itemFk: sale.itemFk,
ticketFk: sale.ticketFk,
workerFk: sale.item().itemType().workerFk,
quantity: sale.quantity,
concept: sale.concept,
nickname: address.nickname
}, transaction);
}
let claim = await Self.findById(params.claimFk);
claim = await claim.updateAttributes({
claimStateFk: resolvedState
}, {transaction: transaction});
await transaction.commit();
return claim;
} catch (e) {
await transaction.rollback();
throw e;
}
};
async function getSale(saleFk) {
return await Self.app.models.Sale.findOne({
include: [
{
relation: 'ticket',
scope: {fields: ['warehouseFk', 'companyFk']}
},
{
relation: 'item',
scope: {
fields: ['typeFk'],
include: {
relation: 'itemType',
scope: {fields: ['workerFk']}
}
}
}],
where: {id: saleFk}
});
}
async function getTicketId(params, transaction) {
const currentDate = new Date();
currentDate.setHours(0, 0, 0, 0);
let ticket = await Self.app.models.Ticket.findOne({
where: {
addressFk: params.addressFk,
companyFk: params.companyFk,
warehouseFk: params.warehouseFk,
shipped: currentDate,
landed: currentDate
}
}, {transaction: transaction});
return ticket && ticket.id;
}
async function createTicket(params, transaction) {
return await Self.app.models.Ticket.new({
shipped: new Date(),
landed: new Date(),
clientFk: params.clientFk,
warehouseFk: params.warehouseFk,
companyFk: params.companyFk,
addressFk: params.addressFk,
userId: params.userId
}, {transaction: transaction});
}
async function sendMessage(ctx, params, transaction) {
const message = `Envio ${params.quantity} unidades de "${params.concept}" (#${params.itemFk}) a `
+ `"${params.nickname}" provenientes del ticket #${params.ticketFk}`;
await Self.app.models.Message.send(ctx, {
recipientFk: params.workerFk,
message: message
}, {transaction: transaction});
}
};

View File

@ -0,0 +1,45 @@
const app = require(`${servicesDir}/claim/server/server`);
describe('regularizeClaim()', () => {
const claimFk = 1;
const pendentState = 1;
const resolvedState = 3;
const trashDestination = 2;
const trashAddress = 12;
let claimEnds = [];
let trashTicket;
afterAll(async() => {
let claim = await app.models.Claim.findById(claimFk);
await claim.updateAttributes({claimStateFk: pendentState});
await app.models.Ticket.destroyById(trashTicket.id);
claimEnds.forEach(async line => {
await line.destroy();
});
});
it('should change claim state to resolved', async() => {
let ctx = {req: {accessToken: {userId: 18}}};
let params = {claimFk: claimFk};
claimEnds = await app.models.ClaimEnd.importTicketSales(ctx, {
claimFk: claimFk,
ticketFk: 1
});
claimEnds.forEach(async claimEnd => {
claimEnd.updateAttributes({claimDestinationFk: trashDestination});
});
let claimBefore = await app.models.Claim.findById(params.claimFk);
await app.models.Claim.regularizeClaim(ctx, params);
let claimAfter = await app.models.Claim.findById(params.claimFk);
trashTicket = await app.models.Ticket.findOne({where: {addressFk: 12}});
expect(trashTicket.addressFk).toEqual(trashAddress);
expect(claimBefore.claimStateFk).toEqual(pendentState);
expect(claimAfter.claimStateFk).toEqual(resolvedState);
});
});

View File

@ -18,6 +18,13 @@
"required": true
}
},
"relations": {
"address": {
"type": "belongsTo",
"model": "Address",
"foreignKey": "addressFk"
}
},
"acls": [
{
"accessType": "READ",

View File

@ -2,4 +2,5 @@ module.exports = Self => {
require('../methods/claim/getSummary')(Self);
require('../methods/claim/createFromSales')(Self);
require('../methods/claim/updateClaim')(Self);
require('../methods/claim/regularizeClaim')(Self);
};

View File

@ -71,13 +71,15 @@ module.exports = function(Self) {
let customer = insurance.classification().customer();
if (!customer.salesPerson()) return;
let salesPerson = customer.salesPerson().user().name;
let salesPersonId = customer.salesPerson().user().id;
let grade = data.grade ? `(Grado ${data.grade})` : '(Sin grado)';
let message = {
message: `He cambiado el crédito asegurado del cliente "${customer.name}" a ${data.credit}${grade}`
let params = {
recipientFk: salesPersonId,
message: `He cambiado el crédito asegurado del `
+ `cliente "${customer.name}" a ${data.credit}${grade}`
};
Self.app.models.Message.send(salesPerson, message, ctx);
Self.app.models.Message.send(ctx, params);
};
// Update from transaction misses ctx accessToken.

View File

@ -4,7 +4,10 @@ VALUES
(105, 'ItemBarcode', '*', 'WRITE', 'ALLOW', 'ROLE', 'marketingBoss'),
(106, 'ItemBotanical', '*', 'WRITE', 'ALLOW', 'ROLE', 'marketingBoss'),
(107, 'ItemNiche', '*', 'WRITE', 'ALLOW', 'ROLE', 'marketingBoss'),
(108, 'ItemPlacement', '*', 'WRITE', 'ALLOW', 'ROLE', 'marketingBoss');
(108, 'ItemPlacement', '*', 'WRITE', 'ALLOW', 'ROLE', 'marketingBoss'),
(109, 'UserConfig', '*', '*', 'ALLOW', 'ROLE', 'employee'),
(110, 'Bank', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
UPDATE salix.ACL
SET model='ItemTag', property='*', accessType='WRITE', permission='ALLOW', principalType='ROLE', principalId='marketingBoss'

View File

@ -0,0 +1,119 @@
USE `vn`;
DROP procedure IF EXISTS `itemDiary`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `itemDiary`(IN vItemId INT, IN vWarehouse INT)
BEGIN
DECLARE vDateInventory DATETIME;
DECLARE vCurdate DATE DEFAULT CURDATE();
DECLARE vDayEnd DATETIME DEFAULT util.dayEnd(vCurdate);
-- traduccion: date, alertLevel, origin, reference, name, In, Out, Balance
SELECT Fechainventario INTO vDateInventory FROM vn2008.tblContadores;
SET @a = 0;
SELECT sql_no_cache DATE(date) AS date,
alertLevel,
stateName,
origin,
reference,
clientFk,
name,
`in`,
`out`,
@a := @a + IFNULL(`in`,0) - IFNULL(`out`,0) as balance,
isPicked,
isTicket
FROM
( SELECT tr.landed as date,
b.quantity as `in`,
NULL as `out`,
IF(tr.isReceived != FALSE,3, IF(tr.isDelivered,1,0)) as alertLevel,
st.name AS stateName,
s.name as name,
e.ref as reference,
e.id as origin,
s.id as clientFk,
TRUE isPicked,
FALSE AS isTicket
FROM vn.buy b
JOIN vn.entry e ON e.id = b.entryFk
JOIN vn.travel tr ON tr.id = e.travelFk
JOIN vn.supplier s ON s.id = e.supplierFk
JOIN vn.alertLevel al ON al.alertLevel =
CASE
WHEN tr.isReceived != FALSE THEN 3
WHEN tr.isDelivered THEN 1
ELSE 0
END
JOIN vn.state st ON st.code = al.code
WHERE tr.landed >= vDateInventory
AND vWarehouse = tr.warehouseInFk
AND b.itemFk = vItemId
AND e.isInventory = 0
UNION ALL
SELECT tr.shipped as date,
NULL as `in`,
b.quantity as `out`,
IF(tr.isReceived != FALSE,3, IF(tr.isDelivered,1,0)) as alertLevel,
st.name AS stateName,
s.name as name,
e.ref as reference,
e.id as origin,
s.id as clientFk,
TRUE isPicked,
FALSE AS isTicket
FROM vn.buy b
JOIN vn.entry e ON e.id = b.entryFk
JOIN vn.travel tr ON tr.id = e.travelFk
JOIN vn.warehouse w ON w.id = tr.warehouseOutFk
JOIN vn.supplier s ON s.id = e.supplierFk
JOIN vn.alertLevel al ON al.alertLevel =
CASE
WHEN tr.isReceived != FALSE THEN 3
WHEN tr.isDelivered THEN 1
ELSE 0
END
JOIN vn.state st ON st.code = al.code
WHERE tr.shipped >= vDateInventory
AND vWarehouse =tr.warehouseOutFk
AND s.id <> 4
AND b.itemFk = vItemId
AND e.isInventory = 0
AND w.isFeedStock = 0
UNION ALL
SELECT t.shipped as date,
NULL as `in`,
s.quantity as `out`,
al.alertLevel as alertLevel,
st.name AS stateName,
t.nickname as name,
t.refFk as reference,
t.id as origin,
t.clientFk,
TRUE as isPicked, -- stk.id as isPicked
TRUE as isTicket
FROM vn.sale s
JOIN vn.ticket t ON t.id = s.ticketFk
LEFT JOIN vn.ticketState ts ON ts.ticket = t.id
JOIN vn.client c ON c.id = t.clientFk
JOIN vn.alertLevel al ON al.alertLevel =
CASE
WHEN t.shipped < vCurdate THEN 3
WHEN t.shipped > vDayEnd THEN 0
ELSE IFNULL(ts.alertLevel, 0)
END
JOIN vn.state st ON st.code = al.code
-- LEFT JOIN vn.saleTracking stk ON stk.saleFk = s.id AND stk.stateFk = 14
WHERE t.shipped >= vDateInventory
AND s.itemFk = vItemId
AND vWarehouse =t.warehouseFk
) AS itemDiary
ORDER BY date DESC, alertLevel DESC, isPicked DESC, `in` DESC, `out` DESC;
END$$
DELIMITER ;

View File

@ -483,7 +483,7 @@ INSERT INTO `vn`.`taxCode`(`id`, `dated`, `code`, `taxTypeFk`, `rate`, `equaliza
VALUES
(1 , CURDATE(), '1111111111', 1, 7.0 , 0.0, 'R', 1, 1, CURDATE(), 1),
(2 , CURDATE(), '2222222222', 2, 16.0, 0.0, 'G', 2, 1, CURDATE(), 1),
(21 , CURDATE(), '3333333333', 1, 18.0, 0.0, 'R', 3, 1, CURDATE(), 1),
(21 , CURDATE(), '3333333333', 1, 7.0, 0.0, 'R', 3, 1, CURDATE(), 1),
(108, CURDATE(), '4444444444', 2, 8.0 , 0.0, 'R', 4, 1, CURDATE(), 1);
INSERT INTO `vn`.`taxClass`(`id`, `description`, `code`)
@ -928,4 +928,7 @@ INSERT INTO `vn`.`orderTicket`(`orderFk`, `ticketFk`)
(18, 18),
(19, 19),
(20, 20),
(21, 21);
(21, 21);
INSERT INTO `vn`.`userConfig` (`userFk`, `warehouseFk`, `companyFk`)
VALUES (9, 1, 442);

View File

@ -51,5 +51,6 @@
"You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo",
"Warehouse cannot be blank": "El almacén no puede quedar en blanco",
"Agency cannot be blank": "La agencia no puede quedar en blanco",
"You don't have enough privileges to do that": "No tienes permisos para para hacer esto"
"You don't have enough privileges to do that": "No tienes permisos para para hacer esto",
"This address doesn't exist": "This address doesn't exist"
}

View File

@ -1,18 +1,12 @@
module.exports = Self => {
Self.remoteMethod('send', {
Self.remoteMethodCtx('send', {
description: 'Send message to user',
accessType: 'WRITE',
accepts: [{
arg: 'recipient',
type: 'string',
required: true,
description: 'The user/alias name',
http: {source: 'path'}
}, {
arg: 'data',
type: 'object',
required: true,
description: 'Message data',
description: 'recipientFk, message',
http: {source: 'body'}
}, {
arg: 'context',
@ -31,10 +25,23 @@ module.exports = Self => {
}
});
Self.send = async(recipient, data, ctx) => {
let query = `SELECT vn.messageSendWithUser(?, ?, ?) AS sent`;
let [result] = await Self.rawSql(query, [ctx.req.accessToken.userId, recipient, data.message]);
Self.send = async(ctx, data, transaction) => {
const userId = ctx.req.accessToken.userId;
const models = Self.app.models;
const sender = await models.Account.findById(userId, transaction);
const recipient = await models.Account.findById(data.recipientFk, transaction);
return result;
await Self.create({
sender: sender.name,
recipient: recipient.name,
message: data.message
}, transaction);
return await models.MessageInbox.create({
sender: sender.name,
recipient: recipient.name,
finalRecipient: recipient.name,
message: data.message
}, transaction);
};
};

View File

@ -3,9 +3,12 @@ const app = require(`${servicesDir}/client/server/server`);
describe('message send()', () => {
it('should call the send method and return the response', async() => {
let ctx = {req: {accessToken: {userId: 1}}};
await app.models.Message.send('salesPerson', {message: 'I changed something'}, ctx)
.then(response => {
expect(response.sent).toEqual(1);
});
let params = {
recipientFk: 1,
message: 'I changed something'
};
let response = await app.models.Message.send(ctx, params, {transaction: 'You'});
expect(response.message).toEqual(params.message);
});
});

View File

@ -0,0 +1,21 @@
module.exports = function(Self) {
Self.remoteMethodCtx('getUserConfig', {
description: 'returns the information from UserConfig model for the active user',
accepts: [],
returns: {
type: 'object',
root: true
},
http: {
path: `/getUserConfig`,
verb: 'get'
}
});
Self.getUserConfig = async ctx => {
let token = ctx.req.accessToken;
let currentUserId = token && token.userId;
return await Self.findOne({userFk: currentUserId});
};
};

View File

@ -0,0 +1,28 @@
module.exports = function(Self) {
Self.remoteMethodCtx('setUserConfig', {
description: 'Change worker of tickets state',
accepts: [{
arg: 'params',
type: 'object',
required: true,
description: 'warehouseFk, companyFk',
http: {source: 'body'}
}],
returns: {
arg: 'response',
type: 'object'
},
http: {
path: `/setUserConfig`,
verb: 'post'
}
});
Self.setUserConfig = async(ctx, params) => {
let token = ctx.req.accessToken;
let currentUserId = token && token.userId;
params.userFk = currentUserId;
return await Self.app.models.UserConfig.upsertWithWhere({userFk: currentUserId}, params);
}
};

View File

@ -0,0 +1,40 @@
{
"name": "Bank",
"base": "VnModel",
"options": {
"mysql": {
"table": "bank"
}
},
"properties": {
"id": {
"type": "Number",
"id": true,
"description": "Identifier"
},
"bank": {
"type": "string",
"required": true
},
"account": {
"type": "string",
"required": true
},
"cash": {
"type": "string",
"required": true
},
"entityFk": {
"type": "string",
"required": true
},
"isActive": {
"type": "string",
"required": true
},
"currencyFk": {
"type": "string",
"required": true
}
}
}

View File

@ -17,8 +17,7 @@
"required": true
},
"userFk": {
"type": "Number",
"required": true
"type": "Number"
},
"action": {
"type": "String",

View File

@ -127,7 +127,7 @@ module.exports = function(Self) {
let userFk;
if (!loopBackContext) userFk = process.env.NODE_ENV ? 1765 : 111;
if (!loopBackContext) userFk = null;
else userFk = loopBackContext.active.accessToken.userId;
let action = setActionType(ctx);

View File

@ -0,0 +1,43 @@
{
"name": "MessageInbox",
"base": "VnModel",
"options": {
"mysql": {
"table": "messageInbox"
}
},
"properties": {
"id": {
"type": "Number",
"id": true,
"description": "Identifier"
},
"sender": {
"type": "String",
"required": true
},
"recipient": {
"type": "String",
"required": true
},
"finalRecipient": {
"type": "String",
"required": true
},
"message": {
"type": "String"
}
},
"relations": {
"remitter": {
"type": "belongsTo",
"model": "User",
"foreignKey": "sender"
},
"receptor": {
"type": "belongsTo",
"model": "User",
"foreignKey": "recipient"
}
}
}

View File

@ -0,0 +1,4 @@
module.exports = Self => {
require('../methods/userConfig/setUserConfig')(Self);
require('../methods/userConfig/getUserConfig')(Self);
};

View File

@ -0,0 +1,46 @@
{
"name": "UserConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "userConfig",
"database": "vn"
}
},
"properties": {
"userFk": {
"id": true,
"type": "Number",
"required": true
},
"warehouseFk": {
"type": "Number"
},
"companyFk": {
"type": "Number"
},
"created": {
"type": "Date"
},
"updated": {
"type": "Date"
}
},
"relations": {
"warehouse": {
"type": "belongsTo",
"model": "Warehouse",
"foreignKey": "warehouseFk"
},
"company": {
"type": "belongsTo",
"model": "Company",
"foreignKey": "companyFk"
},
"account": {
"type": "belongsTo",
"model": "Account",
"foreignKey": "userFk"
}
}
}

View File

@ -42,6 +42,9 @@
"AgencyMode": {
"dataSource": "vn"
},
"Bank": {
"dataSource": "vn"
},
"Client": {
"dataSource": "vn"
},
@ -120,10 +123,16 @@
"Message": {
"dataSource": "vn"
},
"MessageInbox": {
"dataSource": "vn"
},
"WorkerMana": {
"dataSource": "bs"
},
"DeliveryMethod": {
"dataSource": "vn"
},
"UserConfig": {
"dataSource": "vn"
}
}

View File

@ -22,7 +22,6 @@ module.exports = Self => {
});
Self.addToOrder = async params => {
console.log(params);
let isEditable = await Self.app.models.Order.isEditable(params.orderFk);
if (!isEditable)