Merge branch 'develop' of https://e.coding.net/ctcdevteam/hberp/hberp into develop

This commit is contained in:
shijing 2021-12-15 23:07:28 +08:00
commit 1f83b26fa5
31 changed files with 753 additions and 221 deletions

View File

@ -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",

View File

@ -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',

View File

@ -45,21 +45,7 @@
<el-tag v-else-if="scope.row.is_mtestok == true">合格</el-tag></template>
</el-table-column>
<el-table-column
align="center"
label="操作"
width="220px"
>
<template slot-scope="scope">
<el-link
v-if="scope.row.is_mtested == false"
@click="handleMtest(scope)"
>军检</el-link
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="iproductData.count > 0"
@ -68,34 +54,7 @@
:limit.sync="listQuery.page_size"
@pagination="getList"
/>
<el-dialog
:visible.sync="dialogVisible"
:close-on-click-modal="false"
title="军检"
>
<el-form
ref="Form"
:model="mtest"
label-width="150px"
label-position="right"
>
<el-form-item label="军检是否合格">
<el-radio v-model="mtest.is_mtestok" label=True >合格</el-radio>
<el-radio v-model="mtest.is_mtestok" label=False >不合格</el-radio>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="mtest.remark" placeholder="备注" />
</el-form-item>
</el-form>
<div style="text-align: right">
<el-button type="danger" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="smtconfirm('Form')">确认</el-button>
</div>
</el-dialog>
</el-card>
</div>
@ -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("成功");
}
});
}
},
};
</script>

View File

@ -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;

View File

@ -0,0 +1,165 @@
<template>
<div class="app-container">
<el-card style="margin-top: 2px">
<el-table
v-loading="listLoading"
:data="iproductData.results"
border
fit
stripe
highlight-current-row
max-height="700"
height="100"
v-el-height-adaptive-table="{bottomOffset: 50}"
>
<el-table-column type="index" width="50" />
<el-table-column label="成品编号">
<template slot-scope="scope">{{ scope.row.number }}</template>
</el-table-column>
<el-table-column label="成品批次">
<template slot-scope="scope">{{ scope.row.batch }}</template>
</el-table-column>
<el-table-column label="成品名称">
<template slot-scope="scope">{{ scope.row.material_.name }}</template>
</el-table-column>
<el-table-column label="所在仓库">
<template slot-scope="scope">{{ scope.row.warehouse_.name }}</template>
</el-table-column>
<el-table-column label="是否已军检">
<template slot-scope="scope">
<el-tag v-if="scope.row.is_mtested == false">未军检</el-tag>
<el-tag v-else>已军检</el-tag></template>
</el-table-column>
<el-table-column label="军检">
<template slot-scope="scope">
<el-tag v-if="scope.row.is_mtestok == false">不合格</el-tag>
<el-tag v-else-if="scope.row.is_mtestok == true">合格</el-tag></template>
</el-table-column>
<el-table-column
align="center"
label="操作"
width="220px"
>
<template slot-scope="scope">
<el-link
v-if="scope.row.is_mtested == false"
@click="handleMtest(scope)"
>军检</el-link
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="iproductData.count > 0"
:total="iproductData.count"
:page.sync="listQuery.page"
:limit.sync="listQuery.page_size"
@pagination="getList"
/>
<el-dialog
:visible.sync="dialogVisible"
:close-on-click-modal="false"
title="军检"
>
<el-form
ref="Form"
:model="mtest"
label-width="150px"
label-position="right"
>
<el-form-item label="军检是否合格">
<el-radio v-model="mtest.is_mtestok" label=True >合格</el-radio>
<el-radio v-model="mtest.is_mtestok" label=False >不合格</el-radio>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="mtest.remark" placeholder="备注" />
</el-form-item>
</el-form>
<div style="text-align: right">
<el-button type="danger" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="smtconfirm('Form')">确认</el-button>
</div>
</el-dialog>
</el-card>
</div>
</template>
<script>
import { getiproductList,saleMtest} from "@/api/inm";
import checkPermission from "@/utils/permission";
import { genTree } from "@/utils";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
export default {
components: { Pagination },
data() {
return {
iproductData: {
count: 0,
},
listQuery: {
page: 1,
page_size: 20,
},
mtest: {},
salesdetail:"",
saleproduct:"",
dialogVisible:false,
};
},
computed: {},
watch: {},
created() {
this.getList();
},
methods: {
checkPermission,
//半成品列表
getList() {
this.listLoading = true;
this.listQuery.type=1;
getiproductList(this.listQuery).then((response) => {
if (response.data) {
this.iproductData= response.data;
}
this.listLoading = false;
});
},
handleMtest(scope){
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("成功");
}
});
}
},
};
</script>

View File

@ -143,10 +143,48 @@
</el-form-item>
<el-form-item label="选择产品" prop="iproducts">
<div class="trdiv">
<el-transfer v-model="sale.iproducts" :data="iproductoptions" :titles="['未选产品', '已选产品']" right-check-change="change"></el-transfer>
</div>
<el-table
v-loading="listLoading"
:data="iproductoptions"
border
fit
stripe
highlight-current-row
ref="multipleTable"
>
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column type="index" width="50" />
<el-table-column label="成品编号">
<template slot-scope="scope">{{ scope.row.number }}</template>
</el-table-column>
<el-table-column label="成品批次">
<template slot-scope="scope">{{ scope.row.batch }}</template>
</el-table-column>
<el-table-column label="成品名称">
<template slot-scope="scope">{{ scope.row.material_.name }}</template>
</el-table-column>
<el-table-column label="所在仓库">
<template slot-scope="scope">{{ scope.row.warehouse_.name }}</template>
</el-table-column>
<el-table-column label="是否已军检">
<template slot-scope="scope">
<el-tag v-if="scope.row.is_mtested == false">未军检</el-tag>
<el-tag v-else>已军检</el-tag></template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
@ -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();

View File

@ -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']
fields = ['material', 'warehouse', 'batch', 'order', 'is_mtested', 'is_mtestok', 'material__type']

View File

@ -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)

View File

@ -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

View File

@ -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='类型'),
),
]

View File

@ -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 = (

View File

@ -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='关联主生产计划'),
),
]

View File

@ -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)

View File

@ -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()

View File

@ -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='关联的动态产品'),
),
]

View File

@ -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='是否提交'),
),
]

View File

@ -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='是否隐藏'),
),
]

View File

@ -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')
test_record = models.ForeignKey(TestRecord, verbose_name='关联的检记录', on_delete=models.CASCADE, related_name='item_test_record')

View File

@ -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
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

View File

@ -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

View File

@ -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

View File

@ -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)),
]

View File

@ -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):
"""
订单-计划-子计划甘特图
"""
计划-子计划甘特图
"""
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']

View File

@ -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='工作流标识'),
),
]

View File

@ -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):
"""
工单流转日志

View File

@ -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='关联工单'),
),
]

View File

@ -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):
"""

View File

@ -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")

View File

@ -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
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()

View File

@ -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']

View File

@ -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()),