Zum Hauptinhalt springen

Upgrade auf 24.0.0

gefahr

Unbedingt die aktuellen Installationsvoraussetzungen vorab prüfen!

tipp

Diese Anleitung berücksichtigt immer nur den Sprung von der vorhergehenden Version zu der gerade beschriebenen Version. Bei Upgrades über mehrere Versionen hinweg müssen alle Änderungen der Zwischenversionen ebenfalls beachtet werden! Siehe genereller Ablauf von Upgrades.

Breaking Changes

gefahr

Diese Version integriert das neue One Unity Standard-DMS und ist demnach ausschließlich mit OUD Versionen kompatibel, welche bereits das Standard-DMS enthalten (ab Version 23.0.0).

Was wurde gemacht?

Zusammenfassung

  • Der Datenbank-View viewInvoiceFileSearch für die Beleganzeige wurde entfernt und durch eine Scripting-Logik ersetzt.
  • Validierung: Es gibt nun ein Mandantenattribut ptpINVRoundingErrorMargin, das die Rundungstoleranz festlegt, die beim Validieren der Kopf- und Positionsdaten berücksichtigt werden soll INV-447
  • Invoice ist nun im Standard-DMS integriert INV-537
    • Die Outbar "Konfiguration" wurde aufgelöst – deren Inhalt ist nun in der Outbar "One Unity Administration" im Ordner "Rechnungen" zu finden
    • In der Outbar "Rechnungen" ist der Ordner "Meine Vorgänge" nun unter "Meine Rechnungsvorgänge", "Rechnungen" unter "Rechungsprozess" und "Fehlerhafte Rechnungen" unter "Prüfung und Validierung" zu finden.
  • Mithilfe eines Jobs kann nun der regelmäßige Stammdatenupload über den Insiders-Connector eingerichtet werden INV-575
  • Verbesserung des Buchungsvorschlags und der automatischen Zuweisung des fachlichen Prüfers bei der Autoindexierung INV-560
  • Der User Exit beim Anlegen einer neuen Steuerschlüssel-Mappe wurde überarbeitet, sodass der TableDialog nun bei nicht gesetztem Buchungskreis Steuerschlüssel für ALLE Buchungskreise anzeigt. Dieser Fallback greift nun ebenso wenn für einen gesetzten Buchungskreis keine Steuerschlüssel gefunden wurden. INV-574
  • Einige Funktionen des Skripts ou.spc.ptpINV.lib.release.list wurden in ou.spc.ptpINV.lib.release.list.functions ausgelagert, sodass das Skript ou.tmpl.ptpINV.callbacks.release diese Funktionen importieren kann, ohne einen Import-Loop zu erzeugen. INV-579
  • Eine neue Installations- und Upgradelogik wurde implementiert. INV-553
  • Bei Eingabe von '0' als Steuersatz im TableMonitor wurde der TableDialog nicht geöffnet bzw. der zugehörige Steuercode nicht gezogen. Dies wurde behoben. INV-587
  • Invoice verwendet ab jetzt den neuen Webworkflow INV-573
    • Für die Migration des Visioworkflows siehe Otris-Doku
  • Der ptpInvoice Workflow wurde am Mappentyp ptpInvoice als zulässiger Workflow hinzugefügt, sowie als Standard-Workflow hinterlegt INV-589
  • Die Funktion validatePositionsMandatoryColumns wurde jeweils aus den Skripten ou.spc.ptpINV.lib.validation.rmb und ou.spc.ptpINV.lib.validation.rmb entfernt und als eine einzelne Funktion vom gleichen Namen in ou.spc.ptpINV.lib.validation ausgelagert. Der Funktion validatePositions in ou.spc.ptpINV.lib.validation.rmb, die validatePositionsMandatoryColumns aufruft, kann daher nun das Argument mandatoryColumns übergeben werden, um die zu validierenden Spalten zu definieren – davor waren diese fest im Skript definiert. INV-585
  • Neustarts von Workflows sind nun ausschließlich für Benutzer mit der Rolle ptpInvoiceAdmin möglich. INV-572
  • Das Speichern einer Mappe vom Typ cfgVatCode mit einem Steuersatz, der Nachkommastellen hat, wirft nun keinen Fehler mehr. INV-591
  • Am Mappentyp ptpInvoice wurden die Mappenfelder escalationCheck und escalationDate entfernt. INV-593

Manuell auszuführende Schritte

Datenbank

Wie in genereller Ablauf von Upgrades beschrieben, muss je nach DBMS entweder die Database/mssql.sql oder die Database/mariadb.sql ausgeführt werden.

Mappentypen

  • Am Mappentyp ptpInvoice muss - sofern noch nicht vorhanden - unter dem Reiter 'Zulässige Workflows' der ptpInvoice Workflow hinzugefügt werden. Ebenso muss unter dem Reiter 'Workflow' der ptpInvoice Workflow als Standard-Workflow hinterlegt sein.
  • Am Mappentyp ptpInvoice muss in der Trefferliste release das Feld verificationGroup mit der Bezeichnung pf:filetype.ptpInvoice.verificationGroup am Ende hinzugefügt werden.
  • Am Mappentyp ptpInvoice müssen die bisher ungenutzten Felder escalationCheck und escalationDate entfernt werden. Anschließend via Mappen ändern die Änderungen auf alle bestehenden Mappen anwenden.

Workflow

Es muss ein neuer Signaleingang zwischen Status 100 (FinalState_216) und dem letzten Merge-Element davor (DecisionMerge_164) mit dem Name "Speichern des fachlichen Prüfers" und dem verknüpften Skript ou.sp.ptpINV.workflow.receivesignal.logVerificationUser erstellt werden. Dieser speichert für jede erfolgreich abgeschlossenen Mappe den fachlichen Prüfer in der Datenbank.

Workflow-ext

D:\<EASY|otris>\Workflow-ext\js\ou\cust\ptp\invoice\rob-callbacks-multiTable.js

