Add function to retrieve company address as JSON and update invoice procedures

- Created a new function `fds__getCompanyAddressJson` to return a company's postal address as a structured JSON object.
- Modified stored procedures `fds__createInvoice`, `fds__setInvoice`, and `fds__prepInvoice` to include a new parameter `@SendToAddressJson` for handling the address data.
- Updated the invoice table and user-defined types to accommodate the new `SendToAddressJson` field.
- Ensured that the address data is properly retrieved and stored in the invoice records.
This commit is contained in:
2026-07-18 18:01:24 +02:00
parent 5ccd85c38f
commit 628802db19
45 changed files with 1892 additions and 26 deletions
+77
View File
@@ -1504,7 +1504,84 @@ $inv.cSt = function (data) {
}
});
};
/* Country choices for the structured recipient-address dialog (ISO 3166-1 alpha-2, BT-55). */
$inv.adrCountries = [['DE', 'Deutschland'], ['AT', 'Österreich'], ['CH', 'Schweiz'], ['FR', 'Frankreich'],
['NL', 'Niederlande'], ['BE', 'Belgien'], ['LU', 'Luxemburg'], ['IT', 'Italien'], ['ES', 'Spanien'],
['PL', 'Polen'], ['DK', 'Dänemark'], ['CZ', 'Tschechien'], ['GB', 'Großbritannien'], ['US', 'USA']];
/* Compose the free-text postal block (for the inline display) from the structured address —
mirrors the backend InvoiceRecipientAddress.Compose (the VAT id is never part of the block). */
$inv.composeAddress = function (a) {
a = a || {}; let lines = [];
let push = (v) => { v = ('' + (v || '')).trim(); if (v !== '') lines.push(v); };
push(a.name);
if (('' + (a.contact || '')).trim() !== '') push('z.Hd. ' + ('' + a.contact).trim());
push(a.line2 || a.addressLine2);
push(a.street);
push((('' + (a.postalCode || '')) + ' ' + ('' + (a.city || ''))).trim());
let cc = ('' + (a.countryCode || '')).toUpperCase();
if (cc !== '' && cc !== 'DE') push(cc);
return lines.join('\n');
};
/* The structured recipient address currently in effect: the backend keeps it in
CustomValues.sendToAddress (survives a draft refresh); new invoices seed it from
fds__prepInvoice's invoiceaddressData; otherwise start empty. */
$inv.adrCurrent = function (tbl) {
let cv = jObj((tbl.data('new') || {}).CustomValues, 'sendToAddress');
if (typeof cv === 'string' && cv.trim().charAt(0) === '{') { try { cv = JSON.parse(cv); } catch (e) { cv = null; } }
if (cv && typeof cv === 'object') return cv;
let ad = (tbl.data('admin') || {}).invoiceaddressData;
if (typeof ad === 'string' && ad.trim().charAt(0) === '{') { try { return JSON.parse(ad); } catch (e) { } }
else if (ad && typeof ad === 'object') { return ad; }
return {};
};
/* Structured recipient-address dialog (EN 16931 / DATEV). Posts the address delta as a JSON
object; the backend stores it, composes SendToAddress and keeps the PDF unchanged (ADR 0012).
B2C stays effortless: the USt-IdNr. is optional (empty = private person). */
$inv.eAddress = function (ev) {
let tbl = $inv.d.tbl(), cur = $inv.adrCurrent(tbl);
let flds = [
{ name: 'name', label: 'Name / Firma', type: 'text', value: cur.name || '', required: true },
{ name: 'contact', label: 'z.Hd. (optional)', type: 'text', value: cur.contact || '' },
{ name: 'street', label: 'Straße + Nr.', type: 'text', value: cur.street || '' },
{ name: 'line2', label: 'Adresszusatz (optional)', type: 'text', value: cur.line2 || cur.addressLine2 || '' },
{ name: 'postalCode', label: 'PLZ', type: 'text', value: cur.postalCode || '' },
{ name: 'city', label: 'Ort', type: 'text', value: cur.city || '' },
{ name: 'countryCode', label: 'Land', type: 'select', url: $inv.adrCountries, value: (cur.countryCode || 'DE') },
{ name: 'vatId', label: 'USt-IdNr. (nur bei Firmen)', type: 'text', value: cur.vatId || '' }
];
let hint = $$.dc('adr-conformity').append([
$$.dc('hd').text('Hinweis zur DATEV-/eRechnungs-Konformität'),
$$.dc('tx').text('Pflicht: Name, Straße, PLZ, Ort und Land. Die USt-IdNr. nur bei Firmen angeben — '
+ 'für eine Rechnung an eine Privatperson einfach leer lassen.')
]);
$ocms.dlgform(flds, {
title: 'Rechnungsempfänger',
addcontent: hint,
success: function (res) {
let a = {
name: res.name || '', contact: res.contact || '', line2: res.line2 || '',
street: res.street || '', postalCode: res.postalCode || '', city: res.city || '',
countryCode: (res.countryCode || 'DE'), vatId: ('' + (res.vatId || '')).trim()
};
let txt = $inv.composeAddress(a);
if (ev.data && ev.data.t) { ev.data.t.rwText(txt); }
if (typeof (ev.data || {}).change === 'function') { ev.data.change(txt); }
/* Structured delta — the invoice draft backend accepts a JSON object for 'address'. */
$inv.d.sync({ Target: 'address', Value: a });
}
});
};
$inv.eHtml = function (ev) {
/* The recipient address is edited through the structured dialog whenever an invoice draft is
active; reminders (no invoice draft token) keep the plain free-text editor. */
if (ev.data && !(ev.data instanceof jQuery) && ev.data.nme === 'invoiceaddress'
&& $inv.d && typeof $inv.d.token === 'function' && $inv.d.token() !== '') {
return $inv.eAddress.call(this, ev);
}
let t = $(this), frmct = ev.data instanceof jQuery ? ev.data : ev.data.t;
/* Single-line fields must stay plain text — the TinyMCE/html editor wraps the value in <p>
tags, which used to get posted and persisted verbatim (e.g. <p>18.06.2026</p> in the