From 54ee27dcef7d3463cf8b4d48094a821677e619e0 Mon Sep 17 00:00:00 2001 From: gerard Date: Thu, 28 Jun 2018 15:17:50 +0200 Subject: [PATCH 01/16] Tarea #355, Tarea #354, Tarea #352 SALE --- client/ticket/src/sale/index.html | 122 ++++++++++++++++++--------- client/ticket/src/sale/index.js | 111 +++++++++++++++++------- client/ticket/src/sale/locale/es.yml | 5 ++ client/ticket/src/sale/style.scss | 51 ++++++++++- 4 files changed, 213 insertions(+), 76 deletions(-) diff --git a/client/ticket/src/sale/index.html b/client/ticket/src/sale/index.html index 9e7c48f49..74d8e7782 100644 --- a/client/ticket/src/sale/index.html +++ b/client/ticket/src/sale/index.html @@ -73,27 +73,36 @@ {{::sale.itemFk}} - {{sale.quantity}} - - {{sale.price | currency:'€':2}} - {{sale.discount}} % - {{sale.quantity * sale.price | currency:'€':2}} - + + + {{sale.price | currency:'€':2}} + + + {{sale.price | currency:'€':2}} + + + {{sale.discount}} % + + + {{sale.discount}} % + + {{(sale.quantity * sale.price) - ((sale.discount * (sale.quantity * sale.price))/100) | currency:' €':2}} No results @@ -166,35 +175,66 @@ - - + + -
MANÁ: {{$ctrl.workerMana}}
+
MANÁ: {{$ctrl.mana | currency:' €':0}}
-
{{$ctrl.client.name}}
- - + model="$ctrl.editedPrice" + type="number" + on-change="$ctrl.updatePrice()"> + + + - - - - +
+

New price

+

{{($ctrl.sale.quantity * $ctrl.sale.price) + - (($ctrl.sale.discount * ($ctrl.sale.quantity * $ctrl.editedPrice))/100) + | currency:' €':2}}

