src/Invoice/Completor.php
protected function completeVatType()
    {
        // If shop specific code or an event handler has already set the vat
        // type, we don't change it.
        if (empty($this->invoice[Tag::Customer][Tag::Invoice][Tag::VatType])) {
        	// @todo: if we only have one possible vattype, should we use that, or
	        //   should we perform all checks to look for contradictory evidence?
            $vatTypeInfo = $this->getInvoiceLinesVatTypeInfo();
            $message = '';
            $code = 0;
            if (count($vatTypeInfo['intersection']) === 0) {
                // No single vat type is correct for all lines, use the
                // union to guess what went wrong.
                if (count($vatTypeInfo['union']) === 0) {
                    // None of the vat rates of the invoice lines could be
                    // matched with any vat rate for any possible vat type.
                    // Possible causes:
                    // - Invoice has no vat but cannot be a reversed vat invoice
                    //   nor outside the EU, nor are vat free products or
                    //   services sold. Message: 'Check "about your shop"
                    //   settings or the vat rates assigned to your products.'
                    // - Vat rates are incorrect for given country (and date).
                    //   Message: 'Did you configure the correct settings for
                    //   country ..?' or 'Were there recent changes in tax
                    //   rates?'.
                    // - Vat rates are for foreign VAT but the shop does
                    //   not sell foreign VAT products. Message'Check "about
                    //   your shop" settings'.
                    // - Vat rates are Dutch vat rates but shop only sells
                    //   foreign VAT products and client is in the EU. Message:
                    //   'Check the vat rates assigned to your products.'.
                    $message = 'message_warning_no_vattype_at_all';
                    $code = 804;
                } elseif (count($vatTypeInfo['union']) === 1) {
                    // One or more lines could be matched with exactly 1 vat
                    // type, but not all lines.
                    // Possible causes:
                    // - Non matching lines have no vat. Message: 'Manual line
                    //   entered without vat' or 'Check vat settings on those
                    //   products.'.
                    // - Non matching lines have vat. Message: 'Manual line
                    //   entered with incorrect vat' or 'Check vat settings on
                    //   those products.'.
                    $this->invoice[Tag::Customer][Tag::Invoice][Tag::VatType] = reset($vatTypeInfo['union']);
                    $message = 'message_warning_no_vattype_incorrect_lines';
                    $code = 812;
                } else {
                    // Separate lines could be matched with some of the possible
                    // vat types, but not all with the same vat type.
                    // Possible causes:
                    // - Mix of foreign VAT rates and other goods or services.
                    //   Message: 'Split invoice.'.
                    // - Some lines have no vat but no vat free goods or
                    //   services are sold and thus this could be a reversed vat
                    //   (company in EU) or vat free invoice (outside EU).
                    //   Message: check vat settings.
                    // - Mix of margin scheme and normal vat: this can be solved
                    //   by making it a margin scheme invoice and adding
                    //   costprice = 0 to all normal lines.
                    if (in_array(Api::VatType_MarginScheme, $vatTypeInfo['union'])) {
                        $this->invoice[Tag::Customer][Tag::Invoice][Tag::VatType] = Api::VatType_MarginScheme;
                    } else {
                        // Take the first vat type as a guess but add a warning.
                        $this->invoice[Tag::Customer][Tag::Invoice][Tag::VatType] = reset($vatTypeInfo['union']);
                        $message = 'message_warning_no_vattype_must_split';
                        $code = 806;
                    }
                }
            } elseif (count($vatTypeInfo['intersection']) === 1) {
                // Exactly 1 vat type was found to be possible for all lines:
                // use that one as the vat type for the invoice.
                $this->invoice[Tag::Customer][Tag::Invoice][Tag::VatType] = reset($vatTypeInfo['intersection']);
            } else {
                // Multiple vat types were found to be possible for all lines:
                // Take one and add a warning.
                // Possible causes:
                // - Client country has same vat rates as the Netherlands and
                //   shop sells products at foreign vat rates but also other
                //   products or services. Solvable by correct shop settings.
                // - Invoice has no vat and the client is outside the EU and it
                //   is unknown whether the invoice lines contain services or
                //   goods. Perhaps solvable by correct shop settings.
                // - Margin invoice: all lines that have a costprice will
                //   probably also satisfy the normal vat. This is solvable by
                //   making it a margin scheme invoice and adding costprice = 0
                //   to all normal lines.
                if (in_array(Api::VatType_MarginScheme, $vatTypeInfo['union'])) {
                    $this->invoice[Tag::Customer][Tag::Invoice][Tag::VatType] = Api::VatType_MarginScheme;
                } else {
                    // Take the first vat type as a guess but add a warning.
                    $this->invoice[Tag::Customer][Tag::Invoice][Tag::VatType] = reset($vatTypeInfo['intersection']);
                    $message = 'message_warning_no_vattype_multiple_possible';
                    $code = 811;
                }
            }

            if (!empty($message)) {
                // Make the invoice a concept, so it can be changed in Acumulus
                // and add message and meta info.
                $startSentence = count($vatTypeInfo['intersection']) === 0
                    ? 'message_warning_no_vattype'
                    : 'message_warning_multiple_vattypes';
                $this->changeInvoiceToConcept($message, $code, $this->t($startSentence));
            }
            $this->invoice[Tag::Customer][Tag::Invoice][Meta::VatTypesPossibleInvoice] = implode(',', $this->possibleVatTypes);
            $this->invoice[Tag::Customer][Tag::Invoice][Meta::VatTypesPossibleInvoiceLinesIntersection] = implode(',', $vatTypeInfo['intersection']);
            $this->invoice[Tag::Customer][Tag::Invoice][Meta::VatTypesPossibleInvoiceLinesUnion] = implode(',', $vatTypeInfo['union']);
        }
    }
protected function completeVatType()
    {
        // If shop specific code or an event handler has already set the vat
        // type, we don't change it.
        if (empty($this->invoice[Tag::Customer][Tag::Invoice][Tag::VatType])) {
        	// @todo: if we only have one possible vattype, should we use that, or
	        //   should we perform all checks to look for contradictory evidence?
            $vatTypeInfo = $this->getInvoiceLinesVatTypeInfo();
            $message = '';
            $code = 0;
            if (count($vatTypeInfo['intersection']) === 0) {
                // No single vat type is correct for all lines, use the
                // union to guess what went wrong.
                if (count($vatTypeInfo['union']) === 0) {
                    // None of the vat rates of the invoice lines could be
                    // matched with any vat rate for any possible vat type.
                    // Possible causes:
                    // - Invoice has no vat but cannot be a reversed vat invoice
                    //   nor outside the EU, nor are vat free products or
                    //   services sold. Message: 'Check "about your shop"
                    //   settings or the vat rates assigned to your products.'
                    // - Vat rates are incorrect for given country (and date).
                    //   Message: 'Did you configure the correct settings for
                    //   country ..?' or 'Were there recent changes in tax
                    //   rates?'.
                    // - Vat rates are for foreign VAT but the shop does
                    //   not sell foreign VAT products. Message'Check "about
                    //   your shop" settings'.
                    // - Vat rates are Dutch vat rates but shop only sells
                    //   foreign VAT products and client is in the EU. Message:
                    //   'Check the vat rates assigned to your products.'.
                    $message = 'message_warning_no_vattype_at_all';
                    $code = 804;
                } elseif (count($vatTypeInfo['union']) === 1) {
                    // One or more lines could be matched with exactly 1 vat
                    // type, but not all lines.
                    // Possible causes:
                    // - Non matching lines have no vat. Message: 'Manual line
                    //   entered without vat' or 'Check vat settings on those
                    //   products.'.
                    // - Non matching lines have vat. Message: 'Manual line
                    //   entered with incorrect vat' or 'Check vat settings on
                    //   those products.'.
                    $this->invoice[Tag::Customer][Tag::Invoice][Tag::VatType] = reset($vatTypeInfo['union']);
                    $message = 'message_warning_no_vattype_incorrect_lines';
                    $code = 812;
                } else {
                    // Separate lines could be matched with some of the possible
                    // vat types, but not all with the same vat type.
                    // Possible causes:
                    // - Mix of foreign VAT rates and other goods or services.
                    //   Message: 'Split invoice.'.
                    // - Some lines have no vat but no vat free goods or
                    //   services are sold and thus this could be a reversed vat
                    //   (company in EU) or vat free invoice (outside EU).
                    //   Message: check vat settings.
                    // - Mix of margin scheme and normal vat: this can be solved
                    //   by making it a margin scheme invoice and adding
                    //   costprice = 0 to all normal lines.
                    if (in_array(Api::VatType_MarginScheme, $vatTypeInfo['union'])) {
                        $this->invoice[Tag::Customer][Tag::Invoice][Tag::VatType] = Api::VatType_MarginScheme;
                    } else {
                        // Take the first vat type as a guess but add a warning.
                        $this->invoice[Tag::Customer][Tag::Invoice][Tag::VatType] = reset($vatTypeInfo['union']);
                        $message = 'message_warning_no_vattype_must_split';
                        $code = 806;
                    }
                }
            } elseif (count($vatTypeInfo['intersection']) === 1) {
                // Exactly 1 vat type was found to be possible for all lines:
                // use that one as the vat type for the invoice.
                $this->invoice[Tag::Customer][Tag::Invoice][Tag::VatType] = reset($vatTypeInfo['intersection']);
            } else {
                // Multiple vat types were found to be possible for all lines:
                // Take one and add a warning.
                // Possible causes:
                // - Client country has same vat rates as the Netherlands and
                //   shop sells products at foreign vat rates but also other
                //   products or services. Solvable by correct shop settings.
                // - Invoice has no vat and the client is outside the EU and it
                //   is unknown whether the invoice lines contain services or
                //   goods. Perhaps solvable by correct shop settings.
                // - Margin invoice: all lines that have a costprice will
                //   probably also satisfy the normal vat. This is solvable by
                //   making it a margin scheme invoice and adding costprice = 0
                //   to all normal lines.
                if (in_array(Api::VatType_MarginScheme, $vatTypeInfo['union'])) {
                    $this->invoice[Tag::Customer][Tag::Invoice][Tag::VatType] = Api::VatType_MarginScheme;
                } else {
                    // Take the first vat type as a guess but add a warning.
                    $this->invoice[Tag::Customer][Tag::Invoice][Tag::VatType] = reset($vatTypeInfo['intersection']);
                    $message = 'message_warning_no_vattype_multiple_possible';
                    $code = 811;
                }
            }

            if (!empty($message)) {
                // Make the invoice a concept, so it can be changed in Acumulus
                // and add message and meta info.
                $startSentence = count($vatTypeInfo['intersection']) === 0
                    ? 'message_warning_no_vattype'
                    : 'message_warning_multiple_vattypes';
                $this->changeInvoiceToConcept($message, $code, $this->t($startSentence));
            }
            $this->invoice[Tag::Customer][Tag::Invoice][Meta::VatTypesPossibleInvoice] = implode(',', $this->possibleVatTypes);
            $this->invoice[Tag::Customer][Tag::Invoice][Meta::VatTypesPossibleInvoiceLinesIntersection] = implode(',', $vatTypeInfo['intersection']);
            $this->invoice[Tag::Customer][Tag::Invoice][Meta::VatTypesPossibleInvoiceLinesUnion] = implode(',', $vatTypeInfo['union']);
        }
    }
protected function completeVatType ( ) { // If shop specific code or an event handler has already set the vat // type, we don't change it. if ( empty ( $ this -> invoice [ Tag :: Customer ] [ Tag :: Invoice ] [ Tag :: VatType ] ) ) { // @todo: if we only have one possible vattype, should we use that, or //   should we perform all checks to look for contradictory evidence? $ vatTypeInfo = $ this -> getInvoiceLinesVatTypeInfo ( ) ; $ message = '' ; $ code = 0 ; if ( count ( $ vatTypeInfo [ 'intersection' ] ) === 0 ) { // No single vat type is correct for all lines, use the // union to guess what went wrong. if ( count ( $ vatTypeInfo [ 'union' ] ) === 0 ) { // None of the vat rates of the invoice lines could be // matched with any vat rate for any possible vat type. // Possible causes: // - Invoice has no vat but cannot be a reversed vat invoice //   nor outside the EU, nor are vat free products or //   services sold. Message: 'Check "about your shop" //   settings or the vat rates assigned to your products.' // - Vat rates are incorrect for given country (and date). //   Message: 'Did you configure the correct settings for //   country ..?' or 'Were there recent changes in tax //   rates?'. // - Vat rates are for foreign VAT but the shop does //   not sell foreign VAT products. Message'Check "about //   your shop" settings'. // - Vat rates are Dutch vat rates but shop only sells //   foreign VAT products and client is in the EU. Message: //   'Check the vat rates assigned to your products.'. $ message = 'message_warning_no_vattype_at_all' ; $ code = 804 ; } elseif ( count ( $ vatTypeInfo [ 'union' ] ) === 1 ) { // One or more lines could be matched with exactly 1 vat // type, but not all lines. // Possible causes: // - Non matching lines have no vat. Message: 'Manual line //   entered without vat' or 'Check vat settings on those //   products.'. // - Non matching lines have vat. Message: 'Manual line //   entered with incorrect vat' or 'Check vat settings on //   those products.'. $ this -> invoice [ Tag :: Customer ] [ Tag :: Invoice ] [ Tag :: VatType ] = reset ( $ vatTypeInfo [ 'union' ] ) ; $ message = 'message_warning_no_vattype_incorrect_lines' ; $ code = 812 ; } else { // Separate lines could be matched with some of the possible // vat types, but not all with the same vat type. // Possible causes: // - Mix of foreign VAT rates and other goods or services. //   Message: 'Split invoice.'. // - Some lines have no vat but no vat free goods or //   services are sold and thus this could be a reversed vat //   (company in EU) or vat free invoice (outside EU). //   Message: check vat settings. // - Mix of margin scheme and normal vat: this can be solved //   by making it a margin scheme invoice and adding //   costprice = 0 to all normal lines. if ( in_array ( Api :: VatType_MarginScheme , $ vatTypeInfo [ 'union' ] ) ) { $ this -> invoice [ Tag :: Customer ] [ Tag :: Invoice ] [ Tag :: VatType ] = Api :: VatType_MarginScheme ; } else { // Take the first vat type as a guess but add a warning. $ this -> invoice [ Tag :: Customer ] [ Tag :: Invoice ] [ Tag :: VatType ] = reset ( $ vatTypeInfo [ 'union' ] ) ; $ message = 'message_warning_no_vattype_must_split' ; $ code = 806 ; } } } elseif ( count ( $ vatTypeInfo [ 'intersection' ] ) === 1 ) { // Exactly 1 vat type was found to be possible for all lines: // use that one as the vat type for the invoice. $ this -> invoice [ Tag :: Customer ] [ Tag :: Invoice ] [ Tag :: VatType ] = reset ( $ vatTypeInfo [ 'intersection' ] ) ; } else { // Multiple vat types were found to be possible for all lines: // Take one and add a warning. // Possible causes: // - Client country has same vat rates as the Netherlands and //   shop sells products at foreign vat rates but also other //   products or services. Solvable by correct shop settings. // - Invoice has no vat and the client is outside the EU and it //   is unknown whether the invoice lines contain services or //   goods. Perhaps solvable by correct shop settings. // - Margin invoice: all lines that have a costprice will //   probably also satisfy the normal vat. This is solvable by //   making it a margin scheme invoice and adding costprice = 0 //   to all normal lines. if ( in_array ( Api :: VatType_MarginScheme , $ vatTypeInfo [ 'union' ] ) ) { $ this -> invoice [ Tag :: Customer ] [ Tag :: Invoice ] [ Tag :: VatType ] = Api :: VatType_MarginScheme ; } else { // Take the first vat type as a guess but add a warning. $ this -> invoice [ Tag :: Customer ] [ Tag :: Invoice ] [ Tag :: VatType ] = reset ( $ vatTypeInfo [ 'intersection' ] ) ; $ message = 'message_warning_no_vattype_multiple_possible' ; $ code = 811 ; } } if ( ! empty ( $ message ) ) { // Make the invoice a concept, so it can be changed in Acumulus // and add message and meta info. $ startSentence = count ( $ vatTypeInfo [ 'intersection' ] ) === 0 ? 'message_warning_no_vattype' : 'message_warning_multiple_vattypes' ; $ this -> changeInvoiceToConcept ( $ message , $ code , $ this -> t ( $ startSentence ) ) ; } $ this -> invoice [ Tag :: Customer ] [ Tag :: Invoice ] [ Meta :: VatTypesPossibleInvoice ] = implode ( ',' , $ this -> possibleVatTypes ) ; $ this -> invoice [ Tag :: Customer ] [ Tag :: Invoice ] [ Meta :: VatTypesPossibleInvoiceLinesIntersection ] = implode ( ',' , $ vatTypeInfo [ 'intersection' ] ) ; $ this -> invoice [ Tag :: Customer ] [ Tag :: Invoice ] [ Meta :: VatTypesPossibleInvoiceLinesUnion ] = implode ( ',' , $ vatTypeInfo [ 'union' ] ) ; } }
Determines the vattype of the invoice.

This method (and class) is aware of:
- The setting foreignVat.
- The country of the client.
- Whether the client is a company.
- The actual VAT rates on the day of the order.
- Whether there are margin products in the order.

So to start with, any list of (possible) vat types is based on the above.
Furthermore this method and {@see getInvoiceLinesVatTypeInfo()} are aware
of:
- The fact that orders do not have to be split over different vat types,
but that invoices should be split if both national and foreign VAT
rates appear on the order.
- The vat class meta data per line and which classes denote foreign vat.
This info is used to distinguish between NL and foreign vat for EU
countries that have VAT rates in common with NL and the settings
indicate that this shop sells products in both vat type categories.

If multiple vat types are possible, the invoice is sent as concept, so
that it may be corrected in Acumulus.
Determines the vattype of the invoice .