Newer
Older
* This file contains QUI\ERP\Accounting\Invoice\InvoiceTemporary
use QUI\ERP\Accounting\ArticleList;
use QUI\ERP\Accounting\Calculations;
use QUI\ERP\Accounting\Invoice\Utils\Invoice as InvoiceUtils;
use QUI\ERP\Accounting\Payments\Transactions\Transaction;
use QUI\ERP\Exception;

Patrick Müller
committed
use QUI\ERP\Order\Handler as OrderHandler;
use QUI\ERP\ErpEntityInterface;
use QUI\ERP\ErpTransactionsInterface;
use QUI\ERP\ErpCopyInterface;
use function array_flip;
use function class_exists;
use function date;
use function is_array;
use function is_numeric;
use function is_string;
use function json_decode;
use function json_encode;
use function json_last_error;
use function mb_strpos;
use function str_replace;
use function strip_tags;
use function strtotime;
* - Temporary Invoice / Invoice class
* - A temporary invoice is a non posted invoice, it is not a real invoice
* - To create a correct invoice from the temporary invoice, the post method must be used
*
* @package QUI\ERP\Accounting\Invoice
*/
class InvoiceTemporary extends QUI\QDOM implements ErpEntityInterface, ErpTransactionsInterface, ErpCopyInterface
/**
* Special attributes
*/
/**
* If this attribute is set to this InvoiceTemporary the invoice is NOT
* automatically send to the customer when posted REGARDLESS of the setting
* in this module.
*/
const SPECIAL_ATTRIBUTE_DO_NOT_SEND_CREATION_MAIL = 'do_not_send_creation_mail';

Henning Leutz
committed
/**
* @var string
*/

