feat(api): .NET 10 multi-tenant REST API
Full backend implementation: - Multi-tenant cafe/restaurant management (menus, orders, tables, staff) - POS order flow with ZarinPal and Snappfood payment integration - OTP authentication via Kavenegar SMS - QR digital menu with public discover/finder endpoints - Customer loyalty, coupons, CRM - PostgreSQL via EF Core, Redis for caching/sessions - Background jobs, webhook handlers - Full migration history Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Auth;
|
||||
using Meezi.Core.Utilities;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class SendOtpRequestValidator : AbstractValidator<SendOtpRequest>
|
||||
{
|
||||
public SendOtpRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Phone)
|
||||
.NotEmpty()
|
||||
.Must(p => PhoneNormalizer.IsValidIranMobile(PhoneNormalizer.Normalize(p)))
|
||||
.WithMessage("Invalid Iranian mobile number.");
|
||||
}
|
||||
}
|
||||
|
||||
public class VerifyOtpRequestValidator : AbstractValidator<VerifyOtpRequest>
|
||||
{
|
||||
public VerifyOtpRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Phone)
|
||||
.NotEmpty()
|
||||
.Must(p => PhoneNormalizer.IsValidIranMobile(PhoneNormalizer.Normalize(p)))
|
||||
.WithMessage("Invalid Iranian mobile number.");
|
||||
|
||||
RuleFor(x => x.Code)
|
||||
.Must(OtpNormalizer.IsValidSixDigitCode)
|
||||
.WithMessage("OTP must be 6 digits.");
|
||||
}
|
||||
}
|
||||
|
||||
public class RefreshTokenRequestValidator : AbstractValidator<RefreshTokenRequest>
|
||||
{
|
||||
public RefreshTokenRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.RefreshToken).NotEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Billing;
|
||||
using Meezi.Core.Constants;
|
||||
using Meezi.Core.Enums;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class SubscribeRequestValidator : AbstractValidator<SubscribeRequest>
|
||||
{
|
||||
public SubscribeRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.PlanTier)
|
||||
.Must(PlanPricing.IsBillableOnline)
|
||||
.WithMessage("This plan must be purchased via sales contact.");
|
||||
RuleFor(x => x.Months).InclusiveBetween(1, 12);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Menu;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class UpsertBranchMenuOverrideRequestValidator : AbstractValidator<UpsertBranchMenuOverrideRequest>
|
||||
{
|
||||
public UpsertBranchMenuOverrideRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.PriceOverride)
|
||||
.GreaterThanOrEqualTo(0)
|
||||
.When(x => x.PriceOverride.HasValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Public;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class CoffeeAdvisorRequestValidator : AbstractValidator<CoffeeAdvisorRequest>
|
||||
{
|
||||
public CoffeeAdvisorRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Purpose).NotEmpty().MinimumLength(3).MaximumLength(500);
|
||||
RuleFor(x => x.CafeSlug).MaximumLength(80).When(x => !string.IsNullOrEmpty(x.CafeSlug));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Branches;
|
||||
using Meezi.Core.Utilities;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class CreateBranchRequestValidator : AbstractValidator<CreateBranchRequest>
|
||||
{
|
||||
public CreateBranchRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
|
||||
RuleFor(x => x.LoginPhone)
|
||||
.NotEmpty()
|
||||
.Must(p => PhoneNormalizer.IsValidIranMobile(PhoneNormalizer.Normalize(p)))
|
||||
.WithMessage("Invalid Iranian mobile number for branch login.");
|
||||
RuleFor(x => x.ManagerName).MaximumLength(200).When(x => x.ManagerName is not null);
|
||||
RuleFor(x => x.Address).MaximumLength(500).When(x => x.Address is not null);
|
||||
RuleFor(x => x.City).MaximumLength(100).When(x => x.City is not null);
|
||||
RuleFor(x => x.Phone).MaximumLength(20).When(x => x.Phone is not null);
|
||||
}
|
||||
}
|
||||
|
||||
public class PatchBranchRequestValidator : AbstractValidator<PatchBranchRequest>
|
||||
{
|
||||
public PatchBranchRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(200).When(x => x.Name is not null);
|
||||
RuleFor(x => x.Address).MaximumLength(500).When(x => x.Address is not null);
|
||||
RuleFor(x => x.City).MaximumLength(100).When(x => x.City is not null);
|
||||
RuleFor(x => x.Phone).MaximumLength(20).When(x => x.Phone is not null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Crm;
|
||||
using Meezi.Core.Utilities;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class CreateCustomerRequestValidator : AbstractValidator<CreateCustomerRequest>
|
||||
{
|
||||
public CreateCustomerRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
|
||||
RuleFor(x => x.Phone)
|
||||
.NotEmpty()
|
||||
.Must(p => PhoneNormalizer.IsValidIranMobile(PhoneNormalizer.Normalize(p)))
|
||||
.WithMessage("Invalid Iranian mobile number.");
|
||||
RuleFor(x => x.NationalId)
|
||||
.Matches(@"^\d{10}$")
|
||||
.When(x => !string.IsNullOrWhiteSpace(x.NationalId))
|
||||
.WithMessage("National ID must be 10 digits.");
|
||||
RuleFor(x => x.Group).IsInEnum();
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateCustomerRequestValidator : AbstractValidator<UpdateCustomerRequest>
|
||||
{
|
||||
public UpdateCustomerRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Name)
|
||||
.NotEmpty()
|
||||
.MaximumLength(200)
|
||||
.When(x => x.Name is not null);
|
||||
RuleFor(x => x.Phone)
|
||||
.Must(p => PhoneNormalizer.IsValidIranMobile(PhoneNormalizer.Normalize(p!)))
|
||||
.When(x => !string.IsNullOrWhiteSpace(x.Phone))
|
||||
.WithMessage("Invalid Iranian mobile number.");
|
||||
RuleFor(x => x.NationalId)
|
||||
.Matches(@"^\d{10}$")
|
||||
.When(x => !string.IsNullOrWhiteSpace(x.NationalId))
|
||||
.WithMessage("National ID must be 10 digits.");
|
||||
RuleFor(x => x.Group)
|
||||
.IsInEnum()
|
||||
.When(x => x.Group.HasValue);
|
||||
RuleFor(x => x.LoyaltyPoints)
|
||||
.GreaterThanOrEqualTo(0)
|
||||
.When(x => x.LoyaltyPoints.HasValue);
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateCouponRequestValidator : AbstractValidator<CreateCouponRequest>
|
||||
{
|
||||
public CreateCouponRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Code).NotEmpty().MaximumLength(50);
|
||||
RuleFor(x => x.Type).IsInEnum();
|
||||
RuleFor(x => x.Value).GreaterThan(0);
|
||||
}
|
||||
}
|
||||
|
||||
public class SendSmsCampaignRequestValidator : AbstractValidator<SendSmsCampaignRequest>
|
||||
{
|
||||
public SendSmsCampaignRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Message).NotEmpty().MaximumLength(500);
|
||||
RuleFor(x => x)
|
||||
.Must(x => x.TargetGroup.HasValue || (x.Phones is { Count: > 0 }))
|
||||
.WithMessage("Specify TargetGroup or at least one phone number.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Expenses;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class CreateExpenseRequestValidator : AbstractValidator<CreateExpenseRequest>
|
||||
{
|
||||
public CreateExpenseRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.BranchId).NotEmpty();
|
||||
RuleFor(x => x.Amount).GreaterThan(0);
|
||||
RuleFor(x => x.Note).MaximumLength(500).When(x => x.Note is not null);
|
||||
RuleFor(x => x.ReceiptImageUrl).MaximumLength(500).When(x => x.ReceiptImageUrl is not null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Hr;
|
||||
using Meezi.Core.Enums;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class CreateLeaveRequestValidator : AbstractValidator<CreateLeaveRequest>
|
||||
{
|
||||
public CreateLeaveRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.EndDate).GreaterThanOrEqualTo(x => x.StartDate);
|
||||
}
|
||||
}
|
||||
|
||||
public class ReviewLeaveRequestValidator : AbstractValidator<ReviewLeaveRequest>
|
||||
{
|
||||
public ReviewLeaveRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Status)
|
||||
.Must(s => s is LeaveStatus.Approved or LeaveStatus.Rejected)
|
||||
.WithMessage("Status must be Approved or Rejected.");
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateSalaryRequestValidator : AbstractValidator<CreateSalaryRequest>
|
||||
{
|
||||
public CreateSalaryRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.EmployeeId).NotEmpty();
|
||||
RuleFor(x => x.MonthYear).Matches(@"^\d{4}-\d{2}$");
|
||||
RuleFor(x => x.BaseSalary).GreaterThanOrEqualTo(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Kitchen;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class CreateKitchenStationRequestValidator : AbstractValidator<CreateKitchenStationRequest>
|
||||
{
|
||||
public CreateKitchenStationRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(100);
|
||||
RuleFor(x => x.PrinterPort).InclusiveBetween(1, 65535);
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateKitchenStationRequestValidator : AbstractValidator<UpdateKitchenStationRequest>
|
||||
{
|
||||
public UpdateKitchenStationRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Name).MaximumLength(100).When(x => x.Name is not null);
|
||||
RuleFor(x => x.PrinterPort).InclusiveBetween(1, 65535).When(x => x.PrinterPort.HasValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Cafes;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class PatchCafeSettingsRequestValidator : AbstractValidator<PatchCafeSettingsRequest>
|
||||
{
|
||||
public PatchCafeSettingsRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Name).MaximumLength(200).When(x => x.Name is not null);
|
||||
RuleFor(x => x.Phone).MaximumLength(20).When(x => x.Phone is not null);
|
||||
RuleFor(x => x.Address).MaximumLength(500).When(x => x.Address is not null);
|
||||
RuleFor(x => x.City).MaximumLength(100).When(x => x.City is not null);
|
||||
RuleFor(x => x.Description).MaximumLength(2000).When(x => x.Description is not null);
|
||||
RuleFor(x => x.LogoUrl).MaximumLength(500).When(x => x.LogoUrl is not null);
|
||||
RuleFor(x => x.CoverImageUrl).MaximumLength(500).When(x => x.CoverImageUrl is not null);
|
||||
RuleFor(x => x.SnappfoodVendorId).MaximumLength(100).When(x => x.SnappfoodVendorId is not null);
|
||||
When(x => x.Theme is not null, () =>
|
||||
{
|
||||
RuleFor(x => x.Theme!.PaletteId).NotEmpty().MaximumLength(48);
|
||||
RuleFor(x => x.Theme!.PanelStyle).NotEmpty().MaximumLength(24);
|
||||
RuleFor(x => x.Theme!.MenuStyle).NotEmpty().MaximumLength(24);
|
||||
RuleFor(x => x.Theme!.MenuTexture).NotEmpty().MaximumLength(24);
|
||||
RuleFor(x => x.Theme!.Density).NotEmpty().MaximumLength(24);
|
||||
RuleFor(x => x.Theme!.Radius).NotEmpty().MaximumLength(24);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Menu;
|
||||
using Meezi.API.Models.Orders;
|
||||
using Meezi.API.Models.Tables;
|
||||
using Meezi.Core.Constants;
|
||||
using Meezi.Core.Utilities;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class CreateMenuCategoryRequestValidator : AbstractValidator<CreateMenuCategoryRequest>
|
||||
{
|
||||
public CreateMenuCategoryRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
|
||||
RuleFor(x => x.NameEn).NotEmpty().MaximumLength(200);
|
||||
RuleFor(x => x.SortOrder).GreaterThanOrEqualTo(0);
|
||||
RuleFor(x => x.DiscountPercent).InclusiveBetween(0, 100);
|
||||
RuleFor(x => x.Icon).MaximumLength(32).When(x => x.Icon is not null);
|
||||
RuleFor(x => x.IconPresetId)
|
||||
.Must(id => id is null || CategoryIconPresets.IsValidPreset(id))
|
||||
.WithMessage("Invalid icon preset.")
|
||||
.MaximumLength(48)
|
||||
.When(x => x.IconPresetId is not null);
|
||||
RuleFor(x => x.IconStyle)
|
||||
.Must(s => s is null || CategoryIconPresets.IsValidStyle(s))
|
||||
.WithMessage("Invalid icon style.")
|
||||
.MaximumLength(16)
|
||||
.When(x => x.IconStyle is not null);
|
||||
RuleFor(x => x.ImageUrl).MaximumLength(500).When(x => x.ImageUrl is not null);
|
||||
RuleFor(x => x)
|
||||
.Must(x => string.IsNullOrWhiteSpace(x.IconPresetId) == string.IsNullOrWhiteSpace(x.IconStyle))
|
||||
.WithMessage("Icon preset and style must be set together.");
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateMenuItemRequestValidator : AbstractValidator<CreateMenuItemRequest>
|
||||
{
|
||||
public CreateMenuItemRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.CategoryId).NotEmpty();
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
|
||||
RuleFor(x => x.NameEn).NotEmpty().MaximumLength(200);
|
||||
RuleFor(x => x.Price).GreaterThanOrEqualTo(0);
|
||||
RuleFor(x => x.DiscountPercent).InclusiveBetween(0, 100);
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateTableRequestValidator : AbstractValidator<CreateTableRequest>
|
||||
{
|
||||
public CreateTableRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Number).NotEmpty().MaximumLength(50);
|
||||
RuleFor(x => x.Capacity).GreaterThan(0);
|
||||
}
|
||||
}
|
||||
|
||||
public class PatchTableRequestValidator : AbstractValidator<PatchTableRequest>
|
||||
{
|
||||
public PatchTableRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Number).NotEmpty().MaximumLength(50).When(x => x.Number is not null);
|
||||
RuleFor(x => x.Capacity).GreaterThan(0).When(x => x.Capacity.HasValue);
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateBranchTableRequestValidator : AbstractValidator<CreateBranchTableRequest>
|
||||
{
|
||||
public CreateBranchTableRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Number).NotEmpty().MaximumLength(50);
|
||||
RuleFor(x => x.Capacity).GreaterThan(0);
|
||||
}
|
||||
}
|
||||
|
||||
public class PatchBranchTableRequestValidator : AbstractValidator<PatchBranchTableRequest>
|
||||
{
|
||||
public PatchBranchTableRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Number).NotEmpty().MaximumLength(50).When(x => x.Number is not null);
|
||||
RuleFor(x => x.Capacity).GreaterThan(0).When(x => x.Capacity.HasValue);
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateTableSectionRequestValidator : AbstractValidator<CreateTableSectionRequest>
|
||||
{
|
||||
public CreateTableSectionRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(100);
|
||||
}
|
||||
}
|
||||
|
||||
public class PatchTableSectionRequestValidator : AbstractValidator<PatchTableSectionRequest>
|
||||
{
|
||||
public PatchTableSectionRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(100).When(x => x.Name is not null);
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateOrderRequestValidator : AbstractValidator<CreateOrderRequest>
|
||||
{
|
||||
public CreateOrderRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.OrderType).IsInEnum();
|
||||
RuleFor(x => x.Items).NotEmpty();
|
||||
RuleFor(x => x.GuestName).MaximumLength(200).When(x => x.GuestName is not null);
|
||||
RuleFor(x => x.GuestPhone)
|
||||
.Must(p => PhoneNormalizer.IsValidIranMobile(PhoneNormalizer.Normalize(p!)))
|
||||
.When(x => !string.IsNullOrWhiteSpace(x.GuestPhone));
|
||||
RuleForEach(x => x.Items).ChildRules(item =>
|
||||
{
|
||||
item.RuleFor(i => i.MenuItemId).NotEmpty();
|
||||
item.RuleFor(i => i.Quantity).GreaterThan(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateOrderStatusRequestValidator : AbstractValidator<UpdateOrderStatusRequest>
|
||||
{
|
||||
public UpdateOrderStatusRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Status).IsInEnum();
|
||||
}
|
||||
}
|
||||
|
||||
public class RecordPaymentsRequestValidator : AbstractValidator<RecordPaymentsRequest>
|
||||
{
|
||||
public RecordPaymentsRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Payments).NotEmpty();
|
||||
RuleForEach(x => x.Payments).ChildRules(p =>
|
||||
{
|
||||
p.RuleFor(x => x.Method).IsInEnum();
|
||||
p.RuleFor(x => x.Amount).GreaterThan(0);
|
||||
});
|
||||
RuleFor(x => x.LoyaltyPointsToRedeem)
|
||||
.GreaterThanOrEqualTo(0)
|
||||
.When(x => x.LoyaltyPointsToRedeem.HasValue);
|
||||
}
|
||||
}
|
||||
|
||||
public class AppendOrderItemsRequestValidator : AbstractValidator<AppendOrderItemsRequest>
|
||||
{
|
||||
public AppendOrderItemsRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Items).NotEmpty();
|
||||
RuleForEach(x => x.Items).ChildRules(item =>
|
||||
{
|
||||
item.RuleFor(i => i.MenuItemId).NotEmpty();
|
||||
item.RuleFor(i => i.Quantity).GreaterThan(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateOrderSessionRequestValidator : AbstractValidator<UpdateOrderSessionRequest>
|
||||
{
|
||||
public UpdateOrderSessionRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.GuestName).MaximumLength(200).When(x => x.GuestName is not null);
|
||||
RuleFor(x => x.GuestPhone)
|
||||
.Must(p => PhoneNormalizer.IsValidIranMobile(PhoneNormalizer.Normalize(p!)))
|
||||
.When(x => !string.IsNullOrWhiteSpace(x.GuestPhone));
|
||||
}
|
||||
}
|
||||
|
||||
public class SetTableCleaningRequestValidator : AbstractValidator<SetTableCleaningRequest>
|
||||
{
|
||||
public SetTableCleaningRequestValidator() { }
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Printing;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class PatchBranchPrintSettingsRequestValidator : AbstractValidator<PatchBranchPrintSettingsRequest>
|
||||
{
|
||||
public PatchBranchPrintSettingsRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.ReceiptPrinterPort)
|
||||
.InclusiveBetween(1, 65535)
|
||||
.When(x => x.ReceiptPrinterPort.HasValue);
|
||||
RuleFor(x => x.KitchenPrinterPort)
|
||||
.InclusiveBetween(1, 65535)
|
||||
.When(x => x.KitchenPrinterPort.HasValue);
|
||||
RuleFor(x => x.PaperWidthMm)
|
||||
.Must(w => w is null or 58 or 80)
|
||||
.WithMessage("Paper width must be 58 or 80 mm.");
|
||||
RuleFor(x => x.ReceiptHeader).MaximumLength(500).When(x => x.ReceiptHeader is not null);
|
||||
RuleFor(x => x.ReceiptFooter).MaximumLength(500).When(x => x.ReceiptFooter is not null);
|
||||
RuleFor(x => x.WifiPassword).MaximumLength(100).When(x => x.WifiPassword is not null);
|
||||
RuleFor(x => x.PosDevicePort)
|
||||
.InclusiveBetween(1, 65535)
|
||||
.When(x => x.PosDevicePort.HasValue);
|
||||
}
|
||||
}
|
||||
|
||||
public class PosPaymentRequestValidator : AbstractValidator<PosPaymentRequest>
|
||||
{
|
||||
public PosPaymentRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.OrderId).NotEmpty().MaximumLength(64);
|
||||
RuleFor(x => x.Amount).GreaterThan(0);
|
||||
}
|
||||
}
|
||||
|
||||
public class TestPrintRequestValidator : AbstractValidator<TestPrintRequest>
|
||||
{
|
||||
public TestPrintRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.PrinterIp).NotEmpty().MaximumLength(45);
|
||||
RuleFor(x => x.Port).InclusiveBetween(1, 65535);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Public;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class GuestCreateOrderRequestValidator : AbstractValidator<GuestCreateOrderRequest>
|
||||
{
|
||||
public GuestCreateOrderRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Items).NotEmpty();
|
||||
RuleForEach(x => x.Items).ChildRules(item =>
|
||||
{
|
||||
item.RuleFor(i => i.MenuItemId).NotEmpty();
|
||||
item.RuleFor(i => i.Quantity).GreaterThan(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class PlaceGuestOrderRequestValidator : AbstractValidator<PlaceGuestOrderRequest>
|
||||
{
|
||||
public PlaceGuestOrderRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.TableId).NotEmpty();
|
||||
RuleFor(x => x.Items).NotEmpty();
|
||||
RuleForEach(x => x.Items).ChildRules(item =>
|
||||
{
|
||||
item.RuleFor(i => i.MenuItemId).NotEmpty();
|
||||
item.RuleFor(i => i.Quantity).GreaterThan(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateReservationRequestValidator : AbstractValidator<CreateReservationRequest>
|
||||
{
|
||||
public CreateReservationRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.GuestName).NotEmpty().MaximumLength(200);
|
||||
RuleFor(x => x.GuestPhone).NotEmpty().Matches(@"^09\d{9}$");
|
||||
RuleFor(x => x.PartySize).InclusiveBetween(1, 20);
|
||||
RuleFor(x => x.Date).Must(d => d >= DateOnly.FromDateTime(DateTime.UtcNow.Date));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Public;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class CreateCafeReviewRequestValidator : AbstractValidator<CreateCafeReviewRequest>
|
||||
{
|
||||
public CreateCafeReviewRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.AuthorName).NotEmpty().MaximumLength(200);
|
||||
RuleFor(x => x.Rating).InclusiveBetween(1, 5);
|
||||
RuleFor(x => x.AuthorPhone).Matches(@"^09\d{9}$").When(x => !string.IsNullOrEmpty(x.AuthorPhone));
|
||||
RuleFor(x => x.Comment).MaximumLength(2000);
|
||||
}
|
||||
}
|
||||
|
||||
public class ReplyCafeReviewRequestValidator : AbstractValidator<ReplyCafeReviewRequest>
|
||||
{
|
||||
public ReplyCafeReviewRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Reply).NotEmpty().MaximumLength(2000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using FluentValidation;
|
||||
using Meezi.API.Models.Shifts;
|
||||
|
||||
namespace Meezi.API.Validators;
|
||||
|
||||
public class OpenShiftRequestValidator : AbstractValidator<OpenShiftRequest>
|
||||
{
|
||||
public OpenShiftRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.OpeningCash).GreaterThanOrEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
public class CloseShiftRequestValidator : AbstractValidator<CloseShiftRequest>
|
||||
{
|
||||
public CloseShiftRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.ClosingCash).GreaterThanOrEqualTo(0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user