Added waste section

This commit is contained in:
Joan Sanchez 2020-01-15 13:07:33 +01:00
parent 8807f64e09
commit 3a4c379033
9 changed files with 330 additions and 1 deletions

View File

@ -0,0 +1,30 @@
USE `bs`;
DROP procedure IF EXISTS `waste_getDetail`;
DELIMITER $$
USE `bs`$$
CREATE DEFINER=`root`@`%`PROCEDURE `waste_getDetail` ()
BEGIN
DECLARE vWeek INT;
DECLARE vYear INT;
SELECT week, year
INTO vWeek, vYear
FROM vn.time
WHERE dated = CURDATE();
SELECT *, 100 * dwindle / total AS percentage
FROM (
SELECT buyer,
ws.family,
sum(ws.saleTotal) AS total,
sum(ws.saleWaste) AS dwindle
FROM bs.waste ws
WHERE `year` = vYear AND `week` = vWeek
GROUP BY buyer, family
) sub
ORDER BY percentage DESC;
END$$
DELIMITER ;

View File

@ -20,4 +20,5 @@ import './niche';
import './botanical';
import './barcode';
import './summary';
import './waste';

View File

@ -137,6 +137,15 @@
"item": "$ctrl.item"
},
"acl": ["employee"]
}, {
"url" : "/waste",
"state": "item.waste",
"component": "vn-item-waste",
"description": "Waste",
"params": {
"item": "$ctrl.item"
},
"acl": ["employee"]
}
]
}

View File

@ -0,0 +1,120 @@
<vn-crud-model auto-load="true"
vn-id="model"
url="Items/filter"
limit="20"
data="requests"
order="shipped DESC, isOk ASC">
</vn-crud-model>
<vn-data-viewer model="model">
<vn-card>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th field="buer" number>Buyer</vn-th>
<vn-th field="family">Family</vn-th>
<vn-th field="percentage">Percentage</vn-th>
<vn-th field="salesPersonNickname">Mermas</vn-th>
<vn-th field="total">Total</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="request in requests">
<vn-td number>
<span class="link"
ng-click="$ctrl.showTicketDescriptor($event, request.ticketFk)">
{{request.ticketFk}}
</span>
</vn-td>
<vn-td>
<span title="{{::request.shipped | date: 'dd/MM/yyyy'}}"
class="chip {{$ctrl.compareDate(request.shipped)}}">
{{::request.shipped | date: 'dd/MM/yyyy'}}
</span>
</vn-td>
<vn-td>{{::request.warehouse}}</vn-td>
<vn-td>
<span
class="link"
ng-click="$ctrl.showWorkerDescriptor($event, request.salesPersonFk)">
{{::request.salesPersonNickname}}
</span>
</vn-td>
<vn-td title="{{::request.description}}">{{::request.description}}</vn-td>
<vn-td number>{{::request.quantity}}</vn-td>
<vn-td number>{{::request.price | currency: 'EUR':2}}</vn-td>
<vn-td>
<span
class="link"
ng-click="$ctrl.showWorkerDescriptor($event, request.attenderFk)">
{{::request.atenderNickname}}
</span>
</vn-td>
<vn-td-editable disabled="request.isOk != null" number>
<text>{{request.itemFk}}</text>
<field>
<vn-input-number class="dense" vn-focus
ng-model="request.itemFk">
</vn-input-number>
</field>
</vn-td-editable>
<vn-td-editable disabled="!request.itemFk || request.isOk != null" number>
<text number>{{request.saleQuantity}}</text>
<field>
<vn-input-number class="dense" vn-focus
ng-model="request.saleQuantity"
on-change="$ctrl.changeQuantity(request)">
</vn-input-number>
</field>
</vn-td-editable>
<vn-td>
<span
class="link"
ng-click="$ctrl.showItemDescriptor($event, request.itemFk)"
title="{{request.itemDescription}}">
{{request.itemDescription}}
</span>
</vn-td>
<vn-td>{{$ctrl.getState(request.isOk)}}</vn-td>
<vn-td>
<vn-icon
ng-if="request.response.length"
ranslate-attr="{title: request.response}"
icon="insert_drive_file">
</vn-icon>
<vn-icon-button
ng-if="request.isOk != 0"
icon="thumb_down"
ng-click="$ctrl.showDenyReason($event, request)"
translate-attr="{title: 'Discard'}">
</vn-icon-button>
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>
<vn-ticket-descriptor-popover
vn-id="ticketDescriptor">
</vn-ticket-descriptor-popover>
<vn-item-descriptor-popover
vn-id="itemDescriptor">
</vn-item-descriptor-popover>
<vn-dialog
vn-id="denyReason"
on-response="$ctrl.denyRequest($response)">
<tpl-body>
<h5 class="vn-pa-md" translate>Specify the reasons to deny this request</h5>
<vn-horizontal class="vn-pa-md">
<vn-textarea
ng-model="$ctrl.denyObservation">
</vn-textarea>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>

