feat(rbac): enforce permissions on every café write endpoint
CI/CD / CI · API (dotnet build + test) (push) Successful in 40s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m9s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Has been cancelled
CI/CD / Deploy · all services (push) Has been cancelled
CI/CD / CI · API (dotnet build + test) (push) Successful in 40s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m9s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Has been cancelled
CI/CD / Deploy · all services (push) Has been cancelled
Closes the gap where the custom-role matrix was defined but unenforced — most write endpoints only checked café membership, so the API would accept writes a role's UI hid. Adds EnsurePermission(...) to all mutating/sensitive endpoints across 32 controllers, mapped to the granular catalog: - menu/inventory/coupons/customers/expenses/reservations/taxes/branches → CRUD perms - tables/queue/kitchen-stations/print-settings → manage perms - orders → ProcessOrders / EditOrder / VoidOrder / UpdateOrderStatus / HandlePayments, payment corrections → ManageFinancials - HR → CreateStaff / ManageSchedules / ReviewLeave / View+ManageSalaries / ManageStaffCredentials (self-service clock-in/leave preserved) - reports → ViewReports, export → ExportReports, audit → ViewAuditLog - billing → ManageBilling, sms → SendSms/ManageSmsSettings, reviews → ManageReviews, discover/public profile → ManageDiscoverProfile, café settings → ManageCafeSettings, custom roles → ManageRoles Removes legacy [Authorize(Roles=...)] attributes that would have overridden the permission model (orders, branch-menu, pos-device, print). Manual discount/comp have no backend endpoint yet (discounts come from coupons) — gated on the POS UI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -42,7 +42,7 @@ public class AuditController : CafeApiControllerBase
|
|||||||
[FromQuery] int pageSize = 50)
|
[FromQuery] int pageSize = 50)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsurePermission(tenant, Permission.ViewReports) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ViewAuditLog) is { } forbidden) return forbidden;
|
||||||
|
|
||||||
if (page < 1) page = 1;
|
if (page < 1) page = 1;
|
||||||
if (pageSize < 1) pageSize = 50;
|
if (pageSize < 1) pageSize = 50;
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ using Microsoft.AspNetCore.Authorization;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Billing;
|
using Meezi.API.Models.Billing;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
|
|
||||||
namespace Meezi.API.Controllers;
|
namespace Meezi.API.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class BillingController : ControllerBase
|
public class BillingController : CafeApiControllerBase
|
||||||
{
|
{
|
||||||
private readonly IBillingService _billing;
|
private readonly IBillingService _billing;
|
||||||
private readonly IValidator<SubscribeRequest> _subscribeValidator;
|
private readonly IValidator<SubscribeRequest> _subscribeValidator;
|
||||||
@@ -27,13 +28,9 @@ public class BillingController : ControllerBase
|
|||||||
ITenantContext tenant,
|
ITenantContext tenant,
|
||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageBilling) is { } permDenied) return permDenied;
|
||||||
if (string.IsNullOrEmpty(tenant.CafeId))
|
if (string.IsNullOrEmpty(tenant.CafeId))
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
if (tenant.Role != Core.Enums.EmployeeRole.Owner)
|
|
||||||
{
|
|
||||||
return StatusCode(403, new ApiResponse<object>(false, null,
|
|
||||||
new ApiError("OWNER_REQUIRED", "Only the cafe owner can manage subscription billing.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
var validation = await _subscribeValidator.ValidateAsync(request, ct);
|
var validation = await _subscribeValidator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid)
|
if (!validation.IsValid)
|
||||||
@@ -108,11 +105,9 @@ public class BillingController : ControllerBase
|
|||||||
[HttpDelete("api/billing/queued/{paymentId}")]
|
[HttpDelete("api/billing/queued/{paymentId}")]
|
||||||
public async Task<IActionResult> CancelQueued(string paymentId, ITenantContext tenant, CancellationToken ct)
|
public async Task<IActionResult> CancelQueued(string paymentId, ITenantContext tenant, CancellationToken ct)
|
||||||
{
|
{
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageBilling) is { } permDenied) return permDenied;
|
||||||
if (string.IsNullOrEmpty(tenant.CafeId))
|
if (string.IsNullOrEmpty(tenant.CafeId))
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
if (tenant.Role != Core.Enums.EmployeeRole.Owner)
|
|
||||||
return StatusCode(403, new ApiResponse<object>(false, null,
|
|
||||||
new ApiError("OWNER_REQUIRED", "Only the cafe owner can manage subscription billing.")));
|
|
||||||
|
|
||||||
var (ok, code, message) = await _billing.CancelQueuedAsync(tenant.CafeId, paymentId, ct);
|
var (ok, code, message) = await _billing.CancelQueuedAsync(tenant.CafeId, paymentId, ct);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Menu;
|
using Meezi.API.Models.Menu;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Enums;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
@@ -43,7 +43,6 @@ public class BranchMenuController : CafeApiControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("{menuItemId}/override")]
|
[HttpPut("{menuItemId}/override")]
|
||||||
[Authorize(Roles = "Manager,Owner")]
|
|
||||||
public async Task<IActionResult> UpsertOverride(
|
public async Task<IActionResult> UpsertOverride(
|
||||||
string cafeId,
|
string cafeId,
|
||||||
string branchId,
|
string branchId,
|
||||||
@@ -53,8 +52,7 @@ public class BranchMenuController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (!BranchMenuService.CanManageOverrides(tenant.Role))
|
if (EnsurePermission(tenant, Permission.EditMenuItem) is { } permDenied) return permDenied;
|
||||||
return Forbid();
|
|
||||||
|
|
||||||
var validation = await _upsertValidator.ValidateAsync(request, cancellationToken);
|
var validation = await _upsertValidator.ValidateAsync(request, cancellationToken);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
@@ -84,7 +82,6 @@ public class BranchMenuController : CafeApiControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{menuItemId}/override")]
|
[HttpDelete("{menuItemId}/override")]
|
||||||
[Authorize(Roles = "Owner")]
|
|
||||||
public async Task<IActionResult> DeleteOverride(
|
public async Task<IActionResult> DeleteOverride(
|
||||||
string cafeId,
|
string cafeId,
|
||||||
string branchId,
|
string branchId,
|
||||||
@@ -93,6 +90,7 @@ public class BranchMenuController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditMenuItem) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var deleted = await _branchMenu.DeleteOverrideAsync(
|
var deleted = await _branchMenu.DeleteOverrideAsync(
|
||||||
cafeId, branchId, menuItemId, cancellationToken);
|
cafeId, branchId, menuItemId, cancellationToken);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Printing;
|
using Meezi.API.Models.Printing;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Entities;
|
using Meezi.Core.Entities;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Infrastructure.Data;
|
using Meezi.Infrastructure.Data;
|
||||||
@@ -11,7 +11,6 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
namespace Meezi.API.Controllers;
|
namespace Meezi.API.Controllers;
|
||||||
|
|
||||||
[Route("api/cafes/{cafeId}/branches/{branchId}/print-settings")]
|
[Route("api/cafes/{cafeId}/branches/{branchId}/print-settings")]
|
||||||
[Authorize(Roles = "Manager,Owner")]
|
|
||||||
public class BranchPrintSettingsController : CafeApiControllerBase
|
public class BranchPrintSettingsController : CafeApiControllerBase
|
||||||
{
|
{
|
||||||
private readonly AppDbContext _db;
|
private readonly AppDbContext _db;
|
||||||
@@ -54,6 +53,7 @@ public class BranchPrintSettingsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManagePrintSettings) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var validation = await _validator.ValidateAsync(request, ct);
|
var validation = await _validator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Tables;
|
using Meezi.API.Models.Tables;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Enums;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
@@ -68,7 +68,6 @@ public class BranchTablesController : CafeApiControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Authorize(Roles = "Manager,Owner")]
|
|
||||||
public async Task<IActionResult> CreateTable(
|
public async Task<IActionResult> CreateTable(
|
||||||
string cafeId,
|
string cafeId,
|
||||||
string branchId,
|
string branchId,
|
||||||
@@ -77,6 +76,7 @@ public class BranchTablesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied;
|
||||||
if (!await _tables.CanAccessBranchAsync(cafeId, branchId, tenant.UserId, tenant.Role, ct))
|
if (!await _tables.CanAccessBranchAsync(cafeId, branchId, tenant.UserId, tenant.Role, ct))
|
||||||
return Forbid();
|
return Forbid();
|
||||||
|
|
||||||
@@ -88,7 +88,6 @@ public class BranchTablesController : CafeApiControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPatch("{id}")]
|
[HttpPatch("{id}")]
|
||||||
[Authorize(Roles = "Manager,Owner")]
|
|
||||||
public async Task<IActionResult> PatchTable(
|
public async Task<IActionResult> PatchTable(
|
||||||
string cafeId,
|
string cafeId,
|
||||||
string branchId,
|
string branchId,
|
||||||
@@ -98,6 +97,7 @@ public class BranchTablesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied;
|
||||||
if (!await _tables.CanAccessBranchAsync(cafeId, branchId, tenant.UserId, tenant.Role, ct))
|
if (!await _tables.CanAccessBranchAsync(cafeId, branchId, tenant.UserId, tenant.Role, ct))
|
||||||
return Forbid();
|
return Forbid();
|
||||||
|
|
||||||
@@ -109,7 +109,6 @@ public class BranchTablesController : CafeApiControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
[Authorize(Roles = "Manager,Owner")]
|
|
||||||
public async Task<IActionResult> DeleteTable(
|
public async Task<IActionResult> DeleteTable(
|
||||||
string cafeId,
|
string cafeId,
|
||||||
string branchId,
|
string branchId,
|
||||||
@@ -118,6 +117,7 @@ public class BranchTablesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied;
|
||||||
if (!await _tables.CanAccessBranchAsync(cafeId, branchId, tenant.UserId, tenant.Role, ct))
|
if (!await _tables.CanAccessBranchAsync(cafeId, branchId, tenant.UserId, tenant.Role, ct))
|
||||||
return Forbid();
|
return Forbid();
|
||||||
|
|
||||||
@@ -135,6 +135,7 @@ public class BranchTablesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied;
|
||||||
if (!await _tables.CanAccessBranchAsync(cafeId, branchId, tenant.UserId, tenant.Role, ct))
|
if (!await _tables.CanAccessBranchAsync(cafeId, branchId, tenant.UserId, tenant.Role, ct))
|
||||||
return Forbid();
|
return Forbid();
|
||||||
|
|
||||||
@@ -180,7 +181,6 @@ public class BranchTablesController : CafeApiControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("sections")]
|
[HttpPost("sections")]
|
||||||
[Authorize(Roles = "Manager,Owner")]
|
|
||||||
public async Task<IActionResult> CreateSection(
|
public async Task<IActionResult> CreateSection(
|
||||||
string cafeId,
|
string cafeId,
|
||||||
string branchId,
|
string branchId,
|
||||||
@@ -189,6 +189,7 @@ public class BranchTablesController : CafeApiControllerBase
|
|||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied;
|
||||||
if (!await _tables.CanAccessBranchAsync(cafeId, branchId, tenant.UserId, tenant.Role, ct))
|
if (!await _tables.CanAccessBranchAsync(cafeId, branchId, tenant.UserId, tenant.Role, ct))
|
||||||
return Forbid();
|
return Forbid();
|
||||||
|
|
||||||
@@ -200,7 +201,6 @@ public class BranchTablesController : CafeApiControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPatch("sections/{sectionId}")]
|
[HttpPatch("sections/{sectionId}")]
|
||||||
[Authorize(Roles = "Manager,Owner")]
|
|
||||||
public async Task<IActionResult> PatchSection(
|
public async Task<IActionResult> PatchSection(
|
||||||
string cafeId,
|
string cafeId,
|
||||||
string branchId,
|
string branchId,
|
||||||
@@ -210,6 +210,7 @@ public class BranchTablesController : CafeApiControllerBase
|
|||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied;
|
||||||
if (!await _tables.CanAccessBranchAsync(cafeId, branchId, tenant.UserId, tenant.Role, ct))
|
if (!await _tables.CanAccessBranchAsync(cafeId, branchId, tenant.UserId, tenant.Role, ct))
|
||||||
return Forbid();
|
return Forbid();
|
||||||
|
|
||||||
@@ -221,7 +222,6 @@ public class BranchTablesController : CafeApiControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("sections/{sectionId}")]
|
[HttpDelete("sections/{sectionId}")]
|
||||||
[Authorize(Roles = "Manager,Owner")]
|
|
||||||
public async Task<IActionResult> DeleteSection(
|
public async Task<IActionResult> DeleteSection(
|
||||||
string cafeId,
|
string cafeId,
|
||||||
string branchId,
|
string branchId,
|
||||||
@@ -230,6 +230,7 @@ public class BranchTablesController : CafeApiControllerBase
|
|||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied;
|
||||||
if (!await _tables.CanAccessBranchAsync(cafeId, branchId, tenant.UserId, tenant.Role, ct))
|
if (!await _tables.CanAccessBranchAsync(cafeId, branchId, tenant.UserId, tenant.Role, ct))
|
||||||
return Forbid();
|
return Forbid();
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Meezi.API.Models.Branches;
|
using Meezi.API.Models.Branches;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Constants;
|
using Meezi.Core.Constants;
|
||||||
using Meezi.Core.Entities;
|
using Meezi.Core.Entities;
|
||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Enums;
|
||||||
@@ -96,7 +97,7 @@ public class BranchesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureOwner(tenant) is { } ownerDenied) return ownerDenied;
|
if (EnsurePermission(tenant, Permission.CreateBranch) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var validation = await _createValidator.ValidateAsync(request, ct);
|
var validation = await _createValidator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid)
|
if (!validation.IsValid)
|
||||||
@@ -169,7 +170,7 @@ public class BranchesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureOwner(tenant) is { } ownerDenied) return ownerDenied;
|
if (EnsurePermission(tenant, Permission.EditBranch) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var validation = await _patchValidator.ValidateAsync(request, ct);
|
var validation = await _patchValidator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid)
|
if (!validation.IsValid)
|
||||||
@@ -222,7 +223,7 @@ public class BranchesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureOwner(tenant) is { } ownerDenied) return ownerDenied;
|
if (EnsurePermission(tenant, Permission.DeleteBranch) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var (ok, code, message) = await _lifecycle.ScheduleDeletionAsync(cafeId, branchId, ct);
|
var (ok, code, message) = await _lifecycle.ScheduleDeletionAsync(cafeId, branchId, ct);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
@@ -257,7 +258,7 @@ public class BranchesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureOwner(tenant) is { } ownerDenied) return ownerDenied;
|
if (EnsurePermission(tenant, Permission.EditBranch) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var (ok, code, message) = await _lifecycle.RestoreAsync(cafeId, branchId, ct);
|
var (ok, code, message) = await _lifecycle.RestoreAsync(cafeId, branchId, ct);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authorization;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Discover;
|
using Meezi.API.Models.Discover;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Enums;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Infrastructure.Data;
|
using Meezi.Infrastructure.Data;
|
||||||
@@ -45,6 +46,7 @@ public class CafeDiscoverProfileController : CafeApiControllerBase
|
|||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied)
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied)
|
||||||
return denied;
|
return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageDiscoverProfile) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var planTier = tenant.PlanTier ?? PlanTier.Free;
|
var planTier = tenant.PlanTier ?? PlanTier.Free;
|
||||||
if (!await catalog.IsFeatureEnabledForCafeAsync(cafeId, planTier, "discover_profile", ct))
|
if (!await catalog.IsFeatureEnabledForCafeAsync(cafeId, planTier, "discover_profile", ct))
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Public;
|
using Meezi.API.Models.Public;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Discover;
|
using Meezi.Core.Discover;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Infrastructure.Data;
|
using Meezi.Infrastructure.Data;
|
||||||
@@ -71,6 +72,7 @@ public class CafePublicProfileController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageDiscoverProfile) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var cafe = await _db.Cafes.FirstOrDefaultAsync(c => c.Id == cafeId, ct);
|
var cafe = await _db.Cafes.FirstOrDefaultAsync(c => c.Id == cafeId, ct);
|
||||||
if (cafe is null)
|
if (cafe is null)
|
||||||
@@ -121,6 +123,7 @@ public class CafePublicProfileController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageDiscoverProfile) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
if (photo is null || photo.Length == 0)
|
if (photo is null || photo.Length == 0)
|
||||||
return BadRequest(Fail("NO_FILE", "No photo provided."));
|
return BadRequest(Fail("NO_FILE", "No photo provided."));
|
||||||
@@ -155,6 +158,7 @@ public class CafePublicProfileController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageDiscoverProfile) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(url))
|
if (string.IsNullOrWhiteSpace(url))
|
||||||
return BadRequest(Fail("NO_URL", "Provide ?url= of the photo to remove."));
|
return BadRequest(Fail("NO_URL", "Provide ?url= of the photo to remove."));
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using FluentValidation;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Public;
|
using Meezi.API.Models.Public;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Enums;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Infrastructure.Services.Platform;
|
using Meezi.Infrastructure.Services.Platform;
|
||||||
@@ -48,6 +49,7 @@ public class CafeReviewsController : CafeApiControllerBase
|
|||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageReviews) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
// Replying to reviews is a paid feature (Starter+).
|
// Replying to reviews is a paid feature (Starter+).
|
||||||
var tier = tenant.PlanTier ?? PlanTier.Free;
|
var tier = tenant.PlanTier ?? PlanTier.Free;
|
||||||
@@ -76,6 +78,7 @@ public class CafeReviewsController : CafeApiControllerBase
|
|||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageReviews) is { } permDenied) return permDenied;
|
||||||
var data = await _reviews.SetHiddenAsync(cafeId, reviewId, request.IsHidden, ct);
|
var data = await _reviews.SetHiddenAsync(cafeId, reviewId, request.IsHidden, ct);
|
||||||
if (data is null) return NotFoundError();
|
if (data is null) return NotFoundError();
|
||||||
return Ok(new ApiResponse<CafeReviewDto>(true, data));
|
return Ok(new ApiResponse<CafeReviewDto>(true, data));
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Meezi.API.Models.Cafes;
|
using Meezi.API.Models.Cafes;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Enums;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Core.Utilities;
|
using Meezi.Core.Utilities;
|
||||||
@@ -47,6 +48,7 @@ public class CafeSettingsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageCafeSettings) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var validation = await _validator.ValidateAsync(request, ct);
|
var validation = await _validator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid)
|
if (!validation.IsValid)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using FluentValidation;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Crm;
|
using Meezi.API.Models.Crm;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
|
|
||||||
@@ -62,6 +63,7 @@ public class CouponsController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.CreateCoupon) is { } permDenied) return permDenied;
|
||||||
var validation = await _createValidator.ValidateAsync(request, cancellationToken);
|
var validation = await _createValidator.ValidateAsync(request, cancellationToken);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -82,6 +84,7 @@ public class CouponsController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditCoupon) is { } permDenied) return permDenied;
|
||||||
var data = await _couponService.UpdateAsync(cafeId, id, request, cancellationToken);
|
var data = await _couponService.UpdateAsync(cafeId, id, request, cancellationToken);
|
||||||
if (data is null) return NotFoundError();
|
if (data is null) return NotFoundError();
|
||||||
return Ok(new ApiResponse<CouponDto>(true, data));
|
return Ok(new ApiResponse<CouponDto>(true, data));
|
||||||
@@ -95,6 +98,7 @@ public class CouponsController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.DeleteCoupon) is { } permDenied) return permDenied;
|
||||||
var deleted = await _couponService.DeleteAsync(cafeId, id, cancellationToken);
|
var deleted = await _couponService.DeleteAsync(cafeId, id, cancellationToken);
|
||||||
if (!deleted) return NotFoundError();
|
if (!deleted) return NotFoundError();
|
||||||
return Ok(new ApiResponse<object>(true, new { id }));
|
return Ok(new ApiResponse<object>(true, new { id }));
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class CustomRolesController : CafeApiControllerBase
|
|||||||
public async Task<IActionResult> List(string cafeId, ITenantContext tenant, CancellationToken ct)
|
public async Task<IActionResult> List(string cafeId, ITenantContext tenant, CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureOwner(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ManageRoles) is { } forbidden) return forbidden;
|
||||||
|
|
||||||
var roles = await _db.CustomRoles
|
var roles = await _db.CustomRoles
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
@@ -57,7 +57,7 @@ public class CustomRolesController : CafeApiControllerBase
|
|||||||
public async Task<IActionResult> Get(string cafeId, string id, ITenantContext tenant, CancellationToken ct)
|
public async Task<IActionResult> Get(string cafeId, string id, ITenantContext tenant, CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureOwner(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ManageRoles) is { } forbidden) return forbidden;
|
||||||
|
|
||||||
var r = await _db.CustomRoles.AsNoTracking()
|
var r = await _db.CustomRoles.AsNoTracking()
|
||||||
.FirstOrDefaultAsync(x => x.Id == id && x.CafeId == cafeId, ct);
|
.FirstOrDefaultAsync(x => x.Id == id && x.CafeId == cafeId, ct);
|
||||||
@@ -80,7 +80,7 @@ public class CustomRolesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureOwner(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ManageRoles) is { } forbidden) return forbidden;
|
||||||
|
|
||||||
var name = request.Name?.Trim() ?? string.Empty;
|
var name = request.Name?.Trim() ?? string.Empty;
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
@@ -113,7 +113,7 @@ public class CustomRolesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureOwner(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ManageRoles) is { } forbidden) return forbidden;
|
||||||
|
|
||||||
var role = await _db.CustomRoles
|
var role = await _db.CustomRoles
|
||||||
.FirstOrDefaultAsync(r => r.Id == id && r.CafeId == cafeId, ct);
|
.FirstOrDefaultAsync(r => r.Id == id && r.CafeId == cafeId, ct);
|
||||||
@@ -152,7 +152,7 @@ public class CustomRolesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureOwner(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ManageRoles) is { } forbidden) return forbidden;
|
||||||
|
|
||||||
var role = await _db.CustomRoles
|
var role = await _db.CustomRoles
|
||||||
.FirstOrDefaultAsync(r => r.Id == id && r.CafeId == cafeId, ct);
|
.FirstOrDefaultAsync(r => r.Id == id && r.CafeId == cafeId, ct);
|
||||||
@@ -180,7 +180,7 @@ public class CustomRolesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureOwner(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ManageRoles) is { } forbidden) return forbidden;
|
||||||
|
|
||||||
var employee = await _db.Employees
|
var employee = await _db.Employees
|
||||||
.FirstOrDefaultAsync(e => e.Id == employeeId && e.CafeId == cafeId && e.DeletedAt == null, ct);
|
.FirstOrDefaultAsync(e => e.Id == employeeId && e.CafeId == cafeId && e.DeletedAt == null, ct);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using FluentValidation;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Crm;
|
using Meezi.API.Models.Crm;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
|
|
||||||
@@ -57,6 +58,7 @@ public class CustomersController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.CreateCustomer) is { } permDenied) return permDenied;
|
||||||
var validation = await _createValidator.ValidateAsync(request, cancellationToken);
|
var validation = await _createValidator.ValidateAsync(request, cancellationToken);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -77,6 +79,7 @@ public class CustomersController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditCustomer) is { } permDenied) return permDenied;
|
||||||
var validation = await _updateValidator.ValidateAsync(request, cancellationToken);
|
var validation = await _updateValidator.ValidateAsync(request, cancellationToken);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -99,6 +102,7 @@ public class CustomersController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.DeleteCustomer) is { } permDenied) return permDenied;
|
||||||
var deleted = await _customerService.DeleteAsync(cafeId, id, cancellationToken);
|
var deleted = await _customerService.DeleteAsync(cafeId, id, cancellationToken);
|
||||||
if (!deleted) return NotFoundError();
|
if (!deleted) return NotFoundError();
|
||||||
return Ok(new ApiResponse<object>(true, new { id }));
|
return Ok(new ApiResponse<object>(true, new { id }));
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Services.Delivery;
|
using Meezi.API.Services.Delivery;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ public class DeliveryReportsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ViewReports) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var utcTo = to ?? DateTime.UtcNow;
|
var utcTo = to ?? DateTime.UtcNow;
|
||||||
var utcFrom = from ?? utcTo.AddDays(-30);
|
var utcFrom = from ?? utcTo.AddDays(-30);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using FluentValidation;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Expenses;
|
using Meezi.API.Models.Expenses;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
|
|
||||||
@@ -30,14 +30,11 @@ public class ExpensesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.CreateExpense) is { } permDenied) return permDenied;
|
||||||
if (string.IsNullOrEmpty(tenant.UserId))
|
if (string.IsNullOrEmpty(tenant.UserId))
|
||||||
return StatusCode(StatusCodes.Status401Unauthorized,
|
return StatusCode(StatusCodes.Status401Unauthorized,
|
||||||
new ApiResponse<object>(false, null, new ApiError("UNAUTHORIZED", "User context is missing.")));
|
new ApiResponse<object>(false, null, new ApiError("UNAUTHORIZED", "User context is missing.")));
|
||||||
|
|
||||||
if (!CanLogExpense(tenant.Role))
|
|
||||||
return StatusCode(StatusCodes.Status403Forbidden,
|
|
||||||
new ApiResponse<object>(false, null, new ApiError("FORBIDDEN", "You cannot log expenses.")));
|
|
||||||
|
|
||||||
var validation = await _createValidator.ValidateAsync(request, ct);
|
var validation = await _createValidator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -85,10 +82,7 @@ public class ExpensesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.DeleteExpense) is { } permDenied) return permDenied;
|
||||||
if (!CanDeleteExpense(tenant.Role))
|
|
||||||
return StatusCode(StatusCodes.Status403Forbidden,
|
|
||||||
new ApiResponse<object>(false, null, new ApiError("FORBIDDEN", "Only managers can delete expenses.")));
|
|
||||||
|
|
||||||
var result = await _expenses.DeleteExpenseAsync(cafeId, id, ct);
|
var result = await _expenses.DeleteExpenseAsync(cafeId, id, ct);
|
||||||
if (!result.Success)
|
if (!result.Success)
|
||||||
@@ -104,12 +98,6 @@ public class ExpensesController : CafeApiControllerBase
|
|||||||
return Ok(new ApiResponse<object>(true, null));
|
return Ok(new ApiResponse<object>(true, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool CanLogExpense(EmployeeRole? role) =>
|
|
||||||
role is EmployeeRole.Owner or EmployeeRole.Manager or EmployeeRole.Cashier;
|
|
||||||
|
|
||||||
private static bool CanDeleteExpense(EmployeeRole? role) =>
|
|
||||||
role is EmployeeRole.Owner or EmployeeRole.Manager;
|
|
||||||
|
|
||||||
private IActionResult ExpenseResult(ExpenseServiceResult<ExpenseDto> result, int successStatus = StatusCodes.Status200OK)
|
private IActionResult ExpenseResult(ExpenseServiceResult<ExpenseDto> result, int successStatus = StatusCodes.Status200OK)
|
||||||
{
|
{
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Meezi.API.Models.Hr;
|
using Meezi.API.Models.Hr;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Entities;
|
using Meezi.Core.Entities;
|
||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Enums;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
@@ -57,7 +58,7 @@ public class HrController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureManager(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.CreateStaff) is { } forbidden) return forbidden;
|
||||||
|
|
||||||
IActionResult Invalid(string message, string field) =>
|
IActionResult Invalid(string message, string field) =>
|
||||||
BadRequest(new ApiResponse<object>(false, null, new ApiError("VALIDATION_ERROR", message, field)));
|
BadRequest(new ApiResponse<object>(false, null, new ApiError("VALIDATION_ERROR", message, field)));
|
||||||
@@ -204,7 +205,7 @@ public class HrController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureManager(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ManageSchedules) is { } forbidden) return forbidden;
|
||||||
var data = await _hr.UpsertShiftsAsync(cafeId, employeeId, request, ct);
|
var data = await _hr.UpsertShiftsAsync(cafeId, employeeId, request, ct);
|
||||||
return Ok(new ApiResponse<IReadOnlyList<ShiftDto>>(true, data));
|
return Ok(new ApiResponse<IReadOnlyList<ShiftDto>>(true, data));
|
||||||
}
|
}
|
||||||
@@ -217,6 +218,7 @@ public class HrController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ReviewLeave) is { } forbidden) return forbidden;
|
||||||
var data = await _hr.GetLeaveRequestsAsync(cafeId, status, ct);
|
var data = await _hr.GetLeaveRequestsAsync(cafeId, status, ct);
|
||||||
return Ok(new ApiResponse<IReadOnlyList<LeaveRequestDto>>(true, data));
|
return Ok(new ApiResponse<IReadOnlyList<LeaveRequestDto>>(true, data));
|
||||||
}
|
}
|
||||||
@@ -248,7 +250,7 @@ public class HrController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureManager(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ReviewLeave) is { } forbidden) return forbidden;
|
||||||
var validation = await _reviewValidator.ValidateAsync(request, ct);
|
var validation = await _reviewValidator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -265,6 +267,7 @@ public class HrController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ViewSalaries) is { } forbidden) return forbidden;
|
||||||
var data = await _hr.GetSalariesAsync(cafeId, monthYear, ct);
|
var data = await _hr.GetSalariesAsync(cafeId, monthYear, ct);
|
||||||
return Ok(new ApiResponse<IReadOnlyList<EmployeeSalaryDto>>(true, data));
|
return Ok(new ApiResponse<IReadOnlyList<EmployeeSalaryDto>>(true, data));
|
||||||
}
|
}
|
||||||
@@ -277,7 +280,7 @@ public class HrController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureManager(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ManageSalaries) is { } forbidden) return forbidden;
|
||||||
var validation = await _salaryValidator.ValidateAsync(request, ct);
|
var validation = await _salaryValidator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -290,7 +293,7 @@ public class HrController : CafeApiControllerBase
|
|||||||
public async Task<IActionResult> MarkPaid(string cafeId, string salaryId, ITenantContext tenant, CancellationToken ct)
|
public async Task<IActionResult> MarkPaid(string cafeId, string salaryId, ITenantContext tenant, CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureManager(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ManageSalaries) is { } forbidden) return forbidden;
|
||||||
var data = await _hr.MarkSalaryPaidAsync(cafeId, salaryId, ct);
|
var data = await _hr.MarkSalaryPaidAsync(cafeId, salaryId, ct);
|
||||||
if (data is null) return NotFoundError();
|
if (data is null) return NotFoundError();
|
||||||
return Ok(new ApiResponse<EmployeeSalaryDto>(true, data));
|
return Ok(new ApiResponse<EmployeeSalaryDto>(true, data));
|
||||||
@@ -306,7 +309,7 @@ public class HrController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureManager(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ManageStaffCredentials) is { } forbidden) return forbidden;
|
||||||
|
|
||||||
var username = request.Username.Trim().ToLowerInvariant();
|
var username = request.Username.Trim().ToLowerInvariant();
|
||||||
|
|
||||||
@@ -344,7 +347,7 @@ public class HrController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureManager(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ManageStaffCredentials) is { } forbidden) return forbidden;
|
||||||
|
|
||||||
var employee = await _db.Employees
|
var employee = await _db.Employees
|
||||||
.FirstOrDefaultAsync(e => e.Id == employeeId && e.CafeId == cafeId && e.DeletedAt == null, ct);
|
.FirstOrDefaultAsync(e => e.Id == employeeId && e.CafeId == cafeId && e.DeletedAt == null, ct);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
|
|
||||||
@@ -36,6 +37,7 @@ public class InventoryController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.CreateInventory) is { } permDenied) return permDenied;
|
||||||
if (string.IsNullOrWhiteSpace(request.Name))
|
if (string.IsNullOrWhiteSpace(request.Name))
|
||||||
return BadRequest(new ApiResponse<object>(false, null, new ApiError("VALIDATION_ERROR", "Name is required.")));
|
return BadRequest(new ApiResponse<object>(false, null, new ApiError("VALIDATION_ERROR", "Name is required.")));
|
||||||
|
|
||||||
@@ -56,6 +58,7 @@ public class InventoryController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditInventory) is { } permDenied) return permDenied;
|
||||||
var updated = await _inventory.UpdateAsync(cafeId, ingredientId, request, ct);
|
var updated = await _inventory.UpdateAsync(cafeId, ingredientId, request, ct);
|
||||||
if (updated is null) return NotFoundError();
|
if (updated is null) return NotFoundError();
|
||||||
return Ok(new ApiResponse<object>(true, updated));
|
return Ok(new ApiResponse<object>(true, updated));
|
||||||
@@ -69,6 +72,7 @@ public class InventoryController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.DeleteInventory) is { } permDenied) return permDenied;
|
||||||
var deleted = await _inventory.DeleteAsync(cafeId, ingredientId, ct);
|
var deleted = await _inventory.DeleteAsync(cafeId, ingredientId, ct);
|
||||||
if (!deleted) return NotFoundError();
|
if (!deleted) return NotFoundError();
|
||||||
return Ok(new ApiResponse<object>(true, new { id = ingredientId }));
|
return Ok(new ApiResponse<object>(true, new { id = ingredientId }));
|
||||||
@@ -83,6 +87,7 @@ public class InventoryController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditInventory) is { } permDenied) return permDenied;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var updated = await _inventory.AdjustAsync(cafeId, ingredientId, request, tenant.UserId, ct);
|
var updated = await _inventory.AdjustAsync(cafeId, ingredientId, request, tenant.UserId, ct);
|
||||||
@@ -146,6 +151,7 @@ public class InventoryController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditInventory) is { } permDenied) return permDenied;
|
||||||
var recipe = await _inventory.SetRecipeAsync(cafeId, menuItemId, request, ct);
|
var recipe = await _inventory.SetRecipeAsync(cafeId, menuItemId, request, ct);
|
||||||
if (recipe is null) return NotFoundError("Menu item not found.");
|
if (recipe is null) return NotFoundError("Menu item not found.");
|
||||||
return Ok(new ApiResponse<object>(true, recipe));
|
return Ok(new ApiResponse<object>(true, recipe));
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using FluentValidation;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Kitchen;
|
using Meezi.API.Models.Kitchen;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ public class KitchenStationsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageKitchenStations) is { } permDenied) return permDenied;
|
||||||
var validation = await _createValidator.ValidateAsync(request, ct);
|
var validation = await _createValidator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -59,6 +61,7 @@ public class KitchenStationsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageKitchenStations) is { } permDenied) return permDenied;
|
||||||
var validation = await _updateValidator.ValidateAsync(request, ct);
|
var validation = await _updateValidator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -71,6 +74,7 @@ public class KitchenStationsController : CafeApiControllerBase
|
|||||||
public async Task<IActionResult> Delete(string cafeId, string id, ITenantContext tenant, CancellationToken ct)
|
public async Task<IActionResult> Delete(string cafeId, string id, ITenantContext tenant, CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageKitchenStations) is { } permDenied) return permDenied;
|
||||||
var ok = await _stations.DeleteAsync(cafeId, id, ct);
|
var ok = await _stations.DeleteAsync(cafeId, id, ct);
|
||||||
if (!ok) return NotFoundError();
|
if (!ok) return NotFoundError();
|
||||||
return Ok(new ApiResponse<object>(true, new { id }));
|
return Ok(new ApiResponse<object>(true, new { id }));
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Enums;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Infrastructure.Data;
|
using Meezi.Infrastructure.Data;
|
||||||
@@ -20,13 +21,21 @@ public class MediaController : CafeApiControllerBase
|
|||||||
[RequestSizeLimit(5 * 1024 * 1024)]
|
[RequestSizeLimit(5 * 1024 * 1024)]
|
||||||
public Task<IActionResult> UploadMenuImage(
|
public Task<IActionResult> UploadMenuImage(
|
||||||
string cafeId, IFormFile file, ITenantContext tenant, CancellationToken cancellationToken)
|
string cafeId, IFormFile file, ITenantContext tenant, CancellationToken cancellationToken)
|
||||||
=> Upload(cafeId, file, tenant, _media.SaveMenuImageAsync, "INVALID_FILE", "Use JPEG/PNG/WebP up to 5MB.", cancellationToken);
|
{
|
||||||
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return Task.FromResult(denied);
|
||||||
|
if (EnsurePermission(tenant, Permission.EditMenuItem) is { } permDenied) return Task.FromResult(permDenied);
|
||||||
|
return Upload(cafeId, file, tenant, _media.SaveMenuImageAsync, "INVALID_FILE", "Use JPEG/PNG/WebP up to 5MB.", cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("menu-video")]
|
[HttpPost("menu-video")]
|
||||||
[RequestSizeLimit(25 * 1024 * 1024)]
|
[RequestSizeLimit(25 * 1024 * 1024)]
|
||||||
public Task<IActionResult> UploadMenuVideo(
|
public Task<IActionResult> UploadMenuVideo(
|
||||||
string cafeId, IFormFile file, ITenantContext tenant, CancellationToken cancellationToken)
|
string cafeId, IFormFile file, ITenantContext tenant, CancellationToken cancellationToken)
|
||||||
=> Upload(cafeId, file, tenant, _media.SaveMenuVideoAsync, "INVALID_FILE", "Use MP4/WebM/MOV up to 25MB.", cancellationToken);
|
{
|
||||||
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return Task.FromResult(denied);
|
||||||
|
if (EnsurePermission(tenant, Permission.EditMenuItem) is { } permDenied) return Task.FromResult(permDenied);
|
||||||
|
return Upload(cafeId, file, tenant, _media.SaveMenuVideoAsync, "INVALID_FILE", "Use MP4/WebM/MOV up to 25MB.", cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("menu-model3d")]
|
[HttpPost("menu-model3d")]
|
||||||
[RequestSizeLimit(8 * 1024 * 1024)]
|
[RequestSizeLimit(8 * 1024 * 1024)]
|
||||||
@@ -38,6 +47,7 @@ public class MediaController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditMenuItem) is { } permDenied) return permDenied;
|
||||||
var planTier = tenant.PlanTier ?? PlanTier.Free;
|
var planTier = tenant.PlanTier ?? PlanTier.Free;
|
||||||
if (!await catalog.IsFeatureEnabledForCafeAsync(cafeId, planTier, "menu_3d", cancellationToken))
|
if (!await catalog.IsFeatureEnabledForCafeAsync(cafeId, planTier, "menu_3d", cancellationToken))
|
||||||
{
|
{
|
||||||
@@ -63,25 +73,41 @@ public class MediaController : CafeApiControllerBase
|
|||||||
[RequestSizeLimit(5 * 1024 * 1024)]
|
[RequestSizeLimit(5 * 1024 * 1024)]
|
||||||
public Task<IActionResult> UploadTableImage(
|
public Task<IActionResult> UploadTableImage(
|
||||||
string cafeId, IFormFile file, ITenantContext tenant, CancellationToken cancellationToken)
|
string cafeId, IFormFile file, ITenantContext tenant, CancellationToken cancellationToken)
|
||||||
=> Upload(cafeId, file, tenant, _media.SaveTableImageAsync, "INVALID_FILE", "Use JPEG/PNG/WebP up to 5MB.", cancellationToken);
|
{
|
||||||
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return Task.FromResult(denied);
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return Task.FromResult(permDenied);
|
||||||
|
return Upload(cafeId, file, tenant, _media.SaveTableImageAsync, "INVALID_FILE", "Use JPEG/PNG/WebP up to 5MB.", cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("table-video")]
|
[HttpPost("table-video")]
|
||||||
[RequestSizeLimit(25 * 1024 * 1024)]
|
[RequestSizeLimit(25 * 1024 * 1024)]
|
||||||
public Task<IActionResult> UploadTableVideo(
|
public Task<IActionResult> UploadTableVideo(
|
||||||
string cafeId, IFormFile file, ITenantContext tenant, CancellationToken cancellationToken)
|
string cafeId, IFormFile file, ITenantContext tenant, CancellationToken cancellationToken)
|
||||||
=> Upload(cafeId, file, tenant, _media.SaveTableVideoAsync, "INVALID_FILE", "Use MP4/WebM/MOV up to 25MB.", cancellationToken);
|
{
|
||||||
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return Task.FromResult(denied);
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return Task.FromResult(permDenied);
|
||||||
|
return Upload(cafeId, file, tenant, _media.SaveTableVideoAsync, "INVALID_FILE", "Use MP4/WebM/MOV up to 25MB.", cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("cafe-logo")]
|
[HttpPost("cafe-logo")]
|
||||||
[RequestSizeLimit(5 * 1024 * 1024)]
|
[RequestSizeLimit(5 * 1024 * 1024)]
|
||||||
public Task<IActionResult> UploadCafeLogo(
|
public Task<IActionResult> UploadCafeLogo(
|
||||||
string cafeId, IFormFile file, ITenantContext tenant, CancellationToken cancellationToken)
|
string cafeId, IFormFile file, ITenantContext tenant, CancellationToken cancellationToken)
|
||||||
=> Upload(cafeId, file, tenant, _media.SaveCafeLogoAsync, "INVALID_FILE", "Use JPEG/PNG/WebP up to 5MB.", cancellationToken);
|
{
|
||||||
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return Task.FromResult(denied);
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageCafeSettings) is { } permDenied) return Task.FromResult(permDenied);
|
||||||
|
return Upload(cafeId, file, tenant, _media.SaveCafeLogoAsync, "INVALID_FILE", "Use JPEG/PNG/WebP up to 5MB.", cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("cafe-cover")]
|
[HttpPost("cafe-cover")]
|
||||||
[RequestSizeLimit(5 * 1024 * 1024)]
|
[RequestSizeLimit(5 * 1024 * 1024)]
|
||||||
public Task<IActionResult> UploadCafeCover(
|
public Task<IActionResult> UploadCafeCover(
|
||||||
string cafeId, IFormFile file, ITenantContext tenant, CancellationToken cancellationToken)
|
string cafeId, IFormFile file, ITenantContext tenant, CancellationToken cancellationToken)
|
||||||
=> Upload(cafeId, file, tenant, _media.SaveCafeCoverAsync, "INVALID_FILE", "Use JPEG/PNG/WebP up to 5MB.", cancellationToken);
|
{
|
||||||
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return Task.FromResult(denied);
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageCafeSettings) is { } permDenied) return Task.FromResult(permDenied);
|
||||||
|
return Upload(cafeId, file, tenant, _media.SaveCafeCoverAsync, "INVALID_FILE", "Use JPEG/PNG/WebP up to 5MB.", cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Media library for this café — previously uploaded files so the UI can
|
/// <summary>Media library for this café — previously uploaded files so the UI can
|
||||||
/// reuse one instead of re-uploading. Deduplication means each distinct file appears once.</summary>
|
/// reuse one instead of re-uploading. Deduplication means each distinct file appears once.</summary>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Meezi.API.Models.Menu;
|
using Meezi.API.Models.Menu;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Constants;
|
using Meezi.Core.Constants;
|
||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Enums;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
@@ -59,6 +60,7 @@ public class MenuController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.CreateMenuItem) is { } permDenied) return permDenied;
|
||||||
var validation = await _createCategoryValidator.ValidateAsync(request, cancellationToken);
|
var validation = await _createCategoryValidator.ValidateAsync(request, cancellationToken);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -86,6 +88,7 @@ public class MenuController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditMenuItem) is { } permDenied) return permDenied;
|
||||||
var data = await _menuService.UpdateCategoryAsync(cafeId, id, request, cancellationToken);
|
var data = await _menuService.UpdateCategoryAsync(cafeId, id, request, cancellationToken);
|
||||||
if (data is null) return NotFoundError();
|
if (data is null) return NotFoundError();
|
||||||
return Ok(new ApiResponse<MenuCategoryDto>(true, data));
|
return Ok(new ApiResponse<MenuCategoryDto>(true, data));
|
||||||
@@ -95,6 +98,7 @@ public class MenuController : CafeApiControllerBase
|
|||||||
public async Task<IActionResult> DeleteCategory(string cafeId, string id, ITenantContext tenant, CancellationToken cancellationToken)
|
public async Task<IActionResult> DeleteCategory(string cafeId, string id, ITenantContext tenant, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.DeleteMenuItem) is { } permDenied) return permDenied;
|
||||||
var deleted = await _menuService.DeleteCategoryAsync(cafeId, id, cancellationToken);
|
var deleted = await _menuService.DeleteCategoryAsync(cafeId, id, cancellationToken);
|
||||||
if (!deleted) return NotFoundError();
|
if (!deleted) return NotFoundError();
|
||||||
return Ok(new ApiResponse<object>(true, new { id }));
|
return Ok(new ApiResponse<object>(true, new { id }));
|
||||||
@@ -120,6 +124,7 @@ public class MenuController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.CreateMenuItem) is { } permDenied) return permDenied;
|
||||||
var validation = await _createItemValidator.ValidateAsync(request, cancellationToken);
|
var validation = await _createItemValidator.ValidateAsync(request, cancellationToken);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -148,6 +153,7 @@ public class MenuController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditMenuItem) is { } permDenied) return permDenied;
|
||||||
var data = await _menuService.UpdateItemAsync(cafeId, id, request, cancellationToken);
|
var data = await _menuService.UpdateItemAsync(cafeId, id, request, cancellationToken);
|
||||||
if (data is null) return NotFoundError();
|
if (data is null) return NotFoundError();
|
||||||
return Ok(new ApiResponse<MenuItemDto>(true, data));
|
return Ok(new ApiResponse<MenuItemDto>(true, data));
|
||||||
@@ -162,6 +168,7 @@ public class MenuController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditMenuItem) is { } permDenied) return permDenied;
|
||||||
var data = await _menuService.SetAvailabilityAsync(cafeId, id, request.IsAvailable, cancellationToken);
|
var data = await _menuService.SetAvailabilityAsync(cafeId, id, request.IsAvailable, cancellationToken);
|
||||||
if (data is null) return NotFoundError();
|
if (data is null) return NotFoundError();
|
||||||
return Ok(new ApiResponse<MenuItemDto>(true, data));
|
return Ok(new ApiResponse<MenuItemDto>(true, data));
|
||||||
@@ -171,6 +178,7 @@ public class MenuController : CafeApiControllerBase
|
|||||||
public async Task<IActionResult> DeleteItem(string cafeId, string id, ITenantContext tenant, CancellationToken cancellationToken)
|
public async Task<IActionResult> DeleteItem(string cafeId, string id, ITenantContext tenant, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.DeleteMenuItem) is { } permDenied) return permDenied;
|
||||||
var deleted = await _menuService.DeleteItemAsync(cafeId, id, cancellationToken);
|
var deleted = await _menuService.DeleteItemAsync(cafeId, id, cancellationToken);
|
||||||
if (!deleted) return NotFoundError();
|
if (!deleted) return NotFoundError();
|
||||||
return Ok(new ApiResponse<object>(true, new { id }));
|
return Ok(new ApiResponse<object>(true, new { id }));
|
||||||
@@ -193,6 +201,7 @@ public class MenuController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditMenuItem) is { } permDenied) return permDenied;
|
||||||
var tier = tenant.PlanTier ?? PlanTier.Free;
|
var tier = tenant.PlanTier ?? PlanTier.Free;
|
||||||
var (data, code, message) = await _menuAi3d.GenerateFromItemImageAsync(cafeId, id, tier, cancellationToken);
|
var (data, code, message) = await _menuAi3d.GenerateFromItemImageAsync(cafeId, id, tier, cancellationToken);
|
||||||
if (code is not null)
|
if (code is not null)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Orders;
|
using Meezi.API.Models.Orders;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
@@ -120,6 +119,7 @@ public class OrdersController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ProcessOrders) is { } permDenied) return permDenied;
|
||||||
var validation = await _createValidator.ValidateAsync(request, cancellationToken);
|
var validation = await _createValidator.ValidateAsync(request, cancellationToken);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -139,6 +139,7 @@ public class OrdersController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditOrder) is { } permDenied) return permDenied;
|
||||||
var validation = await _appendValidator.ValidateAsync(request, cancellationToken);
|
var validation = await _appendValidator.ValidateAsync(request, cancellationToken);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -150,7 +151,6 @@ public class OrdersController : CafeApiControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPatch("{id}/items/{itemId}/void")]
|
[HttpPatch("{id}/items/{itemId}/void")]
|
||||||
[Authorize(Roles = "Manager,Owner")]
|
|
||||||
public async Task<IActionResult> VoidOrderItem(
|
public async Task<IActionResult> VoidOrderItem(
|
||||||
string cafeId,
|
string cafeId,
|
||||||
string id,
|
string id,
|
||||||
@@ -159,6 +159,7 @@ public class OrdersController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.VoidOrder) is { } permDenied) return permDenied;
|
||||||
if (string.IsNullOrEmpty(tenant.UserId))
|
if (string.IsNullOrEmpty(tenant.UserId))
|
||||||
return StatusCode(StatusCodes.Status403Forbidden,
|
return StatusCode(StatusCodes.Status403Forbidden,
|
||||||
new ApiResponse<object>(false, null, new ApiError("FORBIDDEN", "User context required.")));
|
new ApiResponse<object>(false, null, new ApiError("FORBIDDEN", "User context required.")));
|
||||||
@@ -181,7 +182,6 @@ public class OrdersController : CafeApiControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id}/transfer")]
|
[HttpPost("{id}/transfer")]
|
||||||
[Authorize(Roles = "Manager,Owner,Waiter")]
|
|
||||||
public async Task<IActionResult> TransferTable(
|
public async Task<IActionResult> TransferTable(
|
||||||
string cafeId,
|
string cafeId,
|
||||||
string id,
|
string id,
|
||||||
@@ -190,6 +190,7 @@ public class OrdersController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditOrder) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var result = await _orderService.TransferTableAsync(cafeId, id, request.TargetTableId, cancellationToken);
|
var result = await _orderService.TransferTableAsync(cafeId, id, request.TargetTableId, cancellationToken);
|
||||||
if (!result.Success)
|
if (!result.Success)
|
||||||
@@ -207,6 +208,7 @@ public class OrdersController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditOrder) is { } permDenied) return permDenied;
|
||||||
var validation = await _sessionValidator.ValidateAsync(request, cancellationToken);
|
var validation = await _sessionValidator.ValidateAsync(request, cancellationToken);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -226,6 +228,7 @@ public class OrdersController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.UpdateOrderStatus) is { } permDenied) return permDenied;
|
||||||
var validation = await _statusValidator.ValidateAsync(request, cancellationToken);
|
var validation = await _statusValidator.ValidateAsync(request, cancellationToken);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -243,7 +246,7 @@ public class OrdersController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsurePermission(tenant, Permission.ProcessOrders) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.VoidOrder) is { } forbidden) return forbidden;
|
||||||
|
|
||||||
var result = await _orderService.CancelOrderAsync(
|
var result = await _orderService.CancelOrderAsync(
|
||||||
cafeId, id, request.Reason, tenant.UserId, cancellationToken);
|
cafeId, id, request.Reason, tenant.UserId, cancellationToken);
|
||||||
@@ -279,6 +282,7 @@ public class OrdersController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.HandlePayments) is { } permDenied) return permDenied;
|
||||||
var validation = await _paymentsValidator.ValidateAsync(request, cancellationToken);
|
var validation = await _paymentsValidator.ValidateAsync(request, cancellationToken);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -319,7 +323,7 @@ public class OrdersController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureManager(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ManageFinancials) is { } forbidden) return forbidden;
|
||||||
var validation = await _correctionValidator.ValidateAsync(request, cancellationToken);
|
var validation = await _correctionValidator.ValidateAsync(request, cancellationToken);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Printing;
|
using Meezi.API.Models.Printing;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
|
|
||||||
namespace Meezi.API.Controllers;
|
namespace Meezi.API.Controllers;
|
||||||
|
|
||||||
[Route("api/cafes/{cafeId}/branches/{branchId}/pos-device")]
|
[Route("api/cafes/{cafeId}/branches/{branchId}/pos-device")]
|
||||||
[Authorize(Roles = "Cashier,Manager,Owner")]
|
|
||||||
public class PosDeviceController : CafeApiControllerBase
|
public class PosDeviceController : CafeApiControllerBase
|
||||||
{
|
{
|
||||||
private readonly IPosDeviceService _posDevice;
|
private readonly IPosDeviceService _posDevice;
|
||||||
@@ -30,6 +29,7 @@ public class PosDeviceController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.HandlePayments) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var validation = await _validator.ValidateAsync(request, ct);
|
var validation = await _validator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Printing;
|
using Meezi.API.Models.Printing;
|
||||||
using Meezi.API.Services.Printing;
|
using Meezi.API.Services.Printing;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
|
|
||||||
@@ -41,7 +41,6 @@ public class PrintController : CafeApiControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("test")]
|
[HttpPost("test")]
|
||||||
[Authorize(Roles = "Manager,Owner")]
|
|
||||||
public async Task<IActionResult> TestPrint(
|
public async Task<IActionResult> TestPrint(
|
||||||
string cafeId,
|
string cafeId,
|
||||||
[FromBody] TestPrintRequest request,
|
[FromBody] TestPrintRequest request,
|
||||||
@@ -49,6 +48,7 @@ public class PrintController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManagePrintSettings) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var result = await _printer.TestPrintAsync(request.PrinterIp, request.Port, ct);
|
var result = await _printer.TestPrintAsync(request.PrinterIp, request.Port, ct);
|
||||||
return ToActionResult(result);
|
return ToActionResult(result);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Queue;
|
using Meezi.API.Models.Queue;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Enums;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
@@ -37,6 +38,7 @@ public class QueueController : CafeApiControllerBase
|
|||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageQueue) is { } permDenied) return permDenied;
|
||||||
var (ticket, error) = await _queue.IssueNextAsync(cafeId, tenant.UserId, request, ct);
|
var (ticket, error) = await _queue.IssueNextAsync(cafeId, tenant.UserId, request, ct);
|
||||||
if (error == "BRANCH_NOT_FOUND")
|
if (error == "BRANCH_NOT_FOUND")
|
||||||
return NotFound(new ApiResponse<object>(false, null, new ApiError(error, "Branch not found.")));
|
return NotFound(new ApiResponse<object>(false, null, new ApiError(error, "Branch not found.")));
|
||||||
@@ -54,6 +56,7 @@ public class QueueController : CafeApiControllerBase
|
|||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageQueue) is { } permDenied) return permDenied;
|
||||||
var (ticket, error) = await _queue.UpdateStatusAsync(cafeId, ticketId, request.Status, ct);
|
var (ticket, error) = await _queue.UpdateStatusAsync(cafeId, ticketId, request.Status, ct);
|
||||||
if (error == "NOT_FOUND")
|
if (error == "NOT_FOUND")
|
||||||
return NotFound(new ApiResponse<object>(false, null, new ApiError(error, "Ticket not found.")));
|
return NotFound(new ApiResponse<object>(false, null, new ApiError(error, "Ticket not found.")));
|
||||||
@@ -71,6 +74,7 @@ public class QueueController : CafeApiControllerBase
|
|||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageQueue) is { } permDenied) return permDenied;
|
||||||
var board = await _queue.GetTodayBoardAsync(cafeId, branchId, ct);
|
var board = await _queue.GetTodayBoardAsync(cafeId, branchId, ct);
|
||||||
var next = board.Tickets.FirstOrDefault(t => t.Status == QueueTicketStatus.Waiting);
|
var next = board.Tickets.FirstOrDefault(t => t.Status == QueueTicketStatus.Waiting);
|
||||||
if (next is null)
|
if (next is null)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using Meezi.API.Models.Reports;
|
using Meezi.API.Models.Reports;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
using Meezi.API.Utils;
|
using Meezi.API.Utils;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Enums;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Infrastructure.Services.Platform;
|
using Meezi.Infrastructure.Services.Platform;
|
||||||
@@ -38,6 +39,7 @@ public class ReportsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ViewReports) is { } permDenied) return permDenied;
|
||||||
if (string.IsNullOrWhiteSpace(branchId))
|
if (string.IsNullOrWhiteSpace(branchId))
|
||||||
return BadRequest(new ApiResponse<object>(false, null,
|
return BadRequest(new ApiResponse<object>(false, null,
|
||||||
new ApiError("VALIDATION_ERROR", "branchId is required.", "branchId")));
|
new ApiError("VALIDATION_ERROR", "branchId is required.", "branchId")));
|
||||||
@@ -65,6 +67,7 @@ public class ReportsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ViewReports) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
if (!TryParseReportDate(from, out var startDate) || !TryParseReportDate(to, out var endDate))
|
if (!TryParseReportDate(from, out var startDate) || !TryParseReportDate(to, out var endDate))
|
||||||
return BadRequest(new ApiResponse<object>(false, null,
|
return BadRequest(new ApiResponse<object>(false, null,
|
||||||
@@ -99,6 +102,7 @@ public class ReportsController : CafeApiControllerBase
|
|||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ViewReports) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var maxDays = await MaxHistoryDaysAsync(tenant, ct);
|
var maxDays = await MaxHistoryDaysAsync(tenant, ct);
|
||||||
if (days > maxDays && maxDays != int.MaxValue)
|
if (days > maxDays && maxDays != int.MaxValue)
|
||||||
@@ -120,6 +124,7 @@ public class ReportsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ViewReports) is { } permDenied) return permDenied;
|
||||||
if (date is not null && !JalaliCalendarHelper.TryParseJalaliDate(date, out _, out _, out _))
|
if (date is not null && !JalaliCalendarHelper.TryParseJalaliDate(date, out _, out _, out _))
|
||||||
return BadRequest(new ApiResponse<object>(false, null,
|
return BadRequest(new ApiResponse<object>(false, null,
|
||||||
new ApiError("VALIDATION_ERROR", "Invalid Jalali date. Use yyyy-MM-dd.")));
|
new ApiError("VALIDATION_ERROR", "Invalid Jalali date. Use yyyy-MM-dd.")));
|
||||||
@@ -136,6 +141,7 @@ public class ReportsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ViewReports) is { } permDenied) return permDenied;
|
||||||
if (month is not null && !JalaliCalendarHelper.TryParseJalaliMonth(month, out _, out _))
|
if (month is not null && !JalaliCalendarHelper.TryParseJalaliMonth(month, out _, out _))
|
||||||
return BadRequest(new ApiResponse<object>(false, null,
|
return BadRequest(new ApiResponse<object>(false, null,
|
||||||
new ApiError("VALIDATION_ERROR", "Invalid Jalali month. Use yyyy-MM.")));
|
new ApiError("VALIDATION_ERROR", "Invalid Jalali month. Use yyyy-MM.")));
|
||||||
@@ -152,6 +158,7 @@ public class ReportsController : CafeApiControllerBase
|
|||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ViewReports) is { } permDenied) return permDenied;
|
||||||
var data = await _reports.GetTrendAsync(cafeId, days, ct);
|
var data = await _reports.GetTrendAsync(cafeId, days, ct);
|
||||||
return Ok(new ApiResponse<IReadOnlyList<TrendDayDto>>(true, data));
|
return Ok(new ApiResponse<IReadOnlyList<TrendDayDto>>(true, data));
|
||||||
}
|
}
|
||||||
@@ -165,6 +172,7 @@ public class ReportsController : CafeApiControllerBase
|
|||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ExportReports) is { } permDenied) return permDenied;
|
||||||
if (!string.Equals(format, "excel", StringComparison.OrdinalIgnoreCase))
|
if (!string.Equals(format, "excel", StringComparison.OrdinalIgnoreCase))
|
||||||
return BadRequest(new ApiResponse<object>(false, null,
|
return BadRequest(new ApiResponse<object>(false, null,
|
||||||
new ApiError("VALIDATION_ERROR", "Only excel format is supported.")));
|
new ApiError("VALIDATION_ERROR", "Only excel format is supported.")));
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using FluentValidation;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Public;
|
using Meezi.API.Models.Public;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Enums;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
@@ -30,6 +31,7 @@ public class ReservationsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.CreateReservation) is { } permDenied) return permDenied;
|
||||||
var validation = await _createValidator.ValidateAsync(request, ct);
|
var validation = await _createValidator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -62,6 +64,7 @@ public class ReservationsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.EditReservation) is { } permDenied) return permDenied;
|
||||||
var data = await _reservations.UpdateStatusAsync(cafeId, id, request.Status, ct);
|
var data = await _reservations.UpdateStatusAsync(cafeId, id, request.Status, ct);
|
||||||
if (data is null) return NotFoundError();
|
if (data is null) return NotFoundError();
|
||||||
return Ok(new ApiResponse<ReservationDto>(true, data));
|
return Ok(new ApiResponse<ReservationDto>(true, data));
|
||||||
@@ -75,6 +78,7 @@ public class ReservationsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.DeleteReservation) is { } permDenied) return permDenied;
|
||||||
var deleted = await _reservations.DeleteAsync(cafeId, id, ct);
|
var deleted = await _reservations.DeleteAsync(cafeId, id, ct);
|
||||||
if (!deleted) return NotFoundError();
|
if (!deleted) return NotFoundError();
|
||||||
return Ok(new ApiResponse<object>(true, new { id }));
|
return Ok(new ApiResponse<object>(true, new { id }));
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using FluentValidation;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Shifts;
|
using Meezi.API.Models.Shifts;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ public class ShiftsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.OperateRegister) is { } permDenied) return permDenied;
|
||||||
if (string.IsNullOrEmpty(tenant.UserId))
|
if (string.IsNullOrEmpty(tenant.UserId))
|
||||||
return StatusCode(StatusCodes.Status401Unauthorized,
|
return StatusCode(StatusCodes.Status401Unauthorized,
|
||||||
new ApiResponse<object>(false, null, new ApiError("UNAUTHORIZED", "User context is missing.")));
|
new ApiResponse<object>(false, null, new ApiError("UNAUTHORIZED", "User context is missing.")));
|
||||||
@@ -54,6 +56,7 @@ public class ShiftsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.OperateRegister) is { } permDenied) return permDenied;
|
||||||
if (string.IsNullOrEmpty(tenant.UserId))
|
if (string.IsNullOrEmpty(tenant.UserId))
|
||||||
return StatusCode(StatusCodes.Status401Unauthorized,
|
return StatusCode(StatusCodes.Status401Unauthorized,
|
||||||
new ApiResponse<object>(false, null, new ApiError("UNAUTHORIZED", "User context is missing.")));
|
new ApiResponse<object>(false, null, new ApiError("UNAUTHORIZED", "User context is missing.")));
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using FluentValidation;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Crm;
|
using Meezi.API.Models.Crm;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ public class SmsController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureManager(tenant) is { } forbidden) return forbidden;
|
if (EnsurePermission(tenant, Permission.ManageSmsSettings) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var (success, data, code, message) = await _smsMarketingService.UpdateSettingsAsync(
|
var (success, data, code, message) = await _smsMarketingService.UpdateSettingsAsync(
|
||||||
cafeId, request, cancellationToken);
|
cafeId, request, cancellationToken);
|
||||||
@@ -85,6 +86,7 @@ public class SmsController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.SendSms) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var validation = await _campaignValidator.ValidateAsync(request, cancellationToken);
|
var validation = await _campaignValidator.ValidateAsync(request, cancellationToken);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Orders;
|
using Meezi.API.Models.Orders;
|
||||||
using Meezi.API.Models.Tables;
|
using Meezi.API.Models.Tables;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
|
|
||||||
@@ -65,6 +65,7 @@ public class TablesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied;
|
||||||
var validation = await _createValidator.ValidateAsync(request, ct);
|
var validation = await _createValidator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -82,6 +83,7 @@ public class TablesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied;
|
||||||
var validation = await _patchValidator.ValidateAsync(request, ct);
|
var validation = await _patchValidator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
@@ -104,7 +106,6 @@ public class TablesController : CafeApiControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
[Authorize(Roles = "Manager,Owner")]
|
|
||||||
public async Task<IActionResult> DeleteTable(
|
public async Task<IActionResult> DeleteTable(
|
||||||
string cafeId,
|
string cafeId,
|
||||||
string id,
|
string id,
|
||||||
@@ -112,6 +113,7 @@ public class TablesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var result = await _tableService.DeleteTableAsync(cafeId, id, ct);
|
var result = await _tableService.DeleteTableAsync(cafeId, id, ct);
|
||||||
if (!result.Success)
|
if (!result.Success)
|
||||||
@@ -135,6 +137,7 @@ public class TablesController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageTables) is { } permDenied) return permDenied;
|
||||||
var validation = await _cleaningValidator.ValidateAsync(request, ct);
|
var validation = await _cleaningValidator.ValidateAsync(request, ct);
|
||||||
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
if (!validation.IsValid) return BadRequest(ValidationError(validation));
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ public class TarazController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageFinancials) is { } permDenied) return permDenied;
|
||||||
|
|
||||||
var targetDate = date ?? DateTime.UtcNow.Date;
|
var targetDate = date ?? DateTime.UtcNow.Date;
|
||||||
var result = await _taraz.SubmitDailyInvoicesAsync(cafeId, targetDate, ct);
|
var result = await _taraz.SubmitDailyInvoicesAsync(cafeId, targetDate, ct);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Meezi.API.Models.Taxes;
|
using Meezi.API.Models.Taxes;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Shared;
|
using Meezi.Shared;
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ public class TaxesController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureOwner(tenant) is { } ownerDenied) return ownerDenied;
|
if (EnsurePermission(tenant, Permission.CreateTax) is { } permDenied) return permDenied;
|
||||||
var data = await _taxService.CreateAsync(cafeId, request, cancellationToken);
|
var data = await _taxService.CreateAsync(cafeId, request, cancellationToken);
|
||||||
return Ok(new ApiResponse<TaxDto>(true, data));
|
return Ok(new ApiResponse<TaxDto>(true, data));
|
||||||
}
|
}
|
||||||
@@ -43,7 +44,7 @@ public class TaxesController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureOwner(tenant) is { } ownerDenied) return ownerDenied;
|
if (EnsurePermission(tenant, Permission.EditTax) is { } permDenied) return permDenied;
|
||||||
var data = await _taxService.UpdateAsync(cafeId, id, request, cancellationToken);
|
var data = await _taxService.UpdateAsync(cafeId, id, request, cancellationToken);
|
||||||
if (data is null) return NotFoundError();
|
if (data is null) return NotFoundError();
|
||||||
return Ok(new ApiResponse<TaxDto>(true, data));
|
return Ok(new ApiResponse<TaxDto>(true, data));
|
||||||
@@ -57,7 +58,7 @@ public class TaxesController : CafeApiControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
if (EnsureOwner(tenant) is { } ownerDenied) return ownerDenied;
|
if (EnsurePermission(tenant, Permission.DeleteTax) is { } permDenied) return permDenied;
|
||||||
var deleted = await _taxService.DeleteAsync(cafeId, id, cancellationToken);
|
var deleted = await _taxService.DeleteAsync(cafeId, id, cancellationToken);
|
||||||
if (!deleted) return NotFoundError();
|
if (!deleted) return NotFoundError();
|
||||||
return Ok(new ApiResponse<object>(true, new { id }));
|
return Ok(new ApiResponse<object>(true, new { id }));
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Meezi.Core.Authorization;
|
||||||
using Meezi.Core.Enums;
|
using Meezi.Core.Enums;
|
||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.API.Services;
|
using Meezi.API.Services;
|
||||||
@@ -52,6 +53,7 @@ public class TerminalsController : CafeApiControllerBase
|
|||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||||
|
if (EnsurePermission(tenant, Permission.ManageCafeSettings) is { } permDenied) return permDenied;
|
||||||
await _terminals.RevokeAsync(cafeId, terminalId, ct);
|
await _terminals.RevokeAsync(cafeId, terminalId, ct);
|
||||||
return Ok(new ApiResponse<object>(true, new { revoked = true }));
|
return Ok(new ApiResponse<object>(true, new { revoked = true }));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user