关于实仓与虚仓和调拨单和虚仓调拨单的区别

本文详细介绍了实仓与虚仓的概念,以及实仓、虚仓之间与实仓、虚仓之间的调拨业务的区别,包括成本核算与成本管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 何谓实仓?何谓虚仓?

        核算成本的仓库我们称之为实仓,不核算成本的仓库我们称之为虚仓.

调拨单和虚仓调拨单的区别

        1、实仓同实仓之间的调拨业务:这种业务主要处理不同实仓间的货品调拨,由于实仓是需要核算货品成本的,因此不同仓库间的调拨存在两种情况,同价调拨和异价调拨。

        2、实仓同虚仓之间的调拨业务:这种业务处理的是实仓和虚仓的货品调拨,由于虚仓是不需 要核算货品成本的,因此不同仓库间的调拨只有一种情况,异价调拨,所有从实仓调拨到虚仓的货品成本单价自动为零;所有送虚仓调拨到实仓的货品,用户需要为 货品赋予单价,并作为入库成本参与实仓的货品成本核算。

        3、虚仓同虚仓之间的调拨业务:这种业务处理虚仓间的货品调拨,由于虚仓是不需要核算成本的,因此不同虚仓间的调拨只有一种情况,同价调拨,所有虚仓间的货品在调拨前后,单价为零,只对数量进行管理,因此虚仓调拨单不存在成本核算,也不会生成凭证。

        说完这三种调拨业务,再说一下调拨单和虚仓调拨单的业务处理区别,调拨单处理第一种和第二种调拨业务,虚仓调拨单处理第三种调拨业务

