Merge pull request '4548-invoiceIn-pdf' (!1113) from 4548-invoiceIn-pdf into dev
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
Reviewed-on: #1113 Reviewed-by: Javi Gallego <jgallego@verdnatura.es> Reviewed-by: Joan Sanchez <joan@verdnatura.es>
This commit is contained in:
commit
46aafef965
|
@ -0,0 +1,4 @@
|
|||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES
|
||||
('InvoiceIn', 'invoiceInPdf', 'READ', 'ALLOW', 'ROLE', 'administrative'),
|
||||
('InvoiceIn', 'invoiceInEmail', 'WRITE', 'ALLOW', 'ROLE', 'administrative'),
|
|
@ -0,0 +1,53 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('invoiceInEmail', {
|
||||
description: 'Sends the invoice in email with an attached PDF',
|
||||
accessType: 'WRITE',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The invoice id',
|
||||
http: {source: 'path'}
|
||||
},
|
||||
{
|
||||
arg: 'recipient',
|
||||
type: 'string',
|
||||
description: 'The recipient email',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
arg: 'recipientId',
|
||||
type: 'number',
|
||||
description: 'The recipient id to send to the recipient preferred language',
|
||||
required: false
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: '/:id/invoice-in-email',
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.invoiceInEmail = async ctx => {
|
||||
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('invoiceIn', params);
|
||||
|
||||
return email.send();
|
||||
};
|
||||
};
|
|
@ -0,0 +1,50 @@
|
|||
const {Report} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('invoiceInPdf', {
|
||||
description: 'Returns the invoiceIn pdf',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The invoiceIn id',
|
||||
http: {source: 'path'}
|
||||
}
|
||||
],
|
||||
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/invoice-in-pdf',
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.invoiceInPdf = async(ctx, id) => {
|
||||
const args = Object.assign({}, ctx.args);
|
||||
const params = {lang: ctx.req.getLocale()};
|
||||
delete args.ctx;
|
||||
|
||||
for (const param in args)
|
||||
params[param] = args[param];
|
||||
|
||||
const report = new Report('invoiceIn', params);
|
||||
const stream = await report.toPdfStream();
|
||||
|
||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
||||
};
|
||||
};
|
|
@ -4,4 +4,6 @@ module.exports = Self => {
|
|||
require('../methods/invoice-in/clone')(Self);
|
||||
require('../methods/invoice-in/toBook')(Self);
|
||||
require('../methods/invoice-in/getTotals')(Self);
|
||||
require('../methods/invoice-in/invoiceInPdf')(Self);
|
||||
require('../methods/invoice-in/invoiceInEmail')(Self);
|
||||
};
|
||||
|
|
|
@ -94,6 +94,11 @@
|
|||
"model": "Supplier",
|
||||
"foreignKey": "supplierFk"
|
||||
},
|
||||
"supplierContact": {
|
||||
"type": "hasMany",
|
||||
"model": "SupplierContact",
|
||||
"foreignKey": "supplierFk"
|
||||
},
|
||||
"currency": {
|
||||
"type": "belongsTo",
|
||||
"model": "Currency",
|
||||
|
|
|
@ -8,6 +8,14 @@ class Controller extends ModuleCard {
|
|||
{
|
||||
relation: 'supplier'
|
||||
},
|
||||
{
|
||||
relation: 'supplierContact',
|
||||
scope: {
|
||||
where: {
|
||||
email: {neq: null}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'invoiceInDueDay'
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<vn-descriptor-content
|
||||
module="invoiceIn"
|
||||
<vn-descriptor-content
|
||||
module="invoiceIn"
|
||||
description="$ctrl.invoiceIn.supplierRef"
|
||||
summary="$ctrl.$.summary">
|
||||
<slot-menu>
|
||||
|
@ -10,7 +10,6 @@
|
|||
translate>
|
||||
To book
|
||||
</vn-item>
|
||||
|
||||
<vn-item
|
||||
ng-click="deleteConfirmation.show()"
|
||||
vn-acl="administrative"
|
||||
|
@ -26,6 +25,16 @@
|
|||
translate>
|
||||
Clone Invoice
|
||||
</vn-item>
|
||||
<vn-item
|
||||
ng-click="$ctrl.showPdfInvoice()"
|
||||
translate>
|
||||
Show agricultural invoice as PDF
|
||||
</vn-item>
|
||||
<vn-item
|
||||
ng-click="sendPdfConfirmation.show({email: $ctrl.entity.supplierContact[0].email})"
|
||||
translate>
|
||||
Send agricultural invoice as PDF
|
||||
</vn-item>
|
||||
</slot-menu>
|
||||
<slot-body>
|
||||
<div class="attributes">
|
||||
|
@ -37,7 +46,7 @@
|
|||
</vn-label-value>
|
||||
<vn-label-value label="Supplier">
|
||||
<span ng-click="supplierDescriptor.show($event, $ctrl.invoiceIn.supplier.id)" class="link">
|
||||
{{$ctrl.invoiceIn.supplier.nickname}}
|
||||
{{$ctrl.invoiceIn.supplier.nickname}}
|
||||
</span>
|
||||
</vn-label-value>
|
||||
</div>
|
||||
|
@ -57,9 +66,9 @@
|
|||
icon="icon-invoice-in">
|
||||
</vn-quick-link>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</slot-body>
|
||||
</vn-descriptor-content>
|
||||
<vn-confirm
|
||||
|
@ -75,11 +84,29 @@
|
|||
<vn-supplier-descriptor-popover
|
||||
vn-id="supplierDescriptor">
|
||||
</vn-supplier-descriptor-popover>
|
||||
<vn-confirm
|
||||
<vn-confirm
|
||||
vn-id="confirm-toBookAnyway"
|
||||
message="Are you sure you want to book this invoice?"
|
||||
on-accept="$ctrl.onAcceptToBook()">
|
||||
</vn-confirm>
|
||||
<vn-popup vn-id="summary">
|
||||
<vn-invoice-in-summary invoice-in="$ctrl.invoiceIn"></vn-invoice-in-summary>
|
||||
</vn-popup>
|
||||
</vn-popup>
|
||||
|
||||
<!-- Send PDF invoice confirmation popup -->
|
||||
<vn-dialog
|
||||
vn-id="sendPdfConfirmation"
|
||||
on-accept="$ctrl.sendPdfInvoice($data)"
|
||||
message="Send PDF invoice">
|
||||
<tpl-body>
|
||||
<span translate>Are you sure you want to send it?</span>
|
||||
<vn-textfield vn-one
|
||||
label="Email"
|
||||
ng-model="sendPdfConfirmation.data.email">
|
||||
</vn-textfield>
|
||||
</tpl-body>
|
||||
<tpl-buttons>
|
||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||
<button response="accept" translate>Confirm</button>
|
||||
</tpl-buttons>
|
||||
</vn-dialog>
|
||||
|
|
|
@ -96,6 +96,20 @@ class Controller extends Descriptor {
|
|||
.then(() => this.$state.reload())
|
||||
.then(() => this.vnApp.showSuccess(this.$t('InvoiceIn booked')));
|
||||
}
|
||||
|
||||
showPdfInvoice() {
|
||||
this.vnReport.show(`InvoiceIns/${this.id}/invoice-in-pdf`);
|
||||
}
|
||||
|
||||
sendPdfInvoice($data) {
|
||||
if (!$data.email)
|
||||
return this.vnApp.showError(this.$t(`The email can't be empty`));
|
||||
|
||||
return this.vnEmail.send(`InvoiceIns/${this.entity.id}/invoice-in-email`, {
|
||||
recipient: $data.email,
|
||||
recipientId: this.entity.supplier.id
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnInvoiceInDescriptor', {
|
||||
|
|
|
@ -19,3 +19,5 @@ To book: Contabilizar
|
|||
Total amount: Total importe
|
||||
Total net: Total neto
|
||||
Total stems: Total tallos
|
||||
Show agricultural invoice as PDF: Ver factura agrícola como PDF
|
||||
Send agricultural invoice as PDF: Enviar factura agrícola como PDF
|
||||
|
|
|
@ -7,4 +7,4 @@ copyLink: 'Como alternativa, podes copiar o siguinte link no teu navegador:'
|
|||
poll: Si o deseja, podes responder nosso questionário de satiscação para ajudar-nos a prestar-vos um melhor serviço. Tua opinião é muito importante para nós!
|
||||
help: Cualquer dúvida que surja, no hesites em consultar-la, <strong>Estamos aqui para
|
||||
atender-te!</strong>
|
||||
conclusion: Obrigado por tua atenção!
|
||||
conclusion: Obrigado por tua atenção!
|
||||
|
|
|
@ -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": "invoiceIn.pdf",
|
||||
"component": "invoiceIn"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE html>
|
||||
<html v-bind:lang="$i18n.locale">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<title>{{ $t('subject') }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<table class="grid">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<!-- Empty block -->
|
||||
<div class="grid-row">
|
||||
<div class="grid-block empty"></div>
|
||||
</div>
|
||||
<!-- Header block -->
|
||||
<div class="grid-row">
|
||||
<div class="grid-block">
|
||||
<email-header v-bind="$props"></email-header>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Block -->
|
||||
<div class="grid-row">
|
||||
<div class="grid-block vn-pa-ml">
|
||||
<h1>{{ $t('title') }}</h1>
|
||||
<p>{{$t('dear')}},</p>
|
||||
<p v-html="$t('description')"></p>
|
||||
<p v-html="$t('conclusion')"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer block -->
|
||||
<div class="grid-row">
|
||||
<div class="grid-block">
|
||||
<email-footer v-bind="$props"></email-footer>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Empty block -->
|
||||
<div class="grid-row">
|
||||
<div class="grid-block empty"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
const Component = require(`vn-print/core/component`);
|
||||
const emailHeader = new Component('email-header');
|
||||
const emailFooter = new Component('email-footer');
|
||||
|
||||
module.exports = {
|
||||
name: 'invoiceIn',
|
||||
components: {
|
||||
'email-header': emailHeader.build(),
|
||||
'email-footer': emailFooter.build()
|
||||
}
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
subject: Your agricultural invoice
|
||||
title: Your agricultural invoice
|
||||
dear: Dear supplier
|
||||
description: Attached you can find agricultural receipt generated from your last deliveries. Please return a signed and stamped copy to our administration department.
|
||||
conclusion: Thanks for your attention!
|
|
@ -0,0 +1,5 @@
|
|||
subject: Tu factura agrícola
|
||||
title: Tu factura agrícola
|
||||
dear: Estimado proveedor
|
||||
description: Adjunto puede encontrar recibo agrícola generado de sus últimas entregas. Por favor, devuelva una copia firmada y sellada a nuestro de departamento de administración.
|
||||
conclusion: ¡Gracias por tu atención!
|
|
@ -0,0 +1,5 @@
|
|||
subject: Votre facture agricole
|
||||
title: Votre facture agricole
|
||||
dear: Cher Fournisseur
|
||||
description: Vous trouverez en pièce jointe le reçu agricole généré à partir de vos dernières livraisons. Veuillez retourner une copie signée et tamponnée à notre service administratif.
|
||||
conclusion: Merci pour votre attention!
|
|
@ -0,0 +1,5 @@
|
|||
subject: A sua fatura agrícola
|
||||
title: A sua fatura agrícola
|
||||
dear: Caro Fornecedor
|
||||
description: Em anexo encontra-se o recibo agrícola gerado a partir das suas últimas entregas. Por favor, devolva uma cópia assinada e carimbada ao nosso departamento de administração.
|
||||
conclusion: Obrigado pela atenção.
|
|
@ -0,0 +1,12 @@
|
|||
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/report.css`,
|
||||
`${__dirname}/style.css`])
|
||||
.mergeStyles();
|
|
@ -0,0 +1,42 @@
|
|||
h2 {
|
||||
font-weight: 100;
|
||||
color: #555
|
||||
}
|
||||
|
||||
.table-title {
|
||||
margin-bottom: 15px;
|
||||
font-size: .8rem
|
||||
}
|
||||
|
||||
.table-title h2 {
|
||||
margin: 0 15px 0 0
|
||||
}
|
||||
|
||||
.ticket-info {
|
||||
font-size: 22px
|
||||
}
|
||||
|
||||
|
||||
#nickname h2 {
|
||||
max-width: 400px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#phytosanitary {
|
||||
padding-right: 10px
|
||||
}
|
||||
|
||||
#phytosanitary .flag img {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
#phytosanitary .flag .flag-text {
|
||||
padding-left: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.phytosanitary-info {
|
||||
margin-top: 10px
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
|
@ -0,0 +1,214 @@
|
|||
<!DOCTYPE html>
|
||||
<html v-bind:lang="$i18n.locale">
|
||||
<body>
|
||||
<table class="grid">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
<!-- Header block -->
|
||||
<report-header v-bind="$props"
|
||||
v-bind:company-code="invoice.companyCode">
|
||||
</report-header>
|
||||
<!-- Block -->
|
||||
<div class="grid-row">
|
||||
<div class="grid-block">
|
||||
<div class="columns vn-mb-lg">
|
||||
<div class="size50">
|
||||
<div class="size75 vn-mt-ml">
|
||||
<h1 class="title uppercase">{{$t('title')}}</h1>
|
||||
<table class="row-oriented ticket-info">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="font gray uppercase">{{$t('supplierId')}}</td>
|
||||
<th>{{invoice.supplierId}}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font gray uppercase">{{$t('invoiceId')}}</td>
|
||||
<th>{{invoice.id}}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font gray uppercase">{{$t('date')}}</td>
|
||||
<th>{{invoice.created | date('%d-%m-%Y')}}</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="size50">
|
||||
<div class="panel">
|
||||
<div class="header">{{$t('invoiceData')}}</div>
|
||||
<div class="body">
|
||||
<h3 class="uppercase">{{invoice.name}}</h3>
|
||||
<div>
|
||||
{{invoice.postalAddress}}
|
||||
</div>
|
||||
<div>
|
||||
{{invoice.postcodeCity}}
|
||||
</div>
|
||||
<div v-if="invoice.nif">
|
||||
{{$t('fiscalId')}}: {{invoice.nif}}
|
||||
</div>
|
||||
<div v-if="invoice.phone">
|
||||
{{$t('phone')}}: {{invoice.phone}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="vn-mt-lg" v-for="entry in entries">
|
||||
<div class="table-title clearfix">
|
||||
<div class="pull-left">
|
||||
<h2>{{$t('invoiceId')}}</strong>
|
||||
</div>
|
||||
<div class="pull-left vn-mr-md">
|
||||
<div class="field rectangle">
|
||||
<span>{{entry.id}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-left">
|
||||
<h2>{{$t('date')}}</h2>
|
||||
</div>
|
||||
<div class="pull-left">
|
||||
<div class="field rectangle">
|
||||
<span>{{entry.landed | date}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span id="nickname" class="pull-right">
|
||||
<div class="pull-left">
|
||||
<h2>{{$t('reference')}}</h2>
|
||||
</div>
|
||||
<div class="pull-left">
|
||||
<div class="field rectangle">
|
||||
<span>{{entry.ref}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<table class="column-oriented">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="50%">{{$t('item')}}</th>
|
||||
<th class="number">{{$t('quantity')}}</th>
|
||||
<th class="number">{{$t('buyingValue')}}</th>
|
||||
<th class="number">{{$t('amount')}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-for="buy in entry.buys" class="no-page-break">
|
||||
<tr>
|
||||
<td width="50%">{{buy.name}}</td>
|
||||
<td class="number">{{buy.quantity}}</td>
|
||||
<td class="number">{{buy.buyingValue}}</td>
|
||||
<td class="number">{{buyImport(buy) | currency('EUR', $i18n.locale)}}</td>
|
||||
</tr>
|
||||
<tr class="description font light-gray">
|
||||
<td colspan="4">
|
||||
<span v-if="buy.value5">
|
||||
<strong>{{buy.tag5}}</strong> {{buy.value5}}
|
||||
</span>
|
||||
<span v-if="buy.value6">
|
||||
<strong>{{buy.tag6}}</strong> {{buy.value6}}
|
||||
</span>
|
||||
<span v-if="buy.value7">
|
||||
<strong>{{buy.tag7}}</strong> {{buy.value7}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="3" class="font bold">
|
||||
<span class="pull-right">{{$t('subtotal')}}</span>
|
||||
</td>
|
||||
<td class="number">{{entrySubtotal(entry) | currency('EUR', $i18n.locale)}}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<!-- End of sales block -->
|
||||
|
||||
<div class="columns vn-mt-xl">
|
||||
<!-- Taxes block -->
|
||||
<div id="taxes" class="size50 pull-right no-page-break" v-if="taxes">
|
||||
<table class="column-oriented">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="4">{{$t('taxBreakdown')}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<thead class="light">
|
||||
<tr>
|
||||
<th width="45%">{{$t('type')}}</th>
|
||||
<th width="25%" class="number">
|
||||
{{$t('taxBase')}}
|
||||
</th>
|
||||
<th>{{$t('tax')}}</th>
|
||||
<th class="number">{{$t('fee')}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="tax in taxes">
|
||||
<td width="45%">{{tax.name}}</td>
|
||||
<td width="25%" class="number">
|
||||
{{tax.taxableBase | currency('EUR', $i18n.locale)}}
|
||||
</td>
|
||||
<td>{{tax.rate | percentage}}</td>
|
||||
<td class="number">{{tax.vat | currency('EUR', $i18n.locale)}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="font bold">
|
||||
<td width="45%">{{$t('subtotal')}}</td>
|
||||
<td width="20%" class="number">
|
||||
{{sumTotal(taxes, 'taxableBase') | currency('EUR', $i18n.locale)}}
|
||||
</td>
|
||||
<td></td>
|
||||
<td class="number">{{sumTotal(taxes, 'vat') | currency('EUR', $i18n.locale)}}</td>
|
||||
</tr>
|
||||
<tr class="font bold">
|
||||
<td colspan="2">{{$t('total')}}</td>
|
||||
<td colspan="2" class="number">{{taxTotal() | currency('EUR', $i18n.locale)}}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<div class="panel" v-if="invoice.footNotes">
|
||||
<div class="header">{{$t('notes')}}</div>
|
||||
<div class="body">
|
||||
<span>{{invoice.footNotes}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End of taxes block -->
|
||||
|
||||
<!-- Observations block -->
|
||||
<div class="columns vn-mt-xl">
|
||||
<div class="size50 pull-left no-page-break" >
|
||||
<div class="panel" >
|
||||
<div class="header">{{$t('observations')}}</div>
|
||||
<div class="body">
|
||||
<div>{{$t('payMethod')}}</div>
|
||||
<div>{{invoice.payMethod}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End of observations block -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer block -->
|
||||
<report-footer id="pageFooter"
|
||||
v-bind:company-code="invoice.companyCode"
|
||||
v-bind:left-text="$t('invoiceId')"
|
||||
v-bind:center-text="invoice.name"
|
||||
v-bind="$props">
|
||||
</report-footer>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,94 @@
|
|||
const Component = require(`vn-print/core/component`);
|
||||
const reportHeader = new Component('report-header');
|
||||
const reportFooter = new Component('report-footer');
|
||||
|
||||
module.exports = {
|
||||
name: 'invoiceIn',
|
||||
async serverPrefetch() {
|
||||
this.invoice = await this.fetchInvoice(this.id);
|
||||
this.taxes = await this.fetchTaxes(this.id);
|
||||
|
||||
if (!this.invoice)
|
||||
throw new Error('Something went wrong');
|
||||
|
||||
const entries = await this.fetchEntry(this.id);
|
||||
const buys = await this.fetchBuy(this.id);
|
||||
|
||||
const map = new Map();
|
||||
|
||||
for (let entry of entries) {
|
||||
entry.buys = [];
|
||||
|
||||
map.set(entry.id, entry);
|
||||
}
|
||||
|
||||
for (let buy of buys) {
|
||||
const entry = map.get(buy.entryFk);
|
||||
|
||||
if (entry) entry.buys.push(buy);
|
||||
}
|
||||
|
||||
this.entries = entries;
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
methods: {
|
||||
fetchInvoice(id) {
|
||||
return this.findOneFromDef('invoice', [id]);
|
||||
},
|
||||
fetchEntry(id) {
|
||||
return this.rawSqlFromDef('entry', [id]);
|
||||
},
|
||||
fetchBuy(id) {
|
||||
return this.rawSqlFromDef('buy', [id]);
|
||||
},
|
||||
async fetchTaxes(id) {
|
||||
const taxes = await this.rawSqlFromDef(`taxes`, [id]);
|
||||
return this.taxVat(taxes);
|
||||
},
|
||||
buyImport(buy) {
|
||||
return buy.quantity * buy.buyingValue;
|
||||
},
|
||||
entrySubtotal(entry) {
|
||||
let subTotal = 0.00;
|
||||
for (let buy of entry.buys)
|
||||
subTotal += this.buyImport(buy);
|
||||
|
||||
return subTotal;
|
||||
},
|
||||
sumTotal(rows, prop) {
|
||||
let total = 0.00;
|
||||
for (let row of rows)
|
||||
total += parseFloat(row[prop]);
|
||||
|
||||
return total;
|
||||
},
|
||||
taxVat(taxes) {
|
||||
for (tax of taxes) {
|
||||
let vat = 0;
|
||||
if (tax.rate && tax.taxableBase)
|
||||
vat = (tax.rate / 100) * tax.taxableBase;
|
||||
|
||||
tax.vat = vat;
|
||||
}
|
||||
|
||||
return taxes;
|
||||
},
|
||||
taxTotal() {
|
||||
const base = this.sumTotal(this.taxes, 'taxableBase');
|
||||
const vat = this.sumTotal(this.taxes, 'vat');
|
||||
|
||||
return base + vat;
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'report-header': reportHeader.build(),
|
||||
'report-footer': reportFooter.build(),
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: Number,
|
||||
description: 'The invoice id'
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
reportName: invoice
|
||||
title: Agricultural invoice
|
||||
invoiceId: Agricultural invoice
|
||||
supplierId: Proveedor
|
||||
invoiceData: Invoice data
|
||||
reference: Reference
|
||||
fiscalId: FI / NIF
|
||||
phone: Phone
|
||||
date: Date
|
||||
item: Item
|
||||
quantity: Qty.
|
||||
concept: Concept
|
||||
buyingValue: PSP/u
|
||||
discount: Disc.
|
||||
vat: VAT
|
||||
amount: Amount
|
||||
type: Type
|
||||
taxBase: Tax base
|
||||
tax: Tax
|
||||
fee: Fee
|
||||
total: Total
|
||||
subtotal: Subtotal
|
||||
taxBreakdown: Tax breakdown
|
||||
observations: Observations
|
||||
payMethod: Pay method
|
|
@ -0,0 +1,25 @@
|
|||
reportName: factura
|
||||
title: Factura Agrícola
|
||||
invoiceId: Factura Agrícola
|
||||
supplierId: Proveedor
|
||||
invoiceData: Datos de facturación
|
||||
reference: Referencia
|
||||
fiscalId: CIF / NIF
|
||||
phone: Tlf
|
||||
date: Fecha
|
||||
item: Artículo
|
||||
quantity: Cant.
|
||||
concept: Concepto
|
||||
buyingValue: PVP/u
|
||||
discount: Dto.
|
||||
vat: IVA
|
||||
amount: Importe
|
||||
type: Tipo
|
||||
taxBase: Base imp.
|
||||
tax: Tasa
|
||||
fee: Cuota
|
||||
total: Total
|
||||
subtotal: Subtotal
|
||||
taxBreakdown: Desglose impositivo
|
||||
observations: Observaciones
|
||||
payMethod: Método de pago
|
|
@ -0,0 +1,18 @@
|
|||
SELECT
|
||||
b.id,
|
||||
e.id entryFk,
|
||||
it.name,
|
||||
b.quantity,
|
||||
b.buyingValue,
|
||||
(b.quantity * b.buyingValue) total,
|
||||
it.tag5,
|
||||
it.value5,
|
||||
it.tag6,
|
||||
it.value6,
|
||||
it.tag7,
|
||||
it.value7
|
||||
FROM entry e
|
||||
JOIN invoiceIn i ON i.id = e.invoiceInFk
|
||||
JOIN buy b ON b.entryFk = e.id
|
||||
JOIN item it ON it.id = b.itemFk
|
||||
WHERE i.id = ?
|
|
@ -0,0 +1,8 @@
|
|||
SELECT
|
||||
e.id,
|
||||
t.landed,
|
||||
e.ref
|
||||
FROM entry e
|
||||
JOIN invoiceIn i ON i.id = e.invoiceInFk
|
||||
JOIN travel t ON t.id = e.travelFk
|
||||
WHERE i.id = ?
|
|
@ -0,0 +1,14 @@
|
|||
SELECT
|
||||
i.id,
|
||||
s.id supplierId,
|
||||
i.created,
|
||||
s.name,
|
||||
s.street AS postalAddress,
|
||||
s.nif,
|
||||
s.phone,
|
||||
p.name payMethod
|
||||
FROM invoiceIn i
|
||||
JOIN supplier s ON s.id = i.supplierFk
|
||||
JOIN company c ON c.id = i.companyFk
|
||||
JOIN payMethod p ON p.id = s.payMethodFk
|
||||
WHERE i.id = ?
|
|
@ -0,0 +1,8 @@
|
|||
SELECT
|
||||
ti.iva as name,
|
||||
ti.PorcentajeIva as rate,
|
||||
iit.taxableBase
|
||||
FROM invoiceIn ii
|
||||
JOIN invoiceInTax iit ON ii.id = iit.invoiceInFk
|
||||
JOIN sage.TiposIva ti ON ti.CodigoIva = iit.taxTypeSageFk
|
||||
WHERE ii.id = ?;
|
Loading…
Reference in New Issue