5488-use_checkAccessAcl #1482

Merged
alexm merged 32 commits from 5488-use_checkAccessAcl into dev 2023-05-29 05:20:29 +00:00
44 changed files with 7713 additions and 706 deletions
Showing only changes of commit 28ead70615 - Show all commits

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('Receipt', 'receiptEmail', '*', 'ALLOW', 'ROLE', 'salesAssistant');

View File

@ -0,0 +1,5 @@
DROP TRIGGER `vn`.`deviceProduction_afterInsert`;
DROP TRIGGER `vn`.`deviceProduction_afterUpdate`;
DROP TRIGGER `vn`.`deviceProductionUser_afterDelete`;

View File

@ -0,0 +1,3 @@
UPDATE `salix`.`ACL`
SET model = 'InvoiceOut'
WHERE property IN ('negativeBases', 'negativeBasesCsv');

View File

@ -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() => {

View File

@ -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"
} }

View File

@ -275,7 +275,19 @@
"Collection does not exist": "La colección no existe", "Collection does not exist": "La colección no existe",
"Cannot obtain exclusive lock": "No se puede obtener un bloqueo exclusivo", "Cannot obtain exclusive lock": "No se puede obtener un bloqueo exclusivo",
"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"
} }

View File

@ -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

View File

@ -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

View File

@ -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();
};
};

View File

@ -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'}
}, },
{ {

View File

@ -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',

View File

@ -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>

View File

@ -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'),

View File

@ -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>

View File

@ -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"`
];
};
};

View File

@ -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);
}; };

View File

@ -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';

View File

@ -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

View File

@ -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",

View File

@ -96,16 +96,18 @@ module.exports = Self => {
SELECT f.* SELECT f.*
FROM tmp.filter f`); FROM tmp.filter f`);
stmt.merge(conn.makeWhere(args.filter.where)); if (args.filter) {
stmt.merge(conn.makeOrderBy(args.filter.order)); stmt.merge(conn.makeWhere(args.filter.where));
stmt.merge(conn.makeLimit(args.filter)); stmt.merge(conn.makeOrderBy(args.filter.order));
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];
}; };

View File

@ -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"`
];
};
};

View File

@ -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;

View File

@ -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);
}; };

View File

@ -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';

View File

@ -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>

View File

@ -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
}); });

View File

@ -0,0 +1,2 @@
Has To Invoice: Facturar
Download as CSV: Descargar como CSV

View File

@ -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"
]
} }
] ]
} }

View File

@ -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'}"

View File

@ -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;

View File

@ -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!'));
}); });
} }
} }

View File

@ -23,6 +23,9 @@
"DeviceProduction": { "DeviceProduction": {
"dataSource": "vn" "dataSource": "vn"
}, },
"DeviceProductionLog": {
"dataSource": "vn"
},
"DeviceProductionModels": { "DeviceProductionModels": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -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"]
}
}

View File

@ -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"

View File

@ -1,6 +1,9 @@
{ {
"name": "DeviceProduction", "name": "DeviceProduction",
"base": "VnModel", "base": "Loggable",
"log": {
"model": "DeviceProductionLog"
},
"options": { "options": {
"mysql": { "mysql": {
"table": "deviceProduction" "table": "deviceProduction"

View File

@ -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>

View File

@ -1,6 +1,6 @@
span.separator{ @import "./variables";
border-left: 1px solid black;
height: 100%; .text-grey {
margin: 0 10px; color: $color-font-light;
} }

7899
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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();

View File

@ -0,0 +1,6 @@
[
{
"filename": "receipt.pdf",
"component": "receipt"
}
]

View File

@ -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.

View File

@ -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>

View File

@ -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
}
}
};