<think>在WMS(库管理系统)中,库存调拨模块通常涉及两个主要体:调拨单(TransferOrder)调拨明细(TransferDetail)。一个调拨单包含多个调拨明细(即多个产品)。这是一个典型的一对多关系。 我们将设计两个类: 1. TransferOrder(调拨单主表) 2. TransferDetail(调拨明细表,包含产品信息、数量等) 数据库表结构可能如下: - TransferOrder 表:Id(主键)、调拨单号、调出库、调入库、调拨日期、状态等。 - TransferDetail 表:Id(主键)、TransferOrderId(外键)、ProductId、调拨数量、批次号等。 在代码中,我们可以使用体类来表示这些表,并使用Entity Framework Core(ORM)来操作数据库。 假设我们使用ASP.NET Core Web API,并采用Repository模式(为了代码清晰可维护性),但为了简化,这里我们直接使用DbContext进行演示。 步骤: 1. 定义体类。 2. 创建DbContext。 3. 现增删改查(CRUD)操作。 注意:为了高效,我们使用异步编程。 首先,定义体类: ```csharp public class TransferOrder { public int Id { get; set; } public string TransferOrderNumber { get; set; } // 调拨单号 public int FromWarehouseId { get; set; } // 调出库ID public int ToWarehouseId { get; set; } // 调入库ID public DateTime TransferDate { get; set; } // 调拨日期 public string Status { get; set; } // 状态 // 导航属性:一个调拨单有多个调拨明细 public ICollection<TransferDetail> TransferDetails { get; set; } } public class TransferDetail { public int Id { get; set; } public int TransferOrderId { get; set; } // 外键 public int ProductId { get; set; } // 产品ID public int Quantity { get; set; } // 调拨数量 public string BatchNumber { get; set; } // 批次号 // 导航属性:属于一个调拨单 public TransferOrder TransferOrder { get; set; } } ``` 然后,创建DbContext: ```csharp public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } public DbSet<TransferOrder> TransferOrders { get; set; } public DbSet<TransferDetail> TransferDetails { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置关系(可选,EF Core默认约定已经可以识别) modelBuilder.Entity<TransferOrder>() .HasMany(o => o.TransferDetails) .WithOne(d => d.TransferOrder) .HasForeignKey(d => d.TransferOrderId) .OnDelete(DeleteBehavior.Cascade); // 设置级联删除,当主单删除时,明细自动删除 } } ``` 接下来,我们编写一个服务类(或Repository)来处理调拨单的CRUD操作。 为了高效方便,我们使用以下方法: - 新增:同时添加主单明细 - 修改:更新主单信息,同时更新明细(可能包括明细的增删改) - 删除:删除主单(级联删除明细) - 查询:根据主单ID获取主单及其所有明细 这里,我们使用一个服务类TransferOrderService: ```csharp public class TransferOrderService { private readonly AppDbContext _context; public TransferOrderService(AppDbContext context) { _context = context; } // 新增调拨单(包含明细) public async Task CreateTransferOrderAsync(TransferOrder transferOrder) { _context.TransferOrders.Add(transferOrder); await _context.SaveChangesAsync(); } // 修改调拨单(包括更新明细) // 注意:修改时,我们假设前端传递了完整的调拨单对象(包括修改后的明细) // 策略:先删除原有明细,再添加新的明细(或者根据情况更新,这里采用简单方式:全删全增) public async Task UpdateTransferOrderAsync(TransferOrder transferOrder) { // 先查询出原有的调拨单明细 var existingOrder = await _context.TransferOrders .Include(o => o.TransferDetails) // 包含明细 .FirstOrDefaultAsync(o => o.Id == transferOrder.Id); if (existingOrder == null) { throw new Exception("调拨单不存在"); } // 更新主单信息(除了明细) _context.Entry(existingOrder).CurrentValues.SetValues(transferOrder); // 删除原有明细 _context.TransferDetails.RemoveRange(existingOrder.TransferDetails); // 添加新的明细 foreach (var detail in transferOrder.TransferDetails) { // 注意:这里需要重置明细的Id(如果前端传递了Id,我们也要忽略,因为要重新生成) detail.Id = 0; // 如果数据库是自增主键,设置为0表示新增 existingOrder.TransferDetails.Add(detail); } await _context.SaveChangesAsync(); } // 删除调拨单(级联删除明细) public async Task DeleteTransferOrderAsync(int transferOrderId) { var order = await _context.TransferOrders.FindAsync(transferOrderId); if (order != null) { _context.TransferOrders.Remove(order); await _context.SaveChangesAsync(); } } // 根据ID获取调拨单(包含明细) public async Task<TransferOrder> GetTransferOrderByIdAsync(int id) { return await _context.TransferOrders .Include(o => o.TransferDetails) .FirstOrDefaultAsync(o => o.Id == id); } // 获取所有调拨单(不包含明细,通常列表不需要明细) public async Task<List<TransferOrder>> GetAllTransferOrdersAsync() { return await _context.TransferOrders.ToListAsync(); } } ``` 注意:在更新操作中,我们采用了先删除原有明细再新增的方式。这种方式简单,但可能会带来性能问题(如果明细很多),并且会丢失原有明细的ID(如果明细表有其他关联,需要谨慎)。另一种方式是逐个比较明细,进行更新、删除添加,这样更高效但更复杂。 优化更新操作(可选): 我们可以使用以下策略: 1. 遍历现有明细,如果新明细中不存在(根据ID判断),则删除。 2. 遍历新明细: - 如果明细有ID且存在于现有明细中,则更新。 - 如果明细没有ID(或ID为0),则添加。 3. 注意:如果前端传递的明细中没有包含所有原有明细,那么缺失的将被删除。 但是,为了简化,上面的例子使用了全删全增。在际应用中,如果明细数量不多,可以接受。 最后,在控制器中调用这些服务方法。 注意:在际项目中,你可能需要DTO(数据传输对象)来传递数据,避免循环引用等问题。这里为了简化,直接使用体类。 效率说明: - 使用异步操作提高吞吐量。 - 在查询时,使用Include来加载关联数据,避免延迟加载带来的多次查询(N+1问题)。 - 在更新时,全删全增可能不是最高效的,但代码简单。对于大量明细,建议使用更精细的更新策略。 相关问题: §§相关问题§§: 1. 在Entity Framework Core中,如何配置一对多的关系? 2. 在更新操作中,全删全增明细的方式有什么优缺点?有没有更好的方法? 3. 如何避免在序列化体类时出现循环引用问题? 4. 在删除主体时,如何现级联删除?在EF Core中如何配置?</think>### 库存调拨模块的C#现(1对多关系) 以下是针对WMS库存调拨模块的C#现,采用Entity Framework Core作为ORM框架,包含高效的增删改查操作,并遵循最佳践。 #### 1. 体类定义(主表+明细表) ```csharp // 调拨单主表 public class TransferOrder { public int Id { get; set; } public string OrderNumber { get; set; } = Guid.NewGuid().ToString("N").Substring(0, 10).ToUpper(); // 自动生成单号 public int FromWarehouseId { get; set; } public int ToWarehouseId { get; set; } public DateTime TransferDate { get; set; } = DateTime.UtcNow; public string Status { get; set; } = "Pending"; // 导航属性 (1对多) public ICollection<TransferItem> Items { get; set; } = new List<TransferItem>(); } // 调拨明细表 public class TransferItem { public int Id { get; set; } public int ProductId { get; set; } public int Quantity { get; set; } public string BatchNumber { get; set; } // 外键 public int TransferOrderId { get; set; } public TransferOrder TransferOrder { get; set; } } ``` #### 2. DbContext配置(启用级联删除) ```csharp public class AppDbContext : DbContext { public DbSet<TransferOrder> TransferOrders { get; set; } public DbSet<TransferItem> TransferItems { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置1对多关系 + 级联删除 modelBuilder.Entity<TransferOrder>() .HasMany(o => o.Items) .WithOne(i => i.TransferOrder) .HasForeignKey(i => i.TransferOrderId) .OnDelete(DeleteBehavior.Cascade); // 主单删除时自动删除明细 // 索引优化 modelBuilder.Entity<TransferOrder>() .HasIndex(o => o.OrderNumber) .IsUnique(); } } ``` #### 3. 储服务类(高效CRUD操作) ```csharp public class TransferService { private readonly AppDbContext _context; public TransferService(AppDbContext context) { _context = context; } // 新增调拨单(事务操作) public async Task<TransferOrder> CreateOrderAsync(TransferOrder order) { using var transaction = await _context.Database.BeginTransactionAsync(); try { _context.TransferOrders.Add(order); await _context.SaveChangesAsync(); await transaction.CommitAsync(); return order; } catch { await transaction.RollbackAsync(); throw; } } // 高效更新(避免全量查询) public async Task UpdateOrderAsync(int orderId, Action<TransferOrder> updateAction) { var order = await _context.TransferOrders .Include(o => o.Items) // 显式加载明细 .FirstOrDefaultAsync(o => o.Id == orderId); if (order == null) throw new KeyNotFoundException("调拨单不存在"); updateAction(order); // 外部传入更新逻辑 await _context.SaveChangesAsync(); } // 删除调拨单(级联删除明细) public async Task DeleteOrderAsync(int orderId) { // 高效删除:仅需主键 var order = new TransferOrder { Id = orderId }; _context.TransferOrders.Attach(order); _context.TransferOrders.Remove(order); await _context.SaveChangesAsync(); } // 查询优化:按需加载 public async Task<TransferOrder> GetOrderDetailsAsync(int orderId) { return await _context.TransferOrders .AsNoTracking() // 只读场景提升性能 .Include(o => o.Items) .FirstOrDefaultAsync(o => o.Id == orderId); } // 分页查询(主单列表) public async Task<(List<TransferOrder>, int)> GetOrdersPagedAsync(int page, int pageSize) { var query = _context.TransferOrders .AsNoTracking() .OrderByDescending(o => o.TransferDate); int totalCount = await query.CountAsync(); var orders = await query .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); return (orders, totalCount); } } ``` #### 4. 使用示例(控制器层) ```csharp [ApiController] [Route("api/transfers")] public class TransferController : ControllerBase { private readonly TransferService _service; public TransferController(TransferService service) { _service = service; } [HttpPost] public async Task<IActionResult> Create([FromBody] TransferOrder order) { var created = await _service.CreateOrderAsync(order); return CreatedAtAction(nameof(Get), new { id = created.Id }, created); } [HttpPut("{id}")] public async Task<IActionResult> Update(int id, [FromBody] TransferOrder input) { await _service.UpdateOrderAsync(id, order => { order.FromWarehouseId = input.FromWarehouseId; order.ToWarehouseId = input.ToWarehouseId; order.Status = input.Status; // 明细更新策略:清空后重新添加 order.Items.Clear(); foreach (var item in input.Items) { order.Items.Add(new TransferItem { ProductId = item.ProductId, Quantity = item.Quantity, BatchNumber = item.BatchNumber }); } }); return NoContent(); } } ``` ### 关键设计解释: 1. **高效更新策略**: - 使用`Attach`+状态变更代替全量查询(删除场景) - 通过`Action委托`现局部更新,避免过度查询 - 明细更新采用"清空+重建"模式,简化复杂变更逻辑 2. **性能优化**: - `AsNoTracking()`提升只读查询性能 - 分页查询使用`Skip/Take`而非获取全量数据 - 事务处理确保数据一致性 3. **级联删除**: - 通过`OnDelete(DeleteBehavior.Cascade)`配置 - 删除主单时自动删除所有关联明细 - 避免手动循环删除导致的低效操作 4. **并发控制**: - 使用事务保证原子操作(如创建操作) - 更新时采用"先查询后更新"模式处理业务规则 5. **索引优化**: - 为调拨单号(OrderNumber)添加唯一索引 - 按日期倒序排序提高最近单据查询速度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值