1
0
forked from erp-dev/erp

feat: running on docker fully, and added rabbitmq/worker container

This commit is contained in:
2025-11-25 15:56:13 +08:00
parent cd6a2370fc
commit d90917e764
34 changed files with 2474 additions and 74 deletions

486
docs/20251125-stock.md Normal file
View File

@@ -0,0 +1,486 @@
# Stock Management API Documentation
This document provides detailed documentation for the stock management module APIs, focusing on the three main stock change creation modes: Full, Relaxed, and Restricted.
## Table of Contents
1. [Overview](#overview)
2. [Stock Models](#stock-models)
3. [Stock Change Modes](#stock-change-modes)
4. [API Endpoints](#api-endpoints)
5. [Examples](#examples)
6. [Error Handling](#error-handling)
## Overview
The stock management system handles all inventory movement operations including stock-in (入库) and stock-out (出库) transactions. The system supports three different modes of operation:
1. **Full Mode**: Standard operation with explicit quantity lists
2. **Relaxed Mode**: Automatically splits quantities based on total and unit count
3. **Restricted Mode**: Strict "restricted-in/out" mode for warehouse operations
## Stock Models
### StockChangeRecord
Represents a stock change record (either stock-in or stock-out).
**Fields**:
- `id`: Record ID
- `type`: Change type (1=Stock-in, 2=Stock-out)
- `warehouse`: Associated warehouse
- `source_type`: Source of the change (1=Purchase, 6=Sales, etc.)
- `source_id`: ID of the source document (optional)
- `created_by`: User who created the record
- `is_finished`: Whether the stock change is completed
- `finished_at`: When the record was completed
- `remarks`: Additional notes
### StockChangeDetail
Represents detailed items within a stock change record.
**Fields**:
- `id`: Detail ID
- `product`: Associated product
- `stock_change_record`: Parent stock change record
- `quantity`: Quantity of the product
- `unit`: Unit of measurement
- `is_consumed`: Whether this detail has been consumed (for restricted mode)
- `consume_with`: Reference to the inbound detail being consumed (for restricted mode)
### Inventory
Represents current inventory levels for a product in a warehouse.
**Fields**:
- `id`: Inventory ID
- `product`: Associated product
- `warehouse`: Warehouse location
- `quantity`: Current quantity
- `num_of_rolls`: Number of rolls
- `spec`: Product specification
- `description`: Additional notes
## Stock Change Modes
### Source Types
The system supports various source types for stock changes:
**Incoming (入库) Sources**:
- `1`: Purchase (采购)
- `2`: Sales Return (销退)
- `3`: Transport In (调入)
- `4`: Recheck Addition (盘盈)
- `5`: Combine (合并)
**Outgoing (出库) Sources**:
- `6`: Sales (销售)
- `7`: Purchase Return (采购退货)
- `8`: Transport Out (调出)
- `9`: Recheck Removal (盘亏)
- `10`: Explode (拆卷)
### Warehouse Modes
Warehouses can operate in different modes:
- `1`: Restricted In (严进) - Strict control on stock-in
- `2`: Restricted In/Out (严进严出) - Strict control on both stock-in and stock-out
- `3`: Unrestricted (宽进宽出) - Relaxed controls for both directions
## API Endpoints
### 1. Full Mode Stock Change
**URL**: `POST /api/v1/stock-change/`
**Description**: Creates a stock change record with explicit quantity lists for each product.
**Request Parameters**:
```json
{
"type": 1, // 1=入库, 2=出库
"warehouse": 1, // 仓库ID
"source_type": 1, // 来源类型,见上文说明
"source_id": 123, // 可选来源单据ID
"products": [
{
"product": 1, // 产品ID
"quantity": [85.5, 75.2, 90.0] // 数量列表,每个值对应一条明细
},
{
"product": 2,
"quantity": [120.0]
}
]
}
```
**Response**:
```json
{
"stock_change_record": {
"id": 15,
"type": 1,
"warehouse": 1,
"source_type": 1,
"source_id": 123,
"is_finished": false,
"created_at": "2025-11-25T10:30:00Z",
"updated_at": "2025-11-25T10:30:00Z"
},
"details": [
{
"id": 45,
"product": 1,
"product_name": "纯棉印花布",
"quantity": 85.5,
"unit": 1,
"unit_display": "米",
"stock_change_record": 15,
"is_consumed": false,
"consume_with_id": null
},
{
"id": 46,
"product": 1,
"product_name": "纯棉印花布",
"quantity": 75.2,
"unit": 1,
"unit_display": "米",
"stock_change_record": 15,
"is_consumed": false,
"consume_with_id": null
}
// ... 更多明细
],
"message": "成功创建库存变动记录及 3 条明细",
"created_details_count": 3
}
```
### 2. Relaxed Mode Stock Change
**URL**: `POST /api/v1/stock-change/relaxed/`
**Description**: Creates a stock change record using total quantity and unit count to automatically split into details.
**Request Parameters**:
```json
{
"type": 1, // 1=入库, 2=出库
"warehouse": 1, // 仓库ID
"source_type": 1, // 来源类型
"source_id": 123, // 可选来源单据ID
"products": [
{
"product": 1, // 产品ID
"quantity": {
"value": 250.8, // 总数量
"unit_count": 2.5 // 单条数量默认为1
}
}
]
}
```
**Response**:
```json
{
"stock_change_record": {
"id": 16,
"type": 1,
"warehouse": 1,
"source_type": 1,
"source_id": 123,
"is_finished": false,
"created_at": "2025-11-25T11:00:00Z",
"updated_at": "2025-11-25T11:00:00Z"
},
"details": [
{
"id": 48,
"product": 1,
"product_name": "纯棉印花布",
"quantity": 100.0, // 自动拆分
"unit": 1,
"unit_display": "米",
"stock_change_record": 16,
"is_consumed": false,
"consume_with_id": null
},
{
"id": 49,
"product": 1,
"product_name": "纯棉印花布",
"quantity": 100.0, // 自动拆分
"unit": 1,
"unit_display": "米",
"stock_change_record": 16,
"is_consumed": false,
"consume_with_id": null
},
{
"id": 50,
"product": 1,
"product_name": "纯棉印花布",
"quantity": 50.8, // 剩余数量
"unit": 1,
"unit_display": "米",
"stock_change_record": 16,
"is_consumed": false,
"consume_with_id": null
}
],
"message": "成功创建库存变动记录及 3 条明细",
"created_details_count": 3
}
```
### 3. Restricted Mode Stock Change
**URL**: `POST /api/v1/stock-change/restrict/`
**Description**: Creates a stock change record in "restricted-in-out" mode where outbound details must reference existing inbound details.
**Request Parameters**:
```json
{
"type": 2, // 1=入库, 2=出库
"warehouse": 1, // 仓库ID必须为严进严出模式
"source_type": 6, // 来源类型
"source_id": 123, // 可选来源单据ID
"products": [
{
"product": 1, // 产品ID
"quantity": [150.5], // 数量列表
"consume_with": [23, 24] // 必须指定消耗的入库明细ID列表
}
]
}
```
**Response**:
```json
{
"stock_change_record": {
"id": 17,
"type": 2,
"warehouse": 1,
"source_type": 6,
"source_id": 123,
"is_finished": false,
"created_at": "2025-11-25T11:15:00Z",
"updated_at": "2025-11-25T11:15:00Z"
},
"details": [
{
"id": 51,
"product": 1,
"product_name": "纯棉印花布",
"quantity": 75.25,
"unit": 1,
"unit_display": "米",
"stock_change_record": 17,
"is_consumed": false,
"consume_with_id": 23 // 消耗的入库明细ID
},
{
"id": 52,
"product": 1,
"product_name": "纯棉印花布",
"quantity": 75.25,
"unit": 1,
"unit_display": "米",
"stock_change_record": 17,
"is_consumed": false,
"consume_with_id": 24 // 消耗的入库明细ID
}
],
"message": "成功创建库存变动记录及 2 条明细",
"created_details_count": 2
}
```
### Other Related Endpoints
#### List Stock Changes
**URL**: `GET /api/v1/stock-changes/`
**Response**: List of stock change records with pagination
```json
{
"count": 100,
"next": "http://example.com/api/v1/stock-changes/?page=2",
"previous": null,
"results": [
{
"id": 15,
"type": 1,
"type_display": "入库",
"warehouse": 1,
"source_type": 1,
"source_type_display": "采购",
"is_finished": true,
"finished_at": "2025-11-25T12:00:00Z",
"created_at": "2025-11-25T10:30:00Z"
}
]
}
```
#### Get Stock Change Details
**URL**: `GET /api/v1/stock-change/<id>/`
**Response**: Detailed view of a specific stock change record with its details
```json
{
"stock_change_record": {
"id": 15,
"type": 1,
"type_display": "入库",
"warehouse": 1,
"source_type": 1,
"source_type_display": "采购",
"is_finished": true,
"finished_at": "2025-11-25T12:00:00Z",
"created_at": "2025-11-25T10:30:00Z"
},
"details": [
{
"id": 45,
"product": 1,
"product_name": "纯棉印花布",
"quantity": 85.5,
"unit": 1,
"unit_display": "米",
"is_consumed": false,
"consume_with_id": null
}
]
}
```
## Examples
### Example 1: Creating a Stock-in Record (Full Mode)
```bash
curl -X POST http://example.com/api/v1/stock-change/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_token" \
-d '{
"type": 1,
"warehouse": 1,
"source_type": 1,
"products": [
{
"product": 1,
"quantity": [100.5, 75.3, 120.0]
}
]
}'
```
### Example 2: Creating a Stock-out Record (Relaxed Mode)
```bash
curl -X POST http://example.com/api/v1/stock-change/relaxed/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_token" \
-d '{
"type": 2,
"warehouse": 1,
"source_type": 6,
"products": [
{
"product": 2,
"quantity": {
"value": 250.8,
"unit_count": 2.5
}
}
]
}'
```
### Example 3: Creating a Stock-out Record (Restricted Mode)
```bash
curl -X POST http://example.com/api/v1/stock-change/restrict/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_token" \
-d '{
"type": 2,
"warehouse": 1,
"source_type": 6,
"products": [
{
"product": 3,
"quantity": [100.0, 50.0],
"consume_with": [45, 46]
}
]
}'
```
## Error Handling
### Common Error Responses
#### Authentication Error
```json
{
"error": "无权限访问"
}
```
#### Missing Required Parameters
```json
{
"error": "缺少必要参数",
"message": "请提供 type, warehouse, source_type"
}
```
#### Validation Error
```json
{
"error": "产品数据验证失败",
"details": {
"products": [
"产品列表中存在重复的产品ID"
]
}
}
```
#### Warehouse/Product Visibility Error
```json
{
"error": "仓库ID 1 对当前用户不可见"
}
```
#### Business Logic Error
```json
{
"error": "仓库出入库模式为【严进严出】,该模式暂未支持当前操作"
}
```
## Business Rules
1. **Warehouse Mode Restrictions**:
- Full mode works with all warehouse modes
- Relaxed mode requires "unrestricted" (宽进宽出) or "restricted-in" (严进) warehouses
- Restricted mode requires "restricted-in-out" (严进严出) warehouses
2. **Stock Change Validation**:
- Stock-in can only use incoming source types (1, 2, 3, 4, 5)
- Stock-out can only use outgoing source types (6, 7, 8, 9, 10)
3. **Restricted Mode Specific Rules**:
- Outbound details must reference existing inbound details
- Referenced details must belong to the same warehouse
- Referenced details must not already be consumed
4. **Permission Checks**:
- Users can only access warehouses they have permission to see
- Users can only access products they have permission to see
- All stock change operations require proper authentication

View File

@@ -0,0 +1,60 @@
# StockFlowService 统一出入库服务
## 背景
随着仓库出入库模式(严谨、宽进宽出、严进严出)增多,业务层(采购、销售等)不应关心底层明细拆分及校验差异。`StockFlowService` 提供统一的 `stock_in` / `stock_out` 方法,将模式分支、字段校验与服务层逻辑封装,保证所有模式均复用已有 `create_stock_change_record_*` 逻辑。
## Items Payload 结构
每个产品条目都支持以下可选字段,由服务内部根据仓库模式挑选所需字段:
| 字段 | 说明 | 适用模式 |
| --- | --- | --- |
| `product_id` | 产品 ID必填 | 所有模式 |
| `quantities` | 严谨模式的数量列表 | 严谨、严进严出入库、严进严出出库(入库) |
| `value` | 总数量 | 宽进宽出 |
| `num_of_rolls` | 单条长度/匹数,默认 1 | 宽进宽出 |
| `consume_detail_ids` | 被消耗的入库明细 ID 列表 | 严进严出出库 |
`StockFlowService` 根据 `warehouse.mode` 自动构造对应的服务入参:严谨模式使用 `quantity` 数组,宽进宽出转换为 `{value, unit_count}`,严进严出出库转换为 `consume_with`
## 使用示例
```python
service = StockFlowService(merchant=merchant, created_by=user)
# 严谨入库
service.stock_in(
warehouse_id=warehouse.id,
source_type=StockChangeSourceEnum.PURCHASE,
source_id=purchase.id,
items=[{'product_id': product.id, 'quantities': ['10.5', '5']}],
)
# 宽进宽出出库
service.stock_out(
warehouse_id=unrestricted.id,
source_type=StockChangeSourceEnum.SALES,
source_id=sales.id,
items=[{'product_id': product.id, 'value': '30.5', 'num_of_rolls': 3}],
)
# 严进严出出库
service.stock_out(
warehouse_id=restrict_out.id,
source_type=StockChangeSourceEnum.SALES,
source_id=sales.id,
items=[{'product_id': product.id, 'consume_detail_ids': [detail.id]}],
)
```
## 方案评价
该构想成功实现了以下目标:
- **隐藏模式细节**:业务层仅需关心仓库与产品输入,内部自动匹配严谨/宽进宽出/严进严出逻辑。
- **避免重复实现**:底层仍调用现有 `create_stock_change_record_with_details``create_stock_change_record_relaxed` 等函数,最大化复用。
- **可拓展性**:未来新增模式或派生参数,只需扩展 `StockFlowService``items` 解析与私有方法,无需触及业务层。
整体来看,该方案清晰地分离了“业务调用入口”和“模式细节实现”,有助于后续在采购、销售、生产等更高抽象的流程中快速复用库存操作。***