This commit is contained in:
Juan 2018-07-09 18:07:02 +02:00
commit 303290af53
52 changed files with 607 additions and 157 deletions

View File

@ -6,7 +6,7 @@
vn-one
label="Credit"
model="$ctrl.creditClassification.credit",
rule="CreditInsurance.credit"
rule="creditInsurance.credit"
step="1"
vn-focus>
</vn-textfield>
@ -20,7 +20,7 @@
vn-one
label="Since"
model="$ctrl.creditClassification.started"
ini-options="{dateFormat: 'd-m-Y'}">
ini-options="{enableTime: true, dateFormat: 'd-m-Y', time_24hr: true}">
</vn-date-picker>
</vn-horizontal>
</vn-card>

View File

@ -14,7 +14,7 @@ class Controller {
submit() {
if (this.$scope.form.$invalid)
return this.vnApp.showMessage(this.$translate.instant('Some fields are invalid'));
return this.vnApp.showError(this.$translate.instant('Some fields are invalid'));
let query = `/client/api/creditClassifications/createWithInsurance`;
let data = this.creditClassification;

View File

@ -20,7 +20,7 @@ describe('Client', () => {
describe('_getClassifications()', () => {
it('should perform a GET query to define the classifications property in the controller', () => {
let res = ['some classifications'];
let query = '/client/api/CreditClassifications?filter=%7B%22order%22%3A%22finished%20ASC%2C%20started%20DESC%22%2C%22include%22%3A%5B%7B%22relation%22%3A%22creditInsurances%22%2C%22scope%22%3A%7B%22fields%22%3A%5B%22id%22%2C%22credit%22%2C%22created%22%2C%22grade%22%5D%2C%22order%22%3A%22created%20DESC%22%2C%22limit%22%3A2%7D%7D%5D%2C%22where%22%3A%7B%7D%7D';
let query = '/client/api/CreditClassifications?filter=%7B%22order%22%3A%22finished%20ASC%2C%20started%20DESC%22%2C%22include%22%3A%5B%7B%22relation%22%3A%22insurances%22%2C%22scope%22%3A%7B%22fields%22%3A%5B%22id%22%2C%22credit%22%2C%22created%22%2C%22grade%22%5D%2C%22order%22%3A%22created%20DESC%22%2C%22limit%22%3A2%7D%7D%5D%2C%22where%22%3A%7B%7D%7D';
$httpBackend.whenGET(query).respond(res);
$httpBackend.expectGET(query);

View File

@ -15,7 +15,7 @@
<div><vn-label translate>To</vn-label> {{classification.finished | date:'dd/MM/yyyy'}}</div>
</vn-one>
<vn-vertical vn-one pad-medium-h>
<vn-horizontal ng-repeat="insurance in classification.creditInsurances track by insurance.id">
<vn-horizontal ng-repeat="insurance in classification.insurances track by insurance.id">
<vn-one>
<vn-label-value label="Credit"
value="{{::insurance.credit}}">

View File

@ -17,7 +17,7 @@ class Controller {
order: 'finished ASC, started DESC',
include: [
{
relation: 'creditInsurances',
relation: 'insurances',
scope: {
fields: ['id', 'credit', 'created', 'grade'],
order: 'created DESC',

View File

@ -1,4 +1,4 @@
<mg-ajax path="/client/api/CreditClassifications/{{post.params.classificationId}}/creditInsurances" options="vnPost"></mg-ajax>
<mg-ajax path="/client/api/CreditClassifications/{{post.params.classificationId}}/insurances" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.insurance"
@ -22,7 +22,7 @@
<vn-date-picker vn-one
label="Date"
model="$ctrl.insurance.created"
ini-options="{enableTime: true, dateFormat: 'd-m-Y h:i', time_24hr: true}">
ini-options="{enableTime: true, dateFormat: 'd-m-Y', time_24hr: true}">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>

View File

@ -3,7 +3,7 @@ import ngModule from '../../../module';
class Controller {
constructor($filter) {
this.insurance = {
created: $filter('date')(new Date(), 'yyyy-MM-dd HH:mm')
created: $filter('date')(new Date(), 'yyyy-MM-dd HH:mm:ss')
};
}
}

View File

@ -1,4 +1,4 @@
<mg-ajax path="/client/api/CreditClassifications/{{index.params.classificationId}}/creditInsurances" options="vnIndex"></mg-ajax>
<mg-ajax path="/client/api/CreditClassifications/{{index.params.classificationId}}/insurances" options="vnIndex"></mg-ajax>
<vn-vertical>
<vn-card pad-large>
<vn-vertical>

View File

@ -199,10 +199,17 @@
<vn-label-value label="Credit"
value="{{$ctrl.summary.credit | currency:'€ ':2}}">
</vn-label-value>
<vn-horizontal>
<vn-one>
<vn-label-value label="Secured credit"
value="{{$ctrl.summary.creditInsurance | currency:'€ ':2}}">
</vn-label-value>
</vn-one>
<vn-one title="Grade">
{{$ctrl.grade ? '&nbsp;/ ' + $ctrl.grade : '&nbsp;/ - '}}
</vn-one>
</vn-horizontal>
</vn-one>
</vn-horizontal>
</vn-vertical>
</vn-card>

View File

@ -11,8 +11,12 @@ class Controller {
return;
this.$http.get(`/client/api/Clients/${this.client.id}/summary`).then(res => {
if (res && res.data)
if (res && res.data) {
this.summary = res.data;
if (res.data.classifications.length)
this.grade = res.data.classifications[0].insurances[0].grade;
}
});
}
}

View File

@ -75,15 +75,6 @@ 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();

View File

@ -88,7 +88,7 @@ describe('Item', () => {
describe('submit()', () => {
it("should return an error message 'The barcode must be unique' when the code isnt unique", () => {
spyOn(controller.vnApp, 'showMessage').and.callThrough();
spyOn(controller.vnApp, 'showError').and.callThrough();
controller.barcodes = [
{code: 123454, itemFk: 1, id: 1},
{code: 123454, itemFk: 1}
@ -96,7 +96,7 @@ describe('Item', () => {
controller.oldBarcodes = {1: {id: 1, code: 123454, itemFk: 1}};
controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The barcode must be unique');
expect(controller.vnApp.showError).toHaveBeenCalledWith('The barcode must be unique');
});
it("should perfom a query to delete barcodes", () => {
@ -131,7 +131,7 @@ describe('Item', () => {
});
it("should return a message 'No changes to save' when there are no changes to apply", () => {
spyOn(controller.vnApp, 'showMessage').and.callThrough();
spyOn(controller.vnApp, 'showError').and.callThrough();
controller.oldBarcodes = [
{code: 1, itemFk: 1, id: 1},
{code: 2, itemFk: 1, id: 2}
@ -139,7 +139,7 @@ describe('Item', () => {
controller.barcodes = [];
controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('No changes to save');
expect(controller.vnApp.showError).toHaveBeenCalledWith('No changes to save');
});
});
});

View File

@ -86,7 +86,7 @@ export default class Controller {
}
if (repeatedBarcodes) {
return this.vnApp.showMessage(this.$translate.instant('The barcode must be unique'));
return this.vnApp.showError(this.$translate.instant('The barcode must be unique'));
}
canSubmit = barcodesObj.update.length > 0 || barcodesObj.create.length > 0 || barcodesObj.delete.length > 0;
@ -98,7 +98,7 @@ export default class Controller {
this.$scope.watcher.notifySaved();
});
}
this.vnApp.showMessage(this.$translate.instant('No changes to save'));
this.vnApp.showError(this.$translate.instant('No changes to save'));
}
setOldBarcodes(response) {

View File

@ -24,10 +24,10 @@
<vn-thead>
<vn-tr>
<vn-th>Date</vn-th>
<vn-th number>State</vn-th>
<vn-th number>Origin</vn-th>
<vn-th number>Reference</vn-th>
<vn-th field="name">Worker</vn-th>
<vn-th number>Id</vn-th>
<vn-th>State</vn-th>
<vn-th>Reference</vn-th>
<vn-th>Worker</vn-th>
<vn-th number>In</vn-th>
<vn-th number>Out</vn-th>
<vn-th number>Balance</vn-th>
@ -37,9 +37,9 @@
<vn-tr ng-class="{'warning': $ctrl.isToday(sale.date)}"
ng-repeat="sale in sales" vn-repeat-last on-last="$ctrl.scrollToActive()">
<vn-td>{{::sale.date | date:'dd/MM/yyyy HH:mm' }}</vn-td>
<vn-td number>{{::sale.alertLevel | dashIfEmpty}}</vn-td>
<vn-td number>{{::sale.origin | dashIfEmpty}}</vn-td>
<vn-td number>{{::sale.reference | dashIfEmpty}}</vn-td>
<vn-td>{{::sale.stateName | dashIfEmpty}}</vn-td>
<vn-td>{{::sale.reference | dashIfEmpty}}</vn-td>
<vn-td>{{sale.name | dashIfEmpty}}</vn-td>
<vn-td number>{{::sale.in | dashIfEmpty}}</vn-td>
<vn-td number>{{::sale.out | dashIfEmpty}}</vn-td>

View File

@ -122,11 +122,11 @@ export default class Controller {
});
if (this.$scope.form.$invalid) {
return this.vnApp.showMessage(this.$translate.instant('Some fields are invalid'));
return this.vnApp.showError(this.$translate.instant('Some fields are invalid'));
}
if (repeatedWarehouse) {
return this.vnApp.showMessage(this.$translate.instant('The niche must be unique'));
return this.vnApp.showError(this.$translate.instant('The niche must be unique'));
}
canSubmit = nichesObj.update.length > 0 || nichesObj.create.length > 0 || nichesObj.delete.length > 0;
@ -138,7 +138,7 @@ export default class Controller {
this.$scope.watcher.notifySaved();
});
}
this.vnApp.showMessage(this.$translate.instant('No changes to save'));
this.vnApp.showError(this.$translate.instant('No changes to save'));
}
}

View File

@ -98,7 +98,7 @@ describe('Item', () => {
describe('submit()', () => {
it("should return an error message 'The niche must be unique' when the niche warehouse isnt unique", () => {
controller.$scope.form = {};
spyOn(controller.vnApp, 'showMessage').and.callThrough();
spyOn(controller.vnApp, 'showError').and.callThrough();
controller.niches = [
{warehouseFk: 1, code: 123454, itemFk: 1, id: 1},
{warehouseFk: 1, code: 123454, itemFk: 1}
@ -106,7 +106,7 @@ describe('Item', () => {
controller.oldNiches = {1: {warehouseFk: 1, id: 1, code: 123454, itemFk: 1}};
controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The niche must be unique');
expect(controller.vnApp.showError).toHaveBeenCalledWith('The niche must be unique');
});
it("should perfom a query to delete niches", () => {
@ -144,7 +144,7 @@ describe('Item', () => {
it("should return a message 'No changes to save' when there are no changes to apply", () => {
controller.$scope.form = {$setPristine: () => {}};
spyOn(controller.vnApp, 'showMessage').and.callThrough();
spyOn(controller.vnApp, 'showError').and.callThrough();
controller.oldNiches = [
{warehouseFk: 1, code: 1, itemFk: 1, id: 1},
{warehouseFk: 2, code: 2, itemFk: 1, id: 2}
@ -152,7 +152,7 @@ describe('Item', () => {
controller.niches = [];
controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('No changes to save');
expect(controller.vnApp.showError).toHaveBeenCalledWith('No changes to save');
});
});
});

View File

@ -132,7 +132,7 @@ describe('Item', () => {
// TODO: Server validation should be implemented
xit("should return an error message 'The tag must be unique' when the tag value isnt unique", () => {
controller.$.form = [];
spyOn(controller.vnApp, 'showMessage').and.callThrough();
spyOn(controller.vnApp, 'showError').and.callThrough();
controller.tags = [
{typeFk: 1, value: 123454, itemFk: 1, id: 1},
{typeFk: 1, value: 123454, itemFk: 1}
@ -140,7 +140,7 @@ describe('Item', () => {
controller.orgTags = {1: {typeFk: 1, id: 1, value: 123454, itemFk: 1}};
controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The tag must be unique');
expect(controller.vnApp.showError).toHaveBeenCalledWith('The tag must be unique');
});
it("should perfom a query to delete tags", () => {

View File

@ -29,7 +29,7 @@ export default class Controller {
let url = `/item/api/Items/${this.$stateParams.id}/updateTaxes`;
this.$http.post(url, data).then(
() => this.vnApp.showMessage(this._.instant('Data saved!'))
() => this.vnApp.showSuccess(this._.instant('Data saved!'))
);
}
}

View File

@ -0,0 +1,34 @@
<vn-title>New order</vn-title>
<vn-autocomplete
vn-focus
vn-id="client"
url="/api/Clients"
label="Client"
show-field="name"
value-field="id"
field="$ctrl.clientFk">
<tpl-item>{{id}}: {{name}}</tpl-item>
</vn-autocomplete>
<vn-autocomplete
disabled="!$ctrl.clientFk"
url="{{ $ctrl.clientFk ? '/api/Clients/'+ $ctrl.clientFk +'/addresses' : null }}"
select-fields=["nickname","street","city"]
field="$ctrl.addressFk"
show-field="nickname"
value-field="id"
label="Address">
<tpl-item>{{nickname}}: {{street}}, {{city}}</tpl-item>
</vn-autocomplete>
<vn-date-picker
label="Landed"
model="$ctrl.landed"
ini-options="{enableTime: false}">
</vn-date-picker>
<vn-autocomplete
disabled="!$ctrl.clientFk || !$ctrl.landed"
data="$ctrl._avaibleAgencies"
label="Agency"
show-field="agency"
value-field="id"
field="$ctrl.ticket.agencyModeFk">
</vn-autocomplete>

View File

@ -0,0 +1,93 @@
import ngModule from '../module';
class Controller {
constructor($scope, $http, vnApp, $translate) {
this.$scope = $scope;
this.$http = $http;
this.translate = $translate;
this.vnApp = vnApp;
this.ticket = {};
}
set ticket(value) {
if (value) {
this._ticket = value;
}
}
get ticket() {
return this._ticket;
}
set clientFk(value) {
this.ticket.clientFk = value;
this.addressFk = null;
}
get clientFk() {
return this.ticket.clientFk;
}
set addressFk(value) {
this.ticket.addressFk = value;
this.getAvaibleAgencies();
}
get addressFk() {
return this.ticket.addressFk;
}
set landed(value) {
this.ticket.landed = value;
this.getAvaibleAgencies();
}
get landed() {
return this.ticket.landed;
}
get warehouseFk() {
return this.ticket.warehouseFk;
}
getAvaibleAgencies() {
this.ticket.agencyModeFk = null;
if (this.ticket.landed && this.ticket.addressFk) {
let filter = {addressFk: this.ticket.addressFk, landed: this.ticket.landed};
filter = encodeURIComponent(JSON.stringify(filter));
let query = `/api/Agencies/landsThatDay?filter=${filter}`;
this.$http.get(query).then(res => {
this._avaibleAgencies = res.data[0];
});
}
}
onSubmit() {
this.createOrder();
}
createOrder() {
let params = {
landed: this.ticket.landed,
addressFk: this.ticket.addressFk,
agencyModeFk: this.ticket.agencyModeFk
};
this.$http.post(`order/api/Orders/new`, params).then(res => {
this.vnApp.showSuccess(this.translate.instant('Data saved!'));
return res.data.id;
}).catch(e => {
this.vnApp.showError(this.translate.instant(e.data.error.message));
});
}
}
Controller.$inject = ['$scope', '$http', 'vnApp', '$translate'];
ngModule.component('vnTicketCreateCard', {
template: require('./card.html'),
controller: Controller,
bindings: {
ticket: '<?'
}
});

View File

@ -1,6 +1,6 @@
//import './card.js';
import './card.js';
xdescribe('Ticket', () => {
describe('Ticket', () => {
describe('Component vnTicketCreateCard', () => {
let $componentController;
let $scope;
@ -19,7 +19,7 @@ xdescribe('Ticket', () => {
controller.item = {id: 3};
}));
describe('set clientFk()', () => {
describe('set clientFk', () => {
it(`should set addressFk to null and clientFk to a value`, () => {
controller.clientFk = 2;
@ -28,7 +28,7 @@ xdescribe('Ticket', () => {
});
});
describe('set addressFk()', () => {
describe('set addressFk', () => {
it(`should set agencyModeFk property to null and addressFk to a value`, () => {
controller.addressFk = 101;
@ -37,25 +37,39 @@ xdescribe('Ticket', () => {
});
});
describe('set onSubmit()', () => {
it(`should call createTicket()`, () => {
spyOn(controller, 'createTicket');
describe('getAvaibleAgencies()', () => {
it(`should make a query if landed and addressFk exists`, () => {
controller.ticket.addressFk = 101;
controller.ticket.landed = 101;
let filter = {addressFk: controller.ticket.addressFk, landed: controller.ticket.landed};
filter = encodeURIComponent(JSON.stringify(filter));
$httpBackend.whenGET(`/api/Agencies/landsThatDay?filter=${filter}`).respond({data: 1});
$httpBackend.expectGET(`/api/Agencies/landsThatDay?filter=${filter}`);
controller.getAvaibleAgencies();
$httpBackend.flush();
});
});
describe('onSubmit()', () => {
it(`should call createOrder()`, () => {
spyOn(controller, 'createOrder');
controller.onSubmit();
expect(controller.createTicket).toHaveBeenCalledWith();
expect(controller.createOrder).toHaveBeenCalledWith();
});
});
describe('createTicket()', () => {
describe('createOrder()', () => {
it(`should make a query`, () => {
controller.ticket.clientFk = 101;
controller.ticket.addressFk = 101;
controller.ticket.agencyModeFk = 101;
controller.ticket.shipped = 101;
controller.ticket.landed = 101;
$httpBackend.whenPOST('order/api/Orders/new').respond({data: 'item'});
$httpBackend.whenPOST('order/api/Orders/new').respond({data: 1});
$httpBackend.expectPOST('order/api/Orders/new');
controller.createTicket();
controller.createOrder();
$httpBackend.flush();
});
});

View File

@ -1,18 +1,11 @@
<mg-ajax path="/item/api/Tickets" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.ticket"
form="form"
save="post">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" margin-medium>
<div margin-medium>
<div style="max-width: 70em; margin: 0 auto;" >
<vn-card pad-large>
<vn-title>New ticket</vn-title>
<vn-ticket-create-card vn-id="card" on-save=""></vn-ticket-create-card>
</vn-card>
<vn-button-bar>
<vn-submit label="Create"></vn-submit>
<vn-submit ng-click="$ctrl.onSubmit()" label="Save">
</vn-submit>
</vn-button-bar>
</div>
</form>
</div>

View File

@ -1,20 +1,18 @@
import ngModule from '../module';
class Controller {
constructor($scope, $state) {
constructor($scope, $http, $state) {
this.$ = $scope;
this.$http = $http;
this.$state = $state;
this.Ticket = {};
}
onSubmit() {
this.$.watcher.submit().then(
json => this.$state.go('ticket.card.data', {id: json.data.id})
);
async onSubmit() {
let newOrderID = await this.$.card.createOrder();
this.$state.go("ticket.card.summary", {id: newOrderID});
}
}
Controller.$inject = ['$scope', '$state'];
Controller.$inject = ['$scope', '$http', '$state'];
ngModule.component('vnTicketCreate', {
template: require('./index.html'),

View File

@ -15,18 +15,17 @@ describe('Ticket', () => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
$scope.card = {createTicket: () => {}};
$scope.card = {createOrder: () => {}};
$state = _$state_;
$state.go = () => {};
controller = $componentController('vnTicketCreate', {$scope: $scope, $state: $state});
}));
describe('onSubmit()', () => {
it(`should call createTicket()`, () => {
spyOn(controller.$.card, 'createTicket');
it(`should call createOrder()`, () => {
spyOn(controller.$.card, 'createOrder');
controller.onSubmit();
expect(controller.$.card.createTicket).toHaveBeenCalledWith();
expect(controller.$.card.createOrder).toHaveBeenCalledWith();
});
xit(`should call go()`, () => {

View File

@ -0,0 +1,6 @@
You can't create an order for a frozen client: No puedes crear una orden a un cliente congelado
You can't create an order for a inactive client: No puedes crear una orden a un cliente inactivo
You can't create an order for a client that doesn't has tax data verified:
No puedes crear una orden a un cliente cuyos datos fiscales no han sido verificados
You can't create an order for a client that has a debt: No puedes crear una orden a un cliente que tiene deuda
New order: Nueva orden

View File

@ -1,5 +1,4 @@
import ngModule from '../../module';
import {toJsonDate} from 'core/src/lib/date';
class Controller {
constructor($scope, $http, $translate, vnApp) {
@ -36,7 +35,7 @@ class Controller {
let query = `/ticket/api/sales/${this.ticket.id}/priceDifference`;
let data = {
landed: toJsonDate(this.ticket.landed),
landed: this.ticket.landed,
addressFk: this.ticket.addressFk,
agencyModeFk: this.ticket.agencyModeFk,
warehouseFk: this.ticket.warehouseFk

View File

@ -1,6 +1,6 @@
<vn-horizontal>
<vn-one>{{::$ctrl.sale.concept}}</vn-one>
<vn-two class="ellipsize">
<vn-two>
<section
class="inline-tag ellipsize" ng-class="{'empty': !fetchedTag.value}"
ng-repeat="fetchedTag in $ctrl.sale.item.tags track by $index"

View File

@ -1,23 +1,41 @@
@import "colors";
vn-fetched-tags {
@media screen and (max-width: 1700px){
@media screen and (max-width: 1600px){
& vn-horizontal {
flex-direction: column;
text-align: center;
& vn-two {
text-align: center;
max-width: 10.5em;
margin: 0 auto
}
.inline-tag {
font-size: 0.7em;
padding: 0.3em
}
}
}
@media screen and (max-width: 1200px){
& vn-horizontal {
.inline-tag {
font-size: 0.6em;
padding: 0.2em
}
}
}
& vn-one {
padding-top: 6px
}
& vn-two {
white-space: nowrap
}
& .inline-tag {
background-color: $secondary-font-color;
display: inline-block;

View File

@ -41,7 +41,7 @@
<th number>{{::ticket.id}}</th>
<td>{{::ticket.client.salesPerson.name | dashIfEmpty}}</td>
<td>{{::ticket.shipped | date:'dd/MM/yyyy'}}</td>
<td>{{::ticket.shipped | date:'HH:MM'}}</td>
<td>{{::ticket.shipped | date:'HH:mm'}}</td>
<td>{{::ticket.nickname}}</td>
<td>{{::ticket.address.province.name}}</td>
<td>{{::ticket.tracking.state.name}}</td>

View File

@ -106,11 +106,11 @@ class Controller {
});
if (this.$scope.form.$invalid) {
return this.vnApp.showMessage(this.$translate.instant('Some fields are invalid'));
return this.vnApp.showError(this.$translate.instant('Some fields are invalid'));
}
if (repeatedType) {
return this.vnApp.showMessage(this.$translate.instant('The observation type must be unique'));
return this.vnApp.showError(this.$translate.instant('The observation type must be unique'));
}
canSubmit = observationsObj.update.length > 0 || observationsObj.create.length > 0 || observationsObj.delete.length > 0;
@ -122,7 +122,7 @@ class Controller {
this.$scope.watcher.notifySaved();
});
}
this.vnApp.showMessage(this.$translate.instant('No changes to save'));
this.vnApp.showError(this.$translate.instant('No changes to save'));
}
$onInit() {

View File

@ -90,7 +90,7 @@ describe('ticket', () => {
it("should return an error message 'Some fields are invalid'", () => {
controller.$scope.form = {};
controller.$scope.form.$invalid = true;
spyOn(controller.vnApp, 'showMessage').and.callThrough();
spyOn(controller.vnApp, 'showError').and.callThrough();
controller.ticketObservations = [
{id: 1, observationTypeFk: 1, description: 'one', itemFk: 1},
{observationTypeFk: 1, description: 'one', itemFk: 1}
@ -98,12 +98,12 @@ describe('ticket', () => {
controller.oldObservations = {1: {id: 1, observationTypeFk: 1, description: 'one', itemFk: 1}};
controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('Some fields are invalid');
expect(controller.vnApp.showError).toHaveBeenCalledWith('Some fields are invalid');
});
it("should return an error message 'The observation type must be unique'", () => {
controller.$scope.form = {};
spyOn(controller.vnApp, 'showMessage').and.callThrough();
spyOn(controller.vnApp, 'showError').and.callThrough();
controller.ticketObservations = [
{id: 1, observationTypeFk: 1, description: 'one', itemFk: 1},
{observationTypeFk: 1, description: 'one', itemFk: 1}
@ -111,7 +111,7 @@ describe('ticket', () => {
controller.oldObservations = {1: {id: 1, observationTypeFk: 1, description: 'one', itemFk: 1}};
controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The observation type must be unique');
expect(controller.vnApp.showError).toHaveBeenCalledWith('The observation type must be unique');
});
it("should perfom a query to delete observations", () => {
@ -149,7 +149,7 @@ describe('ticket', () => {
it("should return a message 'No changes to save' when there are no changes to apply", () => {
controller.$scope.form = {$setPristine: () => {}};
spyOn(controller.vnApp, 'showMessage').and.callThrough();
spyOn(controller.vnApp, 'showError').and.callThrough();
controller.oldObservations = [
{id: 1, observationTypeFk: 1, description: 'one', showAddIcon: false},
{id: 2, observationTypeFk: 2, description: 'two', showAddIcon: true}
@ -157,7 +157,7 @@ describe('ticket', () => {
controller.ticketObservations = [];
controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('No changes to save');
expect(controller.vnApp.showError).toHaveBeenCalledWith('No changes to save');
});
});
});

View File

@ -26,10 +26,10 @@ class Controller {
});
if (this.$.form.$invalid)
return this.vnApp.showMessage(this.$translate.instant('Some fields are invalid'));
return this.vnApp.showError(this.$translate.instant('Some fields are invalid'));
if (!this.hasChanges(packagesObj))
return this.vnApp.showMessage(this.$translate.instant('No changes to save'));
return this.vnApp.showError(this.$translate.instant('No changes to save'));
this.$http.post(query, packagesObj).then(res => {
this.$.index.accept();

View File

@ -107,7 +107,7 @@
</td>
<td number>{{(sale.quantity * sale.price) - ((sale.discount * (sale.quantity * sale.price))/100) | currency:' €':2}}</td>
</tr>
<tr ng-if="index.model.count === 0" class="list list-element">
<tr ng-if="$ctrl.sales.length === 0 || !$ctrl.sales" class="list list-element">
<td colspan="8" style="text-align: center" translate>No results</td>
</tr>
<tfoot>
@ -197,7 +197,7 @@
</vn-textfield>
<div class="simulator">
<p class="simulatorTitle" translate>New price</p>
<p>{{($ctrl.sale.quantity * $ctrl.sale.price)
<p>{{($ctrl.sale.quantity * $ctrl.editedPrice)
- (($ctrl.sale.discount * ($ctrl.sale.quantity * $ctrl.editedPrice))/100)
| currency:' €':2}}</p>
<vn-button
@ -273,11 +273,18 @@
ng-click="$ctrl.moveLines($ctrl.moveToTicketFk)">
</vn-icon-button>
</vn-horizontal>
<vn-horizontal>
<vn-button
pointer
label="New ticket"
ng-click="$ctrl.linesToNewTicket()">
</vn-button>
<vn-icon
medium-grey
vn-tooltip="You have to allow pop-ups in your web browser to use this functionality"
icon="info">
</vn-icon>
</vn-horizontal>
</div>
</vn-popover>
</vn-vertical>

View File

@ -51,10 +51,8 @@ class Controller {
getVAT() {
this.$http.get(`/ticket/api/Tickets/${this.ticket.id}/getVAT`).then(res => {
if (res.data) {
this.VAT = res.data;
this.VAT = res.data || 0;
this.total = this.subTotal + this.VAT;
}
});
}
@ -117,6 +115,7 @@ class Controller {
let params = {ticketFk: this.$state.params.id, weekDay: day};
this.$http.patch(`/ticket/api/TicketWeeklies`, params).then(() => {
this.$.addTurn.hide();
this.vnApp.showSuccess(this.translate.instant('Data saved!'));
});
}
@ -204,6 +203,19 @@ class Controller {
this.$state.go("ticket.card.sale", {id: ticketID});
}
// Focus First Input
focusFirstInput(e) {
let firstFocusable = e.querySelector('input, textarea');
if (firstFocusable) {
firstFocusable.addEventListener('focus', () => {
firstFocusable.select();
});
setTimeout(() => {
firstFocusable.focus();
}, 200);
}
}
// Slesperson Mana
getManaSalespersonMana() {
this.$http.get(`/api/Tickets/${this.$state.params.id}/getSalesPersonMana`).then(res => {
@ -232,6 +244,7 @@ class Controller {
};
this.$.editPricePopover.parent = event.target;
this.$.editPricePopover.show();
this.focusFirstInput(this.$.editPricePopover.$element[0]);
}
updatePrice() {
@ -255,11 +268,13 @@ class Controller {
}];
this.$.editPopover.parent = event.target;
this.$.editPopover.show();
this.focusFirstInput(this.$.editPopover.$element[0]);
}
async showEditDialog() {
showEditDialog() {
this.edit = this.getCheckedLines();
this.$.editDialog.show();
this.focusFirstInput(this.$.editDialog.$element[0]);
}
hideEditDialog() {
@ -281,27 +296,6 @@ class Controller {
});
}
/* 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;
});
}
if (this.edit.price != this.sale.price) {
this.$http.post(`/ticket/api/Sales/updatePrice`, {id: this.edit.id, price: this.edit.price}).then(() => {
this.sale.price = this.edit.price;
});
}
if (this.edit.discount != this.sale.discount) {
this.$http.post(`/ticket/api/Sales/updateDiscount`, {id: this.edit.id, discount: this.edit.discount}).then(() => {
this.sale.discount = this.edit.discount;
});
}
this.$.edit.hide();
}*/
/**
* Unmark sale as reserved
*/

View File

@ -12,3 +12,5 @@ Edit price: Editar precio
You are going to delete lines of the ticket: Vas a borrar lineas del ticket
Continue anyway?: ¿Estás seguro?
The new quantity should be smaller than the old one: La nueva cantidad debe de ser menor que la anterior
You have to allow pop-ups in your web browser to use this functionality:
Debes permitir los pop-pups en tu navegador para que esta herramienta funcione correctamente

View File

@ -52,13 +52,13 @@ vn-ticket-sale {
}
vn-popover.transfer{
& table {
table {
min-width: 650px;
margin-bottom: 10px;
}
& i {
vn-icon:nth-child(1) {
padding-top: 0.2em;
font-size: 1.8em;
font-size: 1.7em;
}
}

View File

@ -83,6 +83,9 @@
<td number>{{::sale.discount}} %</td>
<td number>{{::sale.quantity * sale.price | currency:'€':2}}</td>
</tr>
<tr ng-if="!$ctrl.summary.sales" class="list list-element">
<td colspan="8" style="text-align: center" translate>No results</td>
</tr>
</tbody>
</table>
</vn-horizontal>

View File

@ -3,6 +3,7 @@ export * from './module';
import './search-panel';
import './index';
import './create';
import './create/card';
import './card';
import './descriptor';
import './summary';

View File

@ -64,11 +64,10 @@ describe('Ticket', () => {
.wait(selectors.ticketSales.secondSaleText)
.getInnerText(selectors.ticketSales.secondSaleText)
.then(value => {
expect(value).toContain('Yellow');
expect(value).toContain('2');
expect(value).toContain('€23.50');
expect(value).toContain('Red');
expect(value).toContain('€4.50');
expect(value).toContain('0 %');
expect(value).toContain('€47.00');
expect(value).toContain('€45.00');
});
});
});

View File

@ -33,7 +33,7 @@
"model": "Client",
"foreignKey": "client"
},
"creditInsurances": {
"insurances": {
"type": "hasMany",
"model": "CreditInsurance",
"foreignKey": "creditClassification"

View File

@ -0,0 +1,18 @@
USE `vn`;
CREATE TABLE `vn`.`alertLevel` (
`code` VARCHAR(45) CHARACTER SET 'utf8' NOT NULL,
`alertLevel` INT(11) NOT NULL,
PRIMARY KEY (`code`));
ALTER TABLE `vn`.`alertLevel`
ADD CONSTRAINT `fk_code_1`
FOREIGN KEY (`code`)
REFERENCES `vn2008`.`state` (`code`)
ON DELETE CASCADE
ON UPDATE CASCADE;
INSERT INTO `vn`.`alertLevel` (`code`, `alertLevel`) VALUES ('FREE', '0');
INSERT INTO `vn`.`alertLevel` (`code`, `alertLevel`) VALUES ('ON_PREPARATION', '1');
INSERT INTO `vn`.`alertLevel` (`code`, `alertLevel`) VALUES ('PACKED', '2');
INSERT INTO `vn`.`alertLevel` (`code`, `alertLevel`) VALUES ('DELIVERED', '3');

View File

@ -0,0 +1,105 @@
USE `vn`;
DROP procedure IF EXISTS `itemDiary`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `itemDiary`(IN vItemId INT, IN vWarehouse INT)
BEGIN
DECLARE vDateInventory DATETIME;
DECLARE vCurdate DATE DEFAULT CURDATE();
-- traduccion: date, alertLevel, origin, reference, name, In, Out, Balance
SELECT Fechainventario INTO vDateInventory FROM vn2008.tblContadores;
SET @a = 0;
SELECT DATE(date) AS date,
alertLevel,
stateName,
origin,
reference,
name,
`in`,
`out`,
@a := @a + IFNULL(`in`,0) - IFNULL(`out`,0) as balance
FROM
( SELECT tr.landed as date,
b.quantity as `in`,
NULL as `out`,
IF(tr.isReceived != FALSE,3, IF(tr.isDelivered,1,0)) as alertLevel,
st.name AS stateName,
s.name as name,
e.ref as reference,
e.id as origin
FROM vn.buy b
JOIN vn.entry e ON e.id = b.entryFk
JOIN vn.travel tr ON tr.id = e.travelFk
JOIN vn.supplier s ON s.id = e.supplierFk
JOIN vn.alertLevel al ON al.alertLevel =
CASE
WHEN tr.isReceived != FALSE THEN 3
WHEN tr.isDelivered THEN 1
ELSE 0
END
JOIN vn.state st ON st.code = al.code
WHERE tr.landed >= vDateInventory
AND vWarehouse = tr.warehouseInFk
AND b.itemFk = vItemId
AND e.isInventory = 0
UNION ALL
SELECT tr.shipped as date,
NULL as `in`,
b.quantity as `out`,
IF(tr.isReceived != FALSE,3, IF(tr.isDelivered,1,0)) as alertLevel,
st.name AS stateName,
s.name as name,
e.ref as reference,
e.id as origin
FROM vn.buy b
JOIN vn.entry e ON e.id = b.entryFk
JOIN vn.travel tr ON tr.id = e.travelFk
JOIN vn.warehouse w ON w.id = tr.warehouseOutFk
JOIN vn.supplier s ON s.id = e.supplierFk
JOIN vn.alertLevel al ON al.alertLevel =
CASE
WHEN tr.isReceived != FALSE THEN 3
WHEN tr.isDelivered THEN 1
ELSE 0
END
JOIN vn.state st ON st.code = al.code
WHERE tr.shipped >= vDateInventory
AND vWarehouse =tr.warehouseOutFk
AND s.id <> 4
AND b.itemFk = vItemId
AND e.isInventory = 0
AND w.isFeedStock = 0
UNION ALL
SELECT t.shipped as date,
NULL as `in`,
s.quantity as `out`,
IF(t.shipped < vCurdate,3,IF(t.shipped > vCurdate, 0, IFNULL(ts.alertLevel,0))) as alertLevel,
st.name AS stateName,
t.nickname as name,
t.refFk as reference,
t.id as origin
FROM vn.sale s
JOIN vn.ticket t ON t.id = s.ticketFk
LEFT JOIN vn.ticketState ts ON ts.ticket = t.id
JOIN vn.client c ON c.id = t.clientFk
JOIN vn.alertLevel al ON al.alertLevel =
CASE
WHEN t.shipped < vCurdate THEN 3
WHEN t.shipped > vCurdate THEN 0
ELSE IFNULL(ts.alertLevel, 0)
END
JOIN vn.state st ON st.code = al.code
WHERE t.shipped >= vDateInventory
AND s.itemFk = vItemId
AND vWarehouse =t.warehouseFk
) AS itemDiary
ORDER BY date, alertLevel, `in` DESC;
END$$
DELIMITER ;

View File

@ -0,0 +1,27 @@
module.exports = Self => {
Self.remoteMethod('landsThatDay', {
description: 'Returns a list of agencies that can land a shipment on a day for an address',
accessType: '',
accepts: [{
arg: 'filter',
type: 'object',
required: true,
description: 'addressFk'
}],
returns: {
type: 'object',
root: true
},
http: {
path: `/landsThatDay`,
verb: 'get'
}
});
Self.landsThatDay = async filter => {
let query = `CALL vn.agencyHourGetAgency(?, ?)`;
let result = await Self.rawSql(query, [filter.addressFk, filter.landed]);
return result;
};
};

View File

@ -47,6 +47,19 @@ module.exports = Self => {
where: {isDefaultAddress: true},
fields: ['nickname', 'street', 'city', 'postalCode']
}
},
{
relation: 'classifications',
scope: {
include: {
relation: 'insurances',
scope: {
fields: ['id', 'grade', 'created'],
order: 'created DESC'
}
},
where: {finished: null}
}
}
],
where: {id: clientId}

View File

@ -1,5 +1,5 @@
module.exports = Self => {
Self.defineScope({where: {isManaged: {neq: 0}}});
//require('../methods/agency/sendsThatDay')(Self);
require('../methods/agency/landsThatDay')(Self);
require('../methods/agency/getFirstShipped')(Self);
};

View File

@ -160,7 +160,7 @@
"model": "Greuge",
"foreignKey": "clientFk"
},
"creditClassifications": {
"classifications": {
"type": "hasMany",
"model": "CreditClassification",
"foreignKey": "client"

View File

@ -0,0 +1,55 @@
var mysql = require('mysql');
var SqlConnector = require('loopback-connector').SqlConnector;
var MySQL = require('loopback-connector-mysql').MySQL;
var EnumFactory = require('loopback-connector-mysql').EnumFactory;
exports.initialize = function(dataSource, callback) {
dataSource.driver = mysql;
dataSource.connector = new VnMySQL(dataSource.settings);
dataSource.connector.dataSource = dataSource;
var modelBuilder = dataSource.modelBuilder;
var defineType = modelBuilder.defineValueType ?
modelBuilder.defineValueType.bind(modelBuilder) :
modelBuilder.constructor.registerType.bind(modelBuilder.constructor);
defineType(function Point() {});
dataSource.EnumFactory = EnumFactory;
if (callback) {
if (dataSource.settings.lazyConnect) {
process.nextTick(function() {
callback();
});
} else {
dataSource.connector.connect(callback);
}
}
};
exports.VnMySQL = VnMySQL;
function VnMySQL(settings) {
SqlConnector.call(this, 'mysql', settings);
}
VnMySQL.prototype = Object.create(MySQL.prototype);
VnMySQL.constructor = VnMySQL;
VnMySQL.prototype.toColumnValue = function(prop, val) {
if (val == null || !prop || prop.type !== Date)
return MySQL.prototype.toColumnValue.call(this, prop, val);
return val.getFullYear() + '-' +
fillZeros(val.getMonth() + 1) + '-' +
fillZeros(val.getDate()) + ' ' +
fillZeros(val.getHours()) + ':' +
fillZeros(val.getMinutes()) + ':' +
fillZeros(val.getSeconds());
function fillZeros(v) {
return v < 10 ? '0' + v : v;
}
};

View File

@ -3,7 +3,8 @@
"connector": "memory"
},
"vn": {
"connector": "mysql",
"connector": "vn-mysql",
"timezone": "CET",
"database": "vn",
"debug": false,
"host": "${salixHost}",
@ -14,7 +15,7 @@
"acquireTimeout": 20000
},
"salix": {
"connector": "mysql",
"connector": "vn-mysql",
"database": "salix",
"debug": false,
"host": "${salixHost}",
@ -25,7 +26,7 @@
"acquireTimeout": 20000
},
"account": {
"connector": "mysql",
"connector": "vn-mysql",
"database": "account",
"debug": false,
"host": "${salixHost}",
@ -36,7 +37,7 @@
"acquireTimeout": 20000
},
"edi": {
"connector": "mysql",
"connector": "vn-mysql",
"database": "edi",
"debug": false,
"host": "${salixHost}",
@ -47,7 +48,7 @@
"acquireTimeout": 20000
},
"bs": {
"connector": "mysql",
"connector": "vn-mysql",
"database": "bs",
"debug": false,
"host": "${salixHost}",
@ -58,7 +59,7 @@
"acquireTimeout": 20000
},
"hedera": {
"connector": "mysql",
"connector": "vn-mysql",
"database": "hedera",
"debug": false,
"host": "${salixHost}",

View File

@ -1,9 +1,24 @@
let loopback = require('loopback');
let boot = require('loopback-boot');
let DataSource = require('loopback-datasource-juggler').DataSource;
let fs = require('fs-extra');
let i18n = require('i18n');
let path = require('path');
let _resolveConnector = DataSource._resolveConnector;
DataSource._resolveConnector = function(name) {
let testPath = `${__dirname}/connectors/${name}.js`;
if (fs.existsSync(testPath))
return {
connector: require(testPath),
error: null
};
return _resolveConnector.apply(this, arguments);
};
module.exports = {
loopback: loopback,
boot: vnBoot

View File

@ -3,8 +3,8 @@
"port": 3000,
"debug": false,
"defaultLanguage": "es",
"senderMail": "noreply@localhost",
"senderName": "MySender"
"senderMail": "joan@verdnatura.es",
"senderName": "VerdNatura"
},
"mysql": {
"host": "localhost",
@ -14,12 +14,12 @@
"password": "root"
},
"smtp": {
"host": "localhost",
"host": "smtp.verdnatura.es",
"port": 465,
"secure": true,
"auth": {
"user": "noreply",
"pass": ""
"user": "joan",
"pass": "CLed3ejl$"
},
"tls": {
"rejectUnauthorized": false

View File

@ -0,0 +1,51 @@
module.exports = Self => {
Self.remoteMethod('new', {
description: 'Create a new order and returns the new ID',
accessType: 'WRITE',
accepts: [{
arg: 'params',
type: 'object',
http: {source: 'body'}
}],
returns: {
type: 'number',
root: true
},
http: {
path: `/new`,
verb: 'post'
}
});
Self.new = async params => {
let cli = await Self.app.models.Address.findOne({where: {id: params.addressFk}, fields: 'clientFk'});
let client = await Self.app.models.Client.findOne({
where: {id: cli.clientFk},
fields: ['isTaxDataChecked', 'isFreezed', 'isActive']
});
if (client.isFreezed)
throw new Error(`You can't create an order for a frozen client`);
if (!client.isActive)
throw new Error(`You can't create an order for a inactive client`);
if (!client.isTaxDataChecked)
throw new Error(`You can't create an order for a client that doesn't has tax data verified`);
let query = `SELECT vn.clientGetDebt(?, CURDATE()) AS debt`;
let clientDebt = await Self.rawSql(query, [cli.clientFk]);
if (clientDebt[0].debt > 0)
throw new Error(`You can't create an order for a client that has a debt`);
query = `CALL vn.orderListCreate(?, ?, ?, ?);`;
result = await Self.rawSql(query, [
params.landed,
params.agencyModeFk,
params.addressFk,
"SALIX"
]);
return result[0].vOrderId;
};
};

View File

@ -0,0 +1,3 @@
module.exports = Self => {
require('../methods/new')(Self);
};