Hier muss innerhalb der Funktion getVatCodeByVatRate die if-Bedingung if(!vatRate){...} wie unten beschrieben angepasst werden, sodass sich auch bei Eingabe von '0' als Steuersatz im TableMonitor der TableDialog öffnet.

function getVatCodeByVatRate(vatRate, options) {
function clear() {
options.row.set("itemVatRate", "")
options.row.set("itemVatCode", "")
options.row.set("itemVatCodeDescription", "")
}
function clearAndFocusBack() {
clear()
options.row.focus("itemVatRate")
}

clear()

if (vatRate !== 0 && !vatRate) {
return ""
}

var recipient = documentsContext.getFileContext().getFileFieldValue("recipient");

showTableDialog({
...
}

D:\<EASY|otris>\Workflow-ext\js\ou\cust\ptp\invoice\rob-callbacks-multiTable.js

Hier muss in der Funktion getVatCode in der success-Funktion, die an showTableDialog obergeben wird, der Steuersatz anders formatiert werden, sodass auch Steuersätze mit Nachkommastellen korrekt übergeben werden:

Vorher:

        success: function (selectedRows) {
if (selectedRows.length <= 0) {
clearAndFocusBack()
return
}

var selectedRow = selectedRows[0]
options.row.set(invplus.mapping.tableAmountFields.cellVatCode, selectedRow.vatcode)
/* Diese Zeile muss ausgetauscht werden */
options.row.set(invplus.mapping.tableAmountFields.cellTaxRate, parseFloat(selectedRow.vatrate))
/* Ende */

var vatRate = options.row.get(invplus.mapping.tableAmountFields.cellTaxRate);
invplus.calculationAmountMultiTable.calculateAmountForRow(options.row, vatRate !== "");

options.row.focus("itemBookingText")
}

Nachher:

        success: function (selectedRows) {
if (selectedRows.length <= 0) {
clearAndFocusBack()
return
}

var selectedRow = selectedRows[0]
options.row.set(invplus.mapping.tableAmountFields.cellVatCode, selectedRow.vatcode)
/* Diese Zeile muss hinzugefügt werden */
options.row.set(invplus.mapping.tableAmountFields.cellTaxRate, FormatHelper.convertStringToNumber(selectedRow.vatrate))
/* Ende */

var vatRate = options.row.get(invplus.mapping.tableAmountFields.cellTaxRate);
invplus.calculationAmountMultiTable.calculateAmountForRow(options.row, vatRate !== "");

options.row.focus("itemBookingText")
}

Das gleiche muss für die Funktion getVatCodeByVatRate in der selben Datei gemacht werden:

Vorher:

        success: function (selectedRows) {

if (selectedRows.length <= 0) {
clearAndFocusBack()
return
}
var selectedRow = selectedRows[0]
options.row.set(invplus.mapping.tableAmountFields.cellVatCode, selectedRow.vatcode)
/* Diese Zeile muss ausgetauscht werden */
options.row.set(invplus.mapping.tableAmountFields.cellTaxRate, parseFloat(selectedRow.vatrate))
/* Ende */

var vatRate = options.row.get(invplus.mapping.tableAmountFields.cellTaxRate);
invplus.calculationAmountMultiTable.calculateAmountForRow(options.row, vatRate !== "");

options.row.focus("itemBookingText")
}

Nachher:

        success: function (selectedRows) {

if (selectedRows.length <= 0) {
clearAndFocusBack()
return
}
var selectedRow = selectedRows[0]
options.row.set(invplus.mapping.tableAmountFields.cellVatCode, selectedRow.vatcode)
/* Diese Zeile muss hinzugefügt werden */
options.row.set(invplus.mapping.tableAmountFields.cellTaxRate, FormatHelper.convertStringToNumber(selectedRow.vatrate))
/* Ende */

var vatRate = options.row.get(invplus.mapping.tableAmountFields.cellTaxRate);
invplus.calculationAmountMultiTable.calculateAmountForRow(options.row, vatRate !== "");

options.row.focus("itemBookingText")
}

D:\<EASY|otris>\Workflow-ext\js\ou\cust\ptp\invoice\invoice-callbacks-file.js

Hier muss in der success-Funktion der unten mit einem Kommentar markierte Block eingesetzt werden, sodass beim Speichern eines Steuersatzes mit Nachkommastellen kein Fehler mehr geworfen wird.

documents.sdk.exitRegistry.registerFileFieldExitCallback(
"cfgVatCode",
"vatCode",
function (documentsContext, options) {
var fileContext = documentsContext.getFileContext();

var fields = ["vatCode", "vatCodeName", "vatRate"];

// Hole Werte
var value = options.fileFieldValue;
var recipient = fileContext.getFileFieldValue("recipient");

if (!value) {
return;
}

showTableDialog({
title: "Steuerschlüssel auswählen",
script: "ou.spc.cfgVC.callback.functions.getVatCode",
preLookupScript: "ou.spc.cfgVC.callback.functions.lookupVatCode",
params: {
searchValue: value,
recipient: recipient,
},
success: function (selectedRows) {
if (selectedRows.length <= 0) {
fields.forEach(function (field) {
fileContext.setFileFieldValue(field, "");
});
return;
}

var selectedRow = selectedRows[0];
// Do not fill field 'recipient' if it is already set
if (fileContext.getFileFieldValue("recipient") != "") {
delete selectedRow.recipient;
}

/* Diesen Block hinzufügen */
if (selectedRow["vatRate"] !== undefined) {
selectedRow["vatRate"] = documents.sdk.utils.formatNumber(selectedRow["vatRate"], ",", null, 2);
}
/* Block Ende */

fileContext.setFileFieldValues(selectedRow);
},
error: function (error) {
documentsContext.openMessageDialog("Fehler", error);
fields.forEach(function (field) {
fileContext.setFileFieldValue(field, "");
});
},
close: function () {
fields.forEach(function (field) {
fileContext.setFileFieldValue(field, "");
});
},
});
}
);

D:\<EASY|otris>\Workflow-ext/html/ou/sp/ptpINV/cover.html

Die Datei cover.html wurde angepasst, d.h. falls es eine Custom-Version von dieser Datei gibt, muss diese ebenfalls folgende Anpassung in der Funktion executeApplyPositions erhalten:

    executeApplyPositions(rows, shouldReplace) {
...

rows.forEach(function (row) {
var suggestion = validator.createRowWithNetAmountSuggestion() || {}
for (var key in row) {
if (Object.hasOwnProperty.call(row, key) && excludeColumns.indexOf(key) < 0) {
suggestion[key] = row[key]
}
}

/* Diesen Block einfügen */
if (typeof suggestion.itemVatRate === "string") {
suggestion.itemVatRate = FormatHelper.convertStringToNumber(suggestion.itemVatRate);
}
/* Block Ende */

var newRow = multiTable.addRowWithData(suggestion)

if (newRow && newRow.itemVatRate) {
getDefaultVatCode(newRow.itemVatRate, function (entry) {
newRow.itemVatCode = entry.vatcode
newRow.itemVatCodeDescription = entry.vatcodeName
})
}
})

...
},

Scripting

tipp

Alle der Anpassungen unter "Scripting", die sich auf spc-Skripte beziehen, müssen nur übertragen werden, falls für das jeweilige spc-Skript ein gleichnamiges cust-Skript existiert.

ou.spc.ptpINV.install.cfgEnumerationValues

Hier muss die Datenbank 'ptpData' durch 'ousp' ersetzt werden.

Vorher:

...
var db = ou_sp_ptpINV_settings_1.ptpConnections.getDatabaseConnection("ptpData");
...

Nachher:

...
var db = ou_sp_ptpINV_settings_1.ptpConnections.getDatabaseConnection("ousp");
...

ou.spc.ptpINV.settings

Hier muss die Definition von mailTemplateHandlebars entfernt werden, da diese Einstellung nun in ou.cust.oud.settings gesetzt wird:

...
var mailTemplateHandlebars = util.isWindowsOS() ? "".concat(context.getServerInstallPath().replace(/\\server.*$/, ""), "-ext\\reports\\ouMailTemplate.handlebars") : "/usr/share/documents5-ext/reports/ouMailTemplate.handlebars";
exports.mailTemplateHandlebars = mailTemplateHandlebars;
...

ou.spc.ptpINV.settings.monitorRMB

Dieses Skript muss angepasst werden, sodass die Rundungstoleranz auch im Monitor-Gadget verfügbar ist (INV-447):

  • Folgender Import muss hinzugefügt werden:

    var getRoundingErrorMarginAsNumberInEuros =
    require("ou.sp.ptpINV.lib.roundingErrorMargin").getRoundingErrorMarginAsNumberInEuros;
  • Folgender Eintrag muss den monitorOptions hinzugefügt werden:

    var monitorOptions = {
    roundingErrorMargin: getRoundingErrorMarginAsNumberInEuros(),
    ...
    }
  • Die Spalte itemTotalNetAmount bekommt einen neuen Eintrag:

    itemTotalNetAmount: {
    ...
    additionalOptions: {
    roundingErrorMargin: getRoundingErrorMarginAsNumberInEuros()
    },
    ...
  • Die Eigenschaft footerTemplate der Spalte itemTotalNetAmount muss ersetzt werden:

    footerTemplate: function footerTemplate(options) {
    var validator = new invplus.validationAmountMultiTable(
    options.rows,
    options.column.additionalOptions.roundingErrorMargin
    );
    var result = validator.validate();
    var positionsAmount = options.rows
    .reduce(function (acc, row) {
    return acc.plus(+row.itemTotalNetAmount || 0);
    }, new Big(0))
    .round(2)
    .toNumber();
    var textColor = result.success ? "#1b5e20" : "#f44336";
    var styles = [
    result.showWarning
    ? "display: flex;justify-content: space-between;align-items: center"
    : "text-align: right",
    "border-top: solid 2px #000",
    "border-bottom: double 3px #000",
    "margin: -4px",
    "padding-right: 4px",
    "padding-left: 4px",
    "font-weight: bold",
    "color: ".concat(textColor),
    ];
    var formatted = documents.sdk.utils.formatNumber(positionsAmount, null, null, 2);
    return "<div style='"
    .concat(styles.join(";"), "' title='")
    .concat(result.message, "'>")
    .concat(result.showWarning ? "<span class='entypo warning' style='color: #333333'></span>" : "")
    .concat(formatted, " €</div>");
    }
  • Die Eigenschaft click der Spalte compareAmount muss ersetzt werden:

    click: function click(options) {
    var _a;
    var roundingErrorMargin =
    ((_a = multiTableInstances.invoiceItems) === null || _a === void 0
    ? void 0
    : _a.options.roundingErrorMargin) || 0;
    var validator = new invplus.validationAmountMultiTable(options.rows, roundingErrorMargin);
    var result = validator.validate();
    result.showDialog();
    }

ou.spc.ptpINV.settings.monitorROB

Dieses Skript muss angepasst werden, sodass die Rundungstoleranz auch im Monitor-Gadget verfügbar ist (INV-447):

  • Folgender Import muss hinzugefügt werden:

    var getRoundingErrorMarginAsNumberInEuros =
    require("ou.sp.ptpINV.lib.roundingErrorMargin").getRoundingErrorMarginAsNumberInEuros;
  • Folgender Eintrag muss den monitorOptions hinzugefügt werden:

    var monitorOptions = {
    roundingErrorMargin: getRoundingErrorMarginAsNumberInEuros(),
    ...
    }
  • Die Spalte itemTotalNetAmount bekommt einen neuen Eintrag:

    itemTotalNetAmount: {
    ...
    additionalOptions: {
    roundingErrorMargin: getRoundingErrorMarginAsNumberInEuros()
    },
    ...
  • Die Eigenschaft footerTemplate der Spalte itemTotalAmount muss ersetzt werden:

    footerTemplate: function footerTemplate(options) {
    var validator = new invplus.validationAmountMultiTable(
    options.rows,
    options.column.additionalOptions.roundingErrorMargin
    );
    var result = validator.validate();
    var fileContext = documentsContext.getFileContext();
    var docType = fileContext.getFileFieldValue("docType");
    var debitCreditIndicator = docType.substring(0, 1).toUpperCase() === "R" ? "S" : "H";
    var positionsAmount = options.rows
    .reduce(function (acc, row) {
    var amount = +row.itemTotalAmount || 0;
    if (row.itemDebitCreditIndicator && debitCreditIndicator !== row.itemDebitCreditIndicator) {
    // If debitCreditIndicator doesn't match to itemDebitCreditIndicator, flip operator
    amount *= -1;
    }
    return acc.plus(amount);
    }, new Big(0))
    .round(2)
    .toNumber();
    var textColor = result.success ? "#1b5e20" : "#f44336";
    var styles = [
    result.showWarning
    ? "display: flex;justify-content: space-between;align-items: center"
    : "text-align: right",
    "border-top: solid 2px #000",
    "border-bottom: double 3px #000",
    "margin: -4px",
    "padding-right: 4px",
    "padding-left: 4px",
    "font-weight: bold",
    "color: ".concat(textColor),
    ];
    var formatted = documents.sdk.utils.formatNumber(positionsAmount, null, null, 2);
    return "<div style='"
    .concat(styles.join(";"), "' title='")
    .concat(result.message, "'>")
    .concat(result.showWarning ? "<span class='entypo warning' style='color: #333333'></span>" : "")
    .concat(formatted, " €</div>");
    }
  • Die Eigenschaft buttons -> appendNewRow -> click muss angepasst werden:

    click: function click(options) {
    var _a;
    var roundingErrorMargin =
    ((_a = multiTableInstances.invoiceItems) === null || _a === void 0
    ? void 0
    : _a.options.roundingErrorMargin) || 0;
    var validator = new invplus.validationAmountMultiTable(options.rows, roundingErrorMargin);
    ...
    }
  • Die Eigenschaft click der Spalte compareAmount muss ersetzt werden:

    click: function click(options) {
    var _a;
    var roundingErrorMargin =
    ((_a = multiTableInstances.invoiceItems) === null || _a === void 0
    ? void 0
    : _a.options.roundingErrorMargin) || 0;
    var validator = new invplus.validationAmountMultiTable(options.rows, roundingErrorMargin);
    var result = validator.validate();
    result.showDialog();
    }

ou.spc.ptpINV.callback.functions.cover.historyVerification

Hier muss zuerst ein neuer Import hinzugefügt werden:

var ou_spc_ptpINV_lib_proposal_1 = require("ou.spc.ptpINV.lib.proposal");

Anschließend muss die Funktion getQuery wie folgt angepasst werden:

function getQuery(db) {
var verificationUserPriority = (0, ou_spc_ptpINV_lib_proposal_1.getVerificationUserPriorityFor)(
recipient,
vendorId
);
if (method === "history") {
return ou_sp_SelectBuilder_1.SelectBuilder.from(
verificationUserPriority === "assigned"
? "viewVerificationUserLatestAssigned"
: "viewVerificationUserLatestExecuted",
db
)
.select("editor", "string")
.select("invoiceDate", "date")
.select("Anzahl", "number")
.where("recipient = '".concat(recipient, "'"))
.andWhere("vendorId = '".concat(vendorId, "'"))
.orderBy("invoiceDate", false)
.orderBy("Anzahl", false);
}
return ou_sp_SelectBuilder_1.SelectBuilder.from(
verificationUserPriority === "assigned"
? "viewVerificationUserFrequencyAssigned"
: "viewVerificationUserFrequencyExecuted",
db
)
.select("editor", "string")
.select("Prozent", "float")
.select("Anzahl", "number")
.where("recipient = '".concat(recipient, "'"))
.andWhere("vendorId = '".concat(vendorId, "'"))
.orderBy("Prozent", false)
.orderBy("Anzahl", false);
}

ou.spc.ptpINV.filetype.action.manStartWF

Hier muss zuerst ein neuer Import hinzugefügt werden:

var ou_sp_ptpINV_lib_verificationUserMonitor_1 = require("ou.sp.ptpINV.lib.verificationUserMonitor");

Anschließend muss nach dem docFile.sync(); folgende Zeile eingefügt werden:

(0, ou_sp_ptpINV_lib_verificationUserMonitor_1.deleteVerificationUserEntriesForFile)(docFile);

ou.spc.ptpINV.filetype.field.html.cover

In der Funktion getVerificationData muss unter der Variable var methods = ... folgender Code hinzugefügt werden:

var selectedPriority = (0, ou_spc_ptpINV_lib_proposal_1.getVerificationUserPriorityFor)(
docFile.recipient,
docFile.vendorId
);
var selectedPriorityLabel =
selectedPriority === "assigned"
? context.getFromSystemTable("filetype.cfgBP.verificationUserPriority.assigned")
: context.getFromSystemTable("filetype.cfgBP.verificationUserPriority.executed");

Im Rückgabeobjekt der gleichen Funktion muss der Eintrag header erweitert werden:

{
header: "Vergangene Rechnungsprüfer (".concat(selectedPriorityLabel, ")"),
...
}

ou.spc.ptpINV.lib.proposal

Es gibt eine neue Funktion:

/**
* Returns the value for the given key from the first booking proposal
* @param {keyof cfgBookingProposal} key
* @param {string} recipient
* @param {string} vendorId
* @param {string} defaultValue
* @returns {string}
*/
function getValueFromFirstBookingProposal(key, recipient, vendorId, defaultValue) {
if (!recipient || !vendorId) {
logger.debug("getValueFromFirstBookingProposal recipient oder vendorId ist leer");
return defaultValue;
}
var filter = "recipient='".concat(recipient, "' AND vendorId='").concat(vendorId, "'");
logger.debug("Suche nach ptpVendorSettings: ".concat(filter));
var vendorSettingsFiles = new FileResultset("ptpVendorSettings", filter);
if (!vendorSettingsFiles || vendorSettingsFiles.size() <= 0) {
logger.debug("Keine Einträge gefunden");
return defaultValue;
}
var ids = [];
ou_sp_Iterators_1.Iterators.foreach(vendorSettingsFiles, function (vendorSettingsFile) {
ids.push(vendorSettingsFile.uuid);
});
var filterIds = "vendorFileId~".concat(util.getQuoted(ids.join("|")));
logger.debug("Suche nach cfgBookingProposal: ".concat(filterIds));
var files = new FileResultset("cfgBookingProposal", filterIds, "");
if (!files || files.size() <= 0) {
logger.debug("Keine Einträge gefunden");
return defaultValue;
}
var proposal = files.first();
logger.debug("Verwende '".concat(proposal[key], "' aus ").concat(proposal.getid()));
return proposal[key];
}

Zudem haben sich zwei Funktionen geändert:

/**
* Returns the booking proposal method for specified vendor or globally selected proposal method
* @param {string} recipient
* @param {string} vendorId
* @returns {"frequency"|"history"|"custom"|"none"|""}
*/
function getVerificationUserProposalMethodFor(recipient, vendorId) {
var defaultMethod = getGlobalVerificationUserProposalMethod();
var method = getValueFromFirstBookingProposal("verificationUserProposalMethod", recipient, vendorId, defaultMethod);
return method;
}
/**
* Returns the booking proposal method for specified vendor or globally selected proposal method
* @param {string} recipient
* @param {string} vendorId
* @returns {"assigned" | "executed"}
*/
function getVerificationUserPriorityFor(recipient, vendorId) {
var defaultPriority = "executed";
var priority = getValueFromFirstBookingProposal("verificationUserPriority", recipient, vendorId, defaultPriority);
return priority || defaultPriority;
}

ou.spc.ptpINV.lib.proposal.verificationUser.frequency

Zuerst muss ein neuer Import hinzugefügt werden:

var ou_spc_ptpINV_lib_proposal_1 = require("ou.spc.ptpINV.lib.proposal");

Dann muss die Funktion getLastEditor ausgetauscht werden:

function getLastEditor(db, recipient, vendorId) {
var verificationUserPriority = (0, ou_spc_ptpINV_lib_proposal_1.getVerificationUserPriorityFor)(
recipient,
vendorId
);
var view =
verificationUserPriority === "assigned"
? "viewVerificationUserFrequencyAssigned"
: "viewVerificationUserFrequencyExecuted";
var query = ou_sp_SelectBuilder_1.SelectBuilder.from(view, db)
.select("editor", "string")
.select("Prozent", "float")
.where("recipient = '".concat(recipient, "'"))
.andWhere("vendorId = '".concat(vendorId, "'"))
.andWhere("Prozent >= ".concat(minPercentage))
.andWhere("Anzahl >= ".concat(minVerificationCount))
.orderBy("Prozent", false);
logger.debug("SQL: ".concat(query.toSQL()));
var rows = query.execute();
if (rows.length <= 0) {
logger.debug("No data");
return "";
}
logger.debug("Daten aus View: ".concat(JSON.stringify(rows[0])));
return rows[0].editor;
}

ou.spc.ptpINV.lib.proposal.verificationUser.history

Zuerst muss ein neuer Import hinzugefügt werden:

var ou_spc_ptpINV_lib_proposal_1 = require("ou.spc.ptpINV.lib.proposal");

Dann muss die Funktion getLastEditor ausgetauscht werden:

function getLastEditor(db, recipient, vendorId) {
var verificationUserPriority = (0, ou_spc_ptpINV_lib_proposal_1.getVerificationUserPriorityFor)(
recipient,
vendorId
);
var view =
verificationUserPriority === "assigned"
? "viewVerificationUserLatestAssigned"
: "viewVerificationUserLatestExecuted";
var query = ou_sp_SelectBuilder_1.SelectBuilder.from(view, db)
.select("editor", "string")
.select("invoiceDate", "date")
.select("Anzahl", "number")
.where("recipient = '".concat(recipient, "'"))
.where(" AND vendorId = '".concat(vendorId, "'"))
.orderBy("invoiceDate", false)
.orderBy("Anzahl", false);
logger.debug("SQL: ".concat(query.toSQL()));
var rows = query.execute();
var lastEditor = rows.length > 0 ? rows[0].editor : "";
logger.debug("Editor aus View: ".concat(lastEditor));
return lastEditor;
}

ou.tmpl.ptpINV.callbacks.monitor

Hier gibt es zwei neue callbacks:

/**
* Callback bevor die Funktion logVerificationUser ausgeführt wird
* @param { { docFile: ptpInvoice } } data
*/
var logVerificationUserBefore = function logVerificationUserBefore(data) {
logger.debug("trigger logVerificationUser before");
};
/**
* Callback nachdem die Funktion logVerificationUser ausgeführt wurde
* @param { { docFile: ptpInvoice, assigned: string, executed: string } } data
*/
var logVerificationUserAfter = function logVerificationUserAfter(data) {
logger.debug("trigger logVerificationUser after");
};

...

ou_sp_ptpINV_lib_callbacks_1.ptpINVCallbacks.registerCallback("before", "logVerificationUser", logVerificationUserBefore);
ou_sp_ptpINV_lib_callbacks_1.ptpINVCallbacks.registerCallback("after", "logVerificationUser", logVerificationUserAfter);


ou.spc.cfgVC.callback.functions.getVatCode

Hier muss das cust-Skript mit dem zugehörigen spc-Skript verglichen werden und es müssen ggf. die Änderungen, die sich aus dem Vergleich des 'alten' mit dem 'neuen' spc-Skript ergeben, übernommen werden.

spc-Skript vorheriges Release:

context.enableModules(); // ************************************************************************
// One Unity Consulting GmbH & Co. KG
// ************************************************************************
// Beschreibung: User Exit Callback für Steuerschlüssel
// ************************************************************************
if (typeof module !== "undefined") {
exports.executeDirectly = executeDirectly;
}
var ou_sp_gadget_TableDialog_1 = require("ou.sp.gadget.TableDialog");
var ou_spc_ptpINV_settings_1 = require("ou.spc.ptpINV.settings");
var ou_sp_SelectBuilder_1 = require("ou.sp.SelectBuilder");
function executeDirectly() {
var ptpConnections = (0, ou_spc_ptpINV_settings_1.getSettings)().ptpConnections;
var db = ptpConnections.getDatabaseConnection("ptpData");
try {
var args = ou_sp_gadget_TableDialog_1.TableDialog.getParams();
var recipient = args.recipient.replace(/\*/, "%") || "%";
var searchValue = args.searchValue.replace(/\*/, "%") || "%";
var rows = ou_sp_SelectBuilder_1.SelectBuilder.from("vatcode", db)
.select("vatcode", "string")
.select("vatcodeName", "string")
.select("vatrate", "float")
.where("recipient = '".concat(recipient, "'"))
.andWhere("vatcode LIKE '%".concat(searchValue, "%'"))
.orderBy("vatcodeName", false)
.execute();
var table = new ou_sp_gadget_TableDialog_1.TableDialog({
rows: rows,
columns: {
vatcode: {
label: "de:Steuerschlüssel;en:Vat code",
field: "vatCode",
},
vatcodeName: {
label: "de:Bezeichnung;en:Description",
field: "vatCodeName",
},
vatrate: {
label: "de:Steuersatz;en:Vat rate",
field: "vatRate",
type: "number",
},
},
});
return table.transfer();
} catch (error) {
context.errorMessage = error.message;
return -1;
} finally {
if (db) {
db.close();
}
}
}

spc-Skript aktuelles Release:

context.enableModules(); // ************************************************************************
// One Unity Consulting GmbH & Co. KG
// ************************************************************************
// Beschreibung: User Exit Callback für Steuerschlüssel
// ************************************************************************
if (typeof module !== "undefined") {
exports.executeDirectly = executeDirectly;
}
var tslib_1 = require("ou.sp.externals.tslib");
var ou_sp_gadget_TableDialog_1 = require("ou.sp.gadget.TableDialog");
var ou_spc_ptpINV_settings_1 = require("ou.spc.ptpINV.settings");
var ou_sp_SelectBuilder_1 = require("ou.sp.SelectBuilder");
function executeDirectly() {
var columnsVat = {
vatcode: {
label: "de:Steuerschlüssel;en:Vat code",
field: "vatCode",
},
vatcodeName: {
label: "de:Bezeichnung;en:Description",
field: "vatCodeName",
},
vatrate: {
label: "de:Steuersatz;en:Vat rate",
field: "vatRate",
type: "number",
},
};
var columnRecipient = {
recipient: {
label: "de:Buchungskreis;en:Recipient",
field: "recipient",
},
};
var ptpConnections = (0, ou_spc_ptpINV_settings_1.getSettings)().ptpConnections;
var db = ptpConnections.getDatabaseConnection("ptpData");
try {
var args = ou_sp_gadget_TableDialog_1.TableDialog.getParams();
var recipient = args.recipient || "%";
var searchValue = args.searchValue.replace(/\*/, "%") || "%";
var rows = ou_sp_SelectBuilder_1.SelectBuilder.from("vatcode", db)
.select("recipient", "string")
.select("vatcode", "string")
.select("vatcodeName", "string")
.select("vatrate", "float")
.where("recipient ".concat(recipient === "%" ? "LIKE" : "=", " '").concat(recipient, "'"))
.andWhere("vatcode LIKE '%".concat(searchValue, "%'"))
.orderBy("vatcodeName", false)
.execute();
if (recipient === "%") {
// Display results for all recipients
var table_1 = new ou_sp_gadget_TableDialog_1.TableDialog({
rows: rows,
columns: tslib_1.__assign(tslib_1.__assign({}, columnRecipient), columnsVat),
});
return table_1.transfer();
}
if (rows.length > 0) {
// Results found for given recipient -> display results
var table_2 = new ou_sp_gadget_TableDialog_1.TableDialog({
rows: rows,
columns: columnsVat,
});
return table_2.transfer();
}
// No results found for given recipient -> query all recipients
rows = ou_sp_SelectBuilder_1.SelectBuilder.from("vatcode", db)
.select("recipient", "string")
.select("vatcode", "string")
.select("vatcodeName", "string")
.select("vatrate", "float")
.where("vatcode LIKE '%".concat(searchValue, "%'"))
.orderBy("vatcodeName", false)
.execute();
if (rows.length === 1) {
// Add empty row to ensure that table dialog is displayed
rows.push({
recipient: "",
vatcode: "",
vatcodeName: "",
vatrate: null,
});
}
var table = new ou_sp_gadget_TableDialog_1.TableDialog({
rows: rows,
columns: tslib_1.__assign(tslib_1.__assign({}, columnRecipient), columnsVat),
});
return table.transfer();
} catch (error) {
context.errorMessage = error.message;
return -1;
} finally {
if (db) {
db.close();
}
}
}

ou.spc.cfgVC.callback.functions.lookupVatCode

Auch hier muss das cust-Skript mit dem zugehörigen spc-Skript verglichen werden und es müssen ggf. die Änderungen, die sich aus dem Vergleich des 'alten' mit dem 'neuen' spc-Skript ergeben, übernommen werden.

spc-Skript vorheriges Release:

context.enableModules(); // ************************************************************************
// One Unity Consulting GmbH & Co. KG
// ************************************************************************
// Beschreibung: User Exit Callback für Steuerschlüssel
// ************************************************************************
if (typeof module !== "undefined") {
exports.executeDirectly = executeDirectly;
}
var ou_sp_gadget_TableDialog_1 = require("ou.sp.gadget.TableDialog");
var ou_spc_ptpINV_settings_1 = require("ou.spc.ptpINV.settings");
var ou_sp_SelectBuilder_1 = require("ou.sp.SelectBuilder");
function executeDirectly() {
var ptpConnections = (0, ou_spc_ptpINV_settings_1.getSettings)().ptpConnections;
var db = ptpConnections.getDatabaseConnection("ptpData");
try {
var args = ou_sp_gadget_TableDialog_1.TableDialog.getParams();
var recipient = args.recipient.replace(/\*/, "%") || "%";
var searchValue = args.searchValue.replace(/\*/, "%") || "%";
var rows = ou_sp_SelectBuilder_1.SelectBuilder.from("vatcode", db)
.count("vatcodeName", "count")
.where("recipient = '".concat(recipient, "'"))
.andWhere("vatcode LIKE '%".concat(searchValue, "%'"))
.execute();
return rows[0].count;
} catch (error) {
context.errorMessage = error.message;
return -1;
} finally {
if (db) {
db.close();
}
}
}

spc-Skript aktuelles Release:

context.enableModules(); // ************************************************************************
// One Unity Consulting GmbH & Co. KG
// ************************************************************************
// Beschreibung: User Exit Callback für Steuerschlüssel
// ************************************************************************
if (typeof module !== "undefined") {
exports.executeDirectly = executeDirectly;
}
var ou_sp_gadget_TableDialog_1 = require("ou.sp.gadget.TableDialog");
var ou_spc_ptpINV_settings_1 = require("ou.spc.ptpINV.settings");
var ou_sp_SelectBuilder_1 = require("ou.sp.SelectBuilder");
function executeDirectly() {
var ptpConnections = (0, ou_spc_ptpINV_settings_1.getSettings)().ptpConnections;
var db = ptpConnections.getDatabaseConnection("ptpData");
try {
var args = ou_sp_gadget_TableDialog_1.TableDialog.getParams();
var recipient = args.recipient || "%";
var searchValue = args.searchValue.replace(/\*/, "%") || "%";
var rows = ou_sp_SelectBuilder_1.SelectBuilder.from("vatcode", db)
.count("vatcodeName", "count")
.where("recipient ".concat(recipient === "%" ? "LIKE" : "=", " '").concat(recipient, "'"))
.andWhere("vatcode LIKE '%".concat(searchValue, "%'"))
.execute();
if (rows[0].count === 0 && recipient != "%") {
rows = ou_sp_SelectBuilder_1.SelectBuilder.from("vatcode", db)
.count("vatcodeName", "count")
.where("vatcode LIKE '%".concat(searchValue, "%'"))
.execute();
if (rows[0].count === 1) {
// Add empty row to ensure that table dialog is displayed
rows[0].count = rows[0].count + 1;
}
}
return rows[0].count;
} catch (error) {
context.errorMessage = error.message;
return -1;
} finally {
if (db) {
db.close();
}
}
}

ou.spc.ptpINV.lib.index.validation

In der Funktion validateRMB muss dem Funktionsaufruf rmbValidation.validatePositions als zweites Argument nun ein Objekt übergeben werden, das die zu validierenden Spalten definiert:

  errors = tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(errors), false), tslib_1.__read(rmbValidation.validatePositions(docFile, /* Dieses Objekt muss als Argument eingefügt werden */ {
itemOrderNumber: "Bestell-Nr muss gefüllt sein",
itemQuantity: "Menge muss gefüllt sein",
itemSingleAmount: "Einzelpreis muss gefüllt sein",
itemTotalNetAmount: "Nettobetrag muss gefüllt sein"
} /* Objekt Ende */ )), false);

ou.spc.ptpINV.workflow.guard.export

Dem Funktionsaufruf validationRMB.validatePositions muss als zweites Argument ein Objekt übergeben werden, das die zu validierenden Spalten definiert:

    errors = tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(errors), false), tslib_1.__read(validationRMB.validatePositions(docFile, /* Dieses Objekt muss als Argument eingefügt werden */ {
itemOrderNumber: "Bestell-Nr muss gefüllt sein",
itemQuantity: "Menge muss gefüllt sein",
itemSingleAmount: "Einzelpreis muss gefüllt sein",
itemTotalNetAmount: "Nettobetrag muss gefüllt sein"
} /* Objekt Ende */ )), false);

