Tarea #445 claim.detail

This commit is contained in:
gerard 2018-08-30 09:19:09 +02:00
parent e698d84a4f
commit 61b9b0c7bc
11 changed files with 369 additions and 3 deletions

View File

@ -20,10 +20,10 @@
<vn-label-value label="Created"
value="{{$ctrl.claim.created | dateTime: 'dd/MM/yyyy'}}">
</vn-label-value>
<vn-label-value label="Buyer"
<vn-label-value label="Salesperson"
value="{{$ctrl.claim.client.salesPerson.firstName}} {{$ctrl.claim.client.salesPerson.name}}">
</vn-label-value>
<vn-label-value label="Attended"
<vn-label-value label="Attended by"
value="{{$ctrl.claim.worker.firstName}} {{$ctrl.claim.worker.name}}">
</vn-label-value>

View File

@ -0,0 +1,108 @@
<vn-crud-model
vn-id="model"
url="claim/api/ClaimBeginnings"
filter="$ctrl.filter"
data="$ctrl.salesClaimed" on-data-change="$ctrl.onDataChange()">
</vn-crud-model>
<vn-vertical>
<vn-card pad-large>
<vn-vertical>
<vn-title>Detail</vn-title>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th number>Id</vn-th>
<vn-th number>Landed</vn-th>
<vn-th number>Quantity</vn-th>
<vn-th number>Claimed</vn-th>
<vn-th number>Description</vn-th>
<vn-th number>Price</vn-th>
<vn-th number>Disc.</vn-th>
<vn-th number>Total</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="saleClaimed in $ctrl.salesClaimed" vn-repeat-last on-last="$ctrl.focusLastInput()">
<vn-td number>{{saleClaimed.sale.id}}</vn-td>
<vn-td number>{{saleClaimed.sale.ticket.landed | dateTime: 'dd/MM/yyyy'}}</vn-td>
<vn-td number>{{saleClaimed.sale.quantity}}</vn-td>
<vn-td number>
<vn-textfield
id="claimedQuantity"
model="saleClaimed.quantity"
on-change="$ctrl.setClaimedQuantity(saleClaimed.id, saleClaimed.quantity)"
type="text">
</vn-textfield>
</vn-td>
<vn-td number>{{saleClaimed.sale.concept}}</vn-td>
<vn-td number>{{saleClaimed.sale.price | currency:'€':2}}</vn-td>
<vn-td number>{{saleClaimed.sale.discount}} %</vn-td>
<vn-td number>
{{(saleClaimed.sale.quantity * saleClaimed.sale.price) -
((saleClaimed.sale.discount *
(saleClaimed.sale.quantity * saleClaimed.sale.price))/100) | currency:'€':2
}}
</vn-td>
<vn-td number>
<vn-icon-button
medium-grey
margin-medium-v
vn-tooltip="Remove tag"
icon="remove_circle_outline"
ng-click="$ctrl.deleteClaimedSale(saleClaimed.id)"
tabindex="-1">
</vn-icon-button>
</vn-td>
</vn-tr>
</vn-tbody>
<vn-empty-rows ng-if="model.data.length === 0" translate>
No results
</vn-empty-rows>
</vn-table>
<vn-one pad-medium-top>
<vn-icon-button
vn-bind="+"
vn-tooltip="Add tag"
icon="add_circle"
ng-click="$ctrl.openAddSalesDialog()">
</vn-icon-button>
</vn-one>
</vn-vertical>
</vn-card>
<!-- Add Lines Dialog -->
<vn-dialog
vn-id="addSales">
<tpl-body>
<h3 translate>Claimable sales from ticket</h3><h3> {{$ctrl.claim.ticketFk}}</h3>
<vn-table>
<vn-thead>
<vn-tr>
<vn-th number>Id</vn-th>
<vn-th number>Landed</vn-th>
<vn-th number>Quantity</vn-th>
<vn-th number>Description</vn-th>
<vn-th number>Price</vn-th>
<vn-th number>Disc.</vn-th>
<vn-th number>Total</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="sale in $ctrl.salesToClaim" class="clickable" ng-click="$ctrl.addClaimedSale(sale.saleFk)">
<vn-td number>{{sale.saleFk}}</vn-td>
<vn-td number>{{sale.landed | dateTime: 'dd/MM/yyyy'}}</vn-td>
<vn-td number>{{sale.quantity}}</vn-td>
<vn-td number>{{sale.concept}}</vn-td>
<vn-td number>{{sale.price | currency:'€':2}}</vn-td>
<vn-td number>{{sale.discount}} %</vn-td>
<vn-td number>
{{(sale.quantity * sale.price) - ((sale.discount * (sale.quantity * sale.price))/100) | currency:'€':2}}
</vn-td>
</vn-tr>
</vn-tbody>
<vn-empty-rows ng-if="model.data.length === 0" translate>
No results
</vn-empty-rows>
</vn-table>
</tpl-body>
</vn-dialog>

