Client & ticket E2E fixes
gitea/salix/pipeline/head Something is wrong with the build of this commit Details

This commit is contained in:
Juan Ferrer 2020-03-30 17:30:03 +02:00
parent 5909a77587
commit dfb9d32c2a
22 changed files with 202 additions and 223 deletions

View File

@ -228,6 +228,11 @@ let actions = {
await this.waitForTextInField(selector, text); await this.waitForTextInField(selector, text);
}, },
overwrite: async function(selector, text) {
await this.clearInput(selector);
await this.write(selector, text);
},
waitToClick: async function(selector) { waitToClick: async function(selector) {
await this.waitForSelector(selector); await this.waitForSelector(selector);
await this.waitForFunction(checkVisibility, {}, selector); await this.waitForFunction(checkVisibility, {}, selector);

View File

@ -178,12 +178,11 @@ export default {
}, },
clientBalance: { clientBalance: {
balanceButton: 'vn-left-menu a[ui-sref="client.card.balance.index"]',
company: 'vn-client-balance-index vn-autocomplete[ng-model="$ctrl.companyId"]', company: 'vn-client-balance-index vn-autocomplete[ng-model="$ctrl.companyId"]',
newPaymentButton: `vn-float-button`, newPaymentButton: `vn-float-button`,
newPaymentBank: '.vn-dialog.shown vn-autocomplete[ng-model="$ctrl.receipt.bankFk"]', newPaymentBank: '.vn-dialog.shown vn-autocomplete[ng-model="$ctrl.receipt.bankFk"]',
newPaymentAmount: '.vn-dialog.shown vn-input-number[ng-model="$ctrl.receipt.amountPaid"]', newPaymentAmount: '.vn-dialog.shown vn-input-number[ng-model="$ctrl.receipt.amountPaid"]',
saveButton: '.vn-dialog.shown vn-button[label="Save"]', saveButton: '.vn-dialog.shown [response="accept"]',
firstBalanceLine: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(8)' firstBalanceLine: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(8)'
}, },
@ -545,8 +544,8 @@ export default {
firstPrice: 'vn-ticket-service vn-horizontal:nth-child(1) vn-input-number[ng-model="service.price"]', firstPrice: 'vn-ticket-service vn-horizontal:nth-child(1) vn-input-number[ng-model="service.price"]',
firstVatType: 'vn-ticket-service vn-autocomplete[label="Tax class"]', firstVatType: 'vn-ticket-service vn-autocomplete[label="Tax class"]',
fistDeleteServiceButton: 'vn-ticket-service form vn-horizontal:nth-child(1) vn-icon-button[icon="delete"]', fistDeleteServiceButton: 'vn-ticket-service form vn-horizontal:nth-child(1) vn-icon-button[icon="delete"]',
newServiceTypeName: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.newServiceType.name"]', newServiceTypeName: '.vn-dialog.shown vn-textfield[ng-model="newServiceType.name"]',
newServiceTypeExpense: '.vn-dialog.shown vn-autocomplete[ng-model="$ctrl.newServiceType.expenseFk"]', newServiceTypeExpense: '.vn-dialog.shown vn-autocomplete[ng-model="newServiceType.expenseFk"]',
serviceLine: 'vn-ticket-service > form > vn-card > vn-one:nth-child(2) > vn-horizontal', serviceLine: 'vn-ticket-service > form > vn-card > vn-one:nth-child(2) > vn-horizontal',
saveServiceButton: 'button[type=submit]', saveServiceButton: 'button[type=submit]',
saveServiceTypeButton: '.vn-dialog.shown tpl-buttons > button' saveServiceTypeButton: '.vn-dialog.shown tpl-buttons > button'

View File

@ -67,9 +67,7 @@ describe('Client balance path', () => {
it('should create a new payment that sets the balance to positive value', async() => { it('should create a new payment that sets the balance to positive value', async() => {
await page.waitToClick(selectors.clientBalance.newPaymentButton); await page.waitToClick(selectors.clientBalance.newPaymentButton);
await page.waitFor(3000); // didn't manage to make this dynamic to allow clearInput to find the icon clear... :( await page.overwrite(selectors.clientBalance.newPaymentAmount, '100');
await page.clearInput(selectors.clientBalance.newPaymentAmount);
await page.write(selectors.clientBalance.newPaymentAmount, '100');
await page.waitToClick(selectors.clientBalance.saveButton); await page.waitToClick(selectors.clientBalance.saveButton);
let result = await page.waitForLastSnackbar(); let result = await page.waitForLastSnackbar();
@ -85,10 +83,7 @@ describe('Client balance path', () => {
it('should create a new payment that sets the balance back to the original negative value', async() => { it('should create a new payment that sets the balance back to the original negative value', async() => {
await page.waitToClick(selectors.clientBalance.newPaymentButton); await page.waitToClick(selectors.clientBalance.newPaymentButton);
await page.waitFor(3000); // didn't manage to make this dynamic to allow clearInput to find the icon clear... :( await page.overwrite(selectors.clientBalance.newPaymentAmount, '-150');
await page.waitForSelector('.vn-dialog.vn-popup.shown', {visible: true});
await page.clearInput(selectors.clientBalance.newPaymentAmount);
await page.write(selectors.clientBalance.newPaymentAmount, '-150');
await page.waitToClick(selectors.clientBalance.saveButton); await page.waitToClick(selectors.clientBalance.saveButton);
let result = await page.waitForLastSnackbar(); let result = await page.waitForLastSnackbar();
@ -113,7 +108,6 @@ describe('Client balance path', () => {
it('should now search for the user Petter Parker', async() => { it('should now search for the user Petter Parker', async() => {
await page.accessToSearchResult('Petter Parker'); await page.accessToSearchResult('Petter Parker');
await page.waitToClick(selectors.clientBalance.balanceButton);
await page.waitForState('client.card.balance.index'); await page.waitForState('client.card.balance.index');
}); });

View File

@ -81,7 +81,6 @@ describe('Ticket services path', () => {
await page.autocompleteSearch(selectors.ticketService.newServiceTypeExpense, 'Retencion'); await page.autocompleteSearch(selectors.ticketService.newServiceTypeExpense, 'Retencion');
await page.waitToClick(selectors.ticketService.saveServiceTypeButton); await page.waitToClick(selectors.ticketService.saveServiceTypeButton);
await page.write(selectors.ticketService.firstPrice, '999'); await page.write(selectors.ticketService.firstPrice, '999');
await page.waitFor(1000); // time needed for the button to be clickable
await page.waitToClick(selectors.ticketService.saveServiceButton); await page.waitToClick(selectors.ticketService.saveServiceButton);
const result = await page.waitForLastSnackbar(); const result = await page.waitForLastSnackbar();
@ -120,7 +119,6 @@ describe('Ticket services path', () => {
it('should delete the service', async() => { it('should delete the service', async() => {
await page.waitToClick(selectors.ticketService.fistDeleteServiceButton); await page.waitToClick(selectors.ticketService.fistDeleteServiceButton);
await page.waitForNumberOfElements(selectors.ticketService.serviceLine, 0); await page.waitForNumberOfElements(selectors.ticketService.serviceLine, 0);
await page.waitFor(1000); // without this wait it fails to click the save button
await page.waitToClick(selectors.ticketService.saveServiceButton); await page.waitToClick(selectors.ticketService.saveServiceButton);
const result = await page.waitForLastSnackbar(); const result = await page.waitForLastSnackbar();

View File

@ -1,7 +1,7 @@
import selectors from '../../helpers/selectors.js'; import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer'; import getBrowser from '../../helpers/puppeteer';
describe('InvoiceOut descriptor path', () => { fdescribe('InvoiceOut descriptor path', () => {
let browser; let browser;
let page; let page;

View File

@ -6,10 +6,7 @@ import './style.scss';
export default class Confirm extends Dialog { export default class Confirm extends Dialog {
constructor($element, $, $transclude) { constructor($element, $, $transclude) {
super($element, $, $transclude); super($element, $, $transclude);
this.fillSlots(template);
let $template = angular.element(template);
this.fillSlot('body', $template.find('tpl-body'));
this.fillSlot('buttons', $template.find('tpl-buttons'));
} }
} }

View File

@ -21,6 +21,17 @@ export default class Dialog extends Popup {
this.fillDefaultSlot(template); this.fillDefaultSlot(template);
} }
/**
* Fills the dialog slots, it is intended to be used by child classes.
*
* @param {String} template The HTML template string
*/
fillSlots(template) {
let $template = angular.element(template);
this.fillSlot('body', $template.find('tpl-body'));
this.fillSlot('buttons', $template.find('tpl-buttons'));
}
/** /**
* Shows the dialog and optionally registers a handler for the response. * Shows the dialog and optionally registers a handler for the response.
* *
@ -68,7 +79,17 @@ export default class Dialog extends Popup {
respond(response) { respond(response) {
if (!this.shown) if (!this.shown)
return this.$q.resolve(); return this.$q.resolve();
return this.responseHandler(response);
}
/**
* The default response handler, it can be overriden by child classes to
* add custom logic.
*
* @param {String} response The response code
* @return {Boolean} The response handler return
*/
responseHandler(response) {
let handlerArgs = { let handlerArgs = {
$response: response, $response: response,
$data: this.data $data: this.data

View File

@ -79,6 +79,9 @@ vn-layout {
& > .main-view { & > .main-view {
padding-right: $menu-width; padding-right: $menu-width;
} }
[fixed-bottom-right] {
right: 4em + $menu-width;
}
} }
& > .main-view { & > .main-view {
padding-top: $topbar-height; padding-top: $topbar-height;
@ -89,6 +92,11 @@ vn-layout {
padding: $spacing-md; padding: $spacing-md;
box-sizing: border-box box-sizing: border-box
} }
[fixed-bottom-right] {
position: fixed;
bottom: 2em;
right: 2em;
}
&.ng-enter { &.ng-enter {
vn-side-menu { vn-side-menu {
opacity: 0; opacity: 0;
@ -124,6 +132,9 @@ vn-layout {
& > .main-view { & > .main-view {
padding-right: 0; padding-right: 0;
} }
[fixed-bottom-right] {
right: 2em;
}
} }
ui-view > * { ui-view > * {
padding-left: 0; padding-left: 0;

View File

@ -73,11 +73,6 @@ vn-bg-title {
padding: $spacing-lg; padding: $spacing-lg;
max-width: 1000px; max-width: 1000px;
} }
html [fixed-bottom-right] {
position: fixed;
bottom: 2em;
right: 2em;
}
.list > vn-none { .list > vn-none {
min-width: 60px; min-width: 60px;
} }

View File

@ -1,11 +1,6 @@
<vn-dialog <div>
vn-id="dialog"
class="modal-form">
<tpl-body> <tpl-body>
<mg-ajax path="receipts" options="vnPost"></mg-ajax> <h6 translate>New payment</h6>
<vn-horizontal class="header">
<h5><span translate>New payment</span></h5>
</vn-horizontal>
<div class="vn-pa-md"> <div class="vn-pa-md">
<vn-horizontal> <vn-horizontal>
<vn-date-picker <vn-date-picker
@ -13,7 +8,8 @@
label="Date" label="Date"
ng-model="$ctrl.receipt.payed"> ng-model="$ctrl.receipt.payed">
</vn-date-picker> </vn-date-picker>
<vn-autocomplete vn-one <vn-autocomplete
vn-one
url="Companies" url="Companies"
label="Company" label="Company"
show-field="code" show-field="code"
@ -22,7 +18,8 @@
</vn-autocomplete> </vn-autocomplete>
</vn-horizontal> </vn-horizontal>
<vn-horizontal> <vn-horizontal>
<vn-autocomplete vn-one <vn-autocomplete
vn-one
url="Banks" url="Banks"
label="Bank" label="Bank"
show-field="bank" show-field="bank"
@ -39,9 +36,9 @@
</vn-input-number> </vn-input-number>
</vn-horizontal> </vn-horizontal>
</div> </div>
<vn-horizontal class="vn-ma-md buttons-bar">
<vn-button vn-one label="Save" ng-click="$ctrl.save()"></vn-button>
<vn-button vn-one ng-click="$ctrl.hide()" label="Cancel"></vn-button>
</vn-horizontal>
</tpl-body> </tpl-body>
</vn-dialog> <tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate vn-focus>Accept</button>
</tpl-buttons>
</div>

View File

@ -1,10 +1,12 @@
import ngModule from '../../module'; import ngModule from '../../module';
import Section from 'salix/components/section'; import Dialog from 'core/components/dialog';
import './style.scss'; import template from './index.html';
class Controller extends Dialog {
constructor($element, $, $transclude) {
super($element, $, $transclude);
this.fillSlots(template);
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.receipt = { this.receipt = {
payed: new Date(), payed: new Date(),
clientFk: this.$params.id, clientFk: this.$params.id,
@ -51,46 +53,35 @@ class Controller extends Section {
} }
getAmountPaid() { getAmountPaid() {
let filter = { const filter = {
where: { where: {
clientFk: this.$params.id, clientFk: this.$params.id,
companyFk: this.receipt.companyFk companyFk: this.receipt.companyFk
} }
}; };
let query = `ClientRisks?filter=${JSON.stringify(filter)}`; this.$http.get(`ClientRisks`, {filter}).then(res => {
this.$http.get(query).then(res => {
this.receipt.amountPaid = (res.data.length && res.data[0].amount) || null; this.receipt.amountPaid = (res.data.length && res.data[0].amount) || null;
}); });
} }
show() { responseHandler(response) {
this.$.dialog.show(); if (response !== 'accept')
} return super.responseHandler(response);
hide() { return this.$http.post(`Receipts`, this.receipt)
this.$.dialog.hide(); .then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
} .then(() => super.responseHandler(response));
save() {
let query = `receipts`;
this.$http.post(query, this.receipt).then(() => {
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
this.hide();
if (this.onResponse)
this.onResponse();
});
} }
} }
ngModule.component('vnClientBalanceCreate', { ngModule.vnComponent('vnClientBalanceCreate', {
template: require('./index.html'),
controller: Controller, controller: Controller,
transclude: true,
bindings: { bindings: {
payed: '<?', payed: '<?',
bankFk: '<?', bankFk: '<?',
amountPaid: '<?', amountPaid: '<?',
onResponse: '&?',
companyFk: '<?', companyFk: '<?',
description: '<?', description: '<?',
clientFk: '<?' clientFk: '<?'

View File

@ -1,3 +0,0 @@
vn-horizontal.buttons-bar{
text-align: center;
}

View File

@ -10,35 +10,31 @@
filter="$ctrl.filter" filter="$ctrl.filter"
data="$ctrl.clientRisks"> data="$ctrl.clientRisks">
</vn-crud-model> </vn-crud-model>
<vn-side-menu side="right">
<div class="vn-pa-md">
<vn-autocomplete
vn-one
vn-id="company"
ng-model="$ctrl.companyId"
url="Companies"
show-field="code"
value-field="id"
label="Company">
</vn-autocomplete>
<div
class="totalBox"
style="text-align: center;"
ng-if="$ctrl.clientRisks.length">
<h6 translate>Total by company</h6>
<vn-label-value
ng-repeat="riskByCompany in $ctrl.clientRisks"
label="{{riskByCompany.company.code}}"
value="{{riskByCompany.amount | currency: 'EUR':2}}">
</vn-label-value>
</div>
</div>
</vn-side-menu>
<div class="vn-w-lg"> <div class="vn-w-lg">
<vn-card class="vn-pa-md vn-mb-md">
<vn-horizontal
style="align-items: center;">
<vn-one></vn-one>
<vn-one>
<vn-autocomplete
vn-id="company"
class="dense"
ng-model="$ctrl.companyId"
url="Companies"
show-field="code"
value-field="id"
label="Company">
</vn-autocomplete>
</vn-one>
<vn-one>
<div class="totalBox" ng-if="$ctrl.clientRisks.length">
<h6 translate>Total by company</h6>
<vn-auto ng-repeat="riskByCompany in $ctrl.clientRisks">
<vn-label-value
label="{{riskByCompany.company.code}}"
value="{{riskByCompany.amount | currency: 'EUR':2}}">
</vn-label-value>
</vn-auto>
</div>
</vn-one>
</vn-horizontal>
</vn-card>
<vn-data-viewer model="model"> <vn-data-viewer model="model">
<vn-card> <vn-card>
<vn-table model="model"> <vn-table model="model">
@ -117,10 +113,12 @@
vn-tooltip="New payment" vn-tooltip="New payment"
vn-bind="+" vn-bind="+"
fixed-bottom-right fixed-bottom-right
ng-click="$ctrl.openCreateDialog()"> ng-click="balanceCreate.show()">
</vn-float-button> </vn-float-button>
<vn-client-balance-create <vn-client-balance-create
vn-id="balanceCreateDialog"> vn-id="balance-create"
on-accept="$ctrl.getData()"
company-fk="$ctrl.companyId">
</vn-client-balance-create> </vn-client-balance-create>
<vn-worker-descriptor-popover <vn-worker-descriptor-popover
vn-id="workerDescriptor" vn-id="workerDescriptor"

View File

@ -1,6 +1,5 @@
import ngModule from '../../module'; import ngModule from '../../module';
import Section from 'salix/components/section'; import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section { class Controller extends Section {
constructor($element, $) { constructor($element, $) {
@ -74,12 +73,6 @@ class Controller extends Section {
}); });
} }
openCreateDialog() {
this.$.balanceCreateDialog.companyFk = this.companyId;
this.$.balanceCreateDialog.onResponse = () => this.getData();
this.$.balanceCreateDialog.show();
}
showWorkerDescriptor(event, workerFk) { showWorkerDescriptor(event, workerFk) {
if (event.defaultPrevented) return; if (event.defaultPrevented) return;

View File

@ -1,10 +0,0 @@
@import "./variables";
vn-client-balance-index {
.totalBox {
border: $border-thin-light;
text-align: left;
float: right
}
}

View File

@ -57,13 +57,13 @@ class Controller extends ModuleCard {
], ],
}; };
this.$http.get(`Tickets/${this.$params.id}`, {filter}) return this.$http.get(`Tickets/${this.$params.id}`, {filter})
.then(res => this.onData(res.data)); .then(res => this.onData(res.data));
} }
onData(data) { onData(data) {
this.ticket = data; this.ticket = data;
this.$http.get(`Clients/${data.client.id}/getDebt`) return this.$http.get(`Clients/${data.client.id}/getDebt`)
.then(res => this.ticket.client.debt = res.data.debt); .then(res => this.ticket.client.debt = res.data.debt);
} }
} }

View File

@ -153,20 +153,14 @@
</vn-confirm> </vn-confirm>
<vn-dialog <vn-dialog
vn-id="changeShippedDialog" vn-id="changeShippedDialog"
on-response="$ctrl.changeShipped($response)"> on-accept="$ctrl.changeShipped()">
<tpl-body> <tpl-body>
<div> <h6 translate>Change shipped hour</h6>
<h5 style="text-align: center"> <vn-input-time
<span translate>Change shipped hour</span> ng-model="$ctrl.newShipped"
</h5> label="Shipped hour"
<vn-input-time vn-focus>
vn-id="newShipped" </vn-input-time>
vn-one
ng-model="$ctrl.newShipped"
label="Shipped hour"
vn-focus>
</vn-input-time>
</div>
</tpl-body> </tpl-body>
<tpl-buttons> <tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/> <input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>

View File

@ -9,38 +9,45 @@ class Controller extends Component {
this.moreOptions = [ this.moreOptions = [
{ {
name: 'Add turn', name: 'Add turn',
acl: 'buyer', callback: this.showAddTurnDialog,
callback: this.showAddTurnDialog acl: 'buyer'
}, }, {
{name: 'Show Delivery Note', callback: this.showDeliveryNote}, name: 'Show Delivery Note',
{name: 'Send Delivery Note', callback: this.confirmDeliveryNote}, callback: this.showDeliveryNote
{name: 'Delete ticket', callback: this.showDeleteTicketDialog}, }, {
{name: 'Change shipped hour', callback: this.showChangeShipped}, name: 'Send Delivery Note',
{name: 'SMS Pending payment', callback: this.sendPaymentSms}, callback: this.confirmDeliveryNote
{name: 'SMS Minimum import', callback: this.sendImportSms}, }, {
{ name: 'Delete ticket',
callback: this.showDeleteTicketDialog
}, {
name: 'Change shipped hour',
callback: this.showChangeShipped
}, {
name: 'SMS Pending payment',
callback: this.sendPaymentSms
}, {
name: 'SMS Minimum import',
callback: this.sendImportSms
}, {
name: 'Add stowaway', name: 'Add stowaway',
callback: this.showAddStowaway, callback: this.showAddStowaway,
show: () => this.canShowStowaway show: () => this.canShowStowaway
}, }, {
{
name: 'Delete stowaway', name: 'Delete stowaway',
callback: this.showDeleteStowaway, callback: this.showDeleteStowaway,
show: () => this.shouldShowDeleteStowaway() show: () => this.shouldShowDeleteStowaway()
}, }, {
{
name: 'Make invoice', name: 'Make invoice',
acl: 'invoicing',
callback: this.showMakeInvoiceDialog, callback: this.showMakeInvoiceDialog,
show: () => !this.hasInvoice() show: () => !this.hasInvoice(),
}, acl: 'invoicing'
{ }, {
name: 'Regenerate invoice', name: 'Regenerate invoice',
acl: 'invoicing',
callback: this.showRegenerateInvoiceDialog, callback: this.showRegenerateInvoiceDialog,
show: () => this.hasInvoice() show: () => this.hasInvoice(),
}, acl: 'invoicing'
{ }, {
name: 'Recalculate components', name: 'Recalculate components',
callback: this.comfirmRecalculateComponents, callback: this.comfirmRecalculateComponents,
show: () => this.isEditable show: () => this.isEditable
@ -98,22 +105,20 @@ class Controller extends Component {
showChangeShipped() { showChangeShipped() {
if (!this.isEditable) { if (!this.isEditable) {
this.vnApp.showError(this.$translate.instant(`This ticket can't be modified`)); this.vnApp.showError(this.$t(`This ticket can't be modified`));
return; return;
} }
this.newShipped = this.ticket.shipped; this.newShipped = this.ticket.shipped;
this.$.changeShippedDialog.show(); this.$.changeShippedDialog.show();
} }
changeShipped(response) { changeShipped() {
if (response === 'accept') { let data = {shipped: this.newShipped};
let data = {shipped: this.newShipped}; let query = `Tickets/${this.ticket.id}/updateEditableTicket`;
let query = `Tickets/${this.ticket.id}/updateEditableTicket`; this.$http.post(query, data)
this.$http.post(query, data).then(() => { .then(() => this.cardReload())
this.vnApp.showSuccess(this.$translate.instant('Shipped hour updated')); .then(() => this.vnApp.showSuccess(this.$t('Shipped hour updated')));
this.cardReload();
});
}
} }
isTicketModule() { isTicketModule() {
@ -161,13 +166,13 @@ class Controller extends Component {
let params = {ticketFk: this.ticket.id, weekDay: day}; let params = {ticketFk: this.ticket.id, weekDay: day};
this.$http.patch(`TicketWeeklies`, params).then(() => { this.$http.patch(`TicketWeeklies`, params).then(() => {
this.$.addTurn.hide(); this.$.addTurn.hide();
this.vnApp.showSuccess(this.$translate.instant('Data saved!')); this.vnApp.showSuccess(this.$t('Data saved!'));
}); });
} }
showDeleteTicketDialog() { showDeleteTicketDialog() {
if (!this.isEditable) { if (!this.isEditable) {
this.vnApp.showError(this.$translate.instant('This ticket cant be deleted')); this.vnApp.showError(this.$t('This ticket cant be deleted'));
return; return;
} }
@ -179,7 +184,7 @@ class Controller extends Component {
const query = `Tickets/${this.ticket.id}/setDeleted`; const query = `Tickets/${this.ticket.id}/setDeleted`;
this.$http.post(query).then(() => { this.$http.post(query).then(() => {
this.$state.go('ticket.index'); this.$state.go('ticket.index');
this.vnApp.showSuccess(this.$translate.instant('Ticket deleted')); this.vnApp.showSuccess(this.$t('Ticket deleted'));
}); });
} }
} }
@ -213,7 +218,7 @@ class Controller extends Component {
deleteStowaway() { deleteStowaway() {
const query = `Tickets/${this.ticket.id}/deleteStowaway`; const query = `Tickets/${this.ticket.id}/deleteStowaway`;
this.$http.post(query).then(res => { this.$http.post(query).then(res => {
this.vnApp.showSuccess(this.$translate.instant('Data saved!')); this.vnApp.showSuccess(this.$t('Data saved!'));
this.cardReload(); this.cardReload();
}); });
} }
@ -236,7 +241,7 @@ class Controller extends Component {
ticketId: this.ticket.id ticketId: this.ticket.id
}; };
this.$http.get(`email/delivery-note`, {params}).then( this.$http.get(`email/delivery-note`, {params}).then(
() => this.vnApp.showMessage(this.$translate.instant('Notification sent!')) () => this.vnApp.showMessage(this.$t('Notification sent!'))
); );
} }
@ -245,13 +250,13 @@ class Controller extends Component {
ticketId: this.ticket.id, ticketId: this.ticket.id,
created: this.ticket.created created: this.ticket.created
}; };
const message = this.$params.message || this.$translate.instant('Minimum is needed', params); const message = this.$params.message || this.$t('Minimum is needed', params);
this.newSMS = {message}; this.newSMS = {message};
this.showSMSDialog(); this.showSMSDialog();
} }
sendPaymentSms() { sendPaymentSms() {
const message = this.$params.message || this.$translate.instant('Make a payment'); const message = this.$params.message || this.$t('Make a payment');
this.newSMS = {message}; this.newSMS = {message};
this.showSMSDialog(); this.showSMSDialog();
} }
@ -284,7 +289,7 @@ class Controller extends Component {
if (response === 'accept') { if (response === 'accept') {
const query = `Tickets/${this.ticket.id}/makeInvoice`; const query = `Tickets/${this.ticket.id}/makeInvoice`;
this.$http.post(query).then(() => { this.$http.post(query).then(() => {
this.vnApp.showSuccess(this.$translate.instant('Ticket invoiced')); this.vnApp.showSuccess(this.$t('Ticket invoiced'));
this.$state.reload(); this.$state.reload();
}); });
} }
@ -308,7 +313,7 @@ class Controller extends Component {
const invoiceId = this.ticket.invoiceOut.id; const invoiceId = this.ticket.invoiceOut.id;
const query = `InvoiceOuts/${invoiceId}/regenerate`; const query = `InvoiceOuts/${invoiceId}/regenerate`;
this.$http.post(query).then(() => { this.$http.post(query).then(() => {
const snackbarMessage = this.$translate.instant( const snackbarMessage = this.$t(
`Invoice sent for a regeneration, will be available in a few minutes`); `Invoice sent for a regeneration, will be available in a few minutes`);
this.vnApp.showSuccess(snackbarMessage); this.vnApp.showSuccess(snackbarMessage);
}); });
@ -341,7 +346,7 @@ class Controller extends Component {
recalculateComponents() { recalculateComponents() {
const query = `Tickets/${this.ticket.id}/recalculateComponents`; const query = `Tickets/${this.ticket.id}/recalculateComponents`;
this.$http.post(query).then(res => { this.$http.post(query).then(res => {
this.vnApp.showSuccess(this.$translate.instant('Data saved!')); this.vnApp.showSuccess(this.$t('Data saved!'));
}); });
} }
} }

View File

@ -157,8 +157,7 @@ describe('Ticket Component vnTicketDescriptor', () => {
jest.spyOn(controller.vnApp, 'showSuccess'); jest.spyOn(controller.vnApp, 'showSuccess');
jest.spyOn(controller, 'cardReload'); jest.spyOn(controller, 'cardReload');
$httpBackend.when('POST', 'Tickets/12/updateEditableTicket').respond(); $httpBackend.expectRoute('POST', 'Tickets/:id/updateEditableTicket').respond();
$httpBackend.expect('POST', 'Tickets/12/updateEditableTicket').respond();
controller.changeShipped('accept'); controller.changeShipped('accept');
$httpBackend.flush(); $httpBackend.flush();

View File

@ -22,7 +22,7 @@
<vn-icon-button <vn-icon-button
icon="add_circle" icon="add_circle"
vn-tooltip="New service type" vn-tooltip="New service type"
ng-click="$ctrl.newServiceTypeDialog($index, $event)" ng-click="$ctrl.onNewServiceTypeClick(service, $event)"
vn-acl="administrative" vn-acl="administrative"
style="padding: 0;"> style="padding: 0;">
</vn-icon-button> </vn-icon-button>
@ -72,26 +72,28 @@
<!-- Create service type dialog --> <!-- Create service type dialog -->
<vn-dialog class="edit" <vn-dialog class="edit"
vn-id="createServiceTypeDialog" vn-id="newServiceTypeDialog"
on-open="$ctrl.onNewServiceTypeOpen()" on-accept="$ctrl.onNewServiceTypeAccept($data)"
on-response="$ctrl.onNewServiceTypeResponse($response)"> on-close="newServiceType = null">
<tpl-body> <tpl-body>
<h5 class="vn-py-sm" translate>New service type</h5> <h5 class="vn-py-sm" translate>New service type</h5>
<vn-horizontal> <vn-horizontal>
<vn-textfield <vn-textfield
vn-one vn-one
label="Description" label="Description"
ng-model="$ctrl.newServiceType.name" ng-model="newServiceType.name"
vn-focus> vn-focus>
</vn-textfield> </vn-textfield>
</vn-horizontal> </vn-horizontal>
<vn-horizontal> <vn-horizontal>
<vn-autocomplete vn-one vn-focus <vn-autocomplete
vn-one
vn-focus
url="Expenses" url="Expenses"
label="Expense" label="Expense"
show-field="name" show-field="name"
value-field="id" value-field="id"
ng-model="$ctrl.newServiceType.expenseFk"> ng-model="newServiceType.expenseFk">
</vn-autocomplete> </vn-autocomplete>
</vn-horizontal> </vn-horizontal>
</tpl-body> </tpl-body>

View File

@ -17,43 +17,33 @@ class Controller extends Section {
} }
add() { add() {
if (this.defaultTaxClass) { this.$.model.insert({
this.$.model.insert({ taxClassFk: this.defaultTaxClass.id,
taxClassFk: this.defaultTaxClass.id, quantity: 1,
quantity: 1, ticketFk: this.$params.id
ticketFk: this.$params.id });
});
}
} }
onNewServiceTypeOpen() { onNewServiceTypeClick(service, event) {
this.newServiceType = {};
}
newServiceTypeDialog(elementIndex, event) {
event.preventDefault(); event.preventDefault();
this.$.createServiceTypeDialog.show(); this.$.newServiceType = {};
this.currentServiceIndex = elementIndex; this.$.newServiceTypeDialog.show(service);
} }
onNewServiceTypeResponse(response) { onNewServiceTypeAccept(service) {
if (response == 'accept') { if (!this.$.newServiceType.name)
if (!this.newServiceType.name) throw new UserError(`Name can't be empty`);
throw new UserError(`Name can't be empty`);
this.$http.post(`TicketServiceTypes`, this.newServiceType).then(response => { return this.$http.post(`TicketServiceTypes`, this.$.newServiceType)
this.services[this.currentServiceIndex].ticketServiceTypeFk = response.data.id; .then(res => service.ticketServiceTypeFk = res.data.id);
});
}
} }
onSubmit() { onSubmit() {
this.$.watcher.check(); this.$.watcher.check();
this.$.model.save().then(() => { this.$.model.save()
this.$.watcher.notifySaved(); .then(() => this.$.model.refresh())
this.$.model.refresh(); .then(() => this.$.watcher.notifySaved());
});
} }
} }

View File

@ -3,17 +3,16 @@ import './index.js';
describe('Ticket component vnTicketService', () => { describe('Ticket component vnTicketService', () => {
let controller; let controller;
let $httpBackend; let $httpBackend;
let $httpParamSerializer;
let $scope; let $scope;
let $element; let $element;
beforeEach(ngModule('ticket')); beforeEach(ngModule('ticket'));
beforeEach(angular.mock.inject(($componentController, _$httpBackend_, _$httpParamSerializer_, $rootScope) => { beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $rootScope) => {
$element = angular.element(`<div></div>`);
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
$scope = $rootScope.$new(); $scope = $rootScope.$new();
$element = angular.element(`<div></div>`);
controller = $componentController('vnTicketService', {$scope, $element}); controller = $componentController('vnTicketService', {$scope, $element});
})); }));
@ -32,13 +31,13 @@ describe('Ticket component vnTicketService', () => {
}); });
}); });
describe('onNewServiceTypeResponse', () => { describe('onNewServiceTypeAccept', () => {
it(`should throw an error if the new service description is empty`, () => { it(`should throw an error if the new service type name is empty`, () => {
controller.newServiceType = {name: undefined}; $scope.newServiceType = {};
let error; let error;
try { try {
controller.onNewServiceTypeResponse('accept'); controller.onNewServiceTypeAccept({});
} catch (e) { } catch (e) {
error = e.message; error = e.message;
} }
@ -47,18 +46,22 @@ describe('Ticket component vnTicketService', () => {
}); });
it('should set the description of the selected service upon service type creation', () => { it('should set the description of the selected service upon service type creation', () => {
controller.services = [ const service = {
{id: 1, description: 'not too great service'} id: 1,
]; quantity: 10
};
$scope.newServiceType = {
name: 'Totally new stuff'
};
controller.newServiceType = {name: 'totally new stuff'}; $httpBackend.when('POST', 'TicketServiceTypes').respond({
controller.currentServiceIndex = 0; id: 4001,
name: 'Totally new stuff'
$httpBackend.when('POST', 'TicketServiceTypes').respond({id: 4001, name: 'totally new stuff'}); });
controller.onNewServiceTypeResponse('accept'); controller.onNewServiceTypeAccept(service);
$httpBackend.flush(); $httpBackend.flush();
expect(controller.services[0].ticketServiceTypeFk).toEqual(4001); expect(service.ticketServiceTypeFk).toEqual(4001);
}); });
}); });
}); });