+ + +
+ + + + + + + + + + + + + +
@@ -232,11 +272,11 @@ ng-click="$ctrl.moveLines($ctrl.moveToTicketFk)"> - + ng-click="$ctrl.linesToNewTicket()"> +
diff --git a/client/ticket/src/sale/index.js b/client/ticket/src/sale/index.js index 29532e771..2369ca25e 100644 --- a/client/ticket/src/sale/index.js +++ b/client/ticket/src/sale/index.js @@ -3,15 +3,17 @@ import FilterTicketList from '../filter-ticket-list'; import './style.scss'; class Controller extends FilterTicketList { - constructor($scope, $timeout, $stateParams, $http, $state, vnApp) { + constructor($scope, $timeout, $stateParams, $http, vnApp, $translate) { super($scope, $timeout, $stateParams); this.$ = $scope; this.vnApp = vnApp; + this.translate = $translate; this.$timeout = $timeout; this.onOrder('itemFk', 'ASC'); this.$state = $stateParams; this.$http = $http; this.deletable = false; + this.edit = {}; this.moreOptions = [ {callback: this.showAddTurnDialog, name: "Add turn"}, {callback: this.showDeleteTicketDialog, name: "Delete ticket"} @@ -28,7 +30,7 @@ class Controller extends FilterTicketList { this.subTotal = 0.00; sales.forEach(sale => { - this.subTotal += sale.quantity * sale.price; + this.subTotal += (sale.quantity * sale.price) - ((sale.discount * (sale.quantity * sale.price)) / 100); }); } @@ -87,7 +89,7 @@ class Controller extends FilterTicketList { let params = {ticketFk: this.$state.params.id, stateFk: value}; this.$http.post(`/ticket/api/TicketTrackings/changeState`, params).then(() => { this.card.reload(); - this.vnApp.showMessage(this.translate.instant('Data saved')); + this.vnApp.showSuccess(this.translate.instant('Data saved!')); }); } @@ -150,12 +152,22 @@ class Controller extends FilterTicketList { }); } - /* newTicket() { - let params = [this.ticket.clientFk, this.ticket.warehouseFk, this.ticket.companyFk, this.ticket.addressFk, this.ticket.agencyModeFk, null]; - this.$http.post(`/ticket/api/Tickets/create`, params).then(res => { - console.log(res); + // In Progress + linesToNewTicket() { + let ticket = { + oldTicketFk: this.ticket.id, + clientFk: this.ticket.clientFk, + addressFk: this.ticket.addressFk, + agencyModeFk: this.ticket.agencyModeFk, + warehouseFk: this.ticket.warehouseFk + }; + + let sales = this.getCheckedLines(); + + this.$http.post(`/api/Sales/MoveToNewTicket`, {ticket: ticket, sales: sales}).then(res => { + this.goToTicket(res.data.id); }); - }*/ + } goToTicket(ticketID) { this.$state.go("ticket.card.sale", {id: ticketID}); @@ -166,6 +178,12 @@ class Controller extends FilterTicketList { this.$.index.model.instances.splice(instances[i].instance, 1); } } + // Slesperson Mana + getManaSalespersonMana() { + this.$http.get(`/api/Tickets/${this.$state.params.id}/getSalesPersonMana`).then(res => { + this.mana = res.data; + }); + } // Item Descriptor showDescriptor(event, itemFk) { this.$.descriptor.itemFk = itemFk; @@ -177,39 +195,65 @@ class Controller extends FilterTicketList { this.$.popover.relocate(); } - // Ticket Create - showticketCreate() { - console.log(this); - this.$.newTicket.show(); + // Edit Line + showEditPricePopover(event, sale) { + this.sale = sale; + this.editedPrice = this.sale.price; + this.edit = { + ticketFk: this.ticket.id, + id: sale.id, + quantity: sale.quantity + }; + this.$.editPricePopover.parent = event.target; + this.$.editPricePopover.show(); } - onResponse(response) { - if (response === 'ACCEPT') { - let newTicketID = this.$.newTicket.dialog.createTicket(); - console.log(newTicketID); + updatePrice() { + if (this.editedPrice != this.sale.price) { + this.$http.post(`/ticket/api/Sales/updatePrice`, {id: this.edit.id, price: this.editedPrice, ticketFk: this.ticket.id}).then(() => { + this.sale.price = this.edit.price; + this.$.index.accept(); + }); } - } - // Edit Line - _getworkerMana() { - this.$http.get(`/api/WorkerManas/getCurrentWorkerMana`).then(res => { - this.workerMana = res.data[0].mana; - }); + this.$.editPricePopover.hide(); } showEditPopover(event, sale) { this.sale = sale; - this.edit = { + this.edit = [{ + ticketFk: this.ticket.id, id: sale.id, quantity: sale.quantity, price: sale.price, discount: sale.discount - }; - this.$.edit.parent = event.target; - this._getworkerMana(); - this.$.edit.show(); + }]; + this.$.editPopover.parent = event.target; + this.$.editPopover.show(); } - updateLine() { + async showEditDialog() { + this.edit = this.getCheckedLines(); + this.$.editDialog.show(); + } + + hideEditDialog() { + this.$.index.accept(); + this.$.editDialog.hide(); + } + + hideEditPopover() { + this.$.index.accept(); + this.$.editPopover.hide(); + } + + updateQuantity(id, quantity) { + this.$http.post(`/ticket/api/Sales/${id}/updateQuantity`, {quantity: parseInt(quantity)}).then(() => { + this.vnApp.showSuccess(this.translate.instant('Data saved!')); + this.$.index.accept(); + }); + } + + /* updateLine() { if (this.edit.quantity != this.sale.quantity) { this.$http.post(`/ticket/api/Sales/updateQuantity`, {id: this.edit.id, quantity: this.edit.quantity}).then(() => { this.sale.quantity = this.edit.quantity; @@ -228,12 +272,12 @@ class Controller extends FilterTicketList { }); } this.$.edit.hide(); - } + }*/ onMoreClick() { this.removeOptionByName('Mark as reserved'); this.removeOptionByName('Unmark as reserved'); - + this.removeOptionByName('Update discount'); if (!this.isChecked) return; this.moreOptions.push({ @@ -245,6 +289,11 @@ class Controller extends FilterTicketList { callback: this.unmarkAsReserved, name: 'Unmark as reserved'} ); + + this.moreOptions.push({ + callback: this.showEditDialog, + name: 'Update discount'} + ); } /** @@ -294,7 +343,7 @@ class Controller extends FilterTicketList { } } -Controller.$inject = ['$scope', '$timeout', '$state', '$http', 'vnApp']; +Controller.$inject = ['$scope', '$timeout', '$state', '$http', 'vnApp', '$translate']; ngModule.component('vnTicketSale', { template: require('./index.html'), diff --git a/client/ticket/src/sale/locale/es.yml b/client/ticket/src/sale/locale/es.yml index 6860c643b..727be3a78 100644 --- a/client/ticket/src/sale/locale/es.yml +++ b/client/ticket/src/sale/locale/es.yml @@ -4,3 +4,8 @@ Delete ticket: Borrar ticket Mark as reserved: Marcar como reservado Unmark as reserved: Desmarcar como reservado Update discount: Actualizar descuento +There is no changes to save: No hay cambios que guardar +Edit discount: Editar descuento +Move to ticket: Mover a ticket +New ticket: Nuevo ticket +Edit price: Editar precio \ No newline at end of file diff --git a/client/ticket/src/sale/style.scss b/client/ticket/src/sale/style.scss index 83d73bb2a..24cce9966 100644 --- a/client/ticket/src/sale/style.scss +++ b/client/ticket/src/sale/style.scss @@ -1,19 +1,47 @@ @import "colors"; vn-popover.edit { - & div.popover{ width: 200px; } & vn-horizontal.header{ background-color: $main-01; - text-align: center; - & h5{ color: white; + margin: 0 auto; } } + + & p.simulatorTitle{ + margin-bottom: 0px; + font-size: 12px; + color: $main-01; + } + + & vn-label-value{ + padding-bottom: 20px; + } + + div.simulator{ + text-align: center; + } +} + +vn-dialog.edit { + @extend vn-popover.edit; + + .buttons{ + margin-top: 0!important; + } + + p{ + display: none; + } + + vn-ticket-sale-edit-discount > div { + padding-bottom: 0!important; + } } vn-ticket-sale{ @@ -28,7 +56,7 @@ vn-popover.transfer{ min-width: 650px; margin-bottom: 10px; } - & i { + & i { padding-top: 0.2em; font-size: 1.8em; } @@ -41,4 +69,19 @@ vn-dialog.ticket-create{ & vn-card.vn-ticket-create{ padding: 0!important; } +} + +table { + & vn-textfield { + max-width: 100px; + float: right; + } +} + +vn-textfield { + & span.filter { + padding-top: 3px; + padding-right: 3px; + color: $main-font-color + } } \ No newline at end of file From accd66f63af145c6ed0b95395a360d86bf20c5dc Mon Sep 17 00:00:00 2001 From: gerard Date: Thu, 28 Jun 2018 15:21:04 +0200 Subject: [PATCH 02/16] Tarea #352 ticket.lines Modificar descuento --- client/ticket/src/sale/editDiscount.html | 24 +++++ client/ticket/src/sale/editDiscount.js | 71 +++++++++++++++ client/ticket/src/sale/editDiscount.spec.js | 89 +++++++++++++++++++ .../common/methods/sale/updateDiscount.js | 62 +++++++++++++ services/loopback/common/models/sale.js | 7 +- 5 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 client/ticket/src/sale/editDiscount.html create mode 100644 client/ticket/src/sale/editDiscount.js create mode 100644 client/ticket/src/sale/editDiscount.spec.js create mode 100644 services/loopback/common/methods/sale/updateDiscount.js diff --git a/client/ticket/src/sale/editDiscount.html b/client/ticket/src/sale/editDiscount.html new file mode 100644 index 000000000..c6d7e34cd --- /dev/null +++ b/client/ticket/src/sale/editDiscount.html @@ -0,0 +1,24 @@ + +
MANÁ: {{$ctrl.mana | currency:' €':0}}
+
+
+ + + % + + +
+

New price

+

{{($ctrl.edit[0].quantity * $ctrl.edit[0].price) + - (($ctrl.newDiscount * ($ctrl.edit[0].quantity * $ctrl.edit[0].price))/100) + | currency:' €':2}}

+ + +
+
\ No newline at end of file diff --git a/client/ticket/src/sale/editDiscount.js b/client/ticket/src/sale/editDiscount.js new file mode 100644 index 000000000..2ec694c56 --- /dev/null +++ b/client/ticket/src/sale/editDiscount.js @@ -0,0 +1,71 @@ +import ngModule from '../module'; + +class Controller { + constructor($scope, $http, $state, vnApp, $translate) { + this.$scope = $scope; + this.$http = $http; + this.$state = $state; + this.vnApp = vnApp; + this.translate = $translate; + } + + set edit(value) { + this._edit = value; + this.clear(); + this.setNewDiscount(); + } + + get edit() { + return this._edit; + } + + set bulk(value) { + this._bulk = value; + this.setNewDiscount(); + } + + get bulk() { + return this._bulk; + } + + setNewDiscount() { + if (!this.newDiscount && this.edit[0]) + this.newDiscount = this.edit[0].discount; + } + + updateDiscount() { + let editLines = []; + let modified = false; + for (let i = 0; i < this.edit.length; i++) { + if (this.newDiscount != this.edit[0].discount || this.bulk) { + editLines.push({id: this.edit[i].id, discount: this.newDiscount, ticketFk: this.$state.params.id}); + modified = true; + } + } + if (modified) { + this.$http.post(`/ticket/api/Sales/updateDiscount`, {editLines}).then(() => { + this.hide(); + }); + } else { + this.vnApp.showError(this.translate.instant('There is no changes to save')); + } + this.clear(); + } + + clear() { + this.newDiscount = null; + } +} + +Controller.$inject = ['$scope', '$http', '$state', 'vnApp', '$translate']; + +ngModule.component('vnTicketSaleEditDiscount', { + template: require('./editDiscount.html'), + controller: Controller, + bindings: { + edit: ' { + describe('Component vnTicketSaleEditDiscount', () => { + let $componentController; + let controller; + let $httpBackend; + let $state; + let $scope; + + beforeEach(() => { + angular.mock.module('ticket'); + }); + + beforeEach(angular.mock.inject((_$componentController_, _$state_, _$httpBackend_, $rootScope) => { + $componentController = _$componentController_; + $httpBackend = _$httpBackend_; + $httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({}); + $scope = $rootScope.$new(); + $scope.index = {model: {instances: [{id: 1}, {id: 2}]}, accept: () => { + return { + then: () => {} + }; + }}; + $state = _$state_; + $state.params.id = 1; + controller = $componentController('vnTicketSaleEditDiscount', {$scope: $scope, $state: $state}); + controller._edit = [{id: 3, discount: 15}]; + controller.hide = () => {}; + })); + + describe('edit() setter', () => { + it('should set _edit value and call setNewDiscount', () => { + spyOn(controller, 'setNewDiscount'); + controller.edit = {id: 1}; + + expect(controller.edit).toEqual({id: 1}); + expect(controller.setNewDiscount).toHaveBeenCalledWith(); + }); + }); + + describe('bulk() setter', () => { + it('should set _bulk value and call setNewDiscount', () => { + spyOn(controller, 'setNewDiscount'); + controller.bulk = true; + + expect(controller.bulk).toEqual(true); + expect(controller.setNewDiscount).toHaveBeenCalledWith(); + }); + }); + + describe('setNewDiscount()', () => { + it('should set NewDiscount to edit[0].discount value if it doesnt exists', () => { + controller.edit = [{discount: 1}]; + controller.setNewDiscount(); + + expect(controller.newDiscount).toEqual(1); + }); + }); + + describe('updateDiscount()', () => { + it('should make a query if the discount value has been modified or the bulk value is true', () => { + controller.bulk = true; + controller.newDiscount = 15; + + $httpBackend.expectPOST(`/ticket/api/Sales/updateDiscount`).respond(); + controller.updateDiscount(); + + $httpBackend.flush(); + }); + + it('should call vnApp.showError if the discount value hasnt has been modified and the bulk value is false', () => { + controller.bulk = false; + spyOn(controller.vnApp, "showError"); + controller.updateDiscount(); + + expect(controller.vnApp.showError).toHaveBeenCalledWith('There is no changes to save'); + }); + }); + + describe('clear()', () => { + it('should set newDiscount to null', () => { + controller.clear(); + + expect(controller.newDiscount).toEqual(null); + }); + }); + }); +}); diff --git a/services/loopback/common/methods/sale/updateDiscount.js b/services/loopback/common/methods/sale/updateDiscount.js new file mode 100644 index 000000000..5d393c9a6 --- /dev/null +++ b/services/loopback/common/methods/sale/updateDiscount.js @@ -0,0 +1,62 @@ +module.exports = Self => { + Self.remoteMethod('updateDiscount', { + description: 'Changes the discount of a sale', + accessType: '', + accepts: [{ + arg: 'params', + type: 'object', + required: true, + description: 'sale ID, newDiscount, price', + http: {source: 'body'} + }], + returns: { + type: 'string', + root: true + }, + http: { + path: `/updateDiscount`, + verb: 'post' + } + }); + + Self.updateDiscount = async params => { + let model = Self.app.models; + let thisTicketIsEditable = await model.Ticket.isEditable(params.editLines[0].ticketFk); + + if (!thisTicketIsEditable) + throw new Error(`The sales of this ticket can't be modified`); + + let ticket = await model.Ticket.find({ + where: { + id: params.editLines[0].ticketFk + }, + include: [{ + relation: 'client', + scope: { + fields: ['salesPersonFk'] + } + }], + fields: ['id', 'clientFk'] + }); + + let componentToUse; + let usesMana = await model.WorkerMana.findOne({where: {workerFk: ticket[0].client().salesPersonFk}, fields: 'amount'}); + + if (usesMana) + componentToUse = 37; + else + componentToUse = 34; + + for (let i = 0; i < params.editLines.length; i++) { + let currentLine = await model.Sale.findOne({where: {id: params.editLines[i].id}, fields: 'price'}); + let value = (-currentLine.price * params.editLines[i].discount / 100); + await model.SaleComponent.upsert({saleFk: params.editLines[i].id, value: value, componentFk: componentToUse}); + + await model.Sale.update({id: params.editLines[i].id}, {discount: params.editLines[i].discount}); + } + + query = ` + call vn.manaSpellersRequery(?)`; + await Self.rawSql(query, [ticket[0].client().salesPersonFk]); + }; +}; diff --git a/services/loopback/common/models/sale.js b/services/loopback/common/models/sale.js index 2d2aa22f5..9bd300860 100644 --- a/services/loopback/common/models/sale.js +++ b/services/loopback/common/models/sale.js @@ -4,8 +4,9 @@ module.exports = Self => { require('../methods/sale/priceDifference')(Self); require('../methods/sale/crudSale')(Self); require('../methods/sale/moveToTicket')(Self); + require('../methods/sale/moveToNewTicket')(Self); require('../methods/sale/removes')(Self); -// require('../methods/sale/updateDiscount')(Self); -// require('../methods/sale/updatePrice')(Self); -// require('../methods/sale/updateQuantity')(Self); + require('../methods/sale/updateDiscount')(Self); + require('../methods/sale/updatePrice')(Self); + require('../methods/sale/updateQuantity')(Self); }; From 4bc391e083441b983c876d7c886bdbac47fbae25 Mon Sep 17 00:00:00 2001 From: gerard Date: Thu, 28 Jun 2018 15:24:27 +0200 Subject: [PATCH 03/16] Tarea #354 seccion ticket.line menu de mover lineas --- .../common/methods/agency/getFirstShipped.js | 27 ++++++++++ .../common/methods/sale/moveToNewTicket.js | 52 +++++++++++++++++++ .../common/methods/sale/moveToTicket.js | 2 +- .../loopback/common/methods/ticket/create.js | 40 ++++++++++++++ services/loopback/common/models/agency.js | 2 + services/loopback/common/models/ticket.js | 3 +- 6 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 services/loopback/common/methods/agency/getFirstShipped.js create mode 100644 services/loopback/common/methods/sale/moveToNewTicket.js create mode 100644 services/loopback/common/methods/ticket/create.js diff --git a/services/loopback/common/methods/agency/getFirstShipped.js b/services/loopback/common/methods/agency/getFirstShipped.js new file mode 100644 index 000000000..15d831209 --- /dev/null +++ b/services/loopback/common/methods/agency/getFirstShipped.js @@ -0,0 +1,27 @@ +module.exports = Self => { + Self.remoteMethod('getFirstShipped', { + description: 'Returns the first shipped and landed possible for params', + accessType: '', + accepts: [{ + arg: 'params', + type: 'object', + required: true, + description: 'agencyModeFk, addressFk, warehouseFk' + }], + returns: { + type: 'object', + root: true + }, + http: { + path: `/getFirstShipped`, + verb: 'get' + } + }); + + Self.getFirstShipped = async params => { + let query = `CALL vn.agencyHourGetFirstShipped(?, ?, ?)`; + let [result] = await Self.rawSql(query, [params.agencyModeFk, params.addressFk, params.warehouseFk]); + + return result[0]; + }; +}; diff --git a/services/loopback/common/methods/sale/moveToNewTicket.js b/services/loopback/common/methods/sale/moveToNewTicket.js new file mode 100644 index 000000000..7f9854c56 --- /dev/null +++ b/services/loopback/common/methods/sale/moveToNewTicket.js @@ -0,0 +1,52 @@ +module.exports = Self => { + Self.remoteMethod('moveToNewTicket', { + description: 'Change the state of a ticket', + accessType: '', + accepts: [{ + arg: 'ticketParams', + type: 'object', + required: true, + description: '[sales IDs], newTicketFk, actualTicketFk', + http: {source: 'body'} + }, { + arg: 'sales', + type: 'object', + required: true, + description: '[sales IDs]', + http: {source: 'body'} + }], + returns: { + type: 'string', + root: true + }, + http: { + path: `/moveToNewTicket`, + verb: 'post' + } + }); + + Self.moveToNewTicket = async params => { + let model = Self.app.models; + let thisTicketIsEditable = await model.Ticket.isEditable(params.ticket.oldTicketFk); + if (!thisTicketIsEditable) + throw new Error(`The sales of this ticket can't be modified`); + + let travelDates = await model.Agency.getFirstShipped(params.ticket); + console.log(travelDates); + let newTicketParams = { + clientFk: params.ticket.clientFk, + addressFk: params.ticket.addressFk, + agencyModeFk: params.ticket.agencyModeFk, + warehouseFk: params.ticket.warehouseFk, + shipped: travelDates.vShipped, + landed: travelDates.vLanded + }; + + let newTicket = await model.Ticket.create(newTicketParams); + + for (let i = 0; i < params.sales.length; i++) { + await model.Sale.update({id: params.sales[i].id}, {ticketFk: newTicket.id}); + } + return newTicket; + }; +}; diff --git a/services/loopback/common/methods/sale/moveToTicket.js b/services/loopback/common/methods/sale/moveToTicket.js index 45b7f49df..4ecc72d19 100644 --- a/services/loopback/common/methods/sale/moveToTicket.js +++ b/services/loopback/common/methods/sale/moveToTicket.js @@ -26,7 +26,7 @@ module.exports = Self => { let newTicketIsEditable = await Self.app.models.Ticket.isEditable(params.newTicketFk); if (!newTicketIsEditable) - throw new Error(`The sales of this ticket can't be modified`); + throw new Error(`The sales of that ticket can't be modified`); for (let i = 0; i < params.sales.length; i++) { await Self.app.models.Sale.update({id: params.sales[i].id}, {ticketFk: params.newTicketFk}); diff --git a/services/loopback/common/methods/ticket/create.js b/services/loopback/common/methods/ticket/create.js new file mode 100644 index 000000000..db7c100cb --- /dev/null +++ b/services/loopback/common/methods/ticket/create.js @@ -0,0 +1,40 @@ +module.exports = Self => { + Self.remoteMethod('create', { + description: 'Create a newticket and returns the new ID', + accessType: 'WRITE', + accepts: [{ + arg: 'params', + type: 'object', + required: true, + description: 'ClientFk, Shipped, WharehouseFk, CompanyFk, AddressFk, AgencyModeFk, RouteFk, Landed', + http: {source: 'body'} + }], + returns: { + type: 'number', + root: true + }, + http: { + path: `/create`, + verb: 'post' + } + }); + + Self.create = async params => { + let existsAddress = await Self.app.models.Address.findOne({where: {id: params.addressFk, clientFk: params.clientFk}}); + if (!existsAddress) + throw new Error(`This address doesn't exist`); + + let query = `CALL vn.ticketListCreate(?, ?, ?, ?, ?, ?, ?, ?);`; + let result = await Self.rawSql(query, [ + params.clientFk, + params.shipped, + params.wharehouseFk, + params.companyFk, + params.addressFk, + params.agencyModeFk, + params.routeFk, + params.landed + ]); + return result; + }; +}; diff --git a/services/loopback/common/models/agency.js b/services/loopback/common/models/agency.js index ac6c4d03a..251ba183a 100644 --- a/services/loopback/common/models/agency.js +++ b/services/loopback/common/models/agency.js @@ -1,3 +1,5 @@ module.exports = Self => { Self.defineScope({where: {isManaged: {neq: 0}}}); + //require('../methods/agency/sendsThatDay')(Self); + require('../methods/agency/getFirstShipped')(Self); }; diff --git a/services/loopback/common/models/ticket.js b/services/loopback/common/models/ticket.js index 0d74b4eb1..2c26b92f8 100644 --- a/services/loopback/common/models/ticket.js +++ b/services/loopback/common/models/ticket.js @@ -7,9 +7,10 @@ module.exports = Self => { require('../methods/ticket/getTotal')(Self); require('../methods/ticket/getTaxes')(Self); require('../methods/ticket/componentUpdate')(Self); -// require('../methods/ticket/create')(Self); + require('../methods/ticket/create')(Self); require('../methods/ticket/isEditable')(Self); require('../methods/ticket/threeLastActive')(Self); require('../methods/ticket/deleted')(Self); require('../methods/ticket/getVAT')(Self); + require('../methods/ticket/getSalesPersonMana')(Self); }; From ffc8c54899ac87c1235f82128c1bcc0c1d07c623 Mon Sep 17 00:00:00 2001 From: gerard Date: Thu, 28 Jun 2018 15:25:09 +0200 Subject: [PATCH 04/16] Tarea #355 ticket.lineas Modificar cantidad --- .../common/methods/sale/updateQuantity.js | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 services/loopback/common/methods/sale/updateQuantity.js diff --git a/services/loopback/common/methods/sale/updateQuantity.js b/services/loopback/common/methods/sale/updateQuantity.js new file mode 100644 index 000000000..eac9ac309 --- /dev/null +++ b/services/loopback/common/methods/sale/updateQuantity.js @@ -0,0 +1,34 @@ +module.exports = Self => { + Self.remoteMethod('updateQuantity', { + description: 'Changes the quantity of a sale', + accessType: '', + accepts: [{ + arg: 'id', + type: 'number', + required: true, + description: 'sale ID', + http: {source: 'path'} + }, { + arg: 'quantity', + type: 'number', + required: true, + description: 'newQuantity' + }], + returns: { + type: 'string', + root: true + }, + http: { + path: `/:id/updateQuantity`, + verb: 'post' + } + }); + + Self.updateQuantity = async(id, quantity) => { + let currentLine = await Self.app.models.Sale.findOne({where: {id: id}, fields: ['quantity']}); + if (quantity > currentLine.quantity) + throw new Error('The new quantity should be smaller than the old one'); + + return await Self.app.models.Sale.update({id: id}, {quantity: quantity}); + }; +}; From cebb7030c591d59c6d434d02f9cee14bd535c01b Mon Sep 17 00:00:00 2001 From: gerard Date: Thu, 28 Jun 2018 15:27:29 +0200 Subject: [PATCH 05/16] Tarea #373 ticket.sale Modificar precio --- .../common/methods/sale/updatePrice.js | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 services/loopback/common/methods/sale/updatePrice.js diff --git a/services/loopback/common/methods/sale/updatePrice.js b/services/loopback/common/methods/sale/updatePrice.js new file mode 100644 index 000000000..be0b5af31 --- /dev/null +++ b/services/loopback/common/methods/sale/updatePrice.js @@ -0,0 +1,64 @@ +module.exports = Self => { + Self.remoteMethod('updatePrice', { + description: 'Changes the discount of a sale', + accessType: '', + accepts: [{ + arg: 'params', + type: 'object', + required: true, + description: 'sale ID, newPrice', + http: {source: 'body'} + }], + returns: { + type: 'string', + root: true + }, + http: { + path: `/updatePrice`, + verb: 'post' + } + }); + + Self.updatePrice = async params => { + if (!params.price) params.price = 0; + let model = Self.app.models; + let thisTicketIsEditable = await model.Ticket.isEditable(params.ticketFk); + + if (!thisTicketIsEditable) + throw new Error(`The sales of this ticket can't be modified`); + + let ticket = await model.Ticket.find({ + where: { + id: params.ticketFk + }, + include: [{ + relation: 'client', + scope: { + fields: ['salesPersonFk'] + } + }], + fields: ['id', 'clientFk'] + }); + + let usesMana = await model.WorkerMana.findOne({where: {workerFk: ticket[0].client().salesPersonFk}, fields: 'amount'}); + let currentLine = await Self.app.models.Sale.findOne({where: {id: params.id}, fields: 'price'}); + let componentToUse; + + if (usesMana) + componentToUse = 37; + else + componentToUse = 34; + + let value = (params.price - currentLine.price); + + let query = `INSERT INTO vn.saleComponent(saleFk, componentFk, value) + VALUES(?, ?, ?) ON DUPLICATE KEY UPDATE value = value + ?`; + + await Self.rawSql(query, [params.id, componentToUse, value, value]); + + await Self.app.models.Sale.update({id: params.id}, {price: params.price}); + query = ` + call vn.manaSpellersRequery(?)`; + await Self.rawSql(query, [ticket[0].client().salesPersonFk]); + }; +}; From 88033a7539ffdc459400ec018729d9eec8a5069a Mon Sep 17 00:00:00 2001 From: gerard Date: Thu, 28 Jun 2018 15:29:00 +0200 Subject: [PATCH 06/16] Tarea #373, Tarea #355,Tarea #352 getSalesPersonMana --- .../methods/ticket/getSalesPersonMana.js | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 services/loopback/common/methods/ticket/getSalesPersonMana.js diff --git a/services/loopback/common/methods/ticket/getSalesPersonMana.js b/services/loopback/common/methods/ticket/getSalesPersonMana.js new file mode 100644 index 000000000..9b252852a --- /dev/null +++ b/services/loopback/common/methods/ticket/getSalesPersonMana.js @@ -0,0 +1,38 @@ +module.exports = Self => { + Self.remoteMethod('getSalesPersonMana', { + description: 'Returns mana of a salesperson of a ticket', + accessType: 'READ', + accepts: [{ + arg: 'id', + type: 'number', + required: true, + description: 'ticket id', + http: {source: 'path'} + }], + returns: { + root: true + }, + http: { + path: `/:id/getSalesPersonMana`, + verb: 'GET' + } + }); + + Self.getSalesPersonMana = async ticketFk => { + let ticket = await Self.app.models.Ticket.find({ + where: { + id: ticketFk + }, + include: [{ + relation: 'client', + scope: { + fields: ['salesPersonFk'] + } + }], + fields: ['id', 'clientFk'] + }); + let mana = await Self.app.models.WorkerMana.findOne({where: {workerFk: ticket[0].client().salesPersonFk}, fields: 'amount'}); + + return mana.amount | 0; + }; +}; From 5cfb11641c67fc18cc14a5aa84c486d05526fee0 Mon Sep 17 00:00:00 2001 From: gerard Date: Thu, 28 Jun 2018 15:30:11 +0200 Subject: [PATCH 07/16] Tarea #373, Tarea #352 focus on first input popover --- client/core/src/components/popover/popover.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client/core/src/components/popover/popover.js b/client/core/src/components/popover/popover.js index afc1f7c4e..bfdaaaed6 100644 --- a/client/core/src/components/popover/popover.js +++ b/client/core/src/components/popover/popover.js @@ -75,6 +75,15 @@ export default class Popover extends Component { this.document.addEventListener('keydown', this.docKeyDownHandler); this.document.addEventListener('focusin', this.docFocusInHandler); + let firstFocusable = this.element.querySelector('input, textarea'); + if (firstFocusable) { + firstFocusable.addEventListener('focus', () => { + firstFocusable.select(); + }); + setTimeout(() => { + firstFocusable.focus(); + }, 200); + } this.deregisterCallback = this.$transitions.onStart({}, () => this.hide()); this.relocate(); From da69165fb3506a1d09adf27479c49fead669db89 Mon Sep 17 00:00:00 2001 From: gerard Date: Thu, 28 Jun 2018 15:30:48 +0200 Subject: [PATCH 08/16] Tarea #352 ticket.lines Modificar descuento --- client/ticket/src/ticket.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ticket/src/ticket.js b/client/ticket/src/ticket.js index aae0d2005..d07caa7cd 100644 --- a/client/ticket/src/ticket.js +++ b/client/ticket/src/ticket.js @@ -15,6 +15,7 @@ import './expedition'; import './volume'; import './package/index'; import './sale'; +import './sale/editDiscount'; import './tracking/index'; import './tracking/edit'; import './fetched-tags'; From 526929cd56de979e6e858f969f9ab29de6984526 Mon Sep 17 00:00:00 2001 From: gerard Date: Thu, 28 Jun 2018 15:47:14 +0200 Subject: [PATCH 09/16] fdescribe deleted --- client/core/src/components/th/th.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/core/src/components/th/th.spec.js b/client/core/src/components/th/th.spec.js index 9d1c9e95a..fd863b46e 100644 --- a/client/core/src/components/th/th.spec.js +++ b/client/core/src/components/th/th.spec.js @@ -1,7 +1,7 @@ import './index.js'; import template from './index.html'; -fdescribe('Component vnTh', () => { +describe('Component vnTh', () => { let $componentController; let controller; let $element; From 2594f170cf11cd359a71e8fa0124b90988d2bab5 Mon Sep 17 00:00:00 2001 From: gerard Date: Thu, 28 Jun 2018 15:54:54 +0200 Subject: [PATCH 10/16] =?UTF-8?q?Bug=20#137=20Refactorizaci=C3=B3n=20Textf?= =?UTF-8?q?ield?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/src/components/textfield/style.scss | 157 +++++++++++++----- .../src/components/textfield/textfield.html | 69 +++++--- .../src/components/textfield/textfield.js | 60 +++++-- .../components/textfield/textfield.spec.js | 28 +++- 4 files changed, 227 insertions(+), 87 deletions(-) diff --git a/client/core/src/components/textfield/style.scss b/client/core/src/components/textfield/style.scss index 181ad8d8b..10609c55c 100644 --- a/client/core/src/components/textfield/style.scss +++ b/client/core/src/components/textfield/style.scss @@ -1,45 +1,124 @@ +@import "colors"; vn-textfield { - .mdl-chip__action { - position: absolute; - width: auto; - top: 0px; - right: -6px; - margin: 21px 0px; - background: white; - opacity: 1; - z-index: 1; - color: #aaa; - } - .mdl-textfield { - width: 100%; - } - .mdl-textfield__error { - visibility: visible; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - width: 100%; - } - .mdl-textfield.invalid { - .mdl-textfield__input { - border-color: #d50000; - box-shadow: none; + margin: 20px 0!important; + display: block; + + .leftIcons, .rightIcons, .suffix{ + display: inline-flex; + color: $secondary-font-color; + & .material-icons{ + font-size: 20px!important } - .mdl-textfield__label::after { + } + .leftIcons{ + margin-right: 3px; + } + .container{ + width: 100%; + position: relative; + padding-bottom: 2px; + } + .textField{ + width: 100%; + display: inline-flex; + position: relative; + padding: 4px 0; + } + .infix { + position: relative; + display: block; + flex: auto; + width: 100%; + min-width: 0; + } + i.pointer { + visibility: hidden; + } + i.visible { + visibility: visible; + } + label { + position: absolute; + bottom: 2px; + pointer-events: none; + color: $secondary-font-color; + transition-duration: .2s; + transition-timing-function: cubic-bezier(.4,0,.2,1); + } + input { + outline: none; + border: none; + font-family: "Helvetica","Arial",sans-serif; + display: block; + font-size: 16px; + width: 100%; + background: 0 0; + color: inherit; + + &[type=number] { + -moz-appearance: textfield; + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button{ + -webkit-appearance: none; + margin: 0; + } + } + &:invalid { + box-shadow:none; + } + } + .underline{ + position: absolute; + bottom: 0; + height: 1px; + content: ' '; + pointer-events: none; + width: 100%; + background-color: rgba(0,0,0,.12); + } + .selected.underline{ + background-color: rgb(255,152,0); + height: 2px; + left: 50%; + width: 0px!important; + transition-duration: 0.2s; + transition-timing-function: cubic-bezier(.4,0,.2,1); + } + &.not-empty { + & label { + bottom: 24px; + color: $main-01; + font-size: 12px; + } + } + div.selected{ + &.container{ + border-bottom: 0px; + } + & label { + bottom: 24px; + color: $main-01; + font-size: 12px; + } + & .selected.underline{ + left: 0; + width: 100%!important; + } + } + & > div.container > div.textField > div.infix.invalid{ + @extend div.selected; + + & > span.mdl-textfield__error{ + visibility: visible; + margin-top: 9px; + } + & > label{ + color: #d50000; + } + } + .infix.invalid + .underline { + &{ background-color: #d50000; } } - .mdl-textfield--floating-label.invalid .mdl-textfield__label { - color: #d50000; - font-size: 12px; - top: 4px; - } - .material-icons { - font-size: 18px; - float: right; - margin-right: 5px; - } - .material-icons:hover { - color: rgba(0,0,0, .87); - } } \ No newline at end of file diff --git a/client/core/src/components/textfield/textfield.html b/client/core/src/components/textfield/textfield.html index fe69149a3..edd341b9c 100644 --- a/client/core/src/components/textfield/textfield.html +++ b/client/core/src/components/textfield/textfield.html @@ -1,29 +1,46 @@ -
- -
- - info_outline - - - clear - + ng-mouseleave="$ctrl.hasMouseIn = false" + style="display: inline-flex" +> +
+
+
+ +
+ + + +
+
+
+
+ + clear + + + info_outline + +
+
-
diff --git a/client/core/src/components/textfield/textfield.js b/client/core/src/components/textfield/textfield.js index 56fc23955..fb86e2309 100644 --- a/client/core/src/components/textfield/textfield.js +++ b/client/core/src/components/textfield/textfield.js @@ -3,45 +3,73 @@ import Input from '../../lib/input'; import './style.scss'; export default class Textfield extends Input { - constructor($element, $scope, $attrs, vnTemplate) { + constructor($element, $scope, $attrs, vnTemplate, $transclude) { super($element, $scope); vnTemplate.normalizeInputAttrs($attrs); this._value = null; - this.type = $attrs.type || 'text'; + this.type = $attrs.type; this.showActions = false; this.hasInfo = Boolean($attrs.info); this.info = $attrs.info || null; this.hasFocus = false; this.hasMouseIn = false; - componentHandler.upgradeElement($element[0].firstChild); + + if ($transclude) { + $transclude($scope.$parent, tClone => { + this.leftIcons = tClone[0]; + }, null, 'leftIcons'); + $transclude($scope.$parent, tClone => { + this.rightIcons = tClone[0]; + }, null, 'rightIcons'); + } } - get value() { - return this._value; + set onChange(value) { + this.input.addEventListener('change', value); + } + + set leftIcons(value) { + for (let i = 0; i < value.children.length; i++) { + this.element.querySelector('.leftIcons').appendChild(value.children[i]); + } + } + set rightIcons(value) { + for (let i = 0; i < value.children.length; i++) { + this.element.querySelector('.rightIcons').appendChild(value.children[i]); + } } set value(value) { this._value = (value === undefined || value === '') ? null : value; this.input.value = this._value; - this.hasValue = Boolean(this._value); - this.mdlUpdate(); - } + this.hasValue = this._value !== null; + if (this.hasValue) this.element.classList.add('not-empty'); + else this.element.classList.remove('not-empty'); + } + get value() { + return this._value; + } + set type(value) { + this._type = value || 'text'; + } + get type() { + return this._type; + } set vnTabIndex(value) { this.input.tabindex = value; } - clear() { this.value = null; this.input.focus(); } - mdlUpdate() { - let mdlElement = this.element.firstChild.MaterialTextfield; - if (mdlElement) mdlElement.updateClasses_(); - } } -Textfield.$inject = ['$element', '$scope', '$attrs', 'vnTemplate']; +Textfield.$inject = ['$element', '$scope', '$attrs', 'vnTemplate', '$transclude']; ngModule.component('vnTextfield', { template: require('./textfield.html'), + transclude: { + leftIcons: '?tLeftIcons', + rightIcons: '?tRightIcons' + }, controller: Textfield, bindings: { value: '=model', @@ -51,6 +79,8 @@ ngModule.component('vnTextfield', { readonly: ' { $attrs = {}; $timeout = _$timeout_; $element = angular.element('
'); - controller = $componentController('vnTextfield', {$scope, $element, $attrs, $timeout}); + controller = $componentController('vnTextfield', {$scope, $element, $attrs, $timeout, $transclude: null}); })); describe('value() setter', () => { - it(`should set _value, input.value and hasValue properties to null, '' and false then call mdlUpdate()`, () => { - spyOn(controller, 'mdlUpdate'); + it(`should set _value, input.value and hasValue properties to null, '' and false`, () => { let testValue = ''; controller.value = testValue; expect(controller._value).toEqual(null); expect(controller.input.value).toEqual(testValue); expect(controller.hasValue).toEqual(Boolean(testValue)); - expect(controller.mdlUpdate).toHaveBeenCalledWith(); }); - it(`should set _value, input.value and hasValue propertiest to test, test and true then call mdlUpdate()`, () => { - spyOn(controller, 'mdlUpdate'); + it(`should set _value, input.value and hasValue propertiest to test, test and true`, () => { let testValue = 'test'; controller.value = testValue; expect(controller._value).toEqual(testValue); expect(controller.input.value).toEqual(testValue); expect(controller.hasValue).toEqual(Boolean(testValue)); - expect(controller.mdlUpdate).toHaveBeenCalledWith(); + }); + }); + + describe('type() setter', () => { + it(`should set _type to 'text' if theres not given value`, () => { + controller.type = null; + + expect(controller._type).toEqual('text'); + }); + }); + + describe('clear()', () => { + it(`should set value property to null and call focus`, () => { + spyOn(controller.input, 'focus'); + controller.clear(); + + expect(controller.value).toEqual(null); + expect(controller.input.focus).toHaveBeenCalledWith(); }); }); }); From a01fa0e9ad688db16ec5dc605e341bf38a55dc59 Mon Sep 17 00:00:00 2001 From: gerard Date: Thu, 28 Jun 2018 15:56:09 +0200 Subject: [PATCH 11/16] =?UTF-8?q?Bug=20#136=20Refactorizaci=C3=B3n=20DropD?= =?UTF-8?q?own?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/drop-down/drop-down.html | 18 ++++++------------ .../core/src/components/drop-down/drop-down.js | 2 +- .../core/src/components/drop-down/style.scss | 11 ++++++++++- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/client/core/src/components/drop-down/drop-down.html b/client/core/src/components/drop-down/drop-down.html index 9c216be38..c21498e8d 100755 --- a/client/core/src/components/drop-down/drop-down.html +++ b/client/core/src/components/drop-down/drop-down.html @@ -8,18 +8,12 @@ on-close="$ctrl.onClose()">