From 61b9b0c7bc274822a23e2b0f2cf5333fa025bd4a Mon Sep 17 00:00:00 2001 From: gerard Date: Thu, 30 Aug 2018 09:19:09 +0200 Subject: [PATCH] Tarea #445 claim.detail --- client/claim/src/descriptor/index.html | 4 +- client/claim/src/detail/index.html | 108 ++++++++++++++++++ client/claim/src/detail/index.js | 84 ++++++++++++++ client/claim/src/detail/index.spec.js | 94 +++++++++++++++ client/claim/src/detail/locale/es.yml | 7 ++ client/claim/src/detail/style.scss | 20 ++++ client/claim/src/index.js | 1 + client/core/src/directives/index.js | 2 +- .../claim/common/models/claim-beginning.js | 7 ++ .../methods/sale/getClaimableFromTicket.js | 44 +++++++ services/loopback/common/models/sale.js | 1 + 11 files changed, 369 insertions(+), 3 deletions(-) create mode 100644 client/claim/src/detail/index.html create mode 100644 client/claim/src/detail/index.js create mode 100644 client/claim/src/detail/index.spec.js create mode 100644 client/claim/src/detail/locale/es.yml create mode 100644 client/claim/src/detail/style.scss create mode 100644 services/claim/common/models/claim-beginning.js create mode 100644 services/loopback/common/methods/sale/getClaimableFromTicket.js diff --git a/client/claim/src/descriptor/index.html b/client/claim/src/descriptor/index.html index 4bf70e5fe..5fc417e8a 100644 --- a/client/claim/src/descriptor/index.html +++ b/client/claim/src/descriptor/index.html @@ -20,10 +20,10 @@ - - diff --git a/client/claim/src/detail/index.html b/client/claim/src/detail/index.html new file mode 100644 index 000000000..068765e2a --- /dev/null +++ b/client/claim/src/detail/index.html @@ -0,0 +1,108 @@ + + + + + + Detail + + + + Id + Landed + Quantity + Claimed + Description + Price + Disc. + Total + + + + + {{saleClaimed.sale.id}} + {{saleClaimed.sale.ticket.landed | dateTime: 'dd/MM/yyyy'}} + {{saleClaimed.sale.quantity}} + + + + + {{saleClaimed.sale.concept}} + {{saleClaimed.sale.price | currency:'€':2}} + {{saleClaimed.sale.discount}} % + + {{(saleClaimed.sale.quantity * saleClaimed.sale.price) - + ((saleClaimed.sale.discount * + (saleClaimed.sale.quantity * saleClaimed.sale.price))/100) | currency:'€':2 + }} + + + + + + + + + No results + + + + + + + + + + + + +

Claimable sales from ticket

{{$ctrl.claim.ticketFk}}

+ + + + Id + Landed + Quantity + Description + Price + Disc. + Total + + + + + {{sale.saleFk}} + {{sale.landed | dateTime: 'dd/MM/yyyy'}} + {{sale.quantity}} + {{sale.concept}} + {{sale.price | currency:'€':2}} + {{sale.discount}} % + + {{(sale.quantity * sale.price) - ((sale.discount * (sale.quantity * sale.price))/100) | currency:'€':2}} + + + + + No results + + +
+
\ No newline at end of file diff --git a/client/claim/src/detail/index.js b/client/claim/src/detail/index.js new file mode 100644 index 000000000..ae353f903 --- /dev/null +++ b/client/claim/src/detail/index.js @@ -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: '<' + } +}); diff --git a/client/claim/src/detail/index.spec.js b/client/claim/src/detail/index.spec.js new file mode 100644 index 000000000..d6769a022 --- /dev/null +++ b/client/claim/src/detail/index.spec.js @@ -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!'); + }); + }); + }); +}); diff --git a/client/claim/src/detail/locale/es.yml b/client/claim/src/detail/locale/es.yml new file mode 100644 index 000000000..bda82a460 --- /dev/null +++ b/client/claim/src/detail/locale/es.yml @@ -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 \ No newline at end of file diff --git a/client/claim/src/detail/style.scss b/client/claim/src/detail/style.scss new file mode 100644 index 000000000..cb66b31ab --- /dev/null +++ b/client/claim/src/detail/style.scss @@ -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; + } + } + } + } +} + diff --git a/client/claim/src/index.js b/client/claim/src/index.js index 54099a5ba..e779cd595 100644 --- a/client/claim/src/index.js +++ b/client/claim/src/index.js @@ -2,6 +2,7 @@ export * from './module'; import './index/'; import './card'; +import './detail'; import './descriptor'; import './basic-data'; // import './summary'; diff --git a/client/core/src/directives/index.js b/client/core/src/directives/index.js index 07ce5dfe2..a97c88b3c 100644 --- a/client/core/src/directives/index.js +++ b/client/core/src/directives/index.js @@ -9,4 +9,4 @@ import './zoom-image'; import './visible-by'; import './bind'; import './repeat-last'; -import './title'; \ No newline at end of file +import './title'; diff --git a/services/claim/common/models/claim-beginning.js b/services/claim/common/models/claim-beginning.js new file mode 100644 index 000000000..bce69d531 --- /dev/null +++ b/services/claim/common/models/claim-beginning.js @@ -0,0 +1,7 @@ +module.exports = Self => { + // Validations + + Self.validatesUniquenessOf('saleFk', { + message: `A claim with that sale already exists` + }); +}; diff --git a/services/loopback/common/methods/sale/getClaimableFromTicket.js b/services/loopback/common/methods/sale/getClaimableFromTicket.js new file mode 100644 index 000000000..adfb91710 --- /dev/null +++ b/services/loopback/common/methods/sale/getClaimableFromTicket.js @@ -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; + }; +}; diff --git a/services/loopback/common/models/sale.js b/services/loopback/common/models/sale.js index 47feb07d8..7d7a0f869 100644 --- a/services/loopback/common/models/sale.js +++ b/services/loopback/common/models/sale.js @@ -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);