CUB-32: Add usage logging service with EF Core entity, service, controller, and migration
Some checks failed
Dev Build / build-test (pull_request) Failing after 58s
Dev Build / deploy-dev (pull_request) Has been skipped
Dev Build / notify-success (pull_request) Has been skipped
Dev Build / notify-failure (pull_request) Successful in 3s

This commit is contained in:
2026-04-26 18:44:02 +00:00
parent 7d0369b8e9
commit 42e90f028a
11 changed files with 2299 additions and 64 deletions

View File

@@ -0,0 +1,72 @@
using Extrudex.Domain.Base;
using Extrudex.Domain.Enums;
namespace Extrudex.Domain.Entities;
/// <summary>
/// Represents a single filament usage log entry. Records how much filament
/// was consumed, by which printer, at what time, and optionally linked to
/// a print job. This provides a fine-grained audit trail of filament consumption
/// independent of print job lifecycle.
/// </summary>
public class UsageLog : AuditableEntity
{
/// <summary>
/// Foreign key to the spool that provided the filament.
/// </summary>
public Guid SpoolId { get; set; }
/// <summary>
/// Navigation to the spool that provided the filament.
/// </summary>
public Spool Spool { get; set; } = null!;
/// <summary>
/// Foreign key to the printer that consumed the filament.
/// Nullable to support manual entries without a specific printer.
/// </summary>
public Guid? PrinterId { get; set; }
/// <summary>
/// Navigation to the printer that consumed the filament.
/// </summary>
public Printer? Printer { get; set; }
/// <summary>
/// Foreign key to the print job associated with this usage entry.
/// Nullable because usage can be logged before or without a print job.
/// </summary>
public Guid? PrintJobId { get; set; }
/// <summary>
/// Navigation to the print job associated with this usage entry.
/// </summary>
public PrintJob? PrintJob { get; set; }
/// <summary>
/// The number of grams of filament consumed in this usage event.
/// </summary>
public decimal GramsUsed { get; set; }
/// <summary>
/// The number of millimeters of filament extruded in this usage event.
/// Optional — may not be available for all data sources.
/// </summary>
public decimal? MmExtruded { get; set; }
/// <summary>
/// Timestamp when the usage occurred (UTC). This is the actual time of
/// consumption, which may differ from CreatedAt if the entry was recorded later.
/// </summary>
public DateTime UsageTimestamp { get; set; } = DateTime.UtcNow;
/// <summary>
/// The source of the usage data (which integration path provided it).
/// </summary>
public DataSource DataSource { get; set; } = DataSource.Manual;
/// <summary>
/// Optional notes about this usage entry.
/// </summary>
public string? Notes { get; set; }
}

View File

@@ -0,0 +1,57 @@
using Extrudex.Domain.Entities;
using Extrudex.Domain.Enums;
namespace Extrudex.Domain.Interfaces;
/// <summary>
/// Service for recording filament usage entries. Writes to the usage_logs table
/// and provides query capabilities for usage history.
/// </summary>
public interface IUsageLogService
{
/// <summary>
/// Records a filament usage entry.
/// </summary>
/// <param name="spoolId">The spool that provided the filament.</param>
/// <param name="gramsUsed">Grams of filament consumed.</param>
/// <param name="dataSource">Where the data came from.</param>
/// <param name="printerId">Optional printer ID.</param>
/// <param name="printJobId">Optional print job ID.</param>
/// <param name="mmExtruded">Optional mm extruded.</param>
/// <param name="usageTimestamp">When the usage occurred (defaults to UTC now).</param>
/// <param name="notes">Optional notes.</param>
/// <returns>The created UsageLog entity.</returns>
Task<UsageLog> RecordUsageAsync(
Guid spoolId,
decimal gramsUsed,
DataSource dataSource,
Guid? printerId = null,
Guid? printJobId = null,
decimal? mmExtruded = null,
DateTime? usageTimestamp = null,
string? notes = null);
/// <summary>
/// Retrieves usage logs for a specific spool, ordered by usage timestamp descending.
/// </summary>
/// <param name="spoolId">The spool ID to filter by.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A collection of usage logs for the spool.</returns>
Task<IEnumerable<UsageLog>> GetBySpoolAsync(Guid spoolId, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves usage logs for a specific printer, ordered by usage timestamp descending.
/// </summary>
/// <param name="printerId">The printer ID to filter by.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A collection of usage logs for the printer.</returns>
Task<IEnumerable<UsageLog>> GetByPrinterAsync(Guid printerId, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves usage logs for a specific print job, ordered by usage timestamp descending.
/// </summary>
/// <param name="printJobId">The print job ID to filter by.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A collection of usage logs for the print job.</returns>
Task<IEnumerable<UsageLog>> GetByPrintJobAsync(Guid printJobId, CancellationToken cancellationToken = default);
}