View File

@ -0,0 +1,84 @@
import ngModule from '../module';
import './style.scss';
class Controller {
constructor($state, $scope, $http, $translate, vnApp) {
this.$state = $state;
this.$ = $scope;
this.$http = $http;
this.$translate = $translate;
this.vnApp = vnApp;
this.filter = {
where: {claimFk: $state.params.id},
include: [
{relation: 'sale',
scope: {
fields: ['concept', 'ticketFk', 'price', 'quantity', 'discount'],
include: {
relation: 'ticket'
}
}
}
]
};
}
openAddSalesDialog() {
this.getClaimableFromTicket();
this.$.addSales.show();
}
getClaimableFromTicket() {
let json = encodeURIComponent(JSON.stringify(this.claim.ticketFk));
let query = `/api/Sales/getClaimableFromTicket?ticketFk=${json}`;
this.$http.get(query).then(res => {
if (res.data) {
this.salesToClaim = res.data;
}
});
}
addClaimedSale(saleFk) {
let saleToAdd = {saleFk: saleFk, claimFk: this.claim.id, quantity: 0};
let query = `claim/api/ClaimBeginnings/`;
this.$http.post(query, saleToAdd).then(() => {
this.$.model.refresh();
this.$.addSales.hide();
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
});
}
deleteClaimedSale(id) {
let json = encodeURIComponent(JSON.stringify(id));
let query = `claim/api/ClaimBeginnings/${json}`;
this.$http.delete(query).then(() => {
this.$.model.refresh();
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
});
}
focusLastInput() {
let inputs = document.querySelectorAll("#claimedQuantity");
inputs[inputs.length - 1].querySelector("input").select();
}
setClaimedQuantity(id, claimedQuantity) {
let params = {id: id, quantity: claimedQuantity};
let query = `claim/api/ClaimBeginnings/`;
this.$http.patch(query, params).then(() => {
this.$.model.refresh();
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
});
}
}
Controller.$inject = ['$state', '$scope', '$http', '$translate', 'vnApp'];
ngModule.component('vnClaimDetail', {
template: require('./index.html'),
controller: Controller,
bindings: {
claim: '<'
}
});

View File

