5488-use_checkAccessAcl #1482
|
@ -0,0 +1,3 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES
|
||||||
|
('Receipt', 'receiptEmail', '*', 'ALLOW', 'ROLE', 'salesAssistant');
|
|
@ -0,0 +1,5 @@
|
||||||
|
DROP TRIGGER `vn`.`deviceProduction_afterInsert`;
|
||||||
|
DROP TRIGGER `vn`.`deviceProduction_afterUpdate`;
|
||||||
|
|
||||||
|
DROP TRIGGER `vn`.`deviceProductionUser_afterDelete`;
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
UPDATE `salix`.`ACL`
|
||||||
|
SET model = 'InvoiceOut'
|
||||||
|
WHERE property IN ('negativeBases', 'negativeBasesCsv');
|
|
@ -1,6 +1,6 @@
|
||||||
import getBrowser from '../../helpers/puppeteer';
|
import getBrowser from '../../helpers/puppeteer';
|
||||||
|
|
||||||
describe('InvoiceIn negative bases path', () => {
|
describe('InvoiceOut negative bases path', () => {
|
||||||
let browser;
|
let browser;
|
||||||
let page;
|
let page;
|
||||||
const httpRequests = [];
|
const httpRequests = [];
|
||||||
|
@ -9,11 +9,11 @@ describe('InvoiceIn negative bases path', () => {
|
||||||
browser = await getBrowser();
|
browser = await getBrowser();
|
||||||
page = browser.page;
|
page = browser.page;
|
||||||
page.on('request', req => {
|
page.on('request', req => {
|
||||||
if (req.url().includes(`InvoiceIns/negativeBases`))
|
if (req.url().includes(`InvoiceOuts/negativeBases`))
|
||||||
httpRequests.push(req.url());
|
httpRequests.push(req.url());
|
||||||
});
|
});
|
||||||
await page.loginAndModule('administrative', 'invoiceIn');
|
await page.loginAndModule('administrative', 'invoiceOut');
|
||||||
await page.accessToSection('invoiceIn.negative-bases');
|
await page.accessToSection('invoiceOut.negative-bases');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async() => {
|
afterAll(async() => {
|
|
@ -156,6 +156,18 @@
|
||||||
"Component cost not set": "Componente coste no está estabecido",
|
"Component cost not set": "Componente coste no está estabecido",
|
||||||
"Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2",
|
"Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2",
|
||||||
"Description cannot be blank": "Description cannot be blank",
|
"Description cannot be blank": "Description cannot be blank",
|
||||||
|
"company": "Company",
|
||||||
|
"country": "Country",
|
||||||
|
"clientId": "Id client",
|
||||||
|
"clientSocialName": "Client",
|
||||||
|
"amount": "Amount",
|
||||||
|
"taxableBase": "Taxable base",
|
||||||
|
"ticketFk": "Id ticket",
|
||||||
|
"isActive": "Active",
|
||||||
|
"hasToInvoice": "Invoice",
|
||||||
|
"isTaxDataChecked": "Data checked",
|
||||||
|
"comercialId": "Id Comercial",
|
||||||
|
"comercialName": "Comercial",
|
||||||
"Added observation": "Added observation",
|
"Added observation": "Added observation",
|
||||||
"Comment added to client": "Comment added to client"
|
"Comment added to client": "Comment added to client"
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,5 +277,17 @@
|
||||||
"Insert a date range": "Inserte un rango de fechas",
|
"Insert a date range": "Inserte un rango de fechas",
|
||||||
"Added observation": "{{user}} añadió esta observacion: {{text}}",
|
"Added observation": "{{user}} añadió esta observacion: {{text}}",
|
||||||
"Comment added to client": "Observación añadida al cliente {{clientFk}}",
|
"Comment added to client": "Observación añadida al cliente {{clientFk}}",
|
||||||
"Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen"
|
"Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen",
|
||||||
|
"company": "Compañía",
|
||||||
|
"country": "País",
|
||||||
|
"clientId": "Id cliente",
|
||||||
|
"clientSocialName": "Cliente",
|
||||||
|
"amount": "Importe",
|
||||||
|
"taxableBase": "Base",
|
||||||
|
"ticketFk": "Id ticket",
|
||||||
|
"isActive": "Activo",
|
||||||
|
"hasToInvoice": "Facturar",
|
||||||
|
"isTaxDataChecked": "Datos comprobados",
|
||||||
|
"comercialId": "Id comercial",
|
||||||
|
"comercialName": "Comercial"
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,10 @@ class Controller extends Descriptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
sendPickupOrder() {
|
sendPickupOrder() {
|
||||||
|
if (!this.claim.client.email) {
|
||||||
|
this.vnApp.showError(this.$t('The client does not have an email'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
return this.vnEmail.send(`Claims/${this.claim.id}/claim-pickup-email`, {
|
return this.vnEmail.send(`Claims/${this.claim.id}/claim-pickup-email`, {
|
||||||
recipient: this.claim.client.email,
|
recipient: this.claim.client.email,
|
||||||
recipientId: this.claim.clientFk
|
recipientId: this.claim.clientFk
|
||||||
|
|
|
@ -20,3 +20,4 @@ Photos: Fotos
|
||||||
Go to the claim: Ir a la reclamación
|
Go to the claim: Ir a la reclamación
|
||||||
Sale tracking: Líneas preparadas
|
Sale tracking: Líneas preparadas
|
||||||
Ticket tracking: Estados del ticket
|
Ticket tracking: Estados del ticket
|
||||||
|
The client does not have an email: El cliente no tiene email
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
const {Email} = require('vn-print');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('receiptEmail', {
|
||||||
|
description: 'Returns the receipt pdf',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The claim id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'recipient',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The recipient email',
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: [
|
||||||
|
{
|
||||||
|
arg: 'body',
|
||||||
|
type: 'file',
|
||||||
|
root: true
|
||||||
|
}, {
|
||||||
|
arg: 'Content-Type',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
}, {
|
||||||
|
arg: 'Content-Disposition',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: '/:id/receipt-email',
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.receiptEmail = async(ctx, id) => {
|
||||||
|
const args = Object.assign({}, ctx.args);
|
||||||
|
const params = {
|
||||||
|
recipient: args.recipient,
|
||||||
|
lang: ctx.req.getLocale()
|
||||||
|
};
|
||||||
|
|
||||||
|
delete args.ctx;
|
||||||
|
for (const param in args)
|
||||||
|
params[param] = args[param];
|
||||||
|
|
||||||
|
const email = new Email('receipt', params);
|
||||||
|
|
||||||
|
return email.send();
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,12 +1,12 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('receiptPdf', {
|
Self.remoteMethodCtx('receiptPdf', {
|
||||||
description: 'Returns the receipt pdf',
|
description: 'Send the receipt pdf to client',
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
arg: 'id',
|
arg: 'id',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
required: true,
|
required: true,
|
||||||
description: 'The claim id',
|
description: 'The receipt id',
|
||||||
http: {source: 'path'}
|
http: {source: 'path'}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,7 @@ module.exports = function(Self) {
|
||||||
require('../methods/receipt/balanceCompensationEmail')(Self);
|
require('../methods/receipt/balanceCompensationEmail')(Self);
|
||||||
require('../methods/receipt/balanceCompensationPdf')(Self);
|
require('../methods/receipt/balanceCompensationPdf')(Self);
|
||||||
require('../methods/receipt/receiptPdf')(Self);
|
require('../methods/receipt/receiptPdf')(Self);
|
||||||
|
require('../methods/receipt/receiptEmail')(Self);
|
||||||
|
|
||||||
Self.validateBinded('amountPaid', isNotZero, {
|
Self.validateBinded('amountPaid', isNotZero, {
|
||||||
message: 'Amount cannot be zero',
|
message: 'Amount cannot be zero',
|
||||||
|
|
|
@ -84,6 +84,10 @@
|
||||||
label="View receipt"
|
label="View receipt"
|
||||||
ng-model="$ctrl.viewReceipt">
|
ng-model="$ctrl.viewReceipt">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
|
<vn-check
|
||||||
|
label="Send email"
|
||||||
|
ng-model="$ctrl.sendEmail">
|
||||||
|
</vn-check>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</tpl-body>
|
</tpl-body>
|
||||||
<tpl-buttons>
|
<tpl-buttons>
|
||||||
|
|
|
@ -2,9 +2,10 @@ import ngModule from '../../module';
|
||||||
import Dialog from 'core/components/dialog';
|
import Dialog from 'core/components/dialog';
|
||||||
|
|
||||||
class Controller extends Dialog {
|
class Controller extends Dialog {
|
||||||
constructor($element, $, $transclude, vnReport) {
|
constructor($element, $, $transclude, vnReport, vnEmail) {
|
||||||
super($element, $, $transclude);
|
super($element, $, $transclude);
|
||||||
this.vnReport = vnReport;
|
this.vnReport = vnReport;
|
||||||
|
this.vnEmail = vnEmail;
|
||||||
this.receipt = {};
|
this.receipt = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +24,18 @@ class Controller extends Dialog {
|
||||||
|
|
||||||
set clientFk(value) {
|
set clientFk(value) {
|
||||||
this.receipt.clientFk = value;
|
this.receipt.clientFk = value;
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
fields: ['email'],
|
||||||
|
where: {
|
||||||
|
id: value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$http.get(`Clients/findOne`, {filter})
|
||||||
|
.then(res => {
|
||||||
|
this.receipt.email = res.data.email;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get clientFk() {
|
get clientFk() {
|
||||||
|
@ -154,10 +167,13 @@ class Controller extends Dialog {
|
||||||
return super.responseHandler(response);
|
return super.responseHandler(response);
|
||||||
|
|
||||||
const exceededAmount = this.receipt.amountPaid > this.maxAmount;
|
const exceededAmount = this.receipt.amountPaid > this.maxAmount;
|
||||||
|
const isCash = this.bankSelection.accountingType.code == 'cash';
|
||||||
if (this.bankSelection.accountingType.code == 'cash' && exceededAmount)
|
if (isCash && exceededAmount)
|
||||||
return this.vnApp.showError(this.$t('Amount exceeded', {maxAmount: this.maxAmount}));
|
return this.vnApp.showError(this.$t('Amount exceeded', {maxAmount: this.maxAmount}));
|
||||||
|
|
||||||
|
if (isCash && this.sendEmail && !this.receipt.email)
|
||||||
|
return this.vnApp.showError(this.$t('There is no assigned email for this client'));
|
||||||
|
|
||||||
let receiptId;
|
let receiptId;
|
||||||
return this.$http.post(`Clients/${this.clientFk}/createReceipt`, this.receipt)
|
return this.$http.post(`Clients/${this.clientFk}/createReceipt`, this.receipt)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
|
@ -165,6 +181,13 @@ class Controller extends Dialog {
|
||||||
super.responseHandler(response);
|
super.responseHandler(response);
|
||||||
})
|
})
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
|
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
|
||||||
|
.then(() => {
|
||||||
|
if (!this.sendEmail || !isCash) return;
|
||||||
|
const params = {
|
||||||
|
recipient: this.receipt.email
|
||||||
|
};
|
||||||
|
this.vnEmail.send(`Receipts/${receiptId}/receipt-email`, params);
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (this.viewReceipt)
|
if (this.viewReceipt)
|
||||||
this.vnReport.show(`Receipts/${receiptId}/receipt-pdf`);
|
this.vnReport.show(`Receipts/${receiptId}/receipt-pdf`);
|
||||||
|
@ -178,7 +201,7 @@ class Controller extends Dialog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$element', '$scope', '$transclude', 'vnReport'];
|
Controller.$inject = ['$element', '$scope', '$transclude', 'vnReport', 'vnEmail'];
|
||||||
|
|
||||||
ngModule.vnComponent('vnClientBalanceCreate', {
|
ngModule.vnComponent('vnClientBalanceCreate', {
|
||||||
slotTemplate: require('./index.html'),
|
slotTemplate: require('./index.html'),
|
||||||
|
|
|
@ -192,19 +192,19 @@
|
||||||
{{::buy.entryFk}}
|
{{::buy.entryFk}}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td number>{{::buy.buyingValue | currency: 'EUR':2}}</td>
|
<td number>{{::buy.buyingValue | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.freightValue | currency: 'EUR':2}}</td>
|
<td number>{{::buy.freightValue | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.comissionValue | currency: 'EUR':2}}</td>
|
<td number>{{::buy.comissionValue | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.packageValue | currency: 'EUR':2}}</td>
|
<td number>{{::buy.packageValue | currency: 'EUR':3}}</td>
|
||||||
<td>
|
<td>
|
||||||
<vn-check
|
<vn-check
|
||||||
disabled="true"
|
disabled="true"
|
||||||
ng-model="::buy.isIgnored">
|
ng-model="::buy.isIgnored">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</td>
|
</td>
|
||||||
<td number>{{::buy.price2 | currency: 'EUR':2}}</td>
|
<td number>{{::buy.price2 | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.price3 | currency: 'EUR':2}}</td>
|
<td number>{{::buy.price3 | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.minPrice | currency: 'EUR':2}}</td>
|
<td number>{{::buy.minPrice | currency: 'EUR':3}}</td>
|
||||||
<td>{{::buy.ektFk | dashIfEmpty}}</td>
|
<td>{{::buy.ektFk | dashIfEmpty}}</td>
|
||||||
<td>{{::buy.weight}}</td>
|
<td>{{::buy.weight}}</td>
|
||||||
<td>{{::buy.packageFk}}</td>
|
<td>{{::buy.packageFk}}</td>
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
const {toCSV} = require('vn-loopback/util/csv');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
|
||||||
Self.remoteMethodCtx('negativeBasesCsv', {
|
|
||||||
description: 'Returns the negative bases as .csv',
|
|
||||||
accessType: 'READ',
|
|
||||||
accepts: [{
|
|
||||||
arg: 'negativeBases',
|
|
||||||
type: ['object'],
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'from',
|
|
||||||
type: 'date',
|
|
||||||
description: 'From date'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'to',
|
|
||||||
type: 'date',
|
|
||||||
description: 'To date'
|
|
||||||
}],
|
|
||||||
returns: [
|
|
||||||
{
|
|
||||||
arg: 'body',
|
|
||||||
type: 'file',
|
|
||||||
root: true
|
|
||||||
}, {
|
|
||||||
arg: 'Content-Type',
|
|
||||||
type: 'String',
|
|
||||||
http: {target: 'header'}
|
|
||||||
}, {
|
|
||||||
arg: 'Content-Disposition',
|
|
||||||
type: 'String',
|
|
||||||
http: {target: 'header'}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
http: {
|
|
||||||
path: '/negativeBasesCsv',
|
|
||||||
verb: 'GET'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.negativeBasesCsv = async ctx => {
|
|
||||||
const args = ctx.args;
|
|
||||||
const content = toCSV(args.negativeBases);
|
|
||||||
|
|
||||||
return [
|
|
||||||
content,
|
|
||||||
'text/csv',
|
|
||||||
`attachment; filename="negative-bases-${new Date(args.from).toLocaleDateString()}-${new Date(args.to).toLocaleDateString()}.csv"`
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -7,6 +7,4 @@ module.exports = Self => {
|
||||||
require('../methods/invoice-in/invoiceInPdf')(Self);
|
require('../methods/invoice-in/invoiceInPdf')(Self);
|
||||||
require('../methods/invoice-in/invoiceInEmail')(Self);
|
require('../methods/invoice-in/invoiceInEmail')(Self);
|
||||||
require('../methods/invoice-in/getSerial')(Self);
|
require('../methods/invoice-in/getSerial')(Self);
|
||||||
require('../methods/invoice-in/negativeBases')(Self);
|
|
||||||
require('../methods/invoice-in/negativeBasesCsv')(Self);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,4 +15,3 @@ import './create';
|
||||||
import './log';
|
import './log';
|
||||||
import './serial';
|
import './serial';
|
||||||
import './serial-search-panel';
|
import './serial-search-panel';
|
||||||
import './negative-bases';
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
Has To Invoice: Facturar
|
|
||||||
Download as CSV: Descargar como CSV
|
|
||||||
company: Compañía
|
|
||||||
country: País
|
|
||||||
clientId: Id Cliente
|
|
||||||
clientSocialName: Cliente
|
|
||||||
amount: Importe
|
|
||||||
taxableBase: Base
|
|
||||||
ticketFk: Id Ticket
|
|
||||||
isActive: Activo
|
|
||||||
hasToInvoice: Facturar
|
|
||||||
isTaxDataChecked: Datos comprobados
|
|
||||||
comercialId: Id Comercial
|
|
||||||
comercialName: Comercial
|
|
|
@ -10,8 +10,7 @@
|
||||||
"menus": {
|
"menus": {
|
||||||
"main": [
|
"main": [
|
||||||
{ "state": "invoiceIn.index", "icon": "icon-invoice-in"},
|
{ "state": "invoiceIn.index", "icon": "icon-invoice-in"},
|
||||||
{ "state": "invoiceIn.serial", "icon": "icon-invoice-in"},
|
{ "state": "invoiceIn.serial", "icon": "icon-invoice-in"}
|
||||||
{ "state": "invoiceIn.negative-bases", "icon": "icon-ticket"}
|
|
||||||
],
|
],
|
||||||
"card": [
|
"card": [
|
||||||
{
|
{
|
||||||
|
@ -53,15 +52,6 @@
|
||||||
"administrative"
|
"administrative"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "/negative-bases",
|
|
||||||
"state": "invoiceIn.negative-bases",
|
|
||||||
"component": "vn-negative-bases",
|
|
||||||
"description": "Negative bases",
|
|
||||||
"acl": [
|
|
||||||
"administrative"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "/serial",
|
"url": "/serial",
|
||||||
"state": "invoiceIn.serial",
|
"state": "invoiceIn.serial",
|
||||||
|
|
|
@ -96,16 +96,18 @@ module.exports = Self => {
|
||||||
SELECT f.*
|
SELECT f.*
|
||||||
FROM tmp.filter f`);
|
FROM tmp.filter f`);
|
||||||
|
|
||||||
|
if (args.filter) {
|
||||||
stmt.merge(conn.makeWhere(args.filter.where));
|
stmt.merge(conn.makeWhere(args.filter.where));
|
||||||
stmt.merge(conn.makeOrderBy(args.filter.order));
|
stmt.merge(conn.makeOrderBy(args.filter.order));
|
||||||
stmt.merge(conn.makeLimit(args.filter));
|
stmt.merge(conn.makeLimit(args.filter));
|
||||||
|
}
|
||||||
|
|
||||||
const negativeBasesIndex = stmts.push(stmt) - 1;
|
const negativeBasesIndex = stmts.push(stmt) - 1;
|
||||||
|
|
||||||
stmts.push(`DROP TEMPORARY TABLE tmp.filter, tmp.ticket, tmp.ticketTax, tmp.ticketAmount`);
|
stmts.push(`DROP TEMPORARY TABLE tmp.filter, tmp.ticket, tmp.ticketTax, tmp.ticketAmount`);
|
||||||
|
|
||||||
const sql = ParameterizedSQL.join(stmts, ';');
|
const sql = ParameterizedSQL.join(stmts, ';');
|
||||||
const result = await conn.executeStmt(sql, myOptions);
|
const result = await conn.executeStmt(sql);
|
||||||
|
|
||||||
return negativeBasesIndex === 0 ? result : result[negativeBasesIndex];
|
return negativeBasesIndex === 0 ? result : result[negativeBasesIndex];
|
||||||
};
|
};
|
|
@ -0,0 +1,68 @@
|
||||||
|
const {toCSV} = require('vn-loopback/util/csv');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('negativeBasesCsv', {
|
||||||
|
description: 'Returns the negative bases as .csv',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'from',
|
||||||
|
type: 'date',
|
||||||
|
description: 'From date',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'to',
|
||||||
|
type: 'date',
|
||||||
|
description: 'To date',
|
||||||
|
required: true
|
||||||
|
}],
|
||||||
|
returns: [
|
||||||
|
{
|
||||||
|
arg: 'body',
|
||||||
|
type: 'file',
|
||||||
|
root: true
|
||||||
|
}, {
|
||||||
|
arg: 'Content-Type',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
}, {
|
||||||
|
arg: 'Content-Disposition',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: '/negativeBasesCsv',
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.negativeBasesCsv = async(ctx, options) => {
|
||||||
|
const $t = ctx.req.__; // $translate
|
||||||
|
const args = ctx.args;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const negativeBases = await Self.app.models.InvoiceOut.negativeBases(ctx, myOptions);
|
||||||
|
const locatedFields = [];
|
||||||
|
negativeBases.forEach(element => {
|
||||||
|
locatedFields.push(Object.keys(element).map(key => {
|
||||||
|
return {newName: $t(key), value: element[key]};
|
||||||
|
}).filter(item => item !== null)
|
||||||
|
.reduce((result, item) => {
|
||||||
|
result[item.newName] = item.value;
|
||||||
|
return result;
|
||||||
|
}, {}));
|
||||||
|
});
|
||||||
|
const content = toCSV(locatedFields);
|
||||||
|
|
||||||
|
return [
|
||||||
|
content,
|
||||||
|
'text/csv',
|
||||||
|
`attachment; filename="negative-bases-${new Date(args.from).toLocaleDateString()}-${new Date(args.to).toLocaleDateString()}.csv"`
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,19 +1,18 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
describe('invoiceIn negativeBases()', () => {
|
describe('invoiceOut negativeBases()', () => {
|
||||||
it('should return all negative bases in a date range', async() => {
|
it('should return all negative bases in a date range', async() => {
|
||||||
const tx = await models.InvoiceIn.beginTransaction({});
|
const tx = await models.InvoiceOut.beginTransaction({});
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
const ctx = {
|
const ctx = {
|
||||||
args: {
|
args: {
|
||||||
from: new Date().setMonth(new Date().getMonth() - 12),
|
from: new Date().setMonth(new Date().getMonth() - 12),
|
||||||
to: new Date(),
|
to: new Date()
|
||||||
filter: {}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await models.InvoiceIn.negativeBases(ctx, options);
|
const result = await models.InvoiceOut.negativeBases(ctx, options);
|
||||||
|
|
||||||
expect(result.length).toBeGreaterThan(0);
|
expect(result.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
@ -26,7 +25,7 @@ describe('invoiceIn negativeBases()', () => {
|
||||||
|
|
||||||
it('should throw an error if a date range is not in args', async() => {
|
it('should throw an error if a date range is not in args', async() => {
|
||||||
let error;
|
let error;
|
||||||
const tx = await models.InvoiceIn.beginTransaction({});
|
const tx = await models.InvoiceOut.beginTransaction({});
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
const ctx = {
|
const ctx = {
|
||||||
args: {
|
args: {
|
||||||
|
@ -35,7 +34,7 @@ describe('invoiceIn negativeBases()', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await models.InvoiceIn.negativeBases(ctx, options);
|
await models.InvoiceOut.negativeBases(ctx, options);
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
|
@ -17,4 +17,6 @@ module.exports = Self => {
|
||||||
require('../methods/invoiceOut/invoiceCsvEmail')(Self);
|
require('../methods/invoiceOut/invoiceCsvEmail')(Self);
|
||||||
require('../methods/invoiceOut/invoiceOutPdf')(Self);
|
require('../methods/invoiceOut/invoiceOutPdf')(Self);
|
||||||
require('../methods/invoiceOut/getInvoiceDate')(Self);
|
require('../methods/invoiceOut/getInvoiceDate')(Self);
|
||||||
|
require('../methods/invoiceOut/negativeBases')(Self);
|
||||||
|
require('../methods/invoiceOut/negativeBasesCsv')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,3 +10,4 @@ import './descriptor-popover';
|
||||||
import './descriptor-menu';
|
import './descriptor-menu';
|
||||||
import './index/manual';
|
import './index/manual';
|
||||||
import './global-invoicing';
|
import './global-invoicing';
|
||||||
|
import './negative-bases';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
vn-id="model"
|
vn-id="model"
|
||||||
url="InvoiceIns/negativeBases"
|
url="InvoiceOuts/negativeBases"
|
||||||
auto-load="true"
|
auto-load="true"
|
||||||
params="$ctrl.params"
|
params="$ctrl.params"
|
||||||
limit="20">
|
limit="20">
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
<span translate>Country</span>
|
<span translate>Country</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="clientId">
|
<th field="clientId">
|
||||||
<span translate>Id Client</span>
|
<span translate>Client id</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="clientSocialName">
|
<th field="clientSocialName">
|
||||||
<span translate>Client</span>
|
<span translate>Client</span>
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
<span translate>Base</span>
|
<span translate>Base</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="ticketFk">
|
<th field="ticketFk">
|
||||||
<span translate>Id Ticket</span>
|
<span translate>Ticket id</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="isActive">
|
<th field="isActive">
|
||||||
<span translate>Active</span>
|
<span translate>Active</span>
|
|
@ -58,18 +58,7 @@ export default class Controller extends Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadCSV() {
|
downloadCSV() {
|
||||||
const data = [];
|
this.vnReport.show('InvoiceOuts/negativeBasesCsv', {
|
||||||
this.$.model._orgData.forEach(element => {
|
|
||||||
data.push(Object.keys(element).map(key => {
|
|
||||||
return {newName: this.$t(key), value: element[key]};
|
|
||||||
}).filter(item => item !== null)
|
|
||||||
.reduce((result, item) => {
|
|
||||||
result[item.newName] = item.value;
|
|
||||||
return result;
|
|
||||||
}, {}));
|
|
||||||
});
|
|
||||||
this.vnReport.show('InvoiceIns/negativeBasesCsv', {
|
|
||||||
negativeBases: data,
|
|
||||||
from: this.params.from,
|
from: this.params.from,
|
||||||
to: this.params.to
|
to: this.params.to
|
||||||
});
|
});
|
|
@ -0,0 +1,2 @@
|
||||||
|
Has To Invoice: Facturar
|
||||||
|
Download as CSV: Descargar como CSV
|
|
@ -7,8 +7,8 @@
|
||||||
"menus": {
|
"menus": {
|
||||||
"main": [
|
"main": [
|
||||||
{"state": "invoiceOut.index", "icon": "icon-invoice-out"},
|
{"state": "invoiceOut.index", "icon": "icon-invoice-out"},
|
||||||
{"state": "invoiceOut.global-invoicing", "icon": "contact_support"}
|
{"state": "invoiceOut.global-invoicing", "icon": "contact_support"},
|
||||||
|
{ "state": "invoiceOut.negative-bases", "icon": "icon-ticket"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"routes": [
|
"routes": [
|
||||||
|
@ -46,6 +46,15 @@
|
||||||
"state": "invoiceOut.card",
|
"state": "invoiceOut.card",
|
||||||
"abstract": true,
|
"abstract": true,
|
||||||
"component": "vn-invoice-out-card"
|
"component": "vn-invoice-out-card"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/negative-bases",
|
||||||
|
"state": "invoiceOut.negative-bases",
|
||||||
|
"component": "vn-negative-bases",
|
||||||
|
"description": "Negative bases",
|
||||||
|
"acl": [
|
||||||
|
"administrative"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="filter.requesterFk"
|
ng-model="filter.requesterFk"
|
||||||
url="Workers/activeWithRole"
|
url="Workers/activeWithInheritedRole"
|
||||||
search-function="{firstName: $search}"
|
search-function="{firstName: $search}"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
where="{role: 'salesPerson'}"
|
where="{role: 'salesPerson'}"
|
||||||
|
|
|
@ -100,7 +100,7 @@ module.exports = Self => {
|
||||||
dmsTypeId: dmsType.id,
|
dmsTypeId: dmsType.id,
|
||||||
reference: '',
|
reference: '',
|
||||||
description: `Firma del cliente - Ruta ${ticket.route().id}`,
|
description: `Firma del cliente - Ruta ${ticket.route().id}`,
|
||||||
hasFile: true
|
hasFile: false
|
||||||
};
|
};
|
||||||
dms = await models.Dms.uploadFile(ctxUploadFile, myOptions);
|
dms = await models.Dms.uploadFile(ctxUploadFile, myOptions);
|
||||||
gestDocCreated = true;
|
gestDocCreated = true;
|
||||||
|
|
|
@ -326,8 +326,13 @@ class Controller extends Section {
|
||||||
|
|
||||||
return this.$http.post(`Docuwares/${this.id}/upload`, {fileCabinet: 'deliveryNote'})
|
return this.$http.post(`Docuwares/${this.id}/upload`, {fileCabinet: 'deliveryNote'})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.vnApp.showSuccess(this.$t('PDF sent!'));
|
this.$.balanceCreate.amountPaid = this.ticket.totalWithVat;
|
||||||
|
this.$.balanceCreate.clientFk = this.ticket.clientFk;
|
||||||
|
this.$.balanceCreate.description = 'Albaran: ';
|
||||||
|
this.$.balanceCreate.description += this.ticket.id;
|
||||||
|
|
||||||
this.$.balanceCreate.show();
|
this.$.balanceCreate.show();
|
||||||
|
this.vnApp.showSuccess(this.$t('PDF sent!'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@
|
||||||
"DeviceProduction": {
|
"DeviceProduction": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"DeviceProductionLog": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"DeviceProductionModels": {
|
"DeviceProductionModels": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"name": "DeviceProductionLog",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "deviceProductionLog"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number",
|
||||||
|
"forceId": false
|
||||||
|
},
|
||||||
|
"originFk": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"userFk": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"deviceProduction": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"created": {
|
||||||
|
"type": "date"
|
||||||
|
},
|
||||||
|
"oldInstance": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"newInstance": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"changedModel": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"changedModelId": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"user": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Account",
|
||||||
|
"foreignKey": "userFk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scope": {
|
||||||
|
"order": ["created DESC", "id DESC"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "DeviceProductionUser",
|
"name": "DeviceProductionUser",
|
||||||
"base": "VnModel",
|
"base": "Loggable",
|
||||||
|
"log": {
|
||||||
|
"model": "DeviceProductionLog",
|
||||||
|
"relation": "deviceProduction"
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "deviceProductionUser"
|
"table": "deviceProductionUser"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "DeviceProduction",
|
"name": "DeviceProduction",
|
||||||
"base": "VnModel",
|
"base": "Loggable",
|
||||||
|
"log": {
|
||||||
|
"model": "DeviceProductionLog"
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "deviceProduction"
|
"table": "deviceProduction"
|
||||||
|
|
|
@ -31,11 +31,12 @@
|
||||||
value-field="id"
|
value-field="id"
|
||||||
show-field="serialNumber">
|
show-field="serialNumber">
|
||||||
<tpl-item>
|
<tpl-item>
|
||||||
<span>ID: {{id}}</span>
|
<div>
|
||||||
<span class="separator"></span>
|
ID: {{id}}
|
||||||
<span>{{'Model' | translate}}: {{modelFk}}</span>
|
</div>
|
||||||
<span class="separator"></span>
|
<div class="text-caption text-grey">
|
||||||
<span>{{'Serial Number' | translate}}: {{serialNumber}}</span>
|
{{modelFk}}, {{serialNumber}}
|
||||||
|
</div>
|
||||||
</tpl-item>
|
</tpl-item>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
span.separator{
|
@import "./variables";
|
||||||
border-left: 1px solid black;
|
|
||||||
height: 100%;
|
.text-grey {
|
||||||
margin: 0 10px;
|
color: $color-font-light;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,11 @@
|
||||||
|
const Stylesheet = require(`vn-print/core/stylesheet`);
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const vnPrintPath = path.resolve('print');
|
||||||
|
|
||||||
|
module.exports = new Stylesheet([
|
||||||
|
`${vnPrintPath}/common/css/spacing.css`,
|
||||||
|
`${vnPrintPath}/common/css/misc.css`,
|
||||||
|
`${vnPrintPath}/common/css/layout.css`,
|
||||||
|
`${vnPrintPath}/common/css/email.css`])
|
||||||
|
.mergeStyles();
|
|
@ -0,0 +1,6 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"filename": "receipt.pdf",
|
||||||
|
"component": "receipt"
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,5 @@
|
||||||
|
subject: Recibo
|
||||||
|
title: Recibo
|
||||||
|
dear: Estimado cliente
|
||||||
|
description: Ya está disponible el recibo <strong>{0}</strong>. <br/>
|
||||||
|
Puedes descargarlo haciendo clic en el adjunto de este correo.
|
|
@ -0,0 +1,9 @@
|
||||||
|
<email-body v-bind="$props">
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="grid-block vn-pa-ml">
|
||||||
|
<h1>{{ $t('title') }}</h1>
|
||||||
|
<p>{{$t('dear')}},</p>
|
||||||
|
<p v-html="$t('description', [id])"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</email-body>
|
|
@ -0,0 +1,15 @@
|
||||||
|
const Component = require(`vn-print/core/component`);
|
||||||
|
const emailBody = new Component('email-body');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'receipt',
|
||||||
|
components: {
|
||||||
|
'email-body': emailBody.build(),
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue