Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
26 changes: 21 additions & 5 deletions .github/workflows/backend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,23 @@ on:
- 'backend/**'
- '.github/workflows/backend-ci.yml'

# A newer push to the same branch makes an in-flight run redundant.
concurrency:
group: backend-ci-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
build-and-test:
runs-on: ubuntu-latest
timeout-minutes: 20

# Exposed at job level so steps can skip Sonar when the secret is absent, which is what
# happens on a fork or before the token has been configured.
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_BACKEND }}

steps:
- name: Checkout code
Expand All @@ -23,17 +37,18 @@ jobs:
global-json-file: backend/global.json

- name: Setup Java (required by Sonar scanner)
if: env.SONAR_TOKEN != ''
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'

- name: Install SonarCloud scanner
if: env.SONAR_TOKEN != ''
run: dotnet tool install --global dotnet-sonarscanner

- name: Begin SonarCloud analysis
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_BACKEND }}
if: env.SONAR_TOKEN != ''
run: |
dotnet sonarscanner begin \
/k:"RidentIT_RestaurantPOS" \
Expand Down Expand Up @@ -72,13 +87,14 @@ jobs:
working-directory: backend

- name: End SonarCloud analysis
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_BACKEND }}
if: env.SONAR_TOKEN != ''
run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN_BACKEND }}"
working-directory: backend

- name: Upload coverage report
if: always()
uses: actions/upload-artifact@v4
with:
name: backend-coverage
path: backend/**/TestResults/**/coverage.opencover.xml
path: backend/**/TestResults/**/coverage.opencover.xml
if-no-files-found: warn
9 changes: 9 additions & 0 deletions .github/workflows/backend-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,18 @@ on:
- 'backend/**'
- '.github/workflows/backend-pr.yml'

concurrency:
group: backend-pr-${{ github.event.pull_request.number }}
cancel-in-progress: true

permissions:
contents: read
pull-requests: read

jobs:
pr-validation:
runs-on: ubuntu-latest
timeout-minutes: 20

# Expose secret as job-level env so steps can check if it is set
env:
Expand Down
18 changes: 17 additions & 1 deletion .github/workflows/frontend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,20 @@ on:
- 'frontend/**'
- '.github/workflows/frontend-ci.yml'

concurrency:
group: frontend-ci-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
build-and-test:
runs-on: ubuntu-latest
timeout-minutes: 25

env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_FRONTEND }}

steps:
- name: Checkout code
Expand Down Expand Up @@ -41,11 +52,14 @@ jobs:
working-directory: frontend

- name: Upload Coverage to Artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: frontend-coverage
path: frontend/coverage
if-no-files-found: warn

# Playwright serves the built output via `vite preview`, so the build must come first.
- name: Build Application (Vite)
run: npm run build
working-directory: frontend
Expand All @@ -64,11 +78,13 @@ jobs:
with:
name: playwright-report
path: frontend/playwright-report
if-no-files-found: ignore

- name: SonarCloud Main Analysis
if: env.SONAR_TOKEN != ''
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_FRONTEND }}
with:
projectBaseDir: frontend
projectBaseDir: frontend
9 changes: 9 additions & 0 deletions .github/workflows/frontend-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,18 @@ on:
- 'frontend/**'
- '.github/workflows/frontend-pr.yml'

concurrency:
group: frontend-pr-${{ github.event.pull_request.number }}
cancel-in-progress: true

permissions:
contents: read
pull-requests: read

jobs:
pr-validation:
runs-on: ubuntu-latest
timeout-minutes: 20

env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN_FRONTEND }}
Expand Down
8 changes: 7 additions & 1 deletion backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ StyleCopReport.xml
*.scc
logs/
*.db
*.db
*.db-shm
*.db-wal
integrationtests.db

# Chutzpah Test files
Expand Down Expand Up @@ -484,3 +485,8 @@ $RECYCLE.BIN/

