feat(invoiceOut): separate descriptor
gitea/salix/pipeline/head There was a failure building this commit Details

This commit is contained in:
Alex Moreno 2021-11-09 15:10:43 +01:00
parent 127077b985
commit a52c06057f
9 changed files with 413 additions and 196 deletions

View File

@ -0,0 +1,137 @@
<vn-icon-button
icon="more_vert"
vn-popover="menu">
</vn-icon-button>
<vn-menu vn-id="menu">
<vn-list>
<vn-item class="dropdown"
vn-click-stop="showInvoiceMenu.show($event, 'left')"
name="showInvoicePdf"
translate>
Show invoice...
<vn-menu vn-id="showInvoiceMenu">
<vn-list>
<a class="vn-item"
href="api/InvoiceOuts/{{$ctrl.id}}/download?access_token={{$ctrl.vnToken.token}}"
target="_blank"
name="showInvoicePdf"
translate>
Show as PDF
</a>
<vn-item
ng-click="$ctrl.showCsvInvoice()"
translate>
Show as CSV
</vn-item>
</vn-list>
</vn-menu>
</vn-item>
<vn-item class="dropdown"
vn-click-stop="sendInvoiceMenu.show($event, 'left')"
name="sendInvoice"
translate>
Send invoice...
<vn-menu vn-id="sendInvoiceMenu">
<vn-list>
<vn-item
ng-click="sendPdfConfirmation.show({email: $ctrl.invoiceOut.client.email})"
translate>
Send PDF
</vn-item>
<vn-item
ng-click="sendCsvConfirmation.show({email: $ctrl.invoiceOut.client.email})"
translate>
Send CSV
</vn-item>
</vn-list>
</vn-menu>
</vn-item>
<vn-item
ng-click="deleteConfirmation.show()"
vn-acl="invoicing"
vn-acl-action="remove"
name="deleteInvoice"
translate>
Delete Invoice
</vn-item>
<vn-item
ng-click="bookConfirmation.show()"
vn-acl="invoicing"
vn-acl-action="remove"
name="bookInvoice"
translate>
Book invoice
</vn-item>
<vn-item
ng-click="createInvoicePdfConfirmation.show()"
ng-show="$ctrl.hasInvoicing || !$ctrl.invoiceOut.hasPdf"
name="regenerateInvoice"
translate>
{{!$ctrl.invoiceOut.hasPdf ? 'Generate PDF invoice': 'Regenerate PDF invoice'}}
</vn-item>
<vn-item
ng-click="$ctrl.showExportationLetter()"
ng-show="$ctrl.invoiceOut.serial == 'E'"
translate>
Show CIES letter
</vn-item>
</vn-list>
</vn-menu>
<vn-confirm
vn-id="deleteConfirmation"
on-accept="$ctrl.deleteInvoiceOut()"
question="Are you sure you want to delete this invoice?">
</vn-confirm>
<vn-confirm
vn-id="bookConfirmation"
on-accept="$ctrl.bookInvoiceOut()"
question="Are you sure you want to book this invoice?">
</vn-confirm>
<vn-client-descriptor-popover
vn-id="clientDescriptor">
</vn-client-descriptor-popover>
<!-- Create invoice PDF confirmation dialog -->
<vn-confirm
vn-id="createInvoicePdfConfirmation"
on-accept="$ctrl.createPdfInvoice()"
question="Are you sure you want to generate/regenerate the PDF invoice?"
message="Generate PDF invoice document">
</vn-confirm>
<!-- Send PDF invoice confirmation popup -->
<vn-dialog
vn-id="sendPdfConfirmation"
on-accept="$ctrl.sendPdfInvoice($data)"
message="Send PDF invoice">
<tpl-body>
<span translate>Are you sure you want to send it?</span>
<vn-textfield vn-one
ng-model="sendPdfConfirmation.data.email">
</vn-textfield>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Confirm</button>
</tpl-buttons>
</vn-dialog>
<!-- Send CSV invoice confirmation popup -->
<vn-dialog
vn-id="sendCsvConfirmation"
on-accept="$ctrl.sendCsvInvoice($data)"
message="Send CSV invoice">
<tpl-body>
<span translate>Are you sure you want to send it?</span>
<vn-textfield vn-one
ng-model="sendCsvConfirmation.data.email">
</vn-textfield>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Confirm</button>
</tpl-buttons>
</vn-dialog>

View File

