diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index 8f667ca32..efe7906ce 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -8,7 +8,7 @@ ALTER TABLE `vn`.`ticket` AUTO_INCREMENT = 1;
INSERT INTO `salix`.`AccessToken` (`id`, `ttl`, `created`, `userId`)
VALUES
- ('TOTALLY_SECURE_TOKEN', '1209600', CURDATE(), 66);
+ ('DEFAULT_TOKEN', '1209600', CURDATE(), 66);
INSERT INTO `vn`.`ticketConfig` (`id`, `scopeDays`)
@@ -104,17 +104,17 @@ INSERT INTO `vn`.`currency`(`id`, `code`, `name`, `ratio`)
(3, 'GBP', 'Libra', 1),
(4, 'JPY', 'Yen Japones', 1);
-INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`, `continentFk`)
+INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`, `continentFk`, `hasDailyInvoice`, `CEE`)
VALUES
- (1, 'España', 1, 'ES', 1, 24, 4),
- (2, 'Italia', 1, 'IT', 1, 27, 4),
- (3, 'Alemania', 1, 'DE', 1, 22, 4),
- (4, 'Rumania', 1, 'RO', 1, 24, 4),
- (5, 'Holanda', 1, 'NL', 1, 18, 4),
- (8, 'Portugal', 1, 'PT', 1, 27, 4),
- (13,'Ecuador', 0, 'EC', 1, 24, 2),
- (19,'Francia', 1, 'FR', 1, 27, 4),
- (30,'Canarias', 1, 'IC', 1, 24, 4);
+ (1, 'España', 1, 'ES', 1, 24, 4, 0, 1),
+ (2, 'Italia', 1, 'IT', 1, 27, 4, 0, 1),
+ (3, 'Alemania', 1, 'DE', 1, 22, 4, 0, 1),
+ (4, 'Rumania', 1, 'RO', 1, 24, 4, 0, 1),
+ (5, 'Holanda', 1, 'NL', 1, 18, 4, 0, 1),
+ (8, 'Portugal', 1, 'PT', 1, 27, 4, 0, 1),
+ (13,'Ecuador', 0, 'EC', 1, 24, 2, 1, 2),
+ (19,'Francia', 1, 'FR', 1, 27, 4, 0, 1),
+ (30,'Canarias', 1, 'IC', 1, 24, 4, 1, 2);
INSERT INTO `hedera`.`language` (`code`, `name`, `orgName`, `isActive`)
VALUES
@@ -243,7 +243,7 @@ INSERT INTO `vn`.`province`(`id`, `name`, `countryFk`, `autonomyFk`, `warehouseF
VALUES
(1, 'Province one', 1, 1, NULL),
(2, 'Province two', 1, 1, NULL),
- (3, 'Province three', 1, 2, NULL),
+ (3, 'Province three', 30, 2, NULL),
(4, 'Province four', 2, 3, NULL),
(5, 'Province five', 13, 4, NULL);
@@ -455,7 +455,8 @@ INSERT INTO `vn`.`creditInsurance`(`id`, `creditClassification`, `credit`, `crea
INSERT INTO `vn`.`companyGroup`(`id`, `code`)
VALUES
- (1, 'Wayne Industries');
+ (1, 'wayneIndustries'),
+ (2, 'Verdnatura');
INSERT INTO `vn`.`bankEntity`(`id`, `countryFk`, `name`, `bic`)
VALUES
@@ -466,13 +467,13 @@ INSERT INTO `vn`.`supplierAccount`(`id`, `supplierFk`, `iban`, `bankEntityFk`)
VALUES
(241, 442, 'ES111122333344111122221111', 128);
-INSERT INTO `vn`.`company`(`id`, `code`, `supplierAccountFk`, `workerManagerFk`, `companyCode`, `sage200Company`, `expired`, `phytosanitary`)
+INSERT INTO `vn`.`company`(`id`, `code`, `supplierAccountFk`, `workerManagerFk`, `companyCode`, `sage200Company`, `expired`, `companyGroupFk`, `phytosanitary`)
VALUES
- (69 , 'CCs', NULL, 30, NULL, 0, NULL, NULL),
- (442 , 'VNL', 241, 30, 2 , 1, NULL, 'VNL Company - Plant passport'),
- (567 , 'VNH', NULL, 30, NULL, 4, NULL, 'VNH Company - Plant passport'),
- (791 , 'FTH', NULL, 30, NULL, 3, '2015-11-30', NULL),
- (1381, 'ORN', NULL, 30, NULL, 7, NULL, 'ORN Company - Plant passport');
+ (69 , 'CCs', NULL, 30, NULL, 0, NULL, 1, NULL),
+ (442 , 'VNL', 241, 30, 2 , 1, NULL, 2, 'VNL Company - Plant passport'),
+ (567 , 'VNH', NULL, 30, NULL, 4, NULL, 1, 'VNH Company - Plant passport'),
+ (791 , 'FTH', NULL, 30, NULL, 3, '2015-11-30', 1, NULL),
+ (1381, 'ORN', NULL, 30, NULL, 7, NULL, 1, 'ORN Company - Plant passport');
INSERT INTO `vn`.`taxArea` (`code`, `claveOperacionFactura`, `CodigoTransaccion`)
VALUES
@@ -486,7 +487,9 @@ INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaF
('A', 'Global nacional', 1, 'NATIONAL', 0),
('T', 'Española rapida', 1, 'NATIONAL', 0),
('V', 'Intracomunitaria global', 0, 'CEE', 1),
- ('M', 'Múltiple nacional', 1, 'NATIONAL', 0);
+ ('M', 'Múltiple nacional', 1, 'NATIONAL', 0),
+ ('E', 'Exportación rápida', 0, 'WORLD', 0);
+;
INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `hasPdf`)
VALUES
diff --git a/front/core/components/check/style.scss b/front/core/components/check/style.scss
index b84b61ce5..add593570 100644
--- a/front/core/components/check/style.scss
+++ b/front/core/components/check/style.scss
@@ -43,4 +43,10 @@
&.disabled.checked > .btn {
background-color: $color-font-secondary;
}
+
+ &[triple-state]:not(.indeterminate):not(.checked) {
+ .btn {
+ background-color: lighten($color-alert, 5%);
+ }
+ }
}
diff --git a/front/core/components/smart-table/index.js b/front/core/components/smart-table/index.js
index cb0304b34..b0b4d90dc 100644
--- a/front/core/components/smart-table/index.js
+++ b/front/core/components/smart-table/index.js
@@ -171,9 +171,10 @@ export default class SmartTable extends Component {
if (field.length === 2)
sortType = field[1];
+ const priority = this.sortCriteria.length + 1;
const column = this.columns.find(column => column.field == fieldName);
if (column) {
- this.sortCriteria.push({field: fieldName, sortType: sortType});
+ this.sortCriteria.push({field: fieldName, sortType: sortType, priority: priority});
const isASC = sortType == 'ASC';
const isDESC = sortType == 'DESC';
@@ -187,6 +188,8 @@ export default class SmartTable extends Component {
column.element.classList.remove('desc');
column.element.classList.add('asc');
}
+
+ this.setPriority(column.element, priority);
}
}
}
@@ -241,9 +244,13 @@ export default class SmartTable extends Component {
const isDESC = existingCriteria && existingCriteria.sortType == 'DESC';
if (!existingCriteria) {
- this.sortCriteria.push({field: field, sortType: 'ASC'});
+ const priority = this.sortCriteria.length + 1;
+
+ this.sortCriteria.push({field: field, sortType: 'ASC', priority: priority});
element.classList.remove('desc');
element.classList.add('asc');
+
+ this.setPriority(element, priority);
}
if (isDESC) {
@@ -252,6 +259,8 @@ export default class SmartTable extends Component {
}), 1);
element.classList.remove('desc');
element.classList.remove('asc');
+
+ element.querySelector('sort-priority').remove();
}
if (isASC) {
@@ -260,9 +269,29 @@ export default class SmartTable extends Component {
element.classList.add('desc');
}
+ let priority = 0;
+ for (const criteria of this.sortCriteria) {
+ const column = this.columns.find(column => column.field == criteria.field);
+ if (column) {
+ criteria.priority = priority;
+ priority++;
+
+ column.element.querySelector('sort-priority').remove();
+
+ this.setPriority(column.element, priority);
+ }
+ }
+
this.applySort();
}
+ setPriority(column, priority) {
+ const sortPriority = document.createElement('sort-priority');
+ sortPriority.setAttribute('class', 'sort-priority');
+ sortPriority.innerHTML = priority;
+ column.appendChild(sortPriority);
+ }
+
displaySearch() {
const header = this.element.querySelector('thead > tr');
if (!header) return;
diff --git a/front/core/components/smart-table/index.spec.js b/front/core/components/smart-table/index.spec.js
index 94edd45bb..720e24c7e 100644
--- a/front/core/components/smart-table/index.spec.js
+++ b/front/core/components/smart-table/index.spec.js
@@ -96,9 +96,10 @@ describe('Component smartTable', () => {
expect(firstSortCriteria.field).toEqual('id');
expect(firstSortCriteria.sortType).toEqual('ASC');
+ expect(firstSortCriteria.priority).toEqual(1);
});
- it('should insert two new objects to the controller sortCriteria with a sortType values of "ASC" and "DESC"', () => {
+ it('should add new entries to the controller sortCriteria with a sortType values of "ASC" and "DESC"', () => {
const element = document.createElement('div');
controller.model = {order: 'test1, id DESC'};
controller.columns = [
@@ -114,8 +115,11 @@ describe('Component smartTable', () => {
expect(firstSortCriteria.field).toEqual('test1');
expect(firstSortCriteria.sortType).toEqual('ASC');
+ expect(firstSortCriteria.priority).toEqual(1);
+
expect(secondSortCriteria.field).toEqual('id');
expect(secondSortCriteria.sortType).toEqual('DESC');
+ expect(secondSortCriteria.priority).toEqual(2);
});
});
diff --git a/front/core/components/smart-table/style.scss b/front/core/components/smart-table/style.scss
index 1e882f679..bf1c14082 100644
--- a/front/core/components/smart-table/style.scss
+++ b/front/core/components/smart-table/style.scss
@@ -9,7 +9,7 @@ smart-table {
}
th[field][number] {
- & > :before {
+ & > span:before {
vertical-align: middle;
font-family: 'Material Icons';
content: 'arrow_downward';
@@ -19,26 +19,26 @@ smart-table {
}
- &.asc > :before, &.desc > :before {
+ &.asc > span:before, &.desc > span:before {
color: $color-font;
opacity: 1;
}
- &.asc > :before {
+ &.asc > span:before {
content: 'arrow_upward';
}
- &.desc > :before {
+ &.desc > span:before {
content: 'arrow_downward';
}
- &:hover > :before {
+ &:hover > span:before {
opacity: 1;
}
}
th[field]:not([number]) {
- & > :after {
+ & > span:after {
vertical-align: middle;
font-family: 'Material Icons';
content: 'arrow_downward';
@@ -48,20 +48,20 @@ smart-table {
}
- &.asc > :after, &.desc > :after {
+ &.asc > span:after, &.desc > span:after {
color: $color-font;
opacity: 1;
}
- &.asc > :after {
+ &.asc > span:after {
content: 'arrow_upward';
}
- &.desc > :after {
+ &.desc > span:after {
content: 'arrow_downward';
}
- &:hover > :after {
+ &:hover > span:after {
opacity: 1;
}
}
@@ -143,4 +143,16 @@ smart-table {
flex: initial;
width: 33%
}
+}
+.sort-priority {
+ background-color: $color-font-bg-marginal;
+ border-radius: 50%;
+ padding: 2px 5px;
+ display: inline-block;
+ text-align: center;
+ width: 7px;
+ height: 13px;
+
+ font-size: 10px;
+ color: $color-font-bg
}
\ No newline at end of file
diff --git a/modules/entry/front/basic-data/index.html b/modules/entry/front/basic-data/index.html
index 4b7661a8f..f75834045 100644
--- a/modules/entry/front/basic-data/index.html
+++ b/modules/entry/front/basic-data/index.html
@@ -35,6 +35,13 @@
{{::agencyModeName}} - {{::warehouseInName}} ({{::shipped | date: 'dd/MM/yyyy'}}) →
{{::warehouseOutName}} ({{::landed | date: 'dd/MM/yyyy'}})
+
+
+
+
@@ -121,4 +128,94 @@
ng-click="watcher.loadOriginalData()">
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ID
+ Agency
+ Warehouse Out
+ Warehouse In
+ Shipped
+ Landed
+
+
+
+
+
+
+ {{::travel.id}}
+
+
+ {{::travel.agency.name}}
+ {{::travel.warehouseOut.name}}
+ {{::travel.warehouseIn.name}}
+ {{::travel.shipped | date: 'dd/MM/yyyy'}}
+ {{::travel.landed | date: 'dd/MM/yyyy'}}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/entry/front/basic-data/index.js b/modules/entry/front/basic-data/index.js
index 141a365fa..80870c3f3 100644
--- a/modules/entry/front/basic-data/index.js
+++ b/modules/entry/front/basic-data/index.js
@@ -1,10 +1,68 @@
import ngModule from '../module';
import Section from 'salix/components/section';
+import './style.scss';
+class Controller extends Section {
+ showFilterDialog(travel) {
+ this.activeTravel = travel;
+ this.travelFilterParams = {};
+ this.travelFilter = {
+ include: [
+ {
+ relation: 'agency',
+ scope: {
+ fields: ['name']
+ }
+ },
+ {
+ relation: 'warehouseIn',
+ scope: {
+ fields: ['name']
+ }
+ },
+ {
+ relation: 'warehouseOut',
+ scope: {
+ fields: ['name']
+ }
+ }
+ ]
+ };
+
+ this.$.filterDialog.show();
+ }
+
+ selectTravel(id) {
+ this.entry.travelFk = id;
+ this.$.filterDialog.hide();
+ }
+
+ filter() {
+ const filter = this.travelFilter;
+ const params = this.travelFilterParams;
+ const where = {};
+ for (let key in params) {
+ const value = params[key];
+ if (!value) continue;
+
+ switch (key) {
+ case 'agencyFk':
+ case 'warehouseInFk':
+ case 'warehouseOutFk':
+ case 'shipped':
+ case 'landed':
+ where[key] = value;
+ }
+ }
+
+ filter.where = where;
+ this.$.travelsModel.applyFilter(filter);
+ }
+}
ngModule.vnComponent('vnEntryBasicData', {
template: require('./index.html'),
- controller: Section,
bindings: {
entry: '<'
- }
+ },
+ controller: Controller
});
diff --git a/modules/entry/front/basic-data/style.scss b/modules/entry/front/basic-data/style.scss
new file mode 100644
index 000000000..508aa9091
--- /dev/null
+++ b/modules/entry/front/basic-data/style.scss
@@ -0,0 +1,3 @@
+.travelFilter{
+ width: 950px;
+}
diff --git a/modules/invoiceIn/front/search-panel/index.html b/modules/invoiceIn/front/search-panel/index.html
index d26bc063e..609fa56d8 100644
--- a/modules/invoiceIn/front/search-panel/index.html
+++ b/modules/invoiceIn/front/search-panel/index.html
@@ -21,6 +21,20 @@
ng-model="filter.fi">
+
+
+
+ {{::id}} - {{::nickname}}
+
+
+
{
Self.remoteMethodCtx('createPdf', {
@@ -57,39 +57,37 @@ module.exports = Self => {
hasPdf: true
}, myOptions);
- const response = got.stream(`${origin}/api/report/invoice`, {
- searchParams: {
+ return axios.get(`${origin}/api/report/invoice`, {
+ responseType: 'stream',
+ params: {
authorization: auth.id,
invoiceId: id
}
- });
+ }).then(async response => {
+ const issued = invoiceOut.issued;
+ const year = issued.getFullYear().toString();
+ const month = (issued.getMonth() + 1).toString();
+ const day = issued.getDate().toString();
- const issued = invoiceOut.issued;
- const year = issued.getFullYear().toString();
- const month = (issued.getMonth() + 1).toString();
- const day = issued.getDate().toString();
+ const container = await models.InvoiceContainer.container(year);
+ const rootPath = container.client.root;
+ const fileName = `${year}${invoiceOut.ref}.pdf`;
+ const src = path.join(rootPath, year, month, day);
+ fileSrc = path.join(src, fileName);
- const container = await models.InvoiceContainer.container(year);
- const rootPath = container.client.root;
- const fileName = `${year}${invoiceOut.ref}.pdf`;
- const src = path.join(rootPath, year, month, day);
- fileSrc = path.join(src, fileName);
+ await fs.mkdir(src, {recursive: true});
- await fs.mkdir(src, {recursive: true});
+ if (tx) await tx.commit();
- if (tx) await tx.commit();
+ response.data.pipe(fs.createWriteStream(fileSrc));
+ }).catch(async e => {
+ if (fs.existsSync(fileSrc))
+ await fs.unlink(fileSrc);
- const writeStream = fs.createWriteStream(fileSrc);
- writeStream.on('open', () => response.pipe(writeStream));
- writeStream.on('finish', () => writeStream.end());
-
- return new Promise(resolve => {
- writeStream.on('close', () => resolve(invoiceOut));
+ throw e;
});
} catch (e) {
if (tx) await tx.rollback();
- if (fs.existsSync(fileSrc))
- await fs.unlink(fileSrc);
throw e;
}
};
diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/createPdf.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/createPdf.spec.js
index 2f503d11c..7600f065f 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/specs/createPdf.spec.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/specs/createPdf.spec.js
@@ -1,24 +1,28 @@
const models = require('vn-loopback/server/server').models;
-const got = require('got');
+const LoopBackContext = require('loopback-context');
const fs = require('fs-extra');
+const axios = require('axios');
describe('InvoiceOut createPdf()', () => {
const userId = 1;
- const ctx = {
- req: {
-
- accessToken: {userId: userId},
- headers: {origin: 'http://localhost:5000'},
- }
+ const activeCtx = {
+ accessToken: {userId: userId, id: 'DEFAULT_TOKEN'},
+ headers: {origin: 'http://localhost:5000'}
};
+ const ctx = {req: activeCtx};
it('should create a new PDF file and set true the hasPdf property', async() => {
const invoiceId = 1;
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
+ active: activeCtx
+ });
const response = {
- pipe: () => {},
- on: () => {},
+ data: {
+ pipe: () => {},
+ on: () => {},
+ }
};
- spyOn(got, 'stream').and.returnValue(response);
+ spyOn(axios, 'get').and.returnValue(new Promise(resolve => resolve(response)));
spyOn(models.InvoiceContainer, 'container').and.returnValue({
client: {root: '/path'}
});
@@ -32,9 +36,10 @@ describe('InvoiceOut createPdf()', () => {
const options = {transaction: tx};
try {
- const result = await models.InvoiceOut.createPdf(ctx, invoiceId, options);
+ await models.InvoiceOut.createPdf(ctx, invoiceId, options);
+ const invoiceOut = await models.InvoiceOut.findById(invoiceId, null, options);
- expect(result.hasPdf).toBe(true);
+ expect(invoiceOut.hasPdf).toBe(true);
await tx.rollback();
} catch (e) {
diff --git a/modules/invoiceOut/front/descriptor-menu/index.js b/modules/invoiceOut/front/descriptor-menu/index.js
index b6e7e0261..7738845f9 100644
--- a/modules/invoiceOut/front/descriptor-menu/index.js
+++ b/modules/invoiceOut/front/descriptor-menu/index.js
@@ -19,6 +19,10 @@ class Controller extends Section {
this.id = value.id;
}
+ get hasInvoicing() {
+ return this.aclService.hasAny(['invoicing']);
+ }
+
loadData() {
const filter = {
include: [
@@ -51,8 +55,13 @@ class Controller extends Section {
deleteInvoiceOut() {
return this.$http.post(`InvoiceOuts/${this.invoiceOut.id}/delete`)
- .then(() => this.$state.go('invoiceOut.index'))
- .then(() => this.$state.reload())
+ .then(() => {
+ const isInsideInvoiceOut = this.$state.current.name.startsWith('invoiceOut');
+ if (isInsideInvoiceOut)
+ this.$state.go('invoiceOut.index');
+ else
+ this.$state.reload();
+ })
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut deleted')));
}
diff --git a/modules/invoiceOut/front/descriptor-menu/index.spec.js b/modules/invoiceOut/front/descriptor-menu/index.spec.js
index 19822f094..fced12e0d 100644
--- a/modules/invoiceOut/front/descriptor-menu/index.spec.js
+++ b/modules/invoiceOut/front/descriptor-menu/index.spec.js
@@ -50,6 +50,35 @@ describe('vnInvoiceOutDescriptorMenu', () => {
});
});
+ describe('deleteInvoiceOut()', () => {
+ it(`should make a query and call showSuccess()`, () => {
+ controller.invoiceOut = invoiceOut;
+ controller.$state.reload = jest.fn();
+ jest.spyOn(controller.vnApp, 'showSuccess');
+
+ $httpBackend.expectPOST(`InvoiceOuts/${invoiceOut.id}/delete`).respond();
+ controller.deleteInvoiceOut();
+ $httpBackend.flush();
+
+ expect(controller.$state.reload).toHaveBeenCalled();
+ expect(controller.vnApp.showSuccess).toHaveBeenCalled();
+ });
+
+ it(`should make a query and call showSuccess() after state.go if the state wasn't in invoiceOut module`, () => {
+ controller.invoiceOut = invoiceOut;
+ jest.spyOn(controller.$state, 'go').mockReturnValue('ok');
+ jest.spyOn(controller.vnApp, 'showSuccess');
+ controller.$state.current.name = 'invoiceOut.card.something';
+
+ $httpBackend.expectPOST(`InvoiceOuts/${invoiceOut.id}/delete`).respond();
+ controller.deleteInvoiceOut();
+ $httpBackend.flush();
+
+ expect(controller.$state.go).toHaveBeenCalledWith('invoiceOut.index');
+ expect(controller.vnApp.showSuccess).toHaveBeenCalled();
+ });
+ });
+
describe('sendPdfInvoice()', () => {
it('should make a query to the email invoice endpoint and show a message snackbar', () => {
jest.spyOn(controller.vnApp, 'showMessage');
diff --git a/modules/monitor/front/index/tickets/index.html b/modules/monitor/front/index/tickets/index.html
index 065a591c3..23253259a 100644
--- a/modules/monitor/front/index/tickets/index.html
+++ b/modules/monitor/front/index/tickets/index.html
@@ -1,8 +1,8 @@
+ auto-load="false"
+ limit="20">
this.reload())
.then(() => {
- this.$state.go('ticket.index');
+ const isInsideTicket = this.$state.current.name.startsWith('ticket');
+ if (isInsideTicket)
+ this.$state.go('ticket.index');
+
this.vnApp.showSuccess(this.$t('Ticket deleted. You can undo this action within the first hour'));
});
}
diff --git a/modules/ticket/front/descriptor-menu/index.spec.js b/modules/ticket/front/descriptor-menu/index.spec.js
index e9486bcd0..288c7508b 100644
--- a/modules/ticket/front/descriptor-menu/index.spec.js
+++ b/modules/ticket/front/descriptor-menu/index.spec.js
@@ -78,9 +78,22 @@ describe('Ticket Component vnTicketDescriptorMenu', () => {
describe('deleteTicket()', () => {
it('should make a query and call vnApp.showSuccess()', () => {
+ jest.spyOn(controller, 'reload').mockReturnThis();
+ jest.spyOn(controller.vnApp, 'showSuccess');
+
+ $httpBackend.expectPOST(`Tickets/${ticket.id}/setDeleted`).respond();
+ controller.deleteTicket();
+ $httpBackend.flush();
+
+ expect(controller.reload).toHaveBeenCalled();
+ expect(controller.vnApp.showSuccess).toHaveBeenCalled();
+ });
+
+ it(`should make a query and call showSuccess() after state.go if the state wasn't inside ticket module`, () => {
jest.spyOn(controller, 'reload').mockReturnThis();
jest.spyOn(controller.$state, 'go').mockReturnValue('ok');
jest.spyOn(controller.vnApp, 'showSuccess');
+ controller.$state.current.name = 'ticket.card.something';
$httpBackend.expectPOST(`Tickets/${ticket.id}/setDeleted`).respond();
controller.deleteTicket();
diff --git a/package.json b/package.json
index dc132131a..e5b817e20 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
"node": ">=14"
},
"dependencies": {
+ "axios": "^0.25.0",
"bmp-js": "^0.1.0",
"compression": "^1.7.3",
"fs-extra": "^5.0.0",
@@ -42,6 +43,7 @@
"strong-error-handler": "^2.3.2",
"uuid": "^3.3.3",
"vn-loopback": "file:./loopback",
+ "vn-print": "file:./print",
"xml2js": "^0.4.23"
},
"devDependencies": {
diff --git a/print/boot.js b/print/boot.js
index 8cd0eaad7..d5c06264c 100644
--- a/print/boot.js
+++ b/print/boot.js
@@ -1,11 +1,9 @@
const express = require('express');
const path = require('path');
const fs = require('fs');
-const puppeteer = require('puppeteer');
const templatesPath = path.resolve(__dirname, './templates');
const componentsPath = path.resolve(__dirname, './core/components');
-const config = require('./core/config');
module.exports = async app => {
global.appPath = __dirname;
@@ -53,21 +51,4 @@ module.exports = async app => {
app.use(`/api/${templateName}/assets`, express.static(assetsDir));
});
});
-
- // Instantiate Puppeteer browser
- async function launchBrowser() {
- config.browser = await puppeteer.launch({
- headless: true,
- args: [
- '--no-sandbox',
- '--disable-setuid-sandbox',
- '--single-process',
- '--no-zygote'
- ]
- });
-
- config.browser.on('disconnected', launchBrowser);
- }
-
- launchBrowser();
};
diff --git a/print/common/css/layout.css b/print/common/css/layout.css
index 4f521bea4..c1af4807d 100644
--- a/print/common/css/layout.css
+++ b/print/common/css/layout.css
@@ -6,6 +6,8 @@
.grid {
font-family: Arial, Helvetica, sans-serif;
font-size: 16px !important;
+ max-width: 1200px;
+ margin: 0 auto;
width: 100%
}
diff --git a/print/config/print.json b/print/config/print.json
index ceb7cbb28..d288c585d 100755
--- a/print/config/print.json
+++ b/print/config/print.json
@@ -48,6 +48,12 @@
"pool": true
},
"storage": {
- "root": "./storage/dms"
+ "root": "./storage/dms",
+ "invoice": {
+ "root": "./storage/pdfs/invoice"
+ },
+ "signature": {
+ "root": "./storage/signatures"
+ }
}
}
\ No newline at end of file
diff --git a/print/core/component.js b/print/core/component.js
index 12474566e..37656c240 100644
--- a/print/core/component.js
+++ b/print/core/component.js
@@ -27,29 +27,50 @@ class Component {
get locale() {
if (!this._locale)
- this.getLocale();
+ this._locale = this.getLocales();
return this._locale;
}
- getLocale() {
- const mergedLocale = {messages: {}};
+ getLocales() {
+ const mergedLocales = {messages: {}};
const localePath = path.resolve(__dirname, `${this.path}/locale`);
if (!fs.existsSync(localePath))
- return mergedLocale;
+ return mergedLocales;
const localeDir = fs.readdirSync(localePath);
- localeDir.forEach(locale => {
+ for (const locale of localeDir) {
const fullPath = path.join(localePath, '/', locale);
const yamlLocale = fs.readFileSync(fullPath, 'utf8');
const jsonLocale = yaml.safeLoad(yamlLocale);
const localeName = locale.replace('.yml', '');
- mergedLocale.messages[localeName] = jsonLocale;
- });
+ mergedLocales.messages[localeName] = jsonLocale;
+ }
- this._locale = mergedLocale;
+ return mergedLocales;
+ }
+
+ async getUserLocale() {
+ let locale = this.args.auth.locale;
+
+ // Fetches user locale from mixing method getLocale()
+ if (this.args.recipientId) {
+ const component = await this.component();
+ locale = await component.getLocale(this.args.recipientId);
+ }
+
+ const messages = this.locale.messages;
+ const userTranslations = messages[locale];
+
+ if (!userTranslations) {
+ const fallbackLocale = config.i18n.fallbackLocale;
+
+ return messages[fallbackLocale];
+ }
+
+ return userTranslations;
}
get stylesheet() {
@@ -75,7 +96,7 @@ class Component {
build() {
const fullPath = path.resolve(__dirname, this.path);
if (!fs.existsSync(fullPath))
- throw new Error(`Sample "${this.name}" not found`);
+ throw new Error(`Template "${this.name}" not found`);
const component = require(`${this.path}/${this.name}`);
component.i18n = this.locale;
diff --git a/print/core/components/report-header/report-header.html b/print/core/components/report-header/report-header.html
index 2667f14ed..0479e5caf 100644
--- a/print/core/components/report-header/report-header.html
+++ b/print/core/components/report-header/report-header.html
@@ -1,5 +1,9 @@
-
+
{{companyName}}. {{company.street}}.
{{company.postCode}} {{company.city}}.
diff --git a/print/core/components/report-header/report-header.js b/print/core/components/report-header/report-header.js
index 58c06bc1a..50c3a1337 100755
--- a/print/core/components/report-header/report-header.js
+++ b/print/core/components/report-header/report-header.js
@@ -10,9 +10,16 @@ module.exports = {
},
computed: {
companyName() {
- if (!this.company.name) return;
+ if (this.company.name)
+ return this.company.name.toUpperCase();
- return this.company.name.toUpperCase();
+ return;
+ },
+ companyGroup() {
+ if (this.company.groupName)
+ return this.company.groupName.toLowerCase();
+
+ return;
},
companyPhone() {
if (!this.company.phone) return;
@@ -30,8 +37,15 @@ module.exports = {
methods: {
getCompany(code) {
return db.findOne(`
- SELECT s.name, s.street, s.postCode, s.city, s.phone
+ SELECT
+ s.name,
+ s.street,
+ s.postCode,
+ s.city,
+ s.phone,
+ cg.code AS groupName
FROM company c
+ JOIN companyGroup cg ON cg.id = c.companyGroupFk
JOIN supplier s ON s.id = c.id
WHERE c.code = ?`, [code]);
},
diff --git a/print/core/email.js b/print/core/email.js
index 620c1e083..bc8345cab 100644
--- a/print/core/email.js
+++ b/print/core/email.js
@@ -19,22 +19,7 @@ class Email extends Component {
}
async getSubject() {
- const component = await this.component();
- let locale = this.args.auth.locale;
-
- if (this.args.recipientId)
- locale = await component.getLocale(this.args.recipientId);
-
- const messages = this.locale.messages;
- const userTranslations = messages[locale];
-
- if (!userTranslations) {
- const fallbackLocale = config.i18n.fallbackLocale;
-
- return messages[fallbackLocale].subject;
- }
-
- return userTranslations.subject;
+ return (await this.getUserLocale())['subject'];
}
/**
@@ -63,6 +48,7 @@ class Email extends Component {
const reportName = fileName.replace('.pdf', '');
const report = new Report(reportName, this.args);
fileCopy.content = await report.toPdfStream();
+ fileCopy.filename = await report.getFileName();
}
attachments.push(fileCopy);
diff --git a/print/core/report.js b/print/core/report.js
index b20b8e5df..093f5e99e 100644
--- a/print/core/report.js
+++ b/print/core/report.js
@@ -2,6 +2,7 @@ const fs = require('fs');
const path = require('path');
const config = require('./config');
const Component = require('./component');
+const puppeteer = require('puppeteer');
if (!process.env.OPENSSL_CONF)
process.env.OPENSSL_CONF = '/etc/ssl/';
@@ -17,6 +18,10 @@ class Report extends Component {
return `../templates/reports/${this.name}`;
}
+ async getName() {
+ return (await this.getUserLocale())['reportName'];
+ }
+
async toPdfStream() {
const template = await this.render();
const defaultOptions = Object.assign({}, config.pdf);
@@ -27,7 +32,17 @@ class Report extends Component {
if (fs.existsSync(fullPath))
options = require(optionsPath);
- const page = (await config.browser.pages())[0];
+ const browser = await puppeteer.launch({
+ headless: true,
+ args: [
+ '--no-sandbox',
+ '--disable-setuid-sandbox',
+ '--single-process',
+ '--no-zygote'
+ ]
+ });
+
+ const page = (await browser.pages())[0];
await page.emulateMedia('screen');
await page.setContent(template);
@@ -47,8 +62,43 @@ class Report extends Component {
const buffer = await page.pdf(options);
+ await browser.close();
+
return buffer;
}
+
+ /**
+ * Returns all the params that ends with id
+ *
+ * @return {array} List of identifiers
+ */
+ getIdentifiers() {
+ const identifiers = [];
+ const args = this.args;
+ const keys = Object.keys(args);
+
+ for (let arg of keys) {
+ if (arg.endsWith('Id'))
+ identifiers.push(arg);
+ }
+
+ return identifiers;
+ }
+
+ async getFileName() {
+ const args = this.args;
+ const identifiers = this.getIdentifiers(args);
+ const name = await this.getName();
+ const params = [];
+ params.push(name);
+
+ for (let id of identifiers)
+ params.push(args[id]);
+
+ const fileName = params.join('_');
+
+ return `${fileName}.pdf`;
+ }
}
module.exports = Report;
diff --git a/print/core/router.js b/print/core/router.js
index c0f20dd9a..cd64ba07e 100644
--- a/print/core/router.js
+++ b/print/core/router.js
@@ -1,43 +1,30 @@
-const path = require('path');
-const fs = require('fs');
const db = require('./database');
module.exports = app => {
- const methodsPath = path.resolve(__dirname, '../methods');
- const methodsDir = fs.readdirSync(methodsPath);
- const methods = [];
+ const routes = require('../methods/routes');
- // Get all methods
- for (let method of methodsDir) {
- if (method.includes('.js'))
- methods.push(method.replace('.js', ''));
- }
-
- // Auth middleware
- const paths = [];
- for (let method of methods)
- paths.push(`/api/${method}/*`);
-
- app.use(paths, async function(req, res, next) {
- const token = getToken(req);
- const query = `SELECT at.id, at.userId, eu.email, u.lang, at.ttl, at.created
- FROM salix.AccessToken at
- JOIN account.user u ON u.id = at.userid
- JOIN account.emailUser eu ON eu.userFk = u.id
- WHERE at.id = ?`;
+ const paths = routes.map(route => route.url);
+ app.use(paths, async function(request, response, next) {
try {
+ const token = getToken(request);
+ const query = `SELECT at.id, at.userId, eu.email, u.lang, at.ttl, at.created
+ FROM salix.AccessToken at
+ JOIN account.user u ON u.id = at.userid
+ JOIN account.emailUser eu ON eu.userFk = u.id
+ WHERE at.id = ?`;
+
const auth = await db.findOne(query, [token]);
if (!auth || isTokenExpired(auth.created, auth.ttl))
throw new Error('Invalid authorization token');
- const args = Object.assign({}, req.query);
- const props = Object.assign(args, req.body);
+ const args = Object.assign({}, request.query);
+ const props = Object.assign(args, request.body);
props.authorization = auth.id;
- req.args = props;
- req.args.auth = {
+ response.locals = props;
+ response.locals.auth = {
userId: auth.userId,
token: auth.id,
email: auth.email,
@@ -50,6 +37,10 @@ module.exports = app => {
}
});
+ // Register routes
+ for (let route of routes)
+ app.use(route.url, route.cb);
+
function getToken(request) {
const headers = request.headers;
const queryParams = request.query;
@@ -68,8 +59,4 @@ module.exports = app => {
return false;
}
-
- // Mount methods
- for (let method of methods)
- require(`../methods/${method}`)(app);
};
diff --git a/print/core/smtp.js b/print/core/smtp.js
index 36a76dbaf..50a413673 100644
--- a/print/core/smtp.js
+++ b/print/core/smtp.js
@@ -25,14 +25,17 @@ module.exports = {
throw err;
}).finally(async() => {
const attachments = [];
- for (let attachment of options.attachments) {
- const fileName = attachment.filename;
- const filePath = attachment.path;
- if (fileName.includes('.png')) return;
+ if (options.attachments) {
+ for (let attachment of options.attachments) {
+ const fileName = attachment.filename;
+ const filePath = attachment.path;
+ if (fileName.includes('.png')) return;
- if (fileName || filePath)
- attachments.push(filePath ? filePath : fileName);
+ if (fileName || filePath)
+ attachments.push(filePath ? filePath : fileName);
+ }
}
+
const fileNames = attachments.join(',\n');
await db.rawSql(`
INSERT INTO vn.mail (receiver, replyTo, sent, subject, body, attachment, status)
diff --git a/print/core/storage.js b/print/core/storage.js
new file mode 100644
index 000000000..063a2fbec
--- /dev/null
+++ b/print/core/storage.js
@@ -0,0 +1,28 @@
+const config = require('./config.js');
+const path = require('path');
+const fs = require('fs-extra');
+
+module.exports = {
+ async write(stream, options) {
+ const storage = config.storage[options.type];
+
+ if (!storage) return;
+
+ const src = path.join(storage.root, options.path);
+ const fileSrc = path.join(src, options.fileName);
+
+ await fs.mkdir(src, {recursive: true});
+
+ const writeStream = fs.createWriteStream(fileSrc);
+ writeStream.on('open', () => writeStream.write(stream));
+ writeStream.on('finish', () => writeStream.end());
+
+ return new Promise(resolve => {
+ writeStream.on('close', () => resolve());
+ });
+ },
+
+ load(type, data) {
+
+ }
+};
diff --git a/print/core/stylesheet.js b/print/core/stylesheet.js
index ffa141968..42a44fb57 100644
--- a/print/core/stylesheet.js
+++ b/print/core/stylesheet.js
@@ -7,9 +7,8 @@ class Stylesheet {
}
mergeStyles() {
- this.files.forEach(file => {
+ for (const file of this.files)
this.css.push(fs.readFileSync(file));
- });
return this.css.join('\n');
}
diff --git a/print/methods/closure.js b/print/methods/closure.js
deleted file mode 100644
index 07bd1768d..000000000
--- a/print/methods/closure.js
+++ /dev/null
@@ -1,311 +0,0 @@
-const db = require('../core/database');
-const Email = require('../core/email');
-const Report = require('../core/report');
-const smtp = require('../core/smtp');
-const config = require('../core/config');
-
-module.exports = app => {
- app.get('/api/closure/all', async function(req, res, next) {
- try {
- const reqArgs = req.args;
- if (!reqArgs.to)
- throw new Error('The argument to is required');
-
- res.status(200).json({
- message: 'Task executed successfully'
- });
-
- const tickets = await db.rawSql(`
- SELECT
- t.id
- FROM expedition e
- JOIN ticket t ON t.id = e.ticketFk
- JOIN warehouse wh ON wh.id = t.warehouseFk AND wh.hasComission
- JOIN ticketState ts ON ts.ticketFk = t.id
- JOIN alertLevel al ON al.id = ts.alertLevel
- WHERE al.code = 'PACKED'
- AND DATE(t.shipped) BETWEEN DATE_ADD(?, INTERVAL -2 DAY)
- AND util.dayEnd(?)
- AND t.refFk IS NULL
- GROUP BY e.ticketFk`, [reqArgs.to, reqArgs.to]);
- const ticketIds = tickets.map(ticket => ticket.id);
-
- await closeAll(ticketIds, req.args);
- await db.rawSql(`
- UPDATE ticket t
- JOIN ticketState ts ON t.id = ts.ticketFk
- JOIN alertLevel al ON al.id = ts.alertLevel
- JOIN agencyMode am ON am.id = t.agencyModeFk
- JOIN deliveryMethod dm ON dm.id = am.deliveryMethodFk
- JOIN zone z ON z.id = t.zoneFk
- SET t.routeFk = NULL
- WHERE DATE(t.shipped) BETWEEN DATE_ADD(?, INTERVAL -2 DAY)
- AND util.dayEnd(?)
- AND al.code NOT IN('DELIVERED','PACKED')
- AND t.routeFk
- AND z.name LIKE '%MADRID%'`, [reqArgs.to, reqArgs.to]);
- } catch (error) {
- next(error);
- }
- });
-
- app.get('/api/closure/by-ticket', async function(req, res, next) {
- try {
- const reqArgs = req.args;
- if (!reqArgs.ticketId)
- throw new Error('The argument ticketId is required');
-
- res.status(200).json({
- message: 'Task executed successfully'
- });
-
- const tickets = await db.rawSql(`
- SELECT
- t.id
- FROM expedition e
- JOIN ticket t ON t.id = e.ticketFk
- JOIN ticketState ts ON ts.ticketFk = t.id
- JOIN alertLevel al ON al.id = ts.alertLevel
- WHERE al.code = 'PACKED'
- AND t.id = ?
- AND t.refFk IS NULL
- GROUP BY e.ticketFk`, [reqArgs.ticketId]);
- const ticketIds = tickets.map(ticket => ticket.id);
-
- await closeAll(ticketIds, reqArgs);
- } catch (error) {
- next(error);
- }
- });
-
- app.get('/api/closure/by-agency', async function(req, res, next) {
- try {
- const reqArgs = req.args;
- if (!reqArgs.agencyModeId)
- throw new Error('The argument agencyModeId is required');
-
- if (!reqArgs.warehouseId)
- throw new Error('The argument warehouseId is required');
-
- if (!reqArgs.to)
- throw new Error('The argument to is required');
-
- res.status(200).json({
- message: 'Task executed successfully'
- });
-
- const agenciesId = reqArgs.agencyModeId.split(',');
- const tickets = await db.rawSql(`
- SELECT
- t.id
- FROM expedition e
- JOIN ticket t ON t.id = e.ticketFk
- JOIN ticketState ts ON ts.ticketFk = t.id
- JOIN alertLevel al ON al.id = ts.alertLevel
- WHERE al.code = 'PACKED'
- AND t.agencyModeFk IN(?)
- AND t.warehouseFk = ?
- AND DATE(t.shipped) BETWEEN DATE_ADD(:to, INTERVAL -2 DAY)
- AND util.dayEnd(?)
- AND t.refFk IS NULL
- GROUP BY e.ticketFk`, [
- agenciesId,
- reqArgs.warehouseId,
- reqArgs.to,
- reqArgs.to
- ]);
- const ticketIds = tickets.map(ticket => ticket.id);
-
- await closeAll(ticketIds, reqArgs);
- } catch (error) {
- next(error);
- }
- });
-
- app.get('/api/closure/by-route', async function(req, res, next) {
- try {
- const reqArgs = req.args;
- if (!reqArgs.routeId)
- throw new Error('The argument routeId is required');
-
- res.status(200).json({
- message: 'Task executed successfully'
- });
-
- const tickets = await db.rawSql(`
- SELECT
- t.id
- FROM expedition e
- JOIN ticket t ON t.id = e.ticketFk
- JOIN ticketState ts ON ts.ticketFk = t.id
- JOIN alertLevel al ON al.id = ts.alertLevel
- WHERE al.code = 'PACKED'
- AND t.routeFk = ?
- AND t.refFk IS NULL
- GROUP BY e.ticketFk`, [reqArgs.routeId]);
- const ticketIds = tickets.map(ticket => ticket.id);
- await closeAll(ticketIds, reqArgs);
-
- // Send route report to the agency
- const agencyMail = await db.findValue(`
- SELECT am.reportMail
- FROM route r
- JOIN agencyMode am ON am.id = r.agencyModeFk
- WHERE r.id = ?`, [reqArgs.routeId]);
-
- if (agencyMail) {
- const args = Object.assign({
- routeId: reqArgs.routeId,
- recipient: agencyMail
- }, reqArgs);
-
- const email = new Email('driver-route', args);
- await email.send();
- }
- } catch (error) {
- next(error);
- }
- });
-
- async function closeAll(ticketIds, reqArgs) {
- const failedtickets = [];
- const tickets = await db.rawSql(`
- SELECT
- t.id,
- t.clientFk,
- c.name clientName,
- c.email recipient,
- c.salesPersonFk,
- c.isToBeMailed,
- c.hasToInvoice,
- co.hasDailyInvoice,
- eu.email salesPersonEmail
- FROM ticket t
- JOIN client c ON c.id = t.clientFk
- JOIN province p ON p.id = c.provinceFk
- JOIN country co ON co.id = p.countryFk
- LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
- WHERE t.id IN(?)`, [ticketIds]);
-
- for (const ticket of tickets) {
- try {
- await db.rawSql(`CALL vn.ticket_closeByTicket(?)`, [ticket.id]);
-
- if (!ticket.salesPersonFk || !ticket.isToBeMailed) continue;
-
- if (!ticket.recipient) {
- const body = `No se ha podido enviar el albarán ${ticket.id}
- al cliente ${ticket.clientFk} porque no tiene un email especificado.
- Para dejar de recibir esta notificación, asígnale un email o desactiva
- la notificación por email para este cliente.`;
- smtp.send({
- to: ticket.salesPersonEmail,
- subject: 'No se ha podido enviar el albarán',
- html: body
- });
-
- continue;
- }
-
- const hasToInvoice = ticket.hasToInvoice && ticket.hasDailyInvoice;
- if (hasToInvoice) {
- const invoice = await db.findOne(`
- SELECT io.id, io.ref, io.serial, cny.code companyCode
- FROM ticket t
- JOIN invoiceOut io ON io.ref = t.refFk
- JOIN company cny ON cny.id = io.companyFk
- WHERE t.id = ?
- `, [ticket.id]);
-
- const args = Object.assign({
- invoiceId: invoice.id,
- recipientId: ticket.clientFk,
- recipient: ticket.recipient,
- replyTo: ticket.salesPersonEmail
- }, reqArgs);
-
- let mailOptions = {};
- if (invoice.serial == 'E' && invoice.companyCode == 'VNL') {
- const exportation = new Report('exportation', args);
- const stream = await exportation.toPdfStream();
- const fileName = `exportation-${invoice.ref}.pdf`;
- mailOptions = {
- overrideAttachments: false,
- attachments: [{
- filename: fileName,
- content: stream
- }]
- };
- }
-
- const email = new Email('invoice', args);
- await email.send(mailOptions);
- } else {
- const args = Object.assign({
- ticketId: ticket.id,
- recipientId: ticket.clientFk,
- recipient: ticket.recipient,
- replyTo: ticket.salesPersonEmail
- }, reqArgs);
-
- const email = new Email('delivery-note-link', args);
- await email.send();
- }
- } catch (error) {
- // Domain not found
- if (error.responseCode == 450)
- return invalidEmail(ticket);
-
- // Save tickets on a list of failed ids
- failedtickets.push({
- id: ticket.id,
- stacktrace: error
- });
- }
- }
-
- // Send email with failed tickets
- if (failedtickets.length > 0) {
- let body = 'This following tickets have failed:
';
-
- for (ticket of failedtickets) {
- body += `Ticket: ${ticket.id}
-
${ticket.stacktrace}
`;
- }
-
- smtp.send({
- to: config.app.reportEmail,
- subject: '[API] Nightly ticket closure report',
- html: body
- });
- }
- }
-
- async function invalidEmail(ticket) {
- await db.rawSql(`UPDATE client SET email = NULL WHERE id = ?`, [
- ticket.clientFk
- ]);
-
- const oldInstance = `{"email": "${ticket.recipient}"}`;
- const newInstance = `{"email": ""}`;
- await db.rawSql(`
- INSERT INTO clientLog (originFk, userFk, action, changedModel, oldInstance, newInstance)
- VALUES (?, NULL, 'UPDATE', 'Client', ?, ?)`, [
- ticket.clientFk,
- oldInstance,
- newInstance
- ]);
-
- const body = `No se ha podido enviar el albarán ${ticket.id}
- al cliente ${ticket.clientFk} - ${ticket.clientName}
- porque la dirección de email "${ticket.recipient}" no es correcta o no está disponible.
- Para evitar que se repita este error, se ha eliminado la dirección de email de la ficha del cliente.
- Actualiza la dirección de email con una correcta.`;
-
- smtp.send({
- to: ticket.salesPersonEmail,
- subject: 'No se ha podido enviar el albarán',
- html: body
- });
- }
-};
diff --git a/print/methods/closure/closeAll.js b/print/methods/closure/closeAll.js
new file mode 100644
index 000000000..7af3676f2
--- /dev/null
+++ b/print/methods/closure/closeAll.js
@@ -0,0 +1,58 @@
+const db = require('vn-print/core/database');
+const closure = require('./closure');
+
+module.exports = async function(request, response, next) {
+ try {
+ const reqArgs = request.query;
+ if (!reqArgs.to)
+ throw new Error('The argument to is required');
+
+ response.status(200).json({
+ message: 'Success'
+ });
+
+ const tickets = await db.rawSql(`
+ SELECT
+ t.id,
+ t.clientFk,
+ c.name clientName,
+ c.email recipient,
+ c.salesPersonFk,
+ c.isToBeMailed,
+ c.hasToInvoice,
+ co.hasDailyInvoice,
+ eu.email salesPersonEmail
+ FROM ticket t
+ JOIN agencyMode am ON am.id = t.agencyModeFk
+ JOIN warehouse wh ON wh.id = t.warehouseFk AND wh.hasComission
+ JOIN ticketState ts ON ts.ticketFk = t.id
+ JOIN alertLevel al ON al.id = ts.alertLevel
+ JOIN client c ON c.id = t.clientFk
+ JOIN province p ON p.id = c.provinceFk
+ JOIN country co ON co.id = p.countryFk
+ LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
+ WHERE al.code = 'PACKED'
+ AND DATE(t.shipped) BETWEEN DATE_ADD(?, INTERVAL -2 DAY)
+ AND util.dayEnd(?)
+ AND t.refFk IS NULL
+ GROUP BY t.id`, [reqArgs.to, reqArgs.to]);
+
+ await closure.start(tickets, response.locals);
+
+ await db.rawSql(`
+ UPDATE ticket t
+ JOIN ticketState ts ON t.id = ts.ticketFk
+ JOIN alertLevel al ON al.id = ts.alertLevel
+ JOIN agencyMode am ON am.id = t.agencyModeFk
+ JOIN deliveryMethod dm ON dm.id = am.deliveryMethodFk
+ JOIN zone z ON z.id = t.zoneFk
+ SET t.routeFk = NULL
+ WHERE DATE(t.shipped) BETWEEN DATE_ADD(?, INTERVAL -2 DAY)
+ AND util.dayEnd(?)
+ AND al.code NOT IN('DELIVERED','PACKED')
+ AND t.routeFk
+ AND z.name LIKE '%MADRID%'`, [reqArgs.to, reqArgs.to]);
+ } catch (error) {
+ next(error);
+ }
+};
diff --git a/print/methods/closure/closeByAgency.js b/print/methods/closure/closeByAgency.js
new file mode 100644
index 000000000..7807de23a
--- /dev/null
+++ b/print/methods/closure/closeByAgency.js
@@ -0,0 +1,58 @@
+const db = require('vn-print/core/database');
+const closure = require('./closure');
+
+module.exports = async function(request, response, next) {
+ try {
+ const reqArgs = request.query;
+
+ if (!reqArgs.agencyModeId)
+ throw new Error('The argument agencyModeId is required');
+
+ if (!reqArgs.warehouseId)
+ throw new Error('The argument warehouseId is required');
+
+ if (!reqArgs.to)
+ throw new Error('The argument to is required');
+
+ response.status(200).json({
+ message: 'Success'
+ });
+
+ const agencyIds = reqArgs.agencyModeId.split(',');
+ const tickets = await db.rawSql(`
+ SELECT
+ t.id,
+ t.clientFk,
+ c.name clientName,
+ c.email recipient,
+ c.salesPersonFk,
+ c.isToBeMailed,
+ c.hasToInvoice,
+ co.hasDailyInvoice,
+ eu.email salesPersonEmail
+ FROM expedition e
+ JOIN ticket t ON t.id = e.ticketFk
+ JOIN ticketState ts ON ts.ticketFk = t.id
+ JOIN alertLevel al ON al.id = ts.alertLevel
+ JOIN client c ON c.id = t.clientFk
+ JOIN province p ON p.id = c.provinceFk
+ JOIN country co ON co.id = p.countryFk
+ LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
+ WHERE al.code = 'PACKED'
+ AND t.agencyModeFk IN(?)
+ AND t.warehouseFk = ?
+ AND DATE(t.shipped) BETWEEN DATE_ADD(?, INTERVAL -2 DAY)
+ AND util.dayEnd(?)
+ AND t.refFk IS NULL
+ GROUP BY e.ticketFk`, [
+ agencyIds,
+ reqArgs.warehouseId,
+ reqArgs.to,
+ reqArgs.to
+ ]);
+
+ await closure.start(tickets, response.locals);
+ } catch (error) {
+ next(error);
+ }
+};
diff --git a/print/methods/closure/closeByRoute.js b/print/methods/closure/closeByRoute.js
new file mode 100644
index 000000000..2c0bfd1eb
--- /dev/null
+++ b/print/methods/closure/closeByRoute.js
@@ -0,0 +1,61 @@
+const db = require('vn-print/core/database');
+const Email = require('vn-print/core/email');
+const closure = require('./closure');
+
+module.exports = async function(request, response, next) {
+ try {
+ const reqArgs = request.query;
+
+ if (!reqArgs.routeId)
+ throw new Error('The argument routeId is required');
+
+ response.status(200).json({
+ message: 'Success'
+ });
+
+ const tickets = await db.rawSql(`
+ SELECT
+ t.id,
+ t.clientFk,
+ c.name clientName,
+ c.email recipient,
+ c.salesPersonFk,
+ c.isToBeMailed,
+ c.hasToInvoice,
+ co.hasDailyInvoice,
+ eu.email salesPersonEmail
+ FROM expedition e
+ JOIN ticket t ON t.id = e.ticketFk
+ JOIN ticketState ts ON ts.ticketFk = t.id
+ JOIN alertLevel al ON al.id = ts.alertLevel
+ JOIN client c ON c.id = t.clientFk
+ JOIN province p ON p.id = c.provinceFk
+ JOIN country co ON co.id = p.countryFk
+ LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
+ WHERE al.code = 'PACKED'
+ AND t.routeFk = ?
+ AND t.refFk IS NULL
+ GROUP BY e.ticketFk`, [reqArgs.routeId]);
+
+ await closure.start(tickets, response.locals);
+
+ // Send route report to the agency
+ const agencyMail = await db.findValue(`
+ SELECT am.reportMail
+ FROM route r
+ JOIN agencyMode am ON am.id = r.agencyModeFk
+ WHERE r.id = ?`, [reqArgs.routeId]);
+
+ if (agencyMail) {
+ const args = Object.assign({
+ routeId: Number.parseInt(reqArgs.routeId),
+ recipient: agencyMail
+ }, response.locals);
+
+ const email = new Email('driver-route', args);
+ await email.send();
+ }
+ } catch (error) {
+ next(error);
+ }
+};
diff --git a/print/methods/closure/closeByTicket.js b/print/methods/closure/closeByTicket.js
new file mode 100644
index 000000000..c71b3ecd0
--- /dev/null
+++ b/print/methods/closure/closeByTicket.js
@@ -0,0 +1,43 @@
+const db = require('vn-print/core/database');
+const closure = require('./closure');
+
+module.exports = async function(request, response, next) {
+ try {
+ const reqArgs = request.query;
+
+ if (!reqArgs.ticketId)
+ throw new Error('The argument ticketId is required');
+
+ response.status(200).json({
+ message: 'Success'
+ });
+
+ const tickets = await db.rawSql(`
+ SELECT
+ t.id,
+ t.clientFk,
+ c.name clientName,
+ c.email recipient,
+ c.salesPersonFk,
+ c.isToBeMailed,
+ c.hasToInvoice,
+ co.hasDailyInvoice,
+ eu.email salesPersonEmail
+ FROM expedition e
+ JOIN ticket t ON t.id = e.ticketFk
+ JOIN ticketState ts ON ts.ticketFk = t.id
+ JOIN alertLevel al ON al.id = ts.alertLevel
+ JOIN client c ON c.id = t.clientFk
+ JOIN province p ON p.id = c.provinceFk
+ JOIN country co ON co.id = p.countryFk
+ LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
+ WHERE al.code = 'PACKED'
+ AND t.id = ?
+ AND t.refFk IS NULL
+ GROUP BY e.ticketFk`, [reqArgs.ticketId]);
+
+ await closure.start(tickets, response.locals);
+ } catch (error) {
+ next(error);
+ }
+};
diff --git a/print/methods/closure/closure.js b/print/methods/closure/closure.js
new file mode 100644
index 000000000..8cce8237c
--- /dev/null
+++ b/print/methods/closure/closure.js
@@ -0,0 +1,153 @@
+const db = require('vn-print/core/database');
+const Report = require('vn-print/core/report');
+const Email = require('vn-print/core/email');
+const smtp = require('vn-print/core/smtp');
+const config = require('vn-print/core/config');
+const storage = require('vn-print/core/storage');
+
+module.exports = {
+ async start(tickets, reqArgs) {
+ if (tickets.length == 0) return;
+
+ const failedtickets = [];
+ for (const ticket of tickets) {
+ try {
+ await db.rawSql('START TRANSACTION');
+
+ await db.rawSql(`CALL vn.ticket_closeByTicket(?)`, [ticket.id]);
+
+ const invoiceOut = await db.findOne(`
+ SELECT io.id, io.ref, io.serial, cny.code companyCode, io.issued
+ FROM ticket t
+ JOIN invoiceOut io ON io.ref = t.refFk
+ JOIN company cny ON cny.id = io.companyFk
+ WHERE t.id = ?
+ `, [ticket.id]);
+
+ const mailOptions = {
+ overrideAttachments: true,
+ attachments: []
+ };
+
+ const isToBeMailed = ticket.recipient && ticket.salesPersonFk && ticket.isToBeMailed;
+
+ if (invoiceOut) {
+ const args = Object.assign({
+ invoiceId: invoiceOut.id,
+ recipientId: ticket.clientFk,
+ recipient: ticket.recipient,
+ replyTo: ticket.salesPersonEmail
+ }, reqArgs);
+
+ const invoiceReport = new Report('invoice', args);
+ const stream = await invoiceReport.toPdfStream();
+
+ const issued = invoiceOut.issued;
+ const year = issued.getFullYear().toString();
+ const month = (issued.getMonth() + 1).toString();
+ const day = issued.getDate().toString();
+
+ const fileName = `${year}${invoiceOut.ref}.pdf`;
+
+ // Store invoice
+ storage.write(stream, {
+ type: 'invoice',
+ path: `${year}/${month}/${day}`,
+ fileName: fileName
+ });
+
+ await db.rawSql('UPDATE invoiceOut SET hasPdf = true WHERE id = ?', [invoiceOut.id]);
+
+ if (isToBeMailed) {
+ const invoiceAttachment = {
+ filename: fileName,
+ content: stream
+ };
+
+ if (invoiceOut.serial == 'E' && invoiceOut.companyCode == 'VNL') {
+ const exportation = new Report('exportation', args);
+ const stream = await exportation.toPdfStream();
+ const fileName = `CITES-${invoiceOut.ref}.pdf`;
+
+ mailOptions.attachments.push({
+ filename: fileName,
+ content: stream
+ });
+ }
+
+ mailOptions.attachments.push(invoiceAttachment);
+
+ const email = new Email('invoice', args);
+ await email.send(mailOptions);
+ }
+ } else if (isToBeMailed) {
+ const args = Object.assign({
+ ticketId: ticket.id,
+ recipientId: ticket.clientFk,
+ recipient: ticket.recipient,
+ replyTo: ticket.salesPersonEmail
+ }, reqArgs);
+
+ const email = new Email('delivery-note-link', args);
+ await email.send();
+ }
+ await db.rawSql('COMMIT');
+ } catch (error) {
+ await db.rawSql('ROLLBACK');
+ // Domain not found
+ if (error.responseCode == 450)
+ return invalidEmail(ticket);
+
+ // Save tickets on a list of failed ids
+ failedtickets.push({
+ id: ticket.id,
+ stacktrace: error
+ });
+ }
+ }
+
+ // Send email with failed tickets
+ if (failedtickets.length > 0) {
+ let body = 'This following tickets have failed:
';
+
+ for (const ticket of failedtickets) {
+ body += `Ticket: ${ticket.id}
+
${ticket.stacktrace}
`;
+ }
+
+ smtp.send({
+ to: config.app.reportEmail,
+ subject: '[API] Nightly ticket closure report',
+ html: body
+ });
+ }
+ },
+
+ async invalidEmail(ticket) {
+ await db.rawSql(`UPDATE client SET email = NULL WHERE id = ?`, [
+ ticket.clientFk
+ ]);
+
+ const oldInstance = `{"email": "${ticket.recipient}"}`;
+ const newInstance = `{"email": ""}`;
+ await db.rawSql(`
+ INSERT INTO clientLog (originFk, userFk, action, changedModel, oldInstance, newInstance)
+ VALUES (?, NULL, 'UPDATE', 'Client', ?, ?)`, [
+ ticket.clientFk,
+ oldInstance,
+ newInstance
+ ]);
+
+ const body = `No se ha podido enviar el albarán ${ticket.id}
+ al cliente ${ticket.clientFk} - ${ticket.clientName}
+ porque la dirección de email "${ticket.recipient}" no es correcta o no está disponible.
+ Para evitar que se repita este error, se ha eliminado la dirección de email de la ficha del cliente.
+ Actualiza la dirección de email con una correcta.`;
+
+ smtp.send({
+ to: ticket.salesPersonEmail,
+ subject: 'No se ha podido enviar el albarán',
+ html: body
+ });
+ }
+};
diff --git a/print/methods/closure/index.js b/print/methods/closure/index.js
new file mode 100644
index 000000000..fcca76f71
--- /dev/null
+++ b/print/methods/closure/index.js
@@ -0,0 +1,9 @@
+const express = require('express');
+const router = new express.Router();
+
+router.get('/all', require('./closeAll'));
+router.get('/by-ticket', require('./closeByTicket'));
+router.get('/by-agency', require('./closeByAgency'));
+router.get('/by-route', require('./closeByRoute'));
+
+module.exports = router;
diff --git a/print/methods/csv.js b/print/methods/csv.js
deleted file mode 100644
index 4f4cdf2af..000000000
--- a/print/methods/csv.js
+++ /dev/null
@@ -1,31 +0,0 @@
-module.exports = app => {
- app.use('/api/csv/delivery-note', require('./csv/delivery-note')(app));
- app.use('/api/csv/invoice', require('./csv/invoice')(app));
-
- app.toCSV = function toCSV(rows) {
- const [columns] = rows;
- let content = Object.keys(columns).join('\t');
- for (let row of rows) {
- const values = Object.values(row);
- const finalValues = values.map(value => {
- if (value instanceof Date) return formatDate(value);
- if (value === null) return '';
- return value;
- });
- content += '\n';
- content += finalValues.join('\t');
- }
- return content;
- };
-
- function formatDate(date) {
- return new Intl.DateTimeFormat('es', {
- year: 'numeric',
- month: 'numeric',
- day: 'numeric',
- hour: '2-digit',
- minute: '2-digit',
- second: '2-digit'
- }).format(date);
- }
-};
diff --git a/print/methods/csv/csv.js b/print/methods/csv/csv.js
new file mode 100644
index 000000000..d8725582d
--- /dev/null
+++ b/print/methods/csv/csv.js
@@ -0,0 +1,31 @@
+function toCSV(rows) {
+ const [columns] = rows;
+ let content = Object.keys(columns).join('\t');
+ for (let row of rows) {
+ const values = Object.values(row);
+ const finalValues = values.map(value => {
+ if (value instanceof Date) return formatDate(value);
+ if (value === null) return '';
+ return value;
+ });
+ content += '\n';
+ content += finalValues.join('\t');
+ }
+ return content;
+}
+
+function formatDate(date) {
+ return new Intl.DateTimeFormat('es', {
+ year: 'numeric',
+ month: 'numeric',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit'
+ }).format(date);
+}
+
+module.exports = {
+ toCSV,
+ formatDate
+};
diff --git a/print/methods/csv/delivery-note/download.js b/print/methods/csv/delivery-note/download.js
new file mode 100644
index 000000000..d369d5f4a
--- /dev/null
+++ b/print/methods/csv/delivery-note/download.js
@@ -0,0 +1,24 @@
+const path = require('path');
+const db = require('vn-print/core/database');
+
+const {toCSV} = require('../csv');
+const sqlPath = path.join(__dirname, 'sql');
+
+module.exports = async function(request, response, next) {
+ try {
+ const reqArgs = request.query;
+ if (!reqArgs.ticketId)
+ throw new Error('The argument ticketId is required');
+
+ const ticketId = reqArgs.ticketId;
+ const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [ticketId]);
+ const content = toCSV(sales);
+ const fileName = `ticket_${ticketId}.csv`;
+
+ response.setHeader('Content-type', 'text/csv');
+ response.setHeader('Content-Disposition', `inline; filename="${fileName}"`);
+ response.end(content);
+ } catch (error) {
+ next(error);
+ }
+};
diff --git a/print/methods/csv/delivery-note/index.js b/print/methods/csv/delivery-note/index.js
deleted file mode 100644
index 9ef0e33fa..000000000
--- a/print/methods/csv/delivery-note/index.js
+++ /dev/null
@@ -1,82 +0,0 @@
-const express = require('express');
-const router = new express.Router();
-const path = require('path');
-const db = require('../../../core/database');
-const sqlPath = path.join(__dirname, 'sql');
-
-module.exports = app => {
- router.get('/preview', async function(req, res, next) {
- try {
- const reqArgs = req.args;
- if (!reqArgs.ticketId)
- throw new Error('The argument ticketId is required');
-
- const ticketId = reqArgs.ticketId;
- const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [ticketId]);
- const content = app.toCSV(sales);
- const fileName = `ticket_${ticketId}.csv`;
-
- res.setHeader('Content-type', 'application/json; charset=utf-8');
- res.setHeader('Content-Disposition', `inline; filename="${fileName}"`);
- res.end(content);
- } catch (error) {
- next(error);
- }
- });
-
- router.get('/download', async function(req, res, next) {
- try {
- const reqArgs = req.args;
- if (!reqArgs.ticketId)
- throw new Error('The argument ticketId is required');
-
- const ticketId = reqArgs.ticketId;
- const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [ticketId]);
- const content = app.toCSV(sales);
- const fileName = `ticket_${ticketId}.csv`;
-
- res.setHeader('Content-type', 'text/csv');
- res.setHeader('Content-Disposition', `inline; filename="${fileName}"`);
- res.end(content);
- } catch (error) {
- next(error);
- }
- });
-
- const Email = require('../../../core/email');
- router.get('/send', async function(req, res, next) {
- try {
- const reqArgs = req.args;
- if (!reqArgs.ticketId)
- throw new Error('The argument ticketId is required');
-
- const ticketId = reqArgs.ticketId;
- const ticket = await db.findOneFromDef(`${sqlPath}/ticket`, [ticketId]);
- const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [ticketId]);
-
- const args = Object.assign({
- ticketId: (String(ticket.id)),
- recipientId: ticket.clientFk,
- recipient: ticket.recipient,
- replyTo: ticket.salesPersonEmail
- }, reqArgs);
-
- const content = app.toCSV(sales);
- const fileName = `ticket_${ticketId}.csv`;
- const email = new Email('delivery-note', args);
- await email.send({
- overrideAttachments: true,
- attachments: [{
- filename: fileName,
- content: content
- }]
- });
-
- res.status(200).json({message: 'ok'});
- } catch (error) {
- next(error);
- }
- });
-
- return router;
-};
diff --git a/print/methods/csv/delivery-note/send.js b/print/methods/csv/delivery-note/send.js
new file mode 100644
index 000000000..478f20f57
--- /dev/null
+++ b/print/methods/csv/delivery-note/send.js
@@ -0,0 +1,40 @@
+const path = require('path');
+const db = require('vn-print/core/database');
+const Email = require('vn-print/core/email');
+
+const {toCSV} = require('../csv');
+const sqlPath = path.join(__dirname, 'sql');
+
+module.exports = async function(request, response, next) {
+ try {
+ const reqArgs = request.query;
+ if (!reqArgs.ticketId)
+ throw new Error('The argument ticketId is required');
+
+ const ticketId = reqArgs.ticketId;
+ const ticket = await db.findOneFromDef(`${sqlPath}/ticket`, [ticketId]);
+ const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [ticketId]);
+
+ const args = Object.assign({
+ ticketId: (String(ticket.id)),
+ recipientId: ticket.clientFk,
+ recipient: ticket.recipient,
+ replyTo: ticket.salesPersonEmail
+ }, response.locals);
+
+ const content = toCSV(sales);
+ const fileName = `ticket_${ticketId}.csv`;
+ const email = new Email('delivery-note', args);
+ await email.send({
+ overrideAttachments: true,
+ attachments: [{
+ filename: fileName,
+ content: content
+ }]
+ });
+
+ response.status(200).json({message: 'Success'});
+ } catch (error) {
+ next(error);
+ }
+};
diff --git a/print/methods/csv/index.js b/print/methods/csv/index.js
new file mode 100644
index 000000000..6bdd1b60d
--- /dev/null
+++ b/print/methods/csv/index.js
@@ -0,0 +1,9 @@
+const express = require('express');
+const router = new express.Router();
+
+router.get('/delivery-note/download', require('./delivery-note/download'));
+router.get('/delivery-note/send', require('./delivery-note/send'));
+router.get('/invoice/download', require('./invoice/download'));
+router.get('/invoice/send', require('./invoice/send'));
+
+module.exports = router;
diff --git a/print/methods/csv/invoice/download.js b/print/methods/csv/invoice/download.js
new file mode 100644
index 000000000..593d2d8d0
--- /dev/null
+++ b/print/methods/csv/invoice/download.js
@@ -0,0 +1,24 @@
+const path = require('path');
+const db = require('vn-print/core/database');
+
+const {toCSV} = require('../csv');
+const sqlPath = path.join(__dirname, 'sql');
+
+module.exports = async function(request, response, next) {
+ try {
+ const reqArgs = request.query;
+ if (!reqArgs.invoiceId)
+ throw new Error('The argument invoiceId is required');
+
+ const invoiceId = reqArgs.invoiceId;
+ const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [invoiceId]);
+ const content = toCSV(sales);
+ const fileName = `invoice_${invoiceId}.csv`;
+
+ response.setHeader('Content-type', 'text/csv');
+ response.setHeader('Content-Disposition', `inline; filename="${fileName}"`);
+ response.end(content);
+ } catch (error) {
+ next(error);
+ }
+};
diff --git a/print/methods/csv/invoice/index.js b/print/methods/csv/invoice/index.js
deleted file mode 100644
index 8f325be02..000000000
--- a/print/methods/csv/invoice/index.js
+++ /dev/null
@@ -1,82 +0,0 @@
-const express = require('express');
-const router = new express.Router();
-const path = require('path');
-const db = require('../../../core/database');
-const sqlPath = path.join(__dirname, 'sql');
-
-module.exports = app => {
- router.get('/preview', async function(req, res, next) {
- try {
- const reqArgs = req.args;
- if (!reqArgs.invoiceId)
- throw new Error('The argument invoiceId is required');
-
- const invoiceId = reqArgs.invoiceId;
- const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [invoiceId]);
- const content = app.toCSV(sales);
- const fileName = `invoice_${invoiceId}.csv`;
-
- res.setHeader('Content-type', 'application/json; charset=utf-8');
- res.setHeader('Content-Disposition', `inline; filename="${fileName}"`);
- res.end(content);
- } catch (error) {
- next(error);
- }
- });
-
- router.get('/download', async function(req, res, next) {
- try {
- const reqArgs = req.args;
- if (!reqArgs.invoiceId)
- throw new Error('The argument invoiceId is required');
-
- const invoiceId = reqArgs.invoiceId;
- const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [invoiceId]);
- const content = app.toCSV(sales);
- const fileName = `invoice_${invoiceId}.csv`;
-
- res.setHeader('Content-type', 'text/csv');
- res.setHeader('Content-Disposition', `inline; filename="${fileName}"`);
- res.end(content);
- } catch (error) {
- next(error);
- }
- });
-
- const Email = require('../../../core/email');
- router.get('/send', async function(req, res, next) {
- try {
- const reqArgs = req.args;
- if (!reqArgs.invoiceId)
- throw new Error('The argument invoiceId is required');
-
- const invoiceId = reqArgs.invoiceId;
- const invoice = await db.findOneFromDef(`${sqlPath}/invoice`, [invoiceId]);
- const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [invoiceId]);
-
- const args = Object.assign({
- invoiceId: (String(invoice.id)),
- recipientId: invoice.clientFk,
- recipient: invoice.recipient,
- replyTo: invoice.salesPersonEmail
- }, reqArgs);
-
- const content = app.toCSV(sales);
- const fileName = `invoice_${invoiceId}.csv`;
- const email = new Email('invoice', args);
- await email.send({
- overrideAttachments: true,
- attachments: [{
- filename: fileName,
- content: content
- }]
- });
-
- res.status(200).json({message: 'ok'});
- } catch (error) {
- next(error);
- }
- });
-
- return router;
-};
diff --git a/print/methods/csv/invoice/send.js b/print/methods/csv/invoice/send.js
new file mode 100644
index 000000000..919d7aeb1
--- /dev/null
+++ b/print/methods/csv/invoice/send.js
@@ -0,0 +1,40 @@
+const path = require('path');
+const db = require('vn-print/core/database');
+const Email = require('vn-print/core/email');
+
+const {toCSV} = require('../csv');
+const sqlPath = path.join(__dirname, 'sql');
+
+module.exports = async function(request, response, next) {
+ try {
+ const reqArgs = request.query;
+ if (!reqArgs.invoiceId)
+ throw new Error('The argument invoiceId is required');
+
+ const invoiceId = reqArgs.invoiceId;
+ const invoice = await db.findOneFromDef(`${sqlPath}/invoice`, [invoiceId]);
+ const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [invoiceId]);
+
+ const args = Object.assign({
+ invoiceId: (String(invoice.id)),
+ recipientId: invoice.clientFk,
+ recipient: invoice.recipient,
+ replyTo: invoice.salesPersonEmail
+ }, response.locals);
+
+ const content = toCSV(sales);
+ const fileName = `invoice_${invoiceId}.csv`;
+ const email = new Email('invoice', args);
+ await email.send({
+ overrideAttachments: true,
+ attachments: [{
+ filename: fileName,
+ content: content
+ }]
+ });
+
+ response.status(200).json({message: 'Success'});
+ } catch (error) {
+ next(error);
+ }
+};
diff --git a/print/methods/email.js b/print/methods/email.js
deleted file mode 100644
index cc4590e5d..000000000
--- a/print/methods/email.js
+++ /dev/null
@@ -1,33 +0,0 @@
-const Email = require('../core/email');
-
-module.exports = app => {
- app.get(`/api/email/:name`, async(req, res, next) => {
- try {
- const reportName = req.params.name;
- const email = new Email(reportName, req.args);
-
- await email.send();
-
- res.status(200).json({
- message: 'Sent'
- });
- } catch (e) {
- next(e);
- }
- });
-
- app.get(`/api/email/:name/preview`, async(req, res, next) => {
- try {
- const reportName = req.params.name;
- const args = req.args;
- args.isPreview = true;
-
- const email = new Email(reportName, args);
- const rendered = await email.render();
-
- res.send(rendered);
- } catch (e) {
- next(e);
- }
- });
-};
diff --git a/print/methods/email/email.js b/print/methods/email/email.js
new file mode 100644
index 000000000..5d6882f7d
--- /dev/null
+++ b/print/methods/email/email.js
@@ -0,0 +1,16 @@
+const Email = require('vn-print/core/email');
+
+module.exports = async function(request, response, next) {
+ try {
+ const templateName = request.params.name;
+ const args = response.locals;
+ const email = new Email(templateName, args);
+ await email.send();
+
+ response.status(200).json({
+ message: 'Sent'
+ });
+ } catch (error) {
+ next(error);
+ }
+};
diff --git a/print/methods/email/index.js b/print/methods/email/index.js
new file mode 100644
index 000000000..10c2d2325
--- /dev/null
+++ b/print/methods/email/index.js
@@ -0,0 +1,7 @@
+const express = require('express');
+const router = new express.Router();
+
+router.get('/:name', require('./email'));
+router.get('/:name/preview', require('./preview'));
+
+module.exports = router;
diff --git a/print/methods/email/preview.js b/print/methods/email/preview.js
new file mode 100644
index 000000000..e6a1aaf35
--- /dev/null
+++ b/print/methods/email/preview.js
@@ -0,0 +1,14 @@
+const Email = require('vn-print/core/email');
+
+module.exports = async function(request, response, next) {
+ try {
+ const templateName = request.params.name;
+ const args = Object.assign({isPreview: true}, response.locals);
+ const email = new Email(templateName, args);
+ const template = await email.render();
+
+ response.send(template);
+ } catch (error) {
+ next(error);
+ }
+};
diff --git a/print/methods/report.js b/print/methods/report.js
deleted file mode 100644
index 750fec4c8..000000000
--- a/print/methods/report.js
+++ /dev/null
@@ -1,54 +0,0 @@
-const Report = require('../core/report');
-
-module.exports = app => {
- app.get(`/api/report/:name`, async(req, res, next) => {
- try {
- const reportName = req.params.name;
- const fileName = getFileName(reportName, req.args);
- const report = new Report(reportName, req.args);
- if (req.args.preview) {
- const template = await report.render();
- res.send(template);
- } else {
- const stream = await report.toPdfStream();
-
- res.setHeader('Content-type', 'application/pdf');
- res.setHeader('Content-Disposition', `inline; filename="${fileName}"`);
- res.end(stream);
- }
- } catch (error) {
- next(error);
- }
- });
-
- /**
- * Returns all the params that ends with id
- * @param {object} args - Params object
- *
- * @return {array} List of identifiers
- */
- function getIdentifiers(args) {
- const identifiers = [];
- const keys = Object.keys(args);
-
- for (let arg of keys) {
- if (arg.endsWith('Id'))
- identifiers.push(arg);
- }
-
- return identifiers;
- }
-
- function getFileName(name, args) {
- const identifiers = getIdentifiers(args);
- const params = [];
- params.push(name);
-
- for (let id of identifiers)
- params.push(args[id]);
-
- const fileName = params.join('_');
-
- return `${fileName}.pdf`;
- }
-};
diff --git a/print/methods/report/document.js b/print/methods/report/document.js
new file mode 100644
index 000000000..b24abf4ac
--- /dev/null
+++ b/print/methods/report/document.js
@@ -0,0 +1,17 @@
+const Report = require('vn-print/core/report');
+
+module.exports = async function(request, response, next) {
+ try {
+ const reportName = request.params.name;
+ const args = response.locals;
+ const report = new Report(reportName, args);
+ const stream = await report.toPdfStream();
+ const fileName = await report.getFileName();
+
+ response.setHeader('Content-type', 'application/pdf');
+ response.setHeader('Content-Disposition', `inline; filename="${fileName}"`);
+ response.end(stream);
+ } catch (error) {
+ next(error);
+ }
+};
diff --git a/print/methods/report/index.js b/print/methods/report/index.js
new file mode 100644
index 000000000..c422c76df
--- /dev/null
+++ b/print/methods/report/index.js
@@ -0,0 +1,7 @@
+const express = require('express');
+const router = new express.Router();
+
+router.get('/:name', require('./document'));
+router.get('/:name/preview', require('./preview'));
+
+module.exports = router;
diff --git a/print/methods/report/preview.js b/print/methods/report/preview.js
new file mode 100644
index 000000000..0d6ad6f43
--- /dev/null
+++ b/print/methods/report/preview.js
@@ -0,0 +1,13 @@
+const Report = require('vn-print/core/report');
+
+module.exports = async function(request, response, next) {
+ try {
+ const reportName = request.params.name;
+ const report = new Report(reportName, request.query);
+ const template = await report.render();
+
+ response.send(template);
+ } catch (error) {
+ next(error);
+ }
+};
diff --git a/print/methods/routes.js b/print/methods/routes.js
new file mode 100644
index 000000000..0c452028e
--- /dev/null
+++ b/print/methods/routes.js
@@ -0,0 +1,18 @@
+module.exports = [
+ {
+ url: '/api/report',
+ cb: require('./report')
+ },
+ {
+ url: '/api/email',
+ cb: require('./email')
+ },
+ {
+ url: '/api/csv',
+ cb: require('./csv')
+ },
+ {
+ url: '/api/closure',
+ cb: require('./closure')
+ },
+];
diff --git a/print/templates/email/campaign-metrics/campaign-metrics.js b/print/templates/email/campaign-metrics/campaign-metrics.js
index 0ace0fc25..2bd93b725 100755
--- a/print/templates/email/campaign-metrics/campaign-metrics.js
+++ b/print/templates/email/campaign-metrics/campaign-metrics.js
@@ -21,6 +21,7 @@ module.exports = {
},
props: {
recipientId: {
+ type: [Number, String],
required: true
},
from: {
diff --git a/print/templates/email/claim-pickup-order/claim-pickup-order.js b/print/templates/email/claim-pickup-order/claim-pickup-order.js
index 4396b144a..cf4ba7d12 100755
--- a/print/templates/email/claim-pickup-order/claim-pickup-order.js
+++ b/print/templates/email/claim-pickup-order/claim-pickup-order.js
@@ -10,6 +10,7 @@ module.exports = {
},
props: {
claimId: {
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/email/client-debt-statement/client-debt-statement.js b/print/templates/email/client-debt-statement/client-debt-statement.js
index c32e68943..f32f9e239 100755
--- a/print/templates/email/client-debt-statement/client-debt-statement.js
+++ b/print/templates/email/client-debt-statement/client-debt-statement.js
@@ -16,6 +16,7 @@ module.exports = {
},
props: {
recipientId: {
+ type: [Number, String],
required: true
},
from: {
diff --git a/print/templates/email/client-welcome/client-welcome.js b/print/templates/email/client-welcome/client-welcome.js
index f562339cc..eeb11bb78 100755
--- a/print/templates/email/client-welcome/client-welcome.js
+++ b/print/templates/email/client-welcome/client-welcome.js
@@ -18,6 +18,7 @@ module.exports = {
},
props: {
recipientId: {
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/email/delivery-note-link/delivery-note-link.js b/print/templates/email/delivery-note-link/delivery-note-link.js
index 009fe7b5b..471b370d9 100755
--- a/print/templates/email/delivery-note-link/delivery-note-link.js
+++ b/print/templates/email/delivery-note-link/delivery-note-link.js
@@ -10,6 +10,7 @@ module.exports = {
},
props: {
ticketId: {
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/email/delivery-note/delivery-note.js b/print/templates/email/delivery-note/delivery-note.js
index 64839b8e0..ffd2fe202 100755
--- a/print/templates/email/delivery-note/delivery-note.js
+++ b/print/templates/email/delivery-note/delivery-note.js
@@ -10,7 +10,7 @@ module.exports = {
},
props: {
ticketId: {
- type: String,
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/email/driver-route/driver-route.js b/print/templates/email/driver-route/driver-route.js
index de1dd9c39..378cd82ce 100755
--- a/print/templates/email/driver-route/driver-route.js
+++ b/print/templates/email/driver-route/driver-route.js
@@ -10,7 +10,7 @@ module.exports = {
},
props: {
routeId: {
- type: String,
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/email/invoice/invoice.js b/print/templates/email/invoice/invoice.js
index b8d3b8282..d92b65cb3 100755
--- a/print/templates/email/invoice/invoice.js
+++ b/print/templates/email/invoice/invoice.js
@@ -18,7 +18,7 @@ module.exports = {
},
props: {
invoiceId: {
- type: String,
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/email/letter-debtor-nd/letter-debtor-nd.js b/print/templates/email/letter-debtor-nd/letter-debtor-nd.js
index ba9f7957d..5e010d1ba 100755
--- a/print/templates/email/letter-debtor-nd/letter-debtor-nd.js
+++ b/print/templates/email/letter-debtor-nd/letter-debtor-nd.js
@@ -30,9 +30,11 @@ module.exports = {
required: true
},
recipientId: {
+ type: [Number, String],
required: true
},
companyId: {
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/email/letter-debtor-st/letter-debtor-st.js b/print/templates/email/letter-debtor-st/letter-debtor-st.js
index 56fc7c8a8..a514097cf 100755
--- a/print/templates/email/letter-debtor-st/letter-debtor-st.js
+++ b/print/templates/email/letter-debtor-st/letter-debtor-st.js
@@ -1,5 +1,4 @@
const Component = require(`${appPath}/core/component`);
-const db = require(`${appPath}/core/database`);
const emailHeader = new Component('email-header');
const emailFooter = new Component('email-footer');
const attachment = new Component('attachment');
@@ -28,9 +27,11 @@ module.exports = {
},
props: {
recipientId: {
+ type: [Number, String],
required: true
},
companyId: {
+ type: [Number, String],
required: true
},
}
diff --git a/print/templates/email/payment-update/payment-update.js b/print/templates/email/payment-update/payment-update.js
index eb6690c02..2b92976a3 100755
--- a/print/templates/email/payment-update/payment-update.js
+++ b/print/templates/email/payment-update/payment-update.js
@@ -26,6 +26,7 @@ module.exports = {
},
props: {
recipientId: {
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/email/printer-setup/printer-setup.js b/print/templates/email/printer-setup/printer-setup.js
index f6f168163..95dff8ebb 100755
--- a/print/templates/email/printer-setup/printer-setup.js
+++ b/print/templates/email/printer-setup/printer-setup.js
@@ -24,6 +24,7 @@ module.exports = {
},
props: {
recipientId: {
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/email/sepa-core/sepa-core.js b/print/templates/email/sepa-core/sepa-core.js
index 76f8d842f..743c6719c 100755
--- a/print/templates/email/sepa-core/sepa-core.js
+++ b/print/templates/email/sepa-core/sepa-core.js
@@ -16,9 +16,11 @@ module.exports = {
},
props: {
recipientId: {
+ type: [Number, String],
required: true
},
companyId: {
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/email/supplier-campaign-metrics/supplier-campaign-metrics.js b/print/templates/email/supplier-campaign-metrics/supplier-campaign-metrics.js
index 20113d8ea..3cf290e4d 100755
--- a/print/templates/email/supplier-campaign-metrics/supplier-campaign-metrics.js
+++ b/print/templates/email/supplier-campaign-metrics/supplier-campaign-metrics.js
@@ -21,6 +21,7 @@ module.exports = {
},
props: {
recipientId: {
+ type: [Number, String],
required: true
},
from: {
diff --git a/print/templates/reports/campaign-metrics/campaign-metrics.js b/print/templates/reports/campaign-metrics/campaign-metrics.js
index 07d261a61..6669ce067 100755
--- a/print/templates/reports/campaign-metrics/campaign-metrics.js
+++ b/print/templates/reports/campaign-metrics/campaign-metrics.js
@@ -25,6 +25,7 @@ module.exports = {
},
props: {
recipientId: {
+ type: [Number, String],
required: true
},
from: {
diff --git a/print/templates/reports/campaign-metrics/locale/es.yml b/print/templates/reports/campaign-metrics/locale/es.yml
index 8a4cc4637..c455be5a8 100644
--- a/print/templates/reports/campaign-metrics/locale/es.yml
+++ b/print/templates/reports/campaign-metrics/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: consumo-cliente
title: Consumo
Client: Cliente
clientData: Datos del cliente
diff --git a/print/templates/reports/claim-pickup-order/claim-pickup-order.js b/print/templates/reports/claim-pickup-order/claim-pickup-order.js
index 0d1228a4e..fa2124057 100755
--- a/print/templates/reports/claim-pickup-order/claim-pickup-order.js
+++ b/print/templates/reports/claim-pickup-order/claim-pickup-order.js
@@ -32,6 +32,7 @@ module.exports = {
},
props: {
claimId: {
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/reports/claim-pickup-order/locale/es.yml b/print/templates/reports/claim-pickup-order/locale/es.yml
index 9faf9ac06..faa6eac33 100644
--- a/print/templates/reports/claim-pickup-order/locale/es.yml
+++ b/print/templates/reports/claim-pickup-order/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: orden-de-recogida
title: Ord. recogida
claimId: Reclamación
clientId: Cliente
diff --git a/print/templates/reports/client-debt-statement/client-debt-statement.js b/print/templates/reports/client-debt-statement/client-debt-statement.js
index 09b99590b..f006b0a92 100755
--- a/print/templates/reports/client-debt-statement/client-debt-statement.js
+++ b/print/templates/reports/client-debt-statement/client-debt-statement.js
@@ -69,6 +69,7 @@ module.exports = {
},
props: {
recipientId: {
+ type: [Number, String],
required: true
},
from: {
diff --git a/print/templates/reports/client-debt-statement/locale/es.yml b/print/templates/reports/client-debt-statement/locale/es.yml
index ccdce7b5b..2c8e18ee1 100644
--- a/print/templates/reports/client-debt-statement/locale/es.yml
+++ b/print/templates/reports/client-debt-statement/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: extracto-cliente
title: Extracto
clientId: Cliente
clientData: Datos del cliente
diff --git a/print/templates/reports/client-debt-statement/locale/fr.yml b/print/templates/reports/client-debt-statement/locale/fr.yml
index 12534f9ff..4edb29d8a 100644
--- a/print/templates/reports/client-debt-statement/locale/fr.yml
+++ b/print/templates/reports/client-debt-statement/locale/fr.yml
@@ -1,3 +1,4 @@
+reportName: releve-de-compte
title: Relevé de compte
clientId: Client
clientData: Données client
diff --git a/print/templates/reports/cmr-authorization/cmr-authorization.js b/print/templates/reports/cmr-authorization/cmr-authorization.js
index da08b6ec8..1adc75fa6 100755
--- a/print/templates/reports/cmr-authorization/cmr-authorization.js
+++ b/print/templates/reports/cmr-authorization/cmr-authorization.js
@@ -8,9 +8,8 @@ module.exports = {
this.ticket = await this.findOneFromDef('ticket', [this.ticketId]);
if (!this.ticket)
throw new Error('Something went wrong');
-
- this.client = await this.findOneFromDef('client', [this.ticket.clientFk]);
+ this.client = await this.findOneFromDef('client', [this.ticket.clientFk]);
},
computed: {
issued: function() {
@@ -23,6 +22,7 @@ module.exports = {
},
props: {
ticketId: {
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/reports/cmr-authorization/locale/es.yml b/print/templates/reports/cmr-authorization/locale/es.yml
index 779dc0c8f..37e40202d 100644
--- a/print/templates/reports/cmr-authorization/locale/es.yml
+++ b/print/templates/reports/cmr-authorization/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: autorizacion-cmr
description: '{socialName} una sociedad debidamente constituida con responsabilidad limitada
y registrada conforme al derecho de sociedades de {country} y aquí representada por
___________________. {socialName}, con domicilio en {address},
diff --git a/print/templates/reports/credit-request/locale/es.yml b/print/templates/reports/credit-request/locale/es.yml
index e4e9739a5..cd6f92dc5 100644
--- a/print/templates/reports/credit-request/locale/es.yml
+++ b/print/templates/reports/credit-request/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: solicitud-de-credito
fields:
title: Solicitud de crédito
date: Fecha
diff --git a/print/templates/reports/delivery-note/delivery-note.js b/print/templates/reports/delivery-note/delivery-note.js
index 9b3328d05..0ee7c8c91 100755
--- a/print/templates/reports/delivery-note/delivery-note.js
+++ b/print/templates/reports/delivery-note/delivery-note.js
@@ -117,7 +117,7 @@ module.exports = {
},
props: {
ticketId: {
- type: String,
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/reports/delivery-note/locale/en.yml b/print/templates/reports/delivery-note/locale/en.yml
index 8a3ff834b..16d0954e2 100644
--- a/print/templates/reports/delivery-note/locale/en.yml
+++ b/print/templates/reports/delivery-note/locale/en.yml
@@ -1,3 +1,4 @@
+reportName: delivery-note
title: Delivery note
ticketId: Delivery note
clientId: Client
diff --git a/print/templates/reports/delivery-note/locale/es.yml b/print/templates/reports/delivery-note/locale/es.yml
index f9c2e02f3..ca670ad59 100644
--- a/print/templates/reports/delivery-note/locale/es.yml
+++ b/print/templates/reports/delivery-note/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: albaran
title: Albarán
ticketId: Albarán
clientId: Cliente
diff --git a/print/templates/reports/delivery-note/locale/fr.yml b/print/templates/reports/delivery-note/locale/fr.yml
index 72ca771e1..6b3779a5b 100644
--- a/print/templates/reports/delivery-note/locale/fr.yml
+++ b/print/templates/reports/delivery-note/locale/fr.yml
@@ -1,3 +1,4 @@
+reportName: bon-de-livraison
title: Bon de livraison
ticketId: BL
clientId: Client
diff --git a/print/templates/reports/delivery-note/locale/pt.yml b/print/templates/reports/delivery-note/locale/pt.yml
index e83087142..1a9c1fbd1 100644
--- a/print/templates/reports/delivery-note/locale/pt.yml
+++ b/print/templates/reports/delivery-note/locale/pt.yml
@@ -1,3 +1,4 @@
+reportName: nota-de-entrega
title: Nota de Entrega
ticketId: Nota de Entrega
clientId: Cliente
diff --git a/print/templates/reports/driver-route/driver-route.js b/print/templates/reports/driver-route/driver-route.js
index 0b2638239..c34de37cc 100755
--- a/print/templates/reports/driver-route/driver-route.js
+++ b/print/templates/reports/driver-route/driver-route.js
@@ -39,6 +39,7 @@ module.exports = {
},
props: {
routeId: {
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/reports/driver-route/locale/es.yml b/print/templates/reports/driver-route/locale/es.yml
index 4f0f3ac3c..4c922ba64 100644
--- a/print/templates/reports/driver-route/locale/es.yml
+++ b/print/templates/reports/driver-route/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: hoja-de-ruta
title: Hoja de ruta
information: Información
date: Fecha
diff --git a/print/templates/reports/entry-order/entry-order.js b/print/templates/reports/entry-order/entry-order.js
index de396df2c..52a56bf03 100755
--- a/print/templates/reports/entry-order/entry-order.js
+++ b/print/templates/reports/entry-order/entry-order.js
@@ -40,7 +40,7 @@ module.exports = {
},
props: {
entryId: {
- type: String,
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/reports/entry-order/locale/es.yml b/print/templates/reports/entry-order/locale/es.yml
index 3c29d6401..5c633aeaa 100644
--- a/print/templates/reports/entry-order/locale/es.yml
+++ b/print/templates/reports/entry-order/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: pedido-de-entrada
title: Pedido
supplierName: Proveedor
supplierStreet: Dirección
diff --git a/print/templates/reports/exportation/exportation.js b/print/templates/reports/exportation/exportation.js
index f63d17930..fbf663249 100755
--- a/print/templates/reports/exportation/exportation.js
+++ b/print/templates/reports/exportation/exportation.js
@@ -28,6 +28,7 @@ module.exports = {
},
props: {
invoiceId: {
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/reports/exportation/locale/es.yml b/print/templates/reports/exportation/locale/es.yml
index d5fb78b4c..a689e245b 100644
--- a/print/templates/reports/exportation/locale/es.yml
+++ b/print/templates/reports/exportation/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: carta-CITES
title: 'Carta CITES'
toAttention: 'A la atención del Sr. Administrador de la Aduana de la Farga de Moles.'
declaration: 'Por la presente DECLARO, bajo mi responsabilidad, que las mercancías detalladas en la factura
diff --git a/print/templates/reports/extra-community/locale/es.yml b/print/templates/reports/extra-community/locale/es.yml
index 1112b0fe8..36201400f 100644
--- a/print/templates/reports/extra-community/locale/es.yml
+++ b/print/templates/reports/extra-community/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: orden-de-carga
title: Orden de carga
reference: Referencia
information: Información
diff --git a/print/templates/reports/invoice-incoterms/invoice-incoterms.js b/print/templates/reports/invoice-incoterms/invoice-incoterms.js
index 95bf1f397..99e23e15f 100755
--- a/print/templates/reports/invoice-incoterms/invoice-incoterms.js
+++ b/print/templates/reports/invoice-incoterms/invoice-incoterms.js
@@ -32,7 +32,7 @@ module.exports = {
},
props: {
invoiceId: {
- type: String,
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/reports/invoice-incoterms/locale/es.yml b/print/templates/reports/invoice-incoterms/locale/es.yml
index 9828564d7..a69805935 100644
--- a/print/templates/reports/invoice-incoterms/locale/es.yml
+++ b/print/templates/reports/invoice-incoterms/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: factura
title: Factura
invoice: Factura
clientId: Cliente
diff --git a/print/templates/reports/invoice/invoice.js b/print/templates/reports/invoice/invoice.js
index b56a5533c..bd85a812c 100755
--- a/print/templates/reports/invoice/invoice.js
+++ b/print/templates/reports/invoice/invoice.js
@@ -115,7 +115,7 @@ module.exports = {
},
props: {
invoiceId: {
- type: String,
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/reports/invoice/locale/en.yml b/print/templates/reports/invoice/locale/en.yml
new file mode 100644
index 000000000..4e4688b55
--- /dev/null
+++ b/print/templates/reports/invoice/locale/en.yml
@@ -0,0 +1,36 @@
+reportName: invoice
+title: Invoice
+invoice: Invoice
+clientId: Client
+invoiceData: Invoice data
+fiscalId: FI / NIF
+invoiceRef: Invoice {0}
+deliveryNote: Delivery note
+shipped: Shipped
+date: Date
+reference: Ref.
+quantity: Qty.
+concept: Concept
+price: PSP/u
+discount: Disc.
+vat: VAT
+amount: Amount
+type: Type
+taxBase: Tax base
+tax: Tax
+fee: Fee
+total: Total
+subtotal: Subtotal
+taxBreakdown: Tax breakdown
+notes: Notes
+intrastat: Intrastat
+code: Code
+description: Description
+stems: Stems
+netKg: Net kg
+rectifiedInvoices: Rectified invoices
+issued: Issued
+plantPassport: Plant passport
+observations: Observations
+wireTransfer: "Pay method: Transferencia"
+accountNumber: "Account number: {0}"
\ No newline at end of file
diff --git a/print/templates/reports/invoice/locale/es.yml b/print/templates/reports/invoice/locale/es.yml
index 6fdfc8a14..d37e77943 100644
--- a/print/templates/reports/invoice/locale/es.yml
+++ b/print/templates/reports/invoice/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: factura
title: Factura
invoice: Factura
clientId: Cliente
diff --git a/print/templates/reports/letter-debtor/letter-debtor.js b/print/templates/reports/letter-debtor/letter-debtor.js
index bdb3a504a..354b1d8d8 100755
--- a/print/templates/reports/letter-debtor/letter-debtor.js
+++ b/print/templates/reports/letter-debtor/letter-debtor.js
@@ -63,9 +63,11 @@ module.exports = {
},
props: {
recipientId: {
+ type: [Number, String],
required: true
},
companyId: {
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/reports/letter-debtor/locale/es.yml b/print/templates/reports/letter-debtor/locale/es.yml
index a9bd8c796..6c66f0717 100644
--- a/print/templates/reports/letter-debtor/locale/es.yml
+++ b/print/templates/reports/letter-debtor/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: extracto-de-cuenta
title: Extracto
claimId: Reclamación
clientId: Cliente
diff --git a/print/templates/reports/letter-debtor/locale/fr.yml b/print/templates/reports/letter-debtor/locale/fr.yml
index eff39d783..277c1162f 100644
--- a/print/templates/reports/letter-debtor/locale/fr.yml
+++ b/print/templates/reports/letter-debtor/locale/fr.yml
@@ -1,3 +1,4 @@
+reportName: releve-de-compte
title: Relevé de compte
claimId: Réclamation
clientId: Client
diff --git a/print/templates/reports/receipt/locale/es.yml b/print/templates/reports/receipt/locale/es.yml
index 67b9a948f..41be106df 100644
--- a/print/templates/reports/receipt/locale/es.yml
+++ b/print/templates/reports/receipt/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: receipt
title: 'Recibo'
date: 'Fecha'
payed: 'En {0}, a {1} de {2} de {3}'
diff --git a/print/templates/reports/receipt/receipt.js b/print/templates/reports/receipt/receipt.js
index d34735bb7..d7f4dd6da 100755
--- a/print/templates/reports/receipt/receipt.js
+++ b/print/templates/reports/receipt/receipt.js
@@ -25,6 +25,7 @@ module.exports = {
},
props: {
receiptId: {
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/reports/sepa-core/locale/es.yml b/print/templates/reports/sepa-core/locale/es.yml
index bb9cc4e49..5f3f08fc3 100644
--- a/print/templates/reports/sepa-core/locale/es.yml
+++ b/print/templates/reports/sepa-core/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: orden-de-domiciliacion
title: Orden de domiciliación de adeudo SEPA CORE
description: Mediante la firma de esta orden de domiciliación, el deudor autoriza
(A) al acreedor a enviar instrucciones a la entidad del deudor para adeudar su cuenta
diff --git a/print/templates/reports/sepa-core/locale/fr.yml b/print/templates/reports/sepa-core/locale/fr.yml
index 45a9039ea..354c06114 100644
--- a/print/templates/reports/sepa-core/locale/fr.yml
+++ b/print/templates/reports/sepa-core/locale/fr.yml
@@ -1,3 +1,4 @@
+reportName: direct-debit
title: Direct Debit
description: En signant ce formulaire de mandat, vous autorisez VERDNATURA LEVANTE SL
à envoyer des instructions à votre banque pour débiter votre compte, et (B) votre banque
diff --git a/print/templates/reports/sepa-core/locale/pt.yml b/print/templates/reports/sepa-core/locale/pt.yml
index e7127d06b..5459779ec 100644
--- a/print/templates/reports/sepa-core/locale/pt.yml
+++ b/print/templates/reports/sepa-core/locale/pt.yml
@@ -1,3 +1,4 @@
+reportName: autorizacao-de-debito
title: Autorização de débito directo SEPA CORE
description: Ao subscrever esta autorização, está a autorizar a (A) Verdnatura Levante
S.L. a enviar instruções ao seu banco para debitar a sua conta e (B) seu banco a
diff --git a/print/templates/reports/sepa-core/sepa-core.js b/print/templates/reports/sepa-core/sepa-core.js
index 55487d829..7e3dd3566 100755
--- a/print/templates/reports/sepa-core/sepa-core.js
+++ b/print/templates/reports/sepa-core/sepa-core.js
@@ -40,9 +40,11 @@ const rptSepaCore = {
},
props: {
recipientId: {
+ type: [Number, String],
required: true
},
companyId: {
+ type: [Number, String],
required: true
}
}
diff --git a/print/templates/reports/supplier-campaign-metrics/locale/es.yml b/print/templates/reports/supplier-campaign-metrics/locale/es.yml
index 31c1e17dd..1a38541fa 100644
--- a/print/templates/reports/supplier-campaign-metrics/locale/es.yml
+++ b/print/templates/reports/supplier-campaign-metrics/locale/es.yml
@@ -1,3 +1,4 @@
+reportName: consumo-proveedor
title: Consumo
Supplier: Proveedor
supplierData: Datos del proveedor
diff --git a/print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.js b/print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.js
index c37155556..1a460daa9 100755
--- a/print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.js
+++ b/print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.js
@@ -49,6 +49,7 @@ module.exports = {
},
props: {
recipientId: {
+ type: [Number, String],
required: true
},
from: {
diff --git a/print/templates/reports/zone/zone.js b/print/templates/reports/zone/zone.js
index 61c6cddfe..d611e1e53 100755
--- a/print/templates/reports/zone/zone.js
+++ b/print/templates/reports/zone/zone.js
@@ -13,6 +13,7 @@ module.exports = {
},
props: {
routeId: {
+ type: [Number, String],
required: true
}
}
diff --git a/storage/pdfs/invoice/.keep b/storage/pdfs/invoice/.keep
deleted file mode 100644
index e69de29bb..000000000