8093-devToTest_2442_3 #3100
|
@ -1,46 +0,0 @@
|
|||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
const $ = {
|
||||
id: 'vn-order-summary vn-one:nth-child(1) > vn-label-value:nth-child(1) span',
|
||||
alias: 'vn-order-summary vn-one:nth-child(1) > vn-label-value:nth-child(2) span',
|
||||
consignee: 'vn-order-summary vn-one:nth-child(2) > vn-label-value:nth-child(6) span',
|
||||
subtotal: 'vn-order-summary vn-one.taxes > p:nth-child(1)',
|
||||
vat: 'vn-order-summary vn-one.taxes > p:nth-child(2)',
|
||||
total: 'vn-order-summary vn-one.taxes > p:nth-child(3)',
|
||||
sale: 'vn-order-summary vn-tbody > vn-tr',
|
||||
};
|
||||
|
||||
describe('Order summary path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('employee', 'order');
|
||||
await page.accessToSearchResult('16');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should reach the order summary section and check data', async() => {
|
||||
await page.waitForState('order.card.summary');
|
||||
|
||||
const id = await page.innerText($.id);
|
||||
const alias = await page.innerText($.alias);
|
||||
const consignee = await page.innerText($.consignee);
|
||||
const subtotal = await page.innerText($.subtotal);
|
||||
const vat = await page.innerText($.vat);
|
||||
const total = await page.innerText($.total);
|
||||
const sale = await page.countElement($.sale);
|
||||
|
||||
expect(id).toEqual('16');
|
||||
expect(alias).toEqual('Many places');
|
||||
expect(consignee).toEqual('address 26 - Gotham (Province one)');
|
||||
expect(subtotal.length).toBeGreaterThan(1);
|
||||
expect(vat.length).toBeGreaterThan(1);
|
||||
expect(total.length).toBeGreaterThan(1);
|
||||
expect(sale).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
|
@ -1,69 +0,0 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
const $ = {
|
||||
form: 'vn-order-basic-data form',
|
||||
observation: 'vn-order-basic-data form [vn-name="note"]',
|
||||
saveButton: `vn-order-basic-data form button[type=submit]`,
|
||||
acceptButton: '.vn-confirm.shown button[response="accept"]'
|
||||
};
|
||||
|
||||
describe('Order edit basic data path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
|
||||
await page.loginAndModule('employee', 'order');
|
||||
await page.accessToSearchResult('1');
|
||||
await page.accessToSection('order.card.basicData');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
describe('when confirmed order', () => {
|
||||
it('should not be able to change the client', async() => {
|
||||
const message = await page.sendForm($.form, {
|
||||
client: 'Tony Stark',
|
||||
address: 'Tony Stark',
|
||||
});
|
||||
|
||||
expect(message.text).toContain(`You can't make changes on the basic data`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when new order', () => {
|
||||
it('should create an order and edit its basic data', async() => {
|
||||
await page.waitToClick(selectors.globalItems.returnToModuleIndexButton);
|
||||
await page.waitToClick($.acceptButton);
|
||||
await page.waitForContentLoaded();
|
||||
await page.waitToClick(selectors.ordersIndex.createOrderButton);
|
||||
await page.waitForState('order.create');
|
||||
|
||||
await page.autocompleteSearch(selectors.createOrderView.client, 'Jessica Jones');
|
||||
await page.pickDate(selectors.createOrderView.landedDatePicker);
|
||||
await page.autocompleteSearch(selectors.createOrderView.agency, 'Other agency');
|
||||
await page.waitToClick(selectors.createOrderView.createButton);
|
||||
await page.waitForState('order.card.catalog');
|
||||
|
||||
await page.accessToSection('order.card.basicData');
|
||||
|
||||
const values = {
|
||||
client: 'Tony Stark',
|
||||
address: 'Tony Stark',
|
||||
agencyMode: 'Other agency'
|
||||
};
|
||||
|
||||
const message = await page.sendForm($.form, values);
|
||||
await page.reloadSection('order.card.basicData');
|
||||
const formValues = await page.fetchForm($.form, Object.keys(values));
|
||||
|
||||
expect(message.isSuccess).toBeTrue();
|
||||
expect(formValues).toEqual(values);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,48 +0,0 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Order lines', () => {
|
||||
let browser;
|
||||
let page;
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('employee', 'order');
|
||||
await page.accessToSearchResult('8');
|
||||
await page.accessToSection('order.card.line');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should check the order subtotal', async() => {
|
||||
const result = await page
|
||||
.waitToGetProperty(selectors.orderLine.orderSubtotal, 'innerText');
|
||||
|
||||
expect(result).toContain('112.30');
|
||||
});
|
||||
|
||||
it('should delete the first line in the order', async() => {
|
||||
await page.waitToClick(selectors.orderLine.firstLineDeleteButton);
|
||||
await page.waitToClick(selectors.orderLine.confirmButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it('should confirm the order subtotal has changed', async() => {
|
||||
await page.waitForTextInElement(selectors.orderLine.orderSubtotal, '92.80');
|
||||
const result = await page
|
||||
.waitToGetProperty(selectors.orderLine.orderSubtotal, 'innerText');
|
||||
|
||||
expect(result).toContain('92.80');
|
||||
});
|
||||
|
||||
it('should confirm the whole order and redirect to ticket index filtered by clientFk', async() => {
|
||||
await page.waitToClick(selectors.orderLine.confirmOrder);
|
||||
|
||||
await page.expectURL('ticket/index');
|
||||
await page.expectURL('clientFk');
|
||||
});
|
||||
});
|
|
@ -1,97 +0,0 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Order catalog', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('employee', 'order');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should open the create new order form', async() => {
|
||||
await page.waitToClick(selectors.ordersIndex.createOrderButton);
|
||||
await page.waitForState('order.create');
|
||||
});
|
||||
|
||||
it('should create a new order', async() => {
|
||||
await page.autocompleteSearch(selectors.createOrderView.client, 'Tony Stark');
|
||||
await page.pickDate(selectors.createOrderView.landedDatePicker);
|
||||
await page.autocompleteSearch(selectors.createOrderView.agency, 'Other agency');
|
||||
await page.waitToClick(selectors.createOrderView.createButton);
|
||||
await page.waitForState('order.card.catalog');
|
||||
});
|
||||
|
||||
it('should add the realm and type filters and obtain results', async() => {
|
||||
await page.waitToClick(selectors.orderCatalog.plantRealmButton);
|
||||
await page.autocompleteSearch(selectors.orderCatalog.type, 'Anthurium');
|
||||
await page.waitForNumberOfElements('section.product', 4);
|
||||
const result = await page.countElement('section.product');
|
||||
|
||||
expect(result).toEqual(4);
|
||||
});
|
||||
|
||||
it('should perfom an "OR" search for the item tag colors silver and brown', async() => {
|
||||
await page.waitToClick(selectors.orderCatalog.openTagSearch);
|
||||
await page.autocompleteSearch(selectors.orderCatalog.tag, 'Color');
|
||||
await page.autocompleteSearch(selectors.orderCatalog.firstTagAutocomplete, 'silver');
|
||||
await page.waitToClick(selectors.orderCatalog.addTagButton);
|
||||
await page.autocompleteSearch(selectors.orderCatalog.secondTagAutocomplete, 'brown');
|
||||
await page.waitToClick(selectors.orderCatalog.searchTagButton);
|
||||
await page.waitForNumberOfElements('section.product', 4);
|
||||
});
|
||||
|
||||
it('should perfom an "OR" search for the item tag tallos 2 and 9', async() => {
|
||||
await page.waitToClick(selectors.orderCatalog.openTagSearch);
|
||||
await page.autocompleteSearch(selectors.orderCatalog.tag, 'Tallos');
|
||||
await page.write(selectors.orderCatalog.firstTagValue, '2');
|
||||
await page.waitToClick(selectors.orderCatalog.addTagButton);
|
||||
await page.write(selectors.orderCatalog.secondTagValue, '9');
|
||||
await page.waitToClick(selectors.orderCatalog.searchTagButton);
|
||||
await page.waitForNumberOfElements('section.product', 2);
|
||||
});
|
||||
|
||||
it('should perform a general search for category', async() => {
|
||||
await page.write(selectors.orderCatalog.itemTagValue, 'concussion');
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitForNumberOfElements('section.product', 2);
|
||||
});
|
||||
|
||||
it('should perfom an "AND" search for the item tag tallos 2', async() => {
|
||||
await page.waitToClick(selectors.orderCatalog.openTagSearch);
|
||||
await page.autocompleteSearch(selectors.orderCatalog.tag, 'Tallos');
|
||||
await page.write(selectors.orderCatalog.firstTagValue, '2');
|
||||
await page.waitToClick(selectors.orderCatalog.searchTagButton);
|
||||
await page.waitForNumberOfElements('section.product', 1);
|
||||
});
|
||||
|
||||
it('should remove the tag filters and have 4 results', async() => {
|
||||
await page.waitForContentLoaded();
|
||||
await page.waitToClick(selectors.orderCatalog.sixthFilterRemoveButton);
|
||||
await page.waitForContentLoaded();
|
||||
await page.waitToClick(selectors.orderCatalog.fifthFilterRemoveButton);
|
||||
await page.waitForContentLoaded();
|
||||
await page.waitToClick(selectors.orderCatalog.fourthFilterRemoveButton);
|
||||
await page.waitForContentLoaded();
|
||||
await page.waitToClick(selectors.orderCatalog.thirdFilterRemoveButton);
|
||||
|
||||
await page.waitForNumberOfElements('.product', 4);
|
||||
const result = await page.countElement('section.product');
|
||||
|
||||
expect(result).toEqual(4);
|
||||
});
|
||||
|
||||
it('should search for an item by id', async() => {
|
||||
await page.accessToSearchResult('2');
|
||||
await page.waitForNumberOfElements('section.product', 1);
|
||||
const result = await page.countElement('section.product');
|
||||
|
||||
expect(result).toEqual(1);
|
||||
});
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Order Index', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('employee', 'order');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it(`should check the second search result doesn't contain a total of 0€`, async() => {
|
||||
await page.waitToClick(selectors.globalItems.searchButton);
|
||||
const result = await page.waitToGetProperty(selectors.ordersIndex.secondSearchResultTotal, 'innerText');
|
||||
|
||||
expect(result).not.toContain('0.00');
|
||||
});
|
||||
|
||||
it('should search including empty orders', async() => {
|
||||
await page.waitToClick(selectors.ordersIndex.openAdvancedSearch);
|
||||
await page.waitToClick(selectors.ordersIndex.advancedSearchShowEmptyCheckbox);
|
||||
await page.waitToClick(selectors.ordersIndex.advancedSearchButton);
|
||||
await page.waitForTextInElement(selectors.ordersIndex.secondSearchResultTotal, '0.00');
|
||||
const result = await page.waitToGetProperty(selectors.ordersIndex.secondSearchResultTotal, 'innerText');
|
||||
|
||||
expect(result).toContain('0.00');
|
||||
});
|
||||
});
|
|
@ -1,88 +0,0 @@
|
|||
<mg-ajax path="Orders/{{patch.params.id}}/updateBasicData" options="vnPatch"></mg-ajax>
|
||||
<vn-crud-model
|
||||
autoload="true"
|
||||
url="Addresses"
|
||||
data="address"
|
||||
order="nickname"
|
||||
vn-id="address-model">
|
||||
</vn-crud-model>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.order"
|
||||
form="form"
|
||||
save="patch">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="watcher.submit()" class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
url="Clients"
|
||||
label="Client"
|
||||
search-function="{or: [{id: $search}, {name: {like: '%'+ $search +'%'}}]}"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
ng-model="$ctrl.order.clientFk"
|
||||
vn-name="client"
|
||||
selection="$ctrl.selection"
|
||||
fields="['defaultAddressFk']">
|
||||
<tpl-item>
|
||||
<div>{{::name}}</div>
|
||||
<div class="text-secondary text-caption">#{{::id}}</div>
|
||||
</tpl-item>
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
fields="['id', 'nickname']"
|
||||
data="address"
|
||||
label="Address"
|
||||
search-function="$search"
|
||||
show-field="nickname"
|
||||
value-field="id"
|
||||
ng-model="$ctrl.order.addressFk"
|
||||
vn-name="address"
|
||||
on-change="$ctrl.getAvailableAgencies()">
|
||||
<tpl-item>{{::nickname}}</tpl-item>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-date-picker
|
||||
vn-one
|
||||
label="Landed"
|
||||
ng-model="$ctrl.order.landed"
|
||||
vn-name="landed"
|
||||
on-change="$ctrl.getAvailableAgencies()">
|
||||
</vn-date-picker>
|
||||
<vn-autocomplete
|
||||
disabled="!$ctrl.order.addressFk || !$ctrl.order.landed"
|
||||
data="$ctrl._availableAgencies"
|
||||
label="Agency"
|
||||
show-field="agencyMode"
|
||||
value-field="agencyModeFk"
|
||||
ng-model="$ctrl.order.agencyModeFk"
|
||||
vn-name="agencyMode">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textarea
|
||||
vn-one
|
||||
label="Notes"
|
||||
ng-model="$ctrl.order.note"
|
||||
vn-name="note"
|
||||
rule>
|
||||
</vn-textarea>
|
||||
</vn-horizontal>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit
|
||||
disabled="!watcher.dataChanged()"
|
||||
label="Save">
|
||||
</vn-submit>
|
||||
<vn-button
|
||||
class="cancel"
|
||||
label="Undo changes"
|
||||
disabled="!watcher.dataChanged()"
|
||||
ng-click="watcher.loadOriginalData()">
|
||||
</vn-button>
|
||||
</vn-button-bar>
|
||||
</form>
|
|
@ -1,57 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
import './style.scss';
|
||||
|
||||
class Controller extends Section {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
let isDirty = false;
|
||||
this.$.$watch('$ctrl.selection', newValue => {
|
||||
if (newValue) {
|
||||
this.$.addressModel.where = {clientFk: newValue.id};
|
||||
this.$.addressModel.refresh();
|
||||
if (isDirty)
|
||||
this.order.addressFk = newValue.defaultAddressFk;
|
||||
isDirty = true;
|
||||
} else {
|
||||
this.$.addressModel.clear();
|
||||
if (isDirty)
|
||||
this.order.addressFk = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
set order(value = {}) {
|
||||
this._order = value;
|
||||
|
||||
const agencyModeFk = value.agencyModeFk;
|
||||
this.getAvailableAgencies();
|
||||
this._order.agencyModeFk = agencyModeFk;
|
||||
}
|
||||
|
||||
get order() {
|
||||
return this._order;
|
||||
}
|
||||
|
||||
getAvailableAgencies() {
|
||||
const order = this.order;
|
||||
order.agencyModeFk = null;
|
||||
|
||||
const params = {
|
||||
addressFk: order.addressFk,
|
||||
landed: order.landed
|
||||
};
|
||||
if (params.landed && params.addressFk) {
|
||||
this.$http.get(`Agencies/landsThatDay`, {params})
|
||||
.then(res => this._availableAgencies = res.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnOrderBasicData', {
|
||||
controller: Controller,
|
||||
template: require('./index.html'),
|
||||
bindings: {
|
||||
order: '<'
|
||||
}
|
||||
});
|
|
@ -1,67 +0,0 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Order', () => {
|
||||
describe('Component vnOrderBasicData', () => {
|
||||
let $httpBackend;
|
||||
let $httpParamSerializer;
|
||||
let controller;
|
||||
let $scope;
|
||||
|
||||
beforeEach(ngModule('order'));
|
||||
|
||||
beforeEach(inject(($compile, _$httpBackend_, $rootScope, _$httpParamSerializer_) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpParamSerializer = _$httpParamSerializer_;
|
||||
$scope = $rootScope.$new();
|
||||
|
||||
$httpBackend.whenRoute('GET', 'Addresses')
|
||||
.respond([{id: 2, nickname: 'address 2'}]);
|
||||
$httpBackend.whenRoute('GET', 'Clients')
|
||||
.respond([{id: 1, defaultAddressFk: 1}]);
|
||||
$scope.order = {clientFk: 1, addressFk: 1};
|
||||
|
||||
let $element = $compile('<vn-order-basic-data order="order"></vn-order-basic-data>')($scope);
|
||||
$httpBackend.flush();
|
||||
controller = $element.controller('vnOrderBasicData');
|
||||
}));
|
||||
|
||||
afterAll(() => {
|
||||
$scope.$destroy();
|
||||
$element.remove();
|
||||
});
|
||||
|
||||
describe('constructor()', () => {
|
||||
it('should update the address after the client changes', async() => {
|
||||
const addressId = 999;
|
||||
const id = 444;
|
||||
|
||||
controller.selection = {id: id, defaultAddressFk: addressId};
|
||||
$scope.$digest();
|
||||
|
||||
expect(controller.order.addressFk).toEqual(addressId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAvailableAgencies()', () => {
|
||||
it('should set agencyModeFk to null and get the available agencies if the order has landed and client', async() => {
|
||||
controller.order.agencyModeFk = 999;
|
||||
controller.order.addressFk = 999;
|
||||
controller.order.landed = Date.vnNew();
|
||||
|
||||
const expectedAgencies = [{id: 1}, {id: 2}];
|
||||
|
||||
const paramsObj = {
|
||||
addressFk: controller.order.addressFk,
|
||||
landed: controller.order.landed
|
||||
};
|
||||
const serializedParams = $httpParamSerializer(paramsObj);
|
||||
$httpBackend.expect('GET', `Agencies/landsThatDay?${serializedParams}`).respond(expectedAgencies);
|
||||
controller.getAvailableAgencies();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.order.agencyModeFk).toBeDefined();
|
||||
expect(controller._availableAgencies).toEqual(expectedAgencies);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
This form has been disabled because there are lines in this order or it's confirmed: Este formulario ha sido deshabilitado por que esta orden contiene líneas o está confirmada
|
|
@ -1,9 +0,0 @@
|
|||
vn-order-basic-data {
|
||||
.disabledForm {
|
||||
text-align: center;
|
||||
color: red;
|
||||
span {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
<vn-portal slot="menu">
|
||||
<vn-order-descriptor order="$ctrl.order"></vn-order-descriptor>
|
||||
<vn-left-menu source="card"></vn-left-menu>
|
||||
</vn-portal>
|
||||
<ui-view></ui-view>
|
|
@ -1,58 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import ModuleCard from 'salix/components/module-card';
|
||||
|
||||
class Controller extends ModuleCard {
|
||||
reload() {
|
||||
let filter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'agencyMode',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
}
|
||||
}, {
|
||||
relation: 'address',
|
||||
scope: {
|
||||
fields: ['nickname']
|
||||
}
|
||||
}, {
|
||||
relation: 'rows',
|
||||
scope: {
|
||||
fields: ['id']
|
||||
}
|
||||
}, {
|
||||
relation: 'client',
|
||||
scope: {
|
||||
fields: [
|
||||
'salesPersonFk',
|
||||
'name',
|
||||
'isActive',
|
||||
'isFreezed',
|
||||
'isTaxDataChecked'
|
||||
],
|
||||
include: {
|
||||
relation: 'salesPersonUser',
|
||||
scope: {
|
||||
fields: ['id', 'name']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
return this.$q.all([
|
||||
this.$http.get(`Orders/${this.$params.id}`, {filter})
|
||||
.then(res => this.order = res.data),
|
||||
this.$http.get(`Orders/${this.$params.id}/getTotal`)
|
||||
.then(res => ({total: res.data}))
|
||||
]).then(res => {
|
||||
this.order = Object.assign.apply(null, res);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnOrderCard', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -1,31 +0,0 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Order', () => {
|
||||
describe('Component vnOrderCard', () => {
|
||||
let controller;
|
||||
let $httpBackend;
|
||||
let data = {id: 1, name: 'fooName'};
|
||||
let total = 10.5;
|
||||
|
||||
beforeEach(ngModule('order'));
|
||||
|
||||
beforeEach(inject(($componentController, _$httpBackend_, $stateParams) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
|
||||
let $element = angular.element('<div></div>');
|
||||
controller = $componentController('vnOrderCard', {$element});
|
||||
|
||||
$stateParams.id = data.id;
|
||||
$httpBackend.whenRoute('GET', 'Orders/:id').respond(data);
|
||||
$httpBackend.whenRoute('GET', 'Orders/:id/getTotal').respond(200, total);
|
||||
}));
|
||||
|
||||
it('should request data and total, merge them, and set it on the controller', () => {
|
||||
controller.reload();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.order).toEqual(Object.assign({}, data, {total}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
<div class="vn-pa-lg" style="min-width: 18em">
|
||||
<form name="form" ng-submit="$ctrl.onSearch()">
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
vn-id="tag"
|
||||
vn-one
|
||||
selection="filter.tagSelection"
|
||||
ng-model="filter.tagFk"
|
||||
data="$ctrl.resultTags"
|
||||
show-field="name"
|
||||
label="Tag"
|
||||
on-change="itemTag.value = null">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal ng-repeat="tagValue in filter.values">
|
||||
<vn-textfield
|
||||
vn-one
|
||||
ng-show="tag.selection.isFree != false"
|
||||
vn-id="text"
|
||||
label="Value"
|
||||
ng-model="tagValue.value">
|
||||
</vn-textfield>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
ng-show="tag.selection.isFree == false"
|
||||
url="{{'Tags/' + tag.selection.id + '/filterValue'}}"
|
||||
search-function="{value: $search}"
|
||||
label="Value"
|
||||
ng-model="tagValue.value"
|
||||
show-field="value"
|
||||
value-field="value">
|
||||
</vn-autocomplete>
|
||||
<vn-icon-button
|
||||
vn-none
|
||||
vn-tooltip="Remove tag"
|
||||
icon="delete"
|
||||
ng-click="filter.values.splice($index, 1)"
|
||||
tabindex="-1">
|
||||
</vn-icon-button>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-icon-button
|
||||
vn-none
|
||||
vn-bind="+"
|
||||
vn-tooltip="Add value"
|
||||
icon="add_circle"
|
||||
ng-click="$ctrl.addValue()">
|
||||
</vn-icon-button>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal class="vn-mt-lg">
|
||||
<vn-submit label="Search"></vn-submit>
|
||||
</vn-horizontal>
|
||||
</form>
|
||||
</div>
|
|
@ -1,38 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import SearchPanel from 'core/components/searchbar/search-panel';
|
||||
|
||||
class Controller extends SearchPanel {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
|
||||
this.filter = {};
|
||||
}
|
||||
|
||||
get filter() {
|
||||
return this.$.filter;
|
||||
}
|
||||
|
||||
set filter(value) {
|
||||
if (!value)
|
||||
value = {};
|
||||
if (!value.values)
|
||||
value.values = [{}];
|
||||
|
||||
this.$.filter = value;
|
||||
}
|
||||
|
||||
addValue() {
|
||||
this.filter.values.push({});
|
||||
setTimeout(() => this.parentPopover.relocate());
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnOrderCatalogSearchPanel', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
onSubmit: '&?',
|
||||
parentPopover: '<?',
|
||||
resultTags: '<?'
|
||||
}
|
||||
});
|
|
@ -1,81 +0,0 @@
|
|||
<vn-data-viewer
|
||||
model="$ctrl.model">
|
||||
<vn-horizontal class="catalog-list">
|
||||
<section ng-repeat="item in $ctrl.model.data" class="product">
|
||||
<vn-card>
|
||||
<div class="image">
|
||||
<div ng-if="::item.hex != null" class="item-color-background">
|
||||
<div class="item-color" ng-style="{'background-color': '#' + item.hex}"></div>
|
||||
</div>
|
||||
<img
|
||||
ng-src="{{::$root.imagePath('catalog', '200x200', item.id)}}"
|
||||
zoom-image="{{::$root.imagePath('catalog', '1600x900', item.id)}}"
|
||||
on-error-src/>
|
||||
</div>
|
||||
<div class="description">
|
||||
<h3 class="link"
|
||||
ng-click="itemDescriptor.show($event, item.id)">
|
||||
{{::item.name}}
|
||||
</h3>
|
||||
<h4 class="ellipsize">
|
||||
<span translate-attr="::{title: item.subName}">{{::item.subName}}</span>
|
||||
</h4>
|
||||
<div class="tags">
|
||||
<vn-label-value
|
||||
ng-if="::item.value5"
|
||||
label="{{::item.tag5}}"
|
||||
value="{{::item.value5}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
ng-if="::item.value6"
|
||||
label="{{::item.tag6}}"
|
||||
value="{{::item.value6}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
ng-if="::item.value7"
|
||||
label="{{::item.tag7}}"
|
||||
value="{{::item.value7}}">
|
||||
</vn-label-value>
|
||||
</div>
|
||||
<vn-horizontal
|
||||
class="text-right text-caption alert vn-mr-xs"
|
||||
ng-if="::item.minQuantity">
|
||||
<vn-one>
|
||||
<vn-icon
|
||||
icon="production_quantity_limits"
|
||||
translate-attr="{title: 'Minimal quantity'}"
|
||||
class="text-subtitle1">
|
||||
</vn-icon>
|
||||
</vn-one>
|
||||
{{::item.minQuantity}}
|
||||
</vn-horizontal>
|
||||
<div class="footer">
|
||||
<div class="price">
|
||||
<vn-one>
|
||||
<span>{{::item.available}}</span>
|
||||
<span translate>to</span>
|
||||
<span>{{::item.price | currency:'EUR':2}}</span>
|
||||
</vn-one>
|
||||
<vn-icon-button vn-none
|
||||
icon="add_circle"
|
||||
ng-click="pricesPopover.show($event, item)"
|
||||
vn-tooltip="Add">
|
||||
</vn-icon-button>
|
||||
</div>
|
||||
<div class="priceKg" ng-show="::item.priceKg">
|
||||
<span>Precio por kilo {{::item.priceKg | currency: 'EUR'}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</vn-card>
|
||||
</section>
|
||||
</vn-horizontal>
|
||||
</vn-data-viewer>
|
||||
<vn-order-prices-popover
|
||||
vn-id="prices-popover"
|
||||
order="$ctrl.order">
|
||||
</vn-order-prices-popover>
|
||||
<vn-item-descriptor-popover
|
||||
vn-id="item-descriptor"
|
||||
warehouse-fk="$ctrl.vnConfig.warehouseFk">
|
||||
</vn-item-descriptor-popover>
|
|
@ -1,12 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Component from 'core/lib/component';
|
||||
import './style.scss';
|
||||
|
||||
ngModule.vnComponent('vnOrderCatalogView', {
|
||||
template: require('./index.html'),
|
||||
controller: Component,
|
||||
bindings: {
|
||||
order: '<',
|
||||
model: '<'
|
||||
}
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
Order created: Orden creada
|
|
@ -1,50 +0,0 @@
|
|||
@import "variables";
|
||||
|
||||
vn-order-catalog {
|
||||
.catalog-header {
|
||||
border-bottom: $border-thin;
|
||||
padding: $spacing-md;
|
||||
align-items: center;
|
||||
|
||||
& > vn-one {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
|
||||
span {
|
||||
color: $color-font-secondary
|
||||
}
|
||||
}
|
||||
& > vn-auto {
|
||||
width: 448px;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
|
||||
& > * {
|
||||
padding-left: $spacing-md;
|
||||
}
|
||||
}
|
||||
}
|
||||
.catalog-list {
|
||||
padding-top: $spacing-sm;
|
||||
}
|
||||
.item-color-background {
|
||||
background: linear-gradient($color-bg-panel, $color-main);
|
||||
border-radius: 50%;
|
||||
margin-left: 140px;
|
||||
margin-top: 140px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
position: absolute;
|
||||
}
|
||||
.item-color {
|
||||
margin: auto;
|
||||
margin-top: 5px;
|
||||
border-radius: 50%;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
position: relative;
|
||||
}
|
||||
.alert {
|
||||
color: $color-alert;
|
||||
}
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
<vn-crud-model
|
||||
url="ItemCategories"
|
||||
data="categories"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Orders/CatalogFilter"
|
||||
params="{orderFk: $ctrl.$params.id}"
|
||||
limit="50"
|
||||
data="$ctrl.items">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar vn-id="searchbar"
|
||||
auto-state="false"
|
||||
info="Search by item id or name"
|
||||
on-search="$ctrl.onSearch($params)">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-order-catalog-view
|
||||
model="model"
|
||||
order="$ctrl.order">
|
||||
</vn-order-catalog-view>
|
||||
<vn-side-menu side="right">
|
||||
<vn-horizontal class="item-category">
|
||||
<vn-autocomplete vn-id="category"
|
||||
data="categories"
|
||||
ng-model="$ctrl.categoryId"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Category">
|
||||
</vn-autocomplete>
|
||||
<vn-one ng-repeat="category in categories">
|
||||
<vn-icon
|
||||
ng-class="{'active': $ctrl.categoryId == category.id}"
|
||||
icon="{{::category.icon}}"
|
||||
vn-tooltip="{{::category.name}}"
|
||||
ng-click="$ctrl.changeCategory(category.id)">
|
||||
</vn-icon>
|
||||
</vn-one>
|
||||
</vn-horizontal>
|
||||
<vn-vertical class="input">
|
||||
<vn-autocomplete vn-id="type"
|
||||
data="$ctrl.itemTypes"
|
||||
ng-model="$ctrl.typeId"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Type"
|
||||
fields="['categoryFk']"
|
||||
include="'category'">
|
||||
<tpl-item>
|
||||
<div>{{name}}</div>
|
||||
<div class="text-caption text-secondary">
|
||||
{{categoryName}}
|
||||
</div>
|
||||
</tpl-item>
|
||||
</vn-autocomplete>
|
||||
</vn-vertical>
|
||||
<vn-vertical class="input vn-pt-md">
|
||||
<vn-autocomplete
|
||||
vn-id="field"
|
||||
data="$ctrl.orderFields"
|
||||
ng-model="$ctrl.orderField"
|
||||
selection="$ctrl.orderSelection"
|
||||
translate-fields="['name']"
|
||||
order="priority DESC"
|
||||
show-field="name"
|
||||
value-field="field"
|
||||
label="Order by"
|
||||
disabled="!model.data">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
data="$ctrl.orderWays"
|
||||
ng-model="$ctrl.orderWay"
|
||||
translate-fields="['name']"
|
||||
show-field="name"
|
||||
value-field="way"
|
||||
label="Order"
|
||||
disabled="!model.data">
|
||||
</vn-autocomplete>
|
||||
<div ng-if="false && model.moreRows">
|
||||
<span translate>More than</span> {{model.limit}} <span translate>results</span>
|
||||
</div>
|
||||
</vn-vertical>
|
||||
<vn-vertical class="input vn-pt-md">
|
||||
<vn-textfield vn-one
|
||||
vn-id="search"
|
||||
ng-keyUp="$ctrl.onSearchByTag($event)"
|
||||
label="Search tag">
|
||||
<prepend>
|
||||
<vn-icon icon="search"></vn-icon>
|
||||
</prepend>
|
||||
<append>
|
||||
<vn-icon
|
||||
icon="keyboard_arrow_down"
|
||||
ng-click="$ctrl.openPanel($event)"
|
||||
style="cursor: pointer;">
|
||||
</vn-icon>
|
||||
</append>
|
||||
</vn-textfield>
|
||||
</vn-vertical>
|
||||
<vn-popover
|
||||
vn-id="popover"
|
||||
on-close="$ctrl.onPopoverClose()">
|
||||
<vn-order-catalog-search-panel
|
||||
on-submit="$ctrl.onPanelSubmit($filter)"
|
||||
parent-popover="popover"
|
||||
result-tags="$ctrl.resultTags">
|
||||
</vn-order-catalog-search-panel>
|
||||
</vn-popover>
|
||||
<div class="chips">
|
||||
<vn-chip
|
||||
ng-if="$ctrl.itemId"
|
||||
removable="true"
|
||||
vn-tooltip="Item id"
|
||||
on-remove="$ctrl.removeItemId()"
|
||||
class="colored">
|
||||
<span>Id: {{$ctrl.itemId}}</span>
|
||||
</vn-chip>
|
||||
<vn-chip
|
||||
ng-if="$ctrl.itemName"
|
||||
removable="true"
|
||||
vn-tooltip="Item"
|
||||
on-remove="$ctrl.removeItemName()"
|
||||
class="colored">
|
||||
<div>
|
||||
<span>
|
||||
<span translate>Name</span>:
|
||||
</span>
|
||||
<span>{{$ctrl.itemName}}</span>
|
||||
</div>
|
||||
</vn-chip>
|
||||
<vn-chip
|
||||
ng-if="category.selection"
|
||||
removable="true"
|
||||
vn-tooltip="Category"
|
||||
on-remove="$ctrl.categoryId = null"
|
||||
class="colored">
|
||||
<span translate>{{category.selection.name}}</span>
|
||||
</vn-chip>
|
||||
<vn-chip
|
||||
ng-if="type.selection"
|
||||
removable="true"
|
||||
vn-tooltip="Type"
|
||||
on-remove="$ctrl.typeId = null"
|
||||
class="colored">
|
||||
<span translate>{{type.selection.name}}</span>
|
||||
</vn-chip>
|
||||
<vn-chip
|
||||
ng-repeat="tagGroup in $ctrl.tagGroups"
|
||||
removable="true"
|
||||
on-remove="$ctrl.remove($index)"
|
||||
vn-tooltip="{{::$ctrl.formatTooltip(tagGroup)}}"
|
||||
class="colored">
|
||||
<div>
|
||||
<span ng-if="::tagGroup.tagFk">
|
||||
<span translate>{{::tagGroup.tagSelection.name}}</span>:
|
||||
</span>
|
||||
<span ng-repeat="tagValue in tagGroup.values">
|
||||
<span ng-if="$index > 0">,</span>
|
||||
<span>"{{::tagValue.value}}"</span>
|
||||
</span>
|
||||
</div>
|
||||
</vn-chip>
|
||||
</div>
|
||||
</vn-side-menu>
|
|
@ -1,377 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
import './style.scss';
|
||||
|
||||
class Controller extends Section {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
this.itemTypes = [];
|
||||
this._tagGroups = [];
|
||||
|
||||
// Static autocomplete data
|
||||
this.orderWays = [
|
||||
{way: 'ASC', name: 'Ascendant'},
|
||||
{way: 'DESC', name: 'Descendant'},
|
||||
];
|
||||
this.defaultOrderFields = [
|
||||
{field: 'relevancy DESC, name', name: 'Relevancy', priority: 999},
|
||||
{field: 'showOrder, price', name: 'Color and price', priority: 999},
|
||||
{field: 'name', name: 'Name', priority: 999},
|
||||
{field: 'price', name: 'Price', priority: 999}
|
||||
];
|
||||
this.orderFields = [].concat(this.defaultOrderFields);
|
||||
this._orderWay = this.orderWays[0].way;
|
||||
this.orderField = this.orderFields[0].field;
|
||||
}
|
||||
|
||||
$onChanges() {
|
||||
this.getData().then(() => {
|
||||
if (this.order && this.order.isConfirmed)
|
||||
this.$state.go('order.card.line');
|
||||
});
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.$http.get(`Orders/${this.$params.id}`)
|
||||
.then(res => this.order = res.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills order autocomplete with tags
|
||||
* obtained from last filtered
|
||||
*/
|
||||
get order() {
|
||||
return this._order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets filter values from state params
|
||||
*
|
||||
* @param {Object} value - Order data
|
||||
*/
|
||||
set order(value) {
|
||||
this._order = value;
|
||||
|
||||
if (!value) return;
|
||||
|
||||
this.$.$applyAsync(() => {
|
||||
if (this.$params.categoryId)
|
||||
this.categoryId = parseInt(this.$params.categoryId);
|
||||
|
||||
if (this.$params.typeId)
|
||||
this.typeId = parseInt(this.$params.typeId);
|
||||
|
||||
if (this.$params.tagGroups)
|
||||
this.tagGroups = JSON.parse(this.$params.tagGroups);
|
||||
});
|
||||
}
|
||||
|
||||
get items() {
|
||||
return this._items;
|
||||
}
|
||||
|
||||
set items(value) {
|
||||
this._items = value;
|
||||
|
||||
if (!value) return;
|
||||
|
||||
this.fetchResultTags(value);
|
||||
this.buildOrderFilter();
|
||||
}
|
||||
|
||||
get categoryId() {
|
||||
return this._categoryId;
|
||||
}
|
||||
|
||||
set categoryId(value = null) {
|
||||
this._categoryId = value;
|
||||
this.itemTypes = [];
|
||||
this.typeId = null;
|
||||
|
||||
this.updateStateParams();
|
||||
|
||||
if (this.tagGroups.length > 0)
|
||||
this.applyFilters();
|
||||
|
||||
if (value)
|
||||
this.updateItemTypes();
|
||||
}
|
||||
|
||||
changeCategory(id) {
|
||||
if (this._categoryId == id) id = null;
|
||||
this.categoryId = id;
|
||||
}
|
||||
|
||||
get typeId() {
|
||||
return this._typeId;
|
||||
}
|
||||
|
||||
set typeId(value) {
|
||||
this._typeId = value;
|
||||
|
||||
this.updateStateParams();
|
||||
|
||||
if (value || this.tagGroups.length > 0)
|
||||
this.applyFilters();
|
||||
}
|
||||
|
||||
get tagGroups() {
|
||||
return this._tagGroups;
|
||||
}
|
||||
|
||||
set tagGroups(value) {
|
||||
this._tagGroups = value;
|
||||
|
||||
this.updateStateParams();
|
||||
|
||||
if (value.length)
|
||||
this.applyFilters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get order way ASC/DESC
|
||||
*/
|
||||
get orderWay() {
|
||||
return this._orderWay;
|
||||
}
|
||||
|
||||
set orderWay(value) {
|
||||
this._orderWay = value;
|
||||
if (value) this.applyOrder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the order way selection
|
||||
*/
|
||||
get orderSelection() {
|
||||
return this._orderSelection;
|
||||
}
|
||||
|
||||
set orderSelection(value) {
|
||||
this._orderSelection = value;
|
||||
|
||||
if (value) this.applyOrder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply order to model
|
||||
*/
|
||||
applyOrder() {
|
||||
if (this.typeId || this.tagGroups.length > 0 || this.itemName)
|
||||
this.$.model.addFilter(null, {orderBy: this.getOrderBy()});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns order param
|
||||
*
|
||||
* @return {Object} - Order param
|
||||
*/
|
||||
getOrderBy() {
|
||||
const isTag = !!(this.orderSelection && this.orderSelection.isTag);
|
||||
return {
|
||||
field: this.orderField,
|
||||
way: this.orderWay,
|
||||
isTag: isTag
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes item type dropdown data
|
||||
*/
|
||||
updateItemTypes() {
|
||||
let params = {
|
||||
itemCategoryId: this.categoryId
|
||||
};
|
||||
|
||||
const query = `Orders/${this.order.id}/getItemTypeAvailable`;
|
||||
this.$http.get(query, {params}).then(res =>
|
||||
this.itemTypes = res.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search by tag value
|
||||
* @param {object} event
|
||||
*/
|
||||
onSearchByTag(event) {
|
||||
const value = this.$.search.value;
|
||||
if (event.key !== 'Enter' || !value) return;
|
||||
this.tagGroups.push({values: [{value: value}]});
|
||||
this.$.search.value = null;
|
||||
this.updateStateParams();
|
||||
this.applyFilters();
|
||||
}
|
||||
|
||||
remove(index) {
|
||||
this.tagGroups.splice(index, 1);
|
||||
this.updateStateParams();
|
||||
|
||||
if (this.tagGroups.length >= 0 || this.itemId || this.typeId)
|
||||
this.applyFilters();
|
||||
}
|
||||
|
||||
removeItemId() {
|
||||
this.itemId = null;
|
||||
this.$.searchbar.doSearch({}, 'bar');
|
||||
}
|
||||
|
||||
removeItemName() {
|
||||
this.itemName = null;
|
||||
this.$.searchbar.doSearch({}, 'bar');
|
||||
}
|
||||
|
||||
applyFilters(filter = {}) {
|
||||
let newParams = {};
|
||||
let newFilter = Object.assign({}, filter);
|
||||
const model = this.$.model;
|
||||
|
||||
if (this.categoryId)
|
||||
newFilter.categoryFk = this.categoryId;
|
||||
|
||||
if (this.typeId)
|
||||
newFilter.typeFk = this.typeId;
|
||||
|
||||
newParams = {
|
||||
orderFk: this.$params.id,
|
||||
orderBy: this.getOrderBy(),
|
||||
tagGroups: this.tagGroups,
|
||||
};
|
||||
|
||||
return model.applyFilter({where: newFilter}, newParams);
|
||||
}
|
||||
|
||||
openPanel(event) {
|
||||
if (event.defaultPrevented) return;
|
||||
event.preventDefault();
|
||||
|
||||
this.panelFilter = {};
|
||||
this.$.popover.show(this.$.search.element);
|
||||
}
|
||||
|
||||
onPanelSubmit(filter) {
|
||||
this.$.popover.hide();
|
||||
const values = filter.values;
|
||||
const nonEmptyValues = values.filter(tagValue => {
|
||||
return tagValue.value;
|
||||
});
|
||||
|
||||
filter.values = nonEmptyValues;
|
||||
|
||||
if (filter.tagFk && nonEmptyValues.length) {
|
||||
this.tagGroups.push(filter);
|
||||
this.updateStateParams();
|
||||
this.applyFilters();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates url state params from filter values
|
||||
*/
|
||||
updateStateParams() {
|
||||
const params = {};
|
||||
|
||||
params.categoryId = undefined;
|
||||
if (this.categoryId)
|
||||
params.categoryId = this.categoryId;
|
||||
|
||||
params.typeId = undefined;
|
||||
if (this.typeId)
|
||||
params.typeId = this.typeId;
|
||||
|
||||
params.tagGroups = undefined;
|
||||
if (this.tagGroups && this.tagGroups.length)
|
||||
params.tagGroups = JSON.stringify(this.sanitizedTagGroupParam());
|
||||
|
||||
this.$state.go(this.$state.current.name, params);
|
||||
}
|
||||
|
||||
sanitizedTagGroupParam() {
|
||||
const tagGroups = [];
|
||||
for (let tagGroup of this.tagGroups) {
|
||||
const tagParam = {values: []};
|
||||
|
||||
for (let tagValue of tagGroup.values)
|
||||
tagParam.values.push({value: tagValue.value});
|
||||
|
||||
if (tagGroup.tagFk)
|
||||
tagParam.tagFk = tagGroup.tagFk;
|
||||
|
||||
if (tagGroup.tagSelection) {
|
||||
tagParam.tagSelection = {
|
||||
name: tagGroup.tagSelection.name
|
||||
};
|
||||
}
|
||||
|
||||
tagGroups.push(tagParam);
|
||||
}
|
||||
|
||||
return tagGroups;
|
||||
}
|
||||
|
||||
fetchResultTags(items) {
|
||||
const resultTags = [];
|
||||
for (let item of items) {
|
||||
for (let itemTag of item.tags) {
|
||||
const alreadyAdded = resultTags.findIndex(tag => {
|
||||
return tag.tagFk == itemTag.tagFk;
|
||||
});
|
||||
|
||||
if (alreadyAdded == -1)
|
||||
resultTags.push({...itemTag, priority: 1});
|
||||
else
|
||||
resultTags[alreadyAdded].priority += 1;
|
||||
}
|
||||
}
|
||||
this.resultTags = resultTags;
|
||||
}
|
||||
|
||||
buildOrderFilter() {
|
||||
const filter = [].concat(this.defaultOrderFields);
|
||||
for (let tag of this.resultTags)
|
||||
filter.push({...tag, field: tag.id, isTag: true});
|
||||
|
||||
this.orderFields = filter;
|
||||
}
|
||||
|
||||
onSearch(params) {
|
||||
if (!params) return;
|
||||
|
||||
this.itemId = null;
|
||||
this.itemName = null;
|
||||
|
||||
if (params.search) {
|
||||
if (/^\d+$/.test(params.search)) {
|
||||
this.itemId = params.search;
|
||||
return this.applyFilters({
|
||||
'i.id': params.search
|
||||
});
|
||||
} else {
|
||||
this.itemName = params.search;
|
||||
return this.applyFilters({
|
||||
'i.name': {like: `%${params.search}%`}
|
||||
});
|
||||
}
|
||||
} else return this.applyFilters();
|
||||
}
|
||||
|
||||
formatTooltip(tagGroup) {
|
||||
const tagValues = tagGroup.values;
|
||||
|
||||
let title = '';
|
||||
if (tagGroup.tagFk) {
|
||||
const tagName = tagGroup.tagSelection.name;
|
||||
title += `${tagName}: `;
|
||||
}
|
||||
|
||||
for (let [i, tagValue] of tagValues.entries()) {
|
||||
if (i > 0) title += ', ';
|
||||
title += `"${tagValue.value}"`;
|
||||
}
|
||||
|
||||
return `${title}`;
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnOrderCatalog', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -1,386 +0,0 @@
|
|||
import './index.js';
|
||||
import crudModel from 'core/mocks/crud-model';
|
||||
|
||||
describe('Order', () => {
|
||||
describe('Component vnOrderCatalog', () => {
|
||||
let $scope;
|
||||
let $state;
|
||||
let controller;
|
||||
let $httpBackend;
|
||||
|
||||
beforeEach(ngModule('order'));
|
||||
|
||||
beforeEach(inject(($componentController, _$state_, _$httpBackend_, $rootScope) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$scope = $rootScope.$new();
|
||||
$scope.model = crudModel;
|
||||
$scope.search = {};
|
||||
$scope.itemId = {};
|
||||
$state = _$state_;
|
||||
$state.current.name = 'my.current.state';
|
||||
const $element = angular.element('<vn-order-catalog></vn-order-catalog>');
|
||||
controller = $componentController('vnOrderCatalog', {$element, $scope});
|
||||
controller._order = {id: 4};
|
||||
controller.$params = {
|
||||
categoryId: 1,
|
||||
typeId: 2,
|
||||
id: 4
|
||||
};
|
||||
}));
|
||||
|
||||
describe('getData()', () => {
|
||||
it(`should make a query an fetch the order data`, () => {
|
||||
controller._order = null;
|
||||
|
||||
$httpBackend.expect('GET', `Orders/4`).respond(200, {id: 4, isConfirmed: true});
|
||||
$httpBackend.expect('GET', `Orders/4/getItemTypeAvailable?itemCategoryId=1`).respond();
|
||||
controller.getData();
|
||||
$httpBackend.flush();
|
||||
|
||||
const order = controller.order;
|
||||
|
||||
expect(order.id).toEqual(4);
|
||||
expect(order.isConfirmed).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('order() setter', () => {
|
||||
it(`should call scope $applyAsync() method and apply filters from state params`, () => {
|
||||
$httpBackend.expect('GET', `Orders/4/getItemTypeAvailable?itemCategoryId=1`).respond();
|
||||
controller.order = {id: 4};
|
||||
|
||||
$scope.$apply();
|
||||
|
||||
expect(controller.categoryId).toEqual(1);
|
||||
expect(controller.typeId).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('items() setter', () => {
|
||||
it(`should return an object with order params`, () => {
|
||||
jest.spyOn(controller, 'fetchResultTags');
|
||||
jest.spyOn(controller, 'buildOrderFilter');
|
||||
|
||||
const expectedResult = [{field: 'showOrder, price', name: 'Color and price', priority: 999}];
|
||||
const items = [{id: 1, name: 'My Item', tags: [
|
||||
{tagFk: 4, name: 'Length'},
|
||||
{tagFk: 5, name: 'Color'}
|
||||
]}];
|
||||
controller.items = items;
|
||||
|
||||
expect(controller.orderFields.length).toEqual(6);
|
||||
expect(controller.orderFields).toEqual(jasmine.arrayContaining(expectedResult));
|
||||
expect(controller.fetchResultTags).toHaveBeenCalledWith(items);
|
||||
expect(controller.buildOrderFilter).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('categoryId() setter', () => {
|
||||
it(`should set category property to null, call updateStateParams() method and not call applyFilters()`, () => {
|
||||
jest.spyOn(controller, 'updateStateParams');
|
||||
|
||||
controller.categoryId = null;
|
||||
|
||||
expect(controller.updateStateParams).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it(`should set category property and then call updateStateParams() and applyFilters() methods`, () => {
|
||||
jest.spyOn(controller, 'updateStateParams');
|
||||
|
||||
controller.categoryId = 2;
|
||||
|
||||
expect(controller.updateStateParams).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('changeCategory()', () => {
|
||||
it(`should set categoryId property to null if the new value equals to the old one`, () => {
|
||||
controller.categoryId = 2;
|
||||
controller.changeCategory(2);
|
||||
|
||||
expect(controller.categoryId).toBeNull();
|
||||
});
|
||||
|
||||
it(`should set categoryId property`, () => {
|
||||
controller.categoryId = 2;
|
||||
controller.changeCategory(1);
|
||||
|
||||
expect(controller.categoryId).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('typeId() setter', () => {
|
||||
it(`should set type property to null, call updateStateParams() method and not call applyFilters()`, () => {
|
||||
jest.spyOn(controller, 'updateStateParams');
|
||||
jest.spyOn(controller, 'applyFilters');
|
||||
|
||||
controller.typeId = null;
|
||||
|
||||
expect(controller.updateStateParams).toHaveBeenCalledWith();
|
||||
expect(controller.applyFilters).not.toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it(`should set category property and then call updateStateParams() and applyFilters() methods`, () => {
|
||||
jest.spyOn(controller, 'updateStateParams');
|
||||
jest.spyOn(controller, 'applyFilters');
|
||||
|
||||
controller.typeId = 2;
|
||||
|
||||
expect(controller.updateStateParams).toHaveBeenCalledWith();
|
||||
expect(controller.applyFilters).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('tagGroups() setter', () => {
|
||||
it(`should set tagGroups property and then call updateStateParams() and applyFilters() methods`, () => {
|
||||
jest.spyOn(controller, 'updateStateParams');
|
||||
jest.spyOn(controller, 'applyFilters');
|
||||
|
||||
controller.tagGroups = [{tagFk: 11, values: [{value: 'Brown'}]}];
|
||||
|
||||
expect(controller.updateStateParams).toHaveBeenCalledWith();
|
||||
expect(controller.applyFilters).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onSearchByTag()', () => {
|
||||
it(`should not add a new tag if the event key code doesn't equals to 'Enter'`, () => {
|
||||
jest.spyOn(controller, 'applyFilters');
|
||||
|
||||
controller.order = {id: 4};
|
||||
controller.$.search.value = 'Brown';
|
||||
controller.onSearchByTag({key: 'Tab'});
|
||||
|
||||
expect(controller.applyFilters).not.toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it(`should add a new tag if the event key code equals to 'Enter' an then call applyFilters()`, () => {
|
||||
jest.spyOn(controller, 'applyFilters');
|
||||
|
||||
controller.order = {id: 4};
|
||||
controller.$.search.value = 'Brown';
|
||||
controller.onSearchByTag({key: 'Enter'});
|
||||
|
||||
expect(controller.applyFilters).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onSearch()', () => {
|
||||
it(`should apply a filter by item id an then call the applyFilters method`, () => {
|
||||
jest.spyOn(controller, 'applyFilters');
|
||||
|
||||
const itemId = 1;
|
||||
controller.onSearch({search: itemId});
|
||||
|
||||
expect(controller.applyFilters).toHaveBeenCalledWith({
|
||||
'i.id': itemId
|
||||
});
|
||||
});
|
||||
|
||||
it(`should apply a filter by item name an then call the applyFilters method`, () => {
|
||||
jest.spyOn(controller, 'applyFilters');
|
||||
|
||||
const itemName = 'Bow';
|
||||
controller.onSearch({search: itemName});
|
||||
|
||||
expect(controller.applyFilters).toHaveBeenCalledWith({
|
||||
'i.name': {like: `%${itemName}%`}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyFilters()', () => {
|
||||
it(`should call model applyFilter() method with a new filter`, () => {
|
||||
jest.spyOn(controller.$.model, 'applyFilter');
|
||||
|
||||
controller._categoryId = 2;
|
||||
controller._typeId = 4;
|
||||
|
||||
controller.applyFilters();
|
||||
|
||||
expect(controller.$.model.applyFilter).toHaveBeenCalledWith(
|
||||
{where: {categoryFk: 2, typeFk: 4}},
|
||||
{orderFk: 4, orderBy: controller.getOrderBy(), tagGroups: []});
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove()', () => {
|
||||
it(`should remove a tag from tags property`, () => {
|
||||
jest.spyOn(controller, 'applyFilters');
|
||||
|
||||
controller.tagGroups = [
|
||||
{tagFk: 1, values: [{value: 'Brown'}]},
|
||||
{tagFk: 67, values: [{value: 'Concussion'}]}
|
||||
];
|
||||
controller.remove(0);
|
||||
|
||||
const firstTag = controller.tagGroups[0];
|
||||
|
||||
expect(controller.tagGroups.length).toEqual(1);
|
||||
expect(firstTag.tagFk).toEqual(67);
|
||||
expect(controller.applyFilters).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it(`should remove a tag from tags property and call applyFilters() if there's no more tags`, () => {
|
||||
jest.spyOn(controller, 'applyFilters');
|
||||
|
||||
controller._categoryId = 1;
|
||||
controller._typeId = 1;
|
||||
controller.tagGroups = [{tagFk: 1, values: [{value: 'Blue'}]}];
|
||||
controller.remove(0);
|
||||
|
||||
expect(controller.tagGroups.length).toEqual(0);
|
||||
expect(controller.applyFilters).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateStateParams()', () => {
|
||||
it(`should call state go() method passing category and type state params`, () => {
|
||||
jest.spyOn(controller.$state, 'go');
|
||||
|
||||
controller._categoryId = 2;
|
||||
controller._typeId = 4;
|
||||
controller._tagGroups = [
|
||||
{tagFk: 67, values: [{value: 'Concussion'}], tagSelection: {name: 'Category'}}
|
||||
];
|
||||
const tagGroups = JSON.stringify([
|
||||
{values: [{value: 'Concussion'}], tagFk: 67, tagSelection: {name: 'Category'}}
|
||||
]);
|
||||
const expectedResult = {categoryId: 2, typeId: 4, tagGroups: tagGroups};
|
||||
controller.updateStateParams();
|
||||
|
||||
expect(controller.$state.go).toHaveBeenCalledWith('my.current.state', expectedResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getOrderBy()', () => {
|
||||
it(`should return an object with order params`, () => {
|
||||
controller.orderField = 'relevancy DESC, name';
|
||||
controller.orderWay = 'DESC';
|
||||
let expectedResult = {
|
||||
field: 'relevancy DESC, name',
|
||||
way: 'DESC',
|
||||
isTag: false
|
||||
};
|
||||
let result = controller.getOrderBy();
|
||||
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyOrder()', () => {
|
||||
it(`should apply order param to model calling getOrderBy()`, () => {
|
||||
jest.spyOn(controller, 'getOrderBy');
|
||||
jest.spyOn(controller.$.model, 'addFilter');
|
||||
|
||||
controller.field = 'relevancy DESC, name';
|
||||
controller.way = 'ASC';
|
||||
controller._categoryId = 1;
|
||||
controller._typeId = 1;
|
||||
let expectedOrder = {orderBy: controller.getOrderBy()};
|
||||
|
||||
controller.applyOrder();
|
||||
|
||||
expect(controller.getOrderBy).toHaveBeenCalledWith();
|
||||
expect(controller.$.model.addFilter).toHaveBeenCalledWith(null, expectedOrder);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchResultTags()', () => {
|
||||
it(`should create an array of non repeated tags then set the resultTags property`, () => {
|
||||
const items = [
|
||||
{
|
||||
id: 1, name: 'My Item 1', tags: [
|
||||
{tagFk: 4, name: 'Length', value: 1},
|
||||
{tagFk: 5, name: 'Color', value: 'red'}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2, name: 'My Item 2', tags: [
|
||||
{tagFk: 4, name: 'Length', value: 1},
|
||||
{tagFk: 5, name: 'Color', value: 'blue'}
|
||||
]
|
||||
}];
|
||||
controller.fetchResultTags(items);
|
||||
|
||||
expect(controller.resultTags.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildOrderFilter()', () => {
|
||||
it(`should create an array of non repeated tags plus default filters and then set the orderFields property`, () => {
|
||||
const items = [
|
||||
{
|
||||
id: 1, name: 'My Item 1', tags: [
|
||||
{tagFk: 4, name: 'Length'},
|
||||
{tagFk: 5, name: 'Color'}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2, name: 'My Item 2', tags: [
|
||||
{tagFk: 5, name: 'Color'},
|
||||
{tagFk: 6, name: 'Relevancy'}
|
||||
]
|
||||
}];
|
||||
|
||||
controller.fetchResultTags(items);
|
||||
controller.buildOrderFilter();
|
||||
|
||||
expect(controller.orderFields.length).toEqual(7);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatTooltip()', () => {
|
||||
it(`should return a formatted text with the tag name and values`, () => {
|
||||
const tagGroup = {
|
||||
values: [{value: 'Silver'}, {value: 'Brown'}],
|
||||
tagFk: 1,
|
||||
tagSelection: {
|
||||
name: 'Color'
|
||||
}
|
||||
};
|
||||
|
||||
const result = controller.formatTooltip(tagGroup);
|
||||
|
||||
expect(result).toEqual(`Color: "Silver", "Brown"`);
|
||||
});
|
||||
|
||||
it(`should return a formatted text with the tag value`, () => {
|
||||
const tagGroup = {
|
||||
values: [{value: 'Silver'}]
|
||||
};
|
||||
|
||||
const result = controller.formatTooltip(tagGroup);
|
||||
|
||||
expect(result).toEqual(`"Silver"`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sanitizedTagGroupParam()', () => {
|
||||
it(`should return an array of tags`, () => {
|
||||
const dirtyTagGroups = [{
|
||||
values: [{value: 'Silver'}, {value: 'Brown'}],
|
||||
tagFk: 1,
|
||||
tagSelection: {
|
||||
name: 'Color',
|
||||
$orgRow: {name: 'Color'}
|
||||
},
|
||||
$orgIndex: 1
|
||||
}];
|
||||
controller.tagGroups = dirtyTagGroups;
|
||||
|
||||
const expectedResult = [{
|
||||
values: [{value: 'Silver'}, {value: 'Brown'}],
|
||||
tagFk: 1,
|
||||
tagSelection: {
|
||||
name: 'Color'
|
||||
}
|
||||
}];
|
||||
const result = controller.sanitizedTagGroupParam();
|
||||
|
||||
expect(result).toEqual(expect.objectContaining(expectedResult));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
Name: Nombre
|
||||
Search by item id or name: Buscar por id de artículo o nombre
|
||||
OR: O
|
|
@ -1,54 +0,0 @@
|
|||
@import "variables";
|
||||
|
||||
vn-order-catalog vn-side-menu div {
|
||||
& > .input {
|
||||
padding-left: $spacing-md;
|
||||
padding-right: $spacing-md;
|
||||
border-color: $color-spacer;
|
||||
border-bottom: $border-thin;
|
||||
}
|
||||
.item-category {
|
||||
padding: $spacing-sm;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
|
||||
vn-autocomplete[vn-id="category"] {
|
||||
display: none
|
||||
}
|
||||
|
||||
& > vn-one {
|
||||
padding: $spacing-sm;
|
||||
min-width: 33.33%;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
& > vn-icon {
|
||||
padding: $spacing-sm;
|
||||
background-color: $color-font-secondary;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
background-color: $color-main;
|
||||
color: #FFF
|
||||
}
|
||||
& > i:before {
|
||||
font-size: 2.6rem;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: $spacing-md;
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
}
|
||||
vn-autocomplete[vn-id="type"] .list {
|
||||
max-height: 320px
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<vn-autocomplete
|
||||
vn-focus
|
||||
vn-id="client"
|
||||
url="Clients"
|
||||
label="Client"
|
||||
search-function="{or: [{id: $search}, {name: {like: '%'+ $search +'%'}}]}"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
ng-model="$ctrl.clientFk"
|
||||
vn-name="client"
|
||||
order="id">
|
||||
<tpl-item>{{id}}: {{name}}</tpl-item>
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
disabled="!$ctrl.clientFk"
|
||||
url="{{ $ctrl.clientFk ? 'Clients/'+ $ctrl.clientFk +'/addresses' : null }}"
|
||||
fields="['nickname', 'street', 'city']"
|
||||
ng-model="$ctrl.addressFk"
|
||||
vn-name="address"
|
||||
show-field="nickname"
|
||||
value-field="id"
|
||||
label="Address">
|
||||
<tpl-item>{{nickname}}: {{street}}, {{city}}</tpl-item>
|
||||
</vn-autocomplete>
|
||||
<vn-date-picker
|
||||
label="Landed"
|
||||
ng-model="$ctrl.landed"
|
||||
vn-name="landed">
|
||||
</vn-date-picker>
|
||||
<vn-autocomplete
|
||||
disabled="!$ctrl.clientFk || !$ctrl.landed"
|
||||
data="$ctrl._availableAgencies"
|
||||
label="Agency"
|
||||
show-field="agencyMode"
|
||||
value-field="agencyModeFk"
|
||||
ng-model="$ctrl.order.agencyModeFk"
|
||||
vn-name="agencyMode">
|
||||
</vn-autocomplete>
|
|
@ -1,114 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Component from 'core/lib/component';
|
||||
|
||||
class Controller extends Component {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
this.order = {};
|
||||
this.clientFk = this.$params.clientFk;
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
if (this.$params && this.$params.clientFk)
|
||||
this.clientFk = this.$params.clientFk;
|
||||
}
|
||||
|
||||
set order(value) {
|
||||
if (value)
|
||||
this._order = value;
|
||||
}
|
||||
|
||||
get order() {
|
||||
return this._order;
|
||||
}
|
||||
|
||||
set clientFk(value) {
|
||||
this.order.clientFk = value;
|
||||
|
||||
if (value) {
|
||||
let filter = {
|
||||
include: {
|
||||
relation: 'defaultAddress',
|
||||
scope: {
|
||||
fields: 'id'
|
||||
}
|
||||
},
|
||||
where: {id: value}
|
||||
};
|
||||
filter = encodeURIComponent(JSON.stringify(filter));
|
||||
let query = `Clients?filter=${filter}`;
|
||||
this.$http.get(query).then(res => {
|
||||
if (res.data) {
|
||||
let client = res.data[0];
|
||||
let defaultAddress = client.defaultAddress;
|
||||
this.addressFk = defaultAddress.id;
|
||||
}
|
||||
});
|
||||
} else
|
||||
this.addressFk = null;
|
||||
}
|
||||
|
||||
get clientFk() {
|
||||
return this.order.clientFk;
|
||||
}
|
||||
|
||||
set addressFk(value) {
|
||||
this.order.addressFk = value;
|
||||
this.getAvailableAgencies();
|
||||
}
|
||||
|
||||
get addressFk() {
|
||||
return this.order.addressFk;
|
||||
}
|
||||
|
||||
set landed(value) {
|
||||
this.order.landed = value;
|
||||
this.getAvailableAgencies();
|
||||
}
|
||||
|
||||
get landed() {
|
||||
return this.order.landed;
|
||||
}
|
||||
|
||||
get warehouseFk() {
|
||||
return this.order.warehouseFk;
|
||||
}
|
||||
|
||||
getAvailableAgencies() {
|
||||
let order = this.order;
|
||||
order.agencyModeFk = null;
|
||||
|
||||
let params = {
|
||||
addressFk: order.addressFk,
|
||||
landed: order.landed
|
||||
};
|
||||
if (params.landed && params.addressFk) {
|
||||
this.$http.get(`Agencies/landsThatDay`, {params})
|
||||
.then(res => this._availableAgencies = res.data);
|
||||
}
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.createOrder();
|
||||
}
|
||||
|
||||
createOrder() {
|
||||
let params = {
|
||||
landed: this.order.landed,
|
||||
addressId: this.order.addressFk,
|
||||
agencyModeId: this.order.agencyModeFk
|
||||
};
|
||||
this.$http.post(`Orders/new`, params).then(res => {
|
||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||
this.$state.go('order.card.catalog', {id: res.data});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnOrderCreateCard', {
|
||||
template: require('./card.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
order: '<?'
|
||||
}
|
||||
});
|
|
@ -1,104 +0,0 @@
|
|||
import './card.js';
|
||||
|
||||
describe('Order', () => {
|
||||
describe('Component vnOrderCreateCard', () => {
|
||||
let controller;
|
||||
let $httpBackend;
|
||||
let $scope;
|
||||
|
||||
beforeEach(ngModule('order'));
|
||||
|
||||
beforeEach(inject(($componentController, _$httpBackend_, _vnApp_, $rootScope) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$scope = $rootScope.$new();
|
||||
const $element = angular.element('<vn-order-create-card></vn-order-create-card>');
|
||||
controller = $componentController('vnOrderCreateCard', {$element, $scope});
|
||||
controller.item = {id: 3};
|
||||
}));
|
||||
|
||||
describe('set order', () => {
|
||||
it(`should set order if the value given is not null`, () => {
|
||||
controller.order = 1;
|
||||
|
||||
expect(controller.order).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('set clientFk', () => {
|
||||
it(`should set addressFk to null and clientFk to a value and set addressFk to a value given`, () => {
|
||||
let filter = {
|
||||
include: {
|
||||
relation: 'defaultAddress',
|
||||
scope: {
|
||||
fields: 'id'
|
||||
}
|
||||
},
|
||||
where: {id: 2}
|
||||
};
|
||||
filter = encodeURIComponent(JSON.stringify(filter));
|
||||
let response = [
|
||||
{
|
||||
defaultAddress: {id: 1}
|
||||
}
|
||||
];
|
||||
$httpBackend.whenGET(`Clients?filter=${filter}`).respond(response);
|
||||
$httpBackend.expectGET(`Clients?filter=${filter}`);
|
||||
|
||||
controller.clientFk = 2;
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.clientFk).toEqual(2);
|
||||
expect(controller.order.addressFk).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('set addressFk', () => {
|
||||
it(`should set agencyModeFk property to null and addressFk to a value`, () => {
|
||||
controller.addressFk = 1101;
|
||||
|
||||
expect(controller.addressFk).toEqual(1101);
|
||||
expect(controller.order.agencyModeFk).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAvailableAgencies()', () => {
|
||||
it(`should make a query if landed and addressFk exists`, () => {
|
||||
controller.order.addressFk = 1101;
|
||||
controller.order.landed = 1101;
|
||||
|
||||
$httpBackend.whenRoute('GET', 'Agencies/landsThatDay')
|
||||
.respond({data: 1});
|
||||
|
||||
controller.getAvailableAgencies();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onSubmit()', () => {
|
||||
it(`should call createOrder()`, () => {
|
||||
jest.spyOn(controller, 'createOrder');
|
||||
controller.onSubmit();
|
||||
|
||||
expect(controller.createOrder).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('createOrder()', () => {
|
||||
it(`should make a query, call vnApp.showSuccess and $state.go if the response is defined`, () => {
|
||||
controller.order.landed = 1101;
|
||||
controller.order.addressFk = 1101;
|
||||
controller.order.agencyModeFk = 1101;
|
||||
|
||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||
jest.spyOn(controller.$state, 'go');
|
||||
$httpBackend.expect('POST', 'Orders/new', {landed: 1101, addressId: 1101, agencyModeId: 1101}).respond(200, 1);
|
||||
controller.createOrder();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||
expect(controller.$state.go).toHaveBeenCalledWith('order.card.catalog', {id: 1});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<div class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-order-create-card vn-id="card" on-save=""></vn-order-create-card>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit
|
||||
ng-click="$ctrl.onSubmit()"
|
||||
label="Create">
|
||||
</vn-submit>
|
||||
<vn-button
|
||||
class="cancel"
|
||||
label="Cancel"
|
||||
ui-sref="order.index">
|
||||
</vn-button>
|
||||
</vn-button-bar>
|
||||
</div>
|
|
@ -1,14 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
|
||||
class Controller extends Section {
|
||||
async onSubmit() {
|
||||
let newOrderID = await this.$.card.createOrder();
|
||||
this.$state.go('order.card.summary', {id: newOrderID});
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnOrderCreate', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Order', () => {
|
||||
describe('Component vnOrderCreate', () => {
|
||||
let $scope;
|
||||
let controller;
|
||||
|
||||
beforeEach(ngModule('order'));
|
||||
|
||||
beforeEach(inject(($componentController, $rootScope) => {
|
||||
$scope = $rootScope.$new();
|
||||
$scope.card = {createOrder: () => {}};
|
||||
const $element = angular.element('<vn-order-create></vn-order-create>');
|
||||
controller = $componentController('vnOrderCreate', {$element, $scope});
|
||||
}));
|
||||
|
||||
describe('onSubmit()', () => {
|
||||
it(`should call createOrder()`, () => {
|
||||
jest.spyOn(controller.$.card, 'createOrder');
|
||||
controller.onSubmit();
|
||||
|
||||
expect(controller.$.card.createOrder).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it(`should call go()`, async() => {
|
||||
jest.spyOn(controller.$state, 'go');
|
||||
await controller.onSubmit();
|
||||
|
||||
expect(controller.$state.go).toHaveBeenCalledWith('order.card.summary', {id: undefined});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
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 an 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
|
|
@ -1,78 +0,0 @@
|
|||
<vn-descriptor-content
|
||||
module="order"
|
||||
description="$ctrl.order.client.name"
|
||||
summary="$ctrl.$.summary">
|
||||
<slot-menu>
|
||||
<vn-item
|
||||
ng-click="deleteOrderConfirmation.show()"
|
||||
translate>
|
||||
Delete order
|
||||
</vn-item>
|
||||
</slot-menu>
|
||||
<slot-body>
|
||||
<div class="attributes">
|
||||
<vn-label-value
|
||||
label="State"
|
||||
value="{{$ctrl.$t($ctrl.order.isConfirmed ? 'Confirmed' : 'Not confirmed')}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="Sales person">
|
||||
<span
|
||||
ng-click="workerDescriptor.show($event, $ctrl.order.client.salesPersonFk)"
|
||||
class="link">
|
||||
{{$ctrl.order.client.salesPersonUser.name}}
|
||||
</span>
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="Landed"
|
||||
value="{{$ctrl.order.landed | date: 'dd/MM/yyyy' }}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="Agency"
|
||||
value="{{$ctrl.order.agencyMode.name}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="Alias"
|
||||
value="{{$ctrl.order.address.nickname}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="Items"
|
||||
value="{{$ctrl.order.rows.length || 0}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="Total"
|
||||
value="{{$ctrl.order.total | currency: 'EUR': 2}}">
|
||||
</vn-label-value>
|
||||
</div>
|
||||
<div class="quicklinks">
|
||||
<div ng-transclude="btnOne">
|
||||
<vn-quick-link
|
||||
tooltip="Order ticket list"
|
||||
state="['ticket.index', {q: $ctrl.ticketFilter}]"
|
||||
icon="icon-ticket">
|
||||
</vn-quick-link>
|
||||
</div>
|
||||
<div ng-transclude="btnTwo">
|
||||
<vn-quick-link
|
||||
tooltip="Client card"
|
||||
state="['client.card.summary', {id: $ctrl.order.clientFk}]"
|
||||
icon="person">
|
||||
</vn-quick-link>
|
||||
</div>
|
||||
<div ng-transclude="btnThree">
|
||||
</div>
|
||||
</div>
|
||||
</slot-body>
|
||||
</vn-descriptor-content>
|
||||
<vn-confirm
|
||||
vn-id="deleteOrderConfirmation"
|
||||
on-accept="$ctrl.deleteOrder()"
|
||||
message="You are going to delete this order"
|
||||
question="continue anyway?">
|
||||
</vn-confirm>
|
||||
<vn-worker-descriptor-popover
|
||||
vn-id="workerDescriptor">
|
||||
</vn-worker-descriptor-popover>
|
||||
<vn-popup vn-id="summary">
|
||||
<vn-order-summary order="$ctrl.order"></vn-order-summary>
|
||||
</vn-popup>
|
|
@ -1,32 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Descriptor from 'salix/components/descriptor';
|
||||
|
||||
class Controller extends Descriptor {
|
||||
get order() {
|
||||
return this.entity;
|
||||
}
|
||||
|
||||
set order(value) {
|
||||
this.entity = value;
|
||||
}
|
||||
|
||||
get ticketFilter() {
|
||||
return JSON.stringify({orderFk: this.id});
|
||||
}
|
||||
|
||||
deleteOrder() {
|
||||
return this.$http.delete(`Orders/${this.id}`)
|
||||
.then(() => {
|
||||
this.$state.go('order.index');
|
||||
this.vnApp.showSuccess(this.$t('Order deleted'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnOrderDescriptor', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
order: '<'
|
||||
}
|
||||
});
|
|
@ -1,29 +0,0 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Order Component vnOrderDescriptor', () => {
|
||||
let $httpBackend;
|
||||
let controller;
|
||||
const order = {id: 1};
|
||||
|
||||
beforeEach(ngModule('order'));
|
||||
|
||||
beforeEach(inject(($componentController, _$httpBackend_) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
controller = $componentController('vnOrderDescriptor', {$element: null}, {order});
|
||||
}));
|
||||
|
||||
describe('deleteOrder()', () => {
|
||||
it(`should perform a DELETE query`, () => {
|
||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||
jest.spyOn(controller.$state, 'go');
|
||||
|
||||
$httpBackend.expectDELETE(`Orders/${order.id}`).respond();
|
||||
controller.deleteOrder();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||
expect(controller.$state.go).toHaveBeenCalledWith('order.index');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
Client: Cliente
|
||||
Confirmed: Confirmado
|
||||
Not confirmed: Sin confirmar
|
||||
State: Estado
|
||||
Landed: F. entrega
|
||||
Items: Articulos
|
||||
Agency: Agencia
|
||||
Sales person: Comercial
|
||||
Order ticket list: Ticket del pedido
|
||||
Delete order: Eliminar pedido
|
||||
You are going to delete this order: El pedido se eliminará
|
||||
continue anyway?: ¿Continuar de todos modos?
|
|
@ -1,17 +1,3 @@
|
|||
export * from './module';
|
||||
|
||||
import './main';
|
||||
import './index/';
|
||||
import './card';
|
||||
import './descriptor';
|
||||
import './search-panel';
|
||||
import './catalog-search-panel';
|
||||
import './catalog-view';
|
||||
import './catalog';
|
||||
import './summary';
|
||||
import './line';
|
||||
import './prices-popover';
|
||||
import './volume';
|
||||
import './create';
|
||||
import './create/card';
|
||||
import './basic-data';
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
|
||||
<vn-auto-search
|
||||
model="model">
|
||||
</vn-auto-search>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
class="vn-mb-xl">
|
||||
<vn-card>
|
||||
<vn-table model="model">
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th field="id" number>Id</vn-th>
|
||||
<vn-th field="salesPersonFk">Sales person</vn-th>
|
||||
<vn-th field="clientFk">Client</vn-th>
|
||||
<vn-th field="isConfirmed" center>Confirmed</vn-th>
|
||||
<vn-th field="created" center expand>Created</vn-th>
|
||||
<vn-th field="landed" shrink-date>Landed</vn-th>
|
||||
<vn-th field="created" center>Hour</vn-th>
|
||||
<vn-th field="agencyName" center>Agency</vn-th>
|
||||
<vn-th field="total" center>Total</vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<a
|
||||
ng-repeat="order in model.data"
|
||||
class="clickable search-result vn-tr"
|
||||
ui-sref="order.card.summary({id: {{::order.id}}})">
|
||||
<vn-td number>{{::order.id}}</vn-td>
|
||||
<vn-td expand>
|
||||
<span
|
||||
vn-click-stop="workerDescriptor.show($event, order.salesPersonFk)"
|
||||
class="link" >
|
||||
{{::order.name | dashIfEmpty}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td>
|
||||
<span
|
||||
vn-click-stop="clientDescriptor.show($event, order.clientFk)"
|
||||
class="link">
|
||||
{{::order.clientName}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td center>
|
||||
<vn-check
|
||||
ng-model="order.isConfirmed"
|
||||
disabled="true">
|
||||
</vn-check>
|
||||
</vn-td>
|
||||
<vn-td shrink-datetime>{{::order.created | date: 'dd/MM/yyyy HH:mm'}}</vn-td>
|
||||
<vn-td shrink-date>
|
||||
<span class="chip {{$ctrl.compareDate(order.landed)}}">
|
||||
{{::order.landed | date:'dd/MM/yyyy'}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td shrink>{{::(order.hourTheoretical
|
||||
? order.hourTheoretical
|
||||
: order.hourEffective) | dashIfEmpty
|
||||
}}</vn-td>
|
||||
<vn-td expand>{{::order.agencyName}}</vn-td>
|
||||
<vn-td number>{{::order.total | currency: 'EUR': 2 | dashIfEmpty}}</vn-td>
|
||||
<vn-td shrink>
|
||||
<vn-icon-button
|
||||
vn-click-stop="$ctrl.preview(order)"
|
||||
icon="preview"
|
||||
vn-tooltip="Preview">
|
||||
</vn-icon-button>
|
||||
</vn-td>
|
||||
</a>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
</vn-card>
|
||||
</vn-data-viewer>
|
||||
<a
|
||||
ui-sref="order.create"
|
||||
vn-bind="+"
|
||||
vn-tooltip="New order"
|
||||
fixed-bottom-right>
|
||||
<vn-float-button icon="add"></vn-float-button>
|
||||
</a>
|
||||
<vn-client-descriptor-popover
|
||||
vn-id="clientDescriptor">
|
||||
</vn-client-descriptor-popover>
|
||||
<vn-worker-descriptor-popover
|
||||
vn-id="workerDescriptor">
|
||||
</vn-worker-descriptor-popover>
|
||||
<vn-popup vn-id="summary">
|
||||
<vn-order-summary
|
||||
order="$ctrl.selectedOrder">
|
||||
</vn-order-summary>
|
||||
</vn-popup>
|
|
@ -1,26 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
|
||||
export default class Controller extends Section {
|
||||
preview(order) {
|
||||
this.selectedOrder = order;
|
||||
this.$.summary.show();
|
||||
}
|
||||
|
||||
compareDate(date) {
|
||||
let today = Date.vnNew();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
date = new Date(date);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
|
||||
const timeDifference = today - date;
|
||||
if (timeDifference == 0) return 'warning';
|
||||
if (timeDifference < 0) return 'success';
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnOrderIndex', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -1,67 +0,0 @@
|
|||
import './index.js';
|
||||
describe('Component vnOrderIndex', () => {
|
||||
let controller;
|
||||
let $window;
|
||||
let orders = [{
|
||||
id: 1,
|
||||
clientFk: 1,
|
||||
isConfirmed: false
|
||||
}, {
|
||||
id: 2,
|
||||
clientFk: 1,
|
||||
isConfirmed: false
|
||||
}, {
|
||||
id: 3,
|
||||
clientFk: 1,
|
||||
isConfirmed: true
|
||||
}];
|
||||
|
||||
beforeEach(ngModule('order'));
|
||||
|
||||
beforeEach(inject(($componentController, _$window_) => {
|
||||
$window = _$window_;
|
||||
const $element = angular.element('<vn-order-index></vn-order-index>');
|
||||
controller = $componentController('vnOrderIndex', {$element});
|
||||
}));
|
||||
|
||||
describe('compareDate()', () => {
|
||||
it('should return warning when the date is the present', () => {
|
||||
let curDate = Date.vnNew();
|
||||
let result = controller.compareDate(curDate);
|
||||
|
||||
expect(result).toEqual('warning');
|
||||
});
|
||||
|
||||
it('should return sucess when the date is in the future', () => {
|
||||
let futureDate = Date.vnNew();
|
||||
futureDate = futureDate.setDate(futureDate.getDate() + 10);
|
||||
let result = controller.compareDate(futureDate);
|
||||
|
||||
expect(result).toEqual('success');
|
||||
});
|
||||
|
||||
it('should return undefined when the date is in the past', () => {
|
||||
let pastDate = Date.vnNew();
|
||||
pastDate = pastDate.setDate(pastDate.getDate() - 10);
|
||||
let result = controller.compareDate(pastDate);
|
||||
|
||||
expect(result).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('preview()', () => {
|
||||
it('should show the dialog summary', () => {
|
||||
controller.$.summary = {show: () => {}};
|
||||
jest.spyOn(controller.$.summary, 'show');
|
||||
|
||||
let event = new MouseEvent('click', {
|
||||
view: $window,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
});
|
||||
controller.preview(event, orders[0]);
|
||||
|
||||
expect(controller.$.summary.show).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,96 +0,0 @@
|
|||
<vn-data-viewer data="$ctrl.rows" class="vn-w-lg">
|
||||
<vn-card class="vn-pa-lg header" ng-if="$ctrl.rows.length > 0">
|
||||
<div>
|
||||
<vn-label translate>Subtotal</vn-label>
|
||||
{{$ctrl.subtotal | currency: 'EUR':2}}
|
||||
</div>
|
||||
<div>
|
||||
<vn-label translate>VAT</vn-label>
|
||||
{{$ctrl.VAT | currency: 'EUR':2}}
|
||||
</div>
|
||||
<div>
|
||||
<vn-label>Total</vn-label>
|
||||
{{$ctrl.order.total | currency: 'EUR':2}}
|
||||
</div>
|
||||
</vn-card>
|
||||
<vn-card class="vn-mt-md">
|
||||
<vn-table>
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th></vn-th>
|
||||
<vn-th number>Id</vn-th>
|
||||
<vn-th>Description</vn-th>
|
||||
<vn-th>Warehouse</vn-th>
|
||||
<vn-th>Shipped</vn-th>
|
||||
<vn-th number>Quantity</vn-th>
|
||||
<vn-th number>Price</vn-th>
|
||||
<vn-th number>Amount</vn-th>
|
||||
<vn-th ng-if="::!$ctrl.order.isConfirmed"></vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr ng-repeat="row in $ctrl.rows">
|
||||
<vn-td shrink>
|
||||
<img
|
||||
ng-src="{{::$root.imagePath('catalog', '50x50', row.item.id)}}"
|
||||
zoom-image="{{::$root.imagePath('catalog', '1600x900', row.item.id)}}"
|
||||
on-error-src/>
|
||||
</vn-td>
|
||||
<vn-td number>
|
||||
<span ng-click="itemDescriptor.show($event, row.itemFk)"
|
||||
class="link">
|
||||
{{::row.itemFk}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td vn-fetched-tags>
|
||||
<div>
|
||||
<vn-one title="{{::row.item.name}}">{{::row.item.name}}</vn-one>
|
||||
<vn-one ng-if="::row.item.subName">
|
||||
<h3 title="{{::row.item.subName}}">{{::row.item.subName}}</h3>
|
||||
</vn-one>
|
||||
</div>
|
||||
<vn-fetched-tags
|
||||
max-length="6"
|
||||
item="::row.item"
|
||||
tabindex="-1">
|
||||
</vn-fetched-tags>
|
||||
</vn-td>
|
||||
<vn-td>{{::row.warehouse.name}}</vn-td>
|
||||
<vn-td>{{::row.shipped | date: 'dd/MM/yyyy'}}</vn-td>
|
||||
<vn-td number>{{::row.quantity}}</vn-td>
|
||||
<vn-td number>
|
||||
{{::row.price | currency: 'EUR':2}}
|
||||
</vn-td>
|
||||
<vn-td number>
|
||||
{{::row.price * row.quantity | currency: 'EUR':2}}
|
||||
</vn-td>
|
||||
<vn-td shrink ng-if="::!$ctrl.order.isConfirmed">
|
||||
<vn-icon-button
|
||||
vn-tooltip="Remove item"
|
||||
icon="delete"
|
||||
ng-click="deleteRow.show($index)"
|
||||
tabindex="-1">
|
||||
</vn-icon-button>
|
||||
</vn-td>
|
||||
</vn-tr>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
</vn-card>
|
||||
</vn-data-viewer>
|
||||
<vn-float-button
|
||||
icon="check"
|
||||
vn-tooltip="Confirm"
|
||||
ng-click="$ctrl.save()"
|
||||
ng-if="!$ctrl.order.isConfirmed"
|
||||
fixed-bottom-right>
|
||||
</vn-float-button>
|
||||
<vn-item-descriptor-popover
|
||||
vn-id="item-descriptor"
|
||||
warehouse-fk="$ctrl.vnConfig.warehouseFk">
|
||||
</vn-item-descriptor-popover>
|
||||
<vn-confirm
|
||||
vn-id="delete-row"
|
||||
on-accept="$ctrl.deleteRow($data)"
|
||||
question="Delete row"
|
||||
message="Are you sure you want to delete this row?">
|
||||
</vn-confirm>
|
|
@ -1,70 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
import './style.scss';
|
||||
|
||||
class Controller extends Section {
|
||||
$onInit() {
|
||||
this.getRows();
|
||||
}
|
||||
|
||||
set order(value) {
|
||||
this._order = value;
|
||||
this.getVAT();
|
||||
}
|
||||
|
||||
get order() {
|
||||
return this._order;
|
||||
}
|
||||
|
||||
get subtotal() {
|
||||
return this.order ? this.order.total - this.VAT : 0;
|
||||
}
|
||||
|
||||
getRows() {
|
||||
let filter = {
|
||||
where: {orderFk: this.$params.id},
|
||||
include: [
|
||||
{relation: 'item'},
|
||||
{relation: 'warehouse'}
|
||||
]
|
||||
};
|
||||
this.$http.get(`OrderRows`, {filter})
|
||||
.then(res => this.rows = res.data);
|
||||
}
|
||||
|
||||
getVAT() {
|
||||
this.$http.get(`Orders/${this.$params.id}/getVAT`)
|
||||
.then(res => this.VAT = res.data);
|
||||
}
|
||||
|
||||
deleteRow(index) {
|
||||
let [row] = this.rows.splice(index, 1);
|
||||
let params = {
|
||||
rows: [row.id],
|
||||
actualOrderId: this.$params.id
|
||||
};
|
||||
return this.$http.post(`OrderRows/removes`, params)
|
||||
.then(() => this.card.reload())
|
||||
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')));
|
||||
}
|
||||
|
||||
save() {
|
||||
this.$http.post(`Orders/${this.$params.id}/confirm`).then(() => {
|
||||
this.vnApp.showSuccess(this.$t('Order confirmed'));
|
||||
this.$state.go(`ticket.index`, {
|
||||
q: JSON.stringify({clientFk: this.order.clientFk})
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnOrderLine', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
order: '<'
|
||||
},
|
||||
require: {
|
||||
card: '^vnOrderCard'
|
||||
}
|
||||
});
|
|
@ -1,66 +0,0 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Order', () => {
|
||||
describe('Component vnOrderLine', () => {
|
||||
let $state;
|
||||
let controller;
|
||||
let $httpBackend;
|
||||
|
||||
const vat = 10.5;
|
||||
const rows = [
|
||||
{
|
||||
quantity: 4,
|
||||
price: 10.5
|
||||
}, {
|
||||
quantity: 3,
|
||||
price: 2.4
|
||||
}
|
||||
];
|
||||
|
||||
beforeEach(ngModule('order'));
|
||||
|
||||
beforeEach(inject(($componentController, _$state_, _$httpBackend_) => {
|
||||
$state = _$state_;
|
||||
$httpBackend = _$httpBackend_;
|
||||
|
||||
$state.params.id = 1;
|
||||
$httpBackend.whenGET(`OrderRows`).respond(rows);
|
||||
$httpBackend.whenRoute('GET', `Orders/:id/getVAT`).respond(200, vat);
|
||||
|
||||
controller = $componentController('vnOrderLine', {$element: null});
|
||||
}));
|
||||
|
||||
describe('getRows()', () => {
|
||||
it('should make a query to get the rows of a given order', () => {
|
||||
controller.getRows();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.rows).toEqual(rows);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getVAT()', () => {
|
||||
it('should make a query to get the VAT of a given order', () => {
|
||||
controller.getVAT();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.VAT).toBe(vat);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteRow()', () => {
|
||||
it('should remove a row from rows and add save the data if the response is accept', () => {
|
||||
controller.getRows();
|
||||
$httpBackend.flush();
|
||||
|
||||
controller.card = {reload: jasmine.createSpy('reload')};
|
||||
$httpBackend.expectPOST(`OrderRows/removes`).respond();
|
||||
controller.deleteRow(0);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.rows.length).toBe(1);
|
||||
expect(controller.card.reload).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
Delete row: Eliminar linea
|
||||
Order confirmed: Pedido confirmado
|
||||
Are you sure you want to delete this row?: ¿Estas seguro de que quieres eliminar esta línea?
|
|
@ -1,18 +0,0 @@
|
|||
@import "./variables";
|
||||
|
||||
vn-order-line {
|
||||
vn-table {
|
||||
img {
|
||||
border-radius: 50%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
.header {
|
||||
text-align: right;
|
||||
|
||||
& > div {
|
||||
margin-bottom: $spacing-xs;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,8 +2,12 @@ import ngModule from '../module';
|
|||
import ModuleMain from 'salix/components/module-main';
|
||||
|
||||
export default class Order extends ModuleMain {
|
||||
$postLink() {
|
||||
this.filter = {showEmpty: false};
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
}
|
||||
async $onInit() {
|
||||
this.$state.go('home');
|
||||
window.location.href = await this.vnApp.getUrl(`order/`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
<default>
|
||||
<form name="form" class="prices">
|
||||
<vn-table>
|
||||
<vn-tbody>
|
||||
<vn-tr ng-repeat="price in $ctrl.prices">
|
||||
<vn-td class="warehouse" expand>
|
||||
<span
|
||||
class="text"
|
||||
title="{{::price.warehouse}}">
|
||||
{{::price.warehouse}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td number expand>
|
||||
<div>
|
||||
<span
|
||||
ng-click="$ctrl.addQuantity(price)"
|
||||
class="link unselectable">{{::price.grouping}}</span>
|
||||
<span> x {{::price.price | currency: 'EUR': 2}}</span>
|
||||
</div>
|
||||
<div class="price-kg" ng-show="::price.priceKg">
|
||||
{{::price.priceKg | currency: 'EUR'}}/Kg
|
||||
</div>
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<!-- Focus first element -->
|
||||
<vn-input-number ng-if="$index === 0"
|
||||
min="0"
|
||||
name="quantity"
|
||||
ng-model="price.quantity"
|
||||
step="price.grouping"
|
||||
class="dense"
|
||||
vn-focus>
|
||||
</vn-input-number>
|
||||
<vn-input-number ng-if="$index > 0"
|
||||
min="0"
|
||||
name="quantity"
|
||||
ng-model="price.quantity"
|
||||
step="price.grouping"
|
||||
class="dense">
|
||||
</vn-input-number>
|
||||
</vn-td>
|
||||
</vn-tr>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
<div class="footer vn-pa-md">
|
||||
<vn-submit
|
||||
label="Add"
|
||||
ng-click="$ctrl.submit()">
|
||||
</vn-submit>
|
||||
</div>
|
||||
</form>
|
||||
</default>
|
|
@ -1,115 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Popover from 'core/components/popover';
|
||||
import './style.scss';
|
||||
|
||||
class Controller extends Popover {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.totalBasquet = 0;
|
||||
}
|
||||
|
||||
set prices(value) {
|
||||
this._prices = value;
|
||||
if (value && value[0].grouping)
|
||||
this.getTotalQuantity();
|
||||
}
|
||||
|
||||
get prices() {
|
||||
return this._prices;
|
||||
}
|
||||
|
||||
show(parent, item) {
|
||||
this.id = item.id;
|
||||
this.item = JSON.parse(JSON.stringify(item));
|
||||
this.maxQuantity = this.item.available;
|
||||
this.prices = this.item.prices;
|
||||
|
||||
super.show(parent);
|
||||
}
|
||||
|
||||
onClose() {
|
||||
this.id = null;
|
||||
this.item = {};
|
||||
this.tags = {};
|
||||
this._prices = {};
|
||||
this.totalQuantity = 0;
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
getTotalQuantity() {
|
||||
let total = 0;
|
||||
for (let price of this.prices) {
|
||||
if (!price.quantity) price.quantity = 0;
|
||||
total += price.quantity;
|
||||
}
|
||||
|
||||
this.totalQuantity = total;
|
||||
}
|
||||
|
||||
addQuantity(price) {
|
||||
this.getTotalQuantity();
|
||||
const quantity = this.totalQuantity + price.grouping;
|
||||
if (quantity <= this.maxQuantity)
|
||||
price.quantity += price.grouping;
|
||||
}
|
||||
|
||||
getGroupings() {
|
||||
const filledRows = [];
|
||||
for (let priceOption of this.prices) {
|
||||
if (priceOption.quantity && priceOption.quantity > 0) {
|
||||
const priceMatch = filledRows.find(row => {
|
||||
return row.warehouseFk == priceOption.warehouseFk
|
||||
&& row.price == priceOption.price;
|
||||
});
|
||||
|
||||
if (!priceMatch)
|
||||
filledRows.push(Object.assign({}, priceOption));
|
||||
else priceMatch.quantity += priceOption.quantity;
|
||||
}
|
||||
}
|
||||
|
||||
return filledRows;
|
||||
}
|
||||
|
||||
submit() {
|
||||
const filledRows = this.getGroupings();
|
||||
|
||||
try {
|
||||
const hasInvalidGropings = filledRows.some(row =>
|
||||
row.quantity % row.grouping != 0
|
||||
);
|
||||
|
||||
if (filledRows.length <= 0)
|
||||
throw new Error('First you must add some quantity');
|
||||
|
||||
if (hasInvalidGropings)
|
||||
throw new Error(`The amounts doesn't match with the grouping`);
|
||||
|
||||
const params = {
|
||||
orderFk: this.order.id,
|
||||
items: filledRows
|
||||
};
|
||||
this.$http.post(`OrderRows/addToOrder`, params)
|
||||
.then(() => {
|
||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||
this.hide();
|
||||
if (this.card) this.card.reload();
|
||||
});
|
||||
} catch (e) {
|
||||
this.vnApp.showError(this.$t(e.message));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnOrderPricesPopover', {
|
||||
slotTemplate: require('./index.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
order: '<'
|
||||
},
|
||||
require: {
|
||||
card: '?^vnOrderCard'
|
||||
}
|
||||
});
|
|
@ -1,171 +0,0 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Order', () => {
|
||||
describe('Component vnOrderPricesPopover', () => {
|
||||
let controller;
|
||||
let $httpBackend;
|
||||
let orderId = 16;
|
||||
|
||||
beforeEach(ngModule('order'));
|
||||
|
||||
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
const $scope = $rootScope.$new();
|
||||
const $element = angular.element('<vn-order-prices-popover></vn-order-prices-popover>');
|
||||
const $transclude = {
|
||||
$$boundTransclude: {
|
||||
$$slots: []
|
||||
}
|
||||
};
|
||||
controller = $componentController('vnOrderPricesPopover', {$element, $scope, $transclude});
|
||||
controller._prices = [
|
||||
{warehouseFk: 1, grouping: 10, quantity: 0},
|
||||
{warehouseFk: 1, grouping: 100, quantity: 100}
|
||||
];
|
||||
controller.item = {available: 1000};
|
||||
controller.maxQuantity = 1000;
|
||||
controller.order = {id: orderId};
|
||||
}));
|
||||
|
||||
describe('prices() setter', () => {
|
||||
it('should call to the getTotalQuantity() method', () => {
|
||||
controller.getTotalQuantity = jest.fn();
|
||||
|
||||
controller.prices = [
|
||||
{grouping: 10, quantity: 0},
|
||||
{grouping: 100, quantity: 0},
|
||||
{grouping: 1000, quantity: 0},
|
||||
];
|
||||
|
||||
expect(controller.getTotalQuantity).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTotalQuantity()', () => {
|
||||
it('should set the totalQuantity property', () => {
|
||||
controller.getTotalQuantity();
|
||||
|
||||
expect(controller.totalQuantity).toEqual(100);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addQuantity()', () => {
|
||||
it('should call to the getTotalQuantity() method and NOT set the quantity property', () => {
|
||||
jest.spyOn(controller, 'getTotalQuantity');
|
||||
|
||||
controller.prices = [
|
||||
{grouping: 10, quantity: 0},
|
||||
{grouping: 100, quantity: 0},
|
||||
{grouping: 1000, quantity: 1000},
|
||||
];
|
||||
|
||||
const oneThousandGrouping = controller.prices[2];
|
||||
|
||||
expect(oneThousandGrouping.quantity).toEqual(1000);
|
||||
|
||||
controller.addQuantity(oneThousandGrouping);
|
||||
|
||||
expect(controller.getTotalQuantity).toHaveBeenCalledWith();
|
||||
expect(oneThousandGrouping.quantity).toEqual(1000);
|
||||
});
|
||||
|
||||
it('should call to the getTotalQuantity() method and then set the quantity property', () => {
|
||||
jest.spyOn(controller, 'getTotalQuantity');
|
||||
|
||||
const oneHandredGrouping = controller.prices[1];
|
||||
controller.addQuantity(oneHandredGrouping);
|
||||
|
||||
expect(controller.getTotalQuantity).toHaveBeenCalledWith();
|
||||
expect(oneHandredGrouping.quantity).toEqual(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getGroupings()', () => {
|
||||
it('should return a row with the total filled quantity', () => {
|
||||
jest.spyOn(controller, 'getTotalQuantity');
|
||||
|
||||
controller.prices = [
|
||||
{warehouseFk: 1, grouping: 10, quantity: 10},
|
||||
{warehouseFk: 1, grouping: 100, quantity: 100},
|
||||
{warehouseFk: 1, grouping: 1000, quantity: 1000},
|
||||
];
|
||||
|
||||
const rows = controller.getGroupings();
|
||||
const firstRow = rows[0];
|
||||
|
||||
expect(rows.length).toEqual(1);
|
||||
expect(firstRow.quantity).toEqual(1110);
|
||||
});
|
||||
|
||||
it('should return two filled rows with a quantity', () => {
|
||||
jest.spyOn(controller, 'getTotalQuantity');
|
||||
|
||||
controller.prices = [
|
||||
{warehouseFk: 1, grouping: 10, quantity: 10},
|
||||
{warehouseFk: 2, grouping: 10, quantity: 10},
|
||||
{warehouseFk: 1, grouping: 100, quantity: 0},
|
||||
{warehouseFk: 1, grouping: 1000, quantity: 1000},
|
||||
];
|
||||
|
||||
const rows = controller.getGroupings();
|
||||
const firstRow = rows[0];
|
||||
const secondRow = rows[1];
|
||||
|
||||
expect(rows.length).toEqual(2);
|
||||
expect(firstRow.quantity).toEqual(1010);
|
||||
expect(secondRow.quantity).toEqual(10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('submit()', () => {
|
||||
it('should throw an error if none of the rows contains a quantity', () => {
|
||||
jest.spyOn(controller, 'getTotalQuantity');
|
||||
jest.spyOn(controller.vnApp, 'showError');
|
||||
|
||||
controller.prices = [
|
||||
{warehouseFk: 1, grouping: 10, quantity: 0},
|
||||
{warehouseFk: 1, grouping: 100, quantity: 0}
|
||||
];
|
||||
|
||||
controller.submit();
|
||||
|
||||
expect(controller.vnApp.showError).toHaveBeenCalledWith(`First you must add some quantity`);
|
||||
});
|
||||
|
||||
it(`should throw an error if the quantity doesn't match the grouping value`, () => {
|
||||
jest.spyOn(controller, 'getTotalQuantity');
|
||||
jest.spyOn(controller.vnApp, 'showError');
|
||||
|
||||
controller.prices = [
|
||||
{warehouseFk: 1, grouping: 10, quantity: 0},
|
||||
{warehouseFk: 1, grouping: 100, quantity: 1101}
|
||||
];
|
||||
|
||||
controller.submit();
|
||||
|
||||
expect(controller.vnApp.showError).toHaveBeenCalledWith(`The amounts doesn't match with the grouping`);
|
||||
});
|
||||
|
||||
it('should should make an http query and then show a success message', () => {
|
||||
jest.spyOn(controller, 'getTotalQuantity');
|
||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||
|
||||
controller.prices = [
|
||||
{warehouseFk: 1, grouping: 10, quantity: 0},
|
||||
{warehouseFk: 1, grouping: 100, quantity: 100}
|
||||
];
|
||||
|
||||
const params = {
|
||||
orderFk: orderId,
|
||||
items: [{warehouseFk: 1, grouping: 100, quantity: 100}]
|
||||
};
|
||||
|
||||
$httpBackend.expectPOST('OrderRows/addToOrder', params).respond(200);
|
||||
controller.submit();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith(`Data saved!`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
Qty.: Cant.
|
||||
First you must add some quantity: Primero debes agregar alguna cantidad
|
||||
The amounts doesn't match with the grouping: Las cantidades no coinciden con el grouping
|
|
@ -1,18 +0,0 @@
|
|||
@import "variables";
|
||||
|
||||
.vn-order-prices-popover .content {
|
||||
.prices {
|
||||
vn-table {
|
||||
.price-kg {
|
||||
color: $color-font-secondary;
|
||||
font-size: .75rem
|
||||
}
|
||||
.vn-input-number {
|
||||
width: 80px;
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,19 +28,19 @@
|
|||
"abstract": true,
|
||||
"component": "vn-order",
|
||||
"description": "Orders"
|
||||
},
|
||||
},
|
||||
{
|
||||
"url": "/index?q",
|
||||
"state": "order.index",
|
||||
"component": "vn-order-index",
|
||||
"description": "Orders"
|
||||
},
|
||||
},
|
||||
{
|
||||
"url": "/:id",
|
||||
"state": "order.card",
|
||||
"abstract": true,
|
||||
"component": "vn-order-card"
|
||||
},
|
||||
},
|
||||
{
|
||||
"url": "/summary",
|
||||
"state": "order.card.summary",
|
||||
|
@ -49,48 +49,6 @@
|
|||
"params": {
|
||||
"order": "$ctrl.order"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/catalog?q&categoryId&typeId&tagGroups",
|
||||
"state": "order.card.catalog",
|
||||
"component": "vn-order-catalog",
|
||||
"description": "Catalog",
|
||||
"params": {
|
||||
"order": "$ctrl.order"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/volume",
|
||||
"state": "order.card.volume",
|
||||
"component": "vn-order-volume",
|
||||
"description": "Volume",
|
||||
"params": {
|
||||
"order": "$ctrl.order"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/line",
|
||||
"state": "order.card.line",
|
||||
"component": "vn-order-line",
|
||||
"description": "Lines",
|
||||
"params": {
|
||||
"order": "$ctrl.order"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/create?clientFk",
|
||||
"state": "order.create",
|
||||
"component": "vn-order-create",
|
||||
"description": "New order"
|
||||
},
|
||||
{
|
||||
"url": "/basic-data",
|
||||
"state": "order.card.basicData",
|
||||
"component": "vn-order-basic-data",
|
||||
"description": "Basic data",
|
||||
"params": {
|
||||
"order": "$ctrl.order"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
<div class="search-panel">
|
||||
<form ng-submit="$ctrl.onSearch()">
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
vn-one
|
||||
label="General search"
|
||||
ng-model="filter.search"
|
||||
info="Search orders by ticket id"
|
||||
vn-focus>
|
||||
</vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
vn-one
|
||||
label="Client id"
|
||||
ng-model="filter.clientFk">
|
||||
</vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
label="Agency"
|
||||
ng-model="filter.agencyModeFk"
|
||||
url="AgencyModes/isActive"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
<vn-worker-autocomplete
|
||||
vn-one
|
||||
ng-model="filter.workerFk"
|
||||
departments="['VT']"
|
||||
show-field="nickname"
|
||||
label="Sales person">
|
||||
</vn-worker-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-date-picker
|
||||
vn-one
|
||||
label="From landed"
|
||||
ng-model="filter.from">
|
||||
</vn-date-picker>
|
||||
<vn-date-picker
|
||||
vn-one
|
||||
label="To landed"
|
||||
ng-model="filter.to">
|
||||
</vn-date-picker>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
vn-one
|
||||
label="Order id"
|
||||
ng-model="filter.orderFk">
|
||||
</vn-textfield>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
label="Application"
|
||||
ng-model="filter.sourceApp"
|
||||
url="Orders/getSourceValues"
|
||||
show-field="value"
|
||||
value-field="value">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-check
|
||||
vn-one
|
||||
label="My team"
|
||||
ng-model="filter.myTeam"
|
||||
triple-state="true">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
vn-one
|
||||
label="Order confirmed"
|
||||
triple-state="true"
|
||||
ng-model="filter.isConfirmed">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
vn-one
|
||||
label="Show empty"
|
||||
ng-model="filter.showEmpty">
|
||||
</vn-check>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal class="vn-mt-lg">
|
||||
<vn-submit label="Search"></vn-submit>
|
||||
</vn-horizontal>
|
||||
</form>
|
||||
</div>
|
|
@ -1,7 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import SearchPanel from 'core/components/searchbar/search-panel';
|
||||
|
||||
ngModule.vnComponent('vnOrderSearchPanel', {
|
||||
template: require('./index.html'),
|
||||
controller: SearchPanel
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
Order id: Id cesta
|
||||
Client id: Id cliente
|
||||
From landed: Desde f. entrega
|
||||
To landed: Hasta f. entrega
|
||||
To: Hasta
|
||||
Agency: Agencia
|
||||
Application: Aplicación
|
||||
SalesPerson: Comercial
|
||||
Order confirmed: Pedido confirmado
|
||||
Show empty: Mostrar vacías
|
||||
Search orders by ticket id: Buscar pedido por id ticket
|
|
@ -1,131 +0,0 @@
|
|||
<vn-card class="summary">
|
||||
<h5>
|
||||
<a ng-if="::$ctrl.summary.id"
|
||||
vn-tooltip="Go to the order"
|
||||
ui-sref="order.card.summary({id: {{::$ctrl.summary.id}}})"
|
||||
name="goToSummary">
|
||||
<vn-icon-button icon="launch"></vn-icon-button>
|
||||
</a>
|
||||
<span>
|
||||
<span translate>Basket</span> #{{$ctrl.summary.id}} - {{$ctrl.summary.client.name}}
|
||||
({{$ctrl.summary.client.id}})
|
||||
</span>
|
||||
<vn-button
|
||||
disabled="$ctrl.order.isConfirmed"
|
||||
class="flat"
|
||||
style="color: inherit;"
|
||||
label="Confirm"
|
||||
ng-click="$ctrl.save()"
|
||||
vn-tooltip="Confirm lines">
|
||||
</vn-button>
|
||||
</h5>
|
||||
<vn-horizontal class="ticketSummary__data">
|
||||
<vn-one>
|
||||
<vn-label-value label="Id"
|
||||
value="{{$ctrl.summary.id}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value label="Nickname">
|
||||
<span
|
||||
ng-click="clientDescriptor.show($event, $ctrl.summary.clientFk)"
|
||||
class="link">
|
||||
{{$ctrl.summary.address.nickname}}
|
||||
</span>
|
||||
</vn-label-value>
|
||||
<vn-label-value label="Company"
|
||||
value="{{$ctrl.summary.address.companyFk}}">
|
||||
</vn-label-value>
|
||||
<vn-check label="Confirmed" disabled="true"
|
||||
ng-model="$ctrl.summary.isConfirmed">
|
||||
</vn-check>
|
||||
</vn-one>
|
||||
<vn-one>
|
||||
<vn-label-value label="Created"
|
||||
value="{{$ctrl.summary.created | date: 'dd/MM/yyyy HH:mm'}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value label="Confirmed"
|
||||
value="{{$ctrl.summary.confirmed | date: 'dd/MM/yyyy HH:mm'}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value label="Landed"
|
||||
value="{{$ctrl.summary.landed | date: 'dd/MM/yyyy HH:mm'}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value label="Phone">
|
||||
<vn-link-phone
|
||||
phone-number="$ctrl.summary.address.phone"
|
||||
></vn-link-phone>
|
||||
</vn-label-value>
|
||||
<vn-label-value label="Created from"
|
||||
value="{{$ctrl.summary.sourceApp}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value label="Address" no-ellipsize
|
||||
value="{{$ctrl.formattedAddress}}">
|
||||
</vn-label-value>
|
||||
</vn-one>
|
||||
<vn-one>
|
||||
<vn-label-value label="Notes" no-ellipsize
|
||||
value="{{$ctrl.summary.note}}">
|
||||
</vn-label-value>
|
||||
</vn-one>
|
||||
<vn-one class="taxes">
|
||||
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.summary.subTotal | currency: 'EUR':2}}</p>
|
||||
<p><vn-label translate>VAT</vn-label> {{$ctrl.summary.VAT | currency: 'EUR':2}}</p>
|
||||
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.summary.total | currency: 'EUR':2}}</strong></p>
|
||||
</vn-one>
|
||||
<vn-auto>
|
||||
<vn-table>
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th shrink></vn-th>
|
||||
<vn-th shrink>Item</vn-th>
|
||||
<vn-th>Description</vn-th>
|
||||
<vn-th number>Quantity</vn-th>
|
||||
<vn-th number>Price</vn-th>
|
||||
<vn-th number>Amount</vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr ng-repeat="row in $ctrl.summary.rows track by row.id">
|
||||
<vn-td shrink>
|
||||
<vn-icon
|
||||
ng-show="row.visible || row.available"
|
||||
color-main
|
||||
icon="warning"
|
||||
vn-tooltip="Visible: {{::row.visible || 0}} <br> {{::$ctrl.translate.instant('Available')}} {{::row.available || 0}}">
|
||||
</vn-icon>
|
||||
<vn-icon ng-show="row.reserved" icon="icon-reserva"></vn-icon>
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<span
|
||||
ng-click="itemDescriptor.show($event, row.itemFk)"
|
||||
class="link">
|
||||
{{::row.itemFk}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td vn-fetched-tags>
|
||||
<div>
|
||||
<vn-one title="{{::row.item.name}}">{{::row.item.name}}</vn-one>
|
||||
<vn-one ng-if="::row.item.subName">
|
||||
<h3 title="{{::row.item.subName}}">{{::row.item.subName}}</h3>
|
||||
</vn-one>
|
||||
</div>
|
||||
<vn-fetched-tags
|
||||
max-length="6"
|
||||
item="::row.item"
|
||||
tabindex="-1">
|
||||
</vn-fetched-tags>
|
||||
</vn-td>
|
||||
<vn-td number>{{::row.quantity}}</vn-td>
|
||||
<vn-td number>{{::row.price | currency: 'EUR':2}}</vn-td>
|
||||
<vn-td number>{{::row.quantity * row.price | currency: 'EUR':2}}</vn-td>
|
||||
</vn-tr>
|
||||
</vn-tbody>
|
||||
</table>
|
||||
</vn-auto>
|
||||
</vn-horizontal>
|
||||
</vn-card>
|
||||
<vn-item-descriptor-popover
|
||||
vn-id="item-descriptor"
|
||||
warehouse-fk="$ctrl.vnConfig.warehouseFk">
|
||||
</vn-item-descriptor-popover>
|
||||
<vn-client-descriptor-popover
|
||||
vn-id="client-descriptor">
|
||||
</vn-client-descriptor-popover>
|
|
@ -1,41 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Summary from 'salix/components/summary';
|
||||
import './style.scss';
|
||||
|
||||
class Controller extends Summary {
|
||||
setSummary() {
|
||||
this.$http.get(`Orders/${this.order.id}/summary`)
|
||||
.then(res => this.summary = res.data);
|
||||
}
|
||||
|
||||
get formattedAddress() {
|
||||
if (!this.summary) return null;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
save() {
|
||||
this.$http.post(`Orders/${this.order.id}/confirm`).then(() => {
|
||||
this.vnApp.showSuccess(this.$t('Order confirmed'));
|
||||
this.$state.go(`ticket.index`, {
|
||||
q: JSON.stringify({clientFk: this.order.clientFk})
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnOrderSummary', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
order: '<'
|
||||
}
|
||||
});
|
|
@ -1,47 +0,0 @@
|
|||
import './index';
|
||||
|
||||
describe('Order', () => {
|
||||
describe('Component vnOrderSummary', () => {
|
||||
let controller;
|
||||
let $httpBackend;
|
||||
|
||||
beforeEach(ngModule('order'));
|
||||
|
||||
beforeEach(inject(($componentController, _$httpBackend_) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
const $element = angular.element('<vn-order-summary></vn-order-summary>');
|
||||
controller = $componentController('vnOrderSummary', {$element});
|
||||
controller.order = {id: 1};
|
||||
}));
|
||||
|
||||
describe('getSummary()', () => {
|
||||
it('should now perform a GET query and define the summary property', () => {
|
||||
let res = {
|
||||
id: 1,
|
||||
nickname: 'Batman'
|
||||
};
|
||||
$httpBackend.expectGET(`Orders/1/summary`).respond(res);
|
||||
controller.setSummary();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.summary).toEqual(res);
|
||||
});
|
||||
});
|
||||
|
||||
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)');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
@import "./variables";
|
||||
|
||||
vn-order-summary .summary{
|
||||
max-width: $width-lg;
|
||||
|
||||
& > vn-horizontal > vn-one {
|
||||
min-width: 160px;
|
||||
|
||||
&.taxes {
|
||||
border: $border-thin-light;
|
||||
text-align: right;
|
||||
padding: 8px;
|
||||
|
||||
& > p {
|
||||
font-size: 1.2rem;
|
||||
margin: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
<vn-crud-model
|
||||
auto-load="true"
|
||||
vn-id="model"
|
||||
url="OrderRows"
|
||||
filter="::$ctrl.filter"
|
||||
link="{orderFk: $ctrl.$params.id}"
|
||||
limit="20"
|
||||
data="rows"
|
||||
on-data-change="$ctrl.onDataChange()">
|
||||
</vn-crud-model>
|
||||
<mg-ajax path="Orders/{{$ctrl.$params.id}}/getTotalVolume" options="mgEdit"></mg-ajax>
|
||||
<vn-data-viewer model="model" class="header vn-w-lg">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-label-value
|
||||
label="Total"
|
||||
value="{{::edit.model.totalVolume}} M³">
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="Cajas"
|
||||
value="{{::edit.model.totalBoxes | dashIfEmpty}} U">
|
||||
</vn-label-value>
|
||||
</vn-card>
|
||||
<vn-card class="vn-mt-md">
|
||||
<vn-table model="model">
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th shrink field="itemFk" number>Item</vn-th>
|
||||
<vn-th>Description</vn-th>
|
||||
<vn-th shrink field="quantity" number>Quantity</vn-th>
|
||||
<vn-th shrink number>m³ per quantity</vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr ng-repeat="row in rows">
|
||||
<vn-td shrink number>
|
||||
<span
|
||||
ng-click="itemDescriptor.show($event, row.itemFk)"
|
||||
class="link">
|
||||
{{::row.itemFk}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td vn-fetched-tags>
|
||||
<div>
|
||||
<vn-one title="{{::row.item.name}}">{{::row.item.name}}</vn-one>
|
||||
<vn-one ng-if="::row.item.subName">
|
||||
<h3 title="{{::row.item.subName}}">{{::row.item.subName}}</h3>
|
||||
</vn-one>
|
||||
</div>
|
||||
<vn-fetched-tags
|
||||
max-length="6"
|
||||
item="::row.item"
|
||||
tabindex="-1">
|
||||
</vn-fetched-tags>
|
||||
</vn-td>
|
||||
<vn-td shrink number>{{::row.quantity}}</vn-td>
|
||||
<vn-td shrink number>{{::row.volume | number:3}}</vn-td>
|
||||
</vn-tr>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
</vn-card>
|
||||
</vn-data-viewer>
|
||||
<vn-item-descriptor-popover
|
||||
vn-id="item-descriptor"
|
||||
warehouse-fk="$ctrl.vnConfig.warehouseFk">
|
||||
</vn-item-descriptor-popover>
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
import './style.scss';
|
||||
|
||||
class Controller extends Section {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
this.filter = {
|
||||
include: {
|
||||
relation: 'item'
|
||||
},
|
||||
order: 'itemFk'
|
||||
};
|
||||
this.order = {};
|
||||
this.ticketVolumes = [];
|
||||
}
|
||||
|
||||
onDataChange() {
|
||||
this.$http.get(`Orders/${this.$params.id}/getVolumes`)
|
||||
.then(res => {
|
||||
this.$.model.data.forEach(order => {
|
||||
res.data.volumes.forEach(volume => {
|
||||
if (order.itemFk === volume.itemFk)
|
||||
order.volume = volume.volume;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnOrderVolume', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
order: '<'
|
||||
}
|
||||
});
|
|
@ -1,42 +0,0 @@
|
|||
import './index';
|
||||
|
||||
describe('Order', () => {
|
||||
describe('Component vnOrderVolume', () => {
|
||||
let controller;
|
||||
let $httpBackend;
|
||||
let $scope;
|
||||
|
||||
beforeEach(ngModule('order'));
|
||||
|
||||
beforeEach(inject(($componentController, $state, _$httpBackend_, $rootScope) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$scope = $rootScope.$new();
|
||||
$scope.model = {
|
||||
data: [
|
||||
{itemFk: 1},
|
||||
{itemFk: 2}
|
||||
]
|
||||
};
|
||||
|
||||
$state.params.id = 1;
|
||||
const $element = angular.element('<vn-order-volume></vn-order-volume>');
|
||||
controller = $componentController('vnOrderVolume', {$element, $scope});
|
||||
}));
|
||||
|
||||
it('should join the sale volumes to its respective sale', () => {
|
||||
let response = {
|
||||
volumes: [
|
||||
{itemFk: 1, volume: 0.008},
|
||||
{itemFk: 2, volume: 0.003}
|
||||
]
|
||||
};
|
||||
|
||||
$httpBackend.expectGET(`Orders/1/getVolumes`).respond(response);
|
||||
controller.onDataChange();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.$.model.data[0].volume).toBe(0.008);
|
||||
expect(controller.$.model.data[1].volume).toBe(0.003);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
|
||||
@import "./variables";
|
||||
|
||||
vn-order-volume {
|
||||
.header {
|
||||
text-align: right;
|
||||
|
||||
& > div {
|
||||
margin-bottom: $spacing-xs;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue