Questions and problems with Custom Invoicedesign

Hello,

after my post here got the not so satisfying answer that the service is only for Hostet customers, I started to port my design from version 4 to version 5.

I came across the following obstacles and hope you can help me:

  1. in version 4 you could simply drag and drop the customer number into the invoice fields, which made it appear in the $invoiceDetails variable.


    In version 5 it is no longer possible to get the customer number into the invoice details. Have I overlooked something here? I didn’t want to adjust the whole area manually.

  2. in version 4 I had generated the QR code as follows:

  {
    "style": "epcqr",
    "qr": "BCD\n001\n1\nSCT\nBICDDEF1XXX\nKontoinhaber\nIVIBAN123456485236587\nEUR$invoice.balanceAmount\n\n\n$invoice.invoiceNumber Kd-Nr: $client.idNumber\n",
    "fit": "100",
    "eccLevel": "M"
  },

In addition to the bank details, the amount and the invoice number, the customer number was also included here.
In version 5, the variable $sepa_qr_code is the simpler version for most. However, the reference to the customer number is missing here, which is included in the purpose of use for most banking apps. I need this customer number for automatic posting in accounting. Is there any way to include the customer number?

  1. with long invoices, e.g. from my time recording at the end of the month, I’m getting desperate with the invoice footer.
    In version 4 the footer was only on the last page, not quite nice but it didn’t create an ugly image like this when generating.

I think the question about the footer only on the last page has already been answered, then only the question: How do I get a fixed height and thus a corresponding page break for the [data-ref=“table”] so that my invoice design still fits the DIN5008B?

Thanks for the help.

Hi,

  1. It isn’t currently supported in v5.

2 and 3. @david can you please advise?

Hi,

  1. why have you not adopted such a practical feature? On most of the invoices I receive, the customer number can be found right there and not, as invoiceninja offers, at the customer address.

I’ll wait for an answer to 2 and 3 :slight_smile:

This is still possible in v5 just not with the drag and drop UI, you can use a custom design and replace the field stacks with the specific fields you’d like shown.

I’m aware of that, but it’s the stack function that makes it pretty fancy :wink:

Then I’ll probably have to tweak it again, shouldn’t cause too many problems.
However, the other two points are the bigger problem.

Any new information on Point 2 and 3?

Clarifying these two points is imperative.

@david just checking you saw this.

@ITSAW

In the next release - if set - the client.id_number will populate the purpose field of the QR Code.

in relation to footers, I would suggest using in your custom design a structure like this:

<table>

<thead>
</thead>

<tbody>
</tbody>

<tfoot>
</tfoot>

</table>

TS4 has a good example of this. It will allow you to manage the footers without the overlap issue you are seeing.

ts4.html

Thats nice, ty for the quick implementation.

The overlap footer is a little Problem of the

<table id="task-table" cellspacing="0" data-ref="table"></table>

There is no <tfoot> inside, so let me show my Code:

<div class="repeating-header" id="header"></div>
<div id="body">
   <div class="company-logo-wrapper">
      <img class="company-logo" src="$company.logo" alt="$company.name logo">
      <div style="float:right;">
         <div id="company-address"></div>
                  <p>&nbsp;</p>
                  <p>Telefon: $company.phone</p>
                  <p>&nbsp;</p>
                  <p>E-Mail: $company.email</p>
                  <p>$company.website</p>
      </div>
   </div>
   <div class="client-entity-wrapper">
      <div class="wrapper-left-side">
         <div class="text-with-client">
            <div id="vermerkzone">$company.name - $company.address1 - $company.postal_code $company.city</div>
            <div id="anschriftzone">
               <div id="client-details"></div>
                 <!--
                 <p>$client.name</p>
                 <p>$contact.full_name</p>
                 <p>$address1</p>
                 <p>$postal_city_state</p>
                 -->
               </div>
            </div>
         </div>
         <div class="wrapper-right-side">
            <table id="entity-details" cellspacing="0" dir="$dir"></table>
            <div class="sepa_qr">$sepa_qr_code</div>
         </div>
      </div>
   </div>
   <p class="entity-label">$entity_label</p>
   <table id="product-table" cellspacing="0" data-ref="table"></table>
   <table id="task-table" cellspacing="0" data-ref="table"></table>
   <table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
   <table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
   <div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
   <table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
   <div id="statement-payment-table-totals" data-ref="statement-totals"></div>
   <table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
   <div id="statement-aging-table-totals" data-ref="statement-totals"></div>
   <div id="table-totals" cellspacing="0"></div>
</div>
<div class="repeating-footer" id="footer">
   <p data-ref="total_table-footer">$entity_footer</p>
        <script>
            // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
            document.addEventListener('DOMContentLoaded', () => {
                let tables = [
                    'product-table', 'task-table', 'delivery-note-table',
                    'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
                    'statement-invoice-table-totals', 'statement-payment-table-totals', 'statement-aging-table',
                    'client-details','vendor-details'
                ];

                tables.forEach((tableIdentifier) => {
                    console.log(document.getElementById(tableIdentifier));

                    document.getElementById(tableIdentifier)?.childElementCount === 0
                        ? document.getElementById(tableIdentifier).style.setProperty('display', 'none', 'important')
                        : '';
                });
            });
// Hide duplicate customer/contact name
document.addEventListener('DOMContentLoaded', () => {
    document.querySelector('[data-ref="client_details-client.name"]').innerText == document.querySelector('[data-ref="client_details-contact.full_name"]').innerText
    ? document.querySelector('p[data-ref="client_details-contact.full_name"]').remove()
    : '';
});
        </script>
</div>

So tell me if I’m wrong

@ITSAW

You’ll want to update your design to make use of this structure, it will allow you to control the spacing.

Sorry if I’m being annoying, but that’s not true.

If I switch to a table, then I only have one more space below my footer. See here:

The table with the tasks and products still overflows my footer

Try something like

.paymentcondition { page-break-before: always; page-break-inside: avoid; } 

#table-totals {
     page-break-after: always;
     page-break-inside: avoid;
}

#product-table tbody tr {
  
  page-break-inside: avoid;
}

for page breaks and something like

#footer {
        background-color: #ebebeb;
        position: fixed;
grid-template-columns: 1fr;
display: grid;
        bottom: 0;
        border-top: 0px solid #000;
        width: 98.5%;
        min-height:100px;
        padding-top: 0.5rem;
        padding-bottom: 1rem;
        margin-top: 40px;
    }

Currently on mobile, so I hope it displays correctly.

1 Like

@david

Now that I have installed the update, I was able to test the QR code.

Unfortunately, there is an error here:

What should be found in the QRCode was the “number” not the id_number

Apparently the designation has changed here between 4 and 5. In German, the field name for the id_number is “Registernummer” and for the number aka “Kundennummer” only “Nummer”

And, my banking app does not include the number in the purpose of use either

Maybe you should give us the opportunity to customize the SEPA QRs, that would simplify things

I took a closer look at the SEPA QRs.
Sources:

https://zv.psa.at/de/download/qr-code/339-qr-code-und-bcd-definition-3-en/file.html

At the moment, the following data is supplied in the QR code:

Service Tag
Service version
Character set
Identification
BIC
Name
IBAN
Amount
Purpose

According to the app/Helpers/Epc/EpcQrGenerator.php the data is then filled as follows:

Service Tag --> BCD
Version --> 2 (this becomes 002)
Character set --> 1
Identification --> SCT
BIC --> $company2
Name --> $company.name
IBAN --> $company1
Amount -->$amount (formatted with EURXX.XX)
Purpose --> $invoice.number (and now the $client.id_number which is not consistent)

However, according to SEPA, the purpose is a purely 4-digit code. As far as I can remember, this is used internally by the bank and also by various accounting programs to determine the use of the incoming funds (rent, taxes, etc.) It is also referred to as a business code.

Therefore, it would make sense to let us users fill in the fields that are possible according to the documentation.

This applies to the fields:

Purpose
Remittance (Reference)
Remittance (Text)
Information*

My humble idea:

Purpose → (Does not have to be filled, only with 4-digit code)

The two fields are mutually exclusive, which is why only one may be used:

Remittance (Reference) → Generic field like the generated numbers (e.g. customer number/invoice number → 1000666IN202403012034) Cross selection, max 35 bytes

Remittance (text) → Selection from various customer and invoice fields (customer number, invoice number, date, etc.) Max 140 characters

Information* → Does not play a role for the transfer, max. 70 characters

You may want to add IBAN and BIC to the company data, then you wouldn’t have to use a user-defined field for this and, above all, you wouldn’t have to pay attention to the order :wink:

@ITSAW

