Compare commits
50 Commits
Author | SHA1 | Date |
---|---|---|
|
db22300fd0 | |
|
962ca5b1be | |
|
283c566b71 | |
|
d6504fa072 | |
|
5857b77aea | |
|
dd0d6751c1 | |
|
a13ac84ccb | |
|
21e9a49069 | |
|
d815d84333 | |
|
0a5cbca4ed | |
|
ee7908f6bc | |
|
f4bb1d952f | |
|
f501f2a6ea | |
|
e47c853578 | |
|
9362a81bc6 | |
|
5498d98e38 | |
|
a31aa7e337 | |
|
a97804e455 | |
|
2f9e054558 | |
|
29e15f0f8e | |
|
cafa1c9c87 | |
|
bf368bcd26 | |
|
b9372204a6 | |
|
9ad947770a | |
|
1623d2d684 | |
|
4a5f6d9d0e | |
|
b948688ab9 | |
|
91a499c00b | |
|
3b78c4e993 | |
|
c8ce78f50d | |
|
b5514afa2b | |
|
04daccb733 | |
|
e8cd841ef1 | |
|
2831c9b58b | |
|
3175bcf4dc | |
|
5354c064a1 | |
|
6e9b2c8264 | |
|
6aae02044f | |
|
4bccc5a62b | |
|
da5400996f | |
|
0b20199284 | |
|
88bc901c84 | |
|
87b935ab04 | |
|
c3108641f3 | |
|
7b1a6853ab | |
|
5ae9e235df | |
|
b327da2342 | |
|
a581d40ef9 | |
|
3a263735b0 | |
|
4cc8236bcb |
|
@ -20,6 +20,8 @@ db.sqlite3
|
||||||
server/conf*.py
|
server/conf*.py
|
||||||
server/conf.ini
|
server/conf.ini
|
||||||
server/conf*.json
|
server/conf*.json
|
||||||
|
config/conf*.py
|
||||||
|
config/conf*.json
|
||||||
sh/*
|
sh/*
|
||||||
temp/*
|
temp/*
|
||||||
nohup.out
|
nohup.out
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.urls import path, include
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
API_BASE_URL = 'api/am/'
|
API_BASE_URL = 'api/am/'
|
||||||
HTML_BASE_URL = 'am/'
|
HTML_BASE_URL = 'dhtml/am/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('area', AreaViewSet, basename='area')
|
router.register('area', AreaViewSet, basename='area')
|
||||||
|
|
|
@ -4,7 +4,7 @@ from django.utils import timezone
|
||||||
from apps.bi.models import Dataset, DatasetRecord
|
from apps.bi.models import Dataset, DatasetRecord
|
||||||
from apps.bi.services import exec_dataset
|
from apps.bi.services import exec_dataset
|
||||||
import json
|
import json
|
||||||
|
from apps.utils.tools import MyJSONEncoder
|
||||||
|
|
||||||
@shared_task()
|
@shared_task()
|
||||||
def exec_dataset_and_store(code: str, query: str = ''):
|
def exec_dataset_and_store(code: str, query: str = ''):
|
||||||
|
@ -16,4 +16,5 @@ def exec_dataset_and_store(code: str, query: str = ''):
|
||||||
if query:
|
if query:
|
||||||
squery = json.loads(query)
|
squery = json.loads(query)
|
||||||
dtr.full_sql, dtr.result = exec_dataset(dt, squery)
|
dtr.full_sql, dtr.result = exec_dataset(dt, squery)
|
||||||
|
dtr.result = json.loads(json.dumps(dtr.result, cls=MyJSONEncoder))
|
||||||
dtr.save()
|
dtr.save()
|
||||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
||||||
from apps.bi.views import DatasetViewSet, DatasetRecordViewSet
|
from apps.bi.views import DatasetViewSet, DatasetRecordViewSet
|
||||||
|
|
||||||
API_BASE_URL = 'api/bi/'
|
API_BASE_URL = 'api/bi/'
|
||||||
HTML_BASE_URL = 'bi/'
|
HTML_BASE_URL = 'dhtml/bi/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('dataset', DatasetViewSet, basename='dataset')
|
router.register('dataset', DatasetViewSet, basename='dataset')
|
||||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
||||||
from apps.cm.views import LableMatViewSet
|
from apps.cm.views import LableMatViewSet
|
||||||
|
|
||||||
API_BASE_URL = 'api/cm/'
|
API_BASE_URL = 'api/cm/'
|
||||||
HTML_BASE_URL = 'cm/'
|
HTML_BASE_URL = 'dhtml/cm/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('labelmat', LableMatViewSet, basename='labelmat')
|
router.register('labelmat', LableMatViewSet, basename='labelmat')
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.urls import path, include
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
API_BASE_URL = 'api/cms/'
|
API_BASE_URL = 'api/cms/'
|
||||||
HTML_BASE_URL = 'cms/'
|
HTML_BASE_URL = 'dhtml/cms/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('article', ArticleViewSet, basename='article')
|
router.register('article', ArticleViewSet, basename='article')
|
||||||
|
|
|
@ -3,7 +3,7 @@ from apps.develop.views import BackupDatabase, BackupMedia, ReloadClientGit, Rel
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
API_BASE_URL = 'api/develop/'
|
API_BASE_URL = 'api/develop/'
|
||||||
HTML_BASE_URL = 'develop/'
|
HTML_BASE_URL = 'dhtml/develop/'
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('test', TestViewSet, basename='api_test')
|
router.register('test', TestViewSet, basename='api_test')
|
||||||
router.register('correct', CorrectViewSet, basename='correct')
|
router.register('correct', CorrectViewSet, basename='correct')
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.urls import path, include
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
API_BASE_URL = 'api/dpm/'
|
API_BASE_URL = 'api/dpm/'
|
||||||
HTML_BASE_URL = 'dpm/'
|
HTML_BASE_URL = 'dhtml/dpm/'
|
||||||
# cache_areas_info()
|
# cache_areas_info()
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('riskpoint', RiskPointViewSet, basename='riskpoint')
|
router.register('riskpoint', RiskPointViewSet, basename='riskpoint')
|
||||||
|
|
|
@ -4,7 +4,7 @@ from django.urls import path, include
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
API_BASE_URL = 'api/ecm/'
|
API_BASE_URL = 'api/ecm/'
|
||||||
HTML_BASE_URL = 'ecm/'
|
HTML_BASE_URL = 'dhtml/ecm/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('event_cate', EventCateViewSet, basename='event_cate')
|
router.register('event_cate', EventCateViewSet, basename='event_cate')
|
||||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
||||||
from apps.edu.views import QuestioncatViewSet, QuestionViewSet, PaperViewSet, ExamViewSet, ExamRecordViewSet, TrainRecordViewSet
|
from apps.edu.views import QuestioncatViewSet, QuestionViewSet, PaperViewSet, ExamViewSet, ExamRecordViewSet, TrainRecordViewSet
|
||||||
|
|
||||||
API_BASE_URL = 'api/edu/'
|
API_BASE_URL = 'api/edu/'
|
||||||
HTML_BASE_URL = 'edu/'
|
HTML_BASE_URL = 'dhtml/edu/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('questioncat', QuestioncatViewSet, basename='questioncat')
|
router.register('questioncat', QuestioncatViewSet, basename='questioncat')
|
||||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
||||||
from apps.em.views import EquipmentViewSet, EcheckRecordViewSet, EInspectViewSet, EcateViewSet, CdView
|
from apps.em.views import EquipmentViewSet, EcheckRecordViewSet, EInspectViewSet, EcateViewSet, CdView
|
||||||
|
|
||||||
API_BASE_URL = 'api/em/'
|
API_BASE_URL = 'api/em/'
|
||||||
HTML_BASE_URL = 'em/'
|
HTML_BASE_URL = 'dhtml/em/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('equipment', EquipmentViewSet, basename='equipment')
|
router.register('equipment', EquipmentViewSet, basename='equipment')
|
||||||
|
|
|
@ -4,7 +4,7 @@ from apps.enm.views import (MpointViewSet, MpLogxViewSet, MpointStatViewSet,
|
||||||
EnStatViewSet, EnStat2ViewSet, XscriptViewSet)
|
EnStatViewSet, EnStat2ViewSet, XscriptViewSet)
|
||||||
|
|
||||||
API_BASE_URL = 'api/enm/'
|
API_BASE_URL = 'api/enm/'
|
||||||
HTML_BASE_URL = 'enm/'
|
HTML_BASE_URL = 'dhtml/enm/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('mpoint', MpointViewSet, basename='mpoint')
|
router.register('mpoint', MpointViewSet, basename='mpoint')
|
||||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
||||||
from .views import DrainViewSet, DrainEquipViewSet, VehicleAccessViewSet, EnvDataViewSet, CarWashViewSet
|
from .views import DrainViewSet, DrainEquipViewSet, VehicleAccessViewSet, EnvDataViewSet, CarWashViewSet
|
||||||
|
|
||||||
API_BASE_URL = 'api/enp/'
|
API_BASE_URL = 'api/enp/'
|
||||||
HTML_BASE_URL = 'enp/'
|
HTML_BASE_URL = 'dhtml/enp/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('drain', DrainViewSet, basename='drain')
|
router.register('drain', DrainViewSet, basename='drain')
|
||||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
||||||
from apps.fim.views import (PriceSetViewSet, FeeSetViewSet, FeeViewSet)
|
from apps.fim.views import (PriceSetViewSet, FeeSetViewSet, FeeViewSet)
|
||||||
|
|
||||||
API_BASE_URL = 'api/fim/'
|
API_BASE_URL = 'api/fim/'
|
||||||
HTML_BASE_URL = 'fim/'
|
HTML_BASE_URL = 'dhtml/fim/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('priceset', PriceSetViewSet, basename='priceset')
|
router.register('priceset', PriceSetViewSet, basename='priceset')
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.urls import path, include
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
API_BASE_URL = 'api/hrm/'
|
API_BASE_URL = 'api/hrm/'
|
||||||
HTML_BASE_URL = 'hrm/'
|
HTML_BASE_URL = 'dhtml/hrm/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('employee', EmployeeViewSet, basename='employee')
|
router.register('employee', EmployeeViewSet, basename='employee')
|
||||||
|
|
|
@ -226,7 +226,7 @@ class MIOItemSerializer(CustomModelSerializer):
|
||||||
|
|
||||||
class MIODoSerializer(CustomModelSerializer):
|
class MIODoSerializer(CustomModelSerializer):
|
||||||
belong_dept = serializers.PrimaryKeyRelatedField(
|
belong_dept = serializers.PrimaryKeyRelatedField(
|
||||||
label="车间", queryset=Dept.objects.all(), required=True)
|
label="车间", queryset=Dept.objects.all(), required=False)
|
||||||
do_user = serializers.PrimaryKeyRelatedField(
|
do_user = serializers.PrimaryKeyRelatedField(
|
||||||
label="执行人", queryset=User.objects.all(), required=True)
|
label="执行人", queryset=User.objects.all(), required=True)
|
||||||
note = serializers.CharField(
|
note = serializers.CharField(
|
||||||
|
@ -236,16 +236,17 @@ class MIODoSerializer(CustomModelSerializer):
|
||||||
model = MIO
|
model = MIO
|
||||||
fields = ['id', 'number', 'note', 'do_user',
|
fields = ['id', 'number', 'note', 'do_user',
|
||||||
'belong_dept', 'type', 'inout_date', 'mgroup', 'mio_user']
|
'belong_dept', 'type', 'inout_date', 'mgroup', 'mio_user']
|
||||||
extra_kwargs = {'inout_date': {'required': True},
|
extra_kwargs = {'inout_date': {'required': True}, 'do_user': {'required': True}, 'number': {"required": False, "allow_blank": True}}
|
||||||
'do_user': {'required': True}, 'belong_dept': {'required': False}}
|
|
||||||
read_only_fields = ["number"]
|
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
if 'mgroup' in attrs and attrs['mgroup']:
|
if 'mgroup' in attrs and attrs['mgroup']:
|
||||||
attrs['belong_dept'] = attrs['mgroup'].belong_dept
|
attrs['belong_dept'] = attrs['mgroup'].belong_dept
|
||||||
|
if not attrs.get("belong_dept", None):
|
||||||
|
raise ParseError('请选择车间或工段')
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
|
if not validated_data.get("number", None):
|
||||||
validated_data["number"] = MIO.get_a_number(validated_data["type"])
|
validated_data["number"] = MIO.get_a_number(validated_data["type"])
|
||||||
if validated_data['type'] not in [MIO.MIO_TYPE_DO_OUT, MIO.MIO_TYPE_DO_IN]:
|
if validated_data['type'] not in [MIO.MIO_TYPE_DO_OUT, MIO.MIO_TYPE_DO_IN]:
|
||||||
raise ValidationError('出入库类型错误')
|
raise ValidationError('出入库类型错误')
|
||||||
|
@ -265,11 +266,13 @@ class MIOSaleSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = MIO
|
model = MIO
|
||||||
fields = ['id', 'number', 'note', 'order', 'inout_date', 'customer', 'mio_user']
|
fields = ['id', 'number', 'note', 'order', 'inout_date', 'customer', 'mio_user']
|
||||||
extra_kwargs = {'inout_date': {'required': True}}
|
extra_kwargs = {'inout_date': {'required': True}, 'number': {"required": False, "allow_blank": True}}
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
validated_data['type'] = MIO.MIO_TYPE_SALE_OUT
|
validated_data['type'] = MIO.MIO_TYPE_SALE_OUT
|
||||||
order: Order = validated_data.get('order', None)
|
order: Order = validated_data.get('order', None)
|
||||||
|
if not validated_data.get("number", None):
|
||||||
|
validated_data["number"] = MIO.get_a_number(validated_data["type"])
|
||||||
if order:
|
if order:
|
||||||
if order.state in [Order.ORDER_CREATE, Order.ORDER_DELIVERED]:
|
if order.state in [Order.ORDER_CREATE, Order.ORDER_DELIVERED]:
|
||||||
raise ValidationError('销售订单状态错误')
|
raise ValidationError('销售订单状态错误')
|
||||||
|
@ -292,12 +295,12 @@ class MIOPurSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = MIO
|
model = MIO
|
||||||
fields = ['id', 'number', 'note', 'pu_order', 'inout_date', 'supplier', 'mio_user']
|
fields = ['id', 'number', 'note', 'pu_order', 'inout_date', 'supplier', 'mio_user']
|
||||||
extra_kwargs = {'inout_date': {'required': True}}
|
extra_kwargs = {'inout_date': {'required': True}, 'number': {"required": False, "allow_blank": True}}
|
||||||
read_only_fields = ["number"]
|
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
validated_data['type'] = MIO.MIO_TYPE_PUR_IN
|
validated_data['type'] = MIO.MIO_TYPE_PUR_IN
|
||||||
validated_data['number'] = MIO.get_a_number(validated_data["type"])
|
if not validated_data.get("number", None):
|
||||||
|
validated_data["number"] = MIO.get_a_number(validated_data["type"])
|
||||||
pu_order: PuOrder = validated_data.get('pu_order', None)
|
pu_order: PuOrder = validated_data.get('pu_order', None)
|
||||||
if pu_order:
|
if pu_order:
|
||||||
if pu_order.state in [PuOrder.PUORDER_CREATE, PuOrder.PUORDER_DONE]:
|
if pu_order.state in [PuOrder.PUORDER_CREATE, PuOrder.PUORDER_DONE]:
|
||||||
|
@ -320,11 +323,11 @@ class MIOOtherSerializer(CustomModelSerializer):
|
||||||
model = MIO
|
model = MIO
|
||||||
fields = ['id', 'number', 'note', 'supplier',
|
fields = ['id', 'number', 'note', 'supplier',
|
||||||
'customer', 'type', 'inout_date', 'mio_user']
|
'customer', 'type', 'inout_date', 'mio_user']
|
||||||
extra_kwargs = {'inout_date': {'required': True}}
|
extra_kwargs = {'inout_date': {'required': True}, 'number': {"required": False, "allow_blank": True}}
|
||||||
read_only_fields = ["number"]
|
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
validated_data['number'] = MIO.get_a_number(validated_data["type"])
|
if not validated_data.get("number", None):
|
||||||
|
validated_data["number"] = MIO.get_a_number(validated_data["type"])
|
||||||
if validated_data['type'] not in [MIO.MIO_TYPE_OTHER_OUT, MIO.MIO_TYPE_OTHER_IN]:
|
if validated_data['type'] not in [MIO.MIO_TYPE_OTHER_OUT, MIO.MIO_TYPE_OTHER_IN]:
|
||||||
raise ValidationError('出入库类型错误')
|
raise ValidationError('出入库类型错误')
|
||||||
return super().create(validated_data)
|
return super().create(validated_data)
|
||||||
|
|
|
@ -58,6 +58,8 @@ def do_out(item: MIOItem):
|
||||||
raise ParseError("组合件暂不支持追踪单件")
|
raise ParseError("组合件暂不支持追踪单件")
|
||||||
|
|
||||||
xbatches = []
|
xbatches = []
|
||||||
|
if is_zhj:
|
||||||
|
xbatches = [item.batch]
|
||||||
for al in action_list:
|
for al in action_list:
|
||||||
xmaterial:Material = al[0]
|
xmaterial:Material = al[0]
|
||||||
xbatch:str = al[1]
|
xbatch:str = al[1]
|
||||||
|
@ -138,6 +140,8 @@ def do_in(item: MIOItem):
|
||||||
production_dept = None
|
production_dept = None
|
||||||
|
|
||||||
xbatchs = []
|
xbatchs = []
|
||||||
|
if is_zhj:
|
||||||
|
xbatchs = [item.batch]
|
||||||
for al in action_list:
|
for al in action_list:
|
||||||
xmaterial, xbatch, xcount = al
|
xmaterial, xbatch, xcount = al
|
||||||
xbatchs.append(xbatch)
|
xbatchs.append(xbatch)
|
||||||
|
@ -165,7 +169,7 @@ def do_in(item: MIOItem):
|
||||||
else:
|
else:
|
||||||
raise ParseError(f'{str(xmaterial)}-{xbatch}车间物料不足')
|
raise ParseError(f'{str(xmaterial)}-{xbatch}车间物料不足')
|
||||||
|
|
||||||
wm_production_dept = wm.mgroup.belong_dept if wm.mgroup else None
|
wm_production_dept = wm.mgroup.belong_dept if wm.mgroup else wm.belong_dept
|
||||||
if production_dept is None:
|
if production_dept is None:
|
||||||
production_dept = wm_production_dept
|
production_dept = wm_production_dept
|
||||||
elif wm_production_dept and production_dept != wm_production_dept:
|
elif wm_production_dept and production_dept != wm_production_dept:
|
||||||
|
@ -185,6 +189,8 @@ def do_in(item: MIOItem):
|
||||||
"production_dept": production_dept
|
"production_dept": production_dept
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if mb.production_dept is None:
|
||||||
|
mb.production_dept = production_dept
|
||||||
mb.count = mb.count + xcount
|
mb.count = mb.count + xcount
|
||||||
mb.save()
|
mb.save()
|
||||||
|
|
||||||
|
@ -206,6 +212,10 @@ def do_in(item: MIOItem):
|
||||||
)
|
)
|
||||||
if not is_created:
|
if not is_created:
|
||||||
raise ParseError("该批次组合件已存在")
|
raise ParseError("该批次组合件已存在")
|
||||||
|
if mb.production_dept is None:
|
||||||
|
mb.production_dept = production_dept
|
||||||
|
mb.count = mb.count + item.count
|
||||||
|
mb.save()
|
||||||
for mia in mias:
|
for mia in mias:
|
||||||
MaterialBatchA.objects.create(mb=mb, material=mia.material, batch=mia.batch, rate=mia.rate)
|
MaterialBatchA.objects.create(mb=mb, material=mia.material, batch=mia.batch, rate=mia.rate)
|
||||||
|
|
||||||
|
@ -222,8 +232,9 @@ class InmService:
|
||||||
更新物料数量
|
更新物料数量
|
||||||
"""
|
"""
|
||||||
# 统计物料数量
|
# 统计物料数量
|
||||||
m_ids = MIOItem.objects.filter(mio=instance).values_list('material_id', flat=True)
|
m_ids = list(MIOItem.objects.filter(mio=instance).values_list('material_id', flat=True))
|
||||||
cal_material_count(m_ids)
|
m_ids2 = list(MIOItemA.objects.filter(mioitem__mio=instance).values_list('material_id', flat=True))
|
||||||
|
cal_material_count(m_ids+m_ids2)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_inm(cls, instance: MIO, is_reverse: bool = False):
|
def update_inm(cls, instance: MIO, is_reverse: bool = False):
|
||||||
|
@ -251,10 +262,16 @@ class InmService:
|
||||||
do_in(item)
|
do_in(item)
|
||||||
elif instance.type == MIO.MIO_TYPE_SALE_OUT:
|
elif instance.type == MIO.MIO_TYPE_SALE_OUT:
|
||||||
from apps.sam.services import SamService
|
from apps.sam.services import SamService
|
||||||
cls.update_mb(instance, in_or_out)
|
if is_reverse:
|
||||||
|
cls.update_mb(instance, 1)
|
||||||
|
else:
|
||||||
|
cls.update_mb(instance, -1)
|
||||||
SamService.mio_saleout(instance, is_reverse)
|
SamService.mio_saleout(instance, is_reverse)
|
||||||
elif instance.type == MIO.MIO_TYPE_OTHER_OUT:
|
elif instance.type == MIO.MIO_TYPE_OTHER_OUT:
|
||||||
cls.update_mb(instance, in_or_out)
|
if is_reverse:
|
||||||
|
cls.update_mb(instance, 1)
|
||||||
|
else:
|
||||||
|
cls.update_mb(instance, -1)
|
||||||
elif instance.type == MIO.MIO_TYPE_DO_OUT:
|
elif instance.type == MIO.MIO_TYPE_DO_OUT:
|
||||||
mioitems = MIOItem.objects.filter(mio=instance)
|
mioitems = MIOItem.objects.filter(mio=instance)
|
||||||
if is_reverse:
|
if is_reverse:
|
||||||
|
@ -319,7 +336,10 @@ class InmService:
|
||||||
if xcount > 0:
|
if xcount > 0:
|
||||||
defect = defects_map[defect_id]
|
defect = defects_map[defect_id]
|
||||||
m_list.append((material, warehouse, i.batch, xcount, defect, i))
|
m_list.append((material, warehouse, i.batch, xcount, defect, i))
|
||||||
|
|
||||||
|
xbatchs = []
|
||||||
for material, warehouse, batch, change_count, defect, mioitem in m_list:
|
for material, warehouse, batch, change_count, defect, mioitem in m_list:
|
||||||
|
xbatchs.append(batch)
|
||||||
if change_count <= 0:
|
if change_count <= 0:
|
||||||
continue
|
continue
|
||||||
state = WMaterial.WM_OK
|
state = WMaterial.WM_OK
|
||||||
|
@ -370,6 +390,10 @@ class InmService:
|
||||||
else:
|
else:
|
||||||
raise ParseError("不支持的操作")
|
raise ParseError("不支持的操作")
|
||||||
|
|
||||||
|
# 批次统计分析
|
||||||
|
xbatchs = list(set(xbatchs))
|
||||||
|
for xbatch in xbatchs:
|
||||||
|
MyThread(target=get_alldata_with_batch_and_store, args=(xbatch,)).start()
|
||||||
|
|
||||||
def daoru_mb(path: str):
|
def daoru_mb(path: str):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -6,7 +6,7 @@ from apps.inm.views import (
|
||||||
MioPurViewSet, MioOtherViewSet, MIOItemwViewSet)
|
MioPurViewSet, MioOtherViewSet, MIOItemwViewSet)
|
||||||
|
|
||||||
API_BASE_URL = 'api/inm/'
|
API_BASE_URL = 'api/inm/'
|
||||||
HTML_BASE_URL = 'inm/'
|
HTML_BASE_URL = 'dhtml/inm/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('warehouse', WarehouseVIewSet, basename='warehouse')
|
router.register('warehouse', WarehouseVIewSet, basename='warehouse')
|
||||||
|
|
|
@ -254,6 +254,7 @@ class MIOItemViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyMode
|
||||||
"test_date": ["isnull", "exact"]
|
"test_date": ["isnull", "exact"]
|
||||||
}
|
}
|
||||||
ordering = ['create_time']
|
ordering = ['create_time']
|
||||||
|
ordering_fields = ['create_time', 'test_date']
|
||||||
|
|
||||||
def add_info_for_list(self, data):
|
def add_info_for_list(self, data):
|
||||||
|
|
||||||
|
@ -286,6 +287,7 @@ class MIOItemViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyMode
|
||||||
sr.save()
|
sr.save()
|
||||||
# 开始变动库存
|
# 开始变动库存
|
||||||
InmService.update_mb_item(ins, -1, 'count_notok')
|
InmService.update_mb_item(ins, -1, 'count_notok')
|
||||||
|
InmService.update_material_count(ins.mio)
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
@action(methods=['post'], detail=True, perms_map={'post': 'mioitem.test'}, serializer_class=serializers.Serializer)
|
@action(methods=['post'], detail=True, perms_map={'post': 'mioitem.test'}, serializer_class=serializers.Serializer)
|
||||||
|
@ -303,6 +305,7 @@ class MIOItemViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyMode
|
||||||
pass
|
pass
|
||||||
ins.test_date = None
|
ins.test_date = None
|
||||||
ins.save()
|
ins.save()
|
||||||
|
InmService.update_material_count(ins.mio)
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
@action(methods=['post'], detail=True, perms_map={'post': 'mioitem.test'}, serializer_class=MIOItemPurInTestSerializer)
|
@action(methods=['post'], detail=True, perms_map={'post': 'mioitem.test'}, serializer_class=MIOItemPurInTestSerializer)
|
||||||
|
@ -320,6 +323,7 @@ class MIOItemViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyMode
|
||||||
sr = MIOItemPurInTestSerializer(instance=ins, data=request.data)
|
sr = MIOItemPurInTestSerializer(instance=ins, data=request.data)
|
||||||
sr.is_valid(raise_exception=True)
|
sr.is_valid(raise_exception=True)
|
||||||
sr.save()
|
sr.save()
|
||||||
|
InmService.update_material_count(ins.mio)
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=MioItemAnaSerializer)
|
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=MioItemAnaSerializer)
|
||||||
|
|
|
@ -2,7 +2,7 @@ from django.urls import path
|
||||||
from .views import DrfRequestLogViewSet, ServerInfoView, LogView, LogDetailView, index, room, video, DbBackupView, AuditlogViewSet, CeleryInfoView, RedisInfoView
|
from .views import DrfRequestLogViewSet, ServerInfoView, LogView, LogDetailView, index, room, video, DbBackupView, AuditlogViewSet, CeleryInfoView, RedisInfoView
|
||||||
|
|
||||||
API_BASE_URL = 'api/monitor/'
|
API_BASE_URL = 'api/monitor/'
|
||||||
HTML_BASE_URL = 'monitor/'
|
HTML_BASE_URL = 'dhtml/monitor/'
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(HTML_BASE_URL, index),
|
path(HTML_BASE_URL, index),
|
||||||
path(HTML_BASE_URL + 'index/', index),
|
path(HTML_BASE_URL + 'index/', index),
|
||||||
|
|
|
@ -4,7 +4,7 @@ from django.db.models.expressions import F
|
||||||
|
|
||||||
|
|
||||||
class MaterialFilter(filters.FilterSet):
|
class MaterialFilter(filters.FilterSet):
|
||||||
tag = filters.CharFilter(method='filter_tag')
|
tag = filters.CharFilter(method='filter_tag', label="low_inm:库存不足")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Material
|
model = Material
|
||||||
|
@ -27,7 +27,7 @@ class MaterialFilter(filters.FilterSet):
|
||||||
|
|
||||||
def filter_tag(self, queryset, name, value):
|
def filter_tag(self, queryset, name, value):
|
||||||
if value == 'low_inm':
|
if value == 'low_inm':
|
||||||
queryset = queryset.exclude(count_safe=None).filter(
|
queryset = queryset.exclude(count_safe=None).exclude(count_safe__lte=0).filter(
|
||||||
count__lte=F('count_safe'))
|
count__lte=F('count_safe'))
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from apps.mtm.views import (MgroupViewSet, ShiftViewSet, TeamViewSet, MaterialVi
|
||||||
RoutePackViewSet, SruleViewSet, RouteMatViewSet)
|
RoutePackViewSet, SruleViewSet, RouteMatViewSet)
|
||||||
|
|
||||||
API_BASE_URL = 'api/mtm/'
|
API_BASE_URL = 'api/mtm/'
|
||||||
HTML_BASE_URL = 'mtm/'
|
HTML_BASE_URL = 'dhtml/mtm/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('mgroup', MgroupViewSet, basename='mgroup')
|
router.register('mgroup', MgroupViewSet, basename='mgroup')
|
||||||
|
|
|
@ -265,6 +265,7 @@ class RouteViewSet(CustomModelViewSet):
|
||||||
serializer_class = RouteSerializer
|
serializer_class = RouteSerializer
|
||||||
filterset_class = RouteFilter
|
filterset_class = RouteFilter
|
||||||
ordering = ['sort', 'process__sort', 'create_time']
|
ordering = ['sort', 'process__sort', 'create_time']
|
||||||
|
ordering_fields = ['sort', 'process__sort', 'create_time', 'update_time']
|
||||||
select_related_fields = ['material',
|
select_related_fields = ['material',
|
||||||
'process', 'material_in', 'material_out', 'mgroup', 'routepack']
|
'process', 'material_in', 'material_out', 'mgroup', 'routepack']
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
||||||
from apps.opm.views import GasCheckViewSet, OperationViewSet, OplCateViewSet, OplViewSet, OplWorkerViewSet
|
from apps.opm.views import GasCheckViewSet, OperationViewSet, OplCateViewSet, OplViewSet, OplWorkerViewSet
|
||||||
|
|
||||||
API_BASE_URL = 'api/opm/'
|
API_BASE_URL = 'api/opm/'
|
||||||
HTML_BASE_URL = 'opm/'
|
HTML_BASE_URL = 'dhtml/opm/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('opl_cate', OplCateViewSet, basename='opl_cate')
|
router.register('opl_cate', OplCateViewSet, basename='opl_cate')
|
||||||
|
|
|
@ -24,9 +24,12 @@ class UtaskSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Utask
|
model = Utask
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
read_only_fields = ["number"]
|
extra_kwargs = {
|
||||||
|
'number': {"required": False, "allow_blank": True}
|
||||||
|
}
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
|
if not validated_data.get('number', None):
|
||||||
validated_data["number"] = Utask.get_a_number()
|
validated_data["number"] = Utask.get_a_number()
|
||||||
return super().create(validated_data)
|
return super().create(validated_data)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
||||||
from apps.pm.views import (MtaskViewSet, UtaskViewSet, MtaskbViewSet)
|
from apps.pm.views import (MtaskViewSet, UtaskViewSet, MtaskbViewSet)
|
||||||
|
|
||||||
API_BASE_URL = 'api/pm/'
|
API_BASE_URL = 'api/pm/'
|
||||||
HTML_BASE_URL = 'pm/'
|
HTML_BASE_URL = 'dhtml/pm/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('mtask', MtaskViewSet, basename='mtask')
|
router.register('mtask', MtaskViewSet, basename='mtask')
|
||||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
||||||
from apps.pum.views import (SupplierViewSet, PuPlanViewSet, PuPlanItemViewSet, PuOrderViewSet, PuOrderItemViewSet)
|
from apps.pum.views import (SupplierViewSet, PuPlanViewSet, PuPlanItemViewSet, PuOrderViewSet, PuOrderItemViewSet)
|
||||||
|
|
||||||
API_BASE_URL = 'api/pum/'
|
API_BASE_URL = 'api/pum/'
|
||||||
HTML_BASE_URL = 'pum/'
|
HTML_BASE_URL = 'dhtml/pum/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('supplier', SupplierViewSet, basename='supplier')
|
router.register('supplier', SupplierViewSet, basename='supplier')
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 3.2.12 on 2025-04-11 00:40
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('qm', '0048_auto_20250318_1342'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ptest',
|
||||||
|
name='sample_number',
|
||||||
|
field=models.TextField(verbose_name='样品编号'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -410,7 +410,7 @@ class Ptest(CommonAModel):
|
||||||
User, verbose_name='检验员', on_delete=models.CASCADE)
|
User, verbose_name='检验员', on_delete=models.CASCADE)
|
||||||
testitem = models.ForeignKey(
|
testitem = models.ForeignKey(
|
||||||
TestItem, verbose_name='测试项目', on_delete=models.CASCADE, null=True, blank=True)
|
TestItem, verbose_name='测试项目', on_delete=models.CASCADE, null=True, blank=True)
|
||||||
sample_number = models.CharField('样品编号', max_length=20)
|
sample_number = models.TextField('样品编号')
|
||||||
sample_count = models.PositiveIntegerField('样品数量', null=True, blank=True)
|
sample_count = models.PositiveIntegerField('样品数量', null=True, blank=True)
|
||||||
sample_density = models.FloatField('样品密度', null=True, blank=True)
|
sample_density = models.FloatField('样品密度', null=True, blank=True)
|
||||||
specification = models.CharField(
|
specification = models.CharField(
|
||||||
|
|
|
@ -6,7 +6,7 @@ from apps.qm.views import (QuaStatViewSet, TestItemViewSet,
|
||||||
NotOkOptionView, DefectViewSet, QctViewSet, QctTestItemViewSet, QctDefectViewSet, QctMatViewSet)
|
NotOkOptionView, DefectViewSet, QctViewSet, QctTestItemViewSet, QctDefectViewSet, QctMatViewSet)
|
||||||
|
|
||||||
API_BASE_URL = 'api/qm/'
|
API_BASE_URL = 'api/qm/'
|
||||||
HTML_BASE_URL = 'qm/'
|
HTML_BASE_URL = 'dhtml/qm/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('quastat', QuaStatViewSet, basename='quastat')
|
router.register('quastat', QuaStatViewSet, basename='quastat')
|
||||||
|
|
|
@ -4,7 +4,7 @@ from django.urls import path, include
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
API_BASE_URL = 'api/rpm/'
|
API_BASE_URL = 'api/rpm/'
|
||||||
HTML_BASE_URL = 'rpm/'
|
HTML_BASE_URL = 'dhtml/rpm/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('rparty', RpartyViewSet, basename='rparty')
|
router.register('rparty', RpartyViewSet, basename='rparty')
|
||||||
|
|
|
@ -13,21 +13,25 @@ class SamService:
|
||||||
order = mio.order
|
order = mio.order
|
||||||
if order is None:
|
if order is None:
|
||||||
return
|
return
|
||||||
for i in MIOItem.objects.filter(mio=mio):
|
orderitem_qs = OrderItem.objects.filter(order=order)
|
||||||
orderitem = OrderItem.objects.get(order=order, material=i.material)
|
matIds = list(MIOItem.objects.filter(mio=mio).values_list('material__id', flat=True).distinct())
|
||||||
if is_reverse:
|
matIds2 = list(orderitem_qs.values_list('material__id', flat=True).distinct())
|
||||||
delivered_count = orderitem.delivered_count - i.count
|
if set(matIds) != set(matIds2):
|
||||||
else:
|
raise ValidationError('订单与发货单物料不一致')
|
||||||
delivered_count = orderitem.delivered_count + i.count
|
for orderitem in orderitem_qs:
|
||||||
|
material = orderitem.material
|
||||||
|
delivered_count = MIOItem.objects.filter(
|
||||||
|
mio__order=order, material=material, mio__type='sale_out', mio__submit_time__isnull=False).values('count').aggregate(Sum('count'))['count__sum']
|
||||||
|
delivered_count = delivered_count if delivered_count else 0
|
||||||
if delivered_count > orderitem.count:
|
if delivered_count > orderitem.count:
|
||||||
raise ValidationError((f'{i.material.name}-超出订单所需数量'))
|
raise ValidationError((f'{str(material)}-超出订单所需数量'))
|
||||||
elif delivered_count < 0:
|
elif delivered_count < 0:
|
||||||
raise ValidationError((f'{i.material.name}-数量小于0'))
|
raise ValidationError((f'{str(material)}-数量小于0'))
|
||||||
orderitem.delivered_count = delivered_count
|
orderitem.delivered_count = delivered_count
|
||||||
orderitem.save()
|
orderitem.save(update_fields=['delivered_count'])
|
||||||
# 更新order的状态
|
# 更新order的状态
|
||||||
qs = OrderItem.objects.filter(
|
qs = OrderItem.objects.filter(
|
||||||
order=order, count__lte=F('delivered_count'))
|
order=order, count__gt=F('delivered_count'))
|
||||||
order_state = Order.ORDER_DOING
|
order_state = Order.ORDER_DOING
|
||||||
if qs.exists():
|
if qs.exists():
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
||||||
from apps.sam.views import (CustomerViewSet, ContractViewSet, OrderViewSet, OrderItemViewSet)
|
from apps.sam.views import (CustomerViewSet, ContractViewSet, OrderViewSet, OrderItemViewSet)
|
||||||
|
|
||||||
API_BASE_URL = 'api/sam/'
|
API_BASE_URL = 'api/sam/'
|
||||||
HTML_BASE_URL = 'sam/'
|
HTML_BASE_URL = 'dhtml/sam/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('customer', CustomerViewSet, basename='customer')
|
router.register('customer', CustomerViewSet, basename='customer')
|
||||||
|
|
|
@ -57,8 +57,8 @@ class OrderViewSet(CustomModelViewSet):
|
||||||
queryset = Order.objects.all()
|
queryset = Order.objects.all()
|
||||||
serializer_class = OrderSerializer
|
serializer_class = OrderSerializer
|
||||||
select_related_fields = ['contract', 'customer']
|
select_related_fields = ['contract', 'customer']
|
||||||
search_fields = ['number', 'contract__name', 'contract__number', 'customer__name', 'orderitem_order__material__name',
|
search_fields = ['number', 'contract__name', 'contract__number', 'customer__name', 'item_order__material__name',
|
||||||
'orderitem_order__material__specification', 'orderitem_order__material__model']
|
'item_order__material__specification', 'item_order__material__model']
|
||||||
filterset_fields = {
|
filterset_fields = {
|
||||||
"contract": ["exact"],
|
"contract": ["exact"],
|
||||||
"customer": ["exact"],
|
"customer": ["exact"],
|
||||||
|
|
|
@ -7,7 +7,7 @@ from .views import ApkViewSet, FileViewSet, PTaskViewSet, PTaskResultViewSet, Po
|
||||||
from rest_framework import routers
|
from rest_framework import routers
|
||||||
|
|
||||||
API_BASE_URL = 'api/system/'
|
API_BASE_URL = 'api/system/'
|
||||||
HTML_BASE_URL = 'system/'
|
HTML_BASE_URL = 'dhtml/system/'
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
router.register('user', UserViewSet, basename="user")
|
router.register('user', UserViewSet, basename="user")
|
||||||
|
|
|
@ -767,6 +767,8 @@ class SysBaseConfigView(APIView):
|
||||||
config = get_sysconfig()
|
config = get_sysconfig()
|
||||||
base_dict = {key: config[key]
|
base_dict = {key: config[key]
|
||||||
for key in self.read_keys if key in config}
|
for key in self.read_keys if key in config}
|
||||||
|
base_dict.get("base", {})["sys_version"] = settings.SYS_VERSION
|
||||||
|
base_dict.get("base", {})["sys_name"] = settings.SYS_NAME
|
||||||
return Response(base_dict)
|
return Response(base_dict)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from apps.third.views import DahuaTestView, DhCommonViewSet, SpTestView, Speaker
|
||||||
from apps.third.views_d import BltViewSet, TDeviceViewSet, TlogViewSet
|
from apps.third.views_d import BltViewSet, TDeviceViewSet, TlogViewSet
|
||||||
|
|
||||||
API_BASE_URL = 'api/third/'
|
API_BASE_URL = 'api/third/'
|
||||||
HTML_BASE_URL = 'third/'
|
HTML_BASE_URL = 'dhtml/third/'
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
router.register('xunxi', XxCommonViewSet, basename='api_xunxi')
|
router.register('xunxi', XxCommonViewSet, basename='api_xunxi')
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
from django.db.models import DecimalField
|
||||||
|
from django.core.validators import MinValueValidator
|
||||||
|
from django.utils.functional import cached_property
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
|
||||||
class MyFilePathField(serializers.CharField):
|
class MyFilePathField(serializers.CharField):
|
||||||
|
@ -8,3 +12,9 @@ class MyFilePathField(serializers.CharField):
|
||||||
if 'http' in value:
|
if 'http' in value:
|
||||||
return str(value)
|
return str(value)
|
||||||
return settings.BASE_URL + str(value)
|
return settings.BASE_URL + str(value)
|
||||||
|
|
||||||
|
class PositiveDecimalField(DecimalField):
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def validators(self):
|
||||||
|
return [MinValueValidator(Decimal('0.0'))] + super().validators
|
|
@ -10,6 +10,14 @@ from io import BytesIO
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
import ast
|
import ast
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
class MyJSONEncoder(DjangoJSONEncoder):
|
||||||
|
def default(self, obj):
|
||||||
|
if isinstance(obj, Decimal):
|
||||||
|
return float(obj)
|
||||||
|
return super().default(obj)
|
||||||
|
|
||||||
class CodeAnalyzer(ast.NodeVisitor):
|
class CodeAnalyzer(ast.NodeVisitor):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
@ -4,7 +4,7 @@ from rest_framework.routers import DefaultRouter
|
||||||
from apps.vm.views import VisitViewSet, VisitorViewSet, VpeopleViewSet
|
from apps.vm.views import VisitViewSet, VisitorViewSet, VpeopleViewSet
|
||||||
|
|
||||||
API_BASE_URL = 'api/vm/'
|
API_BASE_URL = 'api/vm/'
|
||||||
HTML_BASE_URL = 'vm/'
|
HTML_BASE_URL = 'dhtml/vm/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('visit', VisitViewSet, basename='visit')
|
router.register('visit', VisitViewSet, basename='visit')
|
||||||
|
|
|
@ -4,7 +4,7 @@ from django.urls import path, include
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
API_BASE_URL = 'api/wf/'
|
API_BASE_URL = 'api/wf/'
|
||||||
HTML_BASE_URL = 'wf/'
|
HTML_BASE_URL = 'dhtml/wf/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('workflow', WorkflowViewSet, basename='workflow')
|
router.register('workflow', WorkflowViewSet, basename='workflow')
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Generated by Django 3.2.12 on 2025-04-14 14:44
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pm', '0021_auto_20250317_1040'),
|
||||||
|
('wpm', '0107_alter_handoverb_handover'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='fmlog',
|
||||||
|
name='is_fix',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='是否用于返修'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='fmlog',
|
||||||
|
name='mtask',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='fmlog_mtask', to='pm.mtask', verbose_name='任务'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -128,7 +128,7 @@ class WMaterial(CommonBDModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def count_working(self):
|
def count_working(self):
|
||||||
return Mlogb.objects.filter(wm_in=self, mlog__work_end_time__isnull=True).aggregate(count=Sum('count_use'))['count'] or 0
|
return Mlogb.objects.filter(wm_in=self, mlog__submit_time__isnull=True).aggregate(count=Sum('count_use'))['count'] or 0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def mat_in_qs(cls, mtask: Mtask, qs=None):
|
def mat_in_qs(cls, mtask: Mtask, qs=None):
|
||||||
|
@ -153,9 +153,11 @@ class WMaterial(CommonBDModel):
|
||||||
class Fmlog(CommonADModel):
|
class Fmlog(CommonADModel):
|
||||||
"""TN: 父级生产日志
|
"""TN: 父级生产日志
|
||||||
"""
|
"""
|
||||||
mtask = models.ForeignKey(Mtask, verbose_name='任务', on_delete=models.CASCADE, related_name='fmlog_mtask')
|
mtask = models.ForeignKey(Mtask, verbose_name='任务',
|
||||||
|
on_delete=models.CASCADE, related_name='fmlog_mtask', null=True, blank=True)
|
||||||
mgroup = models.ForeignKey(Mgroup, verbose_name='工段', on_delete=models.CASCADE, related_name='fmlog_mgroup')
|
mgroup = models.ForeignKey(Mgroup, verbose_name='工段', on_delete=models.CASCADE, related_name='fmlog_mgroup')
|
||||||
note = models.TextField('备注', default='', blank=True)
|
note = models.TextField('备注', default='', blank=True)
|
||||||
|
is_fix = models.BooleanField('是否用于返修', default=False) # 返工/复检
|
||||||
enabled = models.BooleanField("是否启用", default=True)
|
enabled = models.BooleanField("是否启用", default=True)
|
||||||
|
|
||||||
class Mlog(CommonADModel):
|
class Mlog(CommonADModel):
|
||||||
|
|
|
@ -213,6 +213,11 @@ class MlogbDefectSerializer(CustomModelSerializer):
|
||||||
fields = ["id", "defect_name", "count", "mlogb", "defect", "defect_okcate"]
|
fields = ["id", "defect_name", "count", "mlogb", "defect", "defect_okcate"]
|
||||||
read_only_fields = EXCLUDE_FIELDS_BASE + ["mlogb"]
|
read_only_fields = EXCLUDE_FIELDS_BASE + ["mlogb"]
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
if attrs["count"] < 0:
|
||||||
|
raise serializers.ValidationError("存在负数!")
|
||||||
|
return attrs
|
||||||
|
|
||||||
class MlogbSerializer(CustomModelSerializer):
|
class MlogbSerializer(CustomModelSerializer):
|
||||||
material_out_ = MaterialSimpleSerializer(
|
material_out_ = MaterialSimpleSerializer(
|
||||||
source='material_out', read_only=True)
|
source='material_out', read_only=True)
|
||||||
|
@ -541,7 +546,7 @@ class MlogSerializer(CustomModelSerializer):
|
||||||
attrs['mgroup'] = fmlog.mgroup
|
attrs['mgroup'] = fmlog.mgroup
|
||||||
attrs['mtask'] = fmlog.mtask
|
attrs['mtask'] = fmlog.mtask
|
||||||
attrs['mtype'] = fmlog.mgroup.mtype
|
attrs['mtype'] = fmlog.mgroup.mtype
|
||||||
if attrs['mtask'].route:
|
if attrs['mtask'] and attrs['mtask'].route:
|
||||||
attrs['route'] = attrs['mtask'].route
|
attrs['route'] = attrs['mtask'].route
|
||||||
# if attrs['mtask'].mtaskb and mtaskb is None:
|
# if attrs['mtask'].mtaskb and mtaskb is None:
|
||||||
# raise ParseError('子任务不能为空')
|
# raise ParseError('子任务不能为空')
|
||||||
|
@ -675,6 +680,8 @@ class MlogbInSerializer(CustomModelSerializer):
|
||||||
'wm_in': {'required': True, "allow_empty": False}}
|
'wm_in': {'required': True, "allow_empty": False}}
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
|
if attrs["count_use"] < 0 or attrs.get("count_pn_jgqbl", 0) < 0 or attrs.get("count_break", 0) < 0:
|
||||||
|
raise ParseError('存在负数!')
|
||||||
mlog:Mlog = attrs['mlog']
|
mlog:Mlog = attrs['mlog']
|
||||||
is_fix = mlog.is_fix
|
is_fix = mlog.is_fix
|
||||||
mtask: Mtask = attrs.get("mtask", None)
|
mtask: Mtask = attrs.get("mtask", None)
|
||||||
|
@ -739,6 +746,10 @@ class MlogbInUpdateSerializer(CustomModelSerializer):
|
||||||
model = Mlogb
|
model = Mlogb
|
||||||
fields = ['id', 'count_use', 'count_pn_jgqbl', 'note', 'mlogbdefect']
|
fields = ['id', 'count_use', 'count_pn_jgqbl', 'note', 'mlogbdefect']
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
if attrs["count_use"] < 0 or attrs.get("count_pn_jgqbl", 0) < 0:
|
||||||
|
raise ParseError('存在负数!')
|
||||||
|
return attrs
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
mlog: Mlog = instance.mlog
|
mlog: Mlog = instance.mlog
|
||||||
mlogbdefect = validated_data.pop("mlogbdefect", None)
|
mlogbdefect = validated_data.pop("mlogbdefect", None)
|
||||||
|
@ -1153,6 +1164,7 @@ class AttLogSerializer(CustomModelSerializer):
|
||||||
class FmlogSerializer(CustomModelSerializer):
|
class FmlogSerializer(CustomModelSerializer):
|
||||||
routepack_name = serializers.CharField(
|
routepack_name = serializers.CharField(
|
||||||
source='mtask.route.routepack.name', read_only=True)
|
source='mtask.route.routepack.name', read_only=True)
|
||||||
|
mgroup_name = serializers.CharField(source='mgroup.name', read_only=True)
|
||||||
mtask_number = serializers.CharField(source='mtask.number', read_only=True)
|
mtask_number = serializers.CharField(source='mtask.number', read_only=True)
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Fmlog
|
model = Fmlog
|
||||||
|
@ -1160,6 +1172,11 @@ class FmlogSerializer(CustomModelSerializer):
|
||||||
read_only_fields = EXCLUDE_FIELDS
|
read_only_fields = EXCLUDE_FIELDS
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
|
is_fix = attrs.get("is_fix", False)
|
||||||
|
if is_fix:
|
||||||
|
attrs["mtask"] = None
|
||||||
|
elif not attrs.get("mtask", None):
|
||||||
|
raise ParseError("请选择任务")
|
||||||
mtask: Mtask = attrs['mtask']
|
mtask: Mtask = attrs['mtask']
|
||||||
if mtask.state != Mtask.MTASK_ASSGINED:
|
if mtask.state != Mtask.MTASK_ASSGINED:
|
||||||
raise ParseError('该任务非下达中不可选择')
|
raise ParseError('该任务非下达中不可选择')
|
||||||
|
|
|
@ -5,13 +5,19 @@ from apps.inm.models import MIOItem
|
||||||
from apps.qm.models import FtestWork
|
from apps.qm.models import FtestWork
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from server.conf import BASE_PROJECT_CODE
|
from django.conf import settings
|
||||||
|
import json
|
||||||
|
from apps.utils.tools import MyJSONEncoder
|
||||||
|
import decimal
|
||||||
|
import logging
|
||||||
|
myLogger = logging.getLogger('log')
|
||||||
|
|
||||||
def get_alldata_with_batch_and_store(batch: str):
|
def get_alldata_with_batch_and_store(batch: str, need_update_time=True):
|
||||||
"""
|
"""
|
||||||
获取某个批次的整体生产数据并保存
|
获取某个批次的整体生产数据并保存
|
||||||
"""
|
"""
|
||||||
need_update = False
|
need_update = False
|
||||||
|
BASE_PROJECT_CODE = getattr(settings, "BASE_PROJECT_CODE", None)
|
||||||
if BASE_PROJECT_CODE == "gzerp":
|
if BASE_PROJECT_CODE == "gzerp":
|
||||||
need_update = True
|
need_update = True
|
||||||
last_time, data = get_alldata_with_batch(batch)
|
last_time, data = get_alldata_with_batch(batch)
|
||||||
|
@ -23,8 +29,9 @@ def get_alldata_with_batch_and_store(batch: str):
|
||||||
bobj, _ = BatchSt.objects.get_or_create(batch=batch, defaults={
|
bobj, _ = BatchSt.objects.get_or_create(batch=batch, defaults={
|
||||||
"last_time": last_time
|
"last_time": last_time
|
||||||
})
|
})
|
||||||
|
if need_update_time:
|
||||||
bobj.last_time = last_time
|
bobj.last_time = last_time
|
||||||
bobj.data = data
|
bobj.data = json.loads(json.dumps(data, cls=MyJSONEncoder))
|
||||||
bobj.save()
|
bobj.save()
|
||||||
|
|
||||||
def get_alldata_with_batch_gx(batch: str):
|
def get_alldata_with_batch_gx(batch: str):
|
||||||
|
@ -78,6 +85,9 @@ def get_alldata_with_batch(batch: str):
|
||||||
data["棒料成型_合格率"] = round((data["棒料成型_count_ok"] * 100/ data["棒料成型_count_real"]), 1)
|
data["棒料成型_合格率"] = round((data["棒料成型_count_ok"] * 100/ data["棒料成型_count_real"]), 1)
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
data["棒料成型_合格率"] = 0
|
data["棒料成型_合格率"] = 0
|
||||||
|
except decimal.InvalidOperation:
|
||||||
|
myLogger.error(f"棒料成型_合格率计算错误decimal.InvalidOperation-{data}")
|
||||||
|
data["棒料成型_合格率"] = 0
|
||||||
|
|
||||||
# 管料成型数据
|
# 管料成型数据
|
||||||
mgroup_glcx = Mgroup.objects.get(name="管料成型")
|
mgroup_glcx = Mgroup.objects.get(name="管料成型")
|
||||||
|
@ -118,11 +128,12 @@ def get_alldata_with_batch(batch: str):
|
||||||
if item.mio.do_user:
|
if item.mio.do_user:
|
||||||
data["七车间入库_仓库执行人"].append(item.mio.mio_user)
|
data["七车间入库_仓库执行人"].append(item.mio.mio_user)
|
||||||
for field in mioitem_count_fields:
|
for field in mioitem_count_fields:
|
||||||
if getattr(item, field) > 0 or field in ["count", "count_notok"]:
|
f_v = getattr(item, field)
|
||||||
|
if f_v is not None and (f_v > 0 or field in ["count", "count_notok"]):
|
||||||
if f'七车间入库_{field}' not in data:
|
if f'七车间入库_{field}' not in data:
|
||||||
data[f'七车间入库_{field}'] = int(getattr(item, field))
|
data[f'七车间入库_{field}'] = f_v
|
||||||
else:
|
else:
|
||||||
data[f'七车间入库_{field}'] += int(getattr(item, field))
|
data[f'七车间入库_{field}'] += f_v
|
||||||
data["七车间入库_合格率"] = round((data["七车间入库_count"] - data["七车间入库_count_notok"]) * 100/ data["七车间入库_count"], 1)
|
data["七车间入库_合格率"] = round((data["七车间入库_count"] - data["七车间入库_count_notok"]) * 100/ data["七车间入库_count"], 1)
|
||||||
data["七车间入库_日期"] = list(set(data["七车间入库_日期"]))
|
data["七车间入库_日期"] = list(set(data["七车间入库_日期"]))
|
||||||
data["七车间入库_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["七车间入库_日期"]])
|
data["七车间入库_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["七车间入库_日期"]])
|
||||||
|
@ -150,11 +161,11 @@ def get_alldata_with_batch(batch: str):
|
||||||
if item.test_user:
|
if item.test_user:
|
||||||
data["十车间入库_抽检人"].append(item.test_user)
|
data["十车间入库_抽检人"].append(item.test_user)
|
||||||
for field in mioitem_count_fields:
|
for field in mioitem_count_fields:
|
||||||
if getattr(item, field) > 0 or field in ["count", "count_notok", "count_sampling"]:
|
if getattr(item, field) is not None and (getattr(item, field) > 0 or field in ["count", "count_notok", "count_sampling"]):
|
||||||
if f'十车间入库_{field}' not in data:
|
if f'十车间入库_{field}' not in data:
|
||||||
data[f'十车间入库_{field}'] = int(getattr(item, field))
|
data[f'十车间入库_{field}'] = getattr(item, field)
|
||||||
else:
|
else:
|
||||||
data[f'十车间入库_{field}'] += int(getattr(item, field))
|
data[f'十车间入库_{field}'] += getattr(item, field)
|
||||||
data["十车间入库_抽检人"] = list(set(data["十车间入库_抽检人"]))
|
data["十车间入库_抽检人"] = list(set(data["十车间入库_抽检人"]))
|
||||||
data["十车间入库_抽检人"] = ";".join([item.name for item in data["十车间入库_抽检人"]])
|
data["十车间入库_抽检人"] = ";".join([item.name for item in data["十车间入库_抽检人"]])
|
||||||
if data["十车间入库_count_sampling"] > 0:
|
if data["十车间入库_count_sampling"] > 0:
|
||||||
|
@ -185,11 +196,11 @@ def get_alldata_with_batch(batch: str):
|
||||||
if item.handle_user:
|
if item.handle_user:
|
||||||
data["管料退火_操作人"].append(item.handle_user)
|
data["管料退火_操作人"].append(item.handle_user)
|
||||||
for field in mlog_count_fields:
|
for field in mlog_count_fields:
|
||||||
if getattr(item, field) > 0 or field in ["count", "count_notok"]:
|
if getattr(item, field) is not None and (getattr(item, field) > 0 or field in ["count", "count_notok"]):
|
||||||
if f'管料退火_{field}' not in data:
|
if f'管料退火_{field}' not in data:
|
||||||
data[f'管料退火_{field}'] = int(getattr(item, field))
|
data[f'管料退火_{field}'] = getattr(item, field)
|
||||||
else:
|
else:
|
||||||
data[f'管料退火_{field}'] += int(getattr(item, field))
|
data[f'管料退火_{field}'] += getattr(item, field)
|
||||||
data["管料退火_日期"] = list(set(data["管料退火_日期"]))
|
data["管料退火_日期"] = list(set(data["管料退火_日期"]))
|
||||||
data["管料退火_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["管料退火_日期"]])
|
data["管料退火_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["管料退火_日期"]])
|
||||||
data["管料退火_操作人"] = list(set(data["管料退火_操作人"]))
|
data["管料退火_操作人"] = list(set(data["管料退火_操作人"]))
|
||||||
|
@ -216,11 +227,11 @@ def get_alldata_with_batch(batch: str):
|
||||||
if item.mio.mio_user:
|
if item.mio.mio_user:
|
||||||
data["六车间领料_车间执行人"].append(item.mio.mio_user)
|
data["六车间领料_车间执行人"].append(item.mio.mio_user)
|
||||||
for field in mioitem_count_fields:
|
for field in mioitem_count_fields:
|
||||||
if getattr(item, field) > 0:
|
if getattr(item, field) is not None and getattr(item, field) > 0:
|
||||||
if f'六车间领料_{field}' not in data:
|
if f'六车间领料_{field}' not in data:
|
||||||
data[f'六车间领料_{field}'] = int(getattr(item, field))
|
data[f'六车间领料_{field}'] = getattr(item, field)
|
||||||
else:
|
else:
|
||||||
data[f'六车间领料_{field}'] += int(getattr(item, field))
|
data[f'六车间领料_{field}'] += getattr(item, field)
|
||||||
data["六车间领料_日期"] = list(set(data["六车间领料_日期"]))
|
data["六车间领料_日期"] = list(set(data["六车间领料_日期"]))
|
||||||
data["六车间领料_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["六车间领料_日期"]])
|
data["六车间领料_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["六车间领料_日期"]])
|
||||||
data["六车间领料_仓库执行人"] = list(set(data["六车间领料_仓库执行人"]))
|
data["六车间领料_仓库执行人"] = list(set(data["六车间领料_仓库执行人"]))
|
||||||
|
@ -262,24 +273,11 @@ def get_alldata_with_batch(batch: str):
|
||||||
data[f'六车间_{mgroup_name}_日期'] = ";".join([item.strftime("%Y-%m-%d") for item in data[f'六车间_{mgroup_name}_日期']])
|
data[f'六车间_{mgroup_name}_日期'] = ";".join([item.strftime("%Y-%m-%d") for item in data[f'六车间_{mgroup_name}_日期']])
|
||||||
data[f'六车间_{mgroup_name}_操作人'] = list(set(data[f'六车间_{mgroup_name}_操作人']))
|
data[f'六车间_{mgroup_name}_操作人'] = list(set(data[f'六车间_{mgroup_name}_操作人']))
|
||||||
data[f'六车间_{mgroup_name}_操作人'] = ";".join([item.name for item in data[f'六车间_{mgroup_name}_操作人']])
|
data[f'六车间_{mgroup_name}_操作人'] = ";".join([item.name for item in data[f'六车间_{mgroup_name}_操作人']])
|
||||||
|
try:
|
||||||
data[f'六车间_{mgroup_name}_合格率'] = round(data[f'六车间_{mgroup_name}_count_ok'] * 100/ data[f'六车间_{mgroup_name}_count_real'], 1)
|
data[f'六车间_{mgroup_name}_合格率'] = round(data[f'六车间_{mgroup_name}_count_ok'] * 100/ data[f'六车间_{mgroup_name}_count_real'], 1)
|
||||||
|
except decimal.InvalidOperation:
|
||||||
# 六车间入库/检验数据
|
myLogger.error(f"六车间_{mgroup_name}_合格率decimal.InvalidOperation-{data}")
|
||||||
# mioitem6_qs2 = MIOItem.objects.filter(mio__belong_dept=dept6, mio__type="do_in",
|
data[f'六车间_{mgroup_name}_合格率'] = 0
|
||||||
# batch=batch,
|
|
||||||
# mio__submit_time__isnull=False)
|
|
||||||
# if mioitem6_qs2.exists():
|
|
||||||
# data["六车间生产入库_日期"] = []
|
|
||||||
# for item in mioitem6_qs:
|
|
||||||
# data["六车间生产入库_日期"].append(item.mio.inout_date)
|
|
||||||
# for field in mioitem_count_fields:
|
|
||||||
# if getattr(item, field) > 0:
|
|
||||||
# if f'六车间生产入库_{field}' not in data:
|
|
||||||
# data[f'六车间生产入库_{field}'] = getattr(item, field)
|
|
||||||
# else:
|
|
||||||
# data[f'六车间生产入库_{field}'] += getattr(item, field)
|
|
||||||
# data["六车间生产入库_日期"] = list(set(data["六车间生产入库_日期"]))
|
|
||||||
# data["六车间生产入库_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["六车间生产入库_日期"]])
|
|
||||||
|
|
||||||
ftestwork_count_fields = FtestWork.count_fields()
|
ftestwork_count_fields = FtestWork.count_fields()
|
||||||
# 六车间中检数据
|
# 六车间中检数据
|
||||||
|
@ -311,6 +309,34 @@ def get_alldata_with_batch(batch: str):
|
||||||
data['六车间中检_检验人'] = list(set(data['六车间中检_检验人']))
|
data['六车间中检_检验人'] = list(set(data['六车间中检_检验人']))
|
||||||
data['六车间中检_检验人'] = ";".join([item.name for item in data['六车间中检_检验人']])
|
data['六车间中检_检验人'] = ";".join([item.name for item in data['六车间中检_检验人']])
|
||||||
|
|
||||||
|
# 六车间入库/检验数据
|
||||||
|
mioitem6_qs2 = MIOItem.objects.filter(mio__belong_dept=dept6, mio__type="do_in",
|
||||||
|
batch=batch,
|
||||||
|
mio__submit_time__isnull=False)
|
||||||
|
if mioitem6_qs2.exists():
|
||||||
|
data["六车间生产入库_日期"] = []
|
||||||
|
data["六车间生产入库_检验日期"] = []
|
||||||
|
data["六车间生产入库_检验人"] = []
|
||||||
|
for item in mioitem6_qs2:
|
||||||
|
data["六车间生产入库_日期"].append(item.mio.inout_date)
|
||||||
|
if item.test_date:
|
||||||
|
data["六车间生产入库_检验日期"].append(item.test_date)
|
||||||
|
for field in mioitem_count_fields:
|
||||||
|
if getattr(item, field) is not None and (getattr(item, field) > 0 or field in ["count", "count_notok"]):
|
||||||
|
if f'六车间生产入库_{field}' not in data:
|
||||||
|
data[f'六车间生产入库_{field}'] = getattr(item, field)
|
||||||
|
else:
|
||||||
|
data[f'六车间生产入库_{field}'] += getattr(item, field)
|
||||||
|
data["六车间生产入库_日期"] = list(set(data["六车间生产入库_日期"]))
|
||||||
|
data["六车间生产入库_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["六车间生产入库_日期"]])
|
||||||
|
data["六车间生产入库_检验日期"] = list(set(data["六车间生产入库_检验日期"]))
|
||||||
|
data["六车间生产入库_检验日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["六车间生产入库_检验日期"]])
|
||||||
|
try:
|
||||||
|
data['六车间生产入库_合格率'] = round((data['六车间生产入库_count'] - data['六车间生产入库_count_notok']) * 100/ data['六车间生产入库_count'], 1)
|
||||||
|
except decimal.InvalidOperation:
|
||||||
|
myLogger.error("六车间生产入库_合格率decimal.InvalidOperation-{data}")
|
||||||
|
data['六车间生产入库_合格率'] = 0
|
||||||
|
|
||||||
# 成品检验数据
|
# 成品检验数据
|
||||||
ftestwork_qs = FtestWork.objects.filter(batch=batch, type="prod")
|
ftestwork_qs = FtestWork.objects.filter(batch=batch, type="prod")
|
||||||
if ftestwork_qs.exists():
|
if ftestwork_qs.exists():
|
||||||
|
@ -340,9 +366,29 @@ def get_alldata_with_batch(batch: str):
|
||||||
data['成品检验_检验人'] = list(set(data['成品检验_检验人']))
|
data['成品检验_检验人'] = list(set(data['成品检验_检验人']))
|
||||||
data['成品检验_检验人'] = ";".join([item.name for item in data['成品检验_检验人']])
|
data['成品检验_检验人'] = ";".join([item.name for item in data['成品检验_检验人']])
|
||||||
data['成品检验_合格率'] = round(data['成品检验_count_ok'] * 100/ data['成品检验_count'], 1)
|
data['成品检验_合格率'] = round(data['成品检验_count_ok'] * 100/ data['成品检验_count'], 1)
|
||||||
|
if getattr(data, "六车间领料_count", 0) > 0:
|
||||||
data["六车间_批次生产合格率"] = round(data["成品检验_count_ok"] * 100/ data["六车间领料_count"], 1)
|
data["六车间_批次生产合格率"] = round(data["成品检验_count_ok"] * 100/ data["六车间领料_count"], 1)
|
||||||
if data["棒料成型_count_real"]:
|
if getattr(data, "棒料成型_count_real", 0) > 0:
|
||||||
data["七车间_批次应出合格率"] = round(data["成品检验_count_ok"] * 100/ data["棒料成型_count_real"], 1)
|
data["七车间_批次应出合格率"] = round(data["成品检验_count_ok"] * 100/ data["棒料成型_count_real"], 1)
|
||||||
|
|
||||||
|
# 销售发货数据
|
||||||
|
mioitem_qs = MIOItem.objects.filter(batch=batch, mio__type="sale_out", mio__submit_time__isnull=False)
|
||||||
|
if mioitem_qs.exists():
|
||||||
|
data["销售发货_日期"] = []
|
||||||
|
data['销售发货_仓库执行人'] = []
|
||||||
|
data['销售发货_count'] = 0
|
||||||
|
for item in mioitem_qs:
|
||||||
|
last_time = item.mio.update_time if item.mio.update_time > last_time else last_time
|
||||||
|
if item.mio.inout_date:
|
||||||
|
data["销售发货_日期"].append(item.mio.inout_date)
|
||||||
|
if item.mio.do_user:
|
||||||
|
data['销售发货_仓库执行人'].append(item.mio.do_user)
|
||||||
|
data['销售发货_count']+= item.count
|
||||||
|
if getattr(data, "棒料成型_count_real", 0) > 0:
|
||||||
|
data["七车间_批次发货合格率"] = round(data["销售发货_count"] * 100/ data["棒料成型_count_real"], 1)
|
||||||
|
if getattr(data, "六车间领料_count", 0) > 0:
|
||||||
|
data["六车间_批次发货合格率"] = round(data["销售发货_count"] * 100/ data["六车间领料_count"], 1)
|
||||||
|
data['销售发货_仓库执行人'] = ";".join([item.name for item in data['销售发货_仓库执行人']])
|
||||||
|
|
||||||
return last_time, data
|
return last_time, data
|
||||||
|
|
|
@ -10,7 +10,7 @@ from apps.wpm.datax import AnaViewSet
|
||||||
|
|
||||||
|
|
||||||
API_BASE_URL = 'api/wpm/'
|
API_BASE_URL = 'api/wpm/'
|
||||||
HTML_BASE_URL = 'wpm/'
|
HTML_BASE_URL = 'dhtml/wpm/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('sflog', SfLogViewSet, basename='sflog')
|
router.register('sflog', SfLogViewSet, basename='sflog')
|
||||||
|
|
|
@ -136,7 +136,7 @@ class WMaterialViewSet(ListModelMixin, CustomGenericViewSet):
|
||||||
serializer_class = WMaterialSerializer
|
serializer_class = WMaterialSerializer
|
||||||
select_related_fields = ['material', 'belong_dept', 'material__process', 'supplier']
|
select_related_fields = ['material', 'belong_dept', 'material__process', 'supplier']
|
||||||
search_fields = ['material__name',
|
search_fields = ['material__name',
|
||||||
'material__number', 'material__specification', 'batch', 'material__model', "defect__name", "notok_sign", 'material_ofrom']
|
'material__number', 'material__specification', 'batch', 'material__model', "defect__name", "notok_sign"]
|
||||||
filterset_class = WMaterialFilter
|
filterset_class = WMaterialFilter
|
||||||
ordering_fields = ["update_time", "state", "count", "count_xtest"]
|
ordering_fields = ["update_time", "state", "count", "count_xtest"]
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from apps.wpmw.views import WprViewSet
|
||||||
|
|
||||||
|
|
||||||
API_BASE_URL = 'api/wpmw/'
|
API_BASE_URL = 'api/wpmw/'
|
||||||
HTML_BASE_URL = 'wpmw/'
|
HTML_BASE_URL = 'dhtml/wpmw/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('wpr', WprViewSet, basename='wpr')
|
router.register('wpr', WprViewSet, basename='wpr')
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
## 2.6.2025033109
|
||||||
|
- feat: 功能新增
|
||||||
|
- 建立及完善批次号数据链 [caoqianming]
|
||||||
|
- 添加子工序以完善同一日志的记录 [caoqianming]
|
||||||
|
- wpr产品追溯上游链 [caoqianming]
|
||||||
|
- 工艺路线routepack支持子图逻辑及任务量算法 [caoqianming]
|
||||||
|
- 出入库及任务编号非必填及自动生成逻辑 [caoqianming]
|
||||||
|
- fix: 问题修复
|
||||||
|
- 车间库存检索bug [caoqianming]
|
||||||
|
- wpr产品创建oinfo的bug [caoqianming]
|
||||||
|
- 其他已知的bug [caoqianming]
|
||||||
|
## 2.6.2025031919
|
||||||
|
- 初始版本
|
|
@ -0,0 +1,12 @@
|
||||||
|
SECRET_KEY = 'xx'
|
||||||
|
DEBUG = False
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
|
'NAME': 'xx',
|
||||||
|
'USER': 'postgres',
|
||||||
|
'PASSWORD': 'xx',
|
||||||
|
'HOST': 'xx',
|
||||||
|
'PORT': '5432',
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import os
|
import os
|
||||||
from . import conf
|
from config import conf
|
||||||
from celery import Celery
|
from celery import Celery
|
||||||
from celery.app.control import Control, Inspect
|
from celery.app.control import Control, Inspect
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,10 @@ from datetime import datetime, timedelta
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
from .conf import *
|
from config.conf import *
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
import logging
|
||||||
|
|
||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
|
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
|
||||||
|
@ -33,8 +35,8 @@ sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
|
||||||
ALLOWED_HOSTS = ['*']
|
ALLOWED_HOSTS = ['*']
|
||||||
|
|
||||||
SYS_NAME = '星途工厂综合管理系统'
|
SYS_NAME = '星途工厂综合管理系统'
|
||||||
SYS_VERSION = '2.3.0'
|
SYS_VERSION = '2.6.2025033109'
|
||||||
|
X_FRAME_OPTIONS = 'SAMEORIGIN'
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
|
@ -270,6 +272,18 @@ LOG_PATH = os.path.join(BASE_DIR, 'log')
|
||||||
if not os.path.exists(LOG_PATH):
|
if not os.path.exists(LOG_PATH):
|
||||||
os.makedirs(LOG_PATH)
|
os.makedirs(LOG_PATH)
|
||||||
|
|
||||||
|
class TimedSizeRotatingHandler(logging.handlers.TimedRotatingFileHandler):
|
||||||
|
def __init__(self, filename, when='midnight', interval=1, backupCount=0,
|
||||||
|
maxBytes=0, encoding=None, delay=False, utc=False, atTime=None):
|
||||||
|
super().__init__(filename, when, interval, backupCount, encoding, delay, utc, atTime)
|
||||||
|
self.maxBytes = maxBytes
|
||||||
|
|
||||||
|
def shouldRollover(self, record):
|
||||||
|
if self.maxBytes > 0 and os.path.exists(self.baseFilename):
|
||||||
|
if os.stat(self.baseFilename).st_size >= self.maxBytes:
|
||||||
|
return True
|
||||||
|
return super().shouldRollover(record)
|
||||||
|
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
'version': 1,
|
'version': 1,
|
||||||
'disable_existing_loggers': False,
|
'disable_existing_loggers': False,
|
||||||
|
@ -293,22 +307,28 @@ LOGGING = {
|
||||||
# 默认记录所有日志
|
# 默认记录所有日志
|
||||||
'default': {
|
'default': {
|
||||||
'level': 'INFO',
|
'level': 'INFO',
|
||||||
'class': 'logging.handlers.RotatingFileHandler',
|
'class': 'server.settings.TimedSizeRotatingHandler',
|
||||||
'filename': os.path.join(LOG_PATH, 'all-{}.log'.format(datetime.now().strftime('%Y-%m-%d'))),
|
'filename': os.path.join(LOG_PATH, 'all.log'),
|
||||||
|
'when': 'midnight', # 每天午夜滚动
|
||||||
|
'interval': 1,
|
||||||
'maxBytes': 1024 * 1024 * 2, # 文件大小
|
'maxBytes': 1024 * 1024 * 2, # 文件大小
|
||||||
'backupCount': 10, # 备份数
|
'backupCount': 30, # 备份数
|
||||||
'formatter': 'standard', # 输出格式
|
'formatter': 'standard', # 输出格式
|
||||||
'encoding': 'utf-8', # 设置默认编码,否则打印出来汉字乱码
|
'encoding': 'utf-8', # 设置默认编码,否则打印出来汉字乱码
|
||||||
|
'delay': True, # 延迟打开文件,减少锁定冲突
|
||||||
},
|
},
|
||||||
# 输出错误日志
|
# 输出错误日志
|
||||||
'error': {
|
'error': {
|
||||||
'level': 'ERROR',
|
'level': 'ERROR',
|
||||||
'class': 'logging.handlers.RotatingFileHandler',
|
'class': 'server.settings.TimedSizeRotatingHandler',
|
||||||
'filename': os.path.join(LOG_PATH, 'error-{}.log'.format(datetime.now().strftime('%Y-%m-%d'))),
|
'filename': os.path.join(LOG_PATH, 'error.log'),
|
||||||
|
'when': 'midnight',
|
||||||
|
'interval': 1,
|
||||||
'maxBytes': 1024 * 1024 * 2, # 文件大小
|
'maxBytes': 1024 * 1024 * 2, # 文件大小
|
||||||
'backupCount': 10, # 备份数
|
'backupCount': 30, # 备份数
|
||||||
'formatter': 'standard', # 输出格式
|
'formatter': 'standard', # 输出格式
|
||||||
'encoding': 'utf-8', # 设置默认编码
|
'encoding': 'utf-8', # 设置默认编码
|
||||||
|
'delay': True, # 延迟打开文件,减少锁定冲突
|
||||||
},
|
},
|
||||||
# 控制台输出
|
# 控制台输出
|
||||||
'console': {
|
'console': {
|
||||||
|
@ -320,12 +340,15 @@ LOGGING = {
|
||||||
# 输出info日志
|
# 输出info日志
|
||||||
'info': {
|
'info': {
|
||||||
'level': 'INFO',
|
'level': 'INFO',
|
||||||
'class': 'logging.handlers.RotatingFileHandler',
|
'class': 'server.settings.TimedSizeRotatingHandler',
|
||||||
'filename': os.path.join(LOG_PATH, 'info-{}.log'.format(datetime.now().strftime('%Y-%m-%d'))),
|
'filename': os.path.join(LOG_PATH, 'info.log'),
|
||||||
|
'when': 'midnight',
|
||||||
|
'interval': 1,
|
||||||
'maxBytes': 1024 * 1024 * 2,
|
'maxBytes': 1024 * 1024 * 2,
|
||||||
'backupCount': 10,
|
'backupCount': 30,
|
||||||
'formatter': 'standard',
|
'formatter': 'standard',
|
||||||
'encoding': 'utf-8', # 设置默认编码
|
'encoding': 'utf-8', # 设置默认编码
|
||||||
|
'delay': True, # 延迟打开文件,减少锁定冲突
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
# 配置用哪几种 handlers 来处理日志
|
# 配置用哪几种 handlers 来处理日志
|
||||||
|
@ -346,7 +369,7 @@ LOGGING = {
|
||||||
}
|
}
|
||||||
|
|
||||||
##### 加载客户可自定义配置并提供操作方法 #####
|
##### 加载客户可自定义配置并提供操作方法 #####
|
||||||
SYS_JSON_PATH = os.path.join(BASE_DIR, 'server/conf.json')
|
SYS_JSON_PATH = os.path.join(BASE_DIR, 'config/conf.json')
|
||||||
|
|
||||||
def get_sysconfig(key='', default='raise_error', reload=False):
|
def get_sysconfig(key='', default='raise_error', reload=False):
|
||||||
"""获取系统配置可指定key字符串
|
"""获取系统配置可指定key字符串
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 设置默认版本号 (格式: 2.6.YYYYMMDDHH)
|
||||||
|
DEFAULT_VERSION="2.6.$(date '+%Y%m%d%H')"
|
||||||
|
|
||||||
|
# 获取参数 (起始tag)
|
||||||
|
TARGET_TAG="$1"
|
||||||
|
|
||||||
|
# 获取最后一个tag
|
||||||
|
LAST_TAG=$(git describe --abbrev=0 --tags 2>/dev/null)
|
||||||
|
|
||||||
|
# 确定版本范围
|
||||||
|
if [ -z "$TARGET_TAG" ]; then
|
||||||
|
if [ -z "$LAST_TAG" ]; then
|
||||||
|
echo "没有找到任何tag,将从头开始生成"
|
||||||
|
RANGE=""
|
||||||
|
else
|
||||||
|
RANGE="$LAST_TAG..HEAD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
RANGE="$TARGET_TAG..HEAD"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 初始化临时文件
|
||||||
|
TEMP_FILE=$(mktemp)
|
||||||
|
|
||||||
|
# 生成变更日志头
|
||||||
|
echo "## $DEFAULT_VERSION" > "$TEMP_FILE"
|
||||||
|
echo >> "$TEMP_FILE"
|
||||||
|
|
||||||
|
# 按类型分类提交记录
|
||||||
|
process_commits() {
|
||||||
|
local type="$1"
|
||||||
|
local header="$2"
|
||||||
|
local pattern="$3"
|
||||||
|
|
||||||
|
# 查找匹配类型的提交
|
||||||
|
git log $RANGE --no-merges --pretty=format:"%s | %an | %ad" --date=short | grep -E "$pattern" | while read -r line; do
|
||||||
|
COMMIT_MSG=$(echo "$line" | cut -d'|' -f1 | sed 's/^ *//;s/ *$//')
|
||||||
|
AUTHOR=$(echo "$line" | cut -d'|' -f2 | sed 's/^ *//;s/ *$//')
|
||||||
|
DATE=$(echo "$line" | cut -d'|' -f3 | sed 's/^ *//;s/ *$//')
|
||||||
|
|
||||||
|
# 如果是第一次找到该类型,先打印标题
|
||||||
|
if [ $printed_header -eq 0 ]; then
|
||||||
|
echo "- $header" >> "$TEMP_FILE"
|
||||||
|
printed_header=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 打印提交信息(移除类型前缀)
|
||||||
|
CLEAN_MSG=$(echo "$COMMIT_MSG" | sed -E "s/^$type:\s*//i")
|
||||||
|
echo " - $CLEAN_MSG [$AUTHOR]" >> "$TEMP_FILE"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# 处理各类型提交(按优先级排序)
|
||||||
|
printed_header=0; process_commits "feat" "feat: 新增功能" "^[fF]eat"
|
||||||
|
printed_header=0; process_commits "fix" "fix: 问题修复" "^[fF]ix"
|
||||||
|
printed_header=0; process_commits "" "other: 其他变更" "^((?![fF]eat|[fF]ix).)*$"
|
||||||
|
|
||||||
|
# 合并到原文件
|
||||||
|
if [ -f changelog.md ]; then
|
||||||
|
cat "$TEMP_FILE" changelog.md > changelog.md.tmp && mv changelog.md.tmp changelog.md
|
||||||
|
rm "$TEMP_FILE"
|
||||||
|
else
|
||||||
|
mv "$TEMP_FILE" changelog.md
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "变更日志已更新到 changelog.md"
|
||||||
|
echo "当前版本号: $DEFAULT_VERSION (请手动修改)"
|
Loading…
Reference in New Issue