View File

@ -0,0 +1,12 @@
import ngModule from '../module';
import Component from 'core/lib/component';
import './style.scss';
export default class Controller extends Component {
}
ngModule.component('vnItemWaste', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,133 @@
import './index.js';
import crudModel from 'core/mocks/crud-model';
describe('Item', () => {
describe('Component vnItemRequest', () => {
let $scope;
let $element;
let controller;
let $httpBackend;
beforeEach(ngModule('item'));
beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
$scope.model = crudModel;
$scope.denyReason = {hide: () => {}};
$element = angular.element('<vn-item-request></vn-item-request>');
controller = $componentController('vnItemRequest', {$element, $scope});
}));
afterAll(() => {
$scope.$destroy();
$element.remove();
});
describe('getState()', () => {
it(`should return an string depending to the isOK value`, () => {
let isOk = null;
let result = controller.getState(isOk);
expect(result).toEqual('Nueva');
isOk = 1;
result = controller.getState(isOk);
expect(result).toEqual('Aceptada');
isOk = 0;
result = controller.getState(isOk);
expect(result).toEqual('Denegada');
});
});
describe('confirmRequest()', () => {
it(`should do nothing if the request does't have itemFk or saleQuantity`, () => {
let request = {};
spyOn(controller.vnApp, 'showSuccess');
controller.confirmRequest(request);
expect(controller.vnApp.showSuccess).not.toHaveBeenCalledWith();
});
it('should perform a query and call vnApp.showSuccess() and refresh if the conditions are met', () => {
spyOn(controller.vnApp, 'showSuccess');
let model = controller.$.model;
spyOn(model, 'refresh');
const expectedResult = {concept: 'Melee Weapon'};
let request = {itemFk: 1, saleQuantity: 1, id: 1};
$httpBackend.when('POST', `TicketRequests/${request.id}/confirm`).respond(expectedResult);
$httpBackend.expect('POST', `TicketRequests/${request.id}/confirm`).respond(expectedResult);
controller.confirmRequest(request);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
});
});
describe('changeQuantity()', () => {
it(`should call confirmRequest() if there's no sale id in the request`, () => {
let request = {};
spyOn(controller, 'confirmRequest');
controller.changeQuantity(request);
expect(controller.confirmRequest).toHaveBeenCalledWith(jasmine.any(Object));
});
it(`should perform a query and call vnApp.showSuccess() if the conditions are met`, () => {
let request = {saleFk: 1, saleQuantity: 1};
spyOn(controller.vnApp, 'showSuccess');
$httpBackend.when('PATCH', `Sales/${request.saleFk}/`).respond();
$httpBackend.expect('PATCH', `Sales/${request.saleFk}/`).respond();
controller.changeQuantity(request);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
});
});
describe('compareDate()', () => {
it(`should return "success" if receives a future date`, () => {
let date = '3019-02-18T11:00:00.000Z';
let result = controller.compareDate(date);
expect(result).toEqual('success');
});
it(`should return "warning" if date is today`, () => {
let date = new Date();
let result = controller.compareDate(date);
expect(result).toEqual('warning');
});
});
describe('denyRequest()', () => {
it(`should perform a query and call vnApp.showSuccess(), refresh(), hide() and set denyObservation to null in the controller`, () => {
spyOn(controller.vnApp, 'showSuccess');
const request = {id: 1};
const expectedResult = {isOk: false, attenderFk: 106, response: 'Denied!'};
controller.selectedRequest = request;
$httpBackend.when('POST', `TicketRequests/${request.id}/deny`).respond(expectedResult);
$httpBackend.expect('POST', `TicketRequests/${request.id}/deny`).respond(expectedResult);
controller.denyRequest('accept');
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
});
});
});
});

View File

@ -0,0 +1,6 @@
Discard: Descartar
Specify the reasons to deny this request: Especifica las razones para descartar la petición
Buy requests: Peticiones de compra
Search request by id or alias: Buscar peticiones por identificador o alias
Requested: Solicitado
Achieved: Conseguido

View File

@ -0,0 +1,18 @@
@import "variables";
vn-item-request {
vn-dialog[vn-id="denyReason"] {
button.close {
display: none
}
vn-button {
margin: 0 auto
}
vn-textarea {
width: 100%
}
}
vn-icon[icon=insert_drive_file]{
color: $color-font-secondary;
}
}

View File

@ -29,6 +29,6 @@ module.exports = Self => {
if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`);
return Self.rawSql('CALL vn.ticketCalculateSale(?)', [id]);
return Self.rawSql('CALL vn.sale_calculateComponent(?, null)', [id]);
};
};