diff --git a/client/item/src/diary/style.scss b/client/item/src/diary/style.scss index eca181bf1..18dbda1aa 100644 --- a/client/item/src/diary/style.scss +++ b/client/item/src/diary/style.scss @@ -17,12 +17,10 @@ vn-item-diary { .balanceNegative .balance { background-color: $main-01; - color: white; } .isIn .in { background-color: $main-02; - color: white; } .truncate { diff --git a/client/order/src/card/index.js b/client/order/src/card/index.js index 9a08a9ddb..71119ad9a 100644 --- a/client/order/src/card/index.js +++ b/client/order/src/card/index.js @@ -47,7 +47,7 @@ class Controller { let query = `/order/api/Orders/${this.$state.params.id}/getTotal`; this.$http.get(query).then(res => { if (res.data) { - this.order.total = res.data.total; + this.order.total = res.data; } }); } diff --git a/client/order/src/card/index.spec.js b/client/order/src/card/index.spec.js index 46bfec136..3d843852a 100644 --- a/client/order/src/card/index.spec.js +++ b/client/order/src/card/index.spec.js @@ -48,7 +48,7 @@ describe('Order', () => { 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'}); + $httpBackend.expectGET(`/order/api/Orders/${controller.$state.params.id}/getTotal`).respond('20M'); controller.getTotal(); $httpBackend.flush(); diff --git a/client/order/src/create/card.js b/client/order/src/create/card.js index ba605ae73..6a477f59e 100644 --- a/client/order/src/create/card.js +++ b/client/order/src/create/card.js @@ -75,8 +75,6 @@ class Controller { this.$http.post(`order/api/Orders/new`, params).then(res => { this.vnApp.showSuccess(this.translate.instant('Data saved!')); this.$state.go("order.card.catalog", {id: res.data}); - }).catch(e => { - this.vnApp.showError(this.translate.instant(e.data.error.message)); }); } } diff --git a/client/order/src/index/index.html b/client/order/src/index/index.html index 8c380726d..9a02eb75f 100644 --- a/client/order/src/index/index.html +++ b/client/order/src/index/index.html @@ -20,7 +20,7 @@ + ui-sref="order.card.summary({id: {{::order.id}}})"> {{::order.id}} @@ -36,6 +36,13 @@ {{::order.sourceApp}} {{::order.created | date:'dd/MM/yyyy HH:mm'}} {{::order.company.code}} + + + + @@ -48,7 +55,13 @@ scroll-selector="ui-view"> - + - \ No newline at end of file + + + + + + \ No newline at end of file diff --git a/client/order/src/index/index.js b/client/order/src/index/index.js index 384cfb13e..e0571c4a4 100644 --- a/client/order/src/index/index.js +++ b/client/order/src/index/index.js @@ -18,26 +18,6 @@ export default class Controller { }; } -/* exprBuilder(param, value) { - switch (param) { - case 'search': - return /^\d+$/.test(value) - ? {id: value} - : {nickname: {regexp: value}}; - case 'from': - return {shipped: {gte: value}}; - case 'to': - return {shipped: {lte: value}}; - case 'nickname': - return {[param]: {regexp: value}}; - case 'id': - case 'clientFk': - case 'agencyModeFk': - case 'warehouseFk': - return {[param]: value}; - } - } */ - showDescriptor(event, clientFk) { this.$scope.descriptor.clientFk = clientFk; this.$scope.descriptor.parent = event.target; @@ -50,11 +30,11 @@ export default class Controller { this.$scope.popover.relocate(); } - preview(event, ticket) { + preview(event, order) { event.preventDefault(); event.stopImmediatePropagation(); - this.$scope.dialogSummaryTicket.show(); - this.ticketSelected = ticket; + this.$scope.orderSummaryDialog.show(); + this.order = order; } } diff --git a/client/order/src/line/index.js b/client/order/src/line/index.js index df83de62a..68d71c9ae 100644 --- a/client/order/src/line/index.js +++ b/client/order/src/line/index.js @@ -48,10 +48,10 @@ class Controller { } getVAT() { - let query = `/order/api/Orders/${this.$state.params.id}/getTaxes`; + let query = `/order/api/Orders/${this.$state.params.id}/getVAT`; this.$http.get(query).then(res => { - this.VAT = res.data.tax; + this.VAT = res.data; }); } diff --git a/client/order/src/line/index.spec.js b/client/order/src/line/index.spec.js index b57e57e0f..1a3332994 100644 --- a/client/order/src/line/index.spec.js +++ b/client/order/src/line/index.spec.js @@ -56,9 +56,9 @@ describe('Order', () => { }); }); - describe('getTaxes()', () => { - it('should make a query to get the taxes of a given order', () => { - $httpBackend.expectGET(`/order/api/Orders/1/getTaxes`).respond({data: {tax: 3}}); + describe('getVAT()', () => { + it('should make a query to get the VAT of a given order', () => { + $httpBackend.expectGET(`/order/api/Orders/1/getVAT`).respond({data: {tax: 3}}); controller.getVAT(); $httpBackend.flush(); }); diff --git a/client/order/src/summary/index.html b/client/order/src/summary/index.html index c5fdbff8b..261a8b965 100644 --- a/client/order/src/summary/index.html +++ b/client/order/src/summary/index.html @@ -1,9 +1,94 @@ - - - - -
Order
-
-
-
-
\ No newline at end of file + + + +
{{$ctrl.summary.id}} - {{$ctrl.summary.client.name}} - {{$ctrl.summary.client.salesPerson.id}}
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Subtotal {{::$ctrl.summary.subTotal | currency:' €':2}}

+

VAT {{::$ctrl.summary.VAT | currency:' €':2}}

+

Total {{::$ctrl.summary.total | currency:' €':2}}

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ItemDescriptionQuantityPriceAmount
+ + + + + + {{("000000"+row.itemFk).slice(-6)}} + + {{::row.quantity}}{{::row.price | currency:'€':2}}{{::row.quantity * row.price | currency:'€':2}}
No results
+
+
+
+ + diff --git a/client/order/src/summary/index.js b/client/order/src/summary/index.js index 8ce557321..e84444718 100644 --- a/client/order/src/summary/index.js +++ b/client/order/src/summary/index.js @@ -1,12 +1,57 @@ import ngModule from '../module'; +import './style.scss'; class Controller { - constructor($http) { + constructor($scope, $http, $state) { + this.$scope = $scope; this.$http = $http; + this.$state = $state; + this.order = {}; + } + + setSummary() { + this.$http.get(`/order/api/Orders/${this.order.id}/summary`).then(res => { + if (res && res.data) { + this.summary = res.data; + } + }); + } + + get formattedAddress() { + if (!this.summary) return; + + let address = this.summary.address; + let province = address.province ? `(${address.province.name})` : ''; + + return `${address.street} - ${address.city} ${province}`; + } + + $onChanges() { + if (this.order && this.order.id) + this.setSummary(); + } + + showDescriptor(event, item) { + this.quicklinks = { + btnThree: { + icon: 'icon-transaction', + state: `item.card.diary({ + id: ${item.id}, + })`, + tooltip: 'Item diary' + } + }; + this.$scope.descriptor.itemFk = item.id; + this.$scope.descriptor.parent = event.target; + this.$scope.descriptor.show(); + } + + onDescriptorLoad() { + this.$scope.popover.relocate(); } } -Controller.$inject = ['$http']; +Controller.$inject = ['$scope', '$http', '$state']; ngModule.component('vnOrderSummary', { template: require('./index.html'), diff --git a/client/order/src/summary/index.spec.js b/client/order/src/summary/index.spec.js new file mode 100644 index 000000000..17f3ebbbe --- /dev/null +++ b/client/order/src/summary/index.spec.js @@ -0,0 +1,50 @@ +import './index'; + +describe('Order', () => { + describe('Component vnOrderSummary', () => { + let $componentController; + let controller; + let $httpBackend; + + beforeEach(() => { + angular.mock.module('order'); + }); + + beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_) => { + $componentController = _$componentController_; + $httpBackend = _$httpBackend_; + $httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({}); + controller = $componentController('vnOrderSummary'); + controller.order = {id: 1}; + })); + + describe('getSummary()', () => { + it('should perform a GET query and define summary property', () => { + let res = {id: 1, nickname: 'Batman'}; + $httpBackend.when('GET', `/order/api/Orders/1/summary`).respond(200, res); + $httpBackend.expect('GET', `/order/api/Orders/1/summary`); + controller.setSummary(); + $httpBackend.flush(); + + expect(controller.summary).toBeDefined(); + expect(controller.summary.nickname).toEqual('Batman'); + }); + }); + + describe('formattedAddress()', () => { + it('should return a full fromatted address with city and province', () => { + controller.summary = { + address: { + province: { + name: 'Gotham' + }, + street: '1007 Mountain Drive', + city: 'Gotham' + } + }; + + expect(controller.formattedAddress).toEqual('1007 Mountain Drive - Gotham (Gotham)'); + }); + }); + }); +}); diff --git a/client/order/src/summary/style.scss b/client/order/src/summary/style.scss new file mode 100644 index 000000000..ca61ee97c --- /dev/null +++ b/client/order/src/summary/style.scss @@ -0,0 +1,25 @@ +.ticketSummary { + .ticketSummary__data { + vn-one { + padding-right: 20px + } + + vn-one:last-child { + padding-right: 0 + } + } + + .ticketSummary__notes { + max-width: 18em + } + + .ticketSummary__taxes { + max-width: 15em; + + & section { + border: 1px solid #CCC; + text-align: right; + padding: 10px + } + } +} \ No newline at end of file diff --git a/client/ticket/src/sale/index.js b/client/ticket/src/sale/index.js index 71c17158d..fd4a7b705 100644 --- a/client/ticket/src/sale/index.js +++ b/client/ticket/src/sale/index.js @@ -189,7 +189,7 @@ class Controller { let sales = this.getCheckedLines(); this.$http.post(`/api/Sales/MoveToNewTicket`, {ticket: ticket, sales: sales}).then(res => { - let url = this.$state.href("ticket.card.sale", {id: res.data}, {absolute: true}); + let url = this.$state.href("ticket.card.sale", {id: res.data.id}, {absolute: true}); window.open(url, '_blank'); this.$scope.transfer.hide(); this.$scope.model.refresh(); diff --git a/services/claim/common/methods/claim-beginning/importToNewRefundTicket.js b/services/claim/common/methods/claim-beginning/importToNewRefundTicket.js index 16efadb8d..f2c691a6e 100644 --- a/services/claim/common/methods/claim-beginning/importToNewRefundTicket.js +++ b/services/claim/common/methods/claim-beginning/importToNewRefundTicket.js @@ -19,12 +19,12 @@ module.exports = Self => { } }); - async function addSalesToTicket(salesToRefund, newRefundTicket, transaction) { + async function addSalesToTicket(salesToRefund, ticketFk, transaction) { let formatedSales = []; salesToRefund.forEach(sale => { let formatedSale = { itemFk: sale.sale().itemFk, - ticketFk: newRefundTicket, + ticketFk: ticketFk, concept: sale.sale().concept, quantity: -Math.abs(sale.quantity), price: sale.sale().price, @@ -113,14 +113,30 @@ module.exports = Self => { try { let newRefundTicket = await models.Ticket.new(params, {transaction: transaction}); - let observation = {description: `Reclama ticket: ${claim.ticketFk}`, ticketFk: newRefundTicket, observationTypeFk: obsevationType.id}; + + let observation = { + description: `Reclama ticket: ${claim.ticketFk}`, + ticketFk: newRefundTicket.id, + observationTypeFk: obsevationType.id + }; await saveObservation(observation, {transaction: transaction}); - await models.TicketTracking.create({ticketFk: newRefundTicket, stateFk: state.id, workerFk: worker.id}, {transaction: transaction}); + + await models.TicketTracking.create({ + ticketFk: newRefundTicket.id, + stateFk: state.id, + workerFk: worker.id + }, {transaction: transaction}); + let salesToRefund = await models.ClaimBeginning.find(salesFilter); - let createdSales = await addSalesToTicket(salesToRefund, newRefundTicket, {transaction: transaction}); + let createdSales = await addSalesToTicket(salesToRefund, newRefundTicket.id, {transaction: transaction}); insertIntoClaimEnd(createdSales, id, worker.id, {transaction: transaction}); - await Self.rawSql('CALL vn.ticketCalculateClon(?, ?)', [newRefundTicket, claim.ticketFk], {transaction: transaction}); + + await Self.rawSql('CALL vn.ticketCalculateClon(?, ?)', [ + newRefundTicket.id, claim.ticketFk + ], {transaction: transaction}); + await transaction.commit(); + return newRefundTicket; } catch (e) { await transaction.rollback(); diff --git a/services/claim/common/methods/claim-beginning/importToNewRefundTicket.spec.js b/services/claim/common/methods/claim-beginning/importToNewRefundTicket.spec.js index e5ae10bf9..542f5c0e9 100644 --- a/services/claim/common/methods/claim-beginning/importToNewRefundTicket.spec.js +++ b/services/claim/common/methods/claim-beginning/importToNewRefundTicket.spec.js @@ -1,16 +1,16 @@ const app = require(`${servicesDir}/claim/server/server`); describe('claimBeginning', () => { - let ticketId; + let ticket; let refundTicketObservations; let refundTicketSales; let salesInsertedInClaimEnd; afterAll(async() => { let promises = []; - promises.push(app.models.Ticket.destroyById(ticketId)); + promises.push(app.models.Ticket.destroyById(ticket.id)); - promises.push(app.models.Ticket.rawSql(`DELETE FROM vn.orderTicket WHERE ticketFk ='${ticketId}';`)); + promises.push(app.models.Ticket.rawSql(`DELETE FROM vn.orderTicket WHERE ticketFk ='${ticket.id}';`)); await Promise.all(promises); }); @@ -19,11 +19,13 @@ describe('claimBeginning', () => { it('should create a new ticket with negative sales, save an observation, update the state and insert the negative sales into claimEnd', async() => { let ctxOfSalesAssistant = {req: {accessToken: {userId: 21}}}; let claimId = 1; - ticketId = await app.models.ClaimBeginning.importToNewRefundTicket(ctxOfSalesAssistant, claimId); - await app.models.Ticket.findById(ticketId); - refundTicketSales = await app.models.Sale.find({where: {ticketFk: ticketId}}); - refundTicketObservations = await app.models.TicketObservation.find({where: {ticketFk: ticketId}}); - let refundTicketState = await app.models.TicketState.findById(ticketId); + ticket = await app.models.ClaimBeginning.importToNewRefundTicket(ctxOfSalesAssistant, claimId); + + await app.models.Ticket.findById(ticket.id); + + refundTicketSales = await app.models.Sale.find({where: {ticketFk: ticket.id}}); + refundTicketObservations = await app.models.TicketObservation.find({where: {ticketFk: ticket.id}}); + let refundTicketState = await app.models.TicketState.findById(ticket.id); salesInsertedInClaimEnd = await app.models.ClaimEnd.find({where: {claimFk: claimId}}); expect(refundTicketSales.length).toEqual(2); diff --git a/services/claim/common/methods/claim/regularizeClaim.js b/services/claim/common/methods/claim/regularizeClaim.js index 4aedcb45c..59d8635a5 100644 --- a/services/claim/common/methods/claim/regularizeClaim.js +++ b/services/claim/common/methods/claim/regularizeClaim.js @@ -133,7 +133,7 @@ module.exports = Self => { } async function createTicket(params, transaction) { - return await Self.app.models.Ticket.new({ + let ticket = await Self.app.models.Ticket.new({ shipped: new Date(), landed: new Date(), clientFk: params.clientFk, @@ -142,6 +142,8 @@ module.exports = Self => { addressFk: params.addressFk, userId: params.userId }, {transaction: transaction}); + + return ticket.id; } async function sendMessage(ctx, params, transaction) { diff --git a/services/db/install/changes/1.2-CHECK/04-ticketCalculateClon.sql b/services/db/install/changes/1.2-CHECK/04-ticketCalculateClon.sql index 6987b126b..5db48eb69 100644 --- a/services/db/install/changes/1.2-CHECK/04-ticketCalculateClon.sql +++ b/services/db/install/changes/1.2-CHECK/04-ticketCalculateClon.sql @@ -5,7 +5,6 @@ DELIMITER $$ USE `vn`$$ CREATE DEFINER=`root`@`%` PROCEDURE `ticketCalculateClon`(IN vTicketNew INT, vTicketOld INT) BEGIN - /* * @vTicketNew id del nuevo ticket clonado * @vTicketOld id ticket original, a partir del qual se clonara el nuevo @@ -41,7 +40,7 @@ BEGIN SELECT vWarehouse warehouseFk,NULL available,s.itemFk, bu.buyFk FROM sale s LEFT JOIN tmp.buyUltimate bu ON bu.itemFk = s.itemFk - WHERE s.ticketFk = vTicketNew GROUP BY s.itemFk; + WHERE s.ticketFk = vTicketOld GROUP BY s.itemFk; CALL ticketComponentCalculate(vAddress,vAgencyMode); diff --git a/services/db/install/changes/1.2-CHECK/06.ACL.sql b/services/db/install/changes/1.2-CHECK/06.ACL.sql index b2bcb70ec..e33b1683a 100644 --- a/services/db/install/changes/1.2-CHECK/06.ACL.sql +++ b/services/db/install/changes/1.2-CHECK/06.ACL.sql @@ -6,8 +6,8 @@ VALUES (107, 'ItemNiche', '*', 'WRITE', 'ALLOW', 'ROLE', 'marketingBoss'), (108, 'ItemPlacement', '*', 'WRITE', 'ALLOW', 'ROLE', 'marketingBoss'), (109, 'UserConfig', '*', '*', 'ALLOW', 'ROLE', 'employee'), -(110, 'Bank', '*', 'READ', 'ALLOW', 'ROLE', 'employee'); - +(110, 'Bank', '*', 'READ', 'ALLOW', 'ROLE', 'employee'), +(111, 'ClientLog', '*', 'READ', 'ALLOW', 'ROLE', 'employee'); UPDATE salix.ACL SET model='ItemTag', property='*', accessType='WRITE', permission='ALLOW', principalType='ROLE', principalId='marketingBoss' diff --git a/services/db/install/changes/1.2-CHECK/09.defaulters.sql b/services/db/install/changes/1.2-CHECK/09.defaulters.sql new file mode 100644 index 000000000..ce38e8c71 --- /dev/null +++ b/services/db/install/changes/1.2-CHECK/09.defaulters.sql @@ -0,0 +1,14 @@ +USE `vn`; +CREATE + OR REPLACE ALGORITHM = UNDEFINED + DEFINER = `root`@`%` + SQL SECURITY DEFINER +VIEW `vn`.`defaulter` AS + SELECT + `d`.`client` AS `clientFk`, + `d`.`date` AS `created`, + `d`.`amount` AS `amount`, + `d`.`defaulterSince` AS `defaulterSinced`, + `d`.`hasChanged` AS `hasChanged` + FROM + `bi`.`defaulters` `d`; diff --git a/services/db/install/dump/fixtures.sql b/services/db/install/dump/fixtures.sql index 7f9009a04..34252ab3d 100644 --- a/services/db/install/dump/fixtures.sql +++ b/services/db/install/dump/fixtures.sql @@ -529,12 +529,12 @@ INSERT INTO `vn`.`ticketPackaging`(`id`, `ticketFk`, `packagingFk`, `quantity`, INSERT INTO `vn`.`sale`(`id`, `itemFk`, `ticketFk`, `concept`, `quantity`, `price`, `discount`, `reserved`, `isPicked`, `created`) VALUES - ( 1, 1, 1 , 'Gem of Time', 5, 9.10, 0, 0, 0, CURDATE()), - ( 2, 2, 1 , 'Gem of Mind', 10, 1.07, 0, 0, 0, CURDATE()), - ( 3, 1, 1 , 'Gem of Time', 2, 9.10, 0, 0, 0, CURDATE()), - ( 4, 4, 1 , 'Mark I' , 20, 3.06, 0, 0, 0, CURDATE()), - ( 5, 1, 2 , 'Gem of Time', 10, 9.10, 0, 0, 0, CURDATE()), - ( 6, 1, 3 , 'Gem of Time', 15, 6.50, 0, 0, 0, CURDATE()), + ( 1, 1, 1 , 'Gem of Time', 5, 9.10, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), + ( 2, 2, 1 , 'Gem of Mind', 10, 1.07, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), + ( 3, 1, 1 , 'Gem of Time', 2, 9.10, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), + ( 4, 4, 1 , 'Mark I' , 20, 3.06, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), + ( 5, 1, 2 , 'Gem of Time', 10, 9.10, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -10 DAY)), + ( 6, 1, 3 , 'Gem of Time', 15, 6.50, 0, 0, 0, DATE_ADD(CURDATE(), INTERVAL -5 DAY)), ( 7, 2, 11, 'Gem of Mind', 15, 1.30, 0, 0, 0, CURDATE()), ( 8, 4, 11, 'Mark I' , 10, 3.26, 0, 0, 0, CURDATE()), ( 9, 1, 16, 'Gem of Time', 5, 9.10, 0, 0, 0, CURDATE()), @@ -837,9 +837,79 @@ INSERT INTO `hedera`.`order`(`id`, `date_send`, `customer_id`, `delivery_method_ INSERT INTO `hedera`.`orderRow`(`id`, `orderFk`, `itemFk`, `warehouseFk`, `shipment`, `amount`, `price`, `rate`, `created`, `saleFk`) VALUES - ( 1, 1, 1, 1 , NULL , 9, 1.5 , 1, CURDATE(), 1), - ( 2, 2, 2, 1 , NULL , 5, 1.23, 2, CURDATE(), 2), - ( 3, 3, 3, 2 , CURDATE(), 3, 0.50, 3, CURDATE(), 3); + ( 1, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 5, 9.10, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 1), + ( 2, 1, 2, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 10, 1.07, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 2), + ( 3, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 2, 9.10, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 3), + ( 4, 1, 4, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 20, 3.06, 0, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 4), + ( 5, 2, 1, 1, DATE_ADD(CURDATE(), INTERVAL -10 DAY), 10, 9.10, 0, DATE_ADD(CURDATE(), INTERVAL -10 DAY), 5), + ( 6, 3, 1, 2, DATE_ADD(CURDATE(), INTERVAL -5 DAY), 15, 6.50, 0, DATE_ADD(CURDATE(), INTERVAL -5 DAY), 6), + ( 7, 11, 2, 1, CURDATE(), 15, 1.30, 0, CURDATE(), 7), + ( 8, 11, 4, 1, CURDATE(), 10, 3.26, 0, CURDATE(), 8), + ( 9, 16, 1, 1, CURDATE(), 5, 9.10, 0, CURDATE(), 9), + ( 10, 16, 2, 1, CURDATE(), 10, 1.07, 0, CURDATE(), 10), + ( 11, 16, 1, 1, CURDATE(), 2, 9.10, 0, CURDATE(), 11), + ( 12, 16, 4, 1, CURDATE(), 20, 3.06, 0, CURDATE(), 12); + +INSERT INTO `hedera`.`orderRowComponent`(`rowFk`, `componentFk`, `price`) + VALUES + ( 1, 15, 0.58), + ( 1, 23, 6.5), + ( 1, 28, 20.72), + ( 1, 29, -18.72), + ( 1, 39, 0.02), + ( 2, 15, 0.058), + ( 2, 21, 0.002), + ( 2, 28, 5.6), + ( 2, 29, -4.6), + ( 2, 39, 0.01), + ( 3, 15, 0.58), + ( 3, 23, 6.5), + ( 3, 28, 20.72), + ( 3, 29, -18.72), + ( 3, 39, 0.02), + ( 4, 15, 0.051), + ( 4, 21, -0.001), + ( 4, 28, 20.72), + ( 4, 29, -19.72), + ( 4, 37, 2), + ( 4, 39, 0.01), + ( 5, 15, 0.58), + ( 5, 23, 6.5), + ( 5, 28, 20.72), + ( 5, 29, -18.72), + ( 5, 39, 0.02), + ( 6, 23, 6.5), + ( 7, 15, 0.29), + ( 7, 28, 5.6), + ( 7, 29, -4.6), + ( 7, 39, 0.01), + ( 8, 15, 0.254), + ( 8, 21, -0.004), + ( 8, 28, 20.72), + ( 8, 29, -19.72), + ( 8, 37, 2), + ( 8, 39, 0.01), + ( 9, 15, 0.58), + ( 9, 23, 6.5), + ( 9, 28, 20.72), + ( 9, 29, -18.72), + ( 9, 39, 0.02), + ( 10, 15, 0.058), + ( 10, 21, 0.002), + ( 10, 28, 5.6), + ( 10, 29, -4.6), + ( 10, 39, 0.01), + ( 11, 15, 0.58), + ( 11, 23, 6.5), + ( 11, 28, 20.72), + ( 11, 29, -18.72), + ( 11, 39, 0.02), + ( 12, 15, 0.051), + ( 12, 22, -0.001), + ( 12, 28, 20.72), + ( 12, 29, -19.72), + ( 12, 37, 2), + ( 12, 39, 0.01); INSERT INTO `vn`.`clientContact`(`id`, `clientFk`, `name`, `phone`) VALUES diff --git a/services/loopback/common/locale/es.json b/services/loopback/common/locale/es.json index 1309790ef..bf4bedd4f 100644 --- a/services/loopback/common/locale/es.json +++ b/services/loopback/common/locale/es.json @@ -52,5 +52,6 @@ "Warehouse cannot be blank": "El almacén no puede quedar en blanco", "Agency cannot be blank": "La agencia no puede quedar en blanco", "You don't have enough privileges to do that": "No tienes permisos para para hacer esto", - "This address doesn't exist": "This address doesn't exist" + "This address doesn't exist": "Este consignatario no existe", + "The sales of this ticket can't be modified": "Los movimientos de este tiquet no pueden ser modificadas" } \ No newline at end of file diff --git a/services/loopback/common/methods/sale/moveToNewTicket.js b/services/loopback/common/methods/sale/moveToNewTicket.js index f69227302..a3cc4c057 100644 --- a/services/loopback/common/methods/sale/moveToNewTicket.js +++ b/services/loopback/common/methods/sale/moveToNewTicket.js @@ -51,14 +51,21 @@ module.exports = Self => { userId: userId }; - let newTicket = await model.Ticket.new(newTicketParams); + let transaction = await Self.beginTransaction({}); + try { + let newTicket = await model.Ticket.new(newTicketParams, {transaction: transaction}); - let promises = []; - for (let i = 0; i < params.sales.length; i++) { - promises.push(model.Sale.update({id: params.sales[i].id}, {ticketFk: newTicket})); + await model.Sale.updateAll( + {ticketFk: params.ticket.oldTicketFk}, + {ticketFk: newTicket.id}, + {transaction: transaction}); + + await transaction.commit(); + + return newTicket; + } catch (e) { + await transaction.rollback(); + throw e; } - await Promise.all(promises); - - return newTicket; }; }; diff --git a/services/loopback/common/methods/ticket/new.js b/services/loopback/common/methods/ticket/new.js index 890a81b0b..274224bc6 100644 --- a/services/loopback/common/methods/ticket/new.js +++ b/services/loopback/common/methods/ticket/new.js @@ -22,7 +22,12 @@ module.exports = Self => { }); Self.new = async(params, transaction) => { - let existsAddress = await Self.app.models.Address.findOne({where: {id: params.addressFk, clientFk: params.clientFk}}); + let existsAddress = await Self.app.models.Address.findOne({ + where: { + id: params.addressFk, + clientFk: params.clientFk} + }); + if (!existsAddress) throw new UserError(`This address doesn't exist`); @@ -40,6 +45,8 @@ module.exports = Self => { params.userId ], transaction); - return result[1][0].newTicketId; + return await Self.findOne({ + where: {id: result[1][0].newTicketId} + }, transaction); }; }; diff --git a/services/loopback/common/methods/ticket/specs/new.spec.js b/services/loopback/common/methods/ticket/specs/new.spec.js index be4c03eb6..cae4fa4cb 100644 --- a/services/loopback/common/methods/ticket/specs/new.spec.js +++ b/services/loopback/common/methods/ticket/specs/new.spec.js @@ -1,11 +1,11 @@ const app = require(`${servicesDir}/ticket/server/server`); describe('ticket new()', () => { - let ticketId; + let ticket; let today = new Date(); afterAll(async() => { - await app.models.Ticket.destroyById(ticketId); + await app.models.Ticket.destroyById(ticket.id); }); it('should throw an error if the address doesnt exist', async() => { @@ -33,10 +33,10 @@ describe('ticket new()', () => { landed: today }; - ticketId = await app.models.Ticket.new(params); + ticket = await app.models.Ticket.new(params); let newestTicketIdInFixtures = 21; - expect(ticketId).toBeGreaterThan(newestTicketIdInFixtures); + expect(ticket.id).toBeGreaterThan(newestTicketIdInFixtures); }); }); diff --git a/services/loopback/common/models/vn-model.js b/services/loopback/common/models/vn-model.js index 2b42d0668..bccb23a34 100644 --- a/services/loopback/common/models/vn-model.js +++ b/services/loopback/common/models/vn-model.js @@ -187,8 +187,8 @@ module.exports = function(Self) { }); }; - Self.rawStmt = function(stmt) { - return this.rawSql(stmt.sql, stmt.params); + Self.rawStmt = function(stmt, options = {}) { + return this.rawSql(stmt.sql, stmt.params, options); }; Self.escapeName = function(name) { diff --git a/services/order/common/methods/order/getTaxes.js b/services/order/common/methods/order/getTaxes.js index ff130c4ff..71bcb2da6 100644 --- a/services/order/common/methods/order/getTaxes.js +++ b/services/order/common/methods/order/getTaxes.js @@ -23,7 +23,7 @@ module.exports = Self => { let query = `CALL hedera.orderGetTax(?); SELECT * FROM tmp.orderTax;`; let res = await Self.rawSql(query, [orderFk]); - let [taxes] = res[1]; + let taxes = res[1]; return taxes; }; diff --git a/services/order/common/methods/order/getTotal.js b/services/order/common/methods/order/getTotal.js index bc2cf1471..d4808054d 100644 --- a/services/order/common/methods/order/getTotal.js +++ b/services/order/common/methods/order/getTotal.js @@ -23,6 +23,6 @@ module.exports = Self => { let query = `SELECT hedera.orderGetTotal(?) total;`; let [total] = await Self.rawSql(query, [orderFk]); - return total; + return total.total; }; }; diff --git a/services/order/common/methods/order/getVAT.js b/services/order/common/methods/order/getVAT.js new file mode 100644 index 000000000..146d1254b --- /dev/null +++ b/services/order/common/methods/order/getVAT.js @@ -0,0 +1,31 @@ +module.exports = Self => { + Self.remoteMethod('getVAT', { + description: 'Returns order total VAT', + accessType: 'READ', + accepts: [{ + arg: 'id', + type: 'number', + required: true, + description: 'order id', + http: {source: 'path'} + }], + returns: { + type: 'number', + root: true + }, + http: { + path: `/:id/getVAT`, + verb: 'GET' + } + }); + + Self.getVAT = async orderId => { + let totalTax = 0.00; + let taxes = await Self.app.models.Order.getTaxes(orderId); + taxes.forEach(tax => { + totalTax += tax.tax; + }); + + return Math.round(totalTax * 100) / 100; + }; +}; diff --git a/services/order/common/methods/order/new.js b/services/order/common/methods/order/new.js index e951b3033..f2caf2347 100644 --- a/services/order/common/methods/order/new.js +++ b/services/order/common/methods/order/new.js @@ -20,8 +20,11 @@ module.exports = Self => { }); Self.new = async params => { - let clientFkByAddress = await Self.app.models.Address.findOne({where: {id: params.addressFk}, fields: 'clientFk'}); - let clientFk = clientFkByAddress.clientFk; + let address = await Self.app.models.Address.findOne({ + where: {id: params.addressFk}, + fields: 'clientFk' + }); + let clientFk = address.clientFk; let client = await Self.app.models.Client.findOne({ where: {id: clientFk}, diff --git a/services/order/common/methods/order/specs/getTaxes.spec.js b/services/order/common/methods/order/specs/getTaxes.spec.js index 4d8b30c27..9aaacdc99 100644 --- a/services/order/common/methods/order/specs/getTaxes.spec.js +++ b/services/order/common/methods/order/specs/getTaxes.spec.js @@ -4,18 +4,19 @@ describe('order getTaxes()', () => { it('should call the getTaxes method and return undefined if its called with a string', async() => { let result = await app.models.Order.getTaxes('pepinillos'); - expect(result).toEqual(undefined); + expect(result.length).toEqual(0); }); it('should call the getTaxes method and return undefined if its called with unknown id', async() => { let result = await app.models.Order.getTaxes(99999999999999999999999); - expect(result).toEqual(undefined); + expect(result.length).toEqual(0); }); it('should call the getTaxes method and return the taxes if its called with a known id', async() => { let result = await app.models.Order.getTaxes(1); - expect(result.tax).toEqual(0.95); + expect(result[0].tax).toEqual(9.49); + expect(result.length).toEqual(1); }); }); diff --git a/services/order/common/methods/order/specs/getTotalVolume.spec.js b/services/order/common/methods/order/specs/getTotalVolume.spec.js index d974d99b3..3e41224bb 100644 --- a/services/order/common/methods/order/specs/getTotalVolume.spec.js +++ b/services/order/common/methods/order/specs/getTotalVolume.spec.js @@ -4,7 +4,7 @@ describe('order getTotalVolume()', () => { it('should return the total', async() => { let result = await app.models.Order.getTotalVolume(1); - expect(result.totalVolume).toEqual(0.072); + expect(result.totalVolume).toEqual(0.078); expect(result.totalBoxes).toBeFalsy(); }); }); diff --git a/services/order/common/methods/order/specs/getVAT.spec.js b/services/order/common/methods/order/specs/getVAT.spec.js new file mode 100644 index 000000000..a347785c7 --- /dev/null +++ b/services/order/common/methods/order/specs/getVAT.spec.js @@ -0,0 +1,17 @@ +const app = require(`${servicesDir}/order/server/server`); + +describe('order getVAT()', () => { + it('should call the getVAT method and return the response', async() => { + await app.models.Order.getVAT(1) + .then(response => { + expect(response).toEqual(9.49); + }); + }); + + it(`should call the getVAT method and return zero if doesn't have lines`, async() => { + await app.models.Order.getVAT(13) + .then(response => { + expect(response).toEqual(0); + }); + }); +}); diff --git a/services/order/common/methods/order/specs/getVolumes.spec.js b/services/order/common/methods/order/specs/getVolumes.spec.js index 6d5d4423e..496be808d 100644 --- a/services/order/common/methods/order/specs/getVolumes.spec.js +++ b/services/order/common/methods/order/specs/getVolumes.spec.js @@ -4,6 +4,6 @@ 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); + expect(result.volume).toEqual(0.04); }); }); diff --git a/services/order/common/methods/order/specs/summary.spec.js b/services/order/common/methods/order/specs/summary.spec.js new file mode 100644 index 000000000..a01680628 --- /dev/null +++ b/services/order/common/methods/order/specs/summary.spec.js @@ -0,0 +1,36 @@ +const app = require(`${servicesDir}/order/server/server`); + +describe('order summary()', () => { + it('should return a summary object containing data from 1 order', async() => { + let result = await app.models.Order.summary(1); + + expect(result.id).toEqual(1); + expect(result.clientFk).toEqual(101); + }); + + it('should return a summary object containing sales from 1 order', async() => { + let result = await app.models.Order.summary(1); + + expect(result.rows.length).toEqual(3); + }); + + it('should return a summary object containing subTotal for 1 order', async() => { + let result = await app.models.Order.summary(1); + + expect(Math.round(result.subTotal * 100) / 100).toEqual(135.60); + }); + + it('should return a summary object containing VAT for 1 order', async() => { + let result = await app.models.Order.summary(1); + + expect(Math.round(result.VAT * 100) / 100).toEqual(9.49); + }); + + it('should return a summary object containing total for 1 order', async() => { + let result = await app.models.Order.summary(1); + let total = result.subTotal + result.VAT; + let expectedTotal = Math.round(total * 100) / 100; + + expect(result.total).toEqual(expectedTotal); + }); +}); diff --git a/services/order/common/methods/order/summary.js b/services/order/common/methods/order/summary.js new file mode 100644 index 000000000..6a1cfeacb --- /dev/null +++ b/services/order/common/methods/order/summary.js @@ -0,0 +1,87 @@ +module.exports = Self => { + Self.remoteMethod('summary', { + description: 'Returns a summary for a given order id', + accessType: 'READ', + accepts: [{ + arg: 'id', + type: 'number', + required: true, + description: 'order id', + http: {source: 'path'} + }], + returns: { + type: [this.modelName], + root: true + }, + http: { + path: `/:id/summary`, + verb: 'GET' + } + }); + + Self.summary = async orderId => { + let models = Self.app.models; + let summary = await getOrderData(Self, orderId); + summary.subTotal = getSubTotal(summary.rows); + summary.VAT = await models.Order.getVAT(orderId); + summary.total = await models.Order.getTotal(orderId); + + return summary; + }; + + async function getOrderData(Self, orderId) { + let filter = { + include: [ + {relation: 'agencyMode', scope: {fields: ['name']}}, + { + relation: 'client', + scope: { + fields: ['salesPersonFk', 'name'], + include: { + relation: 'salesPerson', + fields: ['firstName', 'name'] + } + } + }, + { + relation: 'address', + scope: { + fields: ['street', 'city', 'provinceFk', 'phone', 'nickname'], + include: { + relation: 'province', + scope: { + fields: ['name'] + } + } + } + }, + { + relation: 'rows', + scope: { + include: { + relation: 'item', + scope: { + include: { + relation: 'tags' + } + } + } + } + } + ], + where: {id: orderId} + }; + + return await Self.findOne(filter); + } + + function getSubTotal(rows) { + let subTotal = 0.00; + + rows().forEach(row => { + subTotal += row.quantity * row.price; + }); + + return subTotal; + } +}; diff --git a/services/order/common/models/order.js b/services/order/common/models/order.js index cc3dd15be..4df8090e0 100644 --- a/services/order/common/models/order.js +++ b/services/order/common/models/order.js @@ -6,4 +6,6 @@ module.exports = Self => { require('../methods/order/isEditable')(Self); require('../methods/order/getTotal')(Self); require('../methods/order/catalogFilter')(Self); + require('../methods/order/summary')(Self); + require('../methods/order/getVAT')(Self); };