feat: Add Admin Groups Management Module

- Introduced a new module for managing user groups (`usrgrp`) in the administration interface.
- Implemented CRUD operations for user groups: list, create, rename, and delete.
- Added authorization checks to ensure only users with appropriate permissions can manage groups.
- Created corresponding frontend JavaScript and SCSS files for the new module.
- Updated documentation to reflect the new group management functionality and its authorization model.
- Added SQL stored procedures for group management operations.
- Established a new login script for database access, ensuring proper security practices.
This commit is contained in:
Stefan
2026-09-16 09:35:31 +02:00
parent 1a5ac73f3c
commit 8bcec409c5
59 changed files with 1283 additions and 88 deletions
@@ -0,0 +1,132 @@
using Microsoft.AspNetCore.Mvc;
using OCORE.SQL;
using static OCORE.commons;
using static OCORE.OCORE_dictionaries;
using static OCORE.SQL.sql;
using static OCORE.web.mvc_helper_async;
namespace Fuchs.Controllers;
// Partial class: Administration user-group management module (fn=usrgrp).
//
// Split out as its own module (rather than folded into the usr module) per
// Docs/Decisions/0021-user-group-management.md, following the pattern
// Docs/Decisions/0015-split-admin-user-management-module.md already established for adding a new
// admin-only capability. Shares the Admin/usr modules' fds_sys > 4 gate (AdminMinAuthExclusive,
// GetSystemAdminAuthAsync, AdminJson — all in IntranetController.Admin.cs).
//
// The fis_admin_* stored procs/functions called below enforce the SAME SEPARATE, independent
// authorization axis as the usr module: the per-user "intranet_admin" setting via
// fis_admin_getUserAuth(@authaccount) > 1. An account with fds_sys > 4 but no intranet_admin
// setting will reach these endpoints yet get empty results / no-op writes from SQL — accounts
// doing group management need BOTH.
public partial class IntranetController
{
private async Task<IActionResult> Do_Process_AdminGroups(string fn, string id, string code)
{
_logger.LogDebug("Do_Process_AdminGroups action={Action} code={Code} user={User}", id, code, UserAccountID);
int authLevel = await GetSystemAdminAuthAsync(fn, id, code);
bool authorized = authLevel > AdminMinAuthExclusive;
if (id.Equals("auth", StringComparison.OrdinalIgnoreCase))
return await JSONAsync(new { manage = authorized ? 1 : 0, level = authLevel });
if (!authorized)
{
_logger.LogWarning("AdminGroups access denied for user={User} (fds_sys={Level}) action={Action}",
UserAccountID, authLevel, id);
return Unauthorized401();
}
switch (id.ToLowerInvariant())
{
case "list":
{
var res = await getSQLDatatable_async(
"SELECT * FROM [dbo].[fis_admin_getUserGroupList](@authaccount, NULL) ORDER BY [order];",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@authaccount", UserAccountID)),
Security: DbSec, options: SqlOpt(fn, id, code));
return AdminJson(new { groups = res.DataTable.toArrayofObjectDictionaries() });
}
case "create":
case "rename":
{
if (!HasForm("name")) return BadRequest400();
string name = Form("name");
string? groupId = id.Equals("rename", StringComparison.OrdinalIgnoreCase) ? Form("usergroup_id") : null;
if (id.Equals("rename", StringComparison.OrdinalIgnoreCase) && string.IsNullOrWhiteSpace(groupId))
return BadRequest400();
var pl = StdParamlist(
SQL_VarChar("@authaccount", UserAccountID),
SQL_VarChar("@usergroup_id", groupId, dbNull_IfEmpty: true),
SQL_NVarChar("@name", name));
System.Data.DataRow? savedRow;
try
{
var res = await getSQLDatatable_async(
"EXECUTE [dbo].[fis_admin_setUserGroup] @authaccount, @usergroup_id, @name, @enc_key;",
_intranet.Intranet__SQLConnectionString, pl,
Security: DbSec, options: SqlOpt(fn, id, code));
savedRow = res.Count > 0 ? res.FirstRow : null;
}
catch (Exception ex)
{
await NotifyAdminUserActionFailedAsync("Gruppe speichern fehlgeschlagen",
"Eine Benutzergruppe konnte nicht gespeichert werden.", groupId ?? "", ex);
return StatusCode(500, new { error = "save_failed" });
}
if (savedRow == null)
{
_logger.LogWarning("AdminGroups {Action} not successful group={Group} user={User}", id, groupId, UserAccountID);
return StatusCode(500, new { error = "save_failed" });
}
_logger.LogInformation("AdminGroups {Action} succeeded group={Group} user={User}",
id, savedRow.nz("usergroup_id"), UserAccountID);
return AdminJson(new { ok = true, usergroup_id = savedRow.nz("usergroup_id"), name = savedRow.nz("name") });
}
case "delete":
{
if (!HasForm("usergroup_id")) return BadRequest400();
string groupId = Form("usergroup_id");
bool success;
try
{
var res = await getSQLDatatable_async(
"EXECUTE [dbo].[fis_admin_delUserGroup] @authaccount, @usergroup_id;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@authaccount", UserAccountID), SQL_VarChar("@usergroup_id", groupId)),
Security: DbSec, options: SqlOpt(fn, id, code));
success = res.Count > 0 && res.FirstRow[0] is true;
}
catch (Exception ex)
{
await NotifyAdminUserActionFailedAsync("Gruppe löschen fehlgeschlagen",
$"Die Benutzergruppe {groupId} konnte nicht gelöscht werden.", groupId, ex);
return StatusCode(500, new { error = "delete_failed" });
}
if (!success)
{
_logger.LogWarning("AdminGroups delete not successful group={Group} user={User}", groupId, UserAccountID);
return StatusCode(500, new { error = "delete_failed" });
}
_logger.LogInformation("AdminGroups delete succeeded group={Group} user={User}", groupId, UserAccountID);
return AdminJson(new { ok = true });
}
default:
_logger.LogWarning("AdminGroups: no handler for action={Action}, user={User}", id, UserAccountID);
return BadRequest400();
}
}
}
@@ -1,4 +1,4 @@
using System.Web;
using System.Web;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;
using OCORE.SQL;
@@ -44,10 +44,16 @@ public partial class IntranetController
{
case "list":
{
// Optional useraccount_id narrows the result to a single account so the frontend can
// refresh one row in place (e.g. after an email or group-membership change) instead
// of re-rendering the whole grid and losing the current selection.
string? filterId = HasForm("useraccount_id") ? Form("useraccount_id") : null;
var res = await getSQLDatatable_async(
"SELECT * FROM [dbo].[fis_admin_getUserAccountList](@authaccount, NULL, NULL, NULL) ORDER BY [order];",
"SELECT * FROM [dbo].[fis_admin_getUserAccountList](@authaccount, @useraccount_id, NULL, NULL) ORDER BY [order];",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@authaccount", UserAccountID)),
StdParamlist(
SQL_VarChar("@authaccount", UserAccountID),
SQL_VarChar("@useraccount_id", filterId, dbNull_IfEmpty: true)),
Security: DbSec, options: SqlOpt(fn, id, code));
return AdminJson(new { users = res.DataTable.toArrayofObjectDictionaries() });
}
+1
View File
@@ -168,6 +168,7 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
"bam" => await Do_Process_Bankings(fn, id, code),
"admin" => await Do_Process_Admin(fn, id, code),
"usr" => await Do_Process_AdminUsers(fn, id, code),
"usrgrp" => await Do_Process_AdminGroups(fn, id, code),
"auth" => await HandleAuth(fn, id, code),
"spwc" => await HandleSendPasswordCode(fn, id, code),
"spw" => await HandleSendPassword(fn, id, code),
@@ -0,0 +1,93 @@
---
status: Active
lastUpdated: 2026-09-16
applyTo:
- "Fuchs/Controllers/IntranetController.AdminGroups.cs"
- "Fuchs/js/intranet/modules/fis.usrgrp*.js"
- "Fuchs/js/intranet/modules/fis.usrgrp.scss"
- "Fuchs/js/intranet/fis_main_menu.js"
- "Fuchs_Database/dbo/Functions/fis_admin_getUserGroupList.sql"
- "Fuchs_Database/dbo/Stored Procedures/fis_admin_setUserGroup.sql"
- "Fuchs_Database/dbo/Stored Procedures/fis_admin_delUserGroup.sql"
relatedDecisions:
- "0021-user-group-management.md"
- "0014-admin-user-management.md"
- "0015-split-admin-user-management-module.md"
---
# Group Management module (`usrgrp`)
## Summary
The **Gruppenverwaltung** module lets a privileged operator see every non-deleted usergroup,
create a new one, rename it, and delete it. It's the catalog-management counterpart to the `usr`
module's per-user "Gruppen" dialog, which toggles an individual user's membership in these groups
but cannot create/rename/delete the groups themselves. Same sidemenu-visibility and server-side
authorization gate as `usr`/`admin` (`fds_sys > 4`), own `fn=usrgrp`, own frontend bundle, own
controller partial. See [0021](../Decisions/0021-user-group-management.md) for why this is a
separate module rather than folded into `usr`.
## How it works
### Authorization (two independent axes — same as `usr`, read [0014](../Decisions/0014-admin-user-management.md) first)
1. **Menu visibility + `fds_sys` gate** — `fis_main_menu.js#addAdminMenuButtonsIfAuthorized`
checks `fds_sys > 4` once and, if authorized, adds the "Administration" sidemenu dropdown with
three entries: Benutzerverwaltung (`init:usr`), Gruppenverwaltung (`init:usrgrp`) and
Systemüberwachung (`init:admin`). Every `/do/usrgrp/*` endpoint re-checks `fds_sys > 4`
server-side (`Do_Process_AdminGroups`, reusing `AdminMinAuthExclusive`/`GetSystemAdminAuthAsync`
from `IntranetController.Admin.cs`) and 401s otherwise.
2. **Separate SQL-layer gate** — every `fis_admin_*` proc/function called below independently
checks the caller's `intranet_admin` setting via `fis_admin_getUserAuth(@authaccount) > 1`. An
account with `fds_sys > 4` but no `intranet_admin` setting reaches every `usrgrp` endpoint but
gets empty results / silent no-op writes — not an error. This also applies to
`fis_admin_addGroupMember`/`remGroupMember` (used by the `usr` module's membership dialog,
documented there) — all five group-related `fis_admin_*` write/read objects now carry this
check as of [0021](../Decisions/0021-user-group-management.md), which closed a real gap where
three of them had none.
### Request flow
```
click "Gruppenverwaltung" → $ocms.init('usrgrp')
POST /do/usrgrp/auth → { manage, level } (manage>0 ⇒ authorized)
load /web/fis.usrgrp.de.js + /web/fis.usrgrp.css
$ocms.usrgrp.init2() → init3() → renderLst()
POST /do/usrgrp/list → { groups: [...] } every non-deleted group
base ribbon (eM) → "Anlegen" (always available)
POST /do/usrgrp/create {name} → { ok, usergroup_id, name }
row click → per-row ribbon (iMn): Umbenennen / Löschen
POST /do/usrgrp/rename {usergroup_id, name} → { ok, usergroup_id, name }
POST /do/usrgrp/delete {usergroup_id} → { ok }
```
### Backend (`IntranetController.AdminGroups.cs`)
- `list` → `fis_admin_getUserGroupList(@authaccount, NULL)`, all non-deleted groups with
decrypted names.
- `create`/`rename` → both call `fis_admin_setUserGroup(@authaccount, @usergroup_id, @name,
@enc_key)`; `create` passes `@usergroup_id = NULL` (the proc generates a new id via
`fis_fn_id`), `rename` passes the existing id.
- `delete` → `fis_admin_delUserGroup(@authaccount, @usergroup_id)` (soft-delete: sets
`UserDeleted`/`DateDeleted`). No check for remaining members — a deleted group's membership
rows simply stop being returned by `fis_admin_getUserGroupMembership` (filtered on the group's
own `DateDeleted`), see [0021](../Decisions/0021-user-group-management.md) Consequences.
### Frontend (`fis.usrgrp.js` + `fis.usrgrp_txt_de.js` + `fis.usrgrp.scss`)
Same list+ribbon pattern as `fis.usr.js`: `renderLst` builds a single-column card-style row list;
a row click toggles `.selected` and swaps the `#topbar` ribbon between the base ribbon (`eM`:
"Anlegen") and the per-row actions (`iMn`: Umbenennen/Löschen). `createDlg`/`renameDlg` use
`$ocms.dlgform`; `deleteGroup` follows the `confirm(...)`-before-destructive-action pattern (no
dialog, nothing to collect beyond the confirmation).
## Key files
- `Fuchs/Controllers/IntranetController.AdminGroups.cs` — `Do_Process_AdminGroups` dispatch.
- `Fuchs/Controllers/IntranetController.Admin.cs` — shared `AdminMinAuthExclusive`/
`GetSystemAdminAuthAsync`/`AdminJson` this module reuses.
- `Fuchs/js/intranet/modules/fis.usrgrp.js`, `fis.usrgrp_txt_de.js`, `fis.usrgrp.scss` — the
module, texts, styles (bundled via `bdlconfig.json`, `intranet:usrgrp` context).
- `Fuchs/js/intranet/fis_main_menu.js` (`addAdminMenuButtonsIfAuthorized`) — sidemenu wiring.
- `Fuchs_Database/dbo/Functions/fis_admin_getUserGroupList.sql` — read-side group query.
- `Fuchs_Database/dbo/Stored Procedures/fis_admin_setUserGroup.sql`,
`fis_admin_delUserGroup.sql` — write-side group procs.
## Related decisions
- [0021 — User group management (new module, closed the missing intranet_admin gate)](../Decisions/0021-user-group-management.md)
- [0014 — Admin user management (dual auth axis, original decisions)](../Decisions/0014-admin-user-management.md)
- [0015 — Split Admin into separate Status/User-Management modules](../Decisions/0015-split-admin-user-management-module.md)
+18 -6
View File
@@ -1,6 +1,6 @@
---
status: Active
lastUpdated: 2026-09-14
lastUpdated: 2026-09-16
applyTo:
- "Fuchs/Controllers/IntranetController.AdminUsers.cs"
- "Fuchs/js/intranet/modules/fis.usr*.js"
@@ -12,6 +12,7 @@ relatedDecisions:
- "0014-admin-user-management.md"
- "0015-split-admin-user-management-module.md"
- "0020-user-management-create-and-delete.md"
- "0021-user-group-management.md"
---
# User Management module (`usr`)
@@ -51,7 +52,8 @@ click "Benutzerverwaltung" → $ocms.init('usr')
POST /do/usr/auth → { manage, level } (manage>0 ⇒ authorized)
load /web/fis.usr.de.js + /web/fis.usr.css
$ocms.usr.init2() → init3() → renderLst()
POST /do/usr/list → { users: [...] } grid of every non-deleted account
POST /do/usr/list → { users: [...] } grid of every non-deleted account (incl. `groups` csv)
POST /do/usr/list {useraccount_id} → { users: [one] } single-row refresh (refreshRow)
base ribbon (eM) → "Neuer Benutzer" (always available, not just when a row is selected)
POST /do/usr/create {name, firstname, email, title?, gender?, mobile?} → { ok, useraccount_id, emailed } | 409 duplicate_email
row click → per-row ribbon (iMn): Passwort zurücksetzen / E-Mail ändern / Gruppenzugehörigkeit / Benutzer löschen
@@ -63,8 +65,12 @@ click "Benutzerverwaltung" → $ocms.init('usr')
```
### Backend (`IntranetController.AdminUsers.cs`)
- `list` → `fis_admin_getUserAccountList(@authaccount, NULL, NULL, NULL)`, all non-deleted
accounts with decrypted name/email/disabled flag.
- `list` → `fis_admin_getUserAccountList(@authaccount, @useraccount_id, NULL, NULL)`, all non-deleted
accounts with decrypted name/email/disabled flag plus a `groups` column — a `', '`-separated,
alphabetically ordered csv of the account's active group memberships (`STRING_AGG` over
decrypted `fis_usergroups.name_enc`, deleted groups/memberships excluded). An optional
`useraccount_id` form field narrows the result to one account so the frontend can refresh a
single row without re-rendering the grid.
- `pwdreset` → the admin may type a specific password or leave it blank; validated with a plain
`ValidatePassword(minLength: 6, numUpper: 0, numLower: 0, numNumbers: 0, numSpecial: 0)` — no
hidden composition requirements, matching what the UI actually tells the admin (a prior bug
@@ -82,7 +88,9 @@ click "Benutzerverwaltung" → $ocms.init('usr')
changed (`@password = NULL`, so the `WHEN MATCHED` update path leaves `password_enc` alone). A
duplicate email (`THROW 60001` in the proc) surfaces as HTTP 409 `{ error: "duplicate_email" }`.
- `groups` → the new `fis_admin_getUserGroupMembership(@authaccount, @useraccount_id)` function
returns every group plus a `member` bit for that one user.
returns every group plus a `member` bit for that one user. The group catalog itself (create/
rename/delete) is managed in the separate `usrgrp` module — see
[admin-group-management.md](admin-group-management.md).
- `groupset` → calls `fis_admin_addGroupMember`/`remGroupMember` depending on the posted
`member` flag — applied immediately per checkbox toggle, no separate save step.
- `create` → `fis_admin_createUserAccount(@authaccount, @name, @firstname, @title, @gender,
@@ -101,7 +109,11 @@ click "Benutzerverwaltung" → $ocms.init('usr')
### Frontend (`fis.usr.js` + `fis.usr_txt_de.js` + `fis.usr.scss`)
A self-contained list+ribbon module modeled on Banking (`fis.bam.js`)'s `eM`/`iMn` pattern
rather than the Admin dashboard's card layout: `renderLst` builds a card-style row grid (avatar
initials badge, name/email columns, a status pill) rather than a plain `<table>`; a row click
initials badge, name/email/groups columns, a status pill) rather than a plain `<table>`, with
each row produced by `buildRow(rw)`. Changes that only affect one account (`emailDlg`, every
checkbox toggle in `groupsDlg`) call `refreshRow(rw)`, which re-posts `usr/list` with that
`useraccount_id` and swaps the row in place — selection and the per-row ribbon survive, and the
groups csv is always current. Create/delete/password-reset still do a full `renderLst`. A row click
toggles `.selected` and swaps the `#topbar` ribbon between the base ribbon (`eM`: "Neuer
Benutzer" always available, plus "Neu Laden" once a row was selected) and the four per-row
actions (`iMn`: Passwort zurücksetzen / E-Mail ändern / Gruppenzugehörigkeit / Benutzer löschen).
@@ -0,0 +1,88 @@
---
status: Accepted
date: 2026-09-16
applyTo:
- "Fuchs/Controllers/IntranetController.AdminGroups.cs"
- "Fuchs/js/intranet/modules/fis.usrgrp*.js"
- "Fuchs/js/intranet/modules/fis.usrgrp.scss"
- "Fuchs/js/intranet/fis_main_menu.js"
- "Fuchs_Database/dbo/Functions/fis_admin_getUserGroupList.sql"
- "Fuchs_Database/dbo/Stored Procedures/fis_admin_setUserGroup.sql"
- "Fuchs_Database/dbo/Stored Procedures/fis_admin_delUserGroup.sql"
- "Fuchs_Database/dbo/Stored Procedures/fis_admin_addGroupMember.sql"
- "Fuchs_Database/dbo/Stored Procedures/fis_admin_remGroupMember.sql"
supersededBy: ""
---
# 0021 — User group management: new `usrgrp` module, close the missing `intranet_admin` gate on group writes
## Context
The per-user "Gruppen" dialog in the `usr` module ([0014](0014-admin-user-management.md),
[0015](0015-split-admin-user-management-module.md)) lets an admin toggle an existing user's
membership in each usergroup, but there was no UI anywhere to create, rename or delete a
usergroup itself — `fis_usergroups` could only be populated by running a raw SQL script by hand
(`hlp/db/create user group.sql`). The DB-layer proc `fis_admin_setUserGroup` (create/rename) and
the read-side `fis_admin_getUserGroupList` already existed but were wired to no controller
endpoint, mirroring the exact gap [0020](0020-user-management-create-and-delete.md) closed for
user accounts.
While wiring this up, three of the four `fis_admin_*` group-write procedures were found missing
the module's standard `intranet_admin` authorization check entirely (`fis_admin_addGroupMember`,
`fis_admin_remGroupMember`, `fis_admin_delUserGroup`) — a caller with `fds_sys > 4` but no
`intranet_admin` setting could add/remove a user's group membership or delete a group outright,
the same real authorization hole 0020 fixed for `fis_admin_delUserAccount`. `fis_admin_setUserGroup`
already had the check.
## Decision
- **New standalone module, `fn=usrgrp`** (`IntranetController.AdminGroups.cs`,
`fis.usrgrp.js`/`fis.usrgrp_txt_de.js`/`fis.usrgrp.scss`), added as a third entry in the existing
"Administration" sidemenu dropdown alongside Benutzerverwaltung and Systemüberwachung — per
[0015](0015-split-admin-user-management-module.md)'s own stated consequence that any future
admin-only feature should default to its own module under that dropdown rather than be folded
into an existing one. The dropdown itself already stays visible while any of its children is
open, so switching between Gruppenverwaltung and Benutzerverwaltung needs no dedicated "back"
control.
- **Same dual-authorization gate as every other module under this dropdown**: `fds_sys > 4`
server-side (`AdminMinAuthExclusive`/`GetSystemAdminAuthAsync`, shared from
`IntranetController.Admin.cs`) gates whether the module is reachable/visible at all;
`fis_admin_getUserAuth(@authaccount) > 1` (the `intranet_admin` setting) independently gates
every read/write the `fis_admin_*` procs perform. Both together are what "only admins can see
and change groups" means in this codebase — see the Consequences below for why this is enforced
in four places, not one.
- **New `id` cases in `Do_Process_AdminGroups`**: `list` → `fis_admin_getUserGroupList`, `create`/
`rename` → `fis_admin_setUserGroup` (same proc; `rename` supplies an existing `@usergroup_id`),
`delete` → `fis_admin_delUserGroup`. No membership editing here — that stays in the `usr`
module's existing per-user checkbox dialog, unchanged.
- **Closed the missing `intranet_admin` gate** on `fis_admin_addGroupMember`,
`fis_admin_remGroupMember` and `fis_admin_delUserGroup` by adding the same
`fis_admin_getUserAuth(@authaccount) > 1` guard `fis_admin_setUserGroup` already had, following
the same "no-op instead of error" style 0020 used for `fis_admin_delUserAccount`: a caller
without `intranet_admin >= 2` gets an unmodified result set / a `false` success bit, not a SQL
error, consistent with every other `fis_admin_*` proc in this area.
- **Frontend** mirrors `fis.usr.js`'s list+ribbon pattern (`renderLst`/`eM`/`iMn`): a card-style
row list with a base ribbon button ("Anlegen") and a per-row ribbon ("Umbenennen"/"Löschen") on
row selection, using `$ocms.dlgform` for create/rename and a `confirm(...)` guard (no dialog) for
delete — the same choice 0020 made for deleting a user account.
## Consequences
- Deleting a group does **not** guard against it still having members — its `fis_usergroups_members`
rows simply stop being returned (the membership query already filters on the group's own
`DateDeleted`). No UI currently surfaces "this group has N members" before deletion; acceptable
since deletion is a soft-delete and the group row can be recreated with the same effective
membership restored via `fis_admin_addGroupMember` if this turns out to matter in practice.
- Four separate `fis_admin_*` procs/functions now each independently re-check
`intranet_admin > 1` (`getUserGroupList`, `setUserGroup`, `delUserGroup`, `addGroupMember`,
`remGroupMember` — five, in fact) rather than a single shared gate; this is the established
style in this codebase (every `fis_admin_*` object re-checks for itself, see 0014) and not
something this change revisits.
- Same operational note as 0014/0015/0020: an account needs **both** `fds_sys > 4` and
`intranet_admin >= 2` to actually manage groups, not obvious from the UI.
## Alternatives considered
- **Extend the existing per-user "Gruppen" dialog** with an inline "new group" input and a
delete icon per row, instead of a new module. Rejected per explicit user direction — the user
asked for a dedicated sidemenu entry for group management, separate from the per-user dialog.
- **Fold into the `usr` module** as a second ribbon button. Rejected for the same reason 0015
split `usr` off `admin` in the first place: two unrelated concerns (managing individual user
accounts vs. managing the group catalog) behind one button was already identified as a mistake
once it happened once; the "own module under the dropdown" default from 0015 applies directly.
+19
View File
@@ -191,5 +191,24 @@
"js/intranet/modules/fis.usr.scss"
],
"minify": { "enabled": true }
},
{
"context": "intranet:usrgrp",
"outputFileName": "wwwroot/web/fis.usrgrp.de.js",
"inputFiles": [
"js/intranet/modules/fis.usrgrp_txt_de.js",
"js/intranet/modules/fis.usrgrp.js"
],
"minify": { "enabled": true }
},
{
"context": "intranet:usrgrp",
"outputFileName": "wwwroot/web/fis.usrgrp.min.css",
"inputFiles": [
"css/intranet/oci_variables.scss",
"css/intranet/fis_variables.scss",
"js/intranet/modules/fis.usrgrp.scss"
],
"minify": { "enabled": true }
}
]
+5 -4
View File
@@ -12,10 +12,10 @@
})();
/* "Administration" is a single sidemenu dropdown (ocmsmenu supports nested items via `itm`)
containing two buttons — Benutzerverwaltung (fn=usr) and Systemüberwachung (fn=admin) — both
restricted to users whose fds_sys authorization exceeds 4. One auth check gates the whole
dropdown; it (and therefore both module scripts) is only added after that check resolves, so
lower-privileged users never see it or fetch either module's code.
containing three buttons — Benutzerverwaltung (fn=usr), Gruppenverwaltung (fn=usrgrp) and
Systemüberwachung (fn=admin) — all restricted to users whose fds_sys authorization exceeds 4.
One auth check gates the whole dropdown; it (and therefore all module scripts) is only added
after that check resolves, so lower-privileged users never see it or fetch any module's code.
Called from fis_main_go.js once the DOM (and the initial main menu) is ready. */
$fis.addAdminMenuButtonsIfAuthorized = function () {
if ($('#m_admin_grp').length > 0) { return; } // idempotent
@@ -25,6 +25,7 @@ $fis.addAdminMenuButtonsIfAuthorized = function () {
lbl: $t.m_admin, id: 'm_admin_grp', ico: 'glyphicon glyphicon-cog',
itm: [
{ lbl: $t.m_usr, id: 'm_usr', fnc: 'init:usr', ico: 'glyphicon glyphicon-user' },
{ lbl: $t.m_usrgrp, id: 'm_usrgrp', fnc: 'init:usrgrp', ico: 'glyphicon glyphicon-list' },
{ lbl: $t.m_admin_status, id: 'm_admin', fnc: 'init:admin', ico: 'glyphicon glyphicon-dashboard' }
]
});
+1
View File
@@ -7,6 +7,7 @@
, m_admin: 'Administration'
, m_admin_status: 'Systemüberwachung'
, m_usr: 'Benutzerverwaltung'
, m_usrgrp: 'Gruppenverwaltung'
, rsp: 'Passwort ändern'
, pnm: 'Die Passwörter stimmen nicht überein'
, cps: 'Das neue Passwort wurde gespeichert.'
+38 -15
View File
@@ -30,6 +30,40 @@ let $usr = {
let f = (firstname || '').trim().charAt(0), n = (name || '').trim().charAt(0);
return (f + n).toUpperCase() || '?';
},
/* Builds one (detached) grid row for a user record; shared by renderLst and refreshRow. */
buildRow: function (rw) {
let disabled = bool(rw.disabled, false) === true;
let row = $$.dc('usr_row', { id: 'usr' + rw.useraccount_id });
$$.dc('usr_avatar', row).text($usr.initials(rw.firstname, rw.name));
$$.dc('usr_col_name', row).text(`${rw.firstname || ''} ${rw.name || ''}`.trim());
$$.dc('usr_col_email', row).text(rw.email || '');
$$.dc('usr_col_groups', row).text(rw.groups || '').attr('title', rw.groups || '');
$$.dc('usr_col_status', row).append(
$$.dc(disabled ? 'usr_pill usr_pill_off' : 'usr_pill usr_pill_on').text(disabled ? $uct.disabled : $uct.active)
);
row.click(function () {
row.toggleClass('selected').siblings().rC('selected');
if (row.is('.selected') === true) { $usr.iMn(rw); } else { $usr.eM(true); }
});
return row;
},
/* Re-fetches a single user and swaps its grid row in place, keeping selection + ribbon
(used after changes that only affect that one row: email, group membership). Falls back
to a full reload when the row is no longer in the grid. */
refreshRow: function (rw) {
let old = $('#usr' + rw.useraccount_id);
if (old.length === 0) { $usr.renderLst(); return; }
$ocms.postXT({
url: $ocms.url('usr/list'), data: { useraccount_id: rw.useraccount_id }, success: function (r) {
let fresh = (r.users || [])[0];
if (!fresh) { $usr.renderLst(); return; }
let selected = old.is('.selected');
let row = $usr.buildRow(fresh).toggleClass('selected', selected);
old.replaceWith(row);
if (selected) { $usr.iMn(fresh); }
}, error: function () { $usr.renderLst(); }
});
},
renderLst: function () {
let usrlst = $fis.frm_list(true, true).ldng(1), usrfrm = $$.dc('usrfrm', usrlst);
$usr.eM(true);
@@ -41,25 +75,13 @@ let $usr = {
$$.dc('usr_col_avatar', hdr);
$$.dc('usr_col_name', hdr).text($uct.col_name);
$$.dc('usr_col_email', hdr).text($uct.col_email);
$$.dc('usr_col_groups', hdr).text($uct.col_groups);
$$.dc('usr_col_status', hdr).text($uct.col_status);
if ((r.users || []).length === 0) {
$$.dc('usr_empty', grid).text($uct.empty);
return;
}
$.each(r.users || [], function (ri, rw) {
let disabled = bool(rw.disabled, false) === true;
let row = $$.dc('usr_row', grid, { id: 'usr' + rw.useraccount_id });
$$.dc('usr_avatar', row).text($usr.initials(rw.firstname, rw.name));
$$.dc('usr_col_name', row).text(`${rw.firstname || ''} ${rw.name || ''}`.trim());
$$.dc('usr_col_email', row).text(rw.email || '');
$$.dc('usr_col_status', row).append(
$$.dc(disabled ? 'usr_pill usr_pill_off' : 'usr_pill usr_pill_on').text(disabled ? $uct.disabled : $uct.active)
);
row.click(function () {
row.toggleClass('selected').siblings().rC('selected');
if (row.is('.selected') === true) { $usr.iMn(rw); } else { $usr.eM(true); }
});
});
$.each(r.users || [], function (ri, rw) { grid.append($usr.buildRow(rw)); });
}, error: function () {
usrfrm.empty();
$$.dc('usr_err', usrfrm).text($uct.loadfail);
@@ -142,7 +164,7 @@ let $usr = {
}, error: function (jqXHR) {
let code = ((jqXHR || {}).responseJSON || {}).error;
alert($uct.err[code] || $uct.err_generic);
}, complete: function () { c.ldng(0); $usr.renderLst(); }
}, complete: function () { c.ldng(0); $usr.refreshRow(rw); }
});
}
});
@@ -161,6 +183,7 @@ let $usr = {
$ocms.postXT({
url: $ocms.url('usr/groupset'),
data: { useraccount_id: rw.useraccount_id, usergroup_id: g.usergroup_id, member: checked },
success: function () { $usr.refreshRow(rw); }, /* keep the groups csv in the grid current per toggle */
error: function () {
cb.prop('checked', !checked);
alert($uct.err_generic);
+5 -2
View File
@@ -11,7 +11,7 @@
.usr_row {
display: grid;
grid-template-columns: 2.75rem 1.6fr 1.8fr 8rem;
grid-template-columns: 2.75rem 1.4fr 1.6fr 1.6fr 8rem;
align-items: center;
gap: 0.75rem;
padding: 0.55rem 1rem;
@@ -57,6 +57,8 @@
.usr_col_name { font-weight: 600; color: #222; }
.usr_col_email { color: #555; overflow-wrap: anywhere; }
.usr_col_groups { color: #555; font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.usr_row_hd .usr_col_groups { font-size: inherit; }
.usr_pill {
display: inline-block;
@@ -86,11 +88,12 @@
.usrfrm {
padding: 0.75rem;
.usr_row { grid-template-columns: 2.25rem 1fr; grid-template-areas: "avatar name" "avatar email" "avatar status"; }
.usr_row { grid-template-columns: 2.25rem 1fr; grid-template-areas: "avatar name" "avatar email" "avatar groups" "avatar status"; }
.usr_row_hd { display: none; }
.usr_avatar { grid-area: avatar; }
.usr_col_name { grid-area: name; }
.usr_col_email { grid-area: email; font-size: 0.85rem; }
.usr_col_groups { grid-area: groups; font-size: 0.85rem; white-space: normal; }
.usr_col_status { grid-area: status; }
}
}
@@ -3,6 +3,7 @@ let $uct = {
, rel: 'Neu Laden'
, col_name: 'Name'
, col_email: 'E-Mail'
, col_groups: 'Gruppen'
, col_status: 'Status'
, active: 'Aktiv'
, disabled: 'Deaktiviert'
+107
View File
@@ -0,0 +1,107 @@
let $ugp = {
init2: function () {
$ocms.getScript([], function () { $ugp.init3(); });
},
init3: function () {
$fis.cf(true);
$fis.lf(true);
$('#activemodule').text($ugt.mdl);
$ugp.eM();
$ugp.renderLst();
},
/* Default ribbon (nothing selected). */
eM: function (r) {
let m = [{ lbl: $ugt.newgroup_btn, fnc: $ugp.createDlg }];
if (bool(r, false) === true) { m.push({ lbl: $ugt.rel, fnc: $ugp.renderLst }); }
return $('#topbar').ocmsmenu(m);
},
/* Per-row ribbon shown while a group row is selected. */
iMn: function (rw) {
return $('#topbar').ocmsmenu([
{ lbl: $ugt.rename, fnc: function () { $ugp.renameDlg(rw); } },
{ lbl: $ugt.delete, fnc: function () { $ugp.deleteGroup(rw); } }
]);
},
renderLst: function () {
let ugplst = $fis.frm_list(true, true).ldng(1), ugpfrm = $$.dc('ugpfrm', ugplst);
$ugp.eM(true);
$ocms.postXT({
url: $ocms.url('usrgrp/list'), success: function (r) {
ugpfrm.empty();
let grid = $$.dc('ugp_grid', ugpfrm);
let hdr = $$.dc('ugp_row ugp_row_hd', grid);
$$.dc('ugp_col_name', hdr).text($ugt.col_name);
if ((r.groups || []).length === 0) {
$$.dc('ugp_empty', grid).text($ugt.empty);
return;
}
$.each(r.groups || [], function (gi, rw) {
let row = $$.dc('ugp_row', grid, { id: 'ugp' + rw.usergroup_id });
$$.dc('ugp_col_name', row).text(rw.name || '');
row.click(function () {
row.toggleClass('selected').siblings().rC('selected');
if (row.is('.selected') === true) { $ugp.iMn(rw); } else { $ugp.eM(true); }
});
});
}, error: function () {
ugpfrm.empty();
$$.dc('ugp_err', ugpfrm).text($ugt.loadfail);
}, complete: function () { ugplst.ldng(0); }
});
},
createDlg: function () {
$ocms.dlgform([
{ name: 'name', label: $ugt.name_field, type: 'string', required: true }
], {
title: $ugt.newgroup,
button: $ugt.newgroup_btn,
submit: function (e) {
let c = $(this).ldng(1);
let qs = c.serializeObject(true, { typedvalues: true });
$ocms.postXT({
url: $ocms.url('usrgrp/create'), data: qs, success: function () {
c.trigger('modal_close');
}, error: function (jqXHR) {
let code = ((jqXHR || {}).responseJSON || {}).error;
alert($ugt.err[code] || $ugt.err_generic);
}, complete: function () { c.ldng(0); $ugp.renderLst(); }
});
}
});
},
renameDlg: function (rw) {
$ocms.dlgform([
{ name: 'usergroup_id', type: 'hidden', value: rw.usergroup_id },
{ name: 'name', label: $ugt.name_field, type: 'string', required: true, value: rw.name }
], {
title: $ugt.rename,
button: $ugt.rename_btn,
submit: function (e) {
let c = $(this).ldng(1);
let qs = c.serializeObject(true, { typedvalues: true });
$ocms.postXT({
url: $ocms.url('usrgrp/rename'), data: qs, success: function () {
c.trigger('modal_close');
}, error: function (jqXHR) {
let code = ((jqXHR || {}).responseJSON || {}).error;
alert($ugt.err[code] || $ugt.err_generic);
}, complete: function () { c.ldng(0); $ugp.renderLst(); }
});
}
});
},
deleteGroup: function (rw) {
if (confirm(string($ugt.delete_confirm, [rw.name || '']))) {
$ocms.postXT({
url: $ocms.url('usrgrp/delete'), data: { usergroup_id: rw.usergroup_id }, success: function () {
$ugp.eM(true);
}, error: function (jqXHR) {
let code = ((jqXHR || {}).responseJSON || {}).error;
alert($ugt.err[code] || $ugt.err_generic);
}, complete: function () { $ugp.renderLst(); }
});
}
}
};
let $$ugp = { init2: $ugp.init2, auth: {} };
export default $$ugp;
+58
View File
@@ -0,0 +1,58 @@
.ugpfrm {
padding: 1.5rem 2rem;
.ugp_grid {
border: 1px solid #e2e2e2;
border-radius: 0.5rem;
background: #fff;
box-shadow: 0 1px 3px rgba(50, 50, 50, 0.1);
overflow: hidden;
max-width: 32rem;
}
.ugp_row {
display: grid;
grid-template-columns: 1fr;
align-items: center;
padding: 0.55rem 1rem;
border-bottom: 1px solid #eee;
&:last-child { border-bottom: none; }
}
.ugp_row_hd {
background: $fuchs_blau;
color: #fff;
font-weight: bold;
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.02em;
padding: 0.5rem 1rem;
}
.ugp_row:not(.ugp_row_hd) {
cursor: pointer;
transition: background-color 0.12s ease-in-out;
&:hover { background-color: #f3f6fb; }
&.selected {
background-color: rgba($fuchs_akzent, 0.12);
box-shadow: inset 3px 0 0 $fuchs_akzent;
}
}
.ugp_col_name { font-weight: 600; color: #222; }
.ugp_empty {
padding: 1.5rem 1rem;
text-align: center;
color: #777;
}
.ugp_err {
padding: 1rem 2rem;
color: #b00020;
font-weight: bold;
}
}
@@ -0,0 +1,19 @@
let $ugt = {
mdl: 'Gruppenverwaltung'
, rel: 'Neu Laden'
, col_name: 'Name'
, loadfail: 'Die Gruppenliste konnte nicht geladen werden.'
, empty: 'Es sind keine Benutzergruppen vorhanden.'
, newgroup: 'Neue Gruppe'
, newgroup_btn: 'Anlegen'
, name_field: 'Gruppenname'
, rename: 'Gruppe umbenennen'
, rename_btn: 'Speichern'
, delete: 'Gruppe löschen'
, delete_confirm: 'Soll die Gruppe "{0}" wirklich gelöscht werden? Die Mitgliedschaft aller Benutzer in dieser Gruppe geht dabei verloren.'
, err: {
save_failed: 'Die Gruppe konnte nicht gespeichert werden.',
delete_failed: 'Die Gruppe konnte nicht gelöscht werden.'
}
, err_generic: 'Die Änderung konnte nicht gespeichert werden.'
};
+6 -4
View File
@@ -2845,6 +2845,7 @@ $.extend($t, {
, m_admin: 'Administration'
, m_admin_status: 'Systemüberwachung'
, m_usr: 'Benutzerverwaltung'
, m_usrgrp: 'Gruppenverwaltung'
, rsp: 'Passwort ändern'
, pnm: 'Die Passwörter stimmen nicht überein'
, cps: 'Das neue Passwort wurde gespeichert.'
@@ -3241,10 +3242,10 @@ $fis.draft = {
})();
/* "Administration" is a single sidemenu dropdown (ocmsmenu supports nested items via `itm`)
containing two buttons — Benutzerverwaltung (fn=usr) and Systemüberwachung (fn=admin) — both
restricted to users whose fds_sys authorization exceeds 4. One auth check gates the whole
dropdown; it (and therefore both module scripts) is only added after that check resolves, so
lower-privileged users never see it or fetch either module's code.
containing three buttons — Benutzerverwaltung (fn=usr), Gruppenverwaltung (fn=usrgrp) and
Systemüberwachung (fn=admin) — all restricted to users whose fds_sys authorization exceeds 4.
One auth check gates the whole dropdown; it (and therefore all module scripts) is only added
after that check resolves, so lower-privileged users never see it or fetch any module's code.
Called from fis_main_go.js once the DOM (and the initial main menu) is ready. */
$fis.addAdminMenuButtonsIfAuthorized = function () {
if ($('#m_admin_grp').length > 0) { return; } // idempotent
@@ -3254,6 +3255,7 @@ $fis.addAdminMenuButtonsIfAuthorized = function () {
lbl: $t.m_admin, id: 'm_admin_grp', ico: 'glyphicon glyphicon-cog',
itm: [
{ lbl: $t.m_usr, id: 'm_usr', fnc: 'init:usr', ico: 'glyphicon glyphicon-user' },
{ lbl: $t.m_usrgrp, id: 'm_usrgrp', fnc: 'init:usrgrp', ico: 'glyphicon glyphicon-list' },
{ lbl: $t.m_admin_status, id: 'm_admin', fnc: 'init:admin', ico: 'glyphicon glyphicon-dashboard' }
]
});
+1 -1
View File
File diff suppressed because one or more lines are too long
+17 -2
View File
@@ -28,7 +28,7 @@
}
.usrfrm .usr_row {
display: grid;
grid-template-columns: 2.75rem 1.6fr 1.8fr 8rem;
grid-template-columns: 2.75rem 1.4fr 1.6fr 1.6fr 8rem;
align-items: center;
gap: 0.75rem;
padding: 0.55rem 1rem;
@@ -77,6 +77,16 @@
color: #555;
overflow-wrap: anywhere;
}
.usrfrm .usr_col_groups {
color: #555;
font-size: 0.9rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.usrfrm .usr_row_hd .usr_col_groups {
font-size: inherit;
}
.usrfrm .usr_pill {
display: inline-block;
padding: 0.1rem 0.6rem;
@@ -110,7 +120,7 @@
}
.usrfrm .usr_row {
grid-template-columns: 2.25rem 1fr;
grid-template-areas: "avatar name" "avatar email" "avatar status";
grid-template-areas: "avatar name" "avatar email" "avatar groups" "avatar status";
}
.usrfrm .usr_row_hd {
display: none;
@@ -125,6 +135,11 @@
grid-area: email;
font-size: 0.85rem;
}
.usrfrm .usr_col_groups {
grid-area: groups;
font-size: 0.85rem;
white-space: normal;
}
.usrfrm .usr_col_status {
grid-area: status;
}
+39 -15
View File
@@ -3,6 +3,7 @@ let $uct = {
, rel: 'Neu Laden'
, col_name: 'Name'
, col_email: 'E-Mail'
, col_groups: 'Gruppen'
, col_status: 'Status'
, active: 'Aktiv'
, disabled: 'Deaktiviert'
@@ -75,6 +76,40 @@ let $usr = {
let f = (firstname || '').trim().charAt(0), n = (name || '').trim().charAt(0);
return (f + n).toUpperCase() || '?';
},
/* Builds one (detached) grid row for a user record; shared by renderLst and refreshRow. */
buildRow: function (rw) {
let disabled = bool(rw.disabled, false) === true;
let row = $$.dc('usr_row', { id: 'usr' + rw.useraccount_id });
$$.dc('usr_avatar', row).text($usr.initials(rw.firstname, rw.name));
$$.dc('usr_col_name', row).text(`${rw.firstname || ''} ${rw.name || ''}`.trim());
$$.dc('usr_col_email', row).text(rw.email || '');
$$.dc('usr_col_groups', row).text(rw.groups || '').attr('title', rw.groups || '');
$$.dc('usr_col_status', row).append(
$$.dc(disabled ? 'usr_pill usr_pill_off' : 'usr_pill usr_pill_on').text(disabled ? $uct.disabled : $uct.active)
);
row.click(function () {
row.toggleClass('selected').siblings().rC('selected');
if (row.is('.selected') === true) { $usr.iMn(rw); } else { $usr.eM(true); }
});
return row;
},
/* Re-fetches a single user and swaps its grid row in place, keeping selection + ribbon
(used after changes that only affect that one row: email, group membership). Falls back
to a full reload when the row is no longer in the grid. */
refreshRow: function (rw) {
let old = $('#usr' + rw.useraccount_id);
if (old.length === 0) { $usr.renderLst(); return; }
$ocms.postXT({
url: $ocms.url('usr/list'), data: { useraccount_id: rw.useraccount_id }, success: function (r) {
let fresh = (r.users || [])[0];
if (!fresh) { $usr.renderLst(); return; }
let selected = old.is('.selected');
let row = $usr.buildRow(fresh).toggleClass('selected', selected);
old.replaceWith(row);
if (selected) { $usr.iMn(fresh); }
}, error: function () { $usr.renderLst(); }
});
},
renderLst: function () {
let usrlst = $fis.frm_list(true, true).ldng(1), usrfrm = $$.dc('usrfrm', usrlst);
$usr.eM(true);
@@ -86,25 +121,13 @@ let $usr = {
$$.dc('usr_col_avatar', hdr);
$$.dc('usr_col_name', hdr).text($uct.col_name);
$$.dc('usr_col_email', hdr).text($uct.col_email);
$$.dc('usr_col_groups', hdr).text($uct.col_groups);
$$.dc('usr_col_status', hdr).text($uct.col_status);
if ((r.users || []).length === 0) {
$$.dc('usr_empty', grid).text($uct.empty);
return;
}
$.each(r.users || [], function (ri, rw) {
let disabled = bool(rw.disabled, false) === true;
let row = $$.dc('usr_row', grid, { id: 'usr' + rw.useraccount_id });
$$.dc('usr_avatar', row).text($usr.initials(rw.firstname, rw.name));
$$.dc('usr_col_name', row).text(`${rw.firstname || ''} ${rw.name || ''}`.trim());
$$.dc('usr_col_email', row).text(rw.email || '');
$$.dc('usr_col_status', row).append(
$$.dc(disabled ? 'usr_pill usr_pill_off' : 'usr_pill usr_pill_on').text(disabled ? $uct.disabled : $uct.active)
);
row.click(function () {
row.toggleClass('selected').siblings().rC('selected');
if (row.is('.selected') === true) { $usr.iMn(rw); } else { $usr.eM(true); }
});
});
$.each(r.users || [], function (ri, rw) { grid.append($usr.buildRow(rw)); });
}, error: function () {
usrfrm.empty();
$$.dc('usr_err', usrfrm).text($uct.loadfail);
@@ -187,7 +210,7 @@ let $usr = {
}, error: function (jqXHR) {
let code = ((jqXHR || {}).responseJSON || {}).error;
alert($uct.err[code] || $uct.err_generic);
}, complete: function () { c.ldng(0); $usr.renderLst(); }
}, complete: function () { c.ldng(0); $usr.refreshRow(rw); }
});
}
});
@@ -206,6 +229,7 @@ let $usr = {
$ocms.postXT({
url: $ocms.url('usr/groupset'),
data: { useraccount_id: rw.useraccount_id, usergroup_id: g.usergroup_id, member: checked },
success: function () { $usr.refreshRow(rw); }, /* keep the groups csv in the grid current per toggle */
error: function () {
cb.prop('checked', !checked);
alert($uct.err_generic);
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
.usrfrm{padding:1.5rem 2rem}.usrfrm .usr_grid{border:1px solid #e2e2e2;border-radius:.5rem;background:#fff;box-shadow:0 1px 3px rgba(50,50,50,.1);overflow:hidden}.usrfrm .usr_row{display:grid;grid-template-columns:2.75rem 1.6fr 1.8fr 8rem;align-items:center;gap:.75rem;padding:.55rem 1rem;border-bottom:1px solid #eee}.usrfrm .usr_row:last-child{border-bottom:none}.usrfrm .usr_row_hd{background:#1b4379;color:#fff;font-weight:700;font-size:.85rem;text-transform:uppercase;letter-spacing:.02em;padding:.5rem 1rem}.usrfrm .usr_row:not(.usr_row_hd){cursor:pointer;transition:background-color .12s ease-in-out}.usrfrm .usr_row:not(.usr_row_hd):hover{background-color:#f3f6fb}.usrfrm .usr_row:not(.usr_row_hd).selected{background-color:rgba(86,165,50,.12);box-shadow:inset 3px 0 0 #56a532}.usrfrm .usr_avatar{width:2.1rem;height:2.1rem;border-radius:50%;background:#1b4379;color:#fff;font-size:.78rem;font-weight:700;display:flex;align-items:center;justify-content:center}.usrfrm .usr_col_name{font-weight:600;color:#222}.usrfrm .usr_col_email{color:#555;overflow-wrap:anywhere}.usrfrm .usr_pill{display:inline-block;padding:.1rem .6rem;border-radius:1rem;font-size:.78rem;font-weight:700;white-space:nowrap}.usrfrm .usr_pill_on{background:#e2f3da;color:#2f6d18}.usrfrm .usr_pill_off{background:#ececec;color:#777}.usrfrm .usr_empty{padding:1.5rem 1rem;text-align:center;color:#777}.usrfrm .usr_err{padding:1rem 2rem;color:#b00020;font-weight:700}@media (max-width:640px){.usrfrm{padding:.75rem}.usrfrm .usr_row{grid-template-columns:2.25rem 1fr;grid-template-areas:"avatar name" "avatar email" "avatar status"}.usrfrm .usr_row_hd{display:none}.usrfrm .usr_avatar{grid-area:avatar}.usrfrm .usr_col_name{grid-area:name}.usrfrm .usr_col_email{grid-area:email;font-size:.85rem}.usrfrm .usr_col_status{grid-area:status}}.usr_groups_dlg{padding:.25rem 0}.usr_groups_dlg .usr_group_row{display:flex;align-items:center;gap:.5rem;padding:.35rem 0;border-bottom:1px solid #eee}.usr_groups_dlg .usr_group_row:last-child{border-bottom:none}.usr_groups_dlg .usr_group_row input[type=checkbox]{margin:0}.usr_groups_dlg .usr_group_row label{margin:0;font-weight:400}
.usrfrm{padding:1.5rem 2rem}.usrfrm .usr_grid{border:1px solid #e2e2e2;border-radius:.5rem;background:#fff;box-shadow:0 1px 3px rgba(50,50,50,.1);overflow:hidden}.usrfrm .usr_row{display:grid;grid-template-columns:2.75rem 1.4fr 1.6fr 1.6fr 8rem;align-items:center;gap:.75rem;padding:.55rem 1rem;border-bottom:1px solid #eee}.usrfrm .usr_row:last-child{border-bottom:none}.usrfrm .usr_row_hd{background:#1b4379;color:#fff;font-weight:700;font-size:.85rem;text-transform:uppercase;letter-spacing:.02em;padding:.5rem 1rem}.usrfrm .usr_row:not(.usr_row_hd){cursor:pointer;transition:background-color .12s ease-in-out}.usrfrm .usr_row:not(.usr_row_hd):hover{background-color:#f3f6fb}.usrfrm .usr_row:not(.usr_row_hd).selected{background-color:rgba(86,165,50,.12);box-shadow:inset 3px 0 0 #56a532}.usrfrm .usr_avatar{width:2.1rem;height:2.1rem;border-radius:50%;background:#1b4379;color:#fff;font-size:.78rem;font-weight:700;display:flex;align-items:center;justify-content:center}.usrfrm .usr_col_name{font-weight:600;color:#222}.usrfrm .usr_col_email{color:#555;overflow-wrap:anywhere}.usrfrm .usr_col_groups{color:#555;font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.usrfrm .usr_row_hd .usr_col_groups{font-size:inherit}.usrfrm .usr_pill{display:inline-block;padding:.1rem .6rem;border-radius:1rem;font-size:.78rem;font-weight:700;white-space:nowrap}.usrfrm .usr_pill_on{background:#e2f3da;color:#2f6d18}.usrfrm .usr_pill_off{background:#ececec;color:#777}.usrfrm .usr_empty{padding:1.5rem 1rem;text-align:center;color:#777}.usrfrm .usr_err{padding:1rem 2rem;color:#b00020;font-weight:700}@media (max-width:640px){.usrfrm{padding:.75rem}.usrfrm .usr_row{grid-template-columns:2.25rem 1fr;grid-template-areas:"avatar name" "avatar email" "avatar groups" "avatar status"}.usrfrm .usr_row_hd{display:none}.usrfrm .usr_avatar{grid-area:avatar}.usrfrm .usr_col_name{grid-area:name}.usrfrm .usr_col_email{grid-area:email;font-size:.85rem}.usrfrm .usr_col_groups{grid-area:groups;font-size:.85rem;white-space:normal}.usrfrm .usr_col_status{grid-area:status}}.usr_groups_dlg{padding:.25rem 0}.usr_groups_dlg .usr_group_row{display:flex;align-items:center;gap:.5rem;padding:.35rem 0;border-bottom:1px solid #eee}.usr_groups_dlg .usr_group_row:last-child{border-bottom:none}.usr_groups_dlg .usr_group_row input[type=checkbox]{margin:0}.usr_groups_dlg .usr_group_row label{margin:0;font-weight:400}
+73
View File
@@ -0,0 +1,73 @@
/* basics */
/* Color SCHEME */
/* #8396bd */
/* #e3e6e6 */
/* #ffffff */
/* #262626 */
/* #262626 */
/* #e3e6e6 */
/* #FFC801 */
/* #ffff4a */
/* media breaks */
/* other */
/* mixin */
/* basics */
/* Color SCHEME */
/* #1b4379 #0033b3 */
/*neu: #56a532 ; alt: rgb(32,144,119);*/
/* 10% sw */
.ugpfrm {
padding: 1.5rem 2rem;
}
.ugpfrm .ugp_grid {
border: 1px solid #e2e2e2;
border-radius: 0.5rem;
background: #fff;
box-shadow: 0 1px 3px rgba(50, 50, 50, 0.1);
overflow: hidden;
max-width: 32rem;
}
.ugpfrm .ugp_row {
display: grid;
grid-template-columns: 1fr;
align-items: center;
padding: 0.55rem 1rem;
border-bottom: 1px solid #eee;
}
.ugpfrm .ugp_row:last-child {
border-bottom: none;
}
.ugpfrm .ugp_row_hd {
background: rgb(27, 67, 121);
color: #fff;
font-weight: bold;
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.02em;
padding: 0.5rem 1rem;
}
.ugpfrm .ugp_row:not(.ugp_row_hd) {
cursor: pointer;
transition: background-color 0.12s ease-in-out;
}
.ugpfrm .ugp_row:not(.ugp_row_hd):hover {
background-color: #f3f6fb;
}
.ugpfrm .ugp_row:not(.ugp_row_hd).selected {
background-color: rgba(86, 165, 50, 0.12);
box-shadow: inset 3px 0 0 rgb(86, 165, 50);
}
.ugpfrm .ugp_col_name {
font-weight: 600;
color: #222;
}
.ugpfrm .ugp_empty {
padding: 1.5rem 1rem;
text-align: center;
color: #777;
}
.ugpfrm .ugp_err {
padding: 1rem 2rem;
color: #b00020;
font-weight: bold;
}
+127
View File
@@ -0,0 +1,127 @@
let $ugt = {
mdl: 'Gruppenverwaltung'
, rel: 'Neu Laden'
, col_name: 'Name'
, loadfail: 'Die Gruppenliste konnte nicht geladen werden.'
, empty: 'Es sind keine Benutzergruppen vorhanden.'
, newgroup: 'Neue Gruppe'
, newgroup_btn: 'Anlegen'
, name_field: 'Gruppenname'
, rename: 'Gruppe umbenennen'
, rename_btn: 'Speichern'
, delete: 'Gruppe löschen'
, delete_confirm: 'Soll die Gruppe "{0}" wirklich gelöscht werden? Die Mitgliedschaft aller Benutzer in dieser Gruppe geht dabei verloren.'
, err: {
save_failed: 'Die Gruppe konnte nicht gespeichert werden.',
delete_failed: 'Die Gruppe konnte nicht gelöscht werden.'
}
, err_generic: 'Die Änderung konnte nicht gespeichert werden.'
};
let $ugp = {
init2: function () {
$ocms.getScript([], function () { $ugp.init3(); });
},
init3: function () {
$fis.cf(true);
$fis.lf(true);
$('#activemodule').text($ugt.mdl);
$ugp.eM();
$ugp.renderLst();
},
/* Default ribbon (nothing selected). */
eM: function (r) {
let m = [{ lbl: $ugt.newgroup_btn, fnc: $ugp.createDlg }];
if (bool(r, false) === true) { m.push({ lbl: $ugt.rel, fnc: $ugp.renderLst }); }
return $('#topbar').ocmsmenu(m);
},
/* Per-row ribbon shown while a group row is selected. */
iMn: function (rw) {
return $('#topbar').ocmsmenu([
{ lbl: $ugt.rename, fnc: function () { $ugp.renameDlg(rw); } },
{ lbl: $ugt.delete, fnc: function () { $ugp.deleteGroup(rw); } }
]);
},
renderLst: function () {
let ugplst = $fis.frm_list(true, true).ldng(1), ugpfrm = $$.dc('ugpfrm', ugplst);
$ugp.eM(true);
$ocms.postXT({
url: $ocms.url('usrgrp/list'), success: function (r) {
ugpfrm.empty();
let grid = $$.dc('ugp_grid', ugpfrm);
let hdr = $$.dc('ugp_row ugp_row_hd', grid);
$$.dc('ugp_col_name', hdr).text($ugt.col_name);
if ((r.groups || []).length === 0) {
$$.dc('ugp_empty', grid).text($ugt.empty);
return;
}
$.each(r.groups || [], function (gi, rw) {
let row = $$.dc('ugp_row', grid, { id: 'ugp' + rw.usergroup_id });
$$.dc('ugp_col_name', row).text(rw.name || '');
row.click(function () {
row.toggleClass('selected').siblings().rC('selected');
if (row.is('.selected') === true) { $ugp.iMn(rw); } else { $ugp.eM(true); }
});
});
}, error: function () {
ugpfrm.empty();
$$.dc('ugp_err', ugpfrm).text($ugt.loadfail);
}, complete: function () { ugplst.ldng(0); }
});
},
createDlg: function () {
$ocms.dlgform([
{ name: 'name', label: $ugt.name_field, type: 'string', required: true }
], {
title: $ugt.newgroup,
button: $ugt.newgroup_btn,
submit: function (e) {
let c = $(this).ldng(1);
let qs = c.serializeObject(true, { typedvalues: true });
$ocms.postXT({
url: $ocms.url('usrgrp/create'), data: qs, success: function () {
c.trigger('modal_close');
}, error: function (jqXHR) {
let code = ((jqXHR || {}).responseJSON || {}).error;
alert($ugt.err[code] || $ugt.err_generic);
}, complete: function () { c.ldng(0); $ugp.renderLst(); }
});
}
});
},
renameDlg: function (rw) {
$ocms.dlgform([
{ name: 'usergroup_id', type: 'hidden', value: rw.usergroup_id },
{ name: 'name', label: $ugt.name_field, type: 'string', required: true, value: rw.name }
], {
title: $ugt.rename,
button: $ugt.rename_btn,
submit: function (e) {
let c = $(this).ldng(1);
let qs = c.serializeObject(true, { typedvalues: true });
$ocms.postXT({
url: $ocms.url('usrgrp/rename'), data: qs, success: function () {
c.trigger('modal_close');
}, error: function (jqXHR) {
let code = ((jqXHR || {}).responseJSON || {}).error;
alert($ugt.err[code] || $ugt.err_generic);
}, complete: function () { c.ldng(0); $ugp.renderLst(); }
});
}
});
},
deleteGroup: function (rw) {
if (confirm(string($ugt.delete_confirm, [rw.name || '']))) {
$ocms.postXT({
url: $ocms.url('usrgrp/delete'), data: { usergroup_id: rw.usergroup_id }, success: function () {
$ugp.eM(true);
}, error: function (jqXHR) {
let code = ((jqXHR || {}).responseJSON || {}).error;
alert($ugt.err[code] || $ugt.err_generic);
}, complete: function () { $ugp.renderLst(); }
});
}
}
};
let $$ugp = { init2: $ugp.init2, auth: {} };
export default $$ugp;
+1
View File
@@ -0,0 +1 @@
let $ugt={mdl:"Gruppenverwaltung",rel:"Neu Laden",col_name:"Name",loadfail:"Die Gruppenliste konnte nicht geladen werden.",empty:"Es sind keine Benutzergruppen vorhanden.",newgroup:"Neue Gruppe",newgroup_btn:"Anlegen",name_field:"Gruppenname",rename:"Gruppe umbenennen",rename_btn:"Speichern",delete:"Gruppe löschen",delete_confirm:'Soll die Gruppe "{0}" wirklich gelöscht werden? Die Mitgliedschaft aller Benutzer in dieser Gruppe geht dabei verloren.',err:{save_failed:"Die Gruppe konnte nicht gespeichert werden.",delete_failed:"Die Gruppe konnte nicht gelöscht werden."},err_generic:"Die Änderung konnte nicht gespeichert werden."},$ugp={init2:function(){$ocms.getScript([],(function(){$ugp.init3()}))},init3:function(){$fis.cf(!0),$fis.lf(!0),$("#activemodule").text($ugt.mdl),$ugp.eM(),$ugp.renderLst()},eM:function(e){let n=[{lbl:$ugt.newgroup_btn,fnc:$ugp.createDlg}];return!0===bool(e,!1)&&n.push({lbl:$ugt.rel,fnc:$ugp.renderLst}),$("#topbar").ocmsmenu(n)},iMn:function(e){return $("#topbar").ocmsmenu([{lbl:$ugt.rename,fnc:function(){$ugp.renameDlg(e)}},{lbl:$ugt.delete,fnc:function(){$ugp.deleteGroup(e)}}])},renderLst:function(){let e=$fis.frm_list(!0,!0).ldng(1),n=$$.dc("ugpfrm",e);$ugp.eM(!0),$ocms.postXT({url:$ocms.url("usrgrp/list"),success:function(e){n.empty();let t=$$.dc("ugp_grid",n),r=$$.dc("ugp_row ugp_row_hd",t);$$.dc("ugp_col_name",r).text($ugt.col_name),0!==(e.groups||[]).length?$.each(e.groups||[],(function(e,n){let r=$$.dc("ugp_row",t,{id:"ugp"+n.usergroup_id});$$.dc("ugp_col_name",r).text(n.name||""),r.click((function(){r.toggleClass("selected").siblings().rC("selected"),!0===r.is(".selected")?$ugp.iMn(n):$ugp.eM(!0)}))})):$$.dc("ugp_empty",t).text($ugt.empty)},error:function(){n.empty(),$$.dc("ugp_err",n).text($ugt.loadfail)},complete:function(){e.ldng(0)}})},createDlg:function(){$ocms.dlgform([{name:"name",label:$ugt.name_field,type:"string",required:!0}],{title:$ugt.newgroup,button:$ugt.newgroup_btn,submit:function(e){let n=$(this).ldng(1),t=n.serializeObject(!0,{typedvalues:!0});$ocms.postXT({url:$ocms.url("usrgrp/create"),data:t,success:function(){n.trigger("modal_close")},error:function(e){let n=((e||{}).responseJSON||{}).error;alert($ugt.err[n]||$ugt.err_generic)},complete:function(){n.ldng(0),$ugp.renderLst()}})}})},renameDlg:function(e){$ocms.dlgform([{name:"usergroup_id",type:"hidden",value:e.usergroup_id},{name:"name",label:$ugt.name_field,type:"string",required:!0,value:e.name}],{title:$ugt.rename,button:$ugt.rename_btn,submit:function(e){let n=$(this).ldng(1),t=n.serializeObject(!0,{typedvalues:!0});$ocms.postXT({url:$ocms.url("usrgrp/rename"),data:t,success:function(){n.trigger("modal_close")},error:function(e){let n=((e||{}).responseJSON||{}).error;alert($ugt.err[n]||$ugt.err_generic)},complete:function(){n.ldng(0),$ugp.renderLst()}})}})},deleteGroup:function(e){confirm(string($ugt.delete_confirm,[e.name||""]))&&$ocms.postXT({url:$ocms.url("usrgrp/delete"),data:{usergroup_id:e.usergroup_id},success:function(){$ugp.eM(!0)},error:function(e){let n=((e||{}).responseJSON||{}).error;alert($ugt.err[n]||$ugt.err_generic)},complete:function(){$ugp.renderLst()}})}},$$ugp={init2:$ugp.init2,auth:{}};export default $$ugp;
+1
View File
@@ -0,0 +1 @@
.ugpfrm{padding:1.5rem 2rem}.ugpfrm .ugp_grid{border:1px solid #e2e2e2;border-radius:.5rem;background:#fff;box-shadow:0 1px 3px rgba(50,50,50,.1);overflow:hidden;max-width:32rem}.ugpfrm .ugp_row{display:grid;grid-template-columns:1fr;align-items:center;padding:.55rem 1rem;border-bottom:1px solid #eee}.ugpfrm .ugp_row:last-child{border-bottom:none}.ugpfrm .ugp_row_hd{background:#1b4379;color:#fff;font-weight:700;font-size:.85rem;text-transform:uppercase;letter-spacing:.02em;padding:.5rem 1rem}.ugpfrm .ugp_row:not(.ugp_row_hd){cursor:pointer;transition:background-color .12s ease-in-out}.ugpfrm .ugp_row:not(.ugp_row_hd):hover{background-color:#f3f6fb}.ugpfrm .ugp_row:not(.ugp_row_hd).selected{background-color:rgba(86,165,50,.12);box-shadow:inset 3px 0 0 #56a532}.ugpfrm .ugp_col_name{font-weight:600;color:#222}.ugpfrm .ugp_empty{padding:1.5rem 1rem;text-align:center;color:#777}.ugpfrm .ugp_err{padding:1rem 2rem;color:#b00020;font-weight:700}
+18 -2
View File
@@ -16,6 +16,9 @@ public class FdsMfrClient : IDisposable
private readonly MFRClient _mfrClient;
private readonly ILogger<FdsMfrClient> _logger;
// Full-reset merges of large entity tables exceed SqlCommand's 30 s default.
private const int MergeCommandTimeoutSeconds = 600;
private static MFRClientCredentials DefaultCredentials() =>
new(FdsConfig.MFR_UserName, FdsConfig.MFR_Password);
@@ -257,11 +260,24 @@ public class FdsMfrClient : IDisposable
{
tbl.AcceptChanges();
var dtwa = new DatatableWriterAsync(tbl, FdsShared.FDSConnectionString());
dtwa.CommandAfterError = new SqlCommand($"SELECT * INTO [t_{setId.ToLower()}_{tbl.TableName}] FROM {dtwa.DestinationTableName};");
// One fixed debug copy per entity table (last failed batch) instead of a new t_<setid>_ table per attempt.
string fallbackTable = $"t_err_{tbl.TableName}";
dtwa.CommandAfterError = new SqlCommand(
$"DROP TABLE IF EXISTS [{fallbackTable}]; SELECT * INTO [{fallbackTable}] FROM {dtwa.DestinationTableName};")
{ CommandTimeout = MergeCommandTimeoutSeconds };
dtwa.OnCommandAfterError += (sender, caex) =>
_logger.LogError(caex,
"Update__entitytable: merge into {Table} failed ({Entity}, setid={SetId}, rows={Rows}) — staged batch kept in {FallbackTable}",
tbl.TableName, thisEntityName, setId, trc, fallbackTable);
dtwa.OnError += (sender, exc, message) =>
_logger.LogError(exc,
"Update__entitytable: DatatableWriterAsync submission failed for {Table} ({Entity}, setid={SetId}) — {Message}",
tbl.TableName, thisEntityName, setId, message);
dtwa.CommandAfter = new SqlCommand(
$"EXECUTE [dbo].[{tbl.TableName.Replace("__", "__updt__")}] @tblname, @referencetable, @tgtid;" +
"EXECUTE [dbo].[fds__setStatus] @table,@action,@setid,@info;" +
(additionalCommandAfter ?? ""));
(additionalCommandAfter ?? ""))
{ CommandTimeout = MergeCommandTimeoutSeconds };
dtwa.CommandAfter.Parameters.AddRange(new SqlParameter[]
{
new("@tblname", dtwa.DestinationTableName),
+2
View File
@@ -146,6 +146,8 @@
<Build Include="Security\fds_rwe.sql" />
<Build Include="Security\fuchs_web.sql" />
<Build Include="Security\fuchs_fds.sql" />
<Build Include="Security\fuchs_web.login.sql" />
<Build Include="Security\fuchs_fds.login.sql" />
<Build Include="dbo\Functions\vat_val.sql" />
<Build Include="dbo\Functions\strings_replaceSpecialCharsInHTML.sql" />
<Build Include="dbo\Functions\strings_removeLeading0_255.sql" />
@@ -0,0 +1,6 @@
-- Server-level login referenced by the [fuchs_fds] database user.
-- Exists only so the project model resolves (SQL71501); the real password
-- is managed on the server and never taken from this script.
CREATE LOGIN [fuchs_fds]
WITH PASSWORD = N'placeholder-not-deployed';
@@ -0,0 +1,6 @@
-- Server-level login referenced by the [fuchs_web] database user.
-- Exists only so the project model resolves (SQL71501); the real password
-- is managed on the server and never taken from this script.
CREATE LOGIN [fuchs_web]
WITH PASSWORD = N'placeholder-not-deployed';
@@ -15,7 +15,7 @@ CREATE FUNCTION [dbo].[fis_admin_getUserAccountList]
, @usergroup_id varchar(5)
, @include_nonmembers as bit
)
RETURNS @RET TABLE ( [useraccount_id] varchar(5), [auth] smallint, [name] nvarchar(100) null, [firstname] nvarchar(100) null, [title] varchar(50) NULL, [gender] varchar(1), [email] varchar(255) NULL, [mobile] varchar(50) null, [functions] varchar(1000), [language] varchar(2), [disabled] bit, [nologin] bit, [member] bit, [order] int)
RETURNS @RET TABLE ( [useraccount_id] varchar(5), [auth] smallint, [name] nvarchar(100) null, [firstname] nvarchar(100) null, [title] varchar(50) NULL, [gender] varchar(1), [email] varchar(255) NULL, [mobile] varchar(50) null, [functions] varchar(1000), [language] varchar(2), [disabled] bit, [nologin] bit, [member] bit, [groups] nvarchar(max) NULL, [order] int)
AS
BEGIN
DECLARE @auth smallint = [dbo].[fis_admin_getUserAuth](@authaccount);
@@ -49,6 +49,12 @@ BEGIN
,'disabled' = CASE WHEN ua.[DateDisabled] IS NULL THEN 0 ELSE 1 END
,'nologin' = CAST(CASE WHEN ua.[password_enc] is null THEN 1 WHEN ISNULL(CAST(DECRYPTBYKEY(ua.[password_enc]) as varchar(20)),'') = '' THEN 1 ELSE 0 END as bit)
,'member' = CASE WHEN @usergroup_id IS NULL THEN NULL WHEN sgm.[useraccount_id] is not null THEN 1 ELSE 0 END
,'groups' = (SELECT STRING_AGG(CAST(DECRYPTBYKEY(ug.[name_enc]) as nvarchar(max)), ', ') WITHIN GROUP (ORDER BY CAST(DECRYPTBYKEY(ug.[name_enc]) as nvarchar(255)))
FROM [dbo].[fis_usergroups_members] as ugm
JOIN [dbo].[fis_usergroups] as ug ON ug.[usergroup_id] = ugm.[usergroup_id]
WHERE ugm.[useraccount_id] = ua.[useraccount_id]
AND ugm.[DateDeleted] is null
AND ug.[DateDeleted] is null) /* csv of all active group memberships, alphabetically */
,'order' = ROW_NUMBER() OVER (ORDER BY CAST(DECRYPTBYKEY(ua.[name_enc]) as nvarchar(255)))
FROM [dbo].[fis_useraccounts] as ua
LEFT JOIN SelectedGroupMembers as sgm on sgm.[useraccount_id] = ua.[useraccount_id]
@@ -11,8 +11,9 @@ AS
BEGIN
SET NOCOUNT ON;
DECLARE @utcnow datetime = GETUTCDATE();
DECLARE @auth smallint = [dbo].[fis_admin_getUserAuth](@authaccount);
IF NOT EXISTS (SELECT * FROM [dbo].[fis_usergroups_members] WHERE [usergroup_id] = @usergroup_id AND [useraccount_id] = @useraccount_id and [DateDeleted] is null)
IF @auth > 1 AND NOT EXISTS (SELECT * FROM [dbo].[fis_usergroups_members] WHERE [usergroup_id] = @usergroup_id AND [useraccount_id] = @useraccount_id and [DateDeleted] is null)
BEGIN
INSERT INTO [dbo].[fis_usergroups_members]
@@ -12,8 +12,9 @@ AS
BEGIN
SET NOCOUNT ON;
DECLARE @utcnow datetime = GETUTCDATE();
DECLARE @auth smallint = [dbo].[fis_admin_getUserAuth](@authaccount);
IF EXISTS (SELECT * FROM [dbo].[fis_usergroups] WHERE [usergroup_id] = @usergroup_id and [DateDeleted] is null)
IF @auth > 1 AND EXISTS (SELECT * FROM [dbo].[fis_usergroups] WHERE [usergroup_id] = @usergroup_id and [DateDeleted] is null)
BEGIN
UPDATE [dbo].[fis_usergroups]
@@ -12,8 +12,9 @@ AS
BEGIN
SET NOCOUNT ON;
DECLARE @utcnow datetime = GETUTCDATE();
DECLARE @auth smallint = [dbo].[fis_admin_getUserAuth](@authaccount);
IF EXISTS (SELECT * FROM [dbo].[fis_usergroups_members] WHERE [usergroup_id] = @usergroup_id AND [useraccount_id] = @useraccount_id and [DateDeleted] is null)
IF @auth > 1 AND EXISTS (SELECT * FROM [dbo].[fis_usergroups_members] WHERE [usergroup_id] = @usergroup_id AND [useraccount_id] = @useraccount_id and [DateDeleted] is null)
BEGIN
UPDATE [dbo].[fis_usergroups_members]
@@ -11,12 +11,17 @@ CREATE PROCEDURE [dbo].[mfr__updt__#customvalues]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__#customvalues];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM [' + @tblname + ']';
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
--WITH tmp as (
-- SELECT *, ROW_NUMBER() OVER (PARTITION BY [id],[Property] ORDER BY [version] DESC) as '#' FROM @tmp
--), SOURCE as (
@@ -43,4 +48,15 @@ BEGIN
,SOURCE.[Property]
,SOURCE.[Meta]
,SOURCE.[Value]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,17 @@ CREATE PROCEDURE [dbo].[mfr__updt__#locations]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__#locations];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM [' + @tblname + ']';
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
WITH tmp as (
SELECT *, ROW_NUMBER() OVER (PARTITION BY [id],[Property] ORDER BY [version] DESC) as '#' FROM @tmp
), SOURCE as (
@@ -64,4 +69,15 @@ BEGIN
,SOURCE.[Longitude]
,SOURCE.[Latitude]
,SOURCE.[IsValidLocation]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__*PartnerSet]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__*PartnerSet];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM [' + @tblname + ']';
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__*PartnerSet] as TARGET
USING @tmp as SOURCE ON
@@ -40,4 +44,15 @@ BEGIN
,SOURCE.[PartnerType]
,SOURCE.[PartnerId]
,SOURCE.[EntityId]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__appointments]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__appointments];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__appointments] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -63,4 +67,15 @@ BEGIN
,SOURCE.[ServiceRequestId]
,SOURCE.[ContactId]
,SOURCE.[Note]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__attachments]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__attachments];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__attachments] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -36,4 +40,15 @@ BEGIN
,SOURCE.[Version]
,SOURCE.[URI]
,SOURCE.[StepId]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__comments]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__comments];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__comments] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -45,4 +49,15 @@ BEGIN
,SOURCE.[ServiceRequestId]
,SOURCE.[CompanyId]
,SOURCE.[Content]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,6 +10,9 @@ CREATE PROCEDURE [dbo].[mfr__updt__companies]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @now datetime = GETUTCDATE();
DECLARE @tmp [dbo].[mfr__tt__companies];
@@ -17,6 +20,8 @@ BEGIN
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
IF @referencetable = 'mfr__companies'
BEGIN
DELETE p
@@ -99,4 +104,15 @@ BEGIN
,SOURCE.[DateModified]
,SOURCE.[CustomValues]
,SOURCE.[MainContactId]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__contacts]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__contacts];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__contacts] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -75,4 +79,15 @@ BEGIN
,SOURCE.[ExternalId]
,SOURCE.[Gender]
,SOURCE.[UserId]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__costcenters]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__costcenters];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__costcenters] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -33,4 +37,15 @@ BEGIN
(SOURCE.[Id]
,SOURCE.[Version]
,SOURCE.[Name]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__documents]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__documents];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__documents] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -75,4 +79,15 @@ BEGIN
,SOURCE.[HTMLCode]
,SOURCE.[ServiceRequestId]
,SOURCE.[Note]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__invoices]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__invoices];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__invoices] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -80,4 +84,15 @@ BEGIN
,SOURCE.[InvoiceState]
,SOURCE.[Note]
,newid());
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__items]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__items];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__items] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -96,4 +100,15 @@ BEGIN
,SOURCE.[CreatorId]
,SOURCE.[UnitId]
,SOURCE.[UnitString]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__itemtypes]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__itemtypes];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__itemtypes] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -87,4 +91,15 @@ BEGIN
,SOURCE.[IsWarehouse]
,SOURCE.[UnitId]
,SOURCE.[UnitString]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__itemunits]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__itemunits];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__itemunits] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -33,4 +37,15 @@ BEGIN
(SOURCE.[Id]
,SOURCE.[Version]
,SOURCE.[Name]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__products]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__products];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__products] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -48,4 +52,15 @@ BEGIN
,SOURCE.[DateModified]
,SOURCE.[MappingId]
,SOURCE.[CustomValueStepTemplateId]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__qualifications]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__qualifications];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__qualifications] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -33,4 +37,15 @@ BEGIN
(SOURCE.[Id]
,SOURCE.[Version]
,SOURCE.[Name]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__reports]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__reports];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__reports] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -72,4 +76,15 @@ BEGIN
,SOURCE.[IsInvoice]
,SOURCE.[IsCanceled]
,SOURCE.[CustomValues]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,6 +10,9 @@ CREATE PROCEDURE [dbo].[mfr__updt__serviceobjects]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @now datetime = GETUTCDATE();
DECLARE @tmp [dbo].[mfr__tt__serviceobjects];
@@ -17,6 +20,8 @@ BEGIN
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
IF @referencetable = 'mfr__serviceobjects'
BEGIN
DELETE p
@@ -90,4 +95,15 @@ BEGIN
,SOURCE.[ParentServiceObjectId]
,SOURCE.[CompanyId]
,SOURCE.[ProductId]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,6 +10,7 @@ CREATE PROCEDURE [dbo].[mfr__updt__servicerequests]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
@@ -151,20 +152,11 @@ BEGIN
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
INSERT INTO dbo.[fds__admin_dberrors]
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
-- Transaction uncommittable
--IF (XACT_STATE()) = -1
ROLLBACK TRANSACTION;
-- Transaction committable
--IF (XACT_STATE()) = 1
--COMMIT TRANSACTION;
DECLARE @Message varchar(MAX) = ERROR_MESSAGE(), @Severity int = ERROR_SEVERITY(), @State smallint = ERROR_STATE();
RAISERROR (@Message, @Severity, @State);
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__steplisttemplateinstances]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__steplisttemplateinstances];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__steplisttemplateinstances] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -39,4 +43,15 @@ BEGIN
,SOURCE.[Name]
,SOURCE.[ServiceRequestId]
,SOURCE.[TemplateId]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,6 +10,9 @@ CREATE PROCEDURE [dbo].[mfr__updt__steplisttemplates]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @now datetime = GETUTCDATE();
DECLARE @tmp [dbo].[mfr__tt__steplisttemplates];
@@ -18,6 +21,7 @@ BEGIN
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
IF @referencetable = 'mfr__steplisttemplates'
BEGIN
@@ -56,4 +60,15 @@ BEGIN
,SOURCE.[DateModified]
,SOURCE.[IsReleased]
,SOURCE.[IsDurable]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,6 +10,9 @@ CREATE PROCEDURE [dbo].[mfr__updt__steps]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @now datetime = GETUTCDATE();
DECLARE @tmp [dbo].[mfr__tt__steps];
@@ -53,6 +56,7 @@ BEGIN
END
BEGIN TRANSACTION;
MERGE [dbo].[mfr__steps] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -146,4 +150,15 @@ BEGIN
-- ,[DateDeleted] = @now
-- FROM @ARCHIVE
-- WHERE [ActionType] IN ( 'DELETE', 'UPDATE' );
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__stockmovements]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__stockmovements];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__stockmovements] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -72,4 +76,15 @@ BEGIN
,SOURCE.[TargetDate]
,SOURCE.[CreatedById]
,SOURCE.[ConfirmedById]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,17 @@ CREATE PROCEDURE [dbo].[mfr__updt__tags]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__tags];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
WITH tmp as (
SELECT *, ROW_NUMBER() OVER (PARTITION BY [id] ORDER BY [version] DESC) as '#' FROM @tmp
), SOURCE as (
@@ -43,4 +48,15 @@ BEGIN
,SOURCE.[Name]
,SOURCE.[ColorDefinition]
,SOURCE.[Type]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,12 +10,16 @@ CREATE PROCEDURE [dbo].[mfr__updt__timeevents]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE @tmp [dbo].[mfr__tt__timeevents];
DECLARE @tmp_cmd nvarchar(1000) = N'SELECT DISTINCT * FROM ' + @tblname
INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd;
BEGIN TRANSACTION;
MERGE [dbo].[mfr__timeevents] as TARGET
USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id]
@@ -63,4 +67,15 @@ BEGIN
,SOURCE.[ContactId]
,SOURCE.[ServiceRequestId]
,SOURCE.[Description]);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
THROW;
END CATCH;
END
@@ -10,6 +10,7 @@ CREATE PROCEDURE [dbo].[mfr__updt__users]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
@@ -90,20 +91,11 @@ BEGIN
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
INSERT INTO dbo.[fds__admin_dberrors]
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime])
VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE());
-- Transaction uncommittable
--IF (XACT_STATE()) = -1
ROLLBACK TRANSACTION;
-- Transaction committable
--IF (XACT_STATE()) = 1
--COMMIT TRANSACTION;
DECLARE @Message varchar(MAX) = ERROR_MESSAGE(), @Severity int = ERROR_SEVERITY(), @State smallint = ERROR_STATE();
RAISERROR (@Message, @Severity, @State);
THROW;
END CATCH;
END