Files
Facilitor/APPL/API2/model_invoices.inc
Maykel Geerdink e600505272 MARX#78894: Facturen via REST-API aan kunnen maken.
svn path=/Website/trunk/; revision=68949
2025-05-08 11:40:10 +00:00

380 lines
20 KiB
PHP

<% /*
$Revision$
$Id$
File: model_invoices.inc
Description: Factuur model.
Parameters:
Context:
Notes: TODO: definitieve naamgeving verifieren
TODO: autorisaties voor scope=fo|bo
We had a discussion. The references (order, contract, purchaseorder) are now present by their foreign nodes.
We ourselves want the creditor of the invoice in the result, it is logically a property of an invoice
which we technically do not store with the invoice. Formally the creditor should be obtained by a subsequent
call to the corresponing API. Alternatively we could add those 3 optional nodes with an include option
which is merely designed for 1:n relations, not n:1 like this one.
We do not want to slide down from our purest REST forms (yet) so we made the compromise of these 2 readonly fields.
Let's see how it holds.
*/
%>
<!-- #include file="../Shared/discxalg3d.inc"-->
<!-- #include file="../fin/fin.inc" -->
<!-- #include file="./model_invoicelines.inc"-->
<!-- #include file="./model_custom_fields.inc"-->
<!-- #include file="./model_fac_tracking.inc"-->
<!-- #include file="./model_fin_kenmerk.inc"-->
<%
function model_invoices(fin_key, params)
{
params = params || {};
this.module = "FIN";
this.table = "fin_factuur";
this.primary = "fin_factuur_key";
this.records_name = "invoices";
this.record_name = "invoice";
this.soft_delete = "fin_factuur_verwijder";
// Het interne factuurnr is de id, het externe nummer is name
this.fields =
{"id" : { dbs: "fin_factuur_key", typ: "key" },
"invoicedate" : { dbs: "fin_factuur_datum", typ: "datetime", track: true, label: L("lcl_fin_findate"), filter: "range" },
/*readonly*/ "creditor" : { dbs: "prs_bedrijf_key", typ: "key", foreign: "prs_bedrijf", track: true, label: L("lcl_ord_company_uit"),
sql: "COALESCE(mld_opdr.mld_uitvoerende_keys, bes_bestelopdr.prs_bedrijf_key, cnt_contract.cnt_prs_bedrijf_key)" },
/*readonly*/ "referencetype" : { dbs: "dummy", typ: "varchar",
sql: "DECODE(fin_factuur.mld_opdr_key, NULL, DECODE(fin_factuur.bes_bestelopdr_key, NULL, DECODE(fin_factuur.cnt_contract_key, NULL, '?', 'C'), 'B'), 'O')" },
/*readonly*/ "accountholder" : { dbs: "prs_perslid_key", typ: "key", foreign: "prs_perslid", track: true, label: L("lcl_prs_budgethouder"),
sql: "fin.getfiatteur(fin_factuur.fin_factuur_key)" },
"name" : { dbs: "fin_factuur_nr", typ: "varchar", track: true, label: L("lcl_fin_invoice_nr_extern") },
"order" : { dbs: "mld_opdr_key", typ: "key", foreign: "mld_opdr"},
"contract" : { dbs: "cnt_contract_key", typ: "key", foreign: "cnt_contract"},
"purchaseorder" : { dbs: "bes_bestelopdr_key", typ: "key", foreign: "bes_bestelopdr"},
"total" : { dbs: "fin_factuur_totaal", typ: "float", iscurrency: true, track: true, label: L("lcl_fin_totaal_bedrag") },
"vat" : { dbs: "fin_factuur_totaal_btw", typ: "float", iscurrency: true, track: true, label: L("lcl_fin_totaal_bedrag_btw") },
"blockedaccount" : { dbs: "fin_factuur_gbedrag", typ: "float", iscurrency: true, track: true, label: L("lcl_fin_gbedrag") },
"status" : { dbs: "fin_factuur_statuses_key", typ: "key", foreign: fin.getfinstatustext, track: true, label: L("lcl_fin_fin_status")},
"accountingperiod": { dbs: "fin_factuur_boekmaand", typ: "varchar", track: true, label: L("lcl_fin_divide_period"), multiedit: true },
"debiteur_nr" : { dbs: "fin_factuur_debiteur_nr", typ: "varchar", track: true, label: L("lcl_fin_debtor_nr"), filter: "like", multiedit: true },
"costtype" : { dbs: "prs_kostensoort_key", typ: "key", foreign: "prs_kostensoort", track: true, label: L("lcl_shared_charge_type"), multiedit: true },
"contact" : { dbs: "prs_perslid_key_user", typ: "key", foreign: "prs_perslid", track: true, label: L("lcl_mld_name"), multiedit: true },
"validater" : { dbs: "prs_perslid_key_goedkeur", typ: "number", foreign: "prs_perslid", track: true, label: L("lcl_fin_goedkeurder") },
"source" : { dbs: "fin_factuur_bron", typ: "number", track: true, label: L("lcl_fin_invoice_source") },
"advice" : { dbs: "fin_factuur_advies", typ: "date", track: true, label: L("lcl_fin_adviesdatum") },
"flag" : { dbs: "fin_factuur_flag", typ: "number", track: true, label: L("lcl_fin_flags") },
"contractplace" : { dbs: "cnt_contract_plaats_key", typ: "number", track: true, label: L("lcl_fin_location_scope") },
"remark" : { dbs: "fin_factuur_opmerking", typ: "varchar", track: true, label: L("lcl_fin_remark"), filter: "like" },
"remarkreject" : { dbs: "fin_factuur_opmerking_afw", typ: "varchar", track: true, label: L("lcl_fin_remark_reject"), filter: "like" },
"externnr" : { dbs: "fin_factuur_externnr", typ: "varchar", label: L("extern_nr"), readonly: !(user.has("WEB_FACTAB") || user.has("WEB_FACXNR")) },
"externsyncdate" : { dbs: "fin_factuur_externsyncdate", typ: "datetime", label: L("extern_syncdate"), readonly: !(user.has("WEB_FACTAB") || user.has("WEB_FACXNR")) },
"contractplace" : { dbs: "bes_bestellevr_key", typ: "number", track: true, label: L("lcl_bes_bestellevr_pakbon") }
};
this.includes = {
"invoicelines": {
model: new model_invoicelines(),
joinfield: "invoice",
enable_update: true
},
"custom_fields" : {
model: new model_custom_fields(this, new model_fin_kenmerk("F", { internal: true }), { pNiveau: "F", readman: true, readuse: true }),
joinfield: "flexparentkey",
enable_update: true
},
"tracking": {
model: new model_tracking(["factuur"]),
joinfield: "trackingrefkey"
}
};
function _pre_analyze_fields(params, jsondata) /* analyseer inkomende jsondata, common voor PUT en POST */
{
// De factuurbedragen worden berekend uit de factuurregels. Eventuele waarden die hier gezet zijn negeren.
if (params.method != "POST")
{
delete jsondata.total;
delete jsondata.vat;
}
if (jsondata.status)
{ // Er is een status meegegeven.
jsondata.status = typeof jsondata.status == "object"? jsondata.status.id : jsondata.status;
}
// Status van 3 (foute import) moet bij saven op 2 worden gezet.
// Status 6 (Akkoord) moet bij saven op 2 (Ingevoerd) worden gezet. Factuur dient indien nodig weer gefiatteerd te worden.
if (jsondata.status == 3 || jsondata.status == 6)
{
jsondata.status = 2;
}
};
function _analyze_fields(dbfields, params, jsondata) /* analyseer inkomende data, common voor PUT en POST */
{
if (jsondata.purchaseorder_name && !dbfields.purchaseorder)
{
var sql = "SELECT bes_bestelopdr_key purchaseorder_key"
+ " FROM bes_bestelopdr"
+ " WHERE bes_bestelopdr_id = "+ safe.quoted_sql(jsondata.purchaseorder_name)
+ " ORDER BY bes_bestelopdr_key DESC";
var oRs = Oracle.Execute(sql);
if (!oRs.eof)
{
dbfields["purchaseorder"] = { "dbs": "bes_bestelopdr_key"
, "typ": "key"
, "foreign": "bes_bestelopdr"
, "val" : oRs("purchaseorder_key").Value
};
jsondata.purchaseorder = oRs("purchaseorder_key").Value;
}
oRs.Close();
}
};
function _validate_fields(dbfields, params, jsondata) /* valideer fields, alle constraints die niet door de database worden afgevangen */
{
};
this.REST_GET = function _GET(params)
{
var this_fin = fin.func_enabled_factuur(params.filter.id || -1);
user.auth_required_or_abort(this_fin.canReadAny);
var query = api2.sqlfields(params, this );
query.wheres.push("fin_factuur_verwijder IS NULL");
if (/* self */ 0)
{
query.wheres.push("prs_perslid_key_user=" + user_key);
}
else
{
// TODO: disc3d conditie voor de geldendende autfunction toevoegen
}
query.tables.push("mld_opdr");
query.wheres.push("fin_factuur.mld_opdr_key = mld_opdr.mld_opdr_key(+)");
query.tables.push("bes_bestelopdr");
query.wheres.push("fin_factuur.bes_bestelopdr_key = bes_bestelopdr.bes_bestelopdr_key(+)");
query.tables.push("cnt_contract");
query.wheres.push("fin_factuur.cnt_contract_key = cnt_contract.cnt_contract_key(+)");
var wheres = api2.sqlfilter(params, this);
query.wheres = query.wheres.concat(wheres);
var sql = "SELECT " + query.selects.join(", ")
+ " FROM " + query.tables.join(", ")
+ " WHERE " + query.wheres.join(" AND " )
+ " ORDER BY fin_factuur_key";
if (query.orderbys.length)
sql += ", " + query.orderbys.join(", ");
var json = api2.sql2json (params, sql, this );
return json;
};
this.REST_PUT = function (params, jsondata, the_key) /* update invoice */
{
var fin_key = the_key;
var this_fin = fin.func_enabled_factuur(fin_key);
user.auth_required_or_abort(this_fin.canChange); // Geen wijzigingen toestaan bij onvoldoende rechten.
_pre_analyze_fields(params, jsondata);
//if ("filter" in params && "mode" in params.filter)
// this_fin.canFlexChange = this_fin.canReadAny && params.filter.mode == "attachment"; // Deze mag altijd
// Geen wijzigingen toestaan bij onvoldoende rechten.
user.auth_required_or_abort(this_fin.canChange ||
(this_fin.canReject && jsondata.status == 1) || // Afwijzen.
((this_fin.canUnReject || this_fin.canUnapprove) && jsondata.status == 2) || // On-Afwijzen, On-Fiatteren of On-Goedkeuren.
(this_fin.canAccept && jsondata.status == 5) || // Fiatteren.
(this_fin.canGoedkeur && jsondata.status == 6)) // Goedkeuren.
if (jsondata.status == 1 && this_mld.canReject)
{ // Afwijzen.
var fields = [ { dbs: "fin_factuur_opmerking_afw", typ: "varchar", val: jsondata.remark, track: L("lcl_fin_remark_reject"), len: 4000} ];
var finUpd = buildTrackingUpdate("fin_factuur", "fin_factuur_key = " + fin_key, fields);
oRs = Oracle.Execute(finUpd.sql);
if (finUpd.trackarray.length)
{
fin.trackfactuurupdate(fin_key, L("lcl_fin_is_finupdtrack").format(fin_key) + "\n" + finUpd.trackarray.join("\n"));
}
// Status naar Afgewezen(1).
var sql = "UPDATE fin_factuur"
+ " SET fin_factuur_statuses_key = 1"
+ " WHERE fin_factuur_key = " + fin_key
+ " AND (mld_opdr_key IS NOT NULL OR cnt_contract_key IS NOT NULL OR bes_bestelopdr_key IS NOT NULL)";
Oracle.Execute(sql);
shared.trackaction(this_fin.canAccept? "FINFNO" : "FINGNO", fin_key);
var pResult = new HookResult();
// Klantspecifieke check functie (hookfunction) voor de invoer.
if (!custfunc.fin_postsave(fin_key, pResult))
{
abort_with_warning(pResult.errmsg);
}
}
else if (jsondata.status == 2 && (this_fin.canUnReject || this_fin.canUnapprove))
{ // On-Afwijzen, On-Fiatteren of On-Goedkeuren.
// Huidige status opvragen.
sql = "SELECT fin_factuur_statuses_key"
+ " FROM fin_factuur"
+ " WHERE fin_factuur_key = " + fin_key;
oRs = Oracle.Execute(sql);
var fin_status_old = oRs("fin_factuur_statuses_key").Value;
oRs.Close();
var fields = [ { dbs: "fin_factuur_opmerking_afw", typ: "varchar", val: jsondata.remark, track: L("lcl_fin_remark_reject"), len: 4000} ];
var finUpd = buildTrackingUpdate("fin_factuur", "fin_factuur_key = " + fin_key, fields);
oRs = Oracle.Execute(finUpd.sql);
if (finUpd.trackarray.length)
{
fin.trackfactuurupdate(fin_key, L("lcl_fin_is_finupdtrack").format(fin_key) + "\n" + finUpd.trackarray.join("\n"));
}
// Status terugzetten naar Ingevoerd(2).
sql = "UPDATE fin_factuur"
+ " SET fin_factuur_statuses_key = 2"
+ " WHERE fin_factuur_key = " + fin_key
+ " AND (mld_opdr_key IS NOT NULL OR cnt_contract_key IS NOT NULL OR bes_bestelopdr_key IS NOT NULL)";
Oracle.Execute(sql);
shared.trackaction((fin_status_old == 5? "FINGUN" : "FINFUN"), fin_key); // Vanuit status Afgewezen(1) en Akkoord(6) FINFUN en vanuit status Ter Goedkeuring(5) FINGUN.
}
else if (jsondata.status == 5 && this_fin.canAccept)
{ // Fiatteren.
var fin_enable_goedkeuren = S("fin_enable_goedkeuren");
var fin_status_new = fin_enable_goedkeuren != 0
? "CASE WHEN prs_perslid_key_goedkeur IS NOT NULL THEN 5 ELSE 6 END"
: "6";
sql = "UPDATE fin_factuur"
+ " SET fin_factuur_statuses_key = " + fin_status_new
+ " WHERE fin_factuur_key = " + fin_key
+ " AND (mld_opdr_key IS NOT NULL OR cnt_contract_key IS NOT NULL OR bes_bestelopdr_key IS NOT NULL)";
Oracle.Execute(sql);
shared.trackaction("FINFOK", fin_key);
var sql = "SELECT prs_perslid_key_goedkeur"
+ " FROM fin_factuur f"
+ " WHERE fin_factuur_key = " + fin_key;
oRs = Oracle.Execute(sql);
if (fin_enable_goedkeuren != 0 && oRs("prs_perslid_key_goedkeur").Value > 0)
{ // Factuur is naar status Ter goedkeuring(5) gegaan.
// Notificatie aan de goedkeurder.
fin.sendNeedApprovalNotification(fin_key, oRs("prs_perslid_key_goedkeur").Value);
}
//else // Factuur is naar status Akkoord(6) gegaan.
oRs.Close();
}
else if (jsondata.status == 6 && this_fin.canGoedkeur)
{ // Fiatteren.
var sql = "UPDATE fin_factuur"
+ " SET fin_factuur_statuses_key = 6"
+ " WHERE fin_factuur_key = " + fin_key
+ " AND (mld_opdr_key IS NOT NULL OR cnt_contract_key IS NOT NULL OR bes_bestelopdr_key IS NOT NULL)";
Oracle.Execute(sql);
shared.trackaction("FINGOE", fin_key);
var pResult = new HookResult();
// Klantspecifieke check functie (hookfunction) voor de invoer
if (!custfunc.fin_postsave(fin_key, pResult))
{
abort_with_warning(pResult.errmsg);
}
}
else
{
var dbfields = api2.update_fields(params, this, jsondata); // Build updater
_analyze_fields(dbfields, params, jsondata);
_validate_fields(dbfields, params, jsondata);
var wheres = [" fin_factuur_key = " + fin_key];
var finUpd = buildTrackingUpdate("fin_factuur", wheres.join(" AND " ), dbfields, { noValidateToken: true });
var err = Oracle.Execute(finUpd.sql, true);
if (err.friendlyMsg)
abort_with_warning(err.friendlyMsg);
var fintrack = api2.process_includes(params, this, jsondata, the_key);
// update nog tracken
if (fin_key > 0)
{
fin.trackfactuurupdate(fin_key, L("lcl_fin_is_finupdtrack").format(fin_key) + (finUpd.trackarray.length > 0? "\n" : "") + finUpd.trackarray.join("\n"));
};
}
return { key: fin_key };
};
this.REST_POST = function (params, jsondata) /* new invoice */
{
var this_fin = fin.func_enabled_factuur(-1);
user.auth_required_or_abort(this_fin.canChange); // Geen wijzigingen toestaan bij onvoldoende rechten.
params.isNew = true; // negeer eventuele bestaande keys
_pre_analyze_fields(params, jsondata);
var dbfields = api2.update_fields(params, this, jsondata); // Build updater
_analyze_fields(dbfields, params, jsondata);
_validate_fields(dbfields, params, jsondata);
dbfields["id"] = { dbs: "fin_factuur_key", typ: "key", seq: "fin_s_fin_factuur_key" };
var finIns = buildInsert("fin_factuur", dbfields, { noValidateToken: true} );
var factuur_key = finIns.sequences["fin_factuur_key"];
var err = Oracle.Execute(finIns.sql, true);
if (err.friendlyMsg)
abort_with_warning(err.friendlyMsg);
// factuurregels toevoegen
var fintrack = api2.process_includes(params, this, jsondata, factuur_key);
shared.trackaction("FINNEW", factuur_key);
// factuurmatching
var sql_fm = "BEGIN fin.autoapprovefactuur(" + factuur_key + "); END;";
Oracle.Execute(sql_fm);
return { key: factuur_key };
};
this.REST_DELETE = function (params, the_key) /* delete invoice */
{
var factuur_key = the_key;
var this_fin = fin.func_enabled_factuur(factuur_key);
user.auth_required_or_abort(this_fin.canDelete);
if (this_fin.canDelete) // Dubbel op: bij canDelete=false komt hij hier niet meer.
{
// Verwijderdatum van de factuur zetten en niet fysiek verwijderen
var sql = "UPDATE fin_factuur"
+ " SET fin_factuur_verwijder = SYSDATE"
+ " WHERE fin_factuur_verwijder IS NULL"
+ " AND fin_factuur_key = " + factuur_key;
oRs = Oracle.Execute(sql);
}
};
if (fin_key > 0)
{
params.filter = params.filter || {};
params.filter.id = fin_key;
if (! ("include" in params) )
params.include = { include: ["custom_fields"]};
var xxx_array = this.REST_GET(params);
if (!xxx_array.length)
shared.record_not_found();
this.data = xxx_array[0];
}
}
%>