diff --git a/modules/invoiceOut/back/methods/invoiceOut/invoiceClient.js b/modules/invoiceOut/back/methods/invoiceOut/invoiceClient.js
index 95c51a96d..72bdb3ec7 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/invoiceClient.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/invoiceClient.js
@@ -68,63 +68,57 @@ module.exports = Self => {
const client = await models.Client.findById(args.clientId, {
fields: ['id', 'hasToInvoiceByAddress']
}, myOptions);
- try {
- if (client.hasToInvoiceByAddress) {
- await Self.rawSql('CALL ticketToInvoiceByAddress(?, ?, ?, ?)', [
- args.minShipped,
- args.maxShipped,
- args.addressId,
- args.companyFk
- ], myOptions);
- } else {
- await Self.rawSql('CALL invoiceFromClient(?, ?, ?)', [
- args.maxShipped,
- client.id,
- args.companyFk
- ], myOptions);
- }
- // Make invoice
- const isSpanishCompany = await getIsSpanishCompany(args.companyFk, myOptions);
-
- // Validates ticket nagative base
- const hasAnyNegativeBase = await getNegativeBase(myOptions);
- if (hasAnyNegativeBase && isSpanishCompany)
- return tx.rollback();
-
- query = `SELECT invoiceSerial(?, ?, ?) AS serial`;
- const [invoiceSerial] = await Self.rawSql(query, [
+ if (client.hasToInvoiceByAddress) {
+ await Self.rawSql('CALL ticketToInvoiceByAddress(?, ?, ?, ?)', [
+ args.minShipped,
+ args.maxShipped,
+ args.addressId,
+ args.companyFk
+ ], myOptions);
+ } else {
+ await Self.rawSql('CALL invoiceFromClient(?, ?, ?)', [
+ args.maxShipped,
client.id,
- args.companyFk,
- 'G'
+ args.companyFk
], myOptions);
- const serialLetter = invoiceSerial.serial;
-
- query = `CALL invoiceOut_new(?, ?, NULL, @invoiceId)`;
- await Self.rawSql(query, [
- serialLetter,
- args.invoiceDate
- ], myOptions);
-
- const [newInvoice] = await Self.rawSql(`SELECT @invoiceId id`, null, myOptions);
- if (newInvoice.id) {
- await Self.rawSql('CALL invoiceOutBooking(?)', [newInvoice.id], myOptions);
-
- invoiceId = newInvoice.id;
- }
- } catch (e) {
- const failedClient = {
- id: client.id,
- stacktrace: e
- };
- await notifyFailures(ctx, failedClient, myOptions);
}
- invoiceOut = await models.InvoiceOut.findById(invoiceId, {
- include: {
- relation: 'client'
- }
- }, myOptions);
+ // Make invoice
+ const isSpanishCompany = await getIsSpanishCompany(args.companyFk, myOptions);
+
+ // Validates ticket nagative base
+ const hasAnyNegativeBase = await getNegativeBase(myOptions);
+ if (hasAnyNegativeBase && isSpanishCompany)
+ return tx.rollback();
+
+ query = `SELECT invoiceSerial(?, ?, ?) AS serial`;
+ const [invoiceSerial] = await Self.rawSql(query, [
+ client.id,
+ args.companyFk,
+ 'G'
+ ], myOptions);
+ const serialLetter = invoiceSerial.serial;
+
+ query = `CALL invoiceOut_new(?, ?, NULL, @invoiceId)`;
+ await Self.rawSql(query, [
+ serialLetter,
+ args.invoiceDate
+ ], myOptions);
+ if (client.id == 1102)
+ throw new Error('Error1');
+ const [newInvoice] = await Self.rawSql(`SELECT @invoiceId id`, null, myOptions);
+ if (newInvoice.id) {
+ await Self.rawSql('CALL invoiceOutBooking(?)', [newInvoice.id], myOptions);
+
+ invoiceOut = await models.InvoiceOut.findById(newInvoice.id, {
+ include: {
+ relation: 'client'
+ }
+ }, myOptions);
+
+ invoiceId = newInvoice.id;
+ }
if (tx) await tx.commit();
} catch (e) {
@@ -132,15 +126,14 @@ module.exports = Self => {
throw e;
}
- ctx.args = {
- reference: invoiceOut.ref,
- recipientId: invoiceOut.clientFk,
- recipient: invoiceOut.client().email
- };
- try {
+ if (invoiceId) {
+ ctx.args = {
+ reference: invoiceOut.ref,
+ recipientId: invoiceOut.clientFk,
+ recipient: invoiceOut.client().email
+ };
await models.InvoiceOut.invoiceEmail(ctx, invoiceOut.ref);
- } catch (err) {}
-
+ }
return invoiceId;
};
@@ -165,26 +158,4 @@ module.exports = Self => {
return supplierCompany && supplierCompany.total;
}
-
- async function notifyFailures(ctx, failedClient, options) {
- const models = Self.app.models;
- const userId = ctx.req.accessToken.userId;
- const $t = ctx.req.__; // $translate
-
- const worker = await models.EmailUser.findById(userId, null, options);
- const subject = $t('Global invoicing failed');
- let body = $t(`Wasn't able to invoice the following clients`) + ':
';
-
- body += `ID: ${failedClient.id}
-
${failedClient.stacktrace}
`;
-
- await Self.rawSql(`
- INSERT INTO vn.mail (sender, replyTo, sent, subject, body)
- VALUES (?, ?, FALSE, ?, ?)`, [
- worker.email,
- worker.email,
- subject,
- body
- ], options);
- }
};
diff --git a/modules/invoiceOut/front/global-invoicing/index.html b/modules/invoiceOut/front/global-invoicing/index.html
new file mode 100644
index 000000000..1b14a72f6
--- /dev/null
+++ b/modules/invoiceOut/front/global-invoicing/index.html
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+ Id
+ Status
+
+
+
+
+
+
+ {{::client.id}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/invoiceOut/front/index/global-invoicing/index.js b/modules/invoiceOut/front/global-invoicing/index.js
similarity index 67%
rename from modules/invoiceOut/front/index/global-invoicing/index.js
rename to modules/invoiceOut/front/global-invoicing/index.js
index f772a4936..0750d8d6c 100644
--- a/modules/invoiceOut/front/index/global-invoicing/index.js
+++ b/modules/invoiceOut/front/global-invoicing/index.js
@@ -1,12 +1,14 @@
-import ngModule from '../../module';
-import Dialog from 'core/components/dialog';
+import ngModule from '../module';
+import Section from 'salix/components/section';
+import UserError from 'core/lib/user-error';
import './style.scss';
-class Controller extends Dialog {
+class Controller extends Section {
constructor($element, $, $transclude) {
super($element, $, $transclude);
this.invoice = {
- maxShipped: new Date()
+ maxShipped: new Date(),
+ companyFk: this.vnConfig.companyFk
};
this.clientsNumber = 'allClients';
}
@@ -37,14 +39,6 @@ class Controller extends Dialog {
return this.$http.get('Clients/findOne', {params});
}
- get companyFk() {
- return this.invoice.companyFk;
- }
-
- set companyFk(value) {
- this.invoice.companyFk = value;
- }
-
restartValues() {
this.lastClientId = null;
this.$.invoiceButton.disabled = false;
@@ -69,45 +63,51 @@ class Controller extends Dialog {
};
- const options = this.cancelRequest();
-
- return this.$http.post(`InvoiceOuts/invoiceClient`, params, options)
+ const index = this.$.data.findIndex(element => element.id == clientAndAddress.clientId);
+ return this.$http.post(`InvoiceOuts/invoiceClient`, params)
.then(() => {
+ this.$.data[index].status = 'ok';
+ }).catch(() => {
+ this.$.data[index].status = 'error';
+ }).finally(() => {
clientsAndAddresses.shift();
return this.invoiceOut(invoice, clientsAndAddresses);
});
}
- responseHandler(response) {
+ makeInvoice() {
try {
- if (response !== 'accept')
- return super.responseHandler(response);
-
if (!this.invoice.invoiceDate || !this.invoice.maxShipped)
throw new Error('Invoice date and the max date should be filled');
if (!this.invoice.fromClientId || !this.invoice.toClientId)
throw new Error('Choose a valid clients range');
- this.on('close', () => {
- if (this.canceler) this.canceler.resolve();
- this.vnApp.showSuccess(this.$t('Data saved!'));
- });
-
this.$.invoiceButton.disabled = true;
this.packageInvoicing = true;
- const options = this.cancelRequest();
- this.$http.post(`InvoiceOuts/clientsToInvoice`, this.invoice, options)
+ this.$http.post(`InvoiceOuts/clientsToInvoice`, this.invoice)
.then(res => {
this.packageInvoicing = false;
const invoice = res.data.invoice;
+
+ const clientsIds = [];
+ for (const clientAndAddress of res.data.clientsAndAddresses)
+ clientsIds.push(clientAndAddress.clientId);
+ const dataArr = new Set(clientsIds);
+ const clientsIdsNoRepeat = [...dataArr];
+ const clients = clientsIdsNoRepeat.map(clientId => {
+ return {
+ id: clientId,
+ status: 'waiting'
+ };
+ });
+ this.$.data = clients;
+
const clientsAndAddresses = res.data.clientsAndAddresses;
- if (!clientsAndAddresses.length) return super.responseHandler(response);
- this.lastClientId = clientsAndAddresses[clientsAndAddresses.length - 1].clientId;
+ if (!clientsAndAddresses.length) throw new UserError(`There aren't clients to invoice`);
return this.invoiceOut(invoice, clientsAndAddresses);
})
- .then(() => super.responseHandler(response))
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
.finally(() => this.restartValues());
} catch (e) {
@@ -116,14 +116,15 @@ class Controller extends Dialog {
return false;
}
}
+
+ clean() {
+ this.$.data = this.$.data.filter(client => client.status == 'error');
+ }
}
Controller.$inject = ['$element', '$scope', '$transclude'];
ngModule.vnComponent('vnInvoiceOutGlobalInvoicing', {
- slotTemplate: require('./index.html'),
- controller: Controller,
- bindings: {
- companyFk: ''
- }
+ template: require('./index.html'),
+ controller: Controller
});
diff --git a/modules/invoiceOut/front/global-invoicing/index.spec.js b/modules/invoiceOut/front/global-invoicing/index.spec.js
new file mode 100644
index 000000000..63c87fbea
--- /dev/null
+++ b/modules/invoiceOut/front/global-invoicing/index.spec.js
@@ -0,0 +1,126 @@
+import './index.js';
+import popover from 'core/mocks/popover';
+import crudModel from 'core/mocks/crud-model';
+
+describe('Zone Component vnZoneDeliveryDays', () => {
+ let $httpBackend;
+ let controller;
+ let $element;
+
+ beforeEach(ngModule('zone'));
+
+ beforeEach(inject(($componentController, _$httpBackend_) => {
+ $httpBackend = _$httpBackend_;
+ $element = angular.element(' {
+ it('should set the deliveryMethodFk property as pickup and then perform a query that sets the filter', () => {
+ $httpBackend.expect('GET', 'DeliveryMethods').respond([{id: 999}]);
+ controller.deliveryMethodFk = 'pickUp';
+ $httpBackend.flush();
+
+ expect(controller.agencyFilter).toEqual({deliveryMethodFk: {inq: [999]}});
+ });
+ });
+
+ describe('setParams()', () => {
+ it('should do nothing when no params are received', () => {
+ controller.setParams();
+
+ expect(controller.deliveryMethodFk).toBeUndefined();
+ expect(controller.geoFk).toBeUndefined();
+ expect(controller.agencyModeFk).toBeUndefined();
+ });
+
+ it('should set the controller properties when the params are provided', () => {
+ controller.$params = {
+ deliveryMethodFk: 3,
+ geoFk: 2,
+ agencyModeFk: 1
+ };
+ controller.setParams();
+
+ expect(controller.deliveryMethodFk).toEqual(controller.$params.deliveryMethodFk);
+ expect(controller.geoFk).toEqual(controller.$params.geoFk);
+ expect(controller.agencyModeFk).toEqual(controller.$params.agencyModeFk);
+ });
+ });
+
+ describe('fetchData()', () => {
+ it('should make an HTTP GET query and then call the showMessage() method', () => {
+ jest.spyOn(controller.vnApp, 'showMessage');
+ jest.spyOn(controller.$state, 'go');
+
+ controller.agencyModeFk = 1;
+ controller.deliveryMethodFk = 2;
+ controller.geoFk = 3;
+ controller.$state.current.name = 'myState';
+
+ const expectedData = {events: []};
+
+ const url = 'Zones/getEvents?agencyModeFk=1&deliveryMethodFk=2&geoFk=3';
+
+ $httpBackend.when('GET', 'DeliveryMethods').respond([]);
+ $httpBackend.expect('GET', url).respond({events: []});
+ controller.fetchData();
+ $httpBackend.flush();
+
+ expect(controller.$.data).toEqual(expectedData);
+ expect(controller.vnApp.showMessage).toHaveBeenCalledWith('No service for the specified zone');
+ expect(controller.$state.go).toHaveBeenCalledWith(
+ controller.$state.current.name,
+ {
+ agencyModeFk: 1,
+ deliveryMethodFk: 2,
+ geoFk: 3
+ }
+ );
+ });
+ });
+
+ describe('onSelection()', () => {
+ it('should not call the show popover method if events array is empty', () => {
+ jest.spyOn(controller.$.zoneEvents, 'show');
+
+ const event = new Event('click');
+ const target = document.createElement('div');
+ target.dispatchEvent(event);
+ const events = [];
+ controller.onSelection(event, events);
+
+ expect(controller.$.zoneEvents.show).not.toHaveBeenCalled();
+ });
+
+ it('should call the show() method and call getZoneClosing() with the expected ids', () => {
+ jest.spyOn(controller.$.zoneEvents, 'show');
+
+ const event = new Event('click');
+ const target = document.createElement('div');
+ target.dispatchEvent(event);
+
+ const day = new Date();
+ const events = [
+ {zoneFk: 1},
+ {zoneFk: 2},
+ {zoneFk: 8}
+ ];
+ const params = {
+ zoneIds: [1, 2, 8],
+ date: [day][0]
+ };
+ const response = [{id: 1, hour: ''}];
+
+ $httpBackend.when('POST', 'Zones/getZoneClosing', params).respond({response});
+ controller.onSelection(event, events, [day]);
+ $httpBackend.flush();
+
+ expect(controller.$.zoneEvents.show).toHaveBeenCalledWith(target);
+ expect(controller.zoneClosing.id).toEqual(response.id);
+ });
+ });
+});
diff --git a/modules/invoiceOut/front/global-invoicing/locale/es.yml b/modules/invoiceOut/front/global-invoicing/locale/es.yml
new file mode 100644
index 000000000..5c556fa39
--- /dev/null
+++ b/modules/invoiceOut/front/global-invoicing/locale/es.yml
@@ -0,0 +1,7 @@
+There aren't clients to invoice: No existen clientes para facturar
+Max date: Fecha límite
+Invoice date: Fecha de factura
+Invoice date and the max date should be filled: La fecha de factura y la fecha límite deben rellenarse
+Choose a valid clients range: Selecciona un rango válido de clientes
+Clients range: Rango de clientes
+Calculating packages to invoice...: Calculando paquetes a factura...
diff --git a/modules/invoiceOut/front/global-invoicing/style.scss b/modules/invoiceOut/front/global-invoicing/style.scss
new file mode 100644
index 000000000..41dd3fac2
--- /dev/null
+++ b/modules/invoiceOut/front/global-invoicing/style.scss
@@ -0,0 +1,5 @@
+@import "variables";
+
+.error {
+ color: $color-alert;
+ }
diff --git a/modules/invoiceOut/front/index.js b/modules/invoiceOut/front/index.js
index 0307b2b4b..f7cebc0d0 100644
--- a/modules/invoiceOut/front/index.js
+++ b/modules/invoiceOut/front/index.js
@@ -9,4 +9,4 @@ import './descriptor';
import './descriptor-popover';
import './descriptor-menu';
import './index/manual';
-import './index/global-invoicing';
+import './global-invoicing';
diff --git a/modules/invoiceOut/front/index/global-invoicing/index.html b/modules/invoiceOut/front/index/global-invoicing/index.html
deleted file mode 100644
index c2d1c4304..000000000
--- a/modules/invoiceOut/front/index/global-invoicing/index.html
+++ /dev/null
@@ -1,96 +0,0 @@
-
- Create global invoice
-
-
-
-
-
-
-
-
-
- {{'Calculating packages to invoice...' | translate}}
-
-
-
-
-
-
- {{'Id Client' | translate}}: {{$ctrl.currentClientId}}
- {{'of' | translate}} {{::$ctrl.lastClientId}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{::id}} - {{::name}}
-
-
- {{::id}} - {{::name}}
-
-
-
-
-
-
-
-
-
- {{$ctrl.isInvoicing}}
-
\ No newline at end of file
diff --git a/modules/invoiceOut/front/index/index.html b/modules/invoiceOut/front/index/index.html
index e2cf2120a..e2b0c221f 100644
--- a/modules/invoiceOut/front/index/index.html
+++ b/modules/invoiceOut/front/index/index.html
@@ -18,7 +18,7 @@
-
@@ -37,7 +37,7 @@
class="clickable vn-tr search-result"
ui-sref="invoiceOut.card.summary({id: {{::invoiceOut.id}}})">
-
@@ -103,7 +103,3 @@
-
-
\ No newline at end of file
diff --git a/modules/invoiceOut/front/routes.json b/modules/invoiceOut/front/routes.json
index 09d9f3d33..c396a5334 100644
--- a/modules/invoiceOut/front/routes.json
+++ b/modules/invoiceOut/front/routes.json
@@ -6,7 +6,9 @@
"dependencies": ["worker", "client", "ticket"],
"menus": {
"main": [
- {"state": "invoiceOut.index", "icon": "icon-invoice-out"}
+ {"state": "invoiceOut.index", "icon": "icon-invoice-out"},
+ {"state": "invoiceOut.global-invoicing", "icon": "contact_support"}
+
]
},
"routes": [
@@ -24,6 +26,12 @@
"component": "vn-invoice-out-index",
"description": "InvoiceOut"
},
+ {
+ "url": "/global-invoicing?q",
+ "state": "invoiceOut.global-invoicing",
+ "component": "vn-invoice-out-global-invoicing",
+ "description": "Global invoicing"
+ },
{
"url": "/summary",
"state": "invoiceOut.card.summary",
@@ -40,4 +48,4 @@
"component": "vn-invoice-out-card"
}
]
-}
\ No newline at end of file
+}