Installation — First Run¶
Starting the application for the first time, getting an account into it, and checking that the parts that depend on external services actually work.
Start the application¶
Startup will fail fast if any of these is missing, so fix them in this order:
| Symptom at startup | Cause |
|---|---|
| Cannot open database / login failed | A connection string is wrong, or one of the three databases does not exist |
Null reference in Startup. |
The Storage section is missing — Startup reads Storage while registering services. (The S3 and SNS clients themselves are created lazily; a bad AWS key only shows up at the first upload.) |
| Hangfire schema error | The Hangfire database does not exist (Hangfire creates the schema, not the database) |
Once it is up, / resolves to HomeController.Index, which is behind a permission key — so an
anonymous visitor is redirected to /Account/Login.
The first-account problem¶
A freshly migrated database cannot be signed into — R5-1
Migrations create schema only. There is no seed data in the solution (zero HasData(
calls), no SQL bootstrap script, and no self-registration action. ApplicationUsers is
empty, and every screen that could create a user is itself behind a permission key.
There is no supported "create the first administrator" path. The two workable routes are below.
Route A — restore a database backup (recommended)¶
The realistic path, and how new environments are actually stood up today: take a backup of NKO_DB
and NKO_DB_Warehouse from an existing environment and restore it instead of running migrations
into empty databases.
This brings the identity rows, permission keys, groups and all reference data with it. If the source is production, treat the restored copy as production data — it contains employee records, salaries and identity documents — and sanitise it before using it for development.
Route B — hand-craft the identity rows¶
Only if no backup exists. Insert directly into NKO_DB, in this order:
| # | Table | Required values |
|---|---|---|
| 1 | Application |
User, Normalized (upper case), Password, Security, Is, Is, Two, Lockout |
| 2 | App |
Identity = the Application from step 1, Status = 1, Is, plus the employee fields (Firstname, Lastname, Email, Branch, Date) |
| 3 | App |
One group — this is what the system calls a role |
| 4 | App |
One row per permission key you want to grant (219 keys are in force across the application) |
| 5 | App |
Links the group to those App rows |
| 6 | App |
Links the App to the group, Is |
Two details that make or break this route
- The users table is
dbo.ApplicationUsers, notAspNetUsers.OnModelCreatingremaps Identity's default (.ToTable("ApplicationUsers", "dbo")). An insert intoAspNetUsersfails — the table does not exist. AppUser.Statusmust be1andIsDeletedmust be0. Both columns areNOT NULLwith no database default, so the insert must supply them, andAppUsercarries a global query filter —!IsDeleted && Status == 1. Any other value hides the row from the application: a correct password then fails atSetCustomTracking(appUser.UserId)and every gated page throws inPermissionsHandler(R6-8).
The password hash is BCrypt, not the ASP.NET Identity default
ApplicationUserManager sets PasswordHasher = new BCryptPasswordHasher() in its constructor,
so PasswordHash must be a BCrypt hash generated at work factor 12
(BCrypt.Net.BCrypt.HashPassword(password, BCrypt.Net.BCrypt.GenerateSalt(12))). A hash
produced by the standard Identity hasher — or by any online PBKDF2 tool — will never verify.
Step 2 is not optional even though it looks like employee data: AccountController.Login loads the
matching AppUser and dereferences it, so an ApplicationUsers row with no AppUser throws on the
wrong-password branch before it can report a bad password.
Both AppUserGroupMember.IsEnable = 1 and ApplicationUser.IsEnableLogin = 1 are required.
PermissionsHandler.GetUserPermissions returns an empty list when IsEnableLogin is false, and
only reads groups whose membership row is enabled — either one left at 0 gives a login that can
reach nothing.
What happens at sign-in¶
/Account/Login— username and password.- The password is verified with BCrypt.
IsEnableLoginmust be true, otherwise sign-in stops.- If
Is2FAMandatoryorTwoFactorEnabledis set, a verification code is sent. - If
IsPasswordDefaultis true the user is redirected to Security to change the password before anything else. New accounts created through the HR screens start with the hard-coded password"321321321"(R6-3).
Verification checklist¶
Once you can sign in, confirm each subsystem. Failures here are configuration, not code.
| # | Check | Where | Confirms |
|---|---|---|---|
| 1 | The top navigation lists modules | any page | Permission keys resolve — the top bar (Topbar) filters its menus with Has, so an empty menu means no keys |
| 2 | Open an employee record | / |
Core database and data scoping |
| 3 | Open the warehouse stock grid | / |
The second database and its connection string |
| 4 | Download any attachment | an employee's Attachments tab | AWS S3 credentials |
| 5 | Trigger an approval on a procurement document | a PR's Details screen | E-signature gate and notifications (payroll, training and a few others skip the gate — R3-9) |
| 6 | Watch for a notification badge | the top bar | SignalR hub at / |
| 7 | Export any grid to Excel | any list screen | EPPlus |
| 8 | Export any document to PDF | a purchase order | Rotativa and wkhtmltopdf — see below |
| 9 | SELECT * FROM Exchange returns one row |
SQL | The global exchange rate exists. Without it every TL/EUR purchase request is valued at $0 and skips the segregation-of-duties sign-offs (R7-1). The application cannot create it. |
Known to fail on a fresh install¶
PDF export — R5-3
Step 8 will fail. Startup.Configure calls
RotativaConfiguration.Setup(env.WebRootPath, "Rotativa"), but wwwroot/Rotativa/ does not
exist in the repository and no wkhtmltopdf binary is anywhere in it. Obtain the binaries
matching Rotativa.AspNetCore 1.2.0 and place them in that folder.
E-mail failures are invisible — R1-6
If SMTP is misconfigured nothing will tell you. EmailManager wraps both send paths in an
empty catch, so mail simply never arrives. Verify by triggering an approval and checking the
recipient's mailbox directly — do not rely on the application to report a problem.
Background jobs¶
Three schedulers start with the application (see External Services):
| Mechanism | On first run |
|---|---|
| Quartz | Update runs nightly at 03:00. Export has no schedule — it runs only when an administrator starts a login-history export, which triggers it with Trigger (R1-3) |
| Hangfire | Creates its schema on first start; jobs are queued on demand from the warehouse screens. No dashboard is mapped, so failures are invisible |
IHosted |
Birthday, controlled by Scheduled. Loan exists but is never registered (R7-4) |
After a successful first run¶
- Create the real permission groups and assign keys — Permissions.
- Enter the reference data the modules depend on: branches, departments, positions, leave types, the work calendar, donors and stock categories — Administration.
- Set up the role-holders the approval chains route to — three different mechanisms:
- the HR manager, finance manager and general manager are not configured anywhere: they are whoever holds a position titled exactly "HR and Admin Manager", "Finance Manager" and "General Manager" (and the supply-chain approver "Supply Chain Director", in a department named "Supply Chain"). Create those positions with exactly those names, once each (R6-10);
- each branch's manager, HR, finance, logistics, treasury and accounting holders are set on the
branch edit screen (
/Admin/EditBranch); - the allocator, procurement manager, HR approval, director approval, timesheet officers and
cash boxes are set in
/Admin/GeneralSettings. On a new database that screen crashes until the 21CommonSettingrows exist — insert them by SQL first,SettingId1–21 in order, row 14 namedips(R5-5).
A chain whose role-holder cannot be found stalls without an error.
4. Have each approver upload an e-signature — procurement, warehouse, leave, advance and overtime approvals check BSign.HasSign (payroll, training, jobs, evaluation and destruction do not — R3-9).