# Vim temporary swap files
*.swp

# Auto-generated JWT signing key (see JwtSigningKeyProvider).
# Machine-local secret: never commit it, and never share it between installations.
keys/
*.key
3 changes: 3 additions & 0 deletions backend/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<Project>
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<!-- Run on a newer major runtime when the 9.0 runtime is absent. Keeps the app launchable
on developer machines and tills that only have .NET 10 installed, without retargeting. -->
<RollForward>Major</RollForward>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
Expand Down
9 changes: 7 additions & 2 deletions backend/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,22 @@
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="FluentAssertions" Version="6.12.2" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.18" />
<PackageVersion Include="Testcontainers" Version="3.10.0" />
<PackageVersion Include="Testcontainers.PostgreSql" Version="3.10.0" />
<PackageVersion Include="NetArchTest.Rules" Version="1.3.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.18" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.18" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.18" />
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="9.0.18" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.18" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.18" />
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageVersion Include="FluentValidation" Version="11.12.0" />
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="11.12.0" />
<PackageVersion Include="MediatR" Version="12.5.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.18" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.14.0" />
<PackageVersion Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageVersion Include="Asp.Versioning.Http" Version="8.1.1" />
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.1" />
</ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion backend/global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "9.0.311",
"rollForward": "latestFeature"
"rollForward": "latestMajor"
}
}
24 changes: 24 additions & 0 deletions backend/src/RestaurantPOS.API/Contracts/Auth/AuthRequests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using RestaurantPOS.Domain.Entities;

namespace RestaurantPOS.API.Contracts.Auth;

/// <summary>Sign-in credentials.</summary>
public sealed record LoginRequest(string Username, string Password);

/// <summary>Exchanges a refresh token for a new session.</summary>
public sealed record RefreshRequest(string RefreshToken);

/// <summary>Ends a session. The token is optional so sign-out never fails.</summary>
public sealed record LogoutRequest(string? RefreshToken);

/// <summary>Changes the signed-in user's own password.</summary>
public sealed record ChangePasswordRequest(string CurrentPassword, string NewPassword);

/// <summary>
/// Sets the signed-in administrator's approval PIN. Leave <paramref name="Pin"/> null to have
/// the server generate a random <see cref="User.ApprovalPinLength"/>-digit PIN.
/// </summary>
public sealed record SetApprovalPinRequest(string CurrentPassword, string? Pin);

/// <summary>Presents a PIN for authorisation of a privileged action.</summary>
public sealed record VerifyApprovalPinRequest(string Pin, string? Reason);
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using RestaurantPOS.Domain.Enums;

namespace RestaurantPOS.API.Contracts.Inventory;

public sealed record CreateRawMaterialRequest(
string Name, UnitOfMeasurement UnitOfMeasurement, decimal? MainStoreReorderLevel, decimal? KitchenParLevel);

public sealed record UpdateRawMaterialRequest(
string Name, UnitOfMeasurement UnitOfMeasurement, decimal? MainStoreReorderLevel, decimal? KitchenParLevel);

public sealed record SetRawMaterialActiveRequest(bool IsActive);
25 changes: 25 additions & 0 deletions backend/src/RestaurantPOS.API/Contracts/Inventory/StockRequests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using RestaurantPOS.Domain.Enums;

namespace RestaurantPOS.API.Contracts.Inventory;

public sealed record StockLineRequest(Guid RawMaterialId, decimal Quantity);

public sealed record CreateGoodsReceivedNoteRequest(
Guid SupplierId,
IReadOnlyCollection<StockLineRequest> Lines,
string? Notes,
Guid? PurchaseOrderId = null,
int? QualityRating = null,
bool HasIssue = false);

/// <summary>
/// Both the raw material quantities and the administrator's approval PIN travel in one request,
/// since a release is created only once it is already approved.
/// </summary>
public sealed record CreateStockReleaseRequest(
IReadOnlyCollection<StockLineRequest> Lines, string Pin, string? Notes);

