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

This commit is contained in:
Carlos Jimenez 2018-08-09 13:14:05 +02:00
commit e7d45274d9
27 changed files with 700 additions and 214 deletions

View File

@ -25,8 +25,7 @@ describe('Client', () => {
};
}
};
$httpBackend.get = jasmine.createSpy('get').and.returnValue(Promise.resolve());
controller = $componentController('vnClientBillingData', {$scope: $scope}, {$http: $httpBackend});
controller = $componentController('vnClientBillingData', {$scope: $scope});
controller.client = {id: 101, name: 'Client name', payMethodFk: 4};
}));
@ -47,6 +46,15 @@ describe('Client', () => {
});
});
describe('notifyChanges()', () => {
it(`should call notifyChanges() and perform a GET query`, () => {
$httpBackend.when('GET', `/mailer/notification/payment-update/101`).respond(true);
$httpBackend.expect('GET', `/mailer/notification/payment-update/101`);
controller.notifyChanges();
$httpBackend.flush();
});
});
describe('hasPaymethodChanged()', () => {
it(`should call hasPaymethodChanged() and return true if there are changes on payMethod data`, () => {
controller.client.payMethodFk = 5;

View File

@ -1,4 +1,4 @@
<form name="form" ng-submit="$ctrl.submit()">
<form name="form" ng-submit="$ctrl.onSubmit()">
<vn-card pad-large>
<vn-title>New contract</vn-title>
<vn-horizontal>

View File

@ -12,7 +12,7 @@ class Controller {
};
}
submit() {
onSubmit() {
if (this.$scope.form.$invalid)
return this.vnApp.showError(this.$translate.instant('Some fields are invalid'));

View File

@ -0,0 +1,51 @@
import './index';
describe('Client', () => {
describe('Component vnClientCreditInsuranceCreate', () => {
let $componentController;
let controller;
let $scope;
let $httpBackend;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_) => {
$componentController = _$componentController_;
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
$scope = $rootScope.$new();
$scope.form = {
$invalid: false
};
controller = $componentController('vnClientCreditInsuranceCreate', {$scope: $scope});
controller.client = {id: 101};
controller.card = {
reload: () => {}
};
}));
describe('onSubmit()', () => {
it('should perform a POST query', () => {
controller.creditClassification = {
started: new Date(),
credit: 300,
grade: 1
};
let newData = {
started: new Date(),
credit: 300,
grade: 1,
clientFk: 101
};
$httpBackend.whenPOST(`/client/api/creditClassifications/createWithInsurance`, newData).respond(200, true);
$httpBackend.expectPOST(`/client/api/creditClassifications/createWithInsurance`, newData);
controller.onSubmit();
$httpBackend.flush();
});
});
});
});

View File

@ -1,6 +1,6 @@
import './index';
describe('Client', () => {
fdescribe('Client', () => {
describe('Component vnClientCreditInsuranceIndex', () => {
let $componentController;
let controller;
@ -15,6 +15,7 @@ describe('Client', () => {
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
controller = $componentController('vnClientCreditInsuranceIndex');
controller.client = {id: 101};
}));
describe('_getClassifications()', () => {
@ -73,5 +74,18 @@ describe('Client', () => {
expect(controller.$scope.closeContract.show).toHaveBeenCalledWith();
});
});
describe('returnDialog()', () => {
it('should call the returnDialog method and perform a PATCH query, then call _getClassifications method', () => {
spyOn(controller, '_getClassifications');
controller.classificationId = 1;
$httpBackend.when('PATCH', `/client/api/CreditClassifications/1`).respond(200);
$httpBackend.expect('PATCH', `/client/api/CreditClassifications/1`);
controller.returnDialog('ACCEPT');
$httpBackend.flush();
expect(controller._getClassifications).toHaveBeenCalledWith(101);
});
});
});
});

View File

@ -3,9 +3,7 @@
<vn-auto class="left-block">
<vn-item-descriptor
margin-medium-v
item="$ctrl.item"
item-tags="$ctrl.itemTags"
tags="$ctrl.tags">
item="$ctrl.item">
</vn-item-descriptor>
<vn-left-menu></vn-left-menu>
</vn-auto>

View File

@ -1,73 +1,24 @@
import ngModule from '../module';
class Controller {
constructor($http, $state, $timeout) {
constructor($http, $state) {
this.$http = $http;
this.$state = $state;
this.$timeout = $timeout;
this.item = null;
this.tags = {};
this.itemTags = null;
}
_getItemTags() {
let filter = {
where: {itemFk: this.$state.params.id},
order: "priority ASC",
include: {relation: "tag"}
};
this.$http.get(`/item/api/ItemTags?filter=${JSON.stringify(filter)}`).then(response => {
this.itemTags = response.data;
_getCard() {
this.$http.get(`/item/api/Items/${this.$state.params.id}/getCard`).then(response => {
this.item = response.data;
});
}
_getTags() {
this.$http.get(`/item/api/Tags`).then(response => {
response.data.forEach(tag => {
this.tags[tag.id] = Object.assign({}, tag);
});
});
}
_getItem() {
let filter = {
include: [
{relation: "itemType",
scope: {
fields: ['name', 'workerFk', 'warehouseFk'],
include: {
relation: 'worker',
fields: ['firstName', 'name']
}
}
},
{relation: "origin"},
{relation: "ink"},
{relation: "producer"},
{relation: "intrastat"},
{relation: "expence"}
]
};
this.$http.get(`/item/api/Items/${this.$state.params.id}?filter=${JSON.stringify(filter)}`)
.then(res => {
if (res.data && res.data.id) {
this.$timeout(() => {
this.item = res.data;
});
}
}
);
}
$onInit() {
this._getItem();
this._getTags();
this._getItemTags();
this._getCard();
}
}
Controller.$inject = ['$http', '$state', '$timeout'];
Controller.$inject = ['$http', '$state'];
ngModule.component('vnItemCard', {
template: require('./index.html'),

View File

@ -23,36 +23,10 @@ describe('Item', () => {
controller = $componentController('vnItemCard', {$state: $state});
}));
describe('_getItemTags()', () => {
it('should request to get the ItemTags', () => {
$httpBackend.whenGET('/item/api/ItemTags?filter={"where":{"itemFk":123},"order":"priority ASC","include":{"relation":"tag"}}').respond({data: 'data'});
$httpBackend.expectGET('/item/api/ItemTags?filter={"where":{"itemFk":123},"order":"priority ASC","include":{"relation":"tag"}}');
controller._getItemTags();
$httpBackend.flush();
});
});
describe('_getTags()', () => {
it('should request to get the Tags and store them in the controller using id as keys', () => {
let response = [{id: 5, name: "Diámetro", isFree: true, sourceTable: null, unit: "mm"}];
let result = {5: {id: 5, name: "Diámetro", isFree: true, sourceTable: null, unit: "mm"}};
expect(controller.tags).toEqual({});
$httpBackend.whenGET('/item/api/Tags').respond(response);
$httpBackend.expectGET('/item/api/Tags');
controller._getTags();
$httpBackend.flush();
expect(controller.tags).toEqual(result);
});
});
describe('_getItem()', () => {
it('should request to get the item', () => {
$httpBackend.whenGET('/item/api/Items/123?filter={"include":[{"relation":"itemType","scope":{"fields":["name","workerFk","warehouseFk"],"include":{"relation":"worker","fields":["firstName","name"]}}},{"relation":"origin"},{"relation":"ink"},{"relation":"producer"},{"relation":"intrastat"},{"relation":"expence"}]}').respond({data: 'item'});
$httpBackend.expectGET('/item/api/Items/123?filter={"include":[{"relation":"itemType","scope":{"fields":["name","workerFk","warehouseFk"],"include":{"relation":"worker","fields":["firstName","name"]}}},{"relation":"origin"},{"relation":"ink"},{"relation":"producer"},{"relation":"intrastat"},{"relation":"expence"}]}');
controller._getItem();
describe('_getCard()', () => {
it('should request to get the card', () => {
$httpBackend.expectGET('/item/api/Items/123/getCard').respond();
controller._getCard();
$httpBackend.flush();
});
});

View File

@ -3,15 +3,23 @@ import Component from 'core/src/lib/component';
import './style.scss';
class Controller extends Component {
constructor($element, $scope, $http, $timeout) {
constructor($element, $scope, $http) {
super($element, $scope);
this.$http = $http;
this.$timeout = $timeout;
this._quicklinks = {};
this.isTooltip = true;
this.clear();
}
set itemFk(id) {
this._itemFk = id;
if (id) {
this._getItem();
} else
this.clear();
}
set quicklinks(value = {}) {
this._quicklinks = Object.assign(value, this._quicklinks);
}
@ -30,70 +38,13 @@ class Controller extends Component {
this.$.popover.parent = this.parent;
this.$.popover.show();
}
_getTags() {
this.$http.get(`/item/api/Tags`).then(response => {
response.data.forEach(tag => {
this.tags[tag.id] = Object.assign({}, tag);
});
this.$.popover.relocate();
_getItem() {
this.$http.get(`/item/api/Items/${this._itemFk}/getCard`).then(response => {
this.item = response.data;
});
}
_getItem() {
let filter = {
fields: ['id', 'name', 'image'],
include: [
{
relation: 'itemType',
scope: {
fields: ['workerFk'],
include: {
relation: 'worker',
scope: {
fields: ['firstName', 'name']
}
}
}
}, {
relation: 'tags',
scope: {
fields: ['value', 'tagFk'],
order: 'priority ASC',
include: {
relation: 'tag',
scope: {
fields: ['name']
}
}
}
}
]
};
let json = encodeURIComponent(JSON.stringify(filter));
this.$http.get(`/item/api/Items/${this._itemFk}?filter=${json}`)
.then(res => {
if (!res.data) return;
this.item = res.data;
this.itemTags = this.item.tags;
this.$.popover.relocate();
}
);
}
set itemFk(id) {
this._itemFk = id;
if (id) {
this._getItem();
this._getTags();
} else
this.clear();
}
}
Controller.$inject = ['$element', '$scope', '$http', '$timeout'];
Controller.$inject = ['$element', '$scope', '$http'];
ngModule.component('vnItemDescriptorPopover', {
template: require('./index.html'),

View File

@ -0,0 +1,88 @@
import './index.js';
describe('Item', () => {
describe('Component vnItemDescriptorPopover', () => {
let $componentController;
let $scope;
let controller;
let $httpBackend;
let $element;
beforeEach(() => {
angular.mock.module('item');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_) => {
$componentController = _$componentController_;
$element = angular.element('<vn-item-descriptor-popover></vn-item-descriptor-popover>');
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
$scope = $rootScope.$new();
controller = $componentController('vnItemDescriptorPopover', {$scope, $element, $httpBackend});
controller.itemFk = 1;
controller.parent = 'mariano';
controller.$.popover = {show: () => {}};
}));
describe('itemFk setter', () => {
it(`shoud set _itemFk to a given value and call _getItem if the given value is not null`, () => {
spyOn(controller, '_getItem');
controller.itemFk = 5;
expect(controller._getItem).toHaveBeenCalledWith();
expect(controller._itemFk).toEqual(5);
});
it(`shoud call clear if the given values is null`, () => {
spyOn(controller, 'clear');
controller.itemFk = null;
expect(controller.clear).toHaveBeenCalledWith();
expect(controller._itemFk).toEqual(null);
});
});
describe('quicklinks setter', () => {
it(`shoud set _quicklinks to a given value`, () => {
controller.quicklinks = 3;
expect(controller._quicklinks).toEqual(3);
});
});
describe('clear()', () => {
it(`should set item and itemTags null, and tags {}`, () => {
controller.item = '1';
controller.itemTags = '1';
controller.tags = '1';
controller.clear();
expect(controller.item).toEqual(null);
expect(controller.itemTags).toEqual(null);
expect(controller.tags).toEqual({});
});
});
describe('show()', () => {
it(`should set $.popover.parent and call $.popover.show`, () => {
spyOn(controller.$.popover, 'show');
controller.show();
expect(controller.$.popover.show).toHaveBeenCalledWith();
expect(controller.$.popover.parent).toEqual('mariano');
});
});
describe('_getItem()', () => {
it(`should make a query and set this.item`, () => {
$httpBackend.whenGET(`/item/api/Items/1/getCard`).respond(true);
$httpBackend.expectGET(`/item/api/Items/1/getCard`);
controller._getItem();
$httpBackend.flush();
expect(controller.item).toEqual(true);
});
});
});
});

View File

@ -22,18 +22,28 @@
</vn-float-button>
</a>
</vn-auto>
<vn-horizontal class="state">
<vn-one>
<p class="title" uppercase text-center>Visible</p>
<h5 class="title" text-center>{{$ctrl.item.visible}}</h5>
</vn-one>
<vn-one>
<p class="title" uppercase translate text-center>Available</p>
<h5 text-center class="title">{{$ctrl.item.available}}</h5>
</vn-one>
</vn-horizontal>
<vn-auto pad-medium>
<h5>{{$ctrl.item.id}}</h5>
<vn-label-value label="Name"
value="{{$ctrl.item.name}}">
value="{{::$ctrl.item.name}}">
</vn-label-value>
<vn-label-value label="Buyer"
value="{{$ctrl.item.itemType.worker.firstName}} {{$ctrl.item.itemType.worker.name}}">
</vn-label-value>
<vn-label-value
ng-repeat="itemTag in $ctrl.itemTags | limitTo:4"
label="{{$ctrl.tags[itemTag.tagFk].name}}"
value="{{itemTag.value}}">
ng-repeat="tag in $ctrl.item.tags | limitTo:4"
label="{{tag.tag.name}}"
value="{{tag.value}}">
</vn-label-value>
</vn-auto>
</vn-vertical>

View File

@ -5,10 +5,7 @@ class Controller {
this.$http = $http;
this.$state = $state;
this.order = {};
}
getOrder() {
let filter = {
this.filter = {
include: [
{relation: 'agencyMode', scope: {fields: ['name']}},
{relation: 'address', scope: {fields: ['nickname']}},
@ -25,13 +22,16 @@ class Controller {
}
]
};
}
let json = encodeURIComponent(JSON.stringify(filter));
getOrder() {
let json = encodeURIComponent(JSON.stringify(this.filter));
let query = `/order/api/Orders/${this.$state.params.id}?filter=${json}`;
this.$http.get(query).then(res => {
if (res.data)
if (res.data) {
this.order = res.data;
this.getTotal();
this.getTotal();
}
});
}

View File

@ -0,0 +1,67 @@
import './index.js';
describe('Order', () => {
describe('Component vnOrderCard', () => {
let $componentController;
let $scope;
let controller;
let $httpBackend;
let $state;
beforeEach(() => {
angular.mock.module('order');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_) => {
$componentController = _$componentController_;
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
$scope.card = {createOrder: () => {}};
$state = {params: {id: 1}};
controller = $componentController('vnOrderCard', {$scope: $scope, $state: $state});
}));
describe('getOrder()', () => {
it(`should make a query, save the data in order and call get order if the response has data`, () => {
spyOn(controller, 'getTotal');
let json = encodeURIComponent(JSON.stringify(controller.filter));
$httpBackend.expectGET(`/order/api/Orders/${controller.$state.params.id}?filter=${json}`).respond({id: 1});
controller.getOrder();
$httpBackend.flush();
expect(controller.order).toEqual({id: 1});
expect(controller.getTotal).toHaveBeenCalledWith();
});
it(`should make a query and not call getTotal if the response is not defined`, () => {
spyOn(controller, 'getTotal');
let json = encodeURIComponent(JSON.stringify(controller.filter));
$httpBackend.expectGET(`/order/api/Orders/${controller.$state.params.id}?filter=${json}`).respond(undefined);
controller.getOrder();
$httpBackend.flush();
expect(controller.order).toEqual({});
expect(controller.getTotal).not.toHaveBeenCalledWith();
});
});
describe('getTotal()', () => {
it(`should make a query and save the data in order.total`, () => {
$httpBackend.expectGET(`/order/api/Orders/${controller.$state.params.id}/getTotal`).respond({total: '20M'});
controller.getTotal();
$httpBackend.flush();
expect(controller.order.total).toEqual('20M');
});
it(`should make a query and not save the respones if is not defined`, () => {
$httpBackend.expectGET(`/order/api/Orders/${controller.$state.params.id}/getTotal`).respond();
controller.getTotal();
$httpBackend.flush();
expect(controller.order.total).toEqual(undefined);
});
});
});
});

View File

@ -1,6 +1,6 @@
<vn-crud-model
vn-id="model"
url="/ticket/api/Tickets"
url="/ticket/api/Tickets/filter"
filter="::$ctrl.filter"
limit="20"
data="tickets"
@ -17,55 +17,63 @@
</vn-card>
</div>
<vn-card margin-medium-v pad-medium>
<table class="vn-grid">
<thead>
<tr>
<th translate number>Id</th>
<th translate>Salesperson</th>
<th translate>Date</th>
<th translate>Hour</th>
<th translate>Alias</th>
<th translate>Province</th>
<th translate>State</th>
<th translate>Agency</th>
<th translate>Warehouse</th>
<th translate number>Invoice</th>
<th translate number>Route</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="ticket in tickets"
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th></vn-th>
<vn-th field="id" number>Id</vn-th>
<vn-th field="salesPersonFk">Salesperson</vn-th>
<vn-th field="shipped">Date</vn-th>
<vn-th>Hour</vn-th>
<vn-th field="nickname">Alias</vn-th>
<vn-th field="provinceFk">Province</vn-th>
<vn-th field="stateFk" >State</vn-th>
<vn-th field="agencyModeFk">Agency</vn-th>
<vn-th field="warehouseFk">Warehouse</vn-th>
<vn-th field="refFk" number>Invoice</vn-th>
<vn-th field="routeFk" number>Route</vn-th>
<vn-th number>Total</vn-th>
<vn-th></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="ticket in tickets"
class="{{::$ctrl.compareDate(ticket.shipped)}} clickable"
ui-sref="ticket.card.summary({id: {{::ticket.id}}})">
<th number>{{::ticket.id}}</th>
<td>{{::ticket.client.salesPerson.name | dashIfEmpty}}</td>
<td>{{::ticket.shipped | date:'dd/MM/yyyy'}}</td>
<td>{{::ticket.shipped | date:'HH:mm'}}</td>
<td>
<vn-td>
<vn-icon ng-show="ticket.problem" class="bright"
vn-tooltip="{{ticket.problem}}"
icon="warning">
</vn-icon>
</vn-td>
<vn-td number>{{::ticket.id}}</vn-td>
<vn-td>{{::ticket.salesPerson | dashIfEmpty}}</vn-td>
<vn-td>{{::ticket.shipped | date:'dd/MM/yyyy'}}</vn-td>
<vn-td>{{::ticket.shipped | date:'HH:mm'}}</vn-td>
<vn-td>
<span
class="link"
ng-click="$ctrl.showDescriptor($event, ticket.clientFk)">
{{::ticket.nickname}}
</span>
</td>
<td>{{::ticket.address.province.name}}</td>
<td>{{::ticket.tracking.state.name}}</td>
<td>{{::ticket.agencyMode.name}}</td>
<td>{{::ticket.warehouse.name}}</td>
<td number>{{::ticket.refFk | dashIfEmpty}}</td>
<td number>{{::ticket.routeFk | dashIfEmpty}}</td>
<td>
</vn-td>
<vn-td>{{::ticket.province}}</vn-td>
<vn-td>{{::ticket.state}}</vn-td>
<vn-td>{{::ticket.agencyMode}}</vn-td>
<vn-td>{{::ticket.warehouse}}</vn-td>
<vn-td number>{{::ticket.refFk | dashIfEmpty}}</vn-td>
<vn-td number>{{::ticket.routeFk | dashIfEmpty}}</vn-td>
<vn-td number>{{::ticket.total | currency: '€': 2}}</vn-td>
<vn-td>
<vn-icon-button
ng-click="$ctrl.preview($event, ticket)"
vn-tooltip="Preview"
icon="desktop_windows">
</vn-icon-button>
</td>
</tr>
</tbody>
</table>
<vn-client-descriptor-popover vn-id="descriptor"></vn-client-descriptor-popover>
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
<vn-pagination
model="model"
@ -77,4 +85,5 @@
<tpl-body>
<vn-ticket-summary ticket="$ctrl.ticketSelected"></vn-ticket-summary>
</tpl-body>
</vn-dialog>
</vn-dialog>
<vn-client-descriptor-popover vn-id="descriptor"></vn-client-descriptor-popover>

View File

@ -53,6 +53,11 @@ VAT: IVA
Hour: Hora
The quantity do not match: Las cantidades no coinciden
by: por
Freezed: Congelado
Risk: Riesgo
Delay: Retraso
Code 100: Código 100
Invoice: Factura
#sections
List: Listado

View File

@ -280,7 +280,7 @@ export default {
},
ticketsIndex: {
createTicketButton: `vn-ticket-index ${components.vnFloatButton}`,
searchResult: `vn-ticket-index vn-card > div > table > tbody > tr`,
searchResult: `vn-ticket-index vn-card > div > vn-table > div > vn-tbody > vn-tr`,
searchTicketInput: `vn-ticket-index ${components.vnTextfield}`,
searchButton: `vn-ticket-index vn-searchbar vn-icon-button[icon="search"]`
},

View File

@ -0,0 +1,146 @@
USE `vn`;
DROP procedure IF EXISTS `ticketGetProblems`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `ticketGetProblems`()
BEGIN
/*
* Necesita la tabla tmp.ticket
*
*/
DECLARE vWarehouse INT;
DECLARE vDate DATE;
DECLARE vAvailableCache INT;
DECLARE vVisibleCache INT;
DECLARE vDone INT DEFAULT 0;
DECLARE vCursor CURSOR FOR
SELECT DISTINCT tt.warehouseFk, date(tt.shipped)
FROM tmp.ticket tt
WHERE DATE(tt.shipped) BETWEEN CURDATE()
AND TIMESTAMPADD(DAY, 1.9, CURDATE());
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = 1;
DROP TEMPORARY TABLE IF EXISTS tmp.ticketProblems;
CREATE TEMPORARY TABLE tmp.ticketProblems (
ticketFk INT(11),
problem VARCHAR(50),
INDEX (ticketFk)
)
ENGINE = MEMORY;
-- CONGELADO
INSERT INTO tmp.ticketProblems(ticketFk, problem)
SELECT DISTINCT tt.ticketFk, 'Freezed'
FROM tmp.ticket tt
JOIN vn.client c ON c.id = tt.clientFk
WHERE c.isFreezed;
-- eliminamos tickets con problemas para no volverlos a mirar
DROP TEMPORARY TABLE IF EXISTS tmp.ticketListFiltered;
CREATE TEMPORARY TABLE tmp.ticketListFiltered
(PRIMARY KEY (ticketFk))
ENGINE = MEMORY
SELECT tt.ticketFk, c.id
FROM tmp.ticket tt
JOIN vn.client c ON c.id = tt.clientFk
WHERE c.isFreezed = 0;
DROP TEMPORARY TABLE IF EXISTS tmp.client_list;
CREATE TEMPORARY TABLE tmp.client_list
(PRIMARY KEY (Id_Cliente))
ENGINE = MEMORY
SELECT DISTINCT tt.clientFk AS Id_Cliente
FROM tmp.ticket tt;
-- RIESGO
CALL vn2008.risk_vs_client_list(CURDATE());
INSERT INTO tmp.ticketProblems(ticketFk, problem)
SELECT DISTINCT tt.ticketFk, 'Risk'
FROM tmp.ticketListFiltered tt
JOIN vn.ticket t ON t.id = tt.ticketFk
JOIN vn.agencyMode a ON t.agencyModeFk = a.id
JOIN tmp.risk r ON r.Id_Cliente = t.clientFk
JOIN vn.client c ON c.id = t.clientFk
WHERE r.risk > c.credit + 10
AND a.deliveryMethodFk != 3; -- para que las recogidas se preparen
-- eliminamos tickets con problemas para no volverlos a mirar
DELETE tlf FROM tmp.ticketListFiltered tlf
JOIN tmp.ticketProblems tp ON tlf.ticketFk = tp.ticketFk;
-- CODIGO 100
INSERT INTO tmp.ticketProblems(ticketFk, problem)
SELECT DISTINCT tt.ticketFk, 'Code 100'
FROM tmp.ticket tt
JOIN sale s ON s.ticketFk = tt.ticketFk
WHERE s.itemFk = 100;
-- eliminamos tickets con problemas para no volverlos a mirar
DELETE tlf FROM tmp.ticketListFiltered tlf
JOIN tmp.ticketProblems tp ON tlf.ticketFk = tp.ticketFk;
OPEN vCursor;
WHILE NOT vDone
DO
FETCH vCursor INTO vWarehouse, vDate;
CALL cache.visible_refresh(vVisibleCache, FALSE, vWarehouse);
CALL cache.available_refresh(vAvailableCache, FALSE, vWarehouse, vDate);
-- El disponible es menor que 0
INSERT INTO tmp.ticketProblems(ticketFk, problem)
SELECT tt.ticketFk, i.name
FROM tmp.ticket tt
JOIN vn.ticket t ON t.id = tt.ticketFk
LEFT JOIN vn.sale s ON s.ticketFk = t.id
JOIN vn.item i ON i.id = s.itemFk
JOIN vn.itemType it on it.id = i.typeFk
LEFT JOIN cache.visible v ON i.id = v.item_id
AND v.calc_id = vVisibleCache
LEFT JOIN cache.available av ON av.item_id = i.id
AND av.calc_id = vAvailableCache
WHERE date(t.shipped) = vDate
AND categoryFk != 6
AND s.quantity > IFNULL(v.visible, 0)
AND IFNULL(av.available, 0) < 0
AND s.isPicked = FALSE
AND NOT i.generic
AND vWarehouse = t.warehouseFk;
-- eliminamos tickets con problemas para no volverlos a mirar
DELETE tlf FROM tmp.ticketListFiltered tlf
JOIN tmp.ticketProblems tp ON tlf.ticketFk = tp.ticketFk;
-- Amarillo: El disponible es mayor que cero y la cantidad supera el visible, estando aun sin preparar
INSERT INTO tmp.ticketProblems(ticketFk, problem)
SELECT tt.ticketFk, CONCAT('Delay', i.name)
FROM tmp.ticket tt
JOIN vn.ticket t ON t.id = tt.ticketFk
LEFT JOIN vn.sale s ON s.ticketFk = t.id
JOIN vn.item i ON i.id = s.itemFk
JOIN vn.itemType it on it.id = i.typeFk
LEFT JOIN cache.visible v ON i.id = v.item_id AND v.calc_id = vVisibleCache
LEFT JOIN cache.available av ON av.item_id = i.id AND av.calc_id = vAvailableCache
WHERE IFNULL(av.available, 0) >= 0
AND s.quantity > IFNULL(v.visible, 0)
AND s.isPicked = FALSE
AND s.reserved = FALSE
AND it.categoryFk != 6
AND date(t.shipped) = vDate
AND NOT i.generic
AND CURDATE() = vDate
AND t.warehouseFk = vWarehouse;
END WHILE;
CLOSE vCursor;
DROP TEMPORARY TABLE tmp.ticketListFiltered;
END$$
DELIMITER ;

View File

@ -0,0 +1,32 @@
USE `vn`;
DROP procedure IF EXISTS `ticketGetFullList`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `ticketGetFullList`()
BEGIN
/**
* Obtiene un listado de tickets
* junto con el precio total y los problemas
*
* @table tmp.ticket(ticketFk) Identificadores de los tickets a calcular
* @return Listado de tickets
*/
CALL ticketGetTotal();
CALL ticketGetProblems();
DROP TEMPORARY TABLE IF EXISTS tmp.ticketFullList;
CREATE TEMPORARY TABLE tmp.ticketFullList ENGINE = MEMORY
SELECT t.*, tt.total, tp.problem
FROM tmp.ticket t
JOIN tmp.ticketTotal tt ON tt.ticketFk = t.ticketFk
LEFT JOIN tmp.ticketProblems tp ON tp.ticketFk = t.ticketFk;
DROP TEMPORARY TABLE
tmp.ticket,
tmp.ticketTotal,
tmp.ticketProblems;
END$$
DELIMITER ;

View File

@ -0,0 +1,67 @@
module.exports = Self => {
Self.remoteMethod('getCard', {
description: 'Return the card for a given item id',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'The item id',
http: {source: 'path'}
}],
returns: {
type: 'object',
root: true
},
http: {
path: `/:id/getCard`,
verb: 'GET'
}
});
Self.getCard = async id => {
let item = {};
// Item basic data
let filter = {
where: {id: id},
include: [
{relation: 'itemType',
scope: {
fields: ['id', 'name', 'workerFk', 'warehouseFk'],
include: [{
relation: 'worker',
scope: {
fields: ['id', 'name', 'firstName']
}
}]
}
},
{relation: 'tags',
scope: {
fields: ['id', 'value', 'tagFk'],
include: [{
relation: 'tag',
scope: {
fields: ['id', 'name']
}
}]
}
}
]
};
[item] = await Self.app.models.Item.find(filter);
// Visible Avaible
let query = `
CALL vn.getItemVisibleAvailable(?,curdate(),?,?)`;
let options = [item.id, item.itemType().warehouseFk, false];
let [res] = await Self.rawSql(query, options);
item.available = res[0].available ? res[0].available : '-';
item.visible = res[0].visible ? res[0].visible : '-';
return item;
};
};

View File

@ -20,6 +20,7 @@ module.exports = Self => {
});
Self.getSummary = async id => {
let promises = [];
let summary = {};
// Item basic data and taxes
@ -59,7 +60,7 @@ module.exports = Self => {
}
]
};
[summary.item] = await Self.app.models.Item.find(filter);
promises.push(Self.app.models.Item.find(filter));
// Tags
filter = {
@ -70,28 +71,35 @@ module.exports = Self => {
relation: 'tag'
}
};
summary.tags = await Self.app.models.ItemTag.find(filter);
promises.push(Self.app.models.ItemTag.find(filter));
// Botanical
filter = {
where: {itemFk: id},
include: [{relation: 'genus'}, {relation: 'specie'}]
};
[summary.botanical] = await Self.app.models.ItemBotanical.find(filter);
promises.push(Self.app.models.ItemBotanical.find(filter));
// Niches
filter = {
where: {itemFk: id},
include: {relation: 'warehouse'}
};
summary.niches = await Self.app.models.ItemNiche.find(filter);
promises.push(Self.app.models.ItemNiche.find(filter));
let res = await Promise.all(promises);
[summary.item] = res[0];
summary.tags = res[1];
[summary.botanical] = res[2];
summary.niches = res[3];
// Visible Avaible
let query = `
CALL vn.getItemVisibleAvailable(?,curdate(),?,?)`;
let options = [summary.item.id, summary.item.itemType().warehouseFk, false];
let [res] = await Self.rawSql(query, options);
[res] = await Self.rawSql(query, options);
summary.available = res[0].available ? res[0].available : '-';
summary.visible = res[0].visible ? res[0].visible : '-';

View File

@ -0,0 +1,68 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = Self => {
Self.remoteMethod('filter', {
description: 'Find all instances of the model matched by filter from the data source.',
accessType: 'READ',
accepts: [
{
arg: 'filter',
type: 'Object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
http: {source: 'query'}
}
],
returns: {
type: ['Object'],
root: true
},
http: {
path: `/filter`,
verb: 'GET'
}
});
Self.filter = async filter => {
let stmt = new ParameterizedSQL(
`DROP TEMPORARY TABLE IF EXISTS tmp.ticket;
CREATE TEMPORARY TABLE tmp.ticket
(PRIMARY KEY (ticketFk)) ENGINE = MEMORY
SELECT
t.id,
t.id AS ticketFk,
t.shipped,
t.nickname,
t.refFk,
t.routeFk,
t.agencyModeFk,
t.warehouseFk,
t.clientFk,
c.salesPersonFk,
a.provinceFk,
ts.stateFk,
p.name AS province,
w.name AS warehouse,
am.name AS agencyMode,
st.name AS state,
wk.name AS salesPerson
FROM ticket t
LEFT JOIN address a ON a.id = t.addressFk
LEFT JOIN province p ON p.id = a.provinceFk
LEFT JOIN warehouse w ON w.id = t.warehouseFk
LEFT JOIN agencyMode am ON am.id = t.agencyModeFk
LEFT JOIN ticketState ts ON ts.ticketFk = t.id
LEFT JOIN state st ON st.id = ts.stateFk
LEFT JOIN client c ON c.id = t.clientFk
LEFT JOIN worker wk ON wk.id = c.salesPersonFk`);
stmt.merge(Self.buildSuffix(filter, 't'));
stmt.merge(';CALL ticketGetFullList()');
stmt.merge(';SELECT * FROM tmp.ticketFullList tfl');
stmt.merge(Self.buildSuffix(filter, 'tfl'));
let result = await Self.rawStmt(stmt);
return result[3];
};
};

View File

@ -0,0 +1,11 @@
const app = require(`${servicesDir}/ticket/server/server`);
describe('ticket filter()', () => {
it('should call the filter method', async() => {
let filter = {order: 'shipped DESC'};
let result = await app.models.Ticket.filter(filter);
let ticketId = result[0].id;
expect(ticketId).toEqual(15);
});
});

View File

@ -7,6 +7,7 @@ module.exports = Self => {
require('../methods/item/getDiary')(Self);
require('../methods/item/getLastEntries')(Self);
require('../methods/item/getSummary')(Self);
require('../methods/item/getCard')(Self);
Self.validatesPresenceOf('name', {message: 'Cannot be blank'});
Self.validatesPresenceOf('originFk', {message: 'Cannot be blank'});

View File

@ -15,4 +15,5 @@ module.exports = Self => {
require('../methods/ticket/getSales')(Self);
require('../methods/ticket/getSalesPersonMana')(Self);
require('../methods/ticket/getShipped')(Self);
require('../methods/ticket/filter')(Self);
};

View File

@ -1,5 +1,5 @@
module.exports = Self => {
Self.remoteMethod('getVolume', {
Self.remoteMethod('getVolumes', {
description: 'Returns the volumes of a order',
accessType: 'READ',
accepts: [{
@ -18,7 +18,7 @@ module.exports = Self => {
}
});
Self.getVolume = async orderFk => {
Self.getVolumes = async orderFk => {
let [volume] = await Self.rawSql(`CALL vn.orderListVolume(?)`, [orderFk]);
return volume;
};

View File

@ -0,0 +1,9 @@
const app = require(`../../../../server/server`);
describe('order getVolumes()', () => {
it('should return the volumes of a given order id', async() => {
let [result] = await app.models.Order.getVolumes(1);
expect(result.volume).toEqual(0.072);
});
});

View File

@ -0,0 +1,17 @@
const app = require(`../../../../server/server`);
describe('order itemFilter()', () => {
it('should call the itemFilter method and return an array of items', async() => {
let filter = {
where: {
id: 1,
typeFk: 1
}
};
let result = await app.models.Order.itemFilter(filter);
let firstItemId = result[0].item_id;
expect(result.length).toEqual(2);
expect(firstItemId).toEqual(3);
});
});