ou.spc.ptpINV.workflow.guard.manualExport

Dem Funktionsaufruf rmbValidation.validatePositions muss als zweites Argument ein Objekt übergeben werden, das die zu validierenden Spalten definiert:

      errors = rmbValidation.validatePositions(docFile, /* Dieses Objekt muss als Argument eingefügt werden */ {
itemOrderNumber: "Bestell-Nr muss gefüllt sein",
itemQuantity: "Menge muss gefüllt sein",
itemSingleAmount: "Einzelpreis muss gefüllt sein",
itemTotalNetAmount: "Nettobetrag muss gefüllt sein"
} /* Objekt Ende */);

ou.spc.ptpINV.filetype.action.approvedButtons.ts

Auch hier muss das cust-Skript mit dem zugehörigen spc-Skript verglichen werden und es müssen ggf. die Änderungen, die sich aus dem Vergleich des 'alten' mit dem 'neuen' spc-Skript ergeben, übernommen werden.

spc-Skript vorheriges Release:

import { isLockingWorfklowUser } from "./ou.sp.ptpINV.lib";
function executeDirectly() {
const docFile = context.file as ptpInvoice;
const globalState = docFile.globalState;
const su = context.getSystemUser();
const hasAdmin = su.hasAccessProfile("ptpInvoiceAdmin");
const hasAccounting = su.hasAccessProfile("ptpInvoiceAccounting");
const isArchiveFile = docFile.isArchiveFile();
// Wenn Archivmappe, alle Buttons ausblenden
if (isArchiveFile) {
enumval.forEach((_, index) => {
enumval[index] = "";
});
return;
}
const locksWF = isLockingWorfklowUser(docFile, su.login);
for (var i = 0; i < enumval.length; i++) {
// Aufgabe übernehmen nur für Buchhaltung und nur in Status 30
if (enumval[i] == "takeTask") {
if (!hasAccounting || locksWF || globalState != "30") enumval[i] = "";
continue;
}
// DocType darf nur in den Fehlerstati und der Indexierung geändert werden
if (enumval[i] == "changeDocType") {
if (["0", "10", "20", "91", "95"].indexOf(globalState) < 0 || !locksWF) enumval[i] = "";
continue;
}
// Kontierungsvorlagen dürfen nur in der Indexierung verwendet werden
if (enumval[i] == "loadBookingTemplate" || enumval[i] == "writeBookingTemplate") {
if (globalState != "20" || !locksWF) enumval[i] = "";
continue;
}
}
}