public sealed record CreateStockAdjustmentRequest(
Guid RawMaterialId, StoreType Store, decimal QuantityDelta, string Reason);

public sealed record ConsumeStockRequest(decimal QuantitySold);
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using RestaurantPOS.Domain.Enums;

namespace RestaurantPOS.API.Contracts.Kitchen;

public sealed record AdvanceKitchenTicketRequest(KitchenTicketStatus Status);
27 changes: 27 additions & 0 deletions backend/src/RestaurantPOS.API/Contracts/Orders/OrderRequests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using RestaurantPOS.Domain.Enums;

namespace RestaurantPOS.API.Contracts.Orders;

public sealed record CreateOrderRequest(Guid TableId);

public sealed record OrderItemRequest(Guid MenuItemId, int Quantity, string? SpecialInstructions);

public sealed record AddOrderItemsRequest(IReadOnlyCollection<OrderItemRequest> Items);

/// <summary>
/// The PIN travels with the change rather than being exchanged for a token first, matching how
/// stock releases are approved elsewhere in this system: one request carries both the action and
/// the authority for it, so there is no window where an approval exists unused.
/// </summary>
public sealed record ChangeOrderItemQuantityRequest(int Quantity, string? Pin);

public sealed record RemoveOrderItemRequest(string? Pin);

public sealed record CancelOrderRequest(string? Pin, string? Reason);

public sealed record SetOrderDiscountRequest(DiscountType Type, decimal Value);

public sealed record OrderPaymentRequest(
OrderPaymentMethod Method, decimal Amount, decimal? TenderedAmount, string? Reference);

public sealed record CompleteOrderPaymentRequest(IReadOnlyCollection<OrderPaymentRequest> Payments);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace RestaurantPOS.API.Contracts.Orders;

public sealed record CreateTableRequest(string Number, int Seats = 0, string? Notes = null);

public sealed record UpdateTableRequest(string Number, int Seats, string? Notes);

public sealed record SetTableActiveRequest(bool IsActive);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace RestaurantPOS.API.Contracts.Recipes;

public sealed record CreateMenuItemRequest(string Name, string Category, decimal Price);

public sealed record UpdateMenuItemRequest(string Name, string Category, decimal Price);

public sealed record SetMenuItemActiveRequest(bool IsActive);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace RestaurantPOS.API.Contracts.Recipes;

public sealed record RecipeLineRequest(Guid RawMaterialId, decimal Quantity);

/// <summary>Creates the menu item's recipe if it has none, or replaces its lines if it does.</summary>
public sealed record UpsertRecipeRequest(IReadOnlyCollection<RecipeLineRequest> Lines);

public sealed record SetRecipeEnabledRequest(bool IsEnabled);
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace RestaurantPOS.API.Contracts.Suppliers;

public sealed record PurchaseOrderLineRequest(Guid RawMaterialId, decimal Quantity, decimal UnitPrice);

public sealed record CreatePurchaseOrderRequest(
Guid SupplierId,
IReadOnlyCollection<PurchaseOrderLineRequest> Lines,
DateTime? ExpectedDeliveryDate,
string? Notes);

public sealed record UpdatePurchaseOrderRequest(
IReadOnlyCollection<PurchaseOrderLineRequest> Lines,
DateTime? ExpectedDeliveryDate,
string? Notes);
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using RestaurantPOS.Domain.Enums;

namespace RestaurantPOS.API.Contracts.Suppliers;

public sealed record RecordSupplierPaymentRequest(
decimal Amount,
DateTime PaymentDateUtc,
PaymentMethod Method,
string? InvoiceReference,
string? Notes);
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace RestaurantPOS.API.Contracts.Suppliers;

public sealed record SetSupplierPriceRequest(Guid RawMaterialId, decimal Price);
Loading
Loading