feat: initialize manufacturing progress dashboard
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using DongfangHydro.Dashboard.Api.Data;
|
||||
using DongfangHydro.Dashboard.Api.Domain;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
namespace DongfangHydro.Dashboard.Tests;
|
||||
|
||||
public sealed class BomDataApiTests(DashboardApiFactory factory)
|
||||
: IClassFixture<DashboardApiFactory>
|
||||
{
|
||||
[Fact]
|
||||
public async Task MaterialQuotas_supports_category_keyword_and_paging()
|
||||
{
|
||||
using var client = factory.CreateClient();
|
||||
Guid orderId;
|
||||
await using (var scope = factory.Services.CreateAsyncScope())
|
||||
{
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<DashboardDbContext>();
|
||||
orderId = await dbContext.ProductionOrders.Select(order => order.Id).FirstAsync();
|
||||
dbContext.MaterialQuotas.AddRange(
|
||||
CreateQuota(orderId, "钢板", "1010100182", "钢板/Q235B", 9),
|
||||
CreateQuota(orderId, "标准件", "1110100001", "螺栓/8.8级", 188));
|
||||
await dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
var response = await client.GetAsync(
|
||||
$"/api/orders/{orderId}/material-quotas?category=钢板&keyword=Q235B&page=1&pageSize=10");
|
||||
var body = await response.Content.ReadFromJsonAsync<JsonElement>();
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal(1, body.GetProperty("total").GetInt32());
|
||||
var item = Assert.Single(body.GetProperty("items").EnumerateArray());
|
||||
Assert.Equal("钢板", item.GetProperty("category").GetString());
|
||||
Assert.Equal("1010100182", item.GetProperty("materialCode").GetString());
|
||||
Assert.Equal(33.61m, item.GetProperty("netWeight").GetDecimal());
|
||||
Assert.Equal(35.6266m, item.GetProperty("consumptionQuota").GetDecimal());
|
||||
Assert.Equal("33.61", item.GetProperty("netWeightText").GetString());
|
||||
Assert.Equal("35.6266", item.GetProperty("consumptionQuotaText").GetString());
|
||||
Assert.Equal(9, item.GetProperty("sourceRow").GetInt32());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Tree_exposes_imported_bom_fields()
|
||||
{
|
||||
using var client = factory.CreateClient();
|
||||
Guid orderId;
|
||||
Guid nodeId;
|
||||
await using (var scope = factory.Services.CreateAsyncScope())
|
||||
{
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<DashboardDbContext>();
|
||||
var node = await dbContext.ProductionNodes.FirstAsync(item => item.NodeType == "process");
|
||||
orderId = node.OrderId;
|
||||
nodeId = node.Id;
|
||||
node.Specification = "M16";
|
||||
node.Material = "标准件";
|
||||
node.UnitWeight = 0.2m;
|
||||
node.TotalWeight = 1.6m;
|
||||
node.UnitWeightText = "0.2";
|
||||
node.TotalWeightText = "1.6";
|
||||
node.BomQuantity = -8m;
|
||||
node.BomQuantityText = "-8";
|
||||
node.Remark = "镀锌";
|
||||
node.SourceSequence = "1.1.2";
|
||||
node.SourceSheet = "明细";
|
||||
node.SourceRow = 10;
|
||||
await dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
var root = await client.GetFromJsonAsync<JsonElement>($"/api/orders/{orderId}/tree");
|
||||
var nodeJson = FindNode(root, nodeId);
|
||||
|
||||
Assert.Equal("M16", nodeJson.GetProperty("specification").GetString());
|
||||
Assert.Equal("标准件", nodeJson.GetProperty("material").GetString());
|
||||
Assert.Equal(0.2m, nodeJson.GetProperty("unitWeight").GetDecimal());
|
||||
Assert.Equal(1.6m, nodeJson.GetProperty("totalWeight").GetDecimal());
|
||||
Assert.Equal("0.2", nodeJson.GetProperty("unitWeightText").GetString());
|
||||
Assert.Equal("1.6", nodeJson.GetProperty("totalWeightText").GetString());
|
||||
Assert.Equal(-8m, nodeJson.GetProperty("bomQuantity").GetDecimal());
|
||||
Assert.Equal("-8", nodeJson.GetProperty("bomQuantityText").GetString());
|
||||
Assert.Equal("镀锌", nodeJson.GetProperty("remark").GetString());
|
||||
Assert.Equal("1.1.2", nodeJson.GetProperty("sourceSequence").GetString());
|
||||
Assert.Equal("明细", nodeJson.GetProperty("sourceSheet").GetString());
|
||||
Assert.Equal(10, nodeJson.GetProperty("sourceRow").GetInt32());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Material_leaf_accepts_direct_progress_updates()
|
||||
{
|
||||
using var client = factory.CreateClient();
|
||||
var orderId = Guid.NewGuid();
|
||||
var rootId = Guid.NewGuid();
|
||||
var materialId = Guid.NewGuid();
|
||||
await using (var scope = factory.Services.CreateAsyncScope())
|
||||
{
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<DashboardDbContext>();
|
||||
dbContext.ProductionOrders.Add(new ProductionOrder
|
||||
{
|
||||
Id = orderId,
|
||||
Code = $"ZZ-MAT-{orderId:N}"[..24],
|
||||
ProductName = "物料报工测试",
|
||||
BatchQty = 1,
|
||||
RequiredQty = 10,
|
||||
Status = "waiting",
|
||||
RiskLevel = "normal",
|
||||
DataSource = "test",
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow,
|
||||
RowVersion = [1],
|
||||
});
|
||||
dbContext.ProductionNodes.AddRange(
|
||||
new ProductionNode
|
||||
{
|
||||
Id = rootId,
|
||||
OrderId = orderId,
|
||||
Code = "ROOT",
|
||||
Name = "测试订单",
|
||||
NodeType = "order",
|
||||
SupplyType = "self_made",
|
||||
RequiredQty = 10,
|
||||
Status = "waiting",
|
||||
RiskLevel = "normal",
|
||||
UpdatedAt = DateTimeOffset.UtcNow,
|
||||
RowVersion = [1],
|
||||
},
|
||||
new ProductionNode
|
||||
{
|
||||
Id = materialId,
|
||||
OrderId = orderId,
|
||||
ParentNodeId = rootId,
|
||||
Code = "MAT-01",
|
||||
Name = "钢板",
|
||||
Level = 1,
|
||||
NodeType = "material",
|
||||
SupplyType = "purchased",
|
||||
RequiredQty = 10,
|
||||
Status = "waiting",
|
||||
RiskLevel = "normal",
|
||||
UpdatedAt = DateTimeOffset.UtcNow,
|
||||
RowVersion = [1],
|
||||
});
|
||||
await dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
var tree = await client.GetFromJsonAsync<JsonElement>($"/api/orders/{orderId}/tree");
|
||||
var material = FindNode(tree, materialId);
|
||||
var response = await client.PatchAsJsonAsync(
|
||||
$"/api/nodes/{materialId}/progress",
|
||||
new
|
||||
{
|
||||
completedQty = 10,
|
||||
expectedVersion = material.GetProperty("version").GetString(),
|
||||
status = "done",
|
||||
riskLevel = "normal",
|
||||
});
|
||||
var body = await response.Content.ReadFromJsonAsync<JsonElement>();
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal("material", body.GetProperty("node").GetProperty("nodeType").GetString());
|
||||
Assert.Equal(100, body.GetProperty("node").GetProperty("progress").GetInt32());
|
||||
}
|
||||
|
||||
private static MaterialQuota CreateQuota(
|
||||
Guid orderId,
|
||||
string category,
|
||||
string materialCode,
|
||||
string materialName,
|
||||
int sourceRow)
|
||||
{
|
||||
return new MaterialQuota
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
OrderId = orderId,
|
||||
Category = category,
|
||||
SourceSequence = "1",
|
||||
MaterialCode = materialCode,
|
||||
MaterialName = materialName,
|
||||
Specification = "δ2-1500",
|
||||
Unit = "kg",
|
||||
NetWeight = 33.61m,
|
||||
ConsumptionQuota = 35.6266m,
|
||||
NetWeightText = "33.61",
|
||||
ConsumptionQuotaText = "35.6266",
|
||||
Brand = "无",
|
||||
Remark = string.Empty,
|
||||
SourceSheet = "材料定额",
|
||||
SourceRow = sourceRow,
|
||||
};
|
||||
}
|
||||
|
||||
private static JsonElement FindNode(JsonElement node, Guid nodeId)
|
||||
{
|
||||
if (node.GetProperty("id").GetGuid() == nodeId)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
foreach (var child in node.GetProperty("children").EnumerateArray())
|
||||
{
|
||||
var match = FindNode(child, nodeId);
|
||||
if (match.ValueKind != JsonValueKind.Undefined)
|
||||
{
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user