export { executeDirectly };

spc-Skript aktuelles Release:

import { isLockingWorfklowUser } from "./ou.sp.ptpINV.lib";
function executeDirectly() {
const docFile = context.file as ptpInvoice;
const globalState = docFile.globalState;
const su = context.getSystemUser();
const hasAdmin = su.hasAccessProfile("ptpInvoiceAdmin");
const hasAccounting = su.hasAccessProfile("ptpInvoiceAccounting");
const isArchiveFile = docFile.isArchiveFile();
// Wenn Archivmappe, alle Buttons ausblenden
if (isArchiveFile) {
enumval.forEach((_, index) => {
enumval[index] = "";
});
return;
}
const locksWF = isLockingWorfklowUser(docFile, su.login);
for (var i = 0; i < enumval.length; i++) {
// Aufgabe übernehmen nur für Buchhaltung und nur in Status 30
if (enumval[i] == "takeTask") {
if (!hasAccounting || locksWF || globalState != "30") enumval[i] = "";
continue;
}
// DocType darf nur in den Fehlerstati und der Indexierung geändert werden
if (enumval[i] == "changeDocType") {
if (["0", "10", "20", "91", "95"].indexOf(globalState) < 0 || !locksWF) enumval[i] = "";
continue;
}
// Kontierungsvorlagen dürfen nur in der Indexierung verwendet werden
if (enumval[i] == "loadBookingTemplate" || enumval[i] == "writeBookingTemplate") {
if (globalState != "20" || !locksWF) enumval[i] = "";
continue;
}

// Neustarten des Workflows dürfen nur von ptpInvoiceAdmin verwendet werden
if (enumval[i] == "startReleaseWF") {
if (!hasAdmin) enumval[i] = "";
continue;
}
}
}

