diff --git a/hb_client/package.json b/hb_client/package.json
index 0a17f55..f39a479 100644
--- a/hb_client/package.json
+++ b/hb_client/package.json
@@ -22,6 +22,7 @@
"compression-webpack-plugin": "^5.0.1",
"d3": "^5.14.2",
"dagre-d3": "^0.6.4",
+ "dhtmlx-gantt": "^6.2.1",
"element-ui": "^2.15.5",
"file-saver": "^2.0.2",
"fuse.js": "^6.4.6",
@@ -30,6 +31,7 @@
"nprogress": "0.2.0",
"path-to-regexp": "^6.2.0",
"vue": "^2.6.14",
+ "vue-function-api": "^2.1.2",
"vue-json-editor": "^1.4.3",
"vue-quill-editor": "^3.0.6",
"vue-router": "^3.5.2",
diff --git a/hb_client/src/router/index.js b/hb_client/src/router/index.js
index 3385a19..280e167 100644
--- a/hb_client/src/router/index.js
+++ b/hb_client/src/router/index.js
@@ -227,7 +227,7 @@ export const asyncRoutes = [
path: 'need',
name: 'need',
component: () => import('@/views/wpm/need'),
- meta: { title: '半成品检验', icon: 'example', perms: ['index_manage'] }
+ meta: { title: '过程检验', icon: 'example', perms: ['index_manage'] }
}
,
{
@@ -320,15 +320,15 @@ export const asyncRoutes = [
{
path: '/qm',
component: Layout,
- redirect: '/qm/standard',
+ redirect: '/qm/product',
name: 'qm',
meta: { title: '质量管理', icon: 'example', perms: ['equipment_set'] },
children: [
{
- path: 'standard',
- name: 'standard',
- component: () => import('@/views/qm/standard'),
- meta: { title: '标准', icon: 'example', perms: ['index_manage'] }
+ path: 'product',
+ name: 'product',
+ component: () => import('@/views/qm/product'),
+ meta: { title: '军检', icon: 'example', perms: ['index_manage'] }
},
{
path: 'testitem',
diff --git a/hb_client/src/views/inm/product.vue b/hb_client/src/views/inm/product.vue
index dc4d61f..e5dddef 100644
--- a/hb_client/src/views/inm/product.vue
+++ b/hb_client/src/views/inm/product.vue
@@ -45,21 +45,7 @@
合格
-
-
-
- 军检
-
-
-
+
-
-
-
-
- 合格
- 不合格
-
-
-
-
-
-
-
- 取消
- 确认
-
-
@@ -136,6 +95,7 @@ export default {
//半成品列表
getList() {
this.listLoading = true;
+ this.listQuery.material__type=1;
getiproductList(this.listQuery).then((response) => {
if (response.data) {
this.iproductData= response.data;
@@ -147,18 +107,7 @@ export default {
this.saleproduct=scope.row.id;
this.dialogVisible=true;
},
- smtconfirm(){
- saleMtest(this.saleproduct,this.mtest).then((res) => {
-
- if (res.code >= 200) {
- this.getList();
- this.dialogVisible = false;
- this.$message.success("成功");
- }
-
- });
- }
},
};
diff --git a/hb_client/src/views/inm/wproduct.vue b/hb_client/src/views/inm/wproduct.vue
index d4ff5cf..bb46bf3 100644
--- a/hb_client/src/views/inm/wproduct.vue
+++ b/hb_client/src/views/inm/wproduct.vue
@@ -76,6 +76,7 @@ export default {
//半成品列表
getList() {
this.listLoading = true;
+ this.listQuery.material__type=2;
getiproductList(this.listQuery).then((response) => {
if (response.data) {
this.iproductData= response.data;
diff --git a/hb_client/src/views/qm/product.vue b/hb_client/src/views/qm/product.vue
new file mode 100644
index 0000000..ac58135
--- /dev/null
+++ b/hb_client/src/views/qm/product.vue
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+ {{ scope.row.number }}
+
+
+
+ {{ scope.row.batch }}
+
+
+ {{ scope.row.material_.name }}
+
+
+
+ {{ scope.row.warehouse_.name }}
+
+
+
+
+
+ 未军检
+ 已军检
+
+
+
+
+
+
+
+ 不合格
+ 合格
+
+
+
+
+
+ 军检
+
+
+
+
+
+
+
+
+
+ 合格
+ 不合格
+
+
+
+
+
+
+
+
+ 取消
+ 确认
+
+
+
+
+
+
+
diff --git a/hb_client/src/views/sam/sales.vue b/hb_client/src/views/sam/sales.vue
index 75a1ebc..a6f53ce 100644
--- a/hb_client/src/views/sam/sales.vue
+++ b/hb_client/src/views/sam/sales.vue
@@ -143,10 +143,48 @@
-
-
-
+
+
+
+
+
+
+ {{ scope.row.number }}
+
+
+ {{ scope.row.batch }}
+
+
+ {{ scope.row.material_.name }}
+
+
+
+ {{ scope.row.warehouse_.name }}
+
+
+
+
+
+ 未军检
+ 已军检
+
+
+
+
+
+
+
@@ -183,10 +221,11 @@ export default {
page: 1,
page_size: 20,
},
+ iproducts:[],
orderoptions:[],
customeroptions:[],
materialoptions:[],
- iproductoptions:[],
+ iproductoptions:"",
dialogVisible: false,
dialogType: "new",
@@ -221,19 +260,12 @@ export default {
if (response.data) {
this.sale.customer = response.data.customer;
this.sale.product = response.data.product;
- this.iproductoptions=[];
- getiproductList({material: this.sale.product,page:0}).then((response) => {
+ getiproductList({page:0,material__type:1,material:this.sale.product}).then((response) => {
if (response.data) {
- response.data.forEach((item) => {
- this.iproductoptions.push({
- label: item.number+"__"+item.material_.name+"__"+(item.is_mtested==true?"已军检":"未军检") ,
- key: item.id
- })
- });
+ this.iproductoptions=response.data;
}
-
+
});
-
}
@@ -270,39 +302,27 @@ export default {
});
},
- selectproduct(selval)
- {
- this.iproductoptions=[];
- getiproductList({material:selval,page:0}).then((response) => {
- if (response.data) {
- response.data.forEach((item) => {
- this.iproductoptions.push({
- label: item.number+"__"+item.material_.name+"__"+(item.is_mtested==true?"已军检":"未军检") ,
- key: item.id
- })
- });
- }
-
- });
-
- },
+
//成品
getproductList() {
- this.iproductoptions=[],
- getiproductList({page:0}).then((response) => {
+
+ getiproductList({page:0,material__type:1}).then((response) => {
if (response.data) {
- response.data.forEach((item) => {
- this.iproductoptions.push({
- label: item.number+"__"+item.material_.name+"__"+(item.is_mtested==true?"已军检":"未军检") ,
- key: item.id
- })
- });
+ this.iproductoptions=response.data;
}
});
},
-
+//根据选择的产品弹出对应库里的产品
+selectproduct(){
+ getiproductList({page:0,material__type:1,material:this.sale.product}).then((response) => {
+ if (response.data) {
+ this.iproductoptions=response.data;
+ }
+
+ });
+},
handleFilter() {
@@ -320,7 +340,7 @@ export default {
this.sale = Object.assign({}, defaulteSale);
this.dialogType = "new";
this.dialogVisible = true;
- this.iproductoptions=[];
+ this.iproducts=[];
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
@@ -364,6 +384,17 @@ export default {
}
});
} else {
+
+ this.$refs.multipleTable.selection.forEach((item) => {
+ this.iproducts.push(
+ item.id
+ );
+
+ });
+
+
+
+ this.sale.iproducts=this.iproducts;
createSale(this.sale).then((res) => {
if (res.code >= 200) {
this.getList();
diff --git a/hb_server/apps/inm/filters.py b/hb_server/apps/inm/filters.py
index 2ff12aa..09f8777 100644
--- a/hb_server/apps/inm/filters.py
+++ b/hb_server/apps/inm/filters.py
@@ -14,4 +14,4 @@ class IProductFilterSet(filters.FilterSet):
order = filters.NumberFilter(field_name="wproduct__subproduction_plan__production_plan__order")
class Meta:
model = IProduct
- fields = ['material', 'warehouse', 'batch', 'order', 'is_mtested', 'is_mtestok']
\ No newline at end of file
+ fields = ['material', 'warehouse', 'batch', 'order', 'is_mtested', 'is_mtestok', 'material__type']
\ No newline at end of file
diff --git a/hb_server/apps/inm/models.py b/hb_server/apps/inm/models.py
index 10248cd..e1e9f1a 100644
--- a/hb_server/apps/inm/models.py
+++ b/hb_server/apps/inm/models.py
@@ -75,8 +75,8 @@ class FIFOItem(BaseModel):
"""
出入库详细条目
"""
- is_tested = models.BooleanField('是否已检测', default=False)
- is_testok = models.BooleanField('是否检测合格', default=False)
+ is_tested = models.BooleanField('是否已检验', default=False)
+ is_testok = models.BooleanField('是否检验合格', default=False)
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='仓库')
material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE)
count = models.PositiveIntegerField('数量', default=0)
diff --git a/hb_server/apps/inm/views.py b/hb_server/apps/inm/views.py
index 5ef42dc..0956d4f 100644
--- a/hb_server/apps/inm/views.py
+++ b/hb_server/apps/inm/views.py
@@ -39,7 +39,7 @@ class InventoryViewSet(ListModelMixin, GenericViewSet):
仓库物料表
"""
perms_map = {'*': '*'}
- queryset = Inventory.objects.select_related('material', 'warehouse').all()
+ queryset = Inventory.objects.select_related('material', 'warehouse').filter(count__gt=0).all()
serializer_class = InventorySerializer
filterset_fields = ['material', 'warehouse']
search_fields = []
@@ -48,7 +48,7 @@ class InventoryViewSet(ListModelMixin, GenericViewSet):
class MaterialBatchViewSet(ListModelMixin, GenericViewSet):
perms_map = {'*': '*'}
- queryset = MaterialBatch.objects.select_related('material', 'warehouse').all()
+ queryset = MaterialBatch.objects.select_related('material', 'warehouse').filter(count__gt=0).all()
serializer_class = MaterialBatchSerializer
# filterset_fields = ['material', 'warehouse']
filterset_class = MbFilterSet
@@ -87,14 +87,14 @@ class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=InmTestRecordCreateSerializer)
def test(self, request, pk=None):
"""
- 检测
+ 检验
"""
serializer = InmTestRecordCreateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
record_data = vdata.pop('record_data')
if 'is_testok' not in vdata:
- raise APIException('未填写检测结论')
+ raise APIException('未填写检验结论')
with transaction.atomic():
obj = serializer.save(create_by = self.request.user)
tris = []
@@ -111,7 +111,7 @@ class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
tris.append(TestRecordItem(**m))
TestRecordItem.objects.bulk_create(tris)
- # 如果检测合格
+ # 如果检验合格
if obj.fifo_item:
obj.fifo_item.is_testok = True if obj.is_testok else False
obj.fifo_item.is_tested = True
diff --git a/hb_server/apps/mtm/migrations/0042_alter_recordformfield_field_type.py b/hb_server/apps/mtm/migrations/0042_alter_recordformfield_field_type.py
new file mode 100644
index 0000000..029a7f1
--- /dev/null
+++ b/hb_server/apps/mtm/migrations/0042_alter_recordformfield_field_type.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.9 on 2021-12-14 07:04
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('mtm', '0041_alter_material_type'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='recordformfield',
+ name='field_type',
+ field=models.CharField(choices=[('string', '字符串'), ('int', '整型'), ('float', '浮点'), ('boolean', '布尔'), ('date', '日期'), ('time', '时间'), ('datetime', '日期时间'), ('radio', '单选'), ('checkbox', '多选'), ('select', '单选下拉'), ('selects', '多选下拉'), ('textarea', '文本域'), ('table', '表格'), ('draw', '绘图')], max_length=50, verbose_name='类型'),
+ ),
+ ]
diff --git a/hb_server/apps/mtm/models.py b/hb_server/apps/mtm/models.py
index d4b20d2..7815711 100644
--- a/hb_server/apps/mtm/models.py
+++ b/hb_server/apps/mtm/models.py
@@ -143,6 +143,7 @@ class RecordFormField(CommonAModel):
FIELD_SELECTS = 'selects'
FIELD_TEXTAREA = 'textarea'
FIELD_DRAW = 'draw'
+ FIELD_TABLE = 'table'
FIELD_FROMSYSTEM = 'fromsystem'
field_type_choices = (
('string', '字符串'),
@@ -157,6 +158,7 @@ class RecordFormField(CommonAModel):
('select', '单选下拉'),
('selects', '多选下拉'),
('textarea', '文本域'),
+ ('table', '表格'),
('draw', '绘图'),
)
high_rule_choices = (
diff --git a/hb_server/apps/pm/migrations/0017_auto_20211213_1401.py b/hb_server/apps/pm/migrations/0017_auto_20211213_1401.py
new file mode 100644
index 0000000..9fa357e
--- /dev/null
+++ b/hb_server/apps/pm/migrations/0017_auto_20211213_1401.py
@@ -0,0 +1,49 @@
+# Generated by Django 3.2.9 on 2021-12-13 06:01
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('pm', '0016_auto_20211209_1638'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='productionplan',
+ name='count',
+ field=models.PositiveIntegerField(default=1, verbose_name='生产数量'),
+ ),
+ migrations.AlterField(
+ model_name='productionplan',
+ name='count_ok',
+ field=models.PositiveIntegerField(default=0, verbose_name='合格数'),
+ ),
+ migrations.AlterField(
+ model_name='productionplan',
+ name='count_real',
+ field=models.PositiveIntegerField(default=0, verbose_name='实际产出数'),
+ ),
+ migrations.AlterField(
+ model_name='subproductionplan',
+ name='main_count',
+ field=models.PositiveIntegerField(default=0, verbose_name='应产出数'),
+ ),
+ migrations.AlterField(
+ model_name='subproductionplan',
+ name='main_count_ok',
+ field=models.PositiveIntegerField(default=0, verbose_name='合格数'),
+ ),
+ migrations.AlterField(
+ model_name='subproductionplan',
+ name='main_count_real',
+ field=models.PositiveIntegerField(default=0, verbose_name='实际产出数'),
+ ),
+ migrations.AlterField(
+ model_name='subproductionplan',
+ name='production_plan',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subplan_plan', to='pm.productionplan', verbose_name='关联主生产计划'),
+ ),
+ ]
diff --git a/hb_server/apps/pm/models.py b/hb_server/apps/pm/models.py
index 6a06c29..7588491 100644
--- a/hb_server/apps/pm/models.py
+++ b/hb_server/apps/pm/models.py
@@ -14,12 +14,19 @@ class ProductionPlan(CommonAModel):
"""
生产计划
"""
+ # PLAN_STATE_WAIT = 6
+ # PLAN_STATE_WORKING = 10
+ # PLAN_STATE_DONE = 20
+ # state_choices = (
+ # (PLAN_STATE_WORKING, '进行中'),
+ # (PLAN_STATE_DONE, '已完成')
+ # )
number = models.CharField('编号', max_length=50, unique=True)
order = models.ForeignKey(Order, verbose_name='关联订单', null=True, blank=True, on_delete=models.SET_NULL)
product = models.ForeignKey(Material, verbose_name='生产产品', on_delete=models.CASCADE)
- count = models.IntegerField('生产数量', default=1)
- count_real = models.IntegerField('实际产出数', default=0)
- count_ok = models.IntegerField('合格数', default=0)
+ count = models.PositiveIntegerField('生产数量', default=1)
+ count_real = models.PositiveIntegerField('实际产出数', default=0)
+ count_ok = models.PositiveIntegerField('合格数', default=0)
start_date = models.DateField('计划开工日期')
end_date = models.DateField('计划完工日期')
is_planed = models.BooleanField('是否已排产', default=False)
@@ -47,7 +54,7 @@ class SubProductionPlan(CommonAModel):
(SUBPLAN_STATE_DONE, '已完成')
)
number = models.CharField('子计划编号', max_length=50, unique=True, null=True, blank=True)
- production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE)
+ production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE, related_name='subplan_plan')
subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE, related_name='subplan_subprod')
start_date = models.DateField('计划开工日期')
end_date = models.DateField('计划完工日期')
@@ -56,9 +63,9 @@ class SubProductionPlan(CommonAModel):
process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE)
main_product = models.ForeignKey(Material, verbose_name='主要产品', on_delete=models.CASCADE, null=True, blank=True)
- main_count = models.IntegerField('应产出数', default=0)
- main_count_real = models.IntegerField('实际产出数', default=0)
- main_count_ok = models.IntegerField('合格数', default=0)
+ main_count = models.PositiveIntegerField('应产出数', default=0)
+ main_count_real = models.PositiveIntegerField('实际产出数', default=0)
+ main_count_ok = models.PositiveIntegerField('合格数', default=0)
steps = models.JSONField('工艺步骤', default=list)
diff --git a/hb_server/apps/pm/signals.py b/hb_server/apps/pm/signals.py
index e5cc595..c4871ee 100644
--- a/hb_server/apps/pm/signals.py
+++ b/hb_server/apps/pm/signals.py
@@ -23,8 +23,8 @@ def update_subplan_main(sender, instance, created, **kwargs):
if subplan.main_product.type == Material.MA_TYPE_GOOD:
# 如果是产品,更新主计划进度
plan = subplan.production_plan
- plan.count_real = subplan.count_real
- plan.count_ok = subplan.count_ok
+ plan.count_real = subplan.main_count_real
+ plan.count_ok = subplan.main_count_ok
plan.save()
diff --git a/hb_server/apps/qm/migrations/0017_alter_testrecord_wproduct.py b/hb_server/apps/qm/migrations/0017_alter_testrecord_wproduct.py
new file mode 100644
index 0000000..1c70b53
--- /dev/null
+++ b/hb_server/apps/qm/migrations/0017_alter_testrecord_wproduct.py
@@ -0,0 +1,20 @@
+# Generated by Django 3.2.9 on 2021-12-13 06:01
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('wpm', '0031_auto_20211209_1638'),
+ ('qm', '0016_auto_20211210_1338'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='testrecord',
+ name='wproduct',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='test_wproduct', to='wpm.wproduct', verbose_name='关联的动态产品'),
+ ),
+ ]
diff --git a/hb_server/apps/qm/migrations/0018_testrecord_is_submited.py b/hb_server/apps/qm/migrations/0018_testrecord_is_submited.py
new file mode 100644
index 0000000..89c230b
--- /dev/null
+++ b/hb_server/apps/qm/migrations/0018_testrecord_is_submited.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.9 on 2021-12-14 04:45
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('qm', '0017_alter_testrecord_wproduct'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='testrecord',
+ name='is_submited',
+ field=models.BooleanField(default=False, verbose_name='是否提交'),
+ ),
+ ]
diff --git a/hb_server/apps/qm/migrations/0019_auto_20211214_1504.py b/hb_server/apps/qm/migrations/0019_auto_20211214_1504.py
new file mode 100644
index 0000000..5be96de
--- /dev/null
+++ b/hb_server/apps/qm/migrations/0019_auto_20211214_1504.py
@@ -0,0 +1,26 @@
+# Generated by Django 3.2.9 on 2021-12-14 07:04
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('qm', '0018_testrecord_is_submited'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='testrecorditem',
+ name='need_judge',
+ ),
+ migrations.RemoveField(
+ model_name='testrecorditem',
+ name='sort',
+ ),
+ migrations.AddField(
+ model_name='testrecorditem',
+ name='is_hidden',
+ field=models.BooleanField(default=False, verbose_name='是否隐藏'),
+ ),
+ ]
diff --git a/hb_server/apps/qm/models.py b/hb_server/apps/qm/models.py
index 735c34e..ed7e582 100644
--- a/hb_server/apps/qm/models.py
+++ b/hb_server/apps/qm/models.py
@@ -1,7 +1,7 @@
from django.db import models
from django.db.models.enums import Choices
from apps.mtm.models import RecordForm, RecordFormField
-from apps.system.models import CommonAModel, File
+from apps.system.models import CommonADModel, CommonAModel, File
from utils.model import BaseModel
# Create your models here.
class Standard(CommonAModel):
@@ -43,7 +43,7 @@ class AnalysisItem(CommonAModel):
class Meta:
verbose_name = '检验分析项'
-class TestRecord(CommonAModel):
+class TestRecord(CommonADModel):
"""
检验记录
"""
@@ -70,6 +70,7 @@ class TestRecord(CommonAModel):
subproduction_plan = models.ForeignKey('pm.subproductionplan', verbose_name='关联的生产子计划', on_delete=models.CASCADE, null=True, blank=True)
fifo_item = models.ForeignKey('inm.fifoitem', verbose_name='关联的出入库批次', on_delete=models.CASCADE, null=True, blank=True)
origin_test = models.ForeignKey('self', verbose_name='原检验记录', on_delete=models.CASCADE, null=True, blank=True)
+ is_submited = models.BooleanField('是否提交', default=False)
remark = models.TextField('备注', default='')
@@ -77,13 +78,12 @@ class TestRecordItem(BaseModel):
"""
记录表格字段值
"""
- form_field = models.ForeignKey(RecordFormField, verbose_name='关联字段', on_delete=models.CASCADE, db_constraint=False)
+ form_field = models.ForeignKey(RecordFormField, verbose_name='关联自定义表格字段', on_delete=models.CASCADE, db_constraint=False)
field_name = models.CharField('字段名', max_length=50)
field_key = models.CharField('字段标识', max_length=50)
field_type = models.CharField('字段类型', choices=RecordForm.type_choices, max_length=50)
field_value = models.JSONField('录入值', default=dict, blank=True)
- need_judge = models.BooleanField('是否需要判定', default=False)
- sort = models.IntegerField('排序号', default=1)
+ is_hidden = models.BooleanField('是否隐藏', default=False)
is_testok = models.BooleanField('是否合格', null=True, blank=True)
is_testok_robot = models.BooleanField('自动判定的是否合格', null=True, blank=True)
- test_record = models.ForeignKey(TestRecord, verbose_name='关联的检测记录', on_delete=models.CASCADE, related_name='item_test_record')
\ No newline at end of file
+ test_record = models.ForeignKey(TestRecord, verbose_name='关联的检验记录', on_delete=models.CASCADE, related_name='item_test_record')
\ No newline at end of file
diff --git a/hb_server/apps/qm/serializers.py b/hb_server/apps/qm/serializers.py
index 2ff328b..f329d3a 100644
--- a/hb_server/apps/qm/serializers.py
+++ b/hb_server/apps/qm/serializers.py
@@ -1,8 +1,9 @@
from rest_framework import serializers
from apps.mtm.models import RecordForm, RecordFormField
from apps.mtm.serializers import RecordFormFieldSerializer, RecordFormSimpleSerializer
-from apps.system.serializers import FileSimpleSerializer
+from apps.system.serializers import FileSimpleSerializer, UserSimpleSerializer
from .models import Standard, TestItem, TestRecord, TestRecordItem
+from django.db import transaction
class StandardCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
@@ -38,9 +39,21 @@ class AnalysisItemSerializer(serializers.ModelSerializer):
class TestRecordItemCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TestRecordItem
- fields = ['form_field', 'field_value']
+ fields = ['form_field', 'field_value', 'is_testok']
+
+class TestRecordItemUpdateSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = TestRecordItem
+ fields = ['id', 'field_value', 'is_testok', 'is_hidden']
+
class TestRecordItemSerializer(serializers.ModelSerializer):
+ need_judge = serializers.BooleanField(source='form_field.need_judge', read_only=True)
+ rule_expression = serializers.JSONField(source='form_field.rule_expression', read_only=True)
+ display_expression = serializers.JSONField(source='form_field.display_expression', read_only=True)
+ is_hidden = serializers.BooleanField(source='form_field.is_hidden', read_only=True)
+ help_text = serializers.CharField(source='form_field.help_text', read_only=True)
+ sort = serializers.IntegerField(source='form_field.sort', read_only=True)
class Meta:
model = TestRecordItem
fields = '__all__'
@@ -52,28 +65,67 @@ class TestRecordCreateSerializer(serializers.ModelSerializer):
fields = ['form', 'record_data', 'is_testok', 'fifo_item']
class TestRecordListSerializer(serializers.ModelSerializer):
+ form_ = RecordFormSimpleSerializer(source='form', read_only=True)
+ create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
class Meta:
model = TestRecord
fields = '__all__'
-
-class TestRecordDetailSerializer(serializers.ModelSerializer):
+class TestRecordDetailBaseSerializer(serializers.ModelSerializer):
form_ = RecordFormSimpleSerializer(source='form', read_only=True)
- record_data_ = TestRecordItemSerializer(source='item_test_record', read_only=True, many=True)
+ record_data = serializers.SerializerMethodField()
+ create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
class Meta:
model = TestRecord
fields = '__all__'
- @staticmethod
- def setup_eager_loading(queryset):
- queryset = queryset.select_related('form','fifo_item')
- return queryset
+ def get_record_data(self, obj):
+ return TestRecordItemSerializer(instance=obj.item_test_record.order_by('form_field__sort'), many=True).data
- # def get_record_data_(self, obj):
- # record_data = obj.record_data
- # all_fields = RecordFormField.objects.filter(form=obj.form, is_deletd=False).order_by('sort')
- # all_fields_l = RecordFormFieldSerializer(instance=all_fields, many=True).data
- # for i in all_fields_l:
- # key = i['field_key']
- # i['field_value'] = record_data.get(key, None)
- # return all_fields_l
\ No newline at end of file
+class TestRecordDetailSerializer(serializers.ModelSerializer):
+ form_ = RecordFormSimpleSerializer(source='form', read_only=True)
+ # record_data = TestRecordItemSerializer(source='item_test_record', read_only=True, many=True)
+ record_data = serializers.SerializerMethodField()
+ origin_test_ = TestRecordDetailBaseSerializer(source='origin_test', read_only=True)
+ class Meta:
+ model = TestRecord
+ fields = '__all__'
+
+ def get_record_data(self, obj):
+ return TestRecordItemSerializer(instance=obj.item_test_record.order_by('form_field__sort'), many=True).data
+
+ def to_representation(self, instance):
+ ret = super().to_representation(instance)
+ if instance.origin_test and instance.type == TestRecord.TEST_PROCESS_RE:
+ origin_test = ret['origin_test']
+ o_dict = {}
+ for i in origin_test['record_data']:
+ o_dict[i['field_key']] = i['field_value']
+ for i in ret['record_data']:
+ i['origin_value'] = o_dict[i['field_key']] if i['field_key'] in o_dict else None
+ return super().to_representation(instance)
+
+class TestRecordItemUpdatexSerializer(serializers.Serializer):
+ id = serializers.PrimaryKeyRelatedField(queryset=TestRecordItem.objects.all())
+ field_value = serializers.JSONField(allow_null=True, required=False)
+ is_testok = serializers.BooleanField(allow_null=True, required=False)
+ is_hidden = serializers.BooleanField(default=False)
+
+class TestRecordUpdateSerializer(serializers.ModelSerializer):
+ record_data = TestRecordItemUpdatexSerializer(many=True, write_only=True)
+ class Meta:
+ model = TestRecord
+ fields = ['is_testok', 'record_data']
+
+ def update(self, instance, validated_data):
+ record_data = validated_data.pop('record_data')
+ for attr, value in validated_data.items():
+ setattr(instance, attr, value)
+ instance.save()
+ for i in record_data:
+ tri = i['id']
+ tri.field_value = i['field_value']
+ tri.is_testok = i['is_testok']
+ tri.is_hidden = i['is_hidden']
+ tri.save()
+ return instance
diff --git a/hb_server/apps/qm/views.py b/hb_server/apps/qm/views.py
index 8b3b80e..32d0334 100644
--- a/hb_server/apps/qm/views.py
+++ b/hb_server/apps/qm/views.py
@@ -1,5 +1,6 @@
-from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
-from apps.qm.serializers import StandardCreateUpdateSerializer, StandardSerializer, TestItemCreateUpdateSerializer, TestItemSerializer, TestRecordCreateSerializer, TestRecordDetailSerializer, TestRecordListSerializer
+from rest_framework import exceptions, serializers
+from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
+from apps.qm.serializers import StandardCreateUpdateSerializer, StandardSerializer, TestItemCreateUpdateSerializer, TestItemSerializer, TestRecordCreateSerializer, TestRecordDetailSerializer, TestRecordListSerializer, TestRecordUpdateSerializer
from apps.qm.models import Standard, TestItem, TestRecord, TestRecordItem
from django.shortcuts import render
from rest_framework.viewsets import GenericViewSet, ModelViewSet
@@ -8,6 +9,10 @@ from rest_framework.exceptions import APIException
from rest_framework.response import Response
from rest_framework import status
from django.db import transaction
+from rest_framework.decorators import action
+
+from apps.wpm.models import WProduct
+from apps.wpm.services import WpmServies
# Create your views here.
class StandardViewSet(CreateUpdateModelAMixin, ModelViewSet):
"""
@@ -43,12 +48,12 @@ class TestItemViewSet(CreateUpdateModelAMixin, ModelViewSet):
return TestItemCreateUpdateSerializer
return TestItemSerializer
-class TestRecordViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
+class TestRecordViewSet(ListModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet):
"""
- 检测记录
+ 检验记录
"""
perms_map = {'*': '*'}
- queryset = TestRecord.objects.select_related('fifo_item', 'form').all()
+ queryset = TestRecord.objects.select_related('fifo_item', 'form').prefetch_related('item_test_record').all()
serializer_class = TestRecordListSerializer
filterset_fields = ['wproduct', 'material', 'step', 'subproduction_plan', 'fifo_item', 'origin_test', 'type']
ordering = ['-id']
@@ -58,15 +63,40 @@ class TestRecordViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
return TestRecordListSerializer
elif self.action == 'retrieve':
return TestRecordDetailSerializer
+ elif self.action == 'update':
+ return TestRecordUpdateSerializer
return super().get_serializer_class()
+ def update(self, request, *args, **kwargs):
+ obj = self.get_object()
+ if obj.is_submited:
+ raise exceptions.APIException('该记录已提交不可编辑')
+ return super().update(request, *args, **kwargs)
+
+ def destroy(self, request, *args, **kwargs):
+ obj = self.get_object()
+ if obj.is_submited:
+ raise exceptions.APIException('该记录已提交不可删除')
+ return super().destroy(request, *args, **kwargs)
+
+ @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=TestRecordUpdateSerializer)
+ def submit(self, request, pk=None):
+ obj = self.get_object()
+ # 校验是否有未填项目
+ if obj.type != TestRecord.TEST_PROCESS_RE:
+ if TestRecordItem.objects.filter(field_value__isnull=True, is_hidden=False).exists():
+ raise exceptions.APIException('存在未填写项目')
+ with transaction.atomic():
+ WpmServies.update_wproduct_by_test(obj, request.user)
+ return Response()
+
# def create(self, request, *args, **kwargs):
# serializer = self.get_serializer(data=request.data)
# serializer.is_valid(raise_exception=True)
# vdata = serializer.validated_data
# record_data = vdata.pop('record_data')
# if 'is_testok' not in vdata:
- # raise APIException('未填写检测结论')
+ # raise APIException('未填写检验结论')
# with transaction.atomic():
# obj = serializer.save(create_by = self.request.user)
# tris = []
@@ -83,7 +113,7 @@ class TestRecordViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
# tris.append(TestRecordItem(**m))
# TestRecordItem.objects.bulk_create(tris)
- # # 如果检测合格
+ # # 如果检验合格
# if obj.fifo_item:
# obj.fifo_item.is_testok = True if obj.is_testok else False
# obj.fifo_item.is_tested = True
diff --git a/hb_server/apps/srm/serializers.py b/hb_server/apps/srm/serializers.py
new file mode 100644
index 0000000..a142348
--- /dev/null
+++ b/hb_server/apps/srm/serializers.py
@@ -0,0 +1,22 @@
+from rest_framework import serializers
+from apps.pm.models import ProductionPlan, SubProductionPlan
+from apps.mtm.serializers import ProcessSimpleSerializer
+
+class SubplanGanttSerializer(serializers.ModelSerializer):
+ count = serializers.IntegerField(source='main_count')
+ count_real = serializers.IntegerField(source='main_count_real')
+ count_ok = serializers.IntegerField(source='main_count_ok')
+ process_ = ProcessSimpleSerializer(source='process', read_only=True)
+ class Meta:
+ model = SubProductionPlan
+ fields = ['id', 'number', 'start_date', 'end_date', 'count', 'count_real', 'count_ok', 'start_date_real', 'end_date_real', 'process_']
+
+class PlanGanttSerializer(serializers.ModelSerializer):
+ children = serializers.SerializerMethodField()
+ class Meta:
+ model = ProductionPlan
+ fields = ['id', 'number', 'start_date', 'end_date', 'children', 'count', 'count_real', 'count_ok']
+
+ def get_children(self, obj):
+ subplans = SubProductionPlan.objects.filter(production_plan=obj).order_by('process__number')
+ return SubplanGanttSerializer(instance=subplans, many=True).data
\ No newline at end of file
diff --git a/hb_server/apps/srm/urls.py b/hb_server/apps/srm/urls.py
new file mode 100644
index 0000000..cbaf2ed
--- /dev/null
+++ b/hb_server/apps/srm/urls.py
@@ -0,0 +1,13 @@
+from django.db.models import base
+from rest_framework import urlpatterns
+from django.urls import path, include
+from rest_framework.routers import DefaultRouter
+
+from apps.srm.views import GanttPlan
+
+router = DefaultRouter()
+urlpatterns = [
+ path('gantt/plan/', GanttPlan.as_view()),
+ path('', include(router.urls)),
+]
+
diff --git a/hb_server/apps/srm/views.py b/hb_server/apps/srm/views.py
index 59607d6..a739725 100644
--- a/hb_server/apps/srm/views.py
+++ b/hb_server/apps/srm/views.py
@@ -1,8 +1,17 @@
from django.shortcuts import render
+from rest_framework import serializers
from rest_framework.generics import ListAPIView
+from rest_framework.response import Response
+from apps.pm.models import ProductionPlan, SubProductionPlan
+from apps.srm.serializers import PlanGanttSerializer
# Create your views here.
-class GanttOrder(ListAPIView):
+class GanttPlan(ListAPIView):
"""
- 订单-计划-子计划甘特图
- """
\ No newline at end of file
+ 计划-子计划甘特图
+ """
+ perms_map = {'get':'*'}
+ serializer_class = PlanGanttSerializer
+ queryset = ProductionPlan.objects.filter(is_deleted=False, is_planed=True).prefetch_related('subplan_plan', 'subplan_plan__process')
+ ordering = ['-id']
+
diff --git a/hb_server/apps/wf/migrations/0018_workflow_key.py b/hb_server/apps/wf/migrations/0018_workflow_key.py
new file mode 100644
index 0000000..821adc1
--- /dev/null
+++ b/hb_server/apps/wf/migrations/0018_workflow_key.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.9 on 2021-12-13 06:01
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('wf', '0017_auto_20211203_1501'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='workflow',
+ name='key',
+ field=models.CharField(blank=True, max_length=20, null=True, unique=True, verbose_name='工作流标识'),
+ ),
+ ]
diff --git a/hb_server/apps/wf/models.py b/hb_server/apps/wf/models.py
index ba8c745..5d6625d 100644
--- a/hb_server/apps/wf/models.py
+++ b/hb_server/apps/wf/models.py
@@ -13,7 +13,7 @@ class Workflow(CommonAModel):
工作流
"""
name = models.CharField('名称', max_length=50)
- key = models.CharField('工作流标识', unique=True, max_length=20)
+ key = models.CharField('工作流标识', unique=True, max_length=20, null=True, blank=True)
sn_prefix = models.CharField('流水号前缀', max_length=50, default='hb')
description = models.CharField('描述', max_length=200)
view_permission_check = models.BooleanField('查看权限校验', default=True, help_text='开启后,只允许工单的关联人(创建人、曾经的处理人)有权限查看工单')
@@ -213,18 +213,6 @@ class Ticket(CommonBModel):
multi_all_person = models.JSONField('全部处理的结果', default=dict, blank=True, help_text='需要当前状态处理人全部处理时实际的处理结果,json格式')
-# class TicketCustomField(BaseModel):
-# """
-# 工单数据,自定义字段值
-# """
-# ticket = models.ForeignKey(Ticket, verbose_name='关联工单', on_delete=models.CASCADE)
-# form_field = models.ForeignKey(CustomField, verbose_name='关联字段', on_delete=models.SET_NULL, db_constraint=False, null=True, blank=True)
-# field_name = models.CharField('字段名', max_length=50)
-# field_key = models.CharField('字段标识', max_length=50)
-# field_type = models.CharField('字段类型', choices=CustomField.field_type_choices, max_length=50)
-# field_value = models.JSONField('录入值', default=dict, blank=True)
-# sort = models.IntegerField('排序号', default=1)
-
class TicketFlow(BaseModel):
"""
工单流转日志
diff --git a/hb_server/apps/wpm/migrations/0032_auto_20211214_1245.py b/hb_server/apps/wpm/migrations/0032_auto_20211214_1245.py
new file mode 100644
index 0000000..2107312
--- /dev/null
+++ b/hb_server/apps/wpm/migrations/0032_auto_20211214_1245.py
@@ -0,0 +1,36 @@
+# Generated by Django 3.2.9 on 2021-12-14 04:45
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('wf', '0018_workflow_key'),
+ ('qm', '0018_testrecord_is_submited'),
+ ('wpm', '0031_auto_20211209_1638'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='wproduct',
+ name='test',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wp_test', to='qm.testrecord', verbose_name='当前检验'),
+ ),
+ migrations.AddField(
+ model_name='wproduct',
+ name='ticket',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wp_ticket', to='wf.ticket', verbose_name='当前工单'),
+ ),
+ migrations.AlterField(
+ model_name='wproduct',
+ name='operation',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wp_operation', to='wpm.operation', verbose_name='当前操作'),
+ ),
+ migrations.AlterField(
+ model_name='wprouctticket',
+ name='ticket',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='wt_ticket', to='wf.ticket', verbose_name='关联工单'),
+ ),
+ ]
diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py
index 182ffe8..5cdd3c7 100644
--- a/hb_server/apps/wpm/models.py
+++ b/hb_server/apps/wpm/models.py
@@ -1,4 +1,5 @@
import re
+from rest_framework import exceptions
from django.db import models
from django.db.models.base import Model
import django.utils.timezone as timezone
@@ -7,6 +8,7 @@ from apps.inm.models import FIFO, WareHouse
from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress
from apps.qm.models import TestRecord
from apps.system.models import CommonADModel, CommonAModel, CommonBModel, Organization, User, Dict, File
+from apps.wf.models import Ticket
from utils.model import SoftModel, BaseModel
from simple_history.models import HistoricalRecords
from apps.mtm.models import Material, Process, RecordFormField, Step, RecordForm, SubprodctionMaterial
@@ -54,15 +56,54 @@ class WProduct(CommonAModel):
remark = models.CharField('备注', max_length=200, null=True, blank=True)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE, related_name='wproduct_subplan')
warehouse = models.ForeignKey(WareHouse, verbose_name='所在仓库', on_delete=models.SET_NULL, null=True, blank=True)
- operation = models.ForeignKey('wpm.operation', verbose_name='关联操作',
+ operation = models.ForeignKey('wpm.operation', verbose_name='当前操作',
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_operation')
+ test = models.ForeignKey('qm.testrecord', verbose_name='当前检验',
+ on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_test')
+ ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单',
+ on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_ticket')
@property
def last_process_test(self):
"""
- 最后的工序自检
+ 最后提交的工序自检
"""
- return self.test_wproduct.filter(type=TestRecord.TEST_PROCESS).order_by('-id').first()
+ return self.test_wproduct.filter(type=TestRecord.TEST_PROCESS, is_submited=True).order_by('-id').first()
+
+ # @property
+ # def last_test(self):
+ # """
+ # 最后提交的检验
+ # """
+ # return self.test_wproduct.filter(is_submited=True).order_by('-id').first()
+
+ # @property
+ # def current_test(self):
+ # """
+ # 当前未提交的检验
+ # """
+ # trs = self.test_wproduct.filter(is_submited=False).order_by('-id')
+ # if trs.count() == 1:
+ # return trs[0]
+ # elif trs.count() == 0:
+ # return None
+ # else:
+ # raise exceptions.APIException('存在多条未提交检验记录')
+
+ # @property
+ # def current_ticket(self):
+ # """
+ # 当前是否有进行中工单
+ # """
+ # tickets = Ticket.objects.filter(wt_ticket__wproduct=self, act_state=Ticket.TICKET_ACT_STATE_ONGOING).order_by('-id')
+ # if tickets.count() == 1:
+ # return tickets[0]
+ # elif tickets.count() == 0:
+ # return None
+ # else:
+ # raise exceptions.APIException('存在多条进行中工单')
+
+
class WprouctTicket(CommonAModel):
"""
玻璃审批工单
@@ -72,7 +113,7 @@ class WprouctTicket(CommonAModel):
material = models.ForeignKey(Material, verbose_name='所在物料状态', on_delete=models.CASCADE)
step = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='所在子生产计划', on_delete=models.CASCADE)
- ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.CASCADE)
+ ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.CASCADE, related_name='wt_ticket')
class Pick(CommonADModel):
"""
diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py
index ca3da1d..3cab8b8 100644
--- a/hb_server/apps/wpm/serializers.py
+++ b/hb_server/apps/wpm/serializers.py
@@ -65,7 +65,7 @@ class PickSerializer(serializers.Serializer):
if isLowLevel:
iproducts = i.pop('iproducts')
i['fifo'] = fifo
- i['is_testok'] = True # 默认检测合格
+ i['is_testok'] = True # 默认检验合格
i['subproduction_plan'] = sp
fifoitem = FIFOItem.objects.create(**i)
# 创建再下一个层级
@@ -282,27 +282,29 @@ class OperationSubmitSerializer(serializers.Serializer):
class WpmTestRecordItemCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TestRecordItem
- fields = ['form_field', 'field_value', 'is_testok']
+ fields = ['form_field', 'field_value', 'is_testok', 'is_hidden']
class WpmTestRecordCreateSerializer(serializers.ModelSerializer):
record_data = WpmTestRecordItemCreateSerializer(many=True)
wproduct = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), required=True)
is_testok = serializers.BooleanField(required=False)
+ origin_test = serializers.PrimaryKeyRelatedField(queryset=TestRecord.objects.all(), default=None)
class Meta:
model = TestRecord
- fields = ['form', 'record_data', 'is_testok', 'wproduct']
+ fields = ['form', 'record_data', 'is_testok', 'wproduct', 'is_submited', 'origin_test']
class WpmTestFormInitSerializer(serializers.Serializer):
wproduct = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), required=True)
- form = serializers.PrimaryKeyRelatedField(queryset=RecordForm.objects.all(), required=False)
+ form = serializers.PrimaryKeyRelatedField(queryset=RecordForm.objects.all(), required=True)
- def validate(self, attrs):
- wproduct = attrs['wproduct']
- form = attrs.get('form', None)
- if wproduct.act_state != WProduct.WPR_ACT_STATE_TORETEST:
- if not form:
- raise exceptions.APIException('请指定检查表')
- return super().validate(attrs)
+ # def validate(self, attrs):
+ # wproduct = attrs['wproduct']
+ # form = attrs.get('form', None)
+ # if wproduct.act_state != WProduct.WPR_ACT_STATE_TORETEST:
+ # if not form:
+ # raise exceptions.APIException('请指定检查表')
+ # return super().validate(attrs)
+
class WplanPutInSerializer(serializers.Serializer):
warehouse = serializers.PrimaryKeyRelatedField(queryset=WareHouse.objects.all(), label="仓库ID")
diff --git a/hb_server/apps/wpm/services.py b/hb_server/apps/wpm/services.py
index 20f2870..9d56be0 100644
--- a/hb_server/apps/wpm/services.py
+++ b/hb_server/apps/wpm/services.py
@@ -1,7 +1,10 @@
from typing import List
-from apps.pm.models import SubProductionPlan
-from apps.mtm.models import Step, SubprodctionMaterial
+from apps.pm.models import SubProductionPlan, SubProductionProgress
+from apps.mtm.models import Material, Step, SubprodctionMaterial
+from apps.qm.models import TestRecord
+from apps.system.models import User
from apps.wpm.models import WProduct
+from utils.tools import ranstr
class WpmServies(object):
@classmethod
@@ -31,4 +34,33 @@ class WpmServies(object):
"""
splans = SubProductionPlan.objects.filter(is_deleted=False,
subproduction__usedstep_subproduction__step=step, state=SubProductionPlan.SUBPLAN_STATE_WORKING)
- return splans
\ No newline at end of file
+ return splans
+
+ @classmethod
+ def update_wproduct_by_test(cls, test:TestRecord, user:User):
+ """
+ 根据检验结果更新玻璃及相关状态
+ """
+ is_testok = test.is_testok
+ wproduct = test.wproduct
+ if is_testok:
+ if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: # 复检
+ wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT
+ elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.material.type == Material.MA_TYPE_GOOD: # 成品检验
+ wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST
+ else:
+ wproduct.act_state = WProduct.WPR_ACT_STATE_OK
+ if wproduct.number is None: # 产生半成品编号
+ wproduct.number = 'WP'+ranstr(7)
+ # 更新子计划状态
+ # 更新子计划主产品数
+ instance = SubProductionProgress.objects.get(subproduction_plan=wproduct.subproduction_plan,
+ is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
+ instance.count_ok = instance.count_ok + 1 # 这个地方可能会有问题
+ instance.save()
+ else:# 如果不合格
+ wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK
+ # 需要走不合格品审理的工单
+ wproduct.update_by = user
+ wproduct.test = None
+ wproduct.save()
\ No newline at end of file
diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py
index 4317deb..d426947 100644
--- a/hb_server/apps/wpm/views.py
+++ b/hb_server/apps/wpm/views.py
@@ -16,6 +16,7 @@ from apps.qm.serializers import TestRecordDetailSerializer
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from rest_framework.decorators import action
+from apps.wf.models import Workflow
from apps.wpm.filters import WMaterialFilterSet
from apps.wpm.models import OperationEquip, OperationWproduct, Pick, PickWproduct, WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem
@@ -193,26 +194,29 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
wproduct = vdata['wproduct']
- form = vdata.get('form', None)
+ form = vdata['form']
+ if wproduct.test:
+ raise exceptions.APIException('存在进行中检验')
-
- # 如果是复检记录, 需要带入原数据
+ data = RecordFormDetailSerializer(instance=form).data
+ data['origin_test'] = None
+ data['form'] = form.id
+ # 如果是复检, 需要带入原数据
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST:
# 查找最近一条检验记录
trs = wproduct.last_process_test
if trs:
origin_test = TestRecordDetailSerializer(instance=trs).data
- data = RecordFormDetailSerializer(instance=trs.form).data
- data['origin_test'] = origin_test
+ data['origin_test_'] = origin_test
+ data['origin_test'] = origin_test.get('id', None)
o_dict = {}
- for i in origin_test['record_data_']:
+ for i in origin_test['record_data']:
o_dict[i['field_key']] = i['field_value']
for i in data['form_fields']:
i['origin_value'] = o_dict[i['field_key']] if i['field_key'] in o_dict else None
+ i['is_hidden'] = o_dict[i['is_hidden']] if i['is_hidden'] in o_dict else False
else:
raise exceptions.APIException('原工序检验记录不存在')
- else:
- data = RecordFormDetailSerializer(instance=form).data
# 后续加入系统自带数据
return Response(data)
@@ -221,7 +225,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
@transaction.atomic
def test(self, request, pk=None):
"""
- 检测
+ 检验记录提交
"""
serializer = WpmTestRecordCreateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
@@ -232,11 +236,13 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
WProduct.WPR_ACT_STATE_TORETEST, WProduct.WPR_ACT_STATE_TOFINALTEST, WProduct.WPR_ACT_STATE_TOCOMBTEST]:
raise exceptions.APIException('该产品当前状态不可检验')
if 'is_testok' not in vdata:
- raise exceptions.APIException('未填写检测结论')
+ raise exceptions.APIException('未填写检验结论')
savedict = dict(create_by = self.request.user,
material=wproduct.material, number=wproduct.number, subproduction_plan=wproduct.subproduction_plan, step=wproduct.step)
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST:
+ if not vdata['origin_test']:
+ raise exceptions.APIException('自检记录不存在')
savedict['type'] = TestRecord.TEST_PROCESS_RE
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOFINALTEST:
savedict['type'] = TestRecord.TEST_FINAL
@@ -249,37 +255,20 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
m['field_name'] = form_field.field_name
m['field_key'] = form_field.field_key
m['field_type'] = form_field.field_type
- m['sort'] = form_field.sort
- m['need_judge'] = form_field.need_judge
m['is_testok'] = m['is_testok'] if 'is_testok' in m else None
+ m['is_hidden'] = m['is_hidden'] if 'is_hidden' in m else None
m['test_record'] = obj
tris.append(TestRecordItem(**m))
TestRecordItem.objects.bulk_create(tris)
- # 如果检测合格, 变更动态产品进行状态
-
- if obj.is_testok:
- if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: # 复检
- obj.origin_test = wproduct.last_process_test
- obj.save()
- wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT
- elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.material.type == Material.MA_TYPE_GOOD: # 成品检验
- wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST
- else:
- wproduct.act_state = WProduct.WPR_ACT_STATE_OK
- if wproduct.number is None: # 产生半成品编号
- wproduct.number = 'WP'+ranstr(7)
- # 更新子计划状态
- # 更新子计划主产品数
- instance = SubProductionProgress.objects.get(subproduction_plan=wproduct.subproduction_plan,
- is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
- instance.count_ok = instance.count_ok + 1 # 这个地方可能会有问题
- instance.save()
- else:# 如果不合格
- wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK
- # 需要走不合格品审理单
- wproduct.update_by = request.user
- wproduct.save()
+ # 如果提交检验
+ if obj.is_submited:
+ WpmServies.update_wproduct_by_test(obj, request.user)
+ else:
+ # 保存当前检验
+ wproduct.test = obj
+ wproduct.update_by = request.user
+ wproduct.save()
return Response()
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WproductPutInsSerializer)
@@ -379,6 +368,12 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
wproduct.save()
return Response()
+ @action(methods=['get'], detail=False, perms_map={'post':'*'})
+ def workflows(self, request, pk=None):
+ """
+ 可发起的工作流
+ """
+ wfs = Workflow.objects.get()
class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
"""
生产操作记录
@@ -619,7 +614,7 @@ class OperationEquipViewSet(ListModelMixin, DestroyModelMixin, UpdateModelMixin,
instance.delete()
return Response()
-class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
+class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, UpdateModelMixin, GenericViewSet):
"""
操作使用的自定义表格
"""
@@ -630,6 +625,10 @@ class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
ordering_fields = ['id']
ordering = ['-id']
+ def get_serializer_class(self):
+ if self.action == 'update':
+ return OperationRecordSubmitSerializer
+ return super().get_serializer_class()
@transaction.atomic()
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
@@ -648,12 +647,14 @@ class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
# 后续加入系统带入数据
return Response(data)
- @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=OperationRecordSubmitSerializer)
- def submit(self, request, pk=None):
- serializer = OperationRecordSubmitSerializer(data=request.data, context={'request':self.request})
+
+ def update(self, request, *args, **kwargs):
+ serializer = OperationRecordSubmitSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
opr = self.get_object()
+ if opr.operation.is_submited:
+ raise exceptions.APIException('操作已提交不可修改')
wrds = []
for m in vdata['record_data']: # 保存记录详情
form_field = m['form_field']
diff --git a/hb_server/server/urls.py b/hb_server/server/urls.py
index 3db8e36..b1893f2 100644
--- a/hb_server/server/urls.py
+++ b/hb_server/server/urls.py
@@ -69,7 +69,7 @@ urlpatterns = [
path('api/qm/', include('apps.qm.urls')),
path('api/pm/', include('apps.pm.urls')),
path('api/wpm/', include('apps.wpm.urls')),
-
+ path('api/srm/', include('apps.srm.urls')),
# 工具
path('api/utils/signature/', GenSignature.as_view()),
path('api/utils/develop/', UpdateDevelop.as_view()),