diff --git a/assets/sass/invoice-pdf.scss b/assets/sass/invoice-pdf.scss index 866e59b4..2ad86e83 100644 --- a/assets/sass/invoice-pdf.scss +++ b/assets/sass/invoice-pdf.scss @@ -11,7 +11,7 @@ body { margin: 0; padding: 0; } -table, tr, td, p { +table, tr, td, th, p, h1, h2, h3, h4, h5 { padding: 0; margin: 0; } @@ -25,6 +25,15 @@ td { p { margin-bottom: 12px; } +.text-small { + font-size: 7pt; +} +.padding-left { + padding-left: 15px; +} +.text-center { + text-align: center; +} .text-right { text-align: right; } @@ -40,20 +49,21 @@ p { .wrapper { margin: 4px; } +table { + width: 100%; + border-spacing: 0; +} table.addresses, p { line-height: 14pt; } /* The address table */ table.addresses { - width: 100%; margin-bottom: 30px; } /* The PDF header */ table.header { - border-spacing: 0; padding-bottom: 4px; - border-bottom: 1px solid #ccc; - width: 100%; + border-bottom: 1px solid #999; } table.header .title { font-weight: normal; @@ -61,11 +71,9 @@ table.header .title { } /* The PDF footer */ table.footer { - border-spacing: 0; - border-top: 1px solid #ccc; + border-top: 1px solid #999; font-size: 80%; padding-top: 8px; - width: 100%; line-height: 14px; } table.footer td { @@ -73,7 +81,6 @@ table.footer td { } /* The invoice items list */ table.items { - width: 100%; border-collapse: collapse; margin-top: 50px; margin-bottom: 60px; @@ -88,7 +95,7 @@ table.items tbody td.first { } table.items thead th.last, table.items tbody td.last, -table.items tfoot td.last{ +table.items tfoot td.last { padding-right: 0; } diff --git a/public/build/entrypoints.json b/public/build/entrypoints.json index 7a7bef19..3d08a8a3 100644 --- a/public/build/entrypoints.json +++ b/public/build/entrypoints.json @@ -26,7 +26,7 @@ "build/invoice-pdf.0efd7a97.js" ], "css": [ - "build/invoice-pdf.cdf28364.css" + "build/invoice-pdf.d79f186b.css" ] }, "chart": { @@ -58,7 +58,7 @@ "build/invoice.74279541.js": "sha384-2BXic5Sgorf2tXai6zSAN4wLY2dbg06L03/xMKW6itMcszvtnRArKzfBh6DNcF3f", "build/invoice.13d8ef4e.css": "sha384-B6RN/wZJToSBCZk2JeLokIqWEhbh+Eb9arYbt9dM+YoC2Z6PnCeTwTqSGyexWWJh", "build/invoice-pdf.0efd7a97.js": "sha384-bSdIeRCtEJiYYuc2reb0e5CpJ1Kbd1lQNEkElMTiq1SX0IINzdwJJYf6WnCcHrNC", - "build/invoice-pdf.cdf28364.css": "sha384-DhWomcxV34umbO4PT7u0CJtvH+X3wAYjszoa3tvP1G08QdYkvtwfKhvdnL/Z8f+8", + "build/invoice-pdf.d79f186b.css": "sha384-KSYQKnPrIXiVyfsjoUPzXMIVjPGX7aoYCUKd1eTZUcmKz+a16xKRfzp8bGh8mMoV", "build/2.7ab75d0a.js": "sha384-Eb0w73RbUwsfLVRbeGf0Aql77LzAUDrwqY74pyTgwSnf0dsMKwsjlumzhO2JHnVN", "build/chart.34d60a88.js": "sha384-I57c9DtU3AOG2kzKqIZkIu0hi1aGYHRZ5QG4LKC9+9slzJnAMttPGXoL2cQG3m6y", "build/calendar.541a15eb.js": "sha384-a4HtJGznLY7D1lv25yrokaIVHLY++qak0X2qi83Er1UxWKKLb2Gu6foE+CMA9xhb", diff --git a/public/build/invoice-pdf.cdf28364.css b/public/build/invoice-pdf.cdf28364.css deleted file mode 100644 index f5ee133d..00000000 --- a/public/build/invoice-pdf.cdf28364.css +++ /dev/null @@ -1 +0,0 @@ -body{font-family:sans-serif;font-size:10pt}body,p,table,td,tr{margin:0;padding:0}th{border:none;text-align:left}td{vertical-align:top}p{margin-bottom:12px}.text-right{text-align:right}.text-nowrap{white-space:nowrap}.date{color:#666;font-size:80%;text-align:right;padding-top:6px}.wrapper{margin:4px}p,table.addresses{line-height:14pt}table.addresses{width:100%;margin-bottom:30px}table.header{border-spacing:0;padding-bottom:4px;border-bottom:1px solid #ccc;width:100%}table.header .title{font-weight:400;font-size:140%}table.footer{border-spacing:0;border-top:1px solid #ccc;font-size:80%;padding-top:8px;width:100%;line-height:14px}table.footer td{vertical-align:bottom}table.items{width:100%;border-collapse:collapse;margin-top:50px;margin-bottom:60px}table.items tbody td,table.items thead th{border-bottom:1px solid #ccc}table.items tbody td.first,table.items thead th.first{padding-left:0}table.items tbody td.last,table.items tfoot td.last,table.items thead th.last{padding-right:0}table.items td,table.items th{padding:8px 6px}table.items tfoot td,table.items tfoot th{padding:20px 0 0;line-height:1px}table.items tfoot td.last{padding-left:10px}table.items tr.odd{background-color:#f5f5f5} \ No newline at end of file diff --git a/public/build/invoice-pdf.d79f186b.css b/public/build/invoice-pdf.d79f186b.css new file mode 100644 index 00000000..8cd37b2d --- /dev/null +++ b/public/build/invoice-pdf.d79f186b.css @@ -0,0 +1 @@ +body{font-family:sans-serif;font-size:10pt}body,h1,h2,h3,h4,h5,p,table,td,th,tr{margin:0;padding:0}th{border:none;text-align:left}td{vertical-align:top}p{margin-bottom:12px}.text-small{font-size:7pt}.padding-left{padding-left:15px}.text-center{text-align:center}.text-right{text-align:right}.text-nowrap{white-space:nowrap}.date{color:#666;font-size:80%;text-align:right;padding-top:6px}.wrapper{margin:4px}table{width:100%;border-spacing:0}p,table.addresses{line-height:14pt}table.addresses{margin-bottom:30px}table.header{padding-bottom:4px;border-bottom:1px solid #999}table.header .title{font-weight:400;font-size:140%}table.footer{border-top:1px solid #999;font-size:80%;padding-top:8px;line-height:14px}table.footer td{vertical-align:bottom}table.items{border-collapse:collapse;margin-top:50px;margin-bottom:60px}table.items tbody td,table.items thead th{border-bottom:1px solid #ccc}table.items tbody td.first,table.items thead th.first{padding-left:0}table.items tbody td.last,table.items tfoot td.last,table.items thead th.last{padding-right:0}table.items td,table.items th{padding:8px 6px}table.items tfoot td,table.items tfoot th{padding:20px 0 0;line-height:1px}table.items tfoot td.last{padding-left:10px}table.items tr.odd{background-color:#f5f5f5} \ No newline at end of file diff --git a/public/build/manifest.json b/public/build/manifest.json index ee8eb3cd..379614dc 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -9,7 +9,7 @@ "build/chart.js": "build/chart.34d60a88.js", "build/invoice.css": "build/invoice.13d8ef4e.css", "build/invoice.js": "build/invoice.74279541.js", - "build/invoice-pdf.css": "build/invoice-pdf.cdf28364.css", + "build/invoice-pdf.css": "build/invoice-pdf.d79f186b.css", "build/invoice-pdf.js": "build/invoice-pdf.0efd7a97.js", "build/runtime.js": "build/runtime.098eaae1.js", "build/fonts/fa-brands-400.ttf": "build/fonts/fa-brands-400.085b1dd8.ttf", diff --git a/templates/invoice/renderer/default-pdf.pdf.twig b/templates/invoice/renderer/default-pdf.pdf.twig index 2507e6d0..dc600f83 100644 --- a/templates/invoice/renderer/default-pdf.pdf.twig +++ b/templates/invoice/renderer/default-pdf.pdf.twig @@ -5,9 +5,7 @@ {% set currency = model.currency %} - - - + {% block title %}{{ model.invoiceNumber }}-{{ model.customer.company|default(model.customer.name)|u.snake }}{% endblock %} + + + +
+ + + + + +
+ {{ model.customer.company|default(model.customer.name) }}
+ {{ model.customer.address|nl2br }} + {% if model.customer.vatId is not empty %} +
+ {{ 'label.vat_id'|trans }}: {{ model.customer.vatId }} + {% endif %} +
+ + + + + + + + + + + + + + + + + + {% if model.query.project is not empty and model.query.project.orderNumber is not empty %} + + + + + {% endif %} + {% if model.template.vatId is not empty %} + + + + + {% endif %} +
{{ 'label.date'|trans }}:{{ model.invoiceDate|date_short }}
{{ 'invoice.service_date'|trans }}:{{ model.query.end|month_name }} {{ model.query.end|date('Y') }}
{{ 'invoice.number'|trans }}:{{ model.invoiceNumber }}
{{ 'invoice.due_days'|trans }}:{{ model.dueDate|date_short }}
{{ 'label.orderNumber'|trans }}{{ model.query.project.orderNumber }}
{{ 'label.vat_id'|trans }}:{{ model.template.vatId }}
+
+ +

{{ model.template.title }}

+ + + + + + + + + + + + {% for entry in model.calculator.entries %} + {% set duration = entry.duration|duration(isDecimal) %} + {% if entry.fixedRate is not null %} + {% set rate = entry.fixedRate %} + {% set duration = entry.amount|amount %} + {% else %} + {% set rate = entry.hourlyRate %} + {% endif %} + + + + + + + + {% endfor %} + + + + + + + + + + + + + + + +
{{ 'label.description'|trans }}{{ 'label.unit_price'|trans }}{{ 'label.amount'|trans }}{{ 'label.total_rate'|trans }}
+ {% if entry.description is not empty %} + {{ entry.description|nl2br }} + {% else %} + {% if entry.activity is not null %}{{ entry.activity.name }} / {% endif %}{{ entry.project.name }} + {% endif %} + {{ rate|money(currency) }}{{ duration }}{{ entry.rate|money(currency) }}
+ {{ 'invoice.subtotal'|trans }} + {{ model.calculator.subtotal|money(currency) }}
+ {{ 'invoice.tax'|trans }} ({{ model.calculator.vat }}%) + {{ model.calculator.tax|money(currency) }}
+ {{ 'invoice.total'|trans }} + + {{ model.calculator.total|money(currency) }} +
+ + {% if model.template.paymentTerms is not empty %} +

+ {{ model.template.paymentTerms|md2html }} +

+ {% endif %} +
+ + diff --git a/tests/Invoice/Renderer/PdfRendererTest.php b/tests/Invoice/Renderer/PdfRendererTest.php index aedcda31..2818c4ed 100644 --- a/tests/Invoice/Renderer/PdfRendererTest.php +++ b/tests/Invoice/Renderer/PdfRendererTest.php @@ -31,7 +31,7 @@ class PdfRendererTest extends KernelTestCase $env = new Environment($loader); $sut = new PdfRenderer($env, $this->createMock(MPdfConverter::class)); $this->assertTrue($sut->supports($this->getInvoiceDocument('default.pdf.twig', true))); - $this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.html.twig'))); + $this->assertTrue($sut->supports($this->getInvoiceDocument('freelancer.pdf.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('foo.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx'))); diff --git a/tests/Invoice/Renderer/TwigRendererTest.php b/tests/Invoice/Renderer/TwigRendererTest.php index cb1bc3ba..7012d968 100644 --- a/tests/Invoice/Renderer/TwigRendererTest.php +++ b/tests/Invoice/Renderer/TwigRendererTest.php @@ -31,8 +31,8 @@ class TwigRendererTest extends KernelTestCase $sut = new TwigRenderer($env); $this->assertTrue($sut->supports($this->getInvoiceDocument('default.html.twig'))); - $this->assertTrue($sut->supports($this->getInvoiceDocument('freelancer.html.twig'))); $this->assertTrue($sut->supports($this->getInvoiceDocument('timesheet.html.twig'))); + $this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.pdf.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('foo.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('export.csv'))); diff --git a/tests/Repository/InvoiceDocumentRepositoryTest.php b/tests/Repository/InvoiceDocumentRepositoryTest.php index f1d6b27c..0e857009 100644 --- a/tests/Repository/InvoiceDocumentRepositoryTest.php +++ b/tests/Repository/InvoiceDocumentRepositoryTest.php @@ -32,7 +32,7 @@ class InvoiceDocumentRepositoryTest extends TestCase 'company.docx', 'default.html.twig', 'default-pdf.pdf.twig', - 'freelancer.html.twig', + 'freelancer.pdf.twig', 'timesheet.html.twig', 'text.txt.twig', 'javascript.json.twig', diff --git a/translations/invoice-renderer.de.xlf b/translations/invoice-renderer.de.xlf index 6cf0a43b..cb150695 100644 --- a/translations/invoice-renderer.de.xlf +++ b/translations/invoice-renderer.de.xlf @@ -66,7 +66,7 @@ freelancer - Freiberufler + Einzelnes Leistungsdatum open-spreadsheet diff --git a/translations/invoice-renderer.en.xlf b/translations/invoice-renderer.en.xlf index b0d678a0..05a67fcf 100644 --- a/translations/invoice-renderer.en.xlf +++ b/translations/invoice-renderer.en.xlf @@ -66,7 +66,7 @@ freelancer - Freelancer + Single service date open-spreadsheet