Henning Leutz
committed
/**
* variable data for developers
*
* @var array
*/
protected array $paymentData = [];
/**
* @var ArticleList
*/
protected array | false $addressDelivery = [];
protected ?QUI\ERP\Currency\Currency $Currency = null;
/**
* Invoice constructor.
*
* @param $id
* @param Handler $Handler
*/
public function __construct($id, Handler $Handler)
{
$data = $Handler->getTemporaryInvoiceData($id);
$this->prefix = Settings::getInstance()->getTemporaryInvoicePrefix();
$this->id = $data['id'];
$this->setAttribute('hash', $id);
$this->Articles = new ArticleList();
$this->History = new QUI\ERP\Comments();
if (!empty($data['delivery_address'])) {
$deliveryAddressData = json_decode($data['delivery_address'], true);
$this->addressDelivery = $deliveryAddressData;
} else {
$this->addressDelivery = false;
}
if (
empty($this->addressDelivery['company'])
&& empty($this->addressDelivery['firstname'])
&& empty($this->addressDelivery['lastname'])
&& empty($this->addressDelivery['street_no'])
&& empty($this->addressDelivery['zip'])
&& empty($this->addressDelivery['city'])
&& empty($this->addressDelivery['county'])
) {
$this->addressDelivery = false;
}
} else {
if (isset($data['articles'])) {
$articles = json_decode($data['articles'], true);
if ($articles) {
try {
$this->Articles = new ArticleList($articles);
} catch (QUI\ERP\Exception $Exception) {
QUI\System\Log::addError($Exception->getMessage());
}
}
if (isset($data['history'])) {
$this->History = QUI\ERP\Comments::unserialize($data['history']);
}
if (isset($data['comments'])) {
$this->Comments = QUI\ERP\Comments::unserialize($data['comments']);
if (!empty($data['global_process_id'])) {

Henning Leutz
committed
$this->globalProcessId = $data['global_process_id'];
} else {
$this->globalProcessId = $data['hash'];
}
if (!empty($data['data'])) {
$this->data = json_decode($data['data'], true) ?? [];
}
if (isset($data['custom_data'])) {
$this->customData = json_decode($data['custom_data'], true) ?? [];
// invoice payment data
$paymentData = QUI\Security\Encryption::decrypt($data['payment_data']);
$this->paymentData = json_decode($paymentData, true) ?? [];
$this->type = QUI\ERP\Constants::TYPE_INVOICE_TEMPORARY;
switch ((int)$data['type']) {
case QUI\ERP\Constants::TYPE_INVOICE:
case QUI\ERP\Constants::TYPE_INVOICE_TEMPORARY:
case QUI\ERP\Constants::TYPE_INVOICE_CREDIT_NOTE:
case QUI\ERP\Constants::TYPE_INVOICE_REVERSAL:
$this->type = (int)$data['type'];
break;
}
// database attributes
$data['time_for_payment'] = (int)$data['time_for_payment'];

Henning Leutz
committed
if ($data['time_for_payment'] < 0) {
$data['time_for_payment'] = 0;
}
if ($this->getCustomer() === null && !empty($data['customer_data'])) {
$customer = json_decode($data['customer_data'], true);
if (!empty($customer['uuid'])) {
$this->setAttribute('customer_id', $customer['uuid']);
} elseif (!empty($customer['id'])) {
$this->setAttribute('customer_id', $customer['id']);
}
}
if ($this->getCustomer() === null) {
$this->setAttribute('invoice_address', false);
$this->setAttribute('customer_id', false);
} else {
$isBrutto = QUI\ERP\Utils\User::getBruttoNettoUserStatus($this->getCustomer());
if (QUI\ERP\Utils\User::IS_NETTO_USER === $isBrutto) {
$this->setAttribute('isbrutto', 0);
} else {
$this->setAttribute('isbrutto', 1);
}
$this->Articles->setCurrency($this->getCurrency());
// consider contact person in address
if (!empty($this->getAttribute('invoice_address'))) {
$invoiceAddress = $this->getAttribute('invoice_address');
$invoiceAddress = json_decode($invoiceAddress, true);
if (!empty($this->getAttribute('contact_person'))) {
$invoiceAddress['contactPerson'] = $this->getAttribute('contact_person');
}
if (!empty($invoiceAddress['contactEmail'])) {
$this->setAttribute('contactEmail', $invoiceAddress['contactEmail']);
}
$this->setAttribute('invoice_address', json_encode($invoiceAddress));
$this->shippingId = (int)$data['shipping_id'];
}
// accounting currency, if exists
$accountingCurrencyData = $this->getCustomDataEntry('accountingCurrencyData');
try {
if ($accountingCurrencyData) {
$this->Articles->setExchangeCurrency(
new QUI\ERP\Currency\Currency(
$accountingCurrencyData['accountingCurrency']
)
);
$this->Articles->setExchangeRate($accountingCurrencyData['rate']);
} elseif (QUI\ERP\Currency\Conf::accountingCurrencyEnabled()) {
$this->Articles->setExchangeCurrency(
QUI\ERP\Currency\Conf::getAccountingCurrency()
);
}
protected function databaseTable(): string
{
return Handler::getInstance()->temporaryInvoiceTable();
}
//region Getter
}
/**
* Returns only the integer id
*
* @return int
*/
public function getCleanId(): int
* @return string
* @deprecated use getUUID()
*/
public function getHash(): string
{
return $this->getUUID();
}
/**
* @return string
{
return $this->getAttribute('hash');
}
/**
* @return string
*/
public function getPrefixedNumber(): string
{
return $this->prefix . $this->id;
}

Henning Leutz
committed
/**
* Return the global process id
*
* @return string
*/
public function getGlobalProcessId(): string

Henning Leutz
committed
{
return $this->globalProcessId;
}
/**
* Return the invoice type
* - Handler::TYPE_INVOICE
* - Handler::TYPE_INVOICE_TEMPORARY
* - Handler::TYPE_INVOICE_CREDIT_NOTE
* - Handler::TYPE_INVOICE_REVERSAL
*
* @return int
*/
public function getInvoiceType(): int
{
return $this->type;
}
/**
* Return the customer
*
public function getCustomer(): ?QUI\ERP\User
$invoiceAddress = $this->getAttribute('invoice_address');
$User = QUI::getUsers()->get($this->getAttribute('customer_id'));
if (is_numeric($this->getAttribute('customer_id')) && $User->getUUID()) {
$this->setAttribute('customer_id', $User->getUUID());
}
QUI\System\Log::writeDebugException($Exception);
return null;
}
$userData = [
'country' => $User->getCountry(),
'username' => $User->getUsername(),
'firstname' => $User->getAttribute('firstname'),
'lastname' => $User->getAttribute('lastname'),
'lang' => $User->getLang(),
'email' => $User->getAttribute('email')
];

Henning Leutz
committed
try {
$Customer = QUI\ERP\User::convertUserToErpUser($User);
$userData = $Customer->getAttributes();
} catch (QUI\Exception $Exception) {
QUI\System\Log::writeDebugException($Exception);

Henning Leutz
committed
// address
if (is_string($invoiceAddress)) {
$invoiceAddress = json_decode($invoiceAddress, true);
}
if (!isset($userData['isCompany'])) {
$userData['isCompany'] = false;
if (isset($invoiceAddress['company'])) {
$userData['isCompany'] = true;
}
}
if (!empty($invoiceAddress)) {
if (!empty($this->getAttribute('contact_person'))) {
$invoiceAddress['contactPerson'] = $this->getAttribute('contact_person');
}
$userData['address'] = $invoiceAddress;
if (empty($userData['firstname']) && !empty($invoiceAddress['firstname'])) {
$userData['firstname'] = $invoiceAddress['firstname'];
}
if (empty($userData['lastname']) && !empty($invoiceAddress['lastname'])) {
$userData['lastname'] = $invoiceAddress['lastname'];
}
}
if (empty($userData['country'])) {
try {
$userData['country'] = QUI\ERP\Defaults::getCountry()->getCode();
$userData['country'] = '';
}
}
if (!empty($invoiceAddress['contactEmail'])) {
$userData['contactEmail'] = $invoiceAddress['contactEmail'];
}
try {
return new QUI\ERP\User($userData);
} catch (QUI\Exception $Exception) {
QUI\System\Log::writeException($Exception);
$this->setAttribute('customer_id', false);
$this->setAttribute('invoice_address', false);
return null;
}
/**
* @param string|QUI\ERP\Currency\Currency $currency
public function setCurrency(QUI\ERP\Currency\Currency | string $currency): void
try {
$currency = QUI\ERP\Currency\Handler::getCurrency($currency);
} catch (QUI\Exception $Exception) {
QUI\System\Log::addError($Exception->getMessage());
return;
}
}
if (!($currency instanceof QUI\ERP\Currency\Currency)) {
return;
}
$this->Currency = $currency;
$this->setAttribute('currency_data', $currency->toArray());
$this->Articles->setCurrency($this->Currency);
$this->Currency = $this->getCurrency();
/**
* Return the invoice currency
*
* @return QUI\ERP\Currency\Currency
*
* @throws QUI\Exception
*/
public function getCurrency(): QUI\ERP\Currency\Currency
if ($this->Currency !== null) {
return $this->Currency;
}
$currency = $this->getAttribute('currency_data');
if (!$currency) {
return QUI\ERP\Defaults::getCurrency();
}
if (is_string($currency)) {
$currency = json_decode($currency, true);
}
if (!$currency || !isset($currency['code'])) {
return QUI\ERP\Defaults::getCurrency();
}
$Currency = QUI\ERP\Currency\Handler::getCurrency($currency['code']);
if (isset($currency['rate'])) {
$Currency->setExchangeRate($currency['rate']);
}
$this->Currency = $Currency;
return $Currency;
/**
* Return the editor user
*
* @return null|QUI\Interfaces\Users\User
*/
public function getEditor(): ?QUI\Interfaces\Users\User
{
$Employees = QUI\ERP\Employee\Employees::getInstance();
if ($this->getAttribute('editor_id')) {
try {
return QUI::getUsers()->get($this->getAttribute('editor_id'));
} catch (QUI\Exception) {
}
}
// use default advisor as editor
if ($Employees->getDefaultAdvisor()) {
return $Employees->getDefaultAdvisor();
}
return null;
}
/**
* Return the ordered by user
*
* @return QUI\ERP\User|null
public function getOrderedByUser(): ?QUI\ERP\User
{
if ($this->getAttribute('ordered_by')) {
try {
$User = QUI::getUsers()->get($this->getAttribute('ordered_by'));
return QUI\ERP\User::convertUserToErpUser($User);
}
}
return null;
}
/**
* Return all fields, attributes which are still missing to post the invoice
*
* @return array
* @throws QUI\Exception
* @throws ExceptionStack
public function getMissingAttributes(): array
{
return Utils\Invoice::getMissingAttributes($this);
}
*
* @return InvoiceView
public function getView(): InvoiceView
{
return new InvoiceView($this);
}

Henning Leutz
committed
/**
* @return Payment

Henning Leutz
committed
*/
public function getPayment(): Payment

Henning Leutz
committed
{
$paymentMethod = $this->getAttribute('payment_method');
if (empty($paymentMethod)) {
return new Payment([]);
}

Henning Leutz
committed
try {
$Payment = QUI\ERP\Accounting\Payments\Payments::getInstance()->getPayment($paymentMethod);

Henning Leutz
committed
return new Payment($this->parsePaymentForPaymentData($Payment));

Henning Leutz
committed
} catch (QUI\Exception $Exception) {
QUI\System\Log::writeDebugException($Exception);
}
return new Payment([]);

Henning Leutz
committed
}
/**
* @return array
* @throws QUI\ERP\Exception|QUI\Exception
public function getPaidStatusInformation(): array
{
QUI\ERP\Accounting\Calc::calculatePayments($this);
return [
'paidData' => $this->getAttribute('paid_data'),
'paidDate' => $this->getAttribute('paid_date'),
'paid' => $this->getAttribute('paid'),
'toPay' => $this->getAttribute('toPay')
/**
* Return the is paid status
*
* @return bool
*/
{
try {
if ($this->getAttribute('toPay') === false) {
QUI\ERP\Accounting\Calc::calculatePayments($this);
}
} catch (QUI\Exception $Exception) {
QUI\System\Log::writeException($Exception);
}
return $this->getAttribute('toPay') <= 0;
}
/**
* can a refund be made?
*
* @return bool
*/
public function hasRefund(): bool
//endregion
/**
* Change the invoice type
*
* The following types can be used:
* - Handler::TYPE_INVOICE_TEMPORARY:
* - Handler::TYPE_INVOICE_CREDIT_NOTE:
* - Handler::TYPE_INVOICE_REVERSAL:
*
* @param QUI\Interfaces\Users\User|null $PermissionUser - optional
public function setInvoiceType(int | string $type, null | QUI\Interfaces\Users\User $PermissionUser = null): void
{
QUI\Permissions\Permission::checkPermission(
'quiqqer.invoice.temporary.edit',
$PermissionUser
);
$type = (int)$type;
switch ($type) {
case QUI\ERP\Constants::TYPE_INVOICE_TEMPORARY:
case QUI\ERP\Constants::TYPE_INVOICE_CREDIT_NOTE:
case QUI\ERP\Constants::TYPE_INVOICE_REVERSAL:
break;
default:
return;
}
$this->type = $type;
$typeTitle = QUI::getLocale()->get('quiqqer/invoice', 'invoice.type.' . $type);
$this->addHistory(
QUI::getLocale()->get(
'quiqqer/invoice',
'history.message.temporaryInvoice.type.changed',
'type' => $type,
* Alias for update
* (Save the current temporary invoice data to the database)
* @param QUI\Interfaces\Users\User|null $PermissionUser
* @throws QUI\Permissions\Exception
public function save(null | QUI\Interfaces\Users\User $PermissionUser = null): void
{
$this->update($PermissionUser);
}
/**
* Save the current temporary invoice data to the database
*
* @param QUI\Interfaces\Users\User|null $PermissionUser
*
* @throws QUI\Permissions\Exception
* @throws QUI\Lock\Exception
* @throws QUI\Exception
*/
public function update(null | QUI\Interfaces\Users\User $PermissionUser = null): void
if ($PermissionUser === null) {
$PermissionUser = QUI::getUserBySession();
}
//if (!$this->getCustomer() || $PermissionUser->getId() !== $this->getCustomer()->getId()) {
QUI\Permissions\Permission::checkPermission(
'quiqqer.invoice.temporary.edit',
$PermissionUser
);
//}
$this->checkLocked();
QUI::getEvents()->fireEvent(
'quiqqerInvoiceTemporaryInvoiceSaveBegin',
$isBrutto = QUI\ERP\Defaults::getBruttoNettoStatus();

Henning Leutz
committed
if ($this->getCustomer()) {
$isBrutto = QUI\ERP\Utils\User::getBruttoNettoUserStatus($this->getCustomer());
if ($this->getAttribute('project_name')) {
$projectName = $this->getAttribute('project_name');
}
if ($this->getAttribute('time_for_payment') || $this->getAttribute('time_for_payment') === 0) {
$timeForPayment = (int)$this->getAttribute('time_for_payment');
} else {
$timeForPayment = Settings::getInstance()->get('invoice', 'time_for_payment');
if ($timeForPayment < 0) {
$timeForPayment = 0;
}
if (
$this->getAttribute('date')
&& Orthos::checkMySqlDatetimeSyntax($this->getAttribute('date'))
) {
$date = $this->getAttribute('date');
}
// address
$contactEmail = $this->getAttribute('contactEmail') ?: false;
$Customer = QUI::getUsers()->get($this->getAttribute('customer_id'));
$invoiceAddressData = $this->getAttribute('invoice_address');
if (!is_array($invoiceAddressData)) {
$invoiceAddressData = json_decode($invoiceAddressData, true);
}
if ($invoiceAddressData) {
$Address->setAttributes($invoiceAddressData);
}
$Address->setAttribute('contactEmail', $contactEmail);
$invoiceAddress = $Address->toJSON();
$invoiceAddress = $this->getAttribute('invoice_address');
$invoiceAddressCheck = json_decode($invoiceAddress, true);
$invoiceAddressCheck['contactEmail'] = $contactEmail;
$invoiceAddress = json_encode($invoiceAddressCheck);
} elseif (is_array($invoiceAddress)) {
$invoiceAddress['contactEmail'] = $contactEmail;
$invoiceAddress = json_encode($invoiceAddress);
$Address = new QUI\ERP\Address(
json_decode($invoiceAddress, true)
);
$this->Articles->calc();
$listCalculations = $this->Articles->getCalculations();
$paymentMethod = '';
try {
if ($this->getAttribute('payment_method')) {
$Payments = QUI\ERP\Accounting\Payments\Payments::getInstance();
$Payment = $Payments->getPayment($this->getAttribute('payment_method'));
$paymentMethod = $Payment->getId();
}
} catch (QUI\ERP\Accounting\Payments\Exception $Exception) {
QUI\System\Log::addNotice($Exception->getMessage());
}
//invoice text
$invoiceText = $this->getAttribute('additional_invoice_text');
if ($invoiceText === false) {
$invoiceText = QUI::getLocale()->get('quiqqer/invoice', 'additional.invoice.text');
}
if ($listCalculations['isEuVat'] && empty($listCalculations['vatArray'])) {
$Locale = $this->getCustomer()->getLocale();
$extraText .= $Locale->get('quiqqer/tax', 'message.EUVAT.addition', [
'user' => $this->getCustomer()->getInvoiceName(),
'vatId' => $this->getCustomer()->getAttribute('quiqqer.erp.euVatId')
]);
$invoiceTextClean = trim(strip_tags($invoiceText));
$invoiceTextClean = str_replace("\n", ' ', $invoiceTextClean);
$invoiceTextClean = html_entity_decode($invoiceTextClean);
$invoiceTextClean = preg_replace('#( ){2,}#', "$1", $invoiceTextClean);
$extraTextClean = trim(strip_tags($extraText));
$extraTextClean = str_replace("\n", ' ', $extraTextClean);
$extraTextClean = html_entity_decode($extraTextClean);
$extraTextClean = preg_replace('#( ){2,}#', "$1", $extraTextClean);
if (mb_strpos($invoiceTextClean, $extraTextClean) === false) {
$Editor = $this->getEditor();
$editorId = 0;
$editorName = '';
// use default advisor as editor
if ($Editor) {
$editorName = $Editor->getName();
}
// Ordered By
$OrderedBy = $this->getOrderedByUser();
$orderedBy = $this->getAttribute('customer_id');
// use default advisor as editor
$orderedByName = $OrderedBy->getInvoiceName();
$User = QUI::getUsers()->get($orderedBy);
$User = QUI\ERP\User::convertUserToErpUser($User);
$orderedByName = $User->getInvoiceName();
$shippingId = null;
$shippingData = null;
if ($this->getShipping()) {
$shippingId = $this->getShipping()->getId();
$shippingData = $this->getShipping()->toJSON();
}
$deliveryAddress = '';
$deliveryAddressId = null;
$DeliveryAddress = $this->getDeliveryAddress();
// Save delivery address separately only if it differs from invoice address
if ($DeliveryAddress && !$DeliveryAddress->equals($Address)) {
$deliveryAddress = $DeliveryAddress->toJSON();
$deliveryAddressId = $DeliveryAddress->getUUID();
if (empty($deliveryAddressId)) {
$deliveryAddressId = null;
}
}
// processing status
$processingStatus = null;
if (is_numeric($this->getAttribute('processing_status'))) {
$processingStatus = (int)$this->getAttribute('processing_status');
try {
ProcessingStatus\Handler::getInstance()->getProcessingStatus($processingStatus);
} catch (ProcessingStatus\Exception) {
$processingStatus = null;
}
}
// contact person
$contactPerson = '';
if ($this->getAttribute('contact_person') && $this->getAttribute('contact_person') !== '') {
$contactPerson = Orthos::clear($this->getAttribute('contact_person'));
}
if (
$this->getAttribute('order_date')
&& Orthos::checkMySqlDatetimeSyntax($this->getAttribute('order_date'))
) {
$orderDate = $this->getAttribute('order_date');
}

Patrick Müller
committed
// Determine payment status by order (if an order is linked to the invoice)
$paidStatus = QUI\ERP\Constants::PAYMENT_STATUS_OPEN;
$paidDate = null;
$paidData = '';
$orderId = $this->getAttribute('order_id') ?: null;

Patrick Müller
committed
if (!empty($orderId)) {
$Orders = OrderHandler::getInstance();
try {
$Order = $Orders->getOrderById($orderId);

Patrick Müller
committed
$orderPaidStatusData = $Order->getPaidStatusInformation();
$paidStatus = $Order->getAttribute('paid_status');
$paidDate = $orderPaidStatusData['paidDate'];
$paidData = json_encode($orderPaidStatusData['paidData']);

Patrick Müller
committed
} catch (\Exception $Exception) {
QUI\System\Log::writeDebugException($Exception);
}
}
QUI::getEvents()->fireEvent(
'quiqqerInvoiceTemporaryInvoiceSave',