Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5488-use_checkAccessAcl
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
28ead70615
|
@ -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';
|
||||
|
||||
describe('InvoiceIn negative bases path', () => {
|
||||
describe('InvoiceOut negative bases path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
const httpRequests = [];
|
||||
|
@ -9,11 +9,11 @@ describe('InvoiceIn negative bases path', () => {
|
|||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
page.on('request', req => {
|
||||
if (req.url().includes(`InvoiceIns/negativeBases`))
|
||||
if (req.url().includes(`InvoiceOuts/negativeBases`))
|
||||
httpRequests.push(req.url());
|
||||
});
|
||||
await page.loginAndModule('administrative', 'invoiceIn');
|
||||
await page.accessToSection('invoiceIn.negative-bases');
|
||||
await page.loginAndModule('administrative', 'invoiceOut');
|
||||
await page.accessToSection('invoiceOut.negative-bases');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
|
@ -156,6 +156,18 @@
|
|||
"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",
|
||||
"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",
|
||||
"Comment added to client": "Comment added to client"
|
||||
}
|
||||
|
|
|
@ -275,7 +275,19 @@
|
|||
"Collection does not exist": "La colección no existe",
|
||||
"Cannot obtain exclusive lock": "No se puede obtener un bloqueo exclusivo",
|
||||
"Insert a date range": "Inserte un rango de fechas",
|
||||
"Added observation": "{{user}} añadió esta observacion: {{text}}",
|
||||
"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"
|
||||
"Added observation": "{{user}} añadió esta observacion: {{text}}",
|
||||
"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",
|
||||
"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() {
|
||||
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`, {
|
||||
recipient: this.claim.client.email,
|
||||
recipientId: this.claim.clientFk
|
||||
|
|
|
@ -20,3 +20,4 @@ Photos: Fotos
|
|||
Go to the claim: Ir a la reclamación
|
||||
Sale tracking: Líneas preparadas
|
||||
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 => {
|
||||
Self.remoteMethodCtx('receiptPdf', {
|
||||
description: 'Returns the receipt pdf',
|
||||
description: 'Send the receipt pdf to client',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The claim id',
|
||||
description: 'The receipt id',
|
||||
http: {source: 'path'}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@ module.exports = function(Self) {
|
|||
require('../methods/receipt/balanceCompensationEmail')(Self);
|
||||
require('../methods/receipt/balanceCompensationPdf')(Self);
|
||||
require('../methods/receipt/receiptPdf')(Self);
|
||||
require('../methods/receipt/receiptEmail')(Self);
|
||||
|
||||
Self.validateBinded('amountPaid', isNotZero, {
|
||||
message: 'Amount cannot be zero',
|
||||
|
|
|
@ -84,6 +84,10 @@
|
|||
label="View receipt"
|
||||
ng-model="$ctrl.viewReceipt">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Send email"
|
||||
ng-model="$ctrl.sendEmail">
|
||||
</vn-check>
|
||||
</vn-horizontal>
|
||||
</tpl-body>
|
||||
<tpl-buttons>
|
||||
|
|
|
@ -2,9 +2,10 @@ import ngModule from '../../module';
|
|||
import Dialog from 'core/components/dialog';
|
||||
|
||||
class Controller extends Dialog {
|
||||
constructor($element, $, $transclude, vnReport) {
|
||||
constructor($element, $, $transclude, vnReport, vnEmail) {
|
||||
super($element, $, $transclude);
|
||||
this.vnReport = vnReport;
|
||||
this.vnEmail = vnEmail;
|
||||
this.receipt = {};
|
||||
}
|
||||
|
||||
|
@ -23,6 +24,18 @@ class Controller extends Dialog {
|
|||
|
||||
set 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() {
|
||||
|
@ -154,10 +167,13 @@ class Controller extends Dialog {
|
|||
return super.responseHandler(response);
|
||||
|
||||
const exceededAmount = this.receipt.amountPaid > this.maxAmount;
|
||||
|
||||
if (this.bankSelection.accountingType.code == 'cash' && exceededAmount)
|
||||
const isCash = this.bankSelection.accountingType.code == 'cash';
|
||||
if (isCash && exceededAmount)
|
||||
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;
|
||||
return this.$http.post(`Clients/${this.clientFk}/createReceipt`, this.receipt)
|
||||
.then(res => {
|
||||
|
@ -165,6 +181,13 @@ class Controller extends Dialog {
|
|||
super.responseHandler(response);
|
||||
})
|
||||
.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(() => {
|
||||
if (this.viewReceipt)
|
||||
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', {
|
||||
slotTemplate: require('./index.html'),
|
||||
|
|
|
@ -192,19 +192,19 @@
|
|||
{{::buy.entryFk}}
|
||||
</span>
|
||||
</td>
|
||||
<td number>{{::buy.buyingValue | currency: 'EUR':2}}</td>
|
||||
<td number>{{::buy.freightValue | currency: 'EUR':2}}</td>
|
||||
<td number>{{::buy.comissionValue | currency: 'EUR':2}}</td>
|
||||
<td number>{{::buy.packageValue | currency: 'EUR':2}}</td>
|
||||
<td number>{{::buy.buyingValue | currency: 'EUR':3}}</td>
|
||||
<td number>{{::buy.freightValue | currency: 'EUR':3}}</td>
|
||||
<td number>{{::buy.comissionValue | currency: 'EUR':3}}</td>
|
||||
<td number>{{::buy.packageValue | currency: 'EUR':3}}</td>
|
||||
<td>
|
||||
<vn-check
|
||||
disabled="true"
|
||||
ng-model="::buy.isIgnored">
|
||||
</vn-check>
|
||||
</td>
|
||||
<td number>{{::buy.price2 | currency: 'EUR':2}}</td>
|
||||
<td number>{{::buy.price3 | currency: 'EUR':2}}</td>
|
||||
<td number>{{::buy.minPrice | currency: 'EUR':2}}</td>
|
||||
<td number>{{::buy.price2 | currency: 'EUR':3}}</td>
|
||||
<td number>{{::buy.price3 | currency: 'EUR':3}}</td>
|
||||
<td number>{{::buy.minPrice | currency: 'EUR':3}}</td>
|
||||
<td>{{::buy.ektFk | dashIfEmpty}}</td>
|
||||
<td>{{::buy.weight}}</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/invoiceInEmail')(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 './serial';
|
||||
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": {
|
||||
"main": [
|
||||
{ "state": "invoiceIn.index", "icon": "icon-invoice-in"},
|
||||
{ "state": "invoiceIn.serial", "icon": "icon-invoice-in"},
|
||||
{ "state": "invoiceIn.negative-bases", "icon": "icon-ticket"}
|
||||
{ "state": "invoiceIn.serial", "icon": "icon-invoice-in"}
|
||||
],
|
||||
"card": [
|
||||
{
|
||||
|
@ -53,15 +52,6 @@
|
|||
"administrative"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "/negative-bases",
|
||||
"state": "invoiceIn.negative-bases",
|
||||
"component": "vn-negative-bases",
|
||||
"description": "Negative bases",
|
||||
"acl": [
|
||||
"administrative"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "/serial",
|
||||
"state": "invoiceIn.serial",
|
||||
|
|
|
@ -96,16 +96,18 @@ module.exports = Self => {
|
|||
SELECT f.*
|
||||
FROM tmp.filter f`);
|
||||
|
||||
stmt.merge(conn.makeWhere(args.filter.where));
|
||||
stmt.merge(conn.makeOrderBy(args.filter.order));
|
||||
stmt.merge(conn.makeLimit(args.filter));
|
||||
if (args.filter) {
|
||||
stmt.merge(conn.makeWhere(args.filter.where));
|
||||
stmt.merge(conn.makeOrderBy(args.filter.order));
|
||||
stmt.merge(conn.makeLimit(args.filter));
|
||||
}
|
||||
|
||||
const negativeBasesIndex = stmts.push(stmt) - 1;
|
||||
|
||||
stmts.push(`DROP TEMPORARY TABLE tmp.filter, tmp.ticket, tmp.ticketTax, tmp.ticketAmount`);
|
||||
|
||||
const sql = ParameterizedSQL.join(stmts, ';');
|
||||
const result = await conn.executeStmt(sql, myOptions);
|
||||
const result = await conn.executeStmt(sql);
|
||||
|
||||
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;
|
||||
|
||||
describe('invoiceIn negativeBases()', () => {
|
||||
describe('invoiceOut negativeBases()', () => {
|
||||
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 ctx = {
|
||||
args: {
|
||||
from: new Date().setMonth(new Date().getMonth() - 12),
|
||||
to: new Date(),
|
||||
filter: {}
|
||||
to: new Date()
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await models.InvoiceIn.negativeBases(ctx, options);
|
||||
const result = await models.InvoiceOut.negativeBases(ctx, options);
|
||||
|
||||
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() => {
|
||||
let error;
|
||||
const tx = await models.InvoiceIn.beginTransaction({});
|
||||
const tx = await models.InvoiceOut.beginTransaction({});
|
||||
const options = {transaction: tx};
|
||||
const ctx = {
|
||||
args: {
|
||||
|
@ -35,7 +34,7 @@ describe('invoiceIn negativeBases()', () => {
|
|||
};
|
||||
|
||||
try {
|
||||
await models.InvoiceIn.negativeBases(ctx, options);
|
||||
await models.InvoiceOut.negativeBases(ctx, options);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
error = e;
|
|
@ -17,4 +17,6 @@ module.exports = Self => {
|
|||
require('../methods/invoiceOut/invoiceCsvEmail')(Self);
|
||||
require('../methods/invoiceOut/invoiceOutPdf')(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 './index/manual';
|
||||
import './global-invoicing';
|
||||
import './negative-bases';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="InvoiceIns/negativeBases"
|
||||
url="InvoiceOuts/negativeBases"
|
||||
auto-load="true"
|
||||
params="$ctrl.params"
|
||||
limit="20">
|
||||
|
@ -43,7 +43,7 @@
|
|||
<span translate>Country</span>
|
||||
</th>
|
||||
<th field="clientId">
|
||||
<span translate>Id Client</span>
|
||||
<span translate>Client id</span>
|
||||
</th>
|
||||
<th field="clientSocialName">
|
||||
<span translate>Client</span>
|
||||
|
@ -55,7 +55,7 @@
|
|||
<span translate>Base</span>
|
||||
</th>
|
||||
<th field="ticketFk">
|
||||
<span translate>Id Ticket</span>
|
||||
<span translate>Ticket id</span>
|
||||
</th>
|
||||
<th field="isActive">
|
||||
<span translate>Active</span>
|
|
@ -58,18 +58,7 @@ export default class Controller extends Section {
|
|||
}
|
||||
|
||||
downloadCSV() {
|
||||
const data = [];
|
||||
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,
|
||||
this.vnReport.show('InvoiceOuts/negativeBasesCsv', {
|
||||
from: this.params.from,
|
||||
to: this.params.to
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
Has To Invoice: Facturar
|
||||
Download as CSV: Descargar como CSV
|
|
@ -7,8 +7,8 @@
|
|||
"menus": {
|
||||
"main": [
|
||||
{"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": [
|
||||
|
@ -46,6 +46,15 @@
|
|||
"state": "invoiceOut.card",
|
||||
"abstract": true,
|
||||
"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-one
|
||||
ng-model="filter.requesterFk"
|
||||
url="Workers/activeWithRole"
|
||||
url="Workers/activeWithInheritedRole"
|
||||
search-function="{firstName: $search}"
|
||||
value-field="id"
|
||||
where="{role: 'salesPerson'}"
|
||||
|
|
|
@ -100,7 +100,7 @@ module.exports = Self => {
|
|||
dmsTypeId: dmsType.id,
|
||||
reference: '',
|
||||
description: `Firma del cliente - Ruta ${ticket.route().id}`,
|
||||
hasFile: true
|
||||
hasFile: false
|
||||
};
|
||||
dms = await models.Dms.uploadFile(ctxUploadFile, myOptions);
|
||||
gestDocCreated = true;
|
||||
|
|
|
@ -326,8 +326,13 @@ class Controller extends Section {
|
|||
|
||||
return this.$http.post(`Docuwares/${this.id}/upload`, {fileCabinet: 'deliveryNote'})
|
||||
.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.vnApp.showSuccess(this.$t('PDF sent!'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
"DeviceProduction": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"DeviceProductionLog": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"DeviceProductionModels": {
|
||||
"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",
|
||||
"base": "VnModel",
|
||||
"base": "Loggable",
|
||||
"log": {
|
||||
"model": "DeviceProductionLog",
|
||||
"relation": "deviceProduction"
|
||||
},
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "deviceProductionUser"
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
{
|
||||
"name": "DeviceProduction",
|
||||
"base": "VnModel",
|
||||
"base": "Loggable",
|
||||
"log": {
|
||||
"model": "DeviceProductionLog"
|
||||
},
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "deviceProduction"
|
||||
|
|
|
@ -31,11 +31,12 @@
|
|||
value-field="id"
|
||||
show-field="serialNumber">
|
||||
<tpl-item>
|
||||
<span>ID: {{id}}</span>
|
||||
<span class="separator"></span>
|
||||
<span>{{'Model' | translate}}: {{modelFk}}</span>
|
||||
<span class="separator"></span>
|
||||
<span>{{'Serial Number' | translate}}: {{serialNumber}}</span>
|
||||
<div>
|
||||
ID: {{id}}
|
||||
</div>
|
||||
<div class="text-caption text-grey">
|
||||
{{modelFk}}, {{serialNumber}}
|
||||
</div>
|
||||
</tpl-item>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
span.separator{
|
||||
border-left: 1px solid black;
|
||||
height: 100%;
|
||||
margin: 0 10px;
|
||||
@import "./variables";
|
||||
|
||||
.text-grey {
|
||||
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