@ -0,0 +1,111 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $) {
super($element, $);
}
get invoiceOut() {
return this._invoiceOut;
}
set invoiceOut(value) {
this._invoiceOut = value;
}
loadData() {
const filter = {
include: [
{
relation: 'company',
scope: {
fields: ['id', 'code']
}
}, {
relation: 'client',
scope: {
fields: ['id', 'name', 'email']
}
}
]
};
return this.getData(`InvoiceOuts/${this.invoiceOut.id}`, {filter})
.then(res => this.invoice = res.data);
}
reload() {
return this.loadData().then(() => {
if (this.parentReload)
this.parentReload();
});
}
cardReload() {
// Prevents error when not defined
}
deleteInvoiceOut() {
return this.$http.post(`InvoiceOuts/${this.invoiceOut.id}/delete`)
.then(() => this.$state.go('invoiceOut.index'))
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut deleted')))
.then(() => this.close());
}
bookInvoiceOut() {
return this.$http.post(`InvoiceOuts/${this.invoiceOut.ref}/book`)
.then(() => this.$state.reload())
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut booked')));
}
createPdfInvoice() {
const invoiceId = this.invoiceOut.id;
return this.$http.post(`InvoiceOuts/${invoiceId}/createPdf`)
.then(() => this.reload())
.then(() => {
const snackbarMessage = this.$t(
`The invoice PDF document has been regenerated`);
this.vnApp.showSuccess(snackbarMessage);
});
}
showCsvInvoice() {
this.vnReport.showCsv('invoice', {
recipientId: this.invoiceOut.client.id,
invoiceId: this.id,
});
}
sendPdfInvoice($data) {
return this.vnEmail.send('invoice', {
recipientId: this.invoiceOut.client.id,
recipient: $data.email,
invoiceId: this.id
});
}
sendCsvInvoice($data) {
return this.vnEmail.sendCsv('invoice', {
recipientId: this.invoiceOut.client.id,
recipient: $data.email,
invoiceId: this.id
});
}
showExportationLetter() {
this.vnReport.show('exportation', {
recipientId: this.invoiceOut.client.id,
invoiceId: this.id,
});
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnInvoiceOutDescriptorMenu', {
template: require('./index.html'),
controller: Controller,
bindings: {
invoiceOut: '<',
}
});

View File

