src/Shop/InvoiceManager.php
protected function lockAndSend(array $invoice, Source $invoiceSource, Result $result)
    {
        $doLock = !$this->isTestMode() && in_array($result->getSendStatus(), array(Result::Send_New, Result::Send_LockExpired));

        if ($doLock) {
            // Check if we may expect an expired lock and, if so, remove it.
            if ($result->getSendStatus() === Result::Send_LockExpired) {
                $lockStatus = $this->getAcumulusEntryManager()->deleteLock($invoiceSource);
                if ($lockStatus === AcumulusEntry::Lock_BecameRealEntry) {
                    // Bail out: invoice already sent after all.
                    return $result->setSendStatus(Result::NotSent_AlreadySent);
                }
            }

            // Acquire lock.
            if (!$this->getAcumulusEntryManager()->lockForSending($invoiceSource)) {
                // Bail out: Lock not acquired.
                return $result->setSendStatus(Result::NotSent_LockedForSending);
            }
        }

        try {
            $result = $this->doSend($invoice, $invoiceSource, $result);
        } catch (Exception $e) {
            $result->setException($e);
        }

        // When everything went well, the lock will have been replaced by a real
        // entry. So we only delete the lock in case of errors.
        if ($doLock && $result->hasError()) {
            // deleteLock() is expected to return AcumulusEntry::Lock_Deleted,
            // so we don't act on that return status. With any of the other
            // statuses it is unclear what happened and what the status will be
            // in Acumulus: tell user to check.
            if (($lockStatus = $this->getAcumulusEntryManager()->deleteLock($invoiceSource)) !== AcumulusEntry::Lock_Deleted) {
                $code = $lockStatus === AcumulusEntry::Lock_NoLongerExists ? 903 : 904;
                $result->addWarning($code, '',
                    sprintf($this->t('message_warning_delete_lock_failed'), $this->t($invoiceSource->getType())));
            }
        }

        // Trigger the InvoiceSent event.
        $this->triggerInvoiceSendAfter($invoice, $invoiceSource, $result);

        // Send a mail if there are messages.
        $this->mailInvoiceAddResult($result, $invoiceSource);

        return $result;
    }
protected function lockAndSend(array $invoice, Source $invoiceSource, Result $result)
    {
        $doLock = !$this->isTestMode() && in_array($result->getSendStatus(), array(Result::Send_New, Result::Send_LockExpired));

        if ($doLock) {
            // Check if we may expect an expired lock and, if so, remove it.
            if ($result->getSendStatus() === Result::Send_LockExpired) {
                $lockStatus = $this->getAcumulusEntryManager()->deleteLock($invoiceSource);
                if ($lockStatus === AcumulusEntry::Lock_BecameRealEntry) {
                    // Bail out: invoice already sent after all.
                    return $result->setSendStatus(Result::NotSent_AlreadySent);
                }
            }

            // Acquire lock.
            if (!$this->getAcumulusEntryManager()->lockForSending($invoiceSource)) {
                // Bail out: Lock not acquired.
                return $result->setSendStatus(Result::NotSent_LockedForSending);
            }
        }

        try {
            $result = $this->doSend($invoice, $invoiceSource, $result);
        } catch (Exception $e) {
            $result->setException($e);
        }

        // When everything went well, the lock will have been replaced by a real
        // entry. So we only delete the lock in case of errors.
        if ($doLock && $result->hasError()) {
            // deleteLock() is expected to return AcumulusEntry::Lock_Deleted,
            // so we don't act on that return status. With any of the other
            // statuses it is unclear what happened and what the status will be
            // in Acumulus: tell user to check.
            if (($lockStatus = $this->getAcumulusEntryManager()->deleteLock($invoiceSource)) !== AcumulusEntry::Lock_Deleted) {
                $code = $lockStatus === AcumulusEntry::Lock_NoLongerExists ? 903 : 904;
                $result->addWarning($code, '',
                    sprintf($this->t('message_warning_delete_lock_failed'), $this->t($invoiceSource->getType())));
            }
        }

        // Trigger the InvoiceSent event.
        $this->triggerInvoiceSendAfter($invoice, $invoiceSource, $result);

        // Send a mail if there are messages.
        $this->mailInvoiceAddResult($result, $invoiceSource);

        return $result;
    }
protected function lockAndSend ( array $ invoice , Source $ invoiceSource , Result $ result ) { $ doLock = ! $ this -> isTestMode ( ) && in_array ( $ result -> getSendStatus ( ) , array ( Result :: Send_New , Result :: Send_LockExpired ) ) ; if ( $ doLock ) { // Check if we may expect an expired lock and, if so, remove it. if ( $ result -> getSendStatus ( ) === Result :: Send_LockExpired ) { $ lockStatus = $ this -> getAcumulusEntryManager ( ) -> deleteLock ( $ invoiceSource ) ; if ( $ lockStatus === AcumulusEntry :: Lock_BecameRealEntry ) { // Bail out: invoice already sent after all. return $ result -> setSendStatus ( Result :: NotSent_AlreadySent ) ; } } // Acquire lock. if ( ! $ this -> getAcumulusEntryManager ( ) -> lockForSending ( $ invoiceSource ) ) { // Bail out: Lock not acquired. return $ result -> setSendStatus ( Result :: NotSent_LockedForSending ) ; } } try { $ result = $ this -> doSend ( $ invoice , $ invoiceSource , $ result ) ; } catch ( Exception $ e ) { $ result -> setException ( $ e ) ; } // When everything went well, the lock will have been replaced by a real // entry. So we only delete the lock in case of errors. if ( $ doLock && $ result -> hasError ( ) ) { // deleteLock() is expected to return AcumulusEntry::Lock_Deleted, // so we don't act on that return status. With any of the other // statuses it is unclear what happened and what the status will be // in Acumulus: tell user to check. if ( ( $ lockStatus = $ this -> getAcumulusEntryManager ( ) -> deleteLock ( $ invoiceSource ) ) !== AcumulusEntry :: Lock_Deleted ) { $ code = $ lockStatus === AcumulusEntry :: Lock_NoLongerExists ? 903 : 904 ; $ result -> addWarning ( $ code , '' , sprintf ( $ this -> t ( 'message_warning_delete_lock_failed' ) , $ this -> t ( $ invoiceSource -> getType ( ) ) ) ) ; } } // Trigger the InvoiceSent event. $ this -> triggerInvoiceSendAfter ( $ invoice , $ invoiceSource , $ result ) ; // Send a mail if there are messages. $ this -> mailInvoiceAddResult ( $ result , $ invoiceSource ) ; return $ result ; }
Locks, if needed, the invoice for sending and, if acquired, sends it.

NOTE: the mechanism used to lock and verify if we got the lock is not
atomic, nor fool proof for all possible situations. However, it is a
relatively easy to understand solution that will catch 99,9% of the
situations. If double sending still occurs, some warning mechanisms are
built in (were already built in) to delete one of the entries in Acumulus
and warn the user.

After sending the invoice:
- The invoice sent event gets triggered.
- A mail with the results may be sent.

@param \Siel\Acumulus\Invoice\Source $invoiceSource
@param array $invoice
@param \Siel\Acumulus\Invoice\Result $result

@return \Siel\Acumulus\Invoice\Result
The result structure of the invoice add API call merged with any local
messages.
Locks if needed the invoice for sending and if acquired sends it .