Merge
This commit is contained in:
commit
09a187ecfe
|
@ -111,9 +111,7 @@
|
||||||
"This phone already exists": "Este teléfono ya existe",
|
"This phone already exists": "Este teléfono ya existe",
|
||||||
"You cannot move a parent to its own sons": "No puedes mover un elemento padre a uno de sus hijos",
|
"You cannot move a parent to its own sons": "No puedes mover un elemento padre a uno de sus hijos",
|
||||||
"You can't create a claim for a removed ticket": "No puedes crear una reclamación para un ticket eliminado",
|
"You can't create a claim for a removed ticket": "No puedes crear una reclamación para un ticket eliminado",
|
||||||
"You cannot delete this ticket because is already invoiced, deleted or prepared": "No puedes eliminar este tiquet porque ya está facturado, eliminado o preparado",
|
|
||||||
"You cannot delete a ticket that part of it is being prepared": "No puedes eliminar un ticket en el que una parte que está siendo preparada",
|
"You cannot delete a ticket that part of it is being prepared": "No puedes eliminar un ticket en el que una parte que está siendo preparada",
|
||||||
"You must delete all the buy requests first": "Debes eliminar todas las peticiones de compra primero",
|
"You must delete all the buy requests first": "Debes eliminar todas las peticiones de compra primero",
|
||||||
"Has deleted the ticket id": "Ha eliminado el ticket id [#{{id}}]({{{url}}})",
|
"Has deleted the ticket id": "Ha eliminado el ticket id [#{{id}}]({{{url}}})"
|
||||||
"You cannot remove this ticket because is already invoiced, deleted or prepared": "You cannot remove this ticket because is already invoiced, deleted or prepared"
|
|
||||||
}
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('calculate', {
|
||||||
|
description: 'Calculates the price of a sale and its components',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
description: 'The sale id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
http: {source: 'path'}
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: 'Number',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/calculate`,
|
||||||
|
verb: 'post'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.calculate = async(ctx, id) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
const sale = await Self.findById(id);
|
||||||
|
const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk);
|
||||||
|
|
||||||
|
if (!isEditable)
|
||||||
|
throw new UserError(`The sales of this ticket can't be modified`);
|
||||||
|
|
||||||
|
return Self.rawSql('CALL vn.ticketCalculateSale(?)', [id]);
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
|
describe('sale calculate()', () => {
|
||||||
|
const saleId = 7;
|
||||||
|
|
||||||
|
it('should update the sale price', async() => {
|
||||||
|
const ctx = {req: {accessToken: {userId: 9}}};
|
||||||
|
const response = await app.models.Sale.calculate(ctx, saleId);
|
||||||
|
|
||||||
|
expect(response.affectedRows).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the ticket is not editable', async() => {
|
||||||
|
const ctx = {req: {accessToken: {userId: 9}}};
|
||||||
|
const immutableSaleId = 1;
|
||||||
|
await app.models.Sale.calculate(ctx, immutableSaleId)
|
||||||
|
.catch(response => {
|
||||||
|
expect(response).toEqual(new Error(`The sales of this ticket can't be modified`));
|
||||||
|
error = response;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
|
@ -27,7 +27,7 @@ module.exports = Self => {
|
||||||
const $t = ctx.req.__; // $translate
|
const $t = ctx.req.__; // $translate
|
||||||
|
|
||||||
if (!isEditable)
|
if (!isEditable)
|
||||||
throw new UserError('You cannot delete this ticket because is already invoiced, deleted or prepared');
|
throw new UserError(`The sales of this ticket can't be modified`);
|
||||||
|
|
||||||
// Check if has sales with shelving
|
// Check if has sales with shelving
|
||||||
const sales = await models.Sale.find({
|
const sales = await models.Sale.find({
|
||||||
|
|
|
@ -5,6 +5,7 @@ module.exports = Self => {
|
||||||
require('../methods/sale/updatePrice')(Self);
|
require('../methods/sale/updatePrice')(Self);
|
||||||
require('../methods/sale/updateQuantity')(Self);
|
require('../methods/sale/updateQuantity')(Self);
|
||||||
require('../methods/sale/updateConcept')(Self);
|
require('../methods/sale/updateConcept')(Self);
|
||||||
|
require('../methods/sale/calculate')(Self);
|
||||||
|
|
||||||
Self.validatesPresenceOf('concept', {
|
Self.validatesPresenceOf('concept', {
|
||||||
message: `Concept cannot be blank`
|
message: `Concept cannot be blank`
|
||||||
|
|
|
@ -11,4 +11,4 @@
|
||||||
</vn-step-control>
|
</vn-step-control>
|
||||||
</vn-button-bar>
|
</vn-button-bar>
|
||||||
<ui-view></ui-view>
|
<ui-view></ui-view>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
auto-load="true">
|
auto-load="true">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<form name="form">
|
<form name="form">
|
||||||
<vn-card class="vn-pa-lg">
|
<vn-card class="vn-w-md vn-pa-lg">
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete vn-one
|
<vn-autocomplete vn-one
|
||||||
vn-id="client"
|
vn-id="client"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<form name="form">
|
<form name="form">
|
||||||
<vn-card class="vn-pa-lg">
|
<vn-card class="vn-w-md vn-pa-lg">
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete vn-one
|
<vn-autocomplete vn-one
|
||||||
url="TicketUpdateActions"
|
url="TicketUpdateActions"
|
||||||
|
|
|
@ -13,8 +13,14 @@ class Controller {
|
||||||
this.data.registerChild(this);
|
this.data.registerChild(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
$onChanges() {
|
get ticket() {
|
||||||
this.ticket.option = 1;
|
return this._ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
set ticket(value) {
|
||||||
|
this._ticket = value;
|
||||||
|
|
||||||
|
if (value) this.ticket.option = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
onStepChange(state) {
|
onStepChange(state) {
|
||||||
|
|
|
@ -21,8 +21,7 @@ describe('ticket', () => {
|
||||||
|
|
||||||
describe('onSubmit()', () => {
|
describe('onSubmit()', () => {
|
||||||
it(`should return an error if the item doesn't have option property in the controller`, () => {
|
it(`should return an error if the item doesn't have option property in the controller`, () => {
|
||||||
controller.ticket = {};
|
controller._ticket = {id: 1};
|
||||||
|
|
||||||
controller.onSubmit();
|
controller.onSubmit();
|
||||||
|
|
||||||
expect(vnApp.showError).toHaveBeenCalledWith('Choose an option');
|
expect(vnApp.showError).toHaveBeenCalledWith('Choose an option');
|
||||||
|
|
|
@ -1,42 +1,40 @@
|
||||||
<form name="form">
|
<form name="form">
|
||||||
<vn-card class="vn-pa-lg">
|
<vn-card class="vn-w-lg vn-pa-lg">
|
||||||
<vn-horizontal>
|
<vn-table>
|
||||||
<table class="vn-table">
|
<vn-thead>
|
||||||
<thead>
|
<vn-tr>
|
||||||
<tr>
|
<vn-th number>Item</vn-th>
|
||||||
<th number translate>Item</th>
|
<vn-th style="text-align:center">Description</vn-th>
|
||||||
<th translate style="text-align:center">Description</th>
|
<vn-th number>Quantity</vn-th>
|
||||||
<th number translate>Quantity</th>
|
<vn-th number>Price (PPU)</vn-th>
|
||||||
<th number translate>Price (PPU)</th>
|
<vn-th number>New price (PPU)</vn-th>
|
||||||
<th number translate>New price (PPU)</th>
|
<vn-th number>Price difference</vn-th>
|
||||||
<th number translate>Price difference</th>
|
</vn-tr>
|
||||||
</tr>
|
</vn-thead>
|
||||||
</thead>
|
<vn-tbody>
|
||||||
<tbody>
|
<vn-tr ng-repeat="sale in $ctrl.ticket.sale.items track by sale.id">
|
||||||
<tr ng-repeat="sale in $ctrl.ticket.sale.items track by sale.id">
|
<vn-td number>{{("000000"+sale.itemFk).slice(-6)}}</vn-td>
|
||||||
<td number>{{("000000"+sale.itemFk).slice(-6)}}</td>
|
<vn-td expand>
|
||||||
<td expand>
|
<vn-fetched-tags
|
||||||
<vn-fetched-tags
|
max-length="6"
|
||||||
max-length="6"
|
item="::sale.item"
|
||||||
item="::sale.item"
|
name="::sale.concept">
|
||||||
name="::sale.concept">
|
</vn-fetched-tags>
|
||||||
</vn-fetched-tags>
|
</vn-td>
|
||||||
</td>
|
<vn-td number>{{::sale.quantity}}</vn-td>
|
||||||
<td number>{{::sale.quantity}}</td>
|
<vn-td number>{{::sale.price | currency: 'EUR': 2}}</vn-td>
|
||||||
<td number>{{::sale.price | currency: 'EUR': 2}}</td>
|
<vn-td number>{{::sale.component.newPrice | currency: 'EUR': 2}}</vn-td>
|
||||||
<td number>{{::sale.component.newPrice | currency: 'EUR': 2}}</td>
|
<vn-td number>{{::sale.component.difference | currency: 'EUR': 2}}</vn-td>
|
||||||
<td number>{{::sale.component.difference | currency: 'EUR': 2}}</td>
|
</vn-tr>
|
||||||
</tr>
|
</vn-tbody>
|
||||||
</tbody>
|
<vn-tfoot>
|
||||||
<tfoot>
|
<vn-tr>
|
||||||
<tr>
|
<vn-td colspan="3"></vn-td>
|
||||||
<td colspan="3"></td>
|
<vn-td number><strong>{{$ctrl.totalPrice | currency: 'EUR': 2}}</strong></vn-td>
|
||||||
<td number><strong>{{$ctrl.totalPrice | currency: 'EUR': 2}}</strong></td>
|
<vn-td number><strong>{{$ctrl.totalNewPrice | currency: 'EUR': 2}}</strong></vn-td>
|
||||||
<td number><strong>{{$ctrl.totalNewPrice | currency: 'EUR': 2}}</strong></td>
|
<vn-td number><strong>{{$ctrl.totalPriceDifference | currency: 'EUR': 2}}</strong></vn-td>
|
||||||
<td number><strong>{{$ctrl.totalPriceDifference | currency: 'EUR': 2}}</strong></td>
|
</vn-tr>
|
||||||
</tr>
|
</vn-tfoot>
|
||||||
</tfoot>
|
</vn-table>
|
||||||
</table>
|
|
||||||
</vn-horizontal>
|
|
||||||
</vn-card>
|
</vn-card>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -7,6 +7,17 @@ class Controller {
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
this.data.registerChild(this);
|
this.data.registerChild(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get ticket() {
|
||||||
|
return this._ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
set ticket(value) {
|
||||||
|
this._ticket = value;
|
||||||
|
|
||||||
|
if (!value) return;
|
||||||
|
|
||||||
this.getTotalPrice();
|
this.getTotalPrice();
|
||||||
this.getTotalNewPrice();
|
this.getTotalNewPrice();
|
||||||
this.getTotalDifferenceOfPrice();
|
this.getTotalDifferenceOfPrice();
|
||||||
|
|
|
@ -40,7 +40,7 @@ class Controller extends Component {
|
||||||
|
|
||||||
showChangeShipped() {
|
showChangeShipped() {
|
||||||
if (!this.isEditable) {
|
if (!this.isEditable) {
|
||||||
this.vnApp.showError(this.$translate.instant('This ticket can\'t be modified'));
|
this.vnApp.showError(this.$translate.instant(`This ticket can't be modified`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.newShipped = this.ticket.shipped;
|
this.newShipped = this.ticket.shipped;
|
||||||
|
|
|
@ -32,6 +32,11 @@ class Controller {
|
||||||
callback: this.createClaim,
|
callback: this.createClaim,
|
||||||
show: () => this.isEditable
|
show: () => this.isEditable
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Recalculate price',
|
||||||
|
callback: this.calculateSalePrice,
|
||||||
|
show: () => this.hasOneSaleSelected()
|
||||||
|
},
|
||||||
];
|
];
|
||||||
this._sales = [];
|
this._sales = [];
|
||||||
this.imagesPath = '//verdnatura.es/vn-image-data/catalog';
|
this.imagesPath = '//verdnatura.es/vn-image-data/catalog';
|
||||||
|
@ -534,6 +539,21 @@ class Controller {
|
||||||
this.isEditable = res.data;
|
this.isEditable = res.data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasOneSaleSelected() {
|
||||||
|
if (this.totalCheckedLines() === 1)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateSalePrice() {
|
||||||
|
const sale = this.checkedLines()[0];
|
||||||
|
const query = `Sales/${sale.id}/calculate`;
|
||||||
|
this.$http.post(query).then(res => {
|
||||||
|
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
|
||||||
|
this.$scope.model.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$scope', '$state', '$http', 'vnApp', '$translate'];
|
Controller.$inject = ['$scope', '$state', '$http', 'vnApp', '$translate'];
|
||||||
|
|
|
@ -29,4 +29,5 @@ SMSAvailability: >-
|
||||||
{{notAvailables}} no disponible/s. Disculpe las molestias.
|
{{notAvailables}} no disponible/s. Disculpe las molestias.
|
||||||
Continue anyway?: ¿Continuar de todas formas?
|
Continue anyway?: ¿Continuar de todas formas?
|
||||||
This ticket is now empty: El ticket ha quedado vacio
|
This ticket is now empty: El ticket ha quedado vacio
|
||||||
Do you want to delete it?: ¿Quieres borrarlo?
|
Do you want to delete it?: ¿Quieres borrarlo?
|
||||||
|
Recalculate price: Recalcular precio
|
|
@ -1,5 +1,6 @@
|
||||||
import '../index.js';
|
import '../index.js';
|
||||||
import watcher from 'core/mocks/watcher';
|
import watcher from 'core/mocks/watcher';
|
||||||
|
import crudModel from 'core/mocks/crud-model';
|
||||||
|
|
||||||
describe('Ticket', () => {
|
describe('Ticket', () => {
|
||||||
describe('Component vnTicketSale', () => {
|
describe('Component vnTicketSale', () => {
|
||||||
|
@ -40,6 +41,7 @@ describe('Ticket', () => {
|
||||||
$scope.watcher = watcher;
|
$scope.watcher = watcher;
|
||||||
$scope.sms = {open: () => {}};
|
$scope.sms = {open: () => {}};
|
||||||
$scope.ticket = ticket;
|
$scope.ticket = ticket;
|
||||||
|
$scope.model = crudModel;
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
Object.defineProperties($state.params, {
|
Object.defineProperties($state.params, {
|
||||||
id: {
|
id: {
|
||||||
|
@ -334,5 +336,27 @@ describe('Ticket', () => {
|
||||||
expect(window.open).toHaveBeenCalledWith('/somePath', '_blank');
|
expect(window.open).toHaveBeenCalledWith('/somePath', '_blank');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('hasOneSaleSelected()', () => {
|
||||||
|
it('should return true if just one sale is selected', () => {
|
||||||
|
controller.sales[0].checked = true;
|
||||||
|
|
||||||
|
expect(controller.hasOneSaleSelected()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('calculateSalePrice()', () => {
|
||||||
|
it('should make an HTTP post query ', () => {
|
||||||
|
controller.sales[0].checked = true;
|
||||||
|
|
||||||
|
$httpBackend.when('POST', `Sales/4/calculate`).respond(200);
|
||||||
|
$httpBackend.whenGET(`Tickets/1/subtotal`).respond(200, 227.5);
|
||||||
|
$httpBackend.whenGET(`Tickets/1/getVAT`).respond(200, 10.5);
|
||||||
|
$httpBackend.whenGET(`Tickets/1/isEditable`).respond();
|
||||||
|
|
||||||
|
controller.calculateSalePrice();
|
||||||
|
$httpBackend.flush();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue