213 lines
8.3 KiB
C#
213 lines
8.3 KiB
C#
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;
|
|
}
|
|
}
|