Add Accountant role and CanManageBills/CanManageAccounting permissions
- AppConstants: add Accountant to CompanyRoles; add CanManageBills and CanManageAccounting to Policies - ApplicationUser: add CanManageBills and CanManageAccounting bool fields - UserManagementDtos: expose new fields in all three DTOs - ClaimsPrincipalFactory: emit ManageBills and ManageAccounting claims - Program.cs: add CanManageBills and CanManageAccounting policies; update CanManageInvoices, CanViewReports, CanManagePurchaseOrders, and CanManageVendors to auto-pass for Accountant role - BillsController: replace CanManageInventory with CanManageBills on all write actions (correct policy — bills are not inventory) - BankReconciliationsController: replace CanManageJobs with CanManageAccounting on write actions - CompanyUsersController: add Accountant to validCompanyRoles (both Create/Edit), legacyRole switch, and all permission assignment blocks - Create/Edit views: add Accountant option to role dropdown; add CanManageBills and CanManageAccounting checkboxes; JS auto-checks financial permissions when Accountant role is selected - Migration AddAccountantRolePermissions: adds columns + backfills CanManageBills=1 and CanManageAccounting=1 for all CompanyAdmin users Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -56,7 +56,7 @@ public class BankReconciliationsController : Controller
|
||||
|
||||
// ── Create ───────────────────────────────────────────────────────────────
|
||||
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageJobs)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageAccounting)]
|
||||
public async Task<IActionResult> Create()
|
||||
{
|
||||
if (!AllowAccounting()) return RedirectToAction("Landing", "Reports");
|
||||
@@ -65,7 +65,7 @@ public class BankReconciliationsController : Controller
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageJobs)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageAccounting)]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create(BankReconciliation model)
|
||||
{
|
||||
@@ -171,7 +171,7 @@ public class BankReconciliationsController : Controller
|
||||
/// Returns updated running totals as JSON.
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageJobs)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageAccounting)]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> ToggleCleared(
|
||||
int reconId, string entityType, int entityId, bool isCleared)
|
||||
@@ -207,7 +207,7 @@ public class BankReconciliationsController : Controller
|
||||
|
||||
/// <summary>Completes the reconciliation. Only allowed when Difference == 0.00.</summary>
|
||||
[HttpPost]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageJobs)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageAccounting)]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Complete(int id, decimal difference)
|
||||
{
|
||||
@@ -286,7 +286,7 @@ public class BankReconciliationsController : Controller
|
||||
/// suggestions client-side by auto-checking the corresponding table rows.
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageJobs)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageAccounting)]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> AiSuggestMatches(int reconId)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using AutoMapper;
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using PowderCoating.Shared.Constants;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
@@ -58,13 +58,13 @@ public class BillsController : Controller
|
||||
_usageLogger = usageLogger;
|
||||
}
|
||||
|
||||
// ── Index ────────────────────────────────────────────────────────────────
|
||||
// -- Index ----------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Lists bills and direct expenses in a unified AP ledger view. The <paramref name="type"/>
|
||||
/// parameter lets the caller pin the list to Bills only, Expenses only, or both (null).
|
||||
/// Expenses are inherently fully paid so they are always excluded when the caller filters to
|
||||
/// "Unpaid" or "Overdue" — preventing them from inflating the "amount owed" summary.
|
||||
/// "Unpaid" or "Overdue" — preventing them from inflating the "amount owed" summary.
|
||||
/// Amount-based search strips leading $ and commas before comparing so "$1,234" works naturally.
|
||||
/// </summary>
|
||||
public async Task<IActionResult> Index(string? type, string? search, string? status, int page = 1, int pageSize = 25)
|
||||
@@ -112,7 +112,7 @@ public class BillsController : Controller
|
||||
}));
|
||||
}
|
||||
|
||||
// Expenses are always fully paid — exclude when filtering to unpaid/overdue bills only
|
||||
// Expenses are always fully paid — exclude when filtering to unpaid/overdue bills only
|
||||
if ((type == null || type == "Expense") && status != "Unpaid" && status != "Overdue")
|
||||
{
|
||||
var expSearch = search;
|
||||
@@ -160,13 +160,13 @@ public class BillsController : Controller
|
||||
return View(pagedEntries);
|
||||
}
|
||||
|
||||
// ── Create ───────────────────────────────────────────────────────────────
|
||||
// -- Create ---------------------------------------------------------------
|
||||
|
||||
// ── Create from Purchase Order ────────────────────────────────────────────
|
||||
// -- Create from Purchase Order --------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Scaffolds a new bill pre-filled from a received purchase order. Only POs in
|
||||
/// <c>Received</c> or <c>PartiallyReceived</c> status can be billed — earlier states mean
|
||||
/// <c>Received</c> or <c>PartiallyReceived</c> status can be billed — earlier states mean
|
||||
/// goods have not yet arrived and no liability has been incurred. If a bill already exists for
|
||||
/// the PO the user is redirected to the existing bill to prevent duplicate AP entries.
|
||||
/// Line items are copied from PO items (using inventory item names where available), and
|
||||
@@ -174,7 +174,7 @@ public class BillsController : Controller
|
||||
/// <c>DefaultExpenseAccountId</c> is used to pre-categorise all lines, falling back to the
|
||||
/// first active Expense/COGS account when the vendor has no default configured.
|
||||
/// </summary>
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageInventory)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageBills)]
|
||||
public async Task<IActionResult> CreateFromPurchaseOrder(int purchaseOrderId)
|
||||
{
|
||||
var currentUser = await _userManager.GetUserAsync(User);
|
||||
@@ -248,7 +248,7 @@ public class BillsController : Controller
|
||||
return View("Create", dto);
|
||||
}
|
||||
|
||||
// ── Create ───────────────────────────────────────────────────────────────
|
||||
// -- Create ---------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns the blank bill creation form. When <paramref name="vendorId"/> is supplied the
|
||||
@@ -257,7 +257,7 @@ public class BillsController : Controller
|
||||
/// amount. The AP account is pre-filled with the first active AccountsPayable sub-type account
|
||||
/// so the double-entry pair is ready without manual lookup.
|
||||
/// </summary>
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageInventory)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageBills)]
|
||||
public async Task<IActionResult> Create(int? vendorId)
|
||||
{
|
||||
var dto = new CreateBillDto
|
||||
@@ -291,14 +291,14 @@ public class BillsController : Controller
|
||||
/// review before committing to AP. Empty line items (zero account or zero price) are stripped
|
||||
/// before validation to avoid spurious errors when the browser submits blank rows.
|
||||
/// If <paramref name="payNow"/> is true a <see cref="BillPayment"/> record is inserted
|
||||
/// immediately and the bill status is advanced to <c>Paid</c> or <c>PartiallyPaid</c> —
|
||||
/// immediately and the bill status is advanced to <c>Paid</c> or <c>PartiallyPaid</c> —
|
||||
/// useful for entering historical bills that were already settled. Account balance side
|
||||
/// effects are deliberately deferred to <see cref="MarkOpen"/> so that Draft bills do not
|
||||
/// affect the AP ledger until they are approved. If the bill was created from a PO the
|
||||
/// back-reference <c>PurchaseOrder.BillId</c> is set to establish the 1:1 linkage.
|
||||
/// </summary>
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageInventory)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageBills)]
|
||||
public async Task<IActionResult> Create(CreateBillDto dto, IFormFile? receiptFile,
|
||||
bool payNow = false,
|
||||
DateTime? paymentDate = null,
|
||||
@@ -322,7 +322,7 @@ public class BillsController : Controller
|
||||
{
|
||||
var currentUser = await _userManager.GetUserAsync(User);
|
||||
|
||||
// Period lock check — block if the bill date is in a locked period
|
||||
// Period lock check — block if the bill date is in a locked period
|
||||
if (currentUser != null)
|
||||
{
|
||||
var co = await _unitOfWork.Companies.GetByIdAsync(currentUser.CompanyId);
|
||||
@@ -399,7 +399,7 @@ public class BillsController : Controller
|
||||
await _unitOfWork.CompleteAsync();
|
||||
});
|
||||
|
||||
// Receipt upload after the transaction commits — bill.Id is set and core data
|
||||
// Receipt upload after the transaction commits — bill.Id is set and core data
|
||||
// is secure. A blob failure here leaves the bill intact without an attachment.
|
||||
if (receiptFile != null && receiptFile.Length > 0)
|
||||
{
|
||||
@@ -428,7 +428,7 @@ public class BillsController : Controller
|
||||
}
|
||||
}
|
||||
|
||||
// ── Details ──────────────────────────────────────────────────────────────
|
||||
// -- Details --------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Displays full bill detail including line items, payments, and the payment entry form.
|
||||
@@ -454,7 +454,7 @@ public class BillsController : Controller
|
||||
.ToList();
|
||||
|
||||
ViewBag.BankAccounts = bankAccounts
|
||||
.Select(a => new SelectListItem($"{a.AccountNumber} – {a.Name}", a.Id.ToString()))
|
||||
.Select(a => new SelectListItem($"{a.AccountNumber} – {a.Name}", a.Id.ToString()))
|
||||
.ToList();
|
||||
|
||||
ViewBag.PaymentMethods = Enum.GetValues<PaymentMethod>()
|
||||
@@ -464,7 +464,7 @@ public class BillsController : Controller
|
||||
return View(dto);
|
||||
}
|
||||
|
||||
// ── Edit ─────────────────────────────────────────────────────────────────
|
||||
// -- Edit -----------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns the edit form for a bill. Only <c>Draft</c> bills are editable; once a bill is
|
||||
@@ -472,7 +472,7 @@ public class BillsController : Controller
|
||||
/// unreconciled ledger entries. Paid and Voided bills are also blocked to preserve the
|
||||
/// audit trail.
|
||||
/// </summary>
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageInventory)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageBills)]
|
||||
public async Task<IActionResult> Edit(int? id)
|
||||
{
|
||||
if (id == null) return NotFound();
|
||||
@@ -523,7 +523,7 @@ public class BillsController : Controller
|
||||
/// storage; the old blob is deleted before the new one is written to avoid orphaned files.
|
||||
/// </summary>
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageInventory)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageBills)]
|
||||
public async Task<IActionResult> Edit(int id, EditBillDto dto, IFormFile? receiptFile)
|
||||
{
|
||||
if (id != dto.Id) return NotFound();
|
||||
@@ -620,7 +620,7 @@ public class BillsController : Controller
|
||||
}
|
||||
}
|
||||
|
||||
// ── Mark Open (Draft → Open) ─────────────────────────────────────────────
|
||||
// -- Mark Open (Draft ? Open) ---------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Transitions a bill from <c>Draft</c> to <c>Open</c> (the AP approval step). This is
|
||||
@@ -631,7 +631,7 @@ public class BillsController : Controller
|
||||
/// deferred from bill creation to give users a review window without polluting the ledger.
|
||||
/// </summary>
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageInventory)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageBills)]
|
||||
public async Task<IActionResult> MarkOpen(int id)
|
||||
{
|
||||
var bill = await _unitOfWork.Bills.GetByIdAsync(id, false, b => b.LineItems);
|
||||
@@ -669,7 +669,7 @@ public class BillsController : Controller
|
||||
return RedirectToAction(nameof(Details), new { id });
|
||||
}
|
||||
|
||||
// ── Record Payment ───────────────────────────────────────────────────────
|
||||
// -- Record Payment -------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Records a full or partial payment against an open bill. Overpayment is blocked because
|
||||
@@ -681,7 +681,7 @@ public class BillsController : Controller
|
||||
/// any positive remainder leaves the bill in <c>PartiallyPaid</c>.
|
||||
/// </summary>
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageInventory)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageBills)]
|
||||
public async Task<IActionResult> RecordPayment(RecordBillPaymentDto dto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
@@ -752,7 +752,7 @@ public class BillsController : Controller
|
||||
return RedirectToAction(nameof(Details), new { id = dto.BillId });
|
||||
}
|
||||
|
||||
// ── Delete Payment ───────────────────────────────────────────────────────
|
||||
// -- Delete Payment -------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Reverses a previously recorded payment. All double-entry effects of
|
||||
@@ -762,7 +762,7 @@ public class BillsController : Controller
|
||||
/// <c>PartiallyPaid</c> depending on the remaining <c>AmountPaid</c> after reversal.
|
||||
/// </summary>
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageInventory)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageBills)]
|
||||
public async Task<IActionResult> DeletePayment(int paymentId, int billId)
|
||||
{
|
||||
try
|
||||
@@ -809,7 +809,7 @@ public class BillsController : Controller
|
||||
return RedirectToAction(nameof(Details), new { id = billId });
|
||||
}
|
||||
|
||||
// ── Edit Payment ─────────────────────────────────────────────────────────
|
||||
// -- Edit Payment ---------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Updates non-financial attributes of a payment (date, method, check number, memo) and,
|
||||
@@ -818,7 +818,7 @@ public class BillsController : Controller
|
||||
/// amount on the AP side does not change so no AP balance adjustment is needed.
|
||||
/// </summary>
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageInventory)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageBills)]
|
||||
public async Task<IActionResult> EditPayment(EditBillPaymentDto dto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
@@ -863,11 +863,11 @@ public class BillsController : Controller
|
||||
return RedirectToAction(nameof(Details), new { id = dto.BillId });
|
||||
}
|
||||
|
||||
// ── Void ─────────────────────────────────────────────────────────────────
|
||||
// -- Void -----------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Voids an open or partially-paid bill, removing the remaining AP liability from the ledger.
|
||||
/// Only the unpaid portion (<c>BalanceDue</c>) is reversed on the AP account — any payments
|
||||
/// Only the unpaid portion (<c>BalanceDue</c>) is reversed on the AP account — any payments
|
||||
/// already recorded remain as historical cash transactions. The vendor balance is likewise
|
||||
/// reduced only by the outstanding balance, not the total. To signal "fully settled" without
|
||||
/// leaving a positive <c>BalanceDue</c>, <c>AmountPaid</c> is set equal to <c>Total</c>
|
||||
@@ -922,7 +922,7 @@ public class BillsController : Controller
|
||||
return RedirectToAction(nameof(Details), new { id });
|
||||
}
|
||||
|
||||
// ── AJAX: Vendor default expense account ────────────────────────────────
|
||||
// -- AJAX: Vendor default expense account --------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// AJAX endpoint that returns a vendor's default expense account and payment terms. Called by
|
||||
@@ -940,7 +940,7 @@ public class BillsController : Controller
|
||||
});
|
||||
}
|
||||
|
||||
// ── Helpers ──────────────────────────────────────────────────────────────
|
||||
// -- Helpers --------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Loads all dropdown lists needed by the Create and Edit views into <c>ViewBag</c>: vendors,
|
||||
@@ -979,7 +979,7 @@ public class BillsController : Controller
|
||||
|
||||
/// <summary>
|
||||
/// Generates a sequential payment reference number in the format <c>BPMT-YYMM-####</c>.
|
||||
/// Same monotonic sequence logic as <see cref="GenerateBillNumberAsync"/> — soft-deleted
|
||||
/// Same monotonic sequence logic as <see cref="GenerateBillNumberAsync"/> — soft-deleted
|
||||
/// records are included in the scan so payment numbers are never reused.
|
||||
/// </summary>
|
||||
private async Task<string> GeneratePaymentNumberAsync()
|
||||
@@ -994,7 +994,7 @@ public class BillsController : Controller
|
||||
return $"{prefix}{next:D4}";
|
||||
}
|
||||
|
||||
// ── Receipt File: Download / Remove ─────────────────────────────────────
|
||||
// -- Receipt File: Download / Remove -------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the receipt attachment for a bill as a file-download response. Unlike expense
|
||||
@@ -1022,7 +1022,7 @@ public class BillsController : Controller
|
||||
/// window where the UI shows a broken attachment link.
|
||||
/// </summary>
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageInventory)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageBills)]
|
||||
public async Task<IActionResult> RemoveReceipt(int id)
|
||||
{
|
||||
var bill = await _unitOfWork.Bills.GetByIdAsync(id);
|
||||
@@ -1039,7 +1039,7 @@ public class BillsController : Controller
|
||||
return RedirectToAction(nameof(Details), new { id });
|
||||
}
|
||||
|
||||
// ── AI: Receipt Scanning ─────────────────────────────────────────────────
|
||||
// -- AI: Receipt Scanning -------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// AI-powered receipt scanning endpoint. Accepts an image or PDF of a vendor receipt, passes
|
||||
@@ -1051,7 +1051,7 @@ public class BillsController : Controller
|
||||
/// model can match categories to the company's specific chart of accounts.
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageInventory)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageBills)]
|
||||
[EnableRateLimiting(AppConstants.RateLimitPolicies.Ai)]
|
||||
public async Task<IActionResult> ScanReceipt(IFormFile? receiptImage)
|
||||
{
|
||||
@@ -1092,7 +1092,7 @@ public class BillsController : Controller
|
||||
return Json(result);
|
||||
}
|
||||
|
||||
// ── AI: Account Suggestion ────────────────────────────────────────────────
|
||||
// -- AI: Account Suggestion ------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// AI-powered account categorisation for a single bill line item. When the caller does not
|
||||
@@ -1103,7 +1103,7 @@ public class BillsController : Controller
|
||||
/// full account list in the DOM. Rate-limited to the <c>Ai</c> policy.
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageInventory)]
|
||||
[Authorize(Policy = AppConstants.Policies.CanManageBills)]
|
||||
[EnableRateLimiting(AppConstants.RateLimitPolicies.Ai)]
|
||||
public async Task<IActionResult> SuggestAccount([FromBody] AccountSuggestionRequest request)
|
||||
{
|
||||
@@ -1136,16 +1136,16 @@ public class BillsController : Controller
|
||||
return Json(result);
|
||||
}
|
||||
|
||||
// ── AI: Recurring Bill Detection ──────────────────────────────────────────
|
||||
// -- AI: Recurring Bill Detection ------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// GET page — displays the recurring bill detection tool. No data is pre-fetched here;
|
||||
/// GET page — displays the recurring bill detection tool. No data is pre-fetched here;
|
||||
/// the user triggers the scan by clicking a button which calls <see cref="RunRecurringDetection"/>.
|
||||
/// </summary>
|
||||
public IActionResult RecurringDetection() => View();
|
||||
|
||||
/// <summary>
|
||||
/// AJAX POST — loads up to 12 months of bill history for the company and passes it to
|
||||
/// AJAX POST — loads up to 12 months of bill history for the company and passes it to
|
||||
/// Claude for recurring pattern analysis. Only posted bills (Draft/Open/Partial/Paid) are
|
||||
/// included; Voided bills are excluded so cancelled payments do not distort the pattern.
|
||||
/// Results are returned as JSON for client-side rendering in the view.
|
||||
@@ -1198,7 +1198,7 @@ public class BillsController : Controller
|
||||
}
|
||||
}
|
||||
|
||||
// ── Receipt File Helpers ──────────────────────────────────────────────────
|
||||
// -- Receipt File Helpers --------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Uploads a receipt file to Azure Blob Storage under the path
|
||||
|
||||
@@ -277,6 +277,7 @@ public class CompanyUsersController : Controller
|
||||
{
|
||||
AppConstants.CompanyRoles.CompanyAdmin,
|
||||
AppConstants.CompanyRoles.Manager,
|
||||
AppConstants.CompanyRoles.Accountant,
|
||||
AppConstants.CompanyRoles.Worker,
|
||||
AppConstants.CompanyRoles.Viewer
|
||||
};
|
||||
@@ -329,7 +330,9 @@ public class CompanyUsersController : Controller
|
||||
CanManageVendors = forceAllPermissions || model.CanManageVendors,
|
||||
CanManageMaintenance = forceAllPermissions || model.CanManageMaintenance,
|
||||
CanManageInvoices = forceAllPermissions || model.CanManageInvoices,
|
||||
CanViewReports = forceAllPermissions || model.CanViewReports
|
||||
CanViewReports = forceAllPermissions || model.CanViewReports,
|
||||
CanManageBills = forceAllPermissions || model.CanManageBills,
|
||||
CanManageAccounting = forceAllPermissions || model.CanManageAccounting
|
||||
};
|
||||
|
||||
var result = await _userManager.CreateAsync(user, model.Password);
|
||||
@@ -341,6 +344,7 @@ public class CompanyUsersController : Controller
|
||||
{
|
||||
AppConstants.CompanyRoles.CompanyAdmin => AppConstants.Roles.Administrator,
|
||||
AppConstants.CompanyRoles.Manager => AppConstants.Roles.Manager,
|
||||
AppConstants.CompanyRoles.Accountant => AppConstants.Roles.Employee,
|
||||
AppConstants.CompanyRoles.Worker => AppConstants.Roles.Employee,
|
||||
_ => AppConstants.Roles.ReadOnly
|
||||
};
|
||||
@@ -454,7 +458,9 @@ public class CompanyUsersController : Controller
|
||||
CanManageVendors = user.CanManageVendors,
|
||||
CanManageMaintenance = user.CanManageMaintenance,
|
||||
CanManageInvoices = user.CanManageInvoices,
|
||||
CanViewReports = user.CanViewReports
|
||||
CanViewReports = user.CanViewReports,
|
||||
CanManageBills = user.CanManageBills,
|
||||
CanManageAccounting = user.CanManageAccounting
|
||||
};
|
||||
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
@@ -538,6 +544,7 @@ public class CompanyUsersController : Controller
|
||||
{
|
||||
AppConstants.CompanyRoles.CompanyAdmin,
|
||||
AppConstants.CompanyRoles.Manager,
|
||||
AppConstants.CompanyRoles.Accountant,
|
||||
AppConstants.CompanyRoles.Worker,
|
||||
AppConstants.CompanyRoles.Viewer
|
||||
};
|
||||
@@ -608,6 +615,8 @@ public class CompanyUsersController : Controller
|
||||
user.CanManageMaintenance = forceAllPermissions || model.CanManageMaintenance;
|
||||
user.CanManageInvoices = forceAllPermissions || model.CanManageInvoices;
|
||||
user.CanViewReports = forceAllPermissions || model.CanViewReports;
|
||||
user.CanManageBills = forceAllPermissions || model.CanManageBills;
|
||||
user.CanManageAccounting = forceAllPermissions || model.CanManageAccounting;
|
||||
user.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
var result = await _userManager.UpdateAsync(user);
|
||||
|
||||
Reference in New Issue
Block a user