initial commit

This commit is contained in:
2026-07-21 13:59:38 +07:00
commit c40792266a
1321 changed files with 100465 additions and 0 deletions
@@ -0,0 +1,57 @@
using Indotalent.ConfigBackEnd.Extensions;
using Indotalent.Infrastructure.Database;
using MediatR;
namespace Indotalent.Features.Inventory.TransferOut.Cqrs;
public class CreateTransferOutRequest
{
public DateTime? TransferReleaseDate { get; set; }
public Data.Enums.TransferStatus Status { get; set; }
public string? Description { get; set; }
public string? WarehouseFromId { get; set; }
public string? WarehouseToId { get; set; }
}
public class CreateTransferOutResponse
{
public string? Id { get; set; }
public string? AutoNumber { get; set; }
}
public record CreateTransferOutCommand(CreateTransferOutRequest Data) : IRequest<CreateTransferOutResponse>;
public class CreateTransferOutHandler : IRequestHandler<CreateTransferOutCommand, CreateTransferOutResponse>
{
private readonly AppDbContext _context;
public CreateTransferOutHandler(AppDbContext context) => _context = context;
public async Task<CreateTransferOutResponse> Handle(CreateTransferOutCommand request, CancellationToken cancellationToken)
{
var entityName = nameof(Data.Entities.TransferOut);
var autoNo = await _context.GenerateAutoNumberAsync(
entityName: entityName,
prefixTemplate: $"{entityName.ToShortNameConsonant(3)}/{{Year}}/",
ct: cancellationToken
);
var entity = new Data.Entities.TransferOut
{
AutoNumber = autoNo,
TransferReleaseDate = request.Data.TransferReleaseDate,
Status = request.Data.Status,
Description = request.Data.Description,
WarehouseFromId = request.Data.WarehouseFromId,
WarehouseToId = request.Data.WarehouseToId
};
_context.TransferOut.Add(entity);
await _context.SaveChangesAsync(cancellationToken);
return new CreateTransferOutResponse
{
Id = entity.Id,
AutoNumber = entity.AutoNumber
};
}
}
@@ -0,0 +1,57 @@
using Indotalent.ConfigBackEnd.Extensions;
using Indotalent.Infrastructure.Database;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Indotalent.Features.Inventory.TransferOut.Cqrs;
public class CreateTransferOutItemRequest
{
public string? TransferOutId { get; set; }
public string? ProductId { get; set; }
public double? Movement { get; set; }
}
public record CreateTransferOutItemCommand(CreateTransferOutItemRequest Data) : IRequest<bool>;
public class CreateTransferOutItemHandler : IRequestHandler<CreateTransferOutItemCommand, bool>
{
private readonly AppDbContext _context;
public CreateTransferOutItemHandler(AppDbContext context) => _context = context;
public async Task<bool> Handle(CreateTransferOutItemCommand request, CancellationToken cancellationToken)
{
var master = await _context.TransferOut
.AsNoTracking()
.FirstOrDefaultAsync(x => x.Id == request.Data.TransferOutId, cancellationToken);
if (master == null) return false;
var ivtEntityName = nameof(Data.Entities.InventoryTransaction);
var autoNoIVT = await _context.GenerateAutoNumberAsync(
entityName: ivtEntityName,
prefixTemplate: $"{ivtEntityName.ToShortNameConsonant(3)}/{{Year}}/",
ct: cancellationToken
);
var entity = new Data.Entities.InventoryTransaction
{
ModuleId = master.Id,
ModuleName = nameof(Data.Entities.TransferOut),
ModuleCode = "TO-OUT",
ModuleNumber = master.AutoNumber,
MovementDate = master.TransferReleaseDate!.Value,
Status = (Data.Enums.InventoryTransactionStatus)master.Status,
AutoNumber = autoNoIVT,
WarehouseId = master.WarehouseFromId,
ProductId = request.Data.ProductId,
Movement = request.Data.Movement
};
_context.CalculateInvenTrans(entity);
_context.InventoryTransaction.Add(entity);
await _context.SaveChangesAsync(cancellationToken);
return true;
}
}
@@ -0,0 +1,14 @@
using FluentValidation;
namespace Indotalent.Features.Inventory.TransferOut.Cqrs;
public class CreateTransferOutValidator : AbstractValidator<CreateTransferOutRequest>
{
public CreateTransferOutValidator()
{
RuleFor(x => x.TransferReleaseDate).NotEmpty().WithMessage("Date is required");
RuleFor(x => x.WarehouseFromId).NotEmpty().WithMessage("Source warehouse is required");
RuleFor(x => x.WarehouseToId).NotEmpty().WithMessage("Destination warehouse is required")
.NotEqual(x => x.WarehouseFromId).WithMessage("Source and Destination warehouse cannot be the same");
}
}
@@ -0,0 +1,30 @@
using Indotalent.Infrastructure.Database;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Indotalent.Features.Inventory.TransferOut.Cqrs;
public record DeleteTransferOutByIdCommand(string Id) : IRequest<bool>;
public class DeleteTransferOutByIdHandler : IRequestHandler<DeleteTransferOutByIdCommand, bool>
{
private readonly AppDbContext _context;
public DeleteTransferOutByIdHandler(AppDbContext context) => _context = context;
public async Task<bool> Handle(DeleteTransferOutByIdCommand request, CancellationToken cancellationToken)
{
var entity = await _context.TransferOut
.FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken);
if (entity == null) return false;
var items = await _context.InventoryTransaction
.Where(x => x.ModuleId == request.Id && x.ModuleName == nameof(Data.Entities.TransferOut))
.ToListAsync(cancellationToken);
_context.InventoryTransaction.RemoveRange(items);
_context.TransferOut.Remove(entity);
await _context.SaveChangesAsync(cancellationToken);
return true;
}
}
@@ -0,0 +1,26 @@
using Indotalent.Infrastructure.Database;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Indotalent.Features.Inventory.TransferOut.Cqrs;
public record DeleteTransferOutItemCommand(string Id) : IRequest<bool>;
public class DeleteTransferOutItemHandler : IRequestHandler<DeleteTransferOutItemCommand, bool>
{
private readonly AppDbContext _context;
public DeleteTransferOutItemHandler(AppDbContext context) => _context = context;
public async Task<bool> Handle(DeleteTransferOutItemCommand request, CancellationToken cancellationToken)
{
var entity = await _context.InventoryTransaction
.FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken);
if (entity == null) return false;
_context.InventoryTransaction.Remove(entity);
await _context.SaveChangesAsync(cancellationToken);
return true;
}
}
@@ -0,0 +1,80 @@
using Indotalent.Infrastructure.Database;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Indotalent.Features.Inventory.TransferOut.Cqrs;
public class TransferOutItemResponse
{
public string? Id { get; set; }
public string? ProductId { get; set; }
public string? ProductName { get; set; }
public double? Movement { get; set; }
}
public class GetTransferOutByIdResponse
{
public string? Id { get; set; }
public string? AutoNumber { get; set; }
public DateTime? TransferReleaseDate { get; set; }
public Data.Enums.TransferStatus Status { get; set; }
public string? Description { get; set; }
public string? WarehouseFromId { get; set; }
public string? WarehouseFromName { get; set; }
public string? WarehouseToId { get; set; }
public string? WarehouseToName { get; set; }
public DateTimeOffset? CreatedAt { get; set; }
public string? CreatedBy { get; set; }
public DateTimeOffset? UpdatedAt { get; set; }
public string? UpdatedBy { get; set; }
public List<TransferOutItemResponse> Items { get; set; } = new();
}
public record GetTransferOutByIdQuery(string Id) : IRequest<GetTransferOutByIdResponse?>;
public class GetTransferOutByIdHandler : IRequestHandler<GetTransferOutByIdQuery, GetTransferOutByIdResponse?>
{
private readonly AppDbContext _context;
public GetTransferOutByIdHandler(AppDbContext context) => _context = context;
public async Task<GetTransferOutByIdResponse?> Handle(GetTransferOutByIdQuery request, CancellationToken cancellationToken)
{
var master = await _context.TransferOut
.AsNoTracking()
.Include(x => x.WarehouseFrom)
.Include(x => x.WarehouseTo)
.FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken);
if (master == null) return null;
var items = await _context.InventoryTransaction
.AsNoTracking()
.Include(x => x.Product)
.Where(x => x.ModuleId == request.Id && x.ModuleName == nameof(Data.Entities.TransferOut))
.Select(x => new TransferOutItemResponse
{
Id = x.Id,
ProductId = x.ProductId,
ProductName = x.Product!.Name,
Movement = x.Movement
}).ToListAsync(cancellationToken);
return new GetTransferOutByIdResponse
{
Id = master.Id,
AutoNumber = master.AutoNumber,
TransferReleaseDate = master.TransferReleaseDate,
Status = master.Status,
Description = master.Description,
WarehouseFromId = master.WarehouseFromId,
WarehouseFromName = master.WarehouseFrom?.Name,
WarehouseToId = master.WarehouseToId,
WarehouseToName = master.WarehouseTo?.Name,
CreatedAt = master.CreatedAt,
CreatedBy = master.CreatedBy,
UpdatedAt = master.UpdatedAt,
UpdatedBy = master.UpdatedBy,
Items = items
};
}
}
@@ -0,0 +1,42 @@
using Indotalent.Features.Sales.SalesReturn.Cqrs;
using Indotalent.Infrastructure.Database;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Indotalent.Features.Inventory.TransferOut.Cqrs;
public class GetTransferOutListResponse
{
public string? Id { get; set; }
public string? AutoNumber { get; set; }
public DateTime? TransferReleaseDate { get; set; }
public string? WarehouseFromName { get; set; }
public string? WarehouseToName { get; set; }
public Data.Enums.TransferStatus Status { get; set; }
}
public record GetTransferOutListQuery() : IRequest<List<GetTransferOutListResponse>>;
public class GetTransferOutListHandler : IRequestHandler<GetTransferOutListQuery, List<GetTransferOutListResponse>>
{
private readonly AppDbContext _context;
public GetTransferOutListHandler(AppDbContext context) => _context = context;
public async Task<List<GetTransferOutListResponse>> Handle(GetTransferOutListQuery request, CancellationToken cancellationToken)
{
return await _context.TransferOut
.AsNoTracking()
.Include(x => x.WarehouseFrom)
.Include(x => x.WarehouseTo)
.OrderByDescending(x => x.CreatedAt)
.Select(x => new GetTransferOutListResponse
{
Id = x.Id,
AutoNumber = x.AutoNumber,
TransferReleaseDate = x.TransferReleaseDate,
WarehouseFromName = x.WarehouseFrom!.Name,
WarehouseToName = x.WarehouseTo!.Name,
Status = x.Status
}).ToListAsync(cancellationToken);
}
}
@@ -0,0 +1,61 @@
using Indotalent.Infrastructure.Database;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Indotalent.Data.Enums;
using Indotalent.ConfigBackEnd.Extensions;
namespace Indotalent.Features.Inventory.TransferOut.Cqrs;
public class TransferOutLookupResponse
{
public List<LookupItem> Warehouses { get; set; } = new();
public List<LookupItem> Products { get; set; } = new();
public List<LookupStatusItem> Statuses { get; set; } = new();
}
public class LookupItem
{
public string? Id { get; set; }
public string? Name { get; set; }
}
public class LookupStatusItem
{
public int Value { get; set; }
public string? Name { get; set; }
}
public record GetTransferOutLookupQuery() : IRequest<TransferOutLookupResponse>;
public class GetTransferOutLookupHandler : IRequestHandler<GetTransferOutLookupQuery, TransferOutLookupResponse>
{
private readonly AppDbContext _context;
public GetTransferOutLookupHandler(AppDbContext context) => _context = context;
public async Task<TransferOutLookupResponse> Handle(GetTransferOutLookupQuery request, CancellationToken cancellationToken)
{
var response = new TransferOutLookupResponse();
response.Warehouses = await _context.Warehouse.AsNoTracking()
.Where(x => x.SystemWarehouse == false)
.OrderBy(x => x.Name)
.Select(x => new LookupItem { Id = x.Id, Name = x.Name })
.ToListAsync(cancellationToken);
response.Products = await _context.Product.AsNoTracking()
.Where(x => x.Physical == true)
.OrderBy(x => x.Name)
.Select(x => new LookupItem { Id = x.Id, Name = x.Name })
.ToListAsync(cancellationToken);
response.Statuses = Enum.GetValues(typeof(TransferStatus))
.Cast<TransferStatus>()
.Select(x => new LookupStatusItem
{
Value = (int)x,
Name = x.GetDescription()
}).ToList();
return response;
}
}
@@ -0,0 +1,58 @@
using Indotalent.Infrastructure.Database;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Indotalent.Features.Inventory.TransferOut.Cqrs;
public class UpdateTransferOutRequest
{
public string? Id { get; set; }
public string? AutoNumber { get; set; }
public DateTime? TransferReleaseDate { get; set; }
public Data.Enums.TransferStatus Status { get; set; }
public string? Description { get; set; }
public string? WarehouseFromId { get; set; }
public string? WarehouseToId { get; set; }
public DateTimeOffset? CreatedAt { get; set; }
public string? CreatedBy { get; set; }
public DateTimeOffset? UpdatedAt { get; set; }
public string? UpdatedBy { get; set; }
}
public record UpdateTransferOutCommand(UpdateTransferOutRequest Data) : IRequest<bool>;
public class UpdateTransferOutHandler : IRequestHandler<UpdateTransferOutCommand, bool>
{
private readonly AppDbContext _context;
public UpdateTransferOutHandler(AppDbContext context) => _context = context;
public async Task<bool> Handle(UpdateTransferOutCommand request, CancellationToken cancellationToken)
{
var entity = await _context.TransferOut
.FirstOrDefaultAsync(x => x.Id == request.Data.Id, cancellationToken);
if (entity == null) return false;
entity.TransferReleaseDate = request.Data.TransferReleaseDate;
entity.Status = request.Data.Status;
entity.Description = request.Data.Description;
entity.WarehouseFromId = request.Data.WarehouseFromId;
entity.WarehouseToId = request.Data.WarehouseToId;
var details = await _context.InventoryTransaction
.Where(x => x.ModuleId == entity.Id && x.ModuleName == nameof(Data.Entities.TransferOut))
.ToListAsync(cancellationToken);
foreach (var item in details)
{
item.MovementDate = entity.TransferReleaseDate ?? DateTime.Now;
item.Status = (Data.Enums.InventoryTransactionStatus)entity.Status;
item.WarehouseId = entity.WarehouseFromId;
item.ModuleNumber = entity.AutoNumber;
_context.CalculateInvenTrans(item);
}
await _context.SaveChangesAsync(cancellationToken);
return true;
}
}
@@ -0,0 +1,36 @@
using Indotalent.Infrastructure.Database;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace Indotalent.Features.Inventory.TransferOut.Cqrs;
public class UpdateTransferOutItemRequest
{
public string? Id { get; set; }
public string? ProductId { get; set; }
public double? Movement { get; set; }
}
public record UpdateTransferOutItemCommand(UpdateTransferOutItemRequest Data) : IRequest<bool>;
public class UpdateTransferOutItemHandler : IRequestHandler<UpdateTransferOutItemCommand, bool>
{
private readonly AppDbContext _context;
public UpdateTransferOutItemHandler(AppDbContext context) => _context = context;
public async Task<bool> Handle(UpdateTransferOutItemCommand request, CancellationToken cancellationToken)
{
var entity = await _context.InventoryTransaction
.FirstOrDefaultAsync(x => x.Id == request.Data.Id, cancellationToken);
if (entity == null) return false;
entity.ProductId = request.Data.ProductId;
entity.Movement = request.Data.Movement;
_context.CalculateInvenTrans(entity);
await _context.SaveChangesAsync(cancellationToken);
return true;
}
}
@@ -0,0 +1,14 @@
using FluentValidation;
namespace Indotalent.Features.Inventory.TransferOut.Cqrs;
public class UpdateTransferOutValidator : AbstractValidator<UpdateTransferOutRequest>
{
public UpdateTransferOutValidator()
{
RuleFor(x => x.Id).NotEmpty();
RuleFor(x => x.WarehouseFromId).NotEmpty().WithMessage("Source warehouse is required");
RuleFor(x => x.WarehouseToId).NotEmpty().WithMessage("Destination warehouse is required")
.NotEqual(x => x.WarehouseFromId).WithMessage("Source and Destination warehouse cannot be the same");
}
}