diff --git a/hb_client/src/components/customForm/index.vue b/hb_client/src/components/customForm/index.vue
index 7fcfd50..416696f 100644
--- a/hb_client/src/components/customForm/index.vue
+++ b/hb_client/src/components/customForm/index.vue
@@ -19,7 +19,7 @@
@@ -68,7 +68,7 @@
placeholder="选择日期"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100%"
- @change="keyChange(item.field_key)"
+ @change="keyChange($index,item.field_key)"
/>
@@ -80,7 +80,7 @@
v-model="checkForm[item.field_key]"
style="width: 100%"
placeholder="请选择"
- @change="keyChange(item.field_key)"
+ @change="keyChange($index,item.field_key)"
>
@@ -135,11 +137,22 @@
-
-
- 合格验证
-
-
+
+
+
+
+ 检查合格
+ 检查不合格
+
+
+
+
@@ -159,6 +172,10 @@
type:Number,
default:0
},
+ wproduct: {
+ type:Number,
+ default:0
+ },
hasPicture:{
type:Boolean,
default:false
@@ -167,24 +184,28 @@
mounted() {
let that = this;
this.form = this.formID;
- // debugger;
- // console.log(this.results);
- this.formData=this.results;
- this.formData=[...this.formData];
+ let formData=this.results;
+ that.wproductId=this.wproduct;
+ formData.forEach(item => {
+ let obj = new Object();
+ obj = item;
+ obj.is_testok = true;//是否合格
+ that.formData.push(obj)
+ });
+ that.formData=[...that.formData];
+
for(let i=0;i {
return item.need_judge === true;
});
listJudge.forEach(item => {
let obj = new Object();
obj = item;
- obj.judge = false;
that.judgeList.push(obj)
});
setTimeout(function(){
@@ -193,8 +214,16 @@
},
data(){
return{
+ field:[],
checkForm:{},
+ testrecord:{
+ form:this.formID,
+ record_data:[],
+ is_testok:true,
+ wproduct:null,
+ },
canvas:null,
+ wproductId:null,
ctx:null,
myCanvas_rect:null,
Txt:null,
@@ -207,6 +236,9 @@
canvasImg:'',
formData:[],//表单数组
judgeList:[],//判定数组
+ is_testok:true,
+ testokTrue:true,
+ testokFalse:false,
}
},
methods:{
@@ -276,15 +308,63 @@
return true;
}
},
- keyChange(key){
- key;
- this.$forceUpdate();
- /* debugger;
- let y = this.checkForm[key];
- this.$set(this.checkForm,key,y);*/
- // this.filterBlock();
+ keyChange(index,key){
+ let that = this;
+ let reg = /\{(.+?)\}/g;
+ that.$forceUpdate();
+ that.$nextTick(()=>{
+ if(that.formData[index].need_judge===true){
+ let arr = [],str = '';
+ let item = that.formData[index].rule_expression.replace(/`/g,'');
+ if(item.indexOf('||')>-1){
+ arr = item.split('||');
+ }else if(item.indexOf('&&')>-1){
+ arr = item.split('&&');
+ }else{
+ arr.push(item);
+ }
+ //对每个条件进行判定,如果符合,
+ for (let i = 0;i-1){
+ if(str.indexOf('false')>-1){
+ that.formData[index].is_testok = true;
+ }else{
+ that.formData[index].is_testok = false;
+ }
+ }else{
+ if(str.indexOf('true')>-1){
+ that.formData[index].is_testok = false;
+ }else{
+ that.formData[index].is_testok = true;
+ }
+ }
+ }else{
+ that.formData[index].is_testok = true;
+ }
+ that.judgeList = that.formData.filter(item => {
+ return item.need_judge === true;
+ });
+ that.judgeForm();
+ })
},
- /* base64ToFile */
+ /* 图片保存 */
+ //初始化canvas
canvasInit(){
let that = this;
preDrawAry = [];
@@ -299,6 +379,7 @@
that.draw();
},500);
},
+ //画布添加背景模板图
draw(){
let canvasImg = document.getElementById("canvasImg");
canvasImg.style.width = '500px';
@@ -360,7 +441,6 @@
}
}
},
-
// 文字
word1(){
let canvas3 = document.getElementById('canvas');
@@ -462,6 +542,7 @@
// console.log(res);
})
},
+ //base64ToFile
base64ToFile(baseUrl) {
let arr = baseUrl.split(',');
// let type = arr[0].match(/:(.*?);/)[1]; // 解锁图片类型
@@ -474,7 +555,8 @@
// let fileOfBlob = new File([bufferArray], new Date()+'.jpg');
return new File([bufferArray ],'draw.jpg');
},
- /* base64ToFile */
+ /* 图片保存 */
+ //最终表格判定
judgeForm(){
let that = this ,
reg = /\{(.+?)\}/g,
@@ -508,21 +590,56 @@
}
}
}
- if(str.indexOf('true')>-1){
- that.judgeList[i].judge = true;
+ if(item.indexOf('&&')>-1){
+ if(str.indexOf('false')>-1){
+ that.judgeList[i].is_testok = true;
+ }else{
+ that.judgeList[i].is_testok = false;
+ }
}else{
- that.judgeList[i].judge = false;
+ if(str.indexOf('true')>-1){
+ that.judgeList[i].is_testok = false;
+ }else{
+ that.judgeList[i].is_testok = true;
+ }
}
}
let real = that.judgeList.filter(item=>{
- return item.judge==true;
+ return item.is_testok===false;
});
if(real.length>0){
+ that.is_testok = false;
alert("检验不合格!")
}else{
+ that.is_testok = true;
alert("检验合格!")
}
},
+ //提交检查项目
+ submitfield() {
+ let that = this;
+ debugger;
+ console.log(that.checkForm);
+ debugger;
+ that.field = []; //检查项目
+ that.formData.forEach((item) => {
+ debugger;
+ console.log(that.checkForm[item.field_key]);
+ debugger;
+ that.field.push({
+ form_field: item.id,
+ field_value: that.checkForm[item.field_key],
+ is_testok: item.is_testok//单项检查结果
+ });
+ });
+ console.log(that.field);
+ debugger;
+ that.testrecord.form = that.formID;//检查表
+ that.testrecord.record_data = that.field;//检查项列表
+ that.testrecord.is_testok = that.is_testok;//检查表检查结果
+ that.testrecord.wproduct = that.wproductId;//半成品ID
+ this.$emit('formFunc',that.testrecord);
+ },
}
}
diff --git a/hb_client/src/views/wpm/need.vue b/hb_client/src/views/wpm/need.vue
index 1f6f670..4ed28d8 100644
--- a/hb_client/src/views/wpm/need.vue
+++ b/hb_client/src/views/wpm/need.vue
@@ -1,601 +1,511 @@
-
-
-
-
-
- {{ scope.row.material_.name }}
-
+
+
+
+
+
+ {{ scope.row.material_.name }}
+
-
- {{ scope.row.number }}
-
-
-
- {{ actstate_[scope.row.act_state] }}
-
-
+
+ {{ scope.row.number }}
+
+
+
+ {{ actstate_[scope.row.act_state] }}
+
+
-
- {{ scope.row.step_.name }}
-
+
+ {{ scope.row.step_.name }}
+
-
-
- 检验
-
-
-
-
-
-
-
-
-
-
-
- {{ scope.row.material_.name }}
-
+
+
+ 检验
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ scope.row.material_.name }}
+
-
- {{ scope.row.number }}
-
-
-
- {{ actstate_[scope.row.act_state] }}
-
-
+
+ {{ scope.row.number }}
+
+
+
+ {{ actstate_[scope.row.act_state] }}
+
+
-
- {{ scope.row.step_.name }}
-
+
+ {{ scope.row.step_.name }}
+
+
+
+ 检验
+
+
+
+
+
+
+
+
+
+ 批量入库
+
+
-
- 检验
-
-
-
-
-
-
-
-
- 批量入库
-
-
-
-
-
- {{ scope.row.material_.name }}
-
+ >
+
+
+
+
+ {{ scope.row.material_.name }}
+
-
- {{ scope.row.number }}
-
-
-
- {{ actstate_[scope.row.act_state] }}
-
-
+
+ {{ scope.row.number }}
+
+
+
+ {{ actstate_[scope.row.act_state] }}
+
+
-
- {{ scope.row.step_.name }}
-
+
+ {{ scope.row.step_.name }}
+
+
+
+
+ 入库
+
+
+
+
+
+
+
+
-
-
- 入库
-
-
-
-
-
-
-
-
-
+
+ >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 检查合格
- 检查不合格
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hb_server/apps/inm/filters.py b/hb_server/apps/inm/filters.py
index c71c4fb..3f550a3 100644
--- a/hb_server/apps/inm/filters.py
+++ b/hb_server/apps/inm/filters.py
@@ -1,9 +1,17 @@
from django_filters import rest_framework as filters
from apps.mtm.models import Material
-from .models import MaterialBatch
+from .models import IProduct, MaterialBatch
class MbFilterSet(filters.FilterSet):
material = filters.ModelMultipleChoiceFilter(field_name="material", queryset=Material.objects.all())
class Meta:
model = MaterialBatch
- fields = ['material', 'warehouse']
\ No newline at end of file
+ fields = ['material', 'warehouse']
+
+
+class IProductFilterSet(filters.FilterSet):
+
+ order = filters.NumberFilter(field_name="wproduct__subproduction_plan__production_plan__order")
+ class Meta:
+ model = IProduct
+ fields = ['material', 'warehouse', 'batch', 'order']
\ No newline at end of file
diff --git a/hb_server/apps/inm/migrations/0020_iproduct_is_saled.py b/hb_server/apps/inm/migrations/0020_iproduct_is_saled.py
new file mode 100644
index 0000000..5ca02e7
--- /dev/null
+++ b/hb_server/apps/inm/migrations/0020_iproduct_is_saled.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.9 on 2021-12-06 01:58
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('inm', '0019_auto_20211201_1011'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='iproduct',
+ name='is_saled',
+ field=models.BooleanField(default=False, verbose_name='是否售出'),
+ ),
+ ]
diff --git a/hb_server/apps/inm/migrations/0021_fifoitemproduct_iproduct.py b/hb_server/apps/inm/migrations/0021_fifoitemproduct_iproduct.py
new file mode 100644
index 0000000..9d9ea22
--- /dev/null
+++ b/hb_server/apps/inm/migrations/0021_fifoitemproduct_iproduct.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.2.9 on 2021-12-07 00:42
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('inm', '0020_iproduct_is_saled'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='fifoitemproduct',
+ name='iproduct',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='inm.iproduct', verbose_name='关联库存产品'),
+ ),
+ ]
diff --git a/hb_server/apps/inm/models.py b/hb_server/apps/inm/models.py
index 20d87dd..9059a91 100644
--- a/hb_server/apps/inm/models.py
+++ b/hb_server/apps/inm/models.py
@@ -84,15 +84,6 @@ class FIFOItem(BaseModel):
fifo = models.ForeignKey(FIFO, verbose_name='关联出入库', on_delete=models.CASCADE)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE, null=True, blank=True)
-class FIFOItemProduct(BaseModel):
- """
- 出入库产品
- """
- fifoitem = models.ForeignKey(FIFOItem, verbose_name='关联出入库具体产品', on_delete=models.CASCADE)
- wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True)
- number = models.CharField('物品编号', max_length=50)
- material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE)
-
class IProduct(BaseModel):
"""
具体产品条目
@@ -102,5 +93,18 @@ class IProduct(BaseModel):
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库')
batch = models.CharField('所属批次号', max_length=100, default='')
wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True)
+ is_saled = models.BooleanField('是否售出', default=False)
+
+class FIFOItemProduct(BaseModel):
+ """
+ 出入库产品
+ """
+ fifoitem = models.ForeignKey(FIFOItem, verbose_name='关联出入库具体产品', on_delete=models.CASCADE)
+ wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True)
+ number = models.CharField('物品编号', max_length=50)
+ material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE)
+ iproduct = models.ForeignKey(IProduct, verbose_name='关联库存产品', null=True, blank=True, on_delete=models.SET_NULL)
+
+
diff --git a/hb_server/apps/inm/signals.py b/hb_server/apps/inm/signals.py
index 79d5a19..e659c0c 100644
--- a/hb_server/apps/inm/signals.py
+++ b/hb_server/apps/inm/signals.py
@@ -36,7 +36,7 @@ def update_inm(instance:FIFO, type:int=1):
ips2.append(IProduct(**ip))
IProduct.objects.bulk_create(ips2)
- elif instance.type in [FIFO.FIFO_TYPE_DO_OUT]: # 生产领料
+ elif instance.type in [FIFO.FIFO_TYPE_DO_OUT, FIFO.FIFO_TYPE_SALE_OUT]: # 生产领料 销售出库
# 更新相关表
for i in FIFOItem.objects.filter(fifo=instance):
material = i.material
@@ -49,8 +49,9 @@ def update_inm(instance:FIFO, type:int=1):
o2.save()
material.count = material.count - i.count
material.save()
-
- # 删除IProduct
- numbers = FIFOItemProduct.objects.filter(fifoitem=i).values_list('number', flat=True)
- IProduct.objects.filter(number__in=numbers).delete()
+
+ # 删除IProduct
+ if instance.type == FIFO.FIFO_TYPE_DO_OUT:
+ numbers = FIFOItemProduct.objects.filter(fifoitem=i).values_list('number', flat=True)
+ IProduct.objects.filter(number__in=numbers).delete()
diff --git a/hb_server/apps/inm/views.py b/hb_server/apps/inm/views.py
index 6454c40..8f455e0 100644
--- a/hb_server/apps/inm/views.py
+++ b/hb_server/apps/inm/views.py
@@ -3,7 +3,7 @@ from rest_framework import serializers
from rest_framework.exceptions import APIException
from rest_framework.mixins import DestroyModelMixin, ListModelMixin, RetrieveModelMixin
from rest_framework.viewsets import GenericViewSet, ModelViewSet
-from apps.inm.filters import MbFilterSet
+from apps.inm.filters import IProductFilterSet, MbFilterSet
from apps.inm.models import FIFO, FIFOItem, IProduct, MaterialBatch, WareHouse,Inventory
from apps.inm.serializers import FIFOItemSerializer, FIFOInPurSerializer, FIFOListSerializer, IProductListSerializer, InmTestRecordCreateSerializer, MaterialBatchQuerySerializer, MaterialBatchSerializer, WareHouseSerializer, WareHouseCreateUpdateSerializer,InventorySerializer
@@ -167,9 +167,9 @@ class IProductViewSet(ListModelMixin, GenericViewSet):
半成品库存表
"""
perms_map = {'*': '*'}
- queryset = IProduct.objects.select_related('material', 'warehouse').all()
+ queryset = IProduct.objects.select_related('material', 'warehouse', 'wproduct__subproduction_plan__production_plan__order').filter(is_saled=False)
serializer_class = IProductListSerializer
- filterset_fields = ['material', 'warehouse', 'batch']
+ filterset_class = IProductFilterSet
search_fields = []
ordering_fields = ['create_time']
ordering = ['-create_time']
\ No newline at end of file
diff --git a/hb_server/apps/mtm/serializers.py b/hb_server/apps/mtm/serializers.py
index 153971f..f00b95d 100644
--- a/hb_server/apps/mtm/serializers.py
+++ b/hb_server/apps/mtm/serializers.py
@@ -209,13 +209,13 @@ class RecordFormDetailSerializer(serializers.ModelSerializer):
def get_form_fields(self, obj):
serializer = RecordFormFieldSerializer(instance=RecordFormField.objects.filter(form=obj, is_deleted=False), many=True)
- vdata = serializer.data
+ data = serializer.data
if obj.type == RecordForm.RF_TYPE_TEST:
- for i in vdata:
+ for i in data:
if i['need_judge']:
i['is_testok'] = False
i['is_teskok_robot'] = False
- return serializer.data
+ return data
class RecordFormFieldCreateSerializer(serializers.ModelSerializer):
diff --git a/hb_server/apps/sam/migrations/0005_auto_20211203_1501.py b/hb_server/apps/sam/migrations/0005_auto_20211203_1501.py
new file mode 100644
index 0000000..c819554
--- /dev/null
+++ b/hb_server/apps/sam/migrations/0005_auto_20211203_1501.py
@@ -0,0 +1,28 @@
+# Generated by Django 3.2.9 on 2021-12-03 07:01
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('sam', '0004_order_planed_count'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='order',
+ name='delivered_count',
+ field=models.PositiveIntegerField(default=0, verbose_name='交货数量'),
+ ),
+ migrations.AlterField(
+ model_name='order',
+ name='count',
+ field=models.PositiveIntegerField(default=0, verbose_name='所需数量'),
+ ),
+ migrations.AlterField(
+ model_name='order',
+ name='planed_count',
+ field=models.PositiveIntegerField(default=0, verbose_name='已排数量'),
+ ),
+ ]
diff --git a/hb_server/apps/sam/migrations/0006_auto_20211206_0958.py b/hb_server/apps/sam/migrations/0006_auto_20211206_0958.py
new file mode 100644
index 0000000..9b99454
--- /dev/null
+++ b/hb_server/apps/sam/migrations/0006_auto_20211206_0958.py
@@ -0,0 +1,65 @@
+# Generated by Django 3.2.9 on 2021-12-06 01:58
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('inm', '0020_iproduct_is_saled'),
+ ('mtm', '0041_alter_material_type'),
+ ('sam', '0005_auto_20211203_1501'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='order',
+ name='delivered_count',
+ field=models.PositiveIntegerField(default=0, verbose_name='已交货数量'),
+ ),
+ migrations.AlterField(
+ model_name='order',
+ name='number',
+ field=models.CharField(max_length=100, unique=True, verbose_name='订单编号'),
+ ),
+ migrations.CreateModel(
+ name='Sale',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
+ ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
+ ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
+ ('count', models.PositiveIntegerField(default=0, verbose_name='交货数量')),
+ ('is_audited', models.BooleanField(default=False, verbose_name='是否审核')),
+ ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sale_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
+ ('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sam.customer', verbose_name='客户')),
+ ('order', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sam.order', verbose_name='关联订单')),
+ ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='所需产品')),
+ ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sale_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='SaleProduct',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
+ ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
+ ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
+ ('number', models.CharField(max_length=50, unique=True, verbose_name='物品编号')),
+ ('is_mtested', models.BooleanField(default=False, verbose_name='是否军检')),
+ ('is_mtestok', models.BooleanField(default=True, verbose_name='是否军检合格')),
+ ('iproduct', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sale_iproduct', to='inm.iproduct', verbose_name='关联库存产品')),
+ ('sale', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sam.sale', verbose_name='关联销售记录')),
+ ],
+ options={
+ 'unique_together': {('sale', 'iproduct')},
+ },
+ ),
+ ]
diff --git a/hb_server/apps/sam/migrations/0007_saleproduct_remark.py b/hb_server/apps/sam/migrations/0007_saleproduct_remark.py
new file mode 100644
index 0000000..846c3bc
--- /dev/null
+++ b/hb_server/apps/sam/migrations/0007_saleproduct_remark.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.9 on 2021-12-06 05:26
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('sam', '0006_auto_20211206_0958'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='saleproduct',
+ name='remark',
+ field=models.TextField(blank=True, null=True, verbose_name='备注'),
+ ),
+ ]
diff --git a/hb_server/apps/sam/migrations/0008_alter_saleproduct_number.py b/hb_server/apps/sam/migrations/0008_alter_saleproduct_number.py
new file mode 100644
index 0000000..72e87f2
--- /dev/null
+++ b/hb_server/apps/sam/migrations/0008_alter_saleproduct_number.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.9 on 2021-12-06 06:15
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('sam', '0007_saleproduct_remark'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='saleproduct',
+ name='number',
+ field=models.CharField(max_length=50, verbose_name='物品编号'),
+ ),
+ ]
diff --git a/hb_server/apps/sam/migrations/0009_alter_saleproduct_is_mtestok.py b/hb_server/apps/sam/migrations/0009_alter_saleproduct_is_mtestok.py
new file mode 100644
index 0000000..d034dfa
--- /dev/null
+++ b/hb_server/apps/sam/migrations/0009_alter_saleproduct_is_mtestok.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.9 on 2021-12-07 00:42
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('sam', '0008_alter_saleproduct_number'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='saleproduct',
+ name='is_mtestok',
+ field=models.BooleanField(blank=True, null=True, verbose_name='是否军检合格'),
+ ),
+ ]
diff --git a/hb_server/apps/sam/models.py b/hb_server/apps/sam/models.py
index 583fe87..e82137a 100644
--- a/hb_server/apps/sam/models.py
+++ b/hb_server/apps/sam/models.py
@@ -1,4 +1,4 @@
-from apps.system.models import CommonAModel
+from apps.system.models import CommonADModel, CommonAModel
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.db.models.base import Model
@@ -58,16 +58,45 @@ class Order(CommonAModel):
"""
订单信息
"""
- number = models.CharField('订单编号', max_length=100)
+ number = models.CharField('订单编号', max_length=100, unique=True)
customer = models.ForeignKey(Customer, verbose_name='客户', on_delete=models.CASCADE)
contract = models.ForeignKey(Contract, 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=0)
- planed_count = models.IntegerField('已排数量', default=0)
+ count = models.PositiveIntegerField('所需数量', default=0)
+ planed_count = models.PositiveIntegerField('已排数量', default=0)
+ delivered_count = models.PositiveIntegerField('已交货数量', default=0)
delivery_date = models.DateField('交货日期')
class Meta:
verbose_name = '订单信息'
verbose_name_plural = verbose_name
+
+
+class Sale(CommonADModel):
+ """
+ 销售记录
+ """
+ customer = models.ForeignKey(Customer, verbose_name='客户', on_delete=models.CASCADE)
+ order = models.ForeignKey(Order, verbose_name='关联订单', on_delete=models.CASCADE, null=True, blank=True)
+ product = models.ForeignKey(Material, verbose_name='所需产品', on_delete=models.CASCADE)
+ count = models.PositiveIntegerField('交货数量', default=0)
+ is_audited = models.BooleanField('是否审核', default=False)
+
+
+class SaleProduct(BaseModel):
+ """
+ 具体产品
+ """
+ sale = models.ForeignKey(Sale, verbose_name='关联销售记录', on_delete=models.CASCADE)
+ number = models.CharField('物品编号', max_length=50)
+ iproduct = models.ForeignKey('inm.iproduct', verbose_name='关联库存产品', on_delete=models.CASCADE, related_name='sale_iproduct')
+ is_mtested = models.BooleanField('是否军检', default=False)
+ is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True)
+ remark = models.TextField('备注', null=True, blank=True)
+
+ class Meta:
+ unique_together = (
+ ('sale','iproduct'), # 联合唯一
+ )
+
+
- def __str__(self):
- return self.name
\ No newline at end of file
diff --git a/hb_server/apps/sam/serializers.py b/hb_server/apps/sam/serializers.py
index a4ed099..eb3a840 100644
--- a/hb_server/apps/sam/serializers.py
+++ b/hb_server/apps/sam/serializers.py
@@ -1,6 +1,10 @@
-from rest_framework import serializers
+from django.db import transaction
+from rest_framework import exceptions, serializers
-from .models import Contract, Customer, Order
+from apps.inm.models import IProduct
+from apps.inm.serializers import IProductListSerializer
+
+from .models import Contract, Customer, Order, Sale, SaleProduct
from apps.mtm.serializers import MaterialSimpleSerializer
@@ -54,3 +58,52 @@ class OrderSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = '__all__'
+
+class SaleCreateSerializer(serializers.ModelSerializer):
+ iproducts = serializers.PrimaryKeyRelatedField(queryset=IProduct.objects.all(), many=True)
+ class Meta:
+ model = Sale
+ fields = ['customer', 'order', 'product', 'iproducts']
+
+ def validate(self, attrs):
+ order = attrs.get('order', None)
+ if order:
+ if order.customer:
+ attrs['customer'] = order.customer
+ attrs['product'] = order.product
+ for i in attrs['iproducts']:
+ if i.material is not attrs['product']:
+ raise exceptions.APIException('产品选取错误')
+ return super().validate(attrs)
+
+
+class SaleListSerializer(serializers.ModelSerializer):
+ customer_ = CustomerSimpleSerializer(source='customer', read_only=True)
+ order_ = OrderSimpleSerializer(source='order', read_only=True)
+ product_ = MaterialSimpleSerializer(source='product', read_only=True)
+ class Meta:
+ model = Sale
+ fields = '__all__'
+
+class SaleProductListSerializer(serializers.ModelSerializer):
+ iproduct_ = IProductListSerializer(source='iproduct', read_only=True)
+ class Meta:
+ model = SaleProduct
+ fields = '__all__'
+
+class SaleProductCreateSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = SaleProduct
+ fields = ['sale', 'iproduct']
+
+ def create(self, validated_data):
+ validated_data['number'] = validated_data['iproduct'].number
+ instance = SaleProduct.objects.create(**validated_data)
+ instance.sale.count = SaleProduct.objects.filter(sale=instance.sale).count()
+ instance.sale.save()
+ return instance
+
+class SaleProductMtestSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = SaleProduct
+ fields = ['remark', 'is_mtestok']
\ No newline at end of file
diff --git a/hb_server/apps/sam/urls.py b/hb_server/apps/sam/urls.py
index 4aa607f..7a66e01 100644
--- a/hb_server/apps/sam/urls.py
+++ b/hb_server/apps/sam/urls.py
@@ -1,6 +1,6 @@
from django.db.models import base
from rest_framework import urlpatterns
-from apps.sam.views import CustomerViewSet,ContractViewSet,OrderViewSet
+from apps.sam.views import CustomerViewSet,ContractViewSet,OrderViewSet, SaleProductViewSet, SaleViewSet
from django.urls import path, include
from rest_framework.routers import DefaultRouter
@@ -8,6 +8,8 @@ router = DefaultRouter()
router.register('customer', CustomerViewSet, basename='customer')
router.register('contract', ContractViewSet, basename='contract')
router.register('order', OrderViewSet, basename='order')
+router.register('sale', SaleViewSet, basename='sale')
+router.register('sale_product', SaleProductViewSet, basename='sale_product')
urlpatterns = [
path('', include(router.urls)),
diff --git a/hb_server/apps/sam/views.py b/hb_server/apps/sam/views.py
index 441e7fa..e3098ef 100644
--- a/hb_server/apps/sam/views.py
+++ b/hb_server/apps/sam/views.py
@@ -1,11 +1,19 @@
-from apps.sam.serializers import ContractCreateUpdateSerializer, ContractSerializer, CustomerCreateUpdateSerializer, CustomerSerializer, OrderCreateUpdateSerializer, OrderSerializer
-from apps.sam.models import Contract, Customer, Order
-from rest_framework.viewsets import ModelViewSet
+from django.db import transaction
+from django.db.models.aggregates import Count
+from rest_framework import exceptions, serializers
+from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin
+from apps.mtm.models import Material
+from apps.inm.models import FIFO, FIFOItem, FIFOItemProduct, IProduct, WareHouse
+from apps.inm.signals import update_inm
+from apps.sam.serializers import ContractCreateUpdateSerializer, ContractSerializer, CustomerCreateUpdateSerializer, CustomerSerializer, OrderCreateUpdateSerializer, OrderSerializer, SaleCreateSerializer, SaleListSerializer, SaleProductCreateSerializer, SaleProductListSerializer, SaleProductMtestSerializer
+from apps.sam.models import Contract, Customer, Order, Sale, SaleProduct
+from rest_framework.viewsets import GenericViewSet, ModelViewSet
from apps.system.mixins import CreateUpdateCustomMixin
from django.shortcuts import render
from rest_framework.decorators import action
from django.db.models import F
from rest_framework.response import Response
+from django.utils import timezone
# Create your views here.
class CustomerViewSet(CreateUpdateCustomMixin, ModelViewSet):
"""
@@ -57,7 +65,7 @@ class OrderViewSet(CreateUpdateCustomMixin, ModelViewSet):
def get_serializer_class(self):
if self.action in ['create', 'update']:
return OrderCreateUpdateSerializer
- return OrderSerializer
+ return super().get_serializer_class()
@action(methods=['get'], detail=False, perms_map={'get':'*'})
def toplan(self, request, pk=None):
@@ -67,4 +75,145 @@ class OrderViewSet(CreateUpdateCustomMixin, ModelViewSet):
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
- return Response(serializer.data)
\ No newline at end of file
+ return Response(serializer.data)
+
+
+class SaleViewSet(CreateUpdateCustomMixin, ListModelMixin, RetrieveModelMixin, CreateModelMixin, DestroyModelMixin, GenericViewSet):
+ """
+ 销售记录
+ """
+ perms_map = {'*': '*'}
+ queryset = Sale.objects.select_related('customer', 'order', 'product', 'order__contract').all()
+ serializer_class = SaleListSerializer
+ search_fields = ['customer__name', 'order__number']
+ filterset_fields = ['product', 'order', 'customer']
+ ordering_fields = ['create_time']
+ ordering = ['-create_time']
+
+ def get_serializer_class(self):
+ if self.action == 'create':
+ return SaleCreateSerializer
+ elif self.action == 'retrieve':
+ return SaleListSerializer
+ return super().get_serializer_class()
+
+ def create(self, request, *args, **kwargs):
+ data = request.data
+ serializer = SaleCreateSerializer(data=data)
+ serializer.is_valid(raise_exception=True)
+ vdata = serializer.validated_data
+ with transaction.atomic():
+ iproducts = vdata.pop('iproducts')
+ vdata['count'] = len(iproducts)
+ sale = Sale.objects.create(**vdata)
+ i_l = []
+ for i in iproducts:
+ i_d ={}
+ i_d['sale'] = sale
+ i_d['number'] = i.number
+ i_d['iproduct'] = i
+ i_l.append(SaleProduct(**i_d))
+ SaleProduct.objects.bulk_create(i_l)
+ return Response()
+
+ @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer)
+ @transaction.atomic
+ def audit(self, request, pk=None):
+ """
+ 审核
+ """
+ obj = self.get_object()
+ if obj.is_audited:
+ raise exceptions.APIException('已审核通过')
+ # 创建出库记录
+ fifo = FIFO()
+ fifo.type = FIFO.FIFO_TYPE_SALE_OUT
+ fifo.is_audited = True
+ fifo.auditor = request.user
+ fifo.inout_date = timezone.now()
+ fifo.create_by = request.user
+ fifo.save()
+ # 出库条目
+ spds = SaleProduct.objects.filter(sale=obj)
+ for i in spds:
+ if i.is_mtested and i.is_mtestok:
+ pass
+ else:
+ raise exceptions.APIException('存在未军检产品')
+ # 创建出库条目
+ ips = IProduct.objects.filter(sale_iproduct__sale=obj)
+ items = ips.values('warehouse', 'material', 'batch').annotate(total=Count('id'))
+ for i in items:
+ warehouse = WareHouse.objects.get(id=i['warehouse'])
+ material = Material.objects.get(id=i['material'])
+ fifoitem = FIFOItem()
+ fifoitem.is_tested = True
+ fifoitem.is_testok = True
+ fifoitem.warehouse = warehouse
+ fifoitem.material = material
+ fifoitem.count = i['total']
+ fifoitem.batch = i['batch']
+ fifoitem.fifo = fifo
+ fifoitem.save()
+ items_p = ips.filter(warehouse=warehouse, batch=i['batch'])
+ ipxs = []
+ for i in items_p:
+ # 创建出库明细半成品
+ ip = {}
+ ip['fifoitem'] = fifoitem
+ ip['number'] = i.number
+ ip['material'] = i.material
+ ip['iproduct'] = i
+ ipxs.append(FIFOItemProduct(**ip))
+ FIFOItemProduct.objects.bulk_create(ipxs)
+ # 更新成品库情况
+ ips.update(is_saled=True)
+ # 更新库存
+ update_inm(fifo)
+ # 变更审核状态
+ obj.is_audited = True
+ obj.save()
+ # 变更订单状态
+ if obj.order:
+ order = obj.order
+ order.delivered_count = order.delivered_count + obj.count
+ order.save()
+ return Response()
+
+class SaleProductViewSet(ListModelMixin, DestroyModelMixin, CreateModelMixin, GenericViewSet):
+ """
+ 销售记录关联产品
+ """
+ perms_map = {'*': '*'}
+ queryset = SaleProduct.objects.select_related('iproduct', 'iproduct__material', 'iproduct__warehouse').all()
+ serializer_class = SaleProductListSerializer
+ search_fields = []
+ filterset_fields = ['sale', 'iproduct']
+ ordering_fields = ['create_time']
+ ordering = ['id']
+
+ def get_serializer_class(self):
+ if self.action == 'create':
+ return SaleProductCreateSerializer
+ return super().get_serializer_class()
+
+ def destroy(self, request, *args, **kwargs):
+ obj = self.get_object()
+ obj.sale.count = SaleProduct.objects.filter(sale=obj.sale).count()
+ obj.sale.save()
+ obj.delete()
+ return Response()
+
+ @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=SaleProductMtestSerializer)
+ def mtest(self, request, pk=None):
+ """
+ 军检
+ """
+ obj = self.get_object()
+ if obj.is_mtested:
+ raise exceptions.APIException('已进行军检')
+ obj.remark = request.data.get('remark', None)
+ obj.is_mtested = True
+ obj.is_mtestok = request.data.get('is_mtestok')
+ obj.save()
+ return Response()
\ No newline at end of file
diff --git a/hb_server/apps/wf/migrations/0017_auto_20211203_1501.py b/hb_server/apps/wf/migrations/0017_auto_20211203_1501.py
new file mode 100644
index 0000000..f5b3766
--- /dev/null
+++ b/hb_server/apps/wf/migrations/0017_auto_20211203_1501.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.2.9 on 2021-12-03 07:01
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('wf', '0016_auto_20211024_2349'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='customfield',
+ name='field_type',
+ field=models.CharField(choices=[('string', '字符串'), ('int', '整型'), ('float', '浮点'), ('boolean', '布尔'), ('date', '日期'), ('datetime', '日期时间'), ('radio', '单选'), ('checkbox', '多选'), ('select', '单选下拉'), ('selects', '多选下拉'), ('textarea', '文本域'), ('selectuser', '单选用户'), ('selectusers', '多选用户'), ('file', '附件'), ('draw', '绘图')], help_text='5.字符串,10.整形,15.浮点型,20.布尔,25.日期,30.日期时间,35.单选框,40.多选框,45.下拉列表,50.多选下拉列表,55.文本域,60.用户名, 70.多选的用户名, 80.附件(只保存路径,多个使用逗号隔开)', max_length=50, verbose_name='类型'),
+ ),
+ migrations.AlterField(
+ model_name='ticket',
+ name='ticket_data',
+ field=models.JSONField(default=dict, help_text='工单自定义字段内容', verbose_name='工单数据'),
+ ),
+ ]
diff --git a/hb_server/apps/wf/models.py b/hb_server/apps/wf/models.py
index a9ae071..bc1597c 100644
--- a/hb_server/apps/wf/models.py
+++ b/hb_server/apps/wf/models.py
@@ -152,7 +152,8 @@ class CustomField(CommonAModel):
('textarea', '文本域'),
('selectuser', '单选用户'),
('selectusers', '多选用户'),
- ('file', '附件')
+ ('file', '附件'),
+ ('draw', '绘图')
)
workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流')
field_type = models.CharField('类型', max_length=50, choices=field_type_choices, help_text='5.字符串,10.整形,15.浮点型,20.布尔,25.日期,30.日期时间,35.单选框,40.多选框,45.下拉列表,50.多选下拉列表,55.文本域,60.用户名, 70.多选的用户名, 80.附件(只保存路径,多个使用逗号隔开)')
@@ -201,7 +202,7 @@ class Ticket(CommonBModel):
state = models.ForeignKey(State, on_delete=models.CASCADE, verbose_name='当前状态', related_name='ticket_state')
parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE, verbose_name='父工单')
parent_state = models.ForeignKey(State, null=True, blank=True, on_delete=models.CASCADE, verbose_name='父工单状态', related_name='ticket_parent_state')
- ticket_data = models.JSONField('工单数据', default=dict, help_text='工单所有字段内容')
+ ticket_data = models.JSONField('工单数据', default=dict, help_text='工单自定义字段内容')
in_add_node = models.BooleanField('加签状态中', default=False, help_text='是否处于加签状态下')
add_node_man = models.ForeignKey(User, verbose_name='加签人', on_delete=models.SET_NULL, null=True, blank=True, help_text='加签操作的人,工单当前处理人处理完成后会回到该处理人,当处于加签状态下才有效')
@@ -211,16 +212,17 @@ class Ticket(CommonBModel):
multi_all_person = models.JSONField('全部处理的结果', default=dict, blank=True, help_text='需要当前状态处理人全部处理时实际的处理结果,json格式')
-class TicketData():
- """
- 工单数据,自定义字段值
- """
- 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 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/views.py b/hb_server/apps/wpm/views.py
index 9d3b427..40b4b93 100644
--- a/hb_server/apps/wpm/views.py
+++ b/hb_server/apps/wpm/views.py
@@ -58,7 +58,7 @@ class WPlanViewSet(ListModelMixin, GenericViewSet):
elif request.method=='POST':
serializer= PickHalfsSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
- vdata = serializer.data
+ vdata = serializer.validated_data
first_step = Step.objects.get(pk=sp.steps[0]['id'])
# 创建领料记录
pick = Pick()
@@ -68,12 +68,12 @@ class WPlanViewSet(ListModelMixin, GenericViewSet):
pick.save()
for i in vdata:
if 'wproducts' in i and len(i['wproducts'])>0:
- spp = SubProductionProgress.objects.get(pk=i['id'])
+ spp = i['id']
spp.count_pick = spp.count_pick + len(i['wproducts'])
# if spp.count_pick > spp.count:
# raise exceptions.APIException('超过计划数')
spp.save()
- wps = WProduct.objects.filter(pk__in=[x for x in i['wproducts']], act_state=WProduct.WPR_ACT_STATE_OK)
+ wps = WProduct.objects.filter(pk__in=[x.id for x in i['wproducts']], act_state=WProduct.WPR_ACT_STATE_OK)
wps.update(step=first_step,
act_state=WProduct.WPR_ACT_STATE_TORETEST, is_hidden=False, warehouse=None,
subproduction_plan=sp, update_by=request.user, update_time=timezone.now())
@@ -99,11 +99,11 @@ class WPlanViewSet(ListModelMixin, GenericViewSet):
"""
serializer= WplanPutInSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
- vdata = serializer.data
+ vdata = serializer.validated_data
subplan = self.get_object()
material = subplan.main_product
batch = subplan.number
- warehouse = WareHouse.objects.get(id=vdata['warehouse'])
+ warehouse = vdata['warehouse']
wproducts = WProduct.objects.filter(subproduction_plan=subplan,
act_state=WProduct.WPR_ACT_STATE_OK, material=material, is_deleted=False)
if wproducts.exists():
@@ -253,9 +253,9 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
"""
serializer= WproductPutInsSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
- vdata = serializer.data
- wproducts = WProduct.objects.filter(pk__in=[x for x in vdata['wproducts']])
- warehouse = WareHouse.objects.get(id=vdata['warehouse'])
+ vdata = serializer.validated_data
+ wproducts = WProduct.objects.filter(pk__in=[x.id for x in vdata['wproducts']])
+ warehouse = vdata['warehouse']
for i in wproducts:
if i.act_state is not WProduct.WPR_ACT_STATE_OK:
raise exceptions.APIException('存在不可入库半成品')
@@ -267,12 +267,12 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
is_audited=True, auditor=request.user, inout_date=timezone.now(), create_by=request.user, remark=remark)
# 创建入库明细
for i in wproducts_a:
- spi = SubProductionPlan.objects.get(pk=i['subproduction_plan'])
+ spi = i['subproduction_plan']
fifoitem = FIFOItem()
fifoitem.is_tested = True
fifoitem.is_testok = True
fifoitem.warehouse = warehouse
- fifoitem.material = Material.objects.get(pk=i['material'])
+ fifoitem.material = i['material']
fifoitem.count = i['total']
fifoitem.batch = spi.number
fifoitem.fifo = fifo
@@ -303,9 +303,9 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
"""
serializer= WproductPutInSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
- vdata = serializer.data
+ vdata = serializer.validated_data
wproduct = self.get_object()
- warehouse = WareHouse.objects.get(id=vdata['warehouse'])
+ warehouse = vdata['warehouse']
if wproduct.act_state != WProduct.WPR_ACT_STATE_OK:
raise exceptions.APIException('半成品不可入库')
material = wproduct.material
@@ -598,11 +598,11 @@ class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
def submit(self, request, pk=None):
serializer = OperationRecordSubmitSerializer(data=request.data, context={'request':self.request})
serializer.is_valid(raise_exception=True)
- vdata = serializer.data
+ vdata = serializer.validated_data
opr = self.get_object()
wrds = []
for m in vdata['record_data']: # 保存记录详情
- form_field = RecordFormField.objects.get(pk=m['form_field'])
+ form_field = m['form_field']
m['form_field'] = form_field
m['field_name'] = form_field.field_name
m['field_key'] = form_field.field_key