@ -0,0 +1,109 @@
import './index';
describe('vnInvoiceOutDescriptor', () => {
let controller;
let $httpBackend;
let $httpParamSerializer;
const invoiceOut = {
id: 1,
client: {id: 1101}
};
beforeEach(ngModule('invoiceOut'));
beforeEach(inject(($componentController, _$httpParamSerializer_, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
controller = $componentController('vnInvoiceOutDescriptor', {$element: null});
}));
describe('loadData()', () => {
it(`should perform a get query to store the invoice in data into the controller`, () => {
const id = 1;
const response = {id: 1};
$httpBackend.expectGET(`InvoiceOuts/${id}`).respond(response);
controller.id = id;
$httpBackend.flush();
expect(controller.invoiceOut).toEqual(response);
});
});
describe('createPdfInvoice()', () => {
it('should make a query to the createPdf() endpoint and show a success snackbar', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
controller.invoiceOut = invoiceOut;
$httpBackend.whenGET(`InvoiceOuts/${invoiceOut.id}`).respond();
$httpBackend.expectPOST(`InvoiceOuts/${invoiceOut.id}/createPdf`).respond();
controller.createPdfInvoice();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('showCsvInvoice()', () => {
it('should make a query to the csv invoice download endpoint and show a message snackbar', () => {
jest.spyOn(window, 'open').mockReturnThis();
controller.invoiceOut = invoiceOut;
const expectedParams = {
invoiceId: invoiceOut.id,
recipientId: invoiceOut.client.id
};
const serializedParams = $httpParamSerializer(expectedParams);
const expectedPath = `api/csv/invoice/download?${serializedParams}`;
controller.showCsvInvoice();
expect(window.open).toHaveBeenCalledWith(expectedPath);
});
});
describe('sendPdfInvoice()', () => {
it('should make a query to the email invoice endpoint and show a message snackbar', () => {
jest.spyOn(controller.vnApp, 'showMessage');
controller.invoiceOut = invoiceOut;
const $data = {email: 'brucebanner@gothamcity.com'};
const expectedParams = {
invoiceId: invoiceOut.id,
recipient: $data.email,
recipientId: invoiceOut.client.id
};
const serializedParams = $httpParamSerializer(expectedParams);
$httpBackend.expectGET(`email/invoice?${serializedParams}`).respond();
controller.sendPdfInvoice($data);
$httpBackend.flush();
expect(controller.vnApp.showMessage).toHaveBeenCalled();
});
});
describe('sendCsvInvoice()', () => {
it('should make a query to the csv invoice send endpoint and show a message snackbar', () => {
jest.spyOn(controller.vnApp, 'showMessage');
controller.invoiceOut = invoiceOut;
const $data = {email: 'brucebanner@gothamcity.com'};
const expectedParams = {
invoiceId: invoiceOut.id,
recipient: $data.email,
recipientId: invoiceOut.client.id
};
const serializedParams = $httpParamSerializer(expectedParams);
$httpBackend.expectGET(`csv/invoice/send?${serializedParams}`).respond();
controller.sendCsvInvoice($data);
$httpBackend.flush();
expect(controller.vnApp.showMessage).toHaveBeenCalled();
});
});
});

View File

@ -0,0 +1,20 @@
Volume exceded: Volumen excedido
Volume: Volumen
Client card: Ficha del cliente
Invoice ticket list: Listado de tickets de la factura
Show invoice...: Ver factura...
Send invoice...: Enviar factura...
Send PDF invoice: Enviar factura en PDF
Send CSV invoice: Enviar factura en CSV
Delete Invoice: Eliminar factura
Clone Invoice: Clonar factura
Book invoice: Asentar factura
Generate PDF invoice: Generar PDF factura
Show CIES letter: Ver carta CIES
InvoiceOut deleted: Factura eliminada
Are you sure you want to delete this invoice?: Estas seguro de eliminar esta factura?
Are you sure you want to clone this invoice?: Estas seguro de clonar esta factura?
InvoiceOut booked: Factura asentada
Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?
Regenerate PDF invoice: Regenerar PDF factura
The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado

View File

@ -0,0 +1,24 @@
@import "./effects";
@import "variables";
vn-invoice-out-descriptor-menu {
& > vn-icon-button[icon="more_vert"] {
display: flex;
min-width: 45px;
height: 45px;
box-sizing: border-box;
align-items: center;
justify-content: center;
}
& > vn-icon-button[icon="more_vert"] {
@extend %clickable;
color: inherit;
& > vn-icon {
padding: 10px;
}
vn-icon {
font-size: 1.75rem;
}
}
}

View File

@ -1,81 +1,12 @@
<vn-descriptor-content
module="invoiceOut"
description="$ctrl.invoiceOut.ref">
<slot-menu>
<vn-item class="dropdown"
vn-click-stop="showInvoiceMenu.show($event, 'left')"
name="showInvoicePdf"
translate>
Show invoice...
<vn-menu vn-id="showInvoiceMenu">
<vn-list>
<a class="vn-item"
href="api/InvoiceOuts/{{$ctrl.id}}/download?access_token={{$ctrl.vnToken.token}}"
target="_blank"
name="showInvoicePdf"
translate>
Show as PDF
</a>
<vn-item
ng-click="$ctrl.showCsvInvoice()"
translate>
Show as CSV
</vn-item>
</vn-list>
</vn-menu>
</vn-item>
<vn-item class="dropdown"
vn-click-stop="sendInvoiceMenu.show($event, 'left')"
name="sendInvoice"
translate>
Send invoice...
<vn-menu vn-id="sendInvoiceMenu">
<vn-list>
<vn-item
ng-click="sendPdfConfirmation.show({email: $ctrl.invoiceOut.client.email})"
translate>
Send PDF
</vn-item>
<vn-item
ng-click="sendCsvConfirmation.show({email: $ctrl.invoiceOut.client.email})"
translate>
Send CSV
</vn-item>
</vn-list>
</vn-menu>
</vn-item>
<vn-item
ng-click="deleteConfirmation.show()"
vn-acl="invoicing"
vn-acl-action="remove"
name="deleteInvoice"
translate>
Delete Invoice
</vn-item>
<vn-item
ng-click="bookConfirmation.show()"
vn-acl="invoicing"
vn-acl-action="remove"
name="bookInvoice"
translate>
Book invoice
</vn-item>
<vn-item
ng-click="createInvoicePdfConfirmation.show()"
ng-show="$ctrl.hasInvoicing || !$ctrl.invoiceOut.hasPdf"
name="regenerateInvoice"
translate>
{{!$ctrl.invoiceOut.hasPdf ? 'Generate PDF invoice': 'Regenerate PDF invoice'}}
</vn-item>
<vn-item
ng-click="$ctrl.showExportationLetter()"
ng-show="$ctrl.invoiceOut.serial == 'E'"
translate>
Show CIES letter
</vn-item>
</slot-menu>
<slot-dot-menu>
<vn-invoice-out-descriptor-menu
invoice-out="$ctrl.invoiceOut"
parent-reload="$ctrl.cardReload()"
/>
</slot-dot-menu>
<slot-body>
<div class="attributes">
<vn-label-value
@ -118,59 +49,4 @@
</div>
</div>
</slot-body>
</vn-descriptor-content>
<vn-confirm
vn-id="deleteConfirmation"
on-accept="$ctrl.deleteInvoiceOut()"
question="Are you sure you want to delete this invoice?">
</vn-confirm>
<vn-confirm
vn-id="bookConfirmation"
on-accept="$ctrl.bookInvoiceOut()"
question="Are you sure you want to book this invoice?">
</vn-confirm>
<vn-client-descriptor-popover
vn-id="clientDescriptor">
</vn-client-descriptor-popover>
<!-- Create invoice PDF confirmation dialog -->
<vn-confirm
vn-id="createInvoicePdfConfirmation"
on-accept="$ctrl.createPdfInvoice()"
question="Are you sure you want to generate/regenerate the PDF invoice?"
message="Generate PDF invoice document">
</vn-confirm>
<!-- Send PDF invoice confirmation popup -->
<vn-dialog
vn-id="sendPdfConfirmation"
on-accept="$ctrl.sendPdfInvoice($data)"
message="Send PDF invoice">
<tpl-body>
<span translate>Are you sure you want to send it?</span>
<vn-textfield vn-one
ng-model="sendPdfConfirmation.data.email">
</vn-textfield>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Confirm</button>
</tpl-buttons>
</vn-dialog>
<!-- Send CSV invoice confirmation popup -->
<vn-dialog
vn-id="sendCsvConfirmation"
on-accept="$ctrl.sendCsvInvoice($data)"
message="Send CSV invoice">
<tpl-body>
<span translate>Are you sure you want to send it?</span>
<vn-textfield vn-one
ng-model="sendCsvConfirmation.data.email">
</vn-textfield>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Confirm</button>
</tpl-buttons>
</vn-dialog>
</vn-descriptor-content>

View File

@ -41,70 +41,6 @@ class Controller extends Descriptor {
return this.getData(`InvoiceOuts/${this.id}`, {filter})
.then(res => this.entity = res.data);
}
reload() {
return this.loadData().then(() => {
if (this.cardReload)
this.cardReload();
});
}
cardReload() {
// Prevents error when not defined
}
deleteInvoiceOut() {
return this.$http.post(`InvoiceOuts/${this.id}/delete`)
.then(() => this.$state.go('invoiceOut.index'))
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut deleted')));
}
bookInvoiceOut() {
return this.$http.post(`InvoiceOuts/${this.invoiceOut.ref}/book`)
.then(() => this.$state.reload())
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut booked')));
}
createPdfInvoice() {
const invoiceId = this.invoiceOut.id;
return this.$http.post(`InvoiceOuts/${invoiceId}/createPdf`)
.then(() => this.reload())
.then(() => {
const snackbarMessage = this.$t(
`The invoice PDF document has been regenerated`);
this.vnApp.showSuccess(snackbarMessage);
});
}
showCsvInvoice() {
this.vnReport.showCsv('invoice', {
recipientId: this.invoiceOut.client.id,
invoiceId: this.id,
});
}
sendPdfInvoice($data) {
return this.vnEmail.send('invoice', {
recipientId: this.invoiceOut.client.id,
recipient: $data.email,
invoiceId: this.id
});
}
sendCsvInvoice($data) {
return this.vnEmail.sendCsv('invoice', {
recipientId: this.invoiceOut.client.id,
recipient: $data.email,
invoiceId: this.id
});
}
showExportationLetter() {
this.vnReport.show('exportation', {
recipientId: this.invoiceOut.client.id,
invoiceId: this.id,
});
}
}
ngModule.vnComponent('vnInvoiceOutDescriptor', {
@ -112,6 +48,5 @@ ngModule.vnComponent('vnInvoiceOutDescriptor', {
controller: Controller,
bindings: {
invoiceOut: '<',
cardReload: '&'
}
});

View File

@ -7,5 +7,6 @@ import './summary';
import './card';
import './descriptor';
import './descriptor-popover';
import './descriptor-menu';
import './index/manual';
import './index/global-invoicing';

View File

@ -13,6 +13,10 @@
<vn-icon-button icon="launch"></vn-icon-button>
</a>
<span>{{$ctrl.summary.invoiceOut.ref}} - {{$ctrl.summary.invoiceOut.client.socialName}}</span>
<vn-invoice-out-descriptor-menu
invoice-out="$ctrl.summary.invoiceOut"
parent-reload="$ctrl.reload()"
/>
</h5>
<vn-horizontal>
<vn-one>