@ -0,0 +1,94 @@
import './index.js';
describe('claim', () => {
describe('Component vnClaimDetail', () => {
let $componentController;
let controller;
let $httpBackend;
let $state;
beforeEach(() => {
angular.mock.module('claim');
});
beforeEach(angular.mock.inject((_$componentController_, _$state_, _$httpBackend_, $rootScope) => {
$componentController = _$componentController_;
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
$httpBackend.when('GET', 'claim/api/Claims/ClaimBeginnings').respond({});
$state = _$state_;
$state.params.id = 1;
controller = $componentController('vnClaimDetail', {$state: $state});
controller.claim = {ticketFk: 1};
controller.$.model = {refresh: () => {}};
controller.$.addSales = {
hide: () => {},
show: () => {}
};
}));
describe('openAddSalesDialog()', () => {
it('should call getClaimableFromTicket and $.addSales.show', () => {
controller.$ = {addSales: {show: () => {}}};
spyOn(controller, 'getClaimableFromTicket');
spyOn(controller.$.addSales, 'show');
controller.openAddSalesDialog();
expect(controller.getClaimableFromTicket).toHaveBeenCalledWith();
expect(controller.$.addSales.show).toHaveBeenCalledWith();
});
});
describe('getClaimableFromTicket()', () => {
it('should make a query and set salesToClaim', () => {
$httpBackend.expectGET(`/api/Sales/getClaimableFromTicket?ticketFk=1`).respond(200, 1);
controller.getClaimableFromTicket();
$httpBackend.flush();
expect(controller.salesToClaim).toEqual(1);
});
});
describe('addClaimedSale(saleFk)', () => {
it('should make a post and call refresh, hide and showSuccess', () => {
spyOn(controller.$.model, 'refresh');
spyOn(controller.$.addSales, 'hide');
spyOn(controller.vnApp, 'showSuccess');
$httpBackend.expectPOST(`claim/api/ClaimBeginnings/`).respond({});
controller.addClaimedSale(1);
$httpBackend.flush();
expect(controller.$.model.refresh).toHaveBeenCalledWith();
expect(controller.$.addSales.hide).toHaveBeenCalledWith();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
});
});
describe('deleteClaimedSale(id)', () => {
it('should make a delete and call refresh and showSuccess', () => {
spyOn(controller.$.model, 'refresh');
spyOn(controller.vnApp, 'showSuccess');
$httpBackend.expectDELETE(`claim/api/ClaimBeginnings/1`).respond({});
controller.deleteClaimedSale(1);
$httpBackend.flush();
expect(controller.$.model.refresh).toHaveBeenCalledWith();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
});
});
describe('setClaimedQuantity(id, claimedQuantity)', () => {
it('should make a patch and call refresh and showSuccess', () => {
spyOn(controller.$.model, 'refresh');
spyOn(controller.vnApp, 'showSuccess');
$httpBackend.expectPATCH(`claim/api/ClaimBeginnings/`).respond({});
controller.setClaimedQuantity(1, 1);
$httpBackend.flush();
expect(controller.$.model.refresh).toHaveBeenCalledWith();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
});
});
});
});

View File

@ -0,0 +1,7 @@
Claimed: Reclamados
Disc.: Dto.
Attended by: Atendida por
Landed: Recibido
Price: Precio
Claimable sales from ticket: Lineas reclamables del ticket
Detail: Detalles

View File

@ -0,0 +1,20 @@
vn-claim-detail {
vn-textfield {
margin: 0!important;
max-width: 100px;
}
vn-dialog[vn-id=addSales] {
tpl-body {
width: 950px;
div {
div.buttons {
display: none;
}
vn-table{
min-width: 950px;
}
}
}
}
}

View File

@ -2,6 +2,7 @@ export * from './module';
import './index/';
import './card';
import './detail';
import './descriptor';
import './basic-data';
// import './summary';

View File

@ -9,4 +9,4 @@ import './zoom-image';
import './visible-by';
import './bind';
import './repeat-last';
import './title';
import './title';

View File

@ -0,0 +1,7 @@
module.exports = Self => {
// Validations
Self.validatesUniquenessOf('saleFk', {
message: `A claim with that sale already exists`
});
};

View File

@ -0,0 +1,44 @@
module.exports = Self => {
Self.remoteMethod('getClaimableFromTicket', {
description: 'Gets the claimable sales for a client',
accessType: 'READ',
accepts: [{
arg: 'ticketFk',
type: 'Number',
required: true,
description: 'ticketFk'
}],
returns: {
type: 'string',
root: true
},
http: {
path: `/getClaimableFromTicket`,
verb: 'GET'
}
});
Self.getClaimableFromTicket = async ticketFk => {
let query = `SELECT
s.id AS saleFk,
t.id AS ticketFk,
t.landed,
s.concept,
s.itemFk,
s.quantity,
s.price,
s.discount,
t.nickname
FROM vn.ticket t
INNER JOIN vn.sale s ON s.ticketFk = t.id
LEFT JOIN vn.claimBeginning cb ON cb.saleFk = s.id
WHERE (t.landed) >= TIMESTAMPADD(DAY, -7, CURDATE())
AND t.id = ? AND cb.id IS NULL
ORDER BY t.landed DESC, t.id DESC`;
let claimableSales = await Self.rawSql(query, [ticketFk]);
return claimableSales;
};
};

View File

@ -1,5 +1,6 @@
module.exports = Self => {
require('../methods/sale/filter')(Self);
require('../methods/sale/getClaimableFromTicket')(Self);
require('../methods/sale/saleComponentFilter')(Self);
require('../methods/sale/priceDifference')(Self);
require('../methods/sale/moveToTicket')(Self);