export { executeDirectly };

Alle cust-Skripte, die darüber hinaus (siehe die drei letzten Skripte) die Funktion validatePositionsMandatoryColumns aus dem Skript ou.spc.ptpINV.lib.validation.rmb oder ou.spc.ptpINV.lib.validation.rmb importieren, müssen angepasst werden und die Funktion nun aus dem Skript ou.sp.ptpINV.lib.validation importieren. Zudem is das Objekt, das die zu validierenden Spalten festlegt, für die RMB-Validierung nun nicht mehr fest im Code hinterlegt, sondern muss als zweites Argument an die Funktion übergeben werden. Dies betrifft also nun sowohl die eben genannte Funktion validatePositionsMandatoryColumns als auch die Funktion validatePositions in ou.spc.ptpINV.lib.validation.rmb:


ou.spc.ptpINV.settings.monitorROB.ts

Hier wurde für die Spalte itemVatRate die Eigenschaft decimalPrecision von 0 auf 2 geändert, um auch Steuersätze mit Nachkommastellen anzeigen zu können:

    itemVatRate: {
label: "de:Steuersatz;en:Vat rate",
width: "90px",
type: "number",
decimalPrecision: 2,
selectOnFocus: true,
change: function change(value, options) {
//@ts-ignore
invplus.calculationAmountMultiTable.calculateAmountForRow(options.row, value !== "");
getVatCodeByVatRate(value, options);
}
},