I’ve rolled back the change on the purpose field, however it still is not clear to me which field you would like the client number injected into?

    public function encodeMessage()
    {
        return rtrim(implode("\n", [
            $this->sepa['serviceTag'],
            sprintf('%03d', $this->sepa['version']),
            $this->sepa['characterSet'],
            $this->sepa['identification'],
            isset($this->company?->custom_fields?->company2) ? $this->company->settings->custom_value2 : '',
            $this->company->present()->name(),
            isset($this->company?->custom_fields?->company1) ? $this->company->settings->custom_value1 : '',
            $this->formatMoney($this->amount),
            $this->sepa['purpose'],
            substr($this->invoice->number, 0, 34),
            '',
            ' '
        ]), "\n");

Currently the reference field holds the invoice number, can we use the text field for the client number, will this play nice with the banking apps?

With reference to the graphic “Content Definition”:

Element Max Char Current Planned
Servic Tag 3 Byte BCD BCD
Version 3 Byte 002 002
Coding (Character set) 1 Byte 1 1
Function (Identification) 3 Byte SCT SCT
BIC 8 Byte / 11 Byte (V1 / V2) $company2 $company2
Reciever 70 Character $company.name $company.name
IBAN 34 Byte $company1 $company1
Amount / Currency 15 Byte (Max EUR999999999.99) $amount $amount
Purpose 4 Byte $invoice.number \n
Reference 35 Byte \n \n
Text 140 Character \n $invoice.number $client.number
Display 70 Character \n \n

So i Think your code should be look like this, if i understand your code right :wink:

public function encodeMessage()
    {
        return rtrim(implode("\n", [
            $this->sepa['serviceTag'],
            sprintf('%03d', $this->sepa['version']),
            $this->sepa['characterSet'],
            $this->sepa['identification'],
            isset($this->company?->custom_fields?->company2) ? $this->company->settings->custom_value2 : '',
            $this->company->present()->name(),
            isset($this->company?->custom_fields?->company1) ? $this->company->settings->custom_value1 : '',
            $this->formatMoney($this->amount),
            '',
            '',
            $this->sepa['Text'],
            substr($this->invoice->number $this->client->number, 0, 139),
            '',
            ' '
        ]), "\n");

No Purpose, no Reference, no Display

Hope that clears up the confusion to some extent

@david
Any new findings or information on the subject.
It would be nice if this was not forgotten

@ITSAW

I’m not sure on the suggested output, from my research, we are only adding the reference field (140 char)

so the updated code would look like this for me:

 return rtrim(implode("\n", [
            $this->sepa['serviceTag'],
            sprintf('%03d', $this->sepa['version']),
            $this->sepa['characterSet'],
            $this->sepa['identification'],
            isset($this->company?->custom_fields?->company2) ? $this->company->settings->custom_value2 : '',
            $this->company->present()->name(),
            isset($this->company?->custom_fields?->company1) ? $this->company->settings->custom_value1 : '',
            $this->formatMoney($this->amount),
            $this->sepa['purpose'],
            substr($this->invoice->number, 0, 34),
            substr("{$this->invoice->number} {$this->invoice->client->number}", 0,139),
            ' '
        ]), "\n");

however, the Payment Reference and Remittance Information cannot be used together, only one can:

so there are 12 possible fields:

  1. service tag
  2. version
  3. char set
  4. SCT
  5. BIC
  6. Name of beneficiary
  7. IBAN
  8. Amount in EUR
  9. PURPOSE max chars 4 - optional
  10. Remittance information Structured Creditor ref ie. invoice number
  11. Remittance infomratiom Unstructured - cannot coexist with #10
    12 Beneficiary to orignator information - optional

TLDR. I think the original implementation was correct here.

Good morning,

Now that I have the code, I have really understood the structure. Sorry, my mistake.

The point is, as you said, reference and text, i.e. 10 and 11 cannot exist together.

That’s why you should have the option to compile the fields yourself.

The option to build the QR code yourself in version 4 was more pleasant than always bothering you with it and coming to a solution across the various time zones and only non-verbally.

Alternative implementation:

return rtrim(implode("\n", [
            $this->sepa['serviceTag'],
            sprintf('%03d', $this->sepa['version']),
            $this->sepa['characterSet'],
            $this->sepa['identification'],
            isset($this->company?->custom_fields?->company2) ? $this->company->settings->custom_value2 : '',
            $this->company->present()->name(),
            isset($this->company?->custom_fields?->company1) ? $this->company->settings->custom_value1 : '',
            $this->formatMoney($this->amount),
            $this->sepa['purpose'],
            substr("{$this->invoice->number} {$this->invoice->client->number}", 0, 34),
            substr("{$this->invoice->number} {$this->invoice->client->number}", 0,139),
            ' '
        ]), "\n");

Since the whole thing with the answers, if you are no longer interested in the topic, works semi well, I have adapted the code for myself.

And the QR code works perfectly for the two banking apps with which I was able to test it.

    public function encodeMessage()
    {
        return rtrim(implode("\n", [
            $this->sepa['serviceTag'],
            sprintf('%03d', $this->sepa['version']),
            $this->sepa['characterSet'],
            $this->sepa['identification'],
            isset($this->company?->custom_fields?->company2) ? $this->company->settings->custom_value2 : '',
            $this->company->present()->name(),
            isset($this->company?->custom_fields?->company1) ? $this->company->settings->custom_value1 : '',
            $this->formatMoney($this->amount),
            $this->getPurposeCode(),
            '',
            substr("{$this->invoice->number} Kd-Nr.: {$this->invoice->client->number}", 0,139),
            '',
            ' '
        ]), "\n");
    }