Skip to content
Code-Schnipsel Gruppen Projekte

Revisionen vergleichen

Änderungen werden so angezeigt, als ob die Quellrevision mit der Zielrevision zusammengeführt würde. Erfahre mehr über den Vergleich von Revisionen.

Quelle

Zielprojekt auswählen
No results found

Ziel

Zielprojekt auswählen
  • quiqqer/invoice
1 Ergebnis
Änderungen anzeigen
Commits auf Quelle (7)
...@@ -36,6 +36,7 @@ define('package/quiqqer/invoice/bin/backend/controls/panels/Invoice', [ ...@@ -36,6 +36,7 @@ define('package/quiqqer/invoice/bin/backend/controls/panels/Invoice', [
Binds: [ Binds: [
'print', 'print',
'storno', 'storno',
'download',
'copy', 'copy',
'creditNote', 'creditNote',
'openInfo', 'openInfo',
...@@ -199,6 +200,14 @@ define('package/quiqqer/invoice/bin/backend/controls/panels/Invoice', [ ...@@ -199,6 +200,14 @@ define('package/quiqqer/invoice/bin/backend/controls/panels/Invoice', [
} }
}); });
Actions.appendChild({
icon: 'fa fa-download',
text: QUILocale.get(lg, 'dialog.invoice.download.button'),
events: {
onClick: this.download
}
});
this.fireEvent('actionButtonCreate', [ this.fireEvent('actionButtonCreate', [
this, this,
Actions Actions
...@@ -554,6 +563,13 @@ define('package/quiqqer/invoice/bin/backend/controls/panels/Invoice', [ ...@@ -554,6 +563,13 @@ define('package/quiqqer/invoice/bin/backend/controls/panels/Invoice', [
}); });
}, },
download: function()
{
require(['package/quiqqer/invoice/bin/backend/utils/Dialogs'], (Dialogs)=> {
Dialogs.openDownloadDialog(this.getAttribute('data').hash);
});
},
/** /**
* Opens the storno / cancellation dialog * Opens the storno / cancellation dialog
* *
......
<?php
use QUI\ERP\Accounting\Invoice\Handler;
use QUI\ERP\Accounting\Invoice\Utils\Invoice as InvoiceUtils;
use horstoeko\zugferd\ZugferdProfiles;
use QUI\ERP\Defaults;
use QUI\System\Log;
use Symfony\Component\HttpFoundation\Response;
define('QUIQQER_SYSTEM', true);
define('QUIQQER_AJAX', true);
require_once dirname(__FILE__, 5) . '/header.php';
$User = QUI::getUserBySession();
if (!$User->canUseBackend()) {
exit;
}
if (empty($_REQUEST['invoice'])) {
exit;
}
if (empty($_REQUEST['type'])) {
exit;
}
$invoiceHash = $_REQUEST['invoice'];
$type = mb_strtoupper($_REQUEST['type']);
$profileMap = [
'PROFILE_BASIC' => ZugferdProfiles::PROFILE_BASIC,
'PROFILE_BASICWL' => ZugferdProfiles::PROFILE_BASICWL,
'PROFILE_EN16931' => ZugferdProfiles::PROFILE_EN16931,
'PROFILE_EXTENDED' => ZugferdProfiles::PROFILE_EXTENDED,
'PROFILE_XRECHNUNG' => ZugferdProfiles::PROFILE_XRECHNUNG,
'PROFILE_XRECHNUNG_2' => ZugferdProfiles::PROFILE_XRECHNUNG_2,
'PROFILE_XRECHNUNG_2_1' => ZugferdProfiles::PROFILE_XRECHNUNG_2_1,
'PROFILE_XRECHNUNG_2_2' => ZugferdProfiles::PROFILE_XRECHNUNG_2_2,
'PROFILE_MINIMUM' => ZugferdProfiles::PROFILE_MINIMUM,
'PROFILE_XRECHNUNG_2_3' => ZugferdProfiles::PROFILE_XRECHNUNG_2_3,
'PROFILE_XRECHNUNG_3' => ZugferdProfiles::PROFILE_XRECHNUNG_3,
];
try {
$Invoice = Handler::getInstance()->getInvoiceByHash($invoiceHash);
$fileName = InvoiceUtils::getInvoiceFilename($Invoice);
if ($type === 'PDF') {
$defaultTemplates = Defaults::conf('output', 'default_templates');
$defaultTemplates = json_decode($defaultTemplates, true);
$templateProvider = '';
$templateId = '';
if (!empty($defaultTemplates['Invoice'])) {
$templateProvider = $defaultTemplates['Invoice']['provider'];
$templateId = $defaultTemplates['Invoice']['id'];
}
$Request = QUI::getRequest();
$Request->query->set('id', $invoiceHash);
$Request->query->set('t', 'Invoice');
$Request->query->set('ep', 'quiqqer/invoice');
$Request->query->set('tpl', $templateId);
$Request->query->set('tplpr', $templateProvider);
include dirname(__FILE__, 4) . '/erp/bin/output/backend/download.php';
} else {
$document = InvoiceUtils::getElectronicInvoice($Invoice, $profileMap[$type]);
$content = $document->getContent();
$contentType = 'application/xml';
$fileName .= '.xml';
$response = new Response($content);
$response->headers->set('Content-Type', $contentType);
$response->headers->set('Content-Disposition', 'attachment; filename="' . $fileName . '"');
$response->headers->set('Content-Length', strlen($content));
$response->send();
}
} catch (Exception $e) {
Log::writeException($e);
}
.quiqqer-invoice-dialog-refund-label input { .quiqqer-invoice-dialog-refund-label input {
float: left; float: left;
margin-right: 10px; margin-right: 10px;
} }
\ No newline at end of file
/** invoice download dialog
============================================================================== */
.quiqqer-invoice-download-dialog {
text-align: center;
}
.quiqqer-invoice-download-dialog-buttons {
display: flex;
width: 100%;
gap: 1rem;
flex-wrap: wrap;
margin-top: 2rem;
justify-content: center;
}
.quiqqer-invoice-download-dialog button {
width: 200px;
}
...@@ -11,10 +11,11 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -11,10 +11,11 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
'Locale', 'Locale',
'package/quiqqer/invoice/bin/Invoices', 'package/quiqqer/invoice/bin/Invoices',
'qui/controls/windows/Confirm', 'qui/controls/windows/Confirm',
'qui/controls/windows/Popup',
'css!package/quiqqer/invoice/bin/backend/utils/Dialogs.css' 'css!package/quiqqer/invoice/bin/backend/utils/Dialogs.css'
], function(QUI, QUILocale, Invoices, QUIConfirm) { ], function (QUI, QUILocale, Invoices, QUIConfirm, QUIPopup) {
'use strict'; 'use strict';
const lg = 'quiqqer/invoice'; const lg = 'quiqqer/invoice';
...@@ -28,14 +29,14 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -28,14 +29,14 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
* @param {String} [entityType] * @param {String} [entityType]
* @return {Promise} * @return {Promise}
*/ */
openPrintDialog: function(invoiceId, entityType) { openPrintDialog: function (invoiceId, entityType) {
entityType = entityType || 'Invoice'; entityType = entityType || 'Invoice';
return Invoices.getInvoiceHistory(invoiceId).then(function(comments) { return Invoices.getInvoiceHistory(invoiceId).then(function (comments) {
return new Promise(function(resolve) { return new Promise(function (resolve) {
require([ require([
'package/quiqqer/erp/bin/backend/controls/OutputDialog' 'package/quiqqer/erp/bin/backend/controls/OutputDialog'
], function(OutputDialog) { ], function (OutputDialog) {
new OutputDialog({ new OutputDialog({
entityId: invoiceId, entityId: invoiceId,
entityType: entityType, entityType: entityType,
...@@ -55,11 +56,11 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -55,11 +56,11 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
* @param {String} invoiceId - Invoice ID or Hash * @param {String} invoiceId - Invoice ID or Hash
* @return {Promise} * @return {Promise}
*/ */
openStornoDialog: function(invoiceId) { openStornoDialog: function (invoiceId) {
return Invoices.get(invoiceId).then(function(result) { return Invoices.get(invoiceId).then(function (result) {
const id = result.id_prefix + result.id; const id = result.id_prefix + result.id;
return new Promise(function(resolve, reject) { return new Promise(function (resolve, reject) {
new QUIConfirm({ new QUIConfirm({
icon: 'fa fa-ban', icon: 'fa fa-ban',
texticon: 'fa fa-ban', texticon: 'fa fa-ban',
...@@ -80,7 +81,7 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -80,7 +81,7 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
maxHeight: 500, maxHeight: 500,
maxWidth: 750, maxWidth: 750,
events: { events: {
onOpen: function(Win) { onOpen: function (Win) {
const Container = Win.getContent().getElement('.textbody'); const Container = Win.getContent().getElement('.textbody');
// #locale // #locale
...@@ -111,14 +112,14 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -111,14 +112,14 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
Reason.focus(); Reason.focus();
}, },
onSubmit: function(Win) { onSubmit: function (Win) {
const Reason = Win.getContent().getElement('[name="reason"]'); const Reason = Win.getContent().getElement('[name="reason"]');
const value = Reason.value; const value = Reason.value;
if (value === '') { if (value === '') {
Reason.focus(); Reason.focus();
Reason.required = true; Reason.required = true;
if ('reportValidity' in Reason) { if ('reportValidity' in Reason) {
Reason.reportValidity(); Reason.reportValidity();
} }
...@@ -128,10 +129,10 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -128,10 +129,10 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
Win.Loader.show(); Win.Loader.show();
Invoices.reversalInvoice(result.hash, value).then(function(result) { Invoices.reversalInvoice(result.hash, value).then(function (result) {
Win.close(); Win.close();
resolve(result); resolve(result);
}).catch(function(Exception) { }).catch(function (Exception) {
Win.close(); Win.close();
reject(Exception); reject(Exception);
}); });
...@@ -150,7 +151,7 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -150,7 +151,7 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
* @param {String} invoiceId - Invoice ID or Hash * @param {String} invoiceId - Invoice ID or Hash
* @return {*|Promise} * @return {*|Promise}
*/ */
openCancellationDialog: function(invoiceId) { openCancellationDialog: function (invoiceId) {
return this.openStornoDialog(invoiceId); return this.openStornoDialog(invoiceId);
}, },
...@@ -160,7 +161,7 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -160,7 +161,7 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
* @param {String} invoiceId - Invoice ID or Hash * @param {String} invoiceId - Invoice ID or Hash
* @return {*|Promise} * @return {*|Promise}
*/ */
openReversalDialog: function(invoiceId) { openReversalDialog: function (invoiceId) {
return this.openStornoDialog(invoiceId); return this.openStornoDialog(invoiceId);
}, },
...@@ -170,11 +171,11 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -170,11 +171,11 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
* @param {String} invoiceId - Invoice ID or Hash * @param {String} invoiceId - Invoice ID or Hash
* @return {Promise} * @return {Promise}
*/ */
openCopyDialog: function(invoiceId) { openCopyDialog: function (invoiceId) {
return Invoices.get(invoiceId).then(function(result) { return Invoices.get(invoiceId).then(function (result) {
const id = result.id_prefix + result.id; const id = result.id_prefix + result.id;
return new Promise(function(resolve) { return new Promise(function (resolve) {
new QUIConfirm({ new QUIConfirm({
title: QUILocale.get(lg, 'dialog.invoice.copy.title'), title: QUILocale.get(lg, 'dialog.invoice.copy.title'),
text: QUILocale.get(lg, 'dialog.invoice.copy.text'), text: QUILocale.get(lg, 'dialog.invoice.copy.text'),
...@@ -191,13 +192,13 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -191,13 +192,13 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
textimage: 'fa fa-copy' textimage: 'fa fa-copy'
}, },
events: { events: {
onSubmit: function(Win) { onSubmit: function (Win) {
Win.Loader.show(); Win.Loader.show();
Invoices.copyInvoice(result.hash).then(function(newId) { Invoices.copyInvoice(result.hash).then(function (newId) {
Win.close(); Win.close();
resolve(newId); resolve(newId);
}).then(function() { }).then(function () {
Win.Loader.hide(); Win.Loader.hide();
}); });
} }
...@@ -213,14 +214,14 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -213,14 +214,14 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
* @param {String} invoiceId - Invoice ID or Hash * @param {String} invoiceId - Invoice ID or Hash
* @return {Promise} * @return {Promise}
*/ */
openCreateCreditNoteDialog: function(invoiceId) { openCreateCreditNoteDialog: function (invoiceId) {
const self = this; const self = this;
return Invoices.get(invoiceId).then(function(result) { return Invoices.get(invoiceId).then(function (result) {
let paymentHasRefund = false; let paymentHasRefund = false;
const id = result.id_prefix + result.id; const id = result.id_prefix + result.id;
return new Promise(function(resolve, reject) { return new Promise(function (resolve, reject) {
new QUIConfirm({ new QUIConfirm({
icon: 'fa fa-clipboard', icon: 'fa fa-clipboard',
texticon: 'fa fa-clipboard', texticon: 'fa fa-clipboard',
...@@ -241,10 +242,10 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -241,10 +242,10 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
maxHeight: 400, maxHeight: 400,
maxWidth: 600, maxWidth: 600,
events: { events: {
onOpen: function(Win) { onOpen: function (Win) {
Win.Loader.show(); Win.Loader.show();
Invoices.hasRefund(id).then(function(hasRefund) { Invoices.hasRefund(id).then(function (hasRefund) {
paymentHasRefund = hasRefund; paymentHasRefund = hasRefund;
QUI.fireEvent('quiqqerInvoiceCreateCreditNoteDialogOpen', [id, Win]); QUI.fireEvent('quiqqerInvoiceCreateCreditNoteDialogOpen', [id, Win]);
...@@ -274,16 +275,16 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -274,16 +275,16 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
}); });
}, },
onSubmit: function(Win) { onSubmit: function (Win) {
Win.Loader.show(); Win.Loader.show();
const Content = Win.getContent(), const Content = Win.getContent(),
Refund = Content.getElement('[name="refund"]'); Refund = Content.getElement('[name="refund"]');
const createInvoice = function(values) { const createInvoice = function (values) {
values = values || {}; values = values || {};
Invoices.createCreditNote(result.hash, values).then(function(newId) { Invoices.createCreditNote(result.hash, values).then(function (newId) {
QUI.fireEvent( QUI.fireEvent(
'quiqqerInvoiceCreateCreditNoteDialogSubmit', 'quiqqerInvoiceCreateCreditNoteDialogSubmit',
[newId, Win] [newId, Win]
...@@ -291,7 +292,7 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -291,7 +292,7 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
resolve(newId); resolve(newId);
Win.close(); Win.close();
}).catch(function(Err) { }).catch(function (Err) {
Win.Loader.hide(); Win.Loader.hide();
console.error(Err); console.error(Err);
...@@ -300,7 +301,7 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -300,7 +301,7 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
}; };
if (paymentHasRefund && Refund.checked) { if (paymentHasRefund && Refund.checked) {
self.openRefundWindow(invoiceId).then(function(RefundWindow) { self.openRefundWindow(invoiceId).then(function (RefundWindow) {
if (!RefundWindow) { if (!RefundWindow) {
Win.Loader.hide(); Win.Loader.hide();
return; return;
...@@ -309,7 +310,7 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -309,7 +310,7 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
createInvoice({ createInvoice({
refund: RefundWindow.getValues() refund: RefundWindow.getValues()
}); });
}).catch(function(Err) { }).catch(function (Err) {
Win.Loader.hide(); Win.Loader.hide();
console.error(Err); console.error(Err);
}); });
...@@ -319,7 +320,7 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -319,7 +320,7 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
createInvoice(); createInvoice();
}, },
onCancel: function() { onCancel: function () {
resolve(false); resolve(false);
} }
} }
...@@ -333,23 +334,84 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [ ...@@ -333,23 +334,84 @@ define('package/quiqqer/invoice/bin/backend/utils/Dialogs', [
* @param invoiceId * @param invoiceId
* @return {Promise} * @return {Promise}
*/ */
openRefundWindow: function(invoiceId) { openRefundWindow: function (invoiceId) {
return new Promise(function(resolve) { return new Promise(function (resolve) {
require([ require([
'package/quiqqer/invoice/bin/backend/controls/panels/refund/Window' 'package/quiqqer/invoice/bin/backend/controls/panels/refund/Window'
], function(RefundWindow) { ], function (RefundWindow) {
new RefundWindow({ new RefundWindow({
invoiceId: invoiceId, invoiceId: invoiceId,
autoRefund: false, autoRefund: false,
events: { events: {
onSubmit: resolve, onSubmit: resolve,
onCancel: function() { onCancel: function () {
resolve(false); resolve(false);
} }
} }
}).open(); }).open();
}); });
}); });
},
openDownloadDialog: function (hash) {
new QUIPopup({
icon: 'fa fa-download',
title: QUILocale.get(lg, 'dialog.invoice.download.title'),
autoclose: false,
maxHeight: 400,
maxWidth: 600,
buttons: false,
events: {
onOpen: function (Win) {
Win.Loader.show();
const Content = Win.getContent();
Content.classList.add('quiqqer-invoice-download-dialog');
Content.set(
'html',
'<h3>' + QUILocale.get(lg, 'dialog.invoice.download.header') + '</h3>' +
QUILocale.get(lg, 'dialog.invoice.download.text') +
'<div class="quiqqer-invoice-download-dialog-buttons">' +
' <button value="PDF" class="qui-button">PDF</button>' +
' <button value="PROFILE_BASIC" class="qui-button">ZUGFeRD Basic</button>' +
' <button value="PROFILE_EN16931" class="qui-button">ZUGFeRD EN16931</button>' +
' <button value="PROFILE_EXTENDED" class="qui-button">ZUGFeRD Extended</button>' +
' <button value="PROFILE_XRECHNUNG_2_3" class="qui-button">XRechnung 2.3</button>' +
' <button value="PROFILE_XRECHNUNG_3" class="qui-button">XRechnung 3</button>' +
'</div>'
);
Content.querySelectorAll('button').forEach(function (Button) {
Button.addEventListener('click', function () {
const id = 'download-invoice-' + hash + '-' + Button.value;
new Element('iframe', {
src: URL_OPT_DIR + 'quiqqer/invoice/bin/backend/download.php?' + Object.toQueryString({
invoice: hash,
type: Button.value
}),
id: id,
styles: {
position: 'absolute',
top: -200,
left: -200,
width: 50,
height: 50
}
}).inject(document.body);
(function () {
//document.getElements('#' + id).destroy();
}).delay(1000, this);
});
});
Win.Loader.hide();
}
}
}).open();
} }
}; };
}); });
...@@ -29,7 +29,8 @@ ...@@ -29,7 +29,8 @@
"quiqqer/htmltopdf": "^3", "quiqqer/htmltopdf": "^3",
"quiqqer/utils": "^2", "quiqqer/utils": "^2",
"ramsey/uuid": "^3|^4", "ramsey/uuid": "^3|^4",
"chillerlan/php-qrcode": "^4.3" "chillerlan/php-qrcode": "^4.3",
"horstoeko/zugferd": "^1"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
......
...@@ -91,7 +91,7 @@ ...@@ -91,7 +91,7 @@
<field type="VARCHAR(50) NULL">invoice_address_id</field> <field type="VARCHAR(50) NULL">invoice_address_id</field>
<field type="TEXT NULL">invoice_address</field> <field type="TEXT NULL">invoice_address</field>
<field type="INT NULL">delivery_address_id</field> <field type="VARCHAR(50) NULL">delivery_address_id</field>
<field type="TEXT NULL">delivery_address</field> <field type="TEXT NULL">delivery_address</field>
<field type="VARCHAR(255) NULL">order_id</field> <field type="VARCHAR(255) NULL">order_id</field>
......
...@@ -415,6 +415,43 @@ ...@@ -415,6 +415,43 @@
<en><![CDATA[Edit Invoice status:]]></en> <en><![CDATA[Edit Invoice status:]]></en>
</locale> </locale>
<locale name="invoice.settings.e-invoice.xInvoiceAttachment.text">
<de><![CDATA[X-Rechnung anhängen]]></de>
<en><![CDATA[Attach X-bill]]></en>
</locale>
<locale name="invoice.settings.e-invoice.xInvoiceAttachment.description">
<de><![CDATA[
Bei Rechnungsmails werden Rechnungen automatisch auch als X-Rechnung XML angehängt,
um den Standard für elektronische Rechnungen zu erfüllen.
]]></de>
<en><![CDATA[
Invoices are automatically attached to invoice e-mails as X-invoices xml format,
to comply with the standard for electronic invoices.
]]></en>
</locale>
<locale name="invoice.settings.e-invoice.xInvoiceAttachmentType.text">
<de><![CDATA[X-Rechnung Profil]]></de>
<en><![CDATA[X-bill profile]]></en>
</locale>
<locale name="invoice.settings.e-invoice.zugferdInvoiceAttachment.text">
<de><![CDATA[Zugferd-Rechnung anhängen]]></de>
<en><![CDATA[Attach Zugferd-bill]]></en>
</locale>
<locale name="invoice.settings.e-invoice.zugferdInvoiceAttachment.description">
<de><![CDATA[
Bei Rechnungsmails werden Rechnungen automatisch im Zugferd PDF Format angehängt.
]]></de>
<en><![CDATA[
Invoices are automatically attached to invoice emails in Zugferd PDF format.
]]></en>
</locale>
<locale name="invoice.settings.e-invoice.zugferdInvoiceAttachmentType.text">
<de><![CDATA[Zugferd-Rechnung Profil]]></de>
<en><![CDATA[Zugferd-bill profile]]></en>
</locale>
<locale name="invoice.type.creditNote"> <locale name="invoice.type.creditNote">
<de><![CDATA[Gutschrift]]></de> <de><![CDATA[Gutschrift]]></de>
<en><![CDATA[Credit Note]]></en> <en><![CDATA[Credit Note]]></en>
...@@ -1245,6 +1282,23 @@ ...@@ -1245,6 +1282,23 @@
<en><![CDATA[Please enter a reason for cancellation.]]></en> <en><![CDATA[Please enter a reason for cancellation.]]></en>
</locale> </locale>
<locale name="dialog.invoice.download.button">
<de><![CDATA[Rechnung herunterladen]]></de>
<en><![CDATA[Download invoice]]></en>
</locale>
<locale name="dialog.invoice.download.title">
<de><![CDATA[Lade die Rechnung in verschiedenen Formaten herunter]]></de>
<en><![CDATA[Download the invoice in various formats]]></en>
</locale>
<locale name="dialog.invoice.download.header">
<de><![CDATA[Rechnung herunterladen]]></de>
<en><![CDATA[Download invoice]]></en>
</locale>
<locale name="dialog.invoice.download.text" html="true">
<de><![CDATA[<p>Wähle das gewünschte Format aus, um deine Rechnung herunterzuladen.</p>]]></de>
<en><![CDATA[<p>Select the desired format to download your invoice.</p>]]></en>
</locale>
<locale name="dialog.create.address.title"> <locale name="dialog.create.address.title">
<de><![CDATA[Rechnungsadresse anlegen]]></de> <de><![CDATA[Rechnungsadresse anlegen]]></de>
<en><![CDATA[Create invoice address]]></en> <en><![CDATA[Create invoice address]]></en>
......
...@@ -41,6 +41,23 @@ ...@@ -41,6 +41,23 @@
<conf name="invoiceAddressRequirementThreshold"> <conf name="invoiceAddressRequirementThreshold">
<type><![CDATA[float]]></type> <type><![CDATA[float]]></type>
</conf> </conf>
<conf name="xInvoiceAttachment">
<type><![CDATA[bool]]></type>
<defaultvalue><![CDATA[1]]></defaultvalue>
</conf>
<conf name="xInvoiceAttachmentType">
<type><![CDATA[int]]></type>
<defaultvalue><![CDATA[2]]></defaultvalue>
</conf>
<conf name="zugferdInvoiceAttachment">
<type><![CDATA[bool]]></type>
<defaultvalue><![CDATA[1]]></defaultvalue>
</conf>
<conf name="zugferdInvoiceAttachmentType">
<type><![CDATA[int]]></type>
<defaultvalue><![CDATA[2]]></defaultvalue>
</conf>
</section> </section>
<section name="temporaryInvoice"> <section name="temporaryInvoice">
...@@ -214,6 +231,74 @@ ...@@ -214,6 +231,74 @@
</input> </input>
</settings> </settings>
<settings title="eInvoice" name="eInvoice">
<title>
<locale group="quiqqer/invoice" var="invoice.settings.e-invoice.title"/>
</title>
<input conf="invoice.xInvoiceAttachment" type="checkbox">
<text>
<locale group="quiqqer/invoice"
var="invoice.settings.e-invoice.xInvoiceAttachment.text"/>
</text>
<description>
<locale group="quiqqer/invoice"
var="invoice.settings.e-invoice.xInvoiceAttachment.description"/>
</description>
</input>
<select conf="invoice.xInvoiceAttachmentType">
<text>
<locale group="quiqqer/invoice"
var="invoice.settings.e-invoice.xInvoiceAttachmentType.text"/>
</text>
<option value="0">Basic</option>
<option value="1">Basic WL</option>
<option value="2">EN16931</option>
<option value="3">Extended</option>
<option value="4">XRechnung (Germany only)</option>
<option value="5">XRechnung 2.0 (Germany only)</option>
<option value="6">XRechnung 2.1 (Germany only)</option>
<option value="7">XRechnung 2.2 (Germany only)</option>
<option value="8">Minimum</option>
<option value="9">XRechnung 2.3 (Germany only)</option>
<option value="10">XRechnung 3.0 (Germany only)</option>
</select>
<input conf="invoice.zugferdInvoiceAttachment" type="checkbox">
<text>
<locale group="quiqqer/invoice"
var="invoice.settings.e-invoice.zugferdInvoiceAttachment.text"/>
</text>
<description>
<locale group="quiqqer/invoice"
var="invoice.settings.e-invoice.zugferdInvoiceAttachment.description"/>
</description>
</input>
<select conf="invoice.zugferdInvoiceAttachmentType">
<text>
<locale group="quiqqer/invoice"
var="invoice.settings.e-invoice.zugferdInvoiceAttachmentType.text"/>
</text>
<option value="0">Basic</option>
<option value="1">Basic WL</option>
<option value="2">EN16931</option>
<option value="3">Extended</option>
<option value="4">XRechnung (Germany only)</option>
<option value="5">XRechnung 2.0 (Germany only)</option>
<option value="6">XRechnung 2.1 (Germany only)</option>
<option value="7">XRechnung 2.2 (Germany only)</option>
<option value="8">Minimum</option>
<option value="9">XRechnung 2.3 (Germany only)</option>
<option value="10">XRechnung 3.0 (Germany only)</option>
</select>
</settings>
<settings title="invoiceDownload" name="invoiceDownload"> <settings title="invoiceDownload" name="invoiceDownload">
<title> <title>
<locale group="quiqqer/invoice" var="invoice.settings.download.title"/> <locale group="quiqqer/invoice" var="invoice.settings.download.title"/>
......
...@@ -6,6 +6,9 @@ ...@@ -6,6 +6,9 @@
namespace QUI\ERP\Accounting\Invoice; namespace QUI\ERP\Accounting\Invoice;
use horstoeko\zugferd\ZugferdDocumentBuilder;
use horstoeko\zugferd\ZugferdDocumentPdfBuilder;
use horstoeko\zugferd\ZugferdDocumentPdfMerger;
use QUI; use QUI;
use QUI\ERP\Accounting\Invoice\Output\OutputProviderCancelled; use QUI\ERP\Accounting\Invoice\Output\OutputProviderCancelled;
use QUI\ERP\Accounting\Invoice\Output\OutputProviderCreditNote; use QUI\ERP\Accounting\Invoice\Output\OutputProviderCreditNote;
...@@ -23,6 +26,7 @@ ...@@ -23,6 +26,7 @@
use function file_get_contents; use function file_get_contents;
use function in_array; use function in_array;
use function is_numeric; use function is_numeric;
use function str_replace;
use function strtolower; use function strtolower;
use function strtotime; use function strtotime;
...@@ -287,6 +291,7 @@ public static function onQuiqqerErpGetHistoryByUser( ...@@ -287,6 +291,7 @@ public static function onQuiqqerErpGetHistoryByUser(
* @param string $entityType * @param string $entityType
* @param string $recipient * @param string $recipient
* @param Mailer $Mailer * @param Mailer $Mailer
* @param string $mailFile
* @return void * @return void
* *
* @throws Exception * @throws Exception
...@@ -297,7 +302,8 @@ public static function onQuiqqerErpOutputSendMailBefore( ...@@ -297,7 +302,8 @@ public static function onQuiqqerErpOutputSendMailBefore(
$entityId, $entityId,
string $entityType, string $entityType,
string $recipient, string $recipient,
QUI\Mail\Mailer $Mailer QUI\Mail\Mailer $Mailer,
string $mailFile = ''
): void { ): void {
$allowedEntityTypes = [ $allowedEntityTypes = [
OutputProviderInvoice::getEntityType(), OutputProviderInvoice::getEntityType(),
...@@ -316,6 +322,29 @@ public static function onQuiqqerErpOutputSendMailBefore( ...@@ -316,6 +322,29 @@ public static function onQuiqqerErpOutputSendMailBefore(
return; return;
} }
// extend pdf with e-invoice
$Config = QUI::getPackage('quiqqer/invoice')->getConfig();
if ($Config->getValue('invoice', 'xInvoiceAttachment')) {
$xmlFile = str_replace('.pdf', '.xml', $mailFile);
$document = QUI\ERP\Accounting\Invoice\Utils\Invoice::getElectronicInvoice(
$Invoice,
$Config->getValue('invoice', 'xInvoiceAttachmentType')
);
$document->writeFile($xmlFile);
$Mailer->addAttachment($xmlFile);
}
if (file_exists($mailFile) && $Config->getValue('invoice', 'zugferdInvoiceAttachment')) {
$document = QUI\ERP\Accounting\Invoice\Utils\Invoice::getElectronicInvoice(
$Invoice,
$Config->getValue('invoice', 'xInvoiceAttachmentType')
);
$pdfBuilder = new ZugferdDocumentPdfBuilder($document, $mailFile);
$pdfBuilder->generateDocument()->saveDocument($mailFile);
}
// @todo // @todo
$customerFiles = $Invoice->getCustomerFiles(); $customerFiles = $Invoice->getCustomerFiles();
......
...@@ -6,13 +6,17 @@ ...@@ -6,13 +6,17 @@
namespace QUI\ERP\Accounting\Invoice\Utils; namespace QUI\ERP\Accounting\Invoice\Utils;
use IntlDateFormatter; use DateTime;
use QUI; use QUI;
use QUI\ERP\Accounting\Invoice\Exception; use QUI\ERP\Accounting\Invoice\Exception;
use QUI\ERP\Accounting\Invoice\InvoiceTemporary; use QUI\ERP\Accounting\Invoice\InvoiceTemporary;
use QUI\ERP\Accounting\Invoice\ProcessingStatus\Handler as ProcessingStatuses; use QUI\ERP\Accounting\Invoice\ProcessingStatus\Handler as ProcessingStatuses;
use QUI\ERP\Currency\Currency; use QUI\ERP\Currency\Currency;
use QUI\ExceptionStack; use QUI\ExceptionStack;
use QUI\ERP\Defaults;
use IntlDateFormatter;
use horstoeko\zugferd\ZugferdDocumentBuilder;
use horstoeko\zugferd\ZugferdProfiles;
use function array_map; use function array_map;
use function array_merge; use function array_merge;
...@@ -657,4 +661,128 @@ public static function addressRequirementThreshold(): float ...@@ -657,4 +661,128 @@ public static function addressRequirementThreshold(): float
return floatval($threshold); return floatval($threshold);
} }
public static function getElectronicInvoice(
InvoiceTemporary|QUI\ERP\Accounting\Invoice\Invoice $Invoice,
$type = ZugferdProfiles::PROFILE_EN16931
): ZugferdDocumentBuilder {
$document = ZugferdDocumentBuilder::CreateNew($type);
$date = $Invoice->getAttribute('date');
$date = strtotime($date);
$date = (new DateTime())->setTimestamp($date);
$document->setDocumentInformation(
$Invoice->getPrefixedNumber(),
"380",
$date,
$Invoice->getCurrency()->getCode()
);
// seller / owner
$document
->setDocumentSeller(Defaults::conf('company', 'name'))
->addDocumentSellerGlobalId("4000001123452", "0088")
->addDocumentSellerTaxRegistration("FC", "201/113/40209")
->addDocumentSellerTaxRegistration("VA", "DE123456789")
->setDocumentSellerAddress(
Defaults::conf('company', 'street'),
"",
"",
Defaults::conf('company', 'zip'),
Defaults::conf('company', 'city'),
Defaults::conf('company', 'country') // @todo country ->code<-
)
->setDocumentSellerCommunication(
'EM',
Defaults::conf('company', 'email')
)
->setDocumentSellerContact(
Defaults::conf('company', 'owner'), // @todo contact person
'', // @todo contact department
Defaults::conf('company', 'phone'), // @todo contact phone
Defaults::conf('company', 'fax'), // @todo contact fax
Defaults::conf('company', 'email') // @todo contact email
);
// bank stuff
$bankAccount = QUI\ERP\BankAccounts\Handler::getCompanyBankAccount();
if (!empty($bankAccount)) {
$document->addDocumentPaymentMeanToDirectDebit(
$bankAccount['iban'],
$Invoice->getPrefixedNumber()
);
}
// customer
$Customer = $Invoice->getCustomer();
$document
->setDocumentBuyer(
$Customer->getName(),
$Customer->getCustomerNo()
)
->setDocumentBuyerAddress(
$Customer->getAddress()->getAttribute('street_no'),
"",
"",
$Customer->getAddress()->getAttribute('zip'),
$Customer->getAddress()->getAttribute('city'),
$Customer->getAddress()->getCountry()->getCode()
)
->setDocumentBuyerCommunication('EM', $Customer->getAddress()->getAttribute('email'))
->setDocumentBuyerReference($Customer->getUUID());
// total
$priceCalculation = $Invoice->getPriceCalculation();
$vatTotal = 0;
foreach ($priceCalculation->getVat() as $vat) {
$document->addDocumentTax(
"S",
"VAT",
$priceCalculation->getSum()->value(),
$vat->value(),
$vat->getVat()
);
$vatTotal = $vatTotal + $vat->value();
}
$document->setDocumentSummation(
$priceCalculation->getSum()->value(),
$priceCalculation->getSum()->value(),
$priceCalculation->getSum()->value(),
0.0,
0.0,
$priceCalculation->getSum()->value(),
$vatTotal,
null,
0.0
);
// products
foreach ($Invoice->getArticles() as $Article) {
/* @var $Article QUI\ERP\Accounting\Article */
$article = $Article->toArray();
$document
->addNewPosition($article['position'])
->setDocumentPositionProductDetails(
$article['title'],
$article['description'],
null,
null,
null,
null
)
->setDocumentPositionNetPrice($article['calculated']['nettoPrice'])
->setDocumentPositionQuantity($article['quantity'], "H87")
->addDocumentPositionTax('S', 'VAT', $article['vat'])
->setDocumentPositionLineSummation($article['sum']);
}
return $document;
}
} }