Folgende Funktionen wurden vom Skript ou.spc.ptpINV.lib.release.list in das Skript ou.spc.ptpINV.lib.release.list.functions ausgelagert:

  • validateCreateReleaseListOptions
  • getCurrentRequiredReleaseUser
  • ensureUserWithSufficentReleaseRoleExist
  • getCurrentReleaseEntry
  • getCurrentVerificationEntry
  • markCurrentVerificationUserAsReleased
  • markAllAsInactive
  • ensureNextReleaseUserIsRequiredInCaseOfSubstitute
  • loginNameToFullName
  • getNextReleaseUserSubstitute
  • ensureMinNumberOfReleaseUsers

Existiert das Skript ou.cust.ptpINV.lib.release.list, so muss das Skript ou.cust.ptpINV.lib.release.list.functions erstellt werden. In diesem Skript müssen dann alle Funktionen mit den gleichnamigen Funktionen aus ou.cust.ptpINV.lib.release.list überschrieben werden.

Zudem muss in allen cust-Skripten, die eine der oben genannten Funktionen verwenden, der Import von ou.spc.ptpINV.lib.release.list durch ou.spc.ptpINV.lib.release.list.functions ersetzt werden – dies betrifft folgende Skripte aus dem Standard und ggf. auch eigene cust-Skripte:

  • ou.spc.ptpINV.filetype.action.manStartWF
  • ou.spc.ptpINV.lib.gadgets.verification
  • ou.spc.ptpINV.workflow.decision.checkAfterVerify
  • ou.spc.ptpINV.workflow.guard.backToIndex
  • ou.spc.ptpINV.workflow.guard.verified

Abschließend müssen folgende Skripte gelöscht werden:

  • ou.sp.ptpINV.install
  • ou.sp.ptpINV.update.bookingTemplates
  • ou.sp.ptpINV.update.INV-447
  • ou.sp.ptpINV.update.INV-537
  • ou.sp.ptpINV.update.INV-541
  • ou.sp.ptpINV.update.INV-543
  • ou.sp.ptpINV.update.INV-549
  • ou.sp.ptpINV.update.INV-557
  • ou.sp.ptpINV.update.INV-560
  • ou.sp.ptpINV.update.INV-564
  • ou.sp.ptpINV.update.INV-577
  • ou.sp.ptpINV.update
  • ou.spc.ptpINV.install.adminDashboard