initial commit
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
namespace Indotalent.Shared.Consts;
|
||||
|
||||
public static class GlobalConsts
|
||||
{
|
||||
public const string AppInitial = "WMS";
|
||||
public const string AppName = "WMS: Warehouse Management System";
|
||||
public const string AppTagline = "Smart Warehousing, Seamless Inventory Flow.";
|
||||
|
||||
public const int StringLengthId = 36;
|
||||
public const int StringLengthShort = 500;
|
||||
public const int StringLengthMedium = 1000;
|
||||
public const int StringLengthLong = 4000;
|
||||
|
||||
|
||||
public const int ODataMaxTop = 5000;
|
||||
|
||||
|
||||
public const string BehaviourError = nameof(BehaviourError);
|
||||
public const string GlobalError = nameof(GlobalError);
|
||||
|
||||
public static class Roles
|
||||
{
|
||||
public const string Administrator = "Administrator";
|
||||
public const string Manager = "Manager";
|
||||
public const string Employee = "Employee";
|
||||
}
|
||||
|
||||
public static class Formatting
|
||||
{
|
||||
public const string DateFormat = "dd MMM yyyy";
|
||||
public const string DateTimeFormat = "dd MMM yyyy HH:mm";
|
||||
public const string CurrencyCulture = "id-ID";
|
||||
}
|
||||
|
||||
public static class Pagination
|
||||
{
|
||||
public const int DefaultPageSize = 10;
|
||||
public static readonly int[] PageSizeOptions = { 10, 25, 50, 100 };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Indotalent.Shared.Models;
|
||||
|
||||
public class ApiRequest
|
||||
{
|
||||
public int Skip { get; set; } = 1;
|
||||
public int Top { get; set; } = 5;
|
||||
public string? SearchTerm { get; set; }
|
||||
public string? SortBy { get; set; }
|
||||
public bool IsAscending { get; set; } = true;
|
||||
public bool IsPagedEnabled { get; set; } = true;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Indotalent.Shared.Models;
|
||||
|
||||
public class ApiResponse<T>
|
||||
{
|
||||
public bool IsSuccess { get; set; }
|
||||
public int StatusCode { get; set; }
|
||||
public string? Message { get; set; }
|
||||
public T? Value { get; set; }
|
||||
public PaginationMetadata? Pagination { get; set; }
|
||||
public List<string>? Errors { get; set; }
|
||||
public DateTime ServerTime { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public class PaginationMetadata
|
||||
{
|
||||
public int Count { get; set; }
|
||||
public int Top { get; set; }
|
||||
public int Skip { get; set; }
|
||||
public int TotalPages => (int)Math.Ceiling((double)Count / (Top == 0 ? 1 : Top));
|
||||
public bool HasPrevious => Skip > 0;
|
||||
public bool HasNext => (Skip + Top) < Count;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Indotalent.Shared.Models;
|
||||
|
||||
public class PagedList<T>
|
||||
{
|
||||
public PagedList()
|
||||
{
|
||||
Value = new List<T>();
|
||||
}
|
||||
|
||||
public List<T> Value { get; set; }
|
||||
public int Skip { get; set; }
|
||||
public int Top { get; set; }
|
||||
public int TotalPages { get; set; }
|
||||
public int Count { get; set; }
|
||||
|
||||
[JsonConstructor]
|
||||
public PagedList(List<T> value, int count, int skip, int top, int totalPages)
|
||||
{
|
||||
Value = value;
|
||||
Count = count;
|
||||
Skip = skip;
|
||||
Top = top;
|
||||
TotalPages = totalPages;
|
||||
}
|
||||
|
||||
public PagedList(List<T> value, int count, int skip, int top)
|
||||
{
|
||||
Value = value;
|
||||
Count = count;
|
||||
Skip = skip;
|
||||
Top = top;
|
||||
TotalPages = (int)Math.Ceiling(count / (double)top);
|
||||
}
|
||||
|
||||
public bool HasPrevious => Skip > 0;
|
||||
public bool HasNext => (Skip + Top) < Count;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace Indotalent.Shared.Utils;
|
||||
|
||||
public static class FluentValidationHelper
|
||||
{
|
||||
public static Func<object, string, Task<IEnumerable<string>>> ValidateValue<T>(this AbstractValidator<T> validator) => async (model, propertyName) =>
|
||||
{
|
||||
if (model is not T typedModel)
|
||||
return Array.Empty<string>();
|
||||
|
||||
var context = ValidationContext<T>.CreateWithOptions(typedModel, x => x.IncludeProperties(propertyName));
|
||||
var result = await validator.ValidateAsync(context);
|
||||
|
||||
if (result.IsValid)
|
||||
return Array.Empty<string>();
|
||||
|
||||
return result.Errors.Select(e => e.ErrorMessage);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
using Indotalent.Data.Entities;
|
||||
using Indotalent.Data.Enums;
|
||||
using Indotalent.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Indotalent.Shared.Utils;
|
||||
|
||||
public static class InventoryTransactionHelper
|
||||
{
|
||||
public static void CalculateInvenTrans(AppDbContext context, InventoryTransaction transaction)
|
||||
{
|
||||
if (transaction == null)
|
||||
{
|
||||
throw new Exception("Inventory transaction is null");
|
||||
}
|
||||
|
||||
var moduleName = transaction.ModuleName;
|
||||
|
||||
if (moduleName != "StockCount" && (transaction.Movement ?? 0.0) <= 0.0)
|
||||
{
|
||||
throw new Exception("Quantity must not zero and should be positive.");
|
||||
}
|
||||
|
||||
if (moduleName == "StockCount" && (transaction.QtySCCount ?? 0.0) <= 0.0)
|
||||
{
|
||||
throw new Exception("Quantity must not zero and should be positive.");
|
||||
}
|
||||
|
||||
switch (moduleName)
|
||||
{
|
||||
case "DeliveryOrder":
|
||||
transaction.TransType = InventoryTransType.Out;
|
||||
transaction.WarehouseFromId = transaction.WarehouseId;
|
||||
transaction.WarehouseToId = GetSystemWarehouseId(context, "CUSTOMER");
|
||||
break;
|
||||
|
||||
case "GoodsReceive":
|
||||
transaction.TransType = InventoryTransType.In;
|
||||
transaction.WarehouseFromId = GetSystemWarehouseId(context, "VENDOR");
|
||||
transaction.WarehouseToId = transaction.WarehouseId;
|
||||
break;
|
||||
|
||||
case "SalesReturn":
|
||||
transaction.TransType = InventoryTransType.In;
|
||||
transaction.WarehouseFromId = GetSystemWarehouseId(context, "VENDOR");
|
||||
transaction.WarehouseToId = transaction.WarehouseId;
|
||||
break;
|
||||
|
||||
case "PurchaseReturn":
|
||||
transaction.TransType = InventoryTransType.Out;
|
||||
transaction.WarehouseFromId = transaction.WarehouseId;
|
||||
transaction.WarehouseToId = GetSystemWarehouseId(context, "CUSTOMER");
|
||||
break;
|
||||
|
||||
case "TransferIn":
|
||||
transaction.TransType = InventoryTransType.In;
|
||||
transaction.WarehouseFromId = GetSystemWarehouseId(context, "TRANSIT");
|
||||
transaction.WarehouseToId = transaction.WarehouseId;
|
||||
break;
|
||||
|
||||
case "TransferOut":
|
||||
transaction.TransType = InventoryTransType.Out;
|
||||
transaction.WarehouseFromId = transaction.WarehouseId;
|
||||
transaction.WarehouseToId = GetSystemWarehouseId(context, "TRANSIT");
|
||||
break;
|
||||
|
||||
case "StockCount":
|
||||
transaction.QtySCSys = GetStock(context, transaction.WarehouseId, transaction.ProductId, transaction.Id);
|
||||
transaction.QtySCDelta = transaction.QtySCSys - transaction.QtySCCount;
|
||||
transaction.Movement = Math.Abs(transaction.QtySCDelta ?? 0.0);
|
||||
|
||||
if (transaction.QtySCDelta < 0.0)
|
||||
{
|
||||
transaction.TransType = InventoryTransType.In;
|
||||
transaction.WarehouseFromId = GetSystemWarehouseId(context, "STOCK_COUNT");
|
||||
transaction.WarehouseToId = transaction.WarehouseId;
|
||||
}
|
||||
else
|
||||
{
|
||||
transaction.TransType = InventoryTransType.Out;
|
||||
transaction.WarehouseFromId = transaction.WarehouseId;
|
||||
transaction.WarehouseToId = GetSystemWarehouseId(context, "STOCK_COUNT");
|
||||
}
|
||||
break;
|
||||
|
||||
case "NegativeAdjustment":
|
||||
transaction.TransType = InventoryTransType.Out;
|
||||
transaction.WarehouseFromId = transaction.WarehouseId;
|
||||
transaction.WarehouseToId = GetSystemWarehouseId(context, "ADJUSTMENT");
|
||||
break;
|
||||
|
||||
case "PositiveAdjustment":
|
||||
transaction.TransType = InventoryTransType.In;
|
||||
transaction.WarehouseFromId = GetSystemWarehouseId(context, "ADJUSTMENT");
|
||||
transaction.WarehouseToId = transaction.WarehouseId;
|
||||
break;
|
||||
|
||||
case "Scrapping":
|
||||
transaction.TransType = InventoryTransType.Out;
|
||||
transaction.WarehouseFromId = transaction.WarehouseId;
|
||||
transaction.WarehouseToId = GetSystemWarehouseId(context, "SCRAPPING");
|
||||
break;
|
||||
}
|
||||
|
||||
transaction.Stock = (transaction.Movement ?? 0.0) * (int)(transaction.TransType ?? InventoryTransType.In);
|
||||
}
|
||||
|
||||
public static double GetStock(AppDbContext context, string? warehouseId, string? productId, string? currentId = null)
|
||||
{
|
||||
return context.InventoryTransaction
|
||||
.Where(x =>
|
||||
x.Status == InventoryTransactionStatus.Confirmed &&
|
||||
x.WarehouseId == warehouseId &&
|
||||
x.ProductId == productId &&
|
||||
x.Id != currentId)
|
||||
.Sum(x => x.Stock ?? 0.0);
|
||||
}
|
||||
|
||||
private static string? GetSystemWarehouseId(AppDbContext context, string name)
|
||||
{
|
||||
return context.Warehouse
|
||||
.Where(x => x.SystemWarehouse == true && x.Name == name)
|
||||
.Select(x => x.Id)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Indotalent.Data.Entities;
|
||||
using Indotalent.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Indotalent.Shared.Utils;
|
||||
|
||||
public static class PurchaseOrderHelper
|
||||
{
|
||||
public static void Recalculate(AppDbContext context, string purchaseOrderId)
|
||||
{
|
||||
var purchaseOrder = context.PurchaseOrder
|
||||
.Include(x => x.Tax)
|
||||
.SingleOrDefault(x => x.Id == purchaseOrderId);
|
||||
|
||||
if (purchaseOrder == null)
|
||||
return;
|
||||
|
||||
var purchaseOrderItems = context.PurchaseOrderItem
|
||||
.Where(x => x.PurchaseOrderId == purchaseOrderId)
|
||||
.ToList();
|
||||
|
||||
purchaseOrder.BeforeTaxAmount = purchaseOrderItems.Sum(x => (x.UnitPrice ?? 0) * (decimal)(x.Quantity ?? 0));
|
||||
|
||||
var taxPercentage = purchaseOrder.Tax?.PercentageValue ?? 0;
|
||||
purchaseOrder.TaxAmount = (purchaseOrder.BeforeTaxAmount ?? 0) * taxPercentage / 100;
|
||||
|
||||
purchaseOrder.AfterTaxAmount = (purchaseOrder.BeforeTaxAmount ?? 0) + (purchaseOrder.TaxAmount ?? 0);
|
||||
|
||||
context.PurchaseOrder.Update(purchaseOrder);
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Indotalent.Data.Entities;
|
||||
using Indotalent.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Indotalent.Shared.Utils;
|
||||
|
||||
public static class PurchaseRequisitionHelper
|
||||
{
|
||||
public static void Recalculate(AppDbContext context, string requisitionId)
|
||||
{
|
||||
var requisition = context.PurchaseRequisition
|
||||
.Include(x => x.Tax)
|
||||
.SingleOrDefault(x => x.Id == requisitionId);
|
||||
|
||||
if (requisition == null) return;
|
||||
|
||||
var items = context.PurchaseRequisitionItem
|
||||
.Where(x => x.PurchaseRequisitionId == requisitionId)
|
||||
.ToList();
|
||||
|
||||
requisition.BeforeTaxAmount = items.Sum(x => (x.UnitPrice ?? 0m) * (decimal)(x.Quantity ?? 0.0));
|
||||
|
||||
var taxPercentage = requisition.Tax?.PercentageValue ?? 0;
|
||||
requisition.TaxAmount = (requisition.BeforeTaxAmount ?? 0m) * (decimal)taxPercentage / 100m;
|
||||
|
||||
requisition.AfterTaxAmount = (requisition.BeforeTaxAmount ?? 0m) + (requisition.TaxAmount ?? 0m);
|
||||
|
||||
context.PurchaseRequisition.Update(requisition);
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Indotalent.Data.Entities;
|
||||
using Indotalent.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Indotalent.Shared.Utils;
|
||||
|
||||
public static class SalesOrderHelper
|
||||
{
|
||||
public static void Recalculate(AppDbContext context, string salesOrderId)
|
||||
{
|
||||
var salesOrder = context.SalesOrder
|
||||
.Include(x => x.Tax)
|
||||
.SingleOrDefault(x => x.Id == salesOrderId);
|
||||
|
||||
if (salesOrder == null)
|
||||
return;
|
||||
|
||||
var salesOrderItems = context.SalesOrderItem
|
||||
.Where(x => x.SalesOrderId == salesOrderId)
|
||||
.ToList();
|
||||
|
||||
salesOrder.BeforeTaxAmount = salesOrderItems.Sum(x => x.Total ?? 0m);
|
||||
|
||||
var taxPercentage = salesOrder.Tax?.PercentageValue ?? 0;
|
||||
salesOrder.TaxAmount = (salesOrder.BeforeTaxAmount ?? 0m) * (decimal)taxPercentage / 100m;
|
||||
|
||||
salesOrder.AfterTaxAmount = (salesOrder.BeforeTaxAmount ?? 0m) + (salesOrder.TaxAmount ?? 0m);
|
||||
|
||||
context.SalesOrder.Update(salesOrder);
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Indotalent.Data.Entities;
|
||||
using Indotalent.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Indotalent.Shared.Utils;
|
||||
|
||||
public static class SalesQuotationHelper
|
||||
{
|
||||
public static void Recalculate(AppDbContext context, string quotationId)
|
||||
{
|
||||
var quotation = context.SalesQuotation
|
||||
.Include(x => x.Tax)
|
||||
.SingleOrDefault(x => x.Id == quotationId);
|
||||
|
||||
if (quotation == null) return;
|
||||
|
||||
var items = context.SalesQuotationItem
|
||||
.Where(x => x.SalesQuotationId == quotationId)
|
||||
.ToList();
|
||||
|
||||
quotation.BeforeTaxAmount = items.Sum(x => (x.UnitPrice ?? 0m) * (decimal)(x.Quantity ?? 0.0));
|
||||
|
||||
var taxPercentage = quotation.Tax?.PercentageValue ?? 0;
|
||||
quotation.TaxAmount = (quotation.BeforeTaxAmount ?? 0m) * (decimal)taxPercentage / 100m;
|
||||
|
||||
quotation.AfterTaxAmount = (quotation.BeforeTaxAmount ?? 0m) + (quotation.TaxAmount ?? 0m);
|
||||
|
||||
context.SalesQuotation.Update(quotation);
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace Indotalent.Shared.Utils;
|
||||
|
||||
public static class SequentialGuidGenerator
|
||||
{
|
||||
public static string NewSequentialId()
|
||||
{
|
||||
byte[] guidBytes = Guid.NewGuid().ToByteArray();
|
||||
|
||||
byte[] timestampBytes = BitConverter.GetBytes(DateTime.UtcNow.Ticks);
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse(timestampBytes);
|
||||
}
|
||||
|
||||
guidBytes[10] = timestampBytes[2];
|
||||
guidBytes[11] = timestampBytes[3];
|
||||
guidBytes[12] = timestampBytes[4];
|
||||
guidBytes[13] = timestampBytes[5];
|
||||
guidBytes[14] = timestampBytes[6];
|
||||
guidBytes[15] = timestampBytes[7];
|
||||
|
||||
return new Guid(guidBytes).ToString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user