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.ini
|
||||
server/conf*.json
|
||||
config/conf*.py
|
||||
config/conf*.json
|
||||
sh/*
|
||||
temp/*
|
||||
nohup.out
|
||||
|
|
|
@ -3,7 +3,7 @@ from django.urls import path, include
|
|||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
API_BASE_URL = 'api/am/'
|
||||
HTML_BASE_URL = 'am/'
|
||||
HTML_BASE_URL = 'dhtml/am/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('area', AreaViewSet, basename='area')
|
||||
|
|
|
@ -4,8 +4,8 @@ from django.utils import timezone
|
|||
from apps.bi.models import Dataset, DatasetRecord
|
||||
from apps.bi.services import exec_dataset
|
||||
import json
|
||||
|
||||
|
||||
from apps.utils.tools import MyJSONEncoder
|
||||
|
||||
@shared_task()
|
||||
def exec_dataset_and_store(code: str, query: str = ''):
|
||||
dt = Dataset.objects.get(code=code)
|
||||
|
@ -16,4 +16,5 @@ def exec_dataset_and_store(code: str, query: str = ''):
|
|||
if query:
|
||||
squery = json.loads(query)
|
||||
dtr.full_sql, dtr.result = exec_dataset(dt, squery)
|
||||
dtr.result = json.loads(json.dumps(dtr.result, cls=MyJSONEncoder))
|
||||
dtr.save()
|
||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
|||
from apps.bi.views import DatasetViewSet, DatasetRecordViewSet
|
||||
|
||||
API_BASE_URL = 'api/bi/'
|
||||
HTML_BASE_URL = 'bi/'
|
||||
HTML_BASE_URL = 'dhtml/bi/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('dataset', DatasetViewSet, basename='dataset')
|
||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
|||
from apps.cm.views import LableMatViewSet
|
||||
|
||||
API_BASE_URL = 'api/cm/'
|
||||
HTML_BASE_URL = 'cm/'
|
||||
HTML_BASE_URL = 'dhtml/cm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('labelmat', LableMatViewSet, basename='labelmat')
|
||||
|
|
|
@ -3,7 +3,7 @@ from django.urls import path, include
|
|||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
API_BASE_URL = 'api/cms/'
|
||||
HTML_BASE_URL = 'cms/'
|
||||
HTML_BASE_URL = 'dhtml/cms/'
|
||||
|
||||
router = DefaultRouter()
|
||||
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
|
||||
|
||||
API_BASE_URL = 'api/develop/'
|
||||
HTML_BASE_URL = 'develop/'
|
||||
HTML_BASE_URL = 'dhtml/develop/'
|
||||
router = DefaultRouter()
|
||||
router.register('test', TestViewSet, basename='api_test')
|
||||
router.register('correct', CorrectViewSet, basename='correct')
|
||||
|
|
|
@ -3,7 +3,7 @@ from django.urls import path, include
|
|||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
API_BASE_URL = 'api/dpm/'
|
||||
HTML_BASE_URL = 'dpm/'
|
||||
HTML_BASE_URL = 'dhtml/dpm/'
|
||||
# cache_areas_info()
|
||||
router = DefaultRouter()
|
||||
router.register('riskpoint', RiskPointViewSet, basename='riskpoint')
|
||||
|
|
|
@ -4,7 +4,7 @@ from django.urls import path, include
|
|||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
API_BASE_URL = 'api/ecm/'
|
||||
HTML_BASE_URL = 'ecm/'
|
||||
HTML_BASE_URL = 'dhtml/ecm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
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
|
||||
|
||||
API_BASE_URL = 'api/edu/'
|
||||
HTML_BASE_URL = 'edu/'
|
||||
HTML_BASE_URL = 'dhtml/edu/'
|
||||
|
||||
router = DefaultRouter()
|
||||
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
|
||||
|
||||
API_BASE_URL = 'api/em/'
|
||||
HTML_BASE_URL = 'em/'
|
||||
HTML_BASE_URL = 'dhtml/em/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('equipment', EquipmentViewSet, basename='equipment')
|
||||
|
|
|
@ -4,7 +4,7 @@ from apps.enm.views import (MpointViewSet, MpLogxViewSet, MpointStatViewSet,
|
|||
EnStatViewSet, EnStat2ViewSet, XscriptViewSet)
|
||||
|
||||
API_BASE_URL = 'api/enm/'
|
||||
HTML_BASE_URL = 'enm/'
|
||||
HTML_BASE_URL = 'dhtml/enm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('mpoint', MpointViewSet, basename='mpoint')
|
||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
|||
from .views import DrainViewSet, DrainEquipViewSet, VehicleAccessViewSet, EnvDataViewSet, CarWashViewSet
|
||||
|
||||
API_BASE_URL = 'api/enp/'
|
||||
HTML_BASE_URL = 'enp/'
|
||||
HTML_BASE_URL = 'dhtml/enp/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('drain', DrainViewSet, basename='drain')
|
||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
|||
from apps.fim.views import (PriceSetViewSet, FeeSetViewSet, FeeViewSet)
|
||||
|
||||
API_BASE_URL = 'api/fim/'
|
||||
HTML_BASE_URL = 'fim/'
|
||||
HTML_BASE_URL = 'dhtml/fim/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('priceset', PriceSetViewSet, basename='priceset')
|
||||
|
|
|
@ -3,7 +3,7 @@ from django.urls import path, include
|
|||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
API_BASE_URL = 'api/hrm/'
|
||||
HTML_BASE_URL = 'hrm/'
|
||||
HTML_BASE_URL = 'dhtml/hrm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('employee', EmployeeViewSet, basename='employee')
|
||||
|
|
|
@ -226,7 +226,7 @@ class MIOItemSerializer(CustomModelSerializer):
|
|||
|
||||
class MIODoSerializer(CustomModelSerializer):
|
||||
belong_dept = serializers.PrimaryKeyRelatedField(
|
||||
label="车间", queryset=Dept.objects.all(), required=True)
|
||||
label="车间", queryset=Dept.objects.all(), required=False)
|
||||
do_user = serializers.PrimaryKeyRelatedField(
|
||||
label="执行人", queryset=User.objects.all(), required=True)
|
||||
note = serializers.CharField(
|
||||
|
@ -236,17 +236,18 @@ class MIODoSerializer(CustomModelSerializer):
|
|||
model = MIO
|
||||
fields = ['id', 'number', 'note', 'do_user',
|
||||
'belong_dept', 'type', 'inout_date', 'mgroup', 'mio_user']
|
||||
extra_kwargs = {'inout_date': {'required': True},
|
||||
'do_user': {'required': True}, 'belong_dept': {'required': False}}
|
||||
read_only_fields = ["number"]
|
||||
extra_kwargs = {'inout_date': {'required': True}, 'do_user': {'required': True}, 'number': {"required": False, "allow_blank": True}}
|
||||
|
||||
def validate(self, attrs):
|
||||
if 'mgroup' in attrs and attrs['mgroup']:
|
||||
attrs['belong_dept'] = attrs['mgroup'].belong_dept
|
||||
if not attrs.get("belong_dept", None):
|
||||
raise ParseError('请选择车间或工段')
|
||||
return attrs
|
||||
|
||||
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_DO_OUT, MIO.MIO_TYPE_DO_IN]:
|
||||
raise ValidationError('出入库类型错误')
|
||||
return super().create(validated_data)
|
||||
|
@ -265,11 +266,13 @@ class MIOSaleSerializer(CustomModelSerializer):
|
|||
class Meta:
|
||||
model = MIO
|
||||
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):
|
||||
validated_data['type'] = MIO.MIO_TYPE_SALE_OUT
|
||||
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.state in [Order.ORDER_CREATE, Order.ORDER_DELIVERED]:
|
||||
raise ValidationError('销售订单状态错误')
|
||||
|
@ -292,12 +295,12 @@ class MIOPurSerializer(CustomModelSerializer):
|
|||
class Meta:
|
||||
model = MIO
|
||||
fields = ['id', 'number', 'note', 'pu_order', 'inout_date', 'supplier', 'mio_user']
|
||||
extra_kwargs = {'inout_date': {'required': True}}
|
||||
read_only_fields = ["number"]
|
||||
extra_kwargs = {'inout_date': {'required': True}, 'number': {"required": False, "allow_blank": True}}
|
||||
|
||||
def create(self, validated_data):
|
||||
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)
|
||||
if pu_order:
|
||||
if pu_order.state in [PuOrder.PUORDER_CREATE, PuOrder.PUORDER_DONE]:
|
||||
|
@ -320,11 +323,11 @@ class MIOOtherSerializer(CustomModelSerializer):
|
|||
model = MIO
|
||||
fields = ['id', 'number', 'note', 'supplier',
|
||||
'customer', 'type', 'inout_date', 'mio_user']
|
||||
extra_kwargs = {'inout_date': {'required': True}}
|
||||
read_only_fields = ["number"]
|
||||
extra_kwargs = {'inout_date': {'required': True}, 'number': {"required": False, "allow_blank": True}}
|
||||
|
||||
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]:
|
||||
raise ValidationError('出入库类型错误')
|
||||
return super().create(validated_data)
|
||||
|
|
|
@ -58,6 +58,8 @@ def do_out(item: MIOItem):
|
|||
raise ParseError("组合件暂不支持追踪单件")
|
||||
|
||||
xbatches = []
|
||||
if is_zhj:
|
||||
xbatches = [item.batch]
|
||||
for al in action_list:
|
||||
xmaterial:Material = al[0]
|
||||
xbatch:str = al[1]
|
||||
|
@ -138,6 +140,8 @@ def do_in(item: MIOItem):
|
|||
production_dept = None
|
||||
|
||||
xbatchs = []
|
||||
if is_zhj:
|
||||
xbatchs = [item.batch]
|
||||
for al in action_list:
|
||||
xmaterial, xbatch, xcount = al
|
||||
xbatchs.append(xbatch)
|
||||
|
@ -165,7 +169,7 @@ def do_in(item: MIOItem):
|
|||
else:
|
||||
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:
|
||||
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
|
||||
}
|
||||
)
|
||||
if mb.production_dept is None:
|
||||
mb.production_dept = production_dept
|
||||
mb.count = mb.count + xcount
|
||||
mb.save()
|
||||
|
||||
|
@ -206,6 +212,10 @@ def do_in(item: MIOItem):
|
|||
)
|
||||
if not is_created:
|
||||
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:
|
||||
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)
|
||||
cal_material_count(m_ids)
|
||||
m_ids = list(MIOItem.objects.filter(mio=instance).values_list('material_id', flat=True))
|
||||
m_ids2 = list(MIOItemA.objects.filter(mioitem__mio=instance).values_list('material_id', flat=True))
|
||||
cal_material_count(m_ids+m_ids2)
|
||||
|
||||
@classmethod
|
||||
def update_inm(cls, instance: MIO, is_reverse: bool = False):
|
||||
|
@ -251,10 +262,16 @@ class InmService:
|
|||
do_in(item)
|
||||
elif instance.type == MIO.MIO_TYPE_SALE_OUT:
|
||||
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)
|
||||
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:
|
||||
mioitems = MIOItem.objects.filter(mio=instance)
|
||||
if is_reverse:
|
||||
|
@ -319,7 +336,10 @@ class InmService:
|
|||
if xcount > 0:
|
||||
defect = defects_map[defect_id]
|
||||
m_list.append((material, warehouse, i.batch, xcount, defect, i))
|
||||
|
||||
xbatchs = []
|
||||
for material, warehouse, batch, change_count, defect, mioitem in m_list:
|
||||
xbatchs.append(batch)
|
||||
if change_count <= 0:
|
||||
continue
|
||||
state = WMaterial.WM_OK
|
||||
|
@ -370,6 +390,10 @@ class InmService:
|
|||
else:
|
||||
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):
|
||||
"""
|
||||
|
|
|
@ -6,7 +6,7 @@ from apps.inm.views import (
|
|||
MioPurViewSet, MioOtherViewSet, MIOItemwViewSet)
|
||||
|
||||
API_BASE_URL = 'api/inm/'
|
||||
HTML_BASE_URL = 'inm/'
|
||||
HTML_BASE_URL = 'dhtml/inm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('warehouse', WarehouseVIewSet, basename='warehouse')
|
||||
|
|
|
@ -254,6 +254,7 @@ class MIOItemViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyMode
|
|||
"test_date": ["isnull", "exact"]
|
||||
}
|
||||
ordering = ['create_time']
|
||||
ordering_fields = ['create_time', 'test_date']
|
||||
|
||||
def add_info_for_list(self, data):
|
||||
|
||||
|
@ -286,6 +287,7 @@ class MIOItemViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyMode
|
|||
sr.save()
|
||||
# 开始变动库存
|
||||
InmService.update_mb_item(ins, -1, 'count_notok')
|
||||
InmService.update_material_count(ins.mio)
|
||||
return Response()
|
||||
|
||||
@action(methods=['post'], detail=True, perms_map={'post': 'mioitem.test'}, serializer_class=serializers.Serializer)
|
||||
|
@ -303,6 +305,7 @@ class MIOItemViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyMode
|
|||
pass
|
||||
ins.test_date = None
|
||||
ins.save()
|
||||
InmService.update_material_count(ins.mio)
|
||||
return Response()
|
||||
|
||||
@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.is_valid(raise_exception=True)
|
||||
sr.save()
|
||||
InmService.update_material_count(ins.mio)
|
||||
return Response()
|
||||
|
||||
@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
|
||||
|
||||
API_BASE_URL = 'api/monitor/'
|
||||
HTML_BASE_URL = 'monitor/'
|
||||
HTML_BASE_URL = 'dhtml/monitor/'
|
||||
urlpatterns = [
|
||||
path(HTML_BASE_URL, index),
|
||||
path(HTML_BASE_URL + 'index/', index),
|
||||
|
|
|
@ -4,7 +4,7 @@ from django.db.models.expressions import F
|
|||
|
||||
|
||||
class MaterialFilter(filters.FilterSet):
|
||||
tag = filters.CharFilter(method='filter_tag')
|
||||
tag = filters.CharFilter(method='filter_tag', label="low_inm:库存不足")
|
||||
|
||||
class Meta:
|
||||
model = Material
|
||||
|
@ -27,7 +27,7 @@ class MaterialFilter(filters.FilterSet):
|
|||
|
||||
def filter_tag(self, queryset, name, value):
|
||||
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'))
|
||||
return queryset
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from apps.mtm.views import (MgroupViewSet, ShiftViewSet, TeamViewSet, MaterialVi
|
|||
RoutePackViewSet, SruleViewSet, RouteMatViewSet)
|
||||
|
||||
API_BASE_URL = 'api/mtm/'
|
||||
HTML_BASE_URL = 'mtm/'
|
||||
HTML_BASE_URL = 'dhtml/mtm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('mgroup', MgroupViewSet, basename='mgroup')
|
||||
|
|
|
@ -265,6 +265,7 @@ class RouteViewSet(CustomModelViewSet):
|
|||
serializer_class = RouteSerializer
|
||||
filterset_class = RouteFilter
|
||||
ordering = ['sort', 'process__sort', 'create_time']
|
||||
ordering_fields = ['sort', 'process__sort', 'create_time', 'update_time']
|
||||
select_related_fields = ['material',
|
||||
'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
|
||||
|
||||
API_BASE_URL = 'api/opm/'
|
||||
HTML_BASE_URL = 'opm/'
|
||||
HTML_BASE_URL = 'dhtml/opm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('opl_cate', OplCateViewSet, basename='opl_cate')
|
||||
|
|
|
@ -24,10 +24,13 @@ class UtaskSerializer(CustomModelSerializer):
|
|||
class Meta:
|
||||
model = Utask
|
||||
fields = '__all__'
|
||||
read_only_fields = ["number"]
|
||||
extra_kwargs = {
|
||||
'number': {"required": False, "allow_blank": True}
|
||||
}
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data["number"] = Utask.get_a_number()
|
||||
if not validated_data.get('number', None):
|
||||
validated_data["number"] = Utask.get_a_number()
|
||||
return super().create(validated_data)
|
||||
|
||||
def validate(self, attrs):
|
||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
|||
from apps.pm.views import (MtaskViewSet, UtaskViewSet, MtaskbViewSet)
|
||||
|
||||
API_BASE_URL = 'api/pm/'
|
||||
HTML_BASE_URL = 'pm/'
|
||||
HTML_BASE_URL = 'dhtml/pm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
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)
|
||||
|
||||
API_BASE_URL = 'api/pum/'
|
||||
HTML_BASE_URL = 'pum/'
|
||||
HTML_BASE_URL = 'dhtml/pum/'
|
||||
|
||||
router = DefaultRouter()
|
||||
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)
|
||||
testitem = models.ForeignKey(
|
||||
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_density = models.FloatField('样品密度', null=True, blank=True)
|
||||
specification = models.CharField(
|
||||
|
|
|
@ -6,7 +6,7 @@ from apps.qm.views import (QuaStatViewSet, TestItemViewSet,
|
|||
NotOkOptionView, DefectViewSet, QctViewSet, QctTestItemViewSet, QctDefectViewSet, QctMatViewSet)
|
||||
|
||||
API_BASE_URL = 'api/qm/'
|
||||
HTML_BASE_URL = 'qm/'
|
||||
HTML_BASE_URL = 'dhtml/qm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('quastat', QuaStatViewSet, basename='quastat')
|
||||
|
|
|
@ -4,7 +4,7 @@ from django.urls import path, include
|
|||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
API_BASE_URL = 'api/rpm/'
|
||||
HTML_BASE_URL = 'rpm/'
|
||||
HTML_BASE_URL = 'dhtml/rpm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('rparty', RpartyViewSet, basename='rparty')
|
||||
|
|
|
@ -13,21 +13,25 @@ class SamService:
|
|||
order = mio.order
|
||||
if order is None:
|
||||
return
|
||||
for i in MIOItem.objects.filter(mio=mio):
|
||||
orderitem = OrderItem.objects.get(order=order, material=i.material)
|
||||
if is_reverse:
|
||||
delivered_count = orderitem.delivered_count - i.count
|
||||
else:
|
||||
delivered_count = orderitem.delivered_count + i.count
|
||||
orderitem_qs = OrderItem.objects.filter(order=order)
|
||||
matIds = list(MIOItem.objects.filter(mio=mio).values_list('material__id', flat=True).distinct())
|
||||
matIds2 = list(orderitem_qs.values_list('material__id', flat=True).distinct())
|
||||
if set(matIds) != set(matIds2):
|
||||
raise ValidationError('订单与发货单物料不一致')
|
||||
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:
|
||||
raise ValidationError((f'{i.material.name}-超出订单所需数量'))
|
||||
raise ValidationError((f'{str(material)}-超出订单所需数量'))
|
||||
elif delivered_count < 0:
|
||||
raise ValidationError((f'{i.material.name}-数量小于0'))
|
||||
raise ValidationError((f'{str(material)}-数量小于0'))
|
||||
orderitem.delivered_count = delivered_count
|
||||
orderitem.save()
|
||||
orderitem.save(update_fields=['delivered_count'])
|
||||
# 更新order的状态
|
||||
qs = OrderItem.objects.filter(
|
||||
order=order, count__lte=F('delivered_count'))
|
||||
order=order, count__gt=F('delivered_count'))
|
||||
order_state = Order.ORDER_DOING
|
||||
if qs.exists():
|
||||
pass
|
||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter
|
|||
from apps.sam.views import (CustomerViewSet, ContractViewSet, OrderViewSet, OrderItemViewSet)
|
||||
|
||||
API_BASE_URL = 'api/sam/'
|
||||
HTML_BASE_URL = 'sam/'
|
||||
HTML_BASE_URL = 'dhtml/sam/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('customer', CustomerViewSet, basename='customer')
|
||||
|
|
|
@ -57,8 +57,8 @@ class OrderViewSet(CustomModelViewSet):
|
|||
queryset = Order.objects.all()
|
||||
serializer_class = OrderSerializer
|
||||
select_related_fields = ['contract', 'customer']
|
||||
search_fields = ['number', 'contract__name', 'contract__number', 'customer__name', 'orderitem_order__material__name',
|
||||
'orderitem_order__material__specification', 'orderitem_order__material__model']
|
||||
search_fields = ['number', 'contract__name', 'contract__number', 'customer__name', 'item_order__material__name',
|
||||
'item_order__material__specification', 'item_order__material__model']
|
||||
filterset_fields = {
|
||||
"contract": ["exact"],
|
||||
"customer": ["exact"],
|
||||
|
|
|
@ -7,7 +7,7 @@ from .views import ApkViewSet, FileViewSet, PTaskViewSet, PTaskResultViewSet, Po
|
|||
from rest_framework import routers
|
||||
|
||||
API_BASE_URL = 'api/system/'
|
||||
HTML_BASE_URL = 'system/'
|
||||
HTML_BASE_URL = 'dhtml/system/'
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register('user', UserViewSet, basename="user")
|
||||
|
|
|
@ -767,6 +767,8 @@ class SysBaseConfigView(APIView):
|
|||
config = get_sysconfig()
|
||||
base_dict = {key: config[key]
|
||||
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)
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ from apps.third.views import DahuaTestView, DhCommonViewSet, SpTestView, Speaker
|
|||
from apps.third.views_d import BltViewSet, TDeviceViewSet, TlogViewSet
|
||||
|
||||
API_BASE_URL = 'api/third/'
|
||||
HTML_BASE_URL = 'third/'
|
||||
HTML_BASE_URL = 'dhtml/third/'
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register('xunxi', XxCommonViewSet, basename='api_xunxi')
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
from django.conf import settings
|
||||
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):
|
||||
|
@ -8,3 +12,9 @@ class MyFilePathField(serializers.CharField):
|
|||
if 'http' in value:
|
||||
return 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
|
||||
import ast
|
||||
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):
|
||||
def __init__(self):
|
||||
|
|
|
@ -4,7 +4,7 @@ from rest_framework.routers import DefaultRouter
|
|||
from apps.vm.views import VisitViewSet, VisitorViewSet, VpeopleViewSet
|
||||
|
||||
API_BASE_URL = 'api/vm/'
|
||||
HTML_BASE_URL = 'vm/'
|
||||
HTML_BASE_URL = 'dhtml/vm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('visit', VisitViewSet, basename='visit')
|
||||
|
|
|
@ -4,7 +4,7 @@ from django.urls import path, include
|
|||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
API_BASE_URL = 'api/wf/'
|
||||
HTML_BASE_URL = 'wf/'
|
||||
HTML_BASE_URL = 'dhtml/wf/'
|
||||
|
||||
router = DefaultRouter()
|
||||
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
|
||||
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
|
||||
def mat_in_qs(cls, mtask: Mtask, qs=None):
|
||||
|
@ -153,9 +153,11 @@ class WMaterial(CommonBDModel):
|
|||
class Fmlog(CommonADModel):
|
||||
"""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')
|
||||
note = models.TextField('备注', default='', blank=True)
|
||||
is_fix = models.BooleanField('是否用于返修', default=False) # 返工/复检
|
||||
enabled = models.BooleanField("是否启用", default=True)
|
||||
|
||||
class Mlog(CommonADModel):
|
||||
|
|
|
@ -212,6 +212,11 @@ class MlogbDefectSerializer(CustomModelSerializer):
|
|||
model = MlogbDefect
|
||||
fields = ["id", "defect_name", "count", "mlogb", "defect", "defect_okcate"]
|
||||
read_only_fields = EXCLUDE_FIELDS_BASE + ["mlogb"]
|
||||
|
||||
def validate(self, attrs):
|
||||
if attrs["count"] < 0:
|
||||
raise serializers.ValidationError("存在负数!")
|
||||
return attrs
|
||||
|
||||
class MlogbSerializer(CustomModelSerializer):
|
||||
material_out_ = MaterialSimpleSerializer(
|
||||
|
@ -541,7 +546,7 @@ class MlogSerializer(CustomModelSerializer):
|
|||
attrs['mgroup'] = fmlog.mgroup
|
||||
attrs['mtask'] = fmlog.mtask
|
||||
attrs['mtype'] = fmlog.mgroup.mtype
|
||||
if attrs['mtask'].route:
|
||||
if attrs['mtask'] and attrs['mtask'].route:
|
||||
attrs['route'] = attrs['mtask'].route
|
||||
# if attrs['mtask'].mtaskb and mtaskb is None:
|
||||
# raise ParseError('子任务不能为空')
|
||||
|
@ -675,6 +680,8 @@ class MlogbInSerializer(CustomModelSerializer):
|
|||
'wm_in': {'required': True, "allow_empty": False}}
|
||||
|
||||
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']
|
||||
is_fix = mlog.is_fix
|
||||
mtask: Mtask = attrs.get("mtask", None)
|
||||
|
@ -739,6 +746,10 @@ class MlogbInUpdateSerializer(CustomModelSerializer):
|
|||
model = Mlogb
|
||||
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):
|
||||
mlog: Mlog = instance.mlog
|
||||
mlogbdefect = validated_data.pop("mlogbdefect", None)
|
||||
|
@ -1153,6 +1164,7 @@ class AttLogSerializer(CustomModelSerializer):
|
|||
class FmlogSerializer(CustomModelSerializer):
|
||||
routepack_name = serializers.CharField(
|
||||
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)
|
||||
class Meta:
|
||||
model = Fmlog
|
||||
|
@ -1160,6 +1172,11 @@ class FmlogSerializer(CustomModelSerializer):
|
|||
read_only_fields = EXCLUDE_FIELDS
|
||||
|
||||
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']
|
||||
if mtask.state != Mtask.MTASK_ASSGINED:
|
||||
raise ParseError('该任务非下达中不可选择')
|
||||
|
|
|
@ -5,13 +5,19 @@ from apps.inm.models import MIOItem
|
|||
from apps.qm.models import FtestWork
|
||||
from django.utils import timezone
|
||||
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
|
||||
BASE_PROJECT_CODE = getattr(settings, "BASE_PROJECT_CODE", None)
|
||||
if BASE_PROJECT_CODE == "gzerp":
|
||||
need_update = True
|
||||
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={
|
||||
"last_time": last_time
|
||||
})
|
||||
bobj.last_time = last_time
|
||||
bobj.data = data
|
||||
if need_update_time:
|
||||
bobj.last_time = last_time
|
||||
bobj.data = json.loads(json.dumps(data, cls=MyJSONEncoder))
|
||||
bobj.save()
|
||||
|
||||
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)
|
||||
except ZeroDivisionError:
|
||||
data["棒料成型_合格率"] = 0
|
||||
except decimal.InvalidOperation:
|
||||
myLogger.error(f"棒料成型_合格率计算错误decimal.InvalidOperation-{data}")
|
||||
data["棒料成型_合格率"] = 0
|
||||
|
||||
# 管料成型数据
|
||||
mgroup_glcx = Mgroup.objects.get(name="管料成型")
|
||||
|
@ -118,11 +128,12 @@ def get_alldata_with_batch(batch: str):
|
|||
if item.mio.do_user:
|
||||
data["七车间入库_仓库执行人"].append(item.mio.mio_user)
|
||||
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:
|
||||
data[f'七车间入库_{field}'] = int(getattr(item, field))
|
||||
data[f'七车间入库_{field}'] = f_v
|
||||
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["七车间入库_日期"] = list(set(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:
|
||||
data["十车间入库_抽检人"].append(item.test_user)
|
||||
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:
|
||||
data[f'十车间入库_{field}'] = int(getattr(item, field))
|
||||
data[f'十车间入库_{field}'] = getattr(item, field)
|
||||
else:
|
||||
data[f'十车间入库_{field}'] += int(getattr(item, field))
|
||||
data[f'十车间入库_{field}'] += getattr(item, field)
|
||||
data["十车间入库_抽检人"] = list(set(data["十车间入库_抽检人"]))
|
||||
data["十车间入库_抽检人"] = ";".join([item.name for item in data["十车间入库_抽检人"]])
|
||||
if data["十车间入库_count_sampling"] > 0:
|
||||
|
@ -185,11 +196,11 @@ def get_alldata_with_batch(batch: str):
|
|||
if item.handle_user:
|
||||
data["管料退火_操作人"].append(item.handle_user)
|
||||
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:
|
||||
data[f'管料退火_{field}'] = int(getattr(item, field))
|
||||
data[f'管料退火_{field}'] = getattr(item, field)
|
||||
else:
|
||||
data[f'管料退火_{field}'] += int(getattr(item, field))
|
||||
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["管料退火_操作人"]))
|
||||
|
@ -216,11 +227,11 @@ def get_alldata_with_batch(batch: str):
|
|||
if item.mio.mio_user:
|
||||
data["六车间领料_车间执行人"].append(item.mio.mio_user)
|
||||
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:
|
||||
data[f'六车间领料_{field}'] = int(getattr(item, field))
|
||||
data[f'六车间领料_{field}'] = getattr(item, field)
|
||||
else:
|
||||
data[f'六车间领料_{field}'] += int(getattr(item, field))
|
||||
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["六车间领料_仓库执行人"]))
|
||||
|
@ -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}_操作人'] = list(set(data[f'六车间_{mgroup_name}_操作人']))
|
||||
data[f'六车间_{mgroup_name}_操作人'] = ";".join([item.name for item in data[f'六车间_{mgroup_name}_操作人']])
|
||||
data[f'六车间_{mgroup_name}_合格率'] = round(data[f'六车间_{mgroup_name}_count_ok'] * 100/ data[f'六车间_{mgroup_name}_count_real'], 1)
|
||||
|
||||
# 六车间入库/检验数据
|
||||
# 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["六车间生产入库_日期"] = []
|
||||
# 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["六车间生产入库_日期"]])
|
||||
try:
|
||||
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}")
|
||||
data[f'六车间_{mgroup_name}_合格率'] = 0
|
||||
|
||||
ftestwork_count_fields = FtestWork.count_fields()
|
||||
# 六车间中检数据
|
||||
|
@ -311,6 +309,34 @@ def get_alldata_with_batch(batch: str):
|
|||
data['六车间中检_检验人'] = list(set(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")
|
||||
if ftestwork_qs.exists():
|
||||
|
@ -340,9 +366,29 @@ def get_alldata_with_batch(batch: str):
|
|||
data['成品检验_检验人'] = list(set(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 data["棒料成型_count_real"]:
|
||||
if getattr(data, "六车间领料_count", 0) > 0:
|
||||
data["六车间_批次生产合格率"] = round(data["成品检验_count_ok"] * 100/ data["六车间领料_count"], 1)
|
||||
if getattr(data, "棒料成型_count_real", 0) > 0:
|
||||
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
|
||||
|
|
@ -10,7 +10,7 @@ from apps.wpm.datax import AnaViewSet
|
|||
|
||||
|
||||
API_BASE_URL = 'api/wpm/'
|
||||
HTML_BASE_URL = 'wpm/'
|
||||
HTML_BASE_URL = 'dhtml/wpm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('sflog', SfLogViewSet, basename='sflog')
|
||||
|
|
|
@ -136,7 +136,7 @@ class WMaterialViewSet(ListModelMixin, CustomGenericViewSet):
|
|||
serializer_class = WMaterialSerializer
|
||||
select_related_fields = ['material', 'belong_dept', 'material__process', 'supplier']
|
||||
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
|
||||
ordering_fields = ["update_time", "state", "count", "count_xtest"]
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ from apps.wpmw.views import WprViewSet
|
|||
|
||||
|
||||
API_BASE_URL = 'api/wpmw/'
|
||||
HTML_BASE_URL = 'wpmw/'
|
||||
HTML_BASE_URL = 'dhtml/wpmw/'
|
||||
|
||||
router = DefaultRouter()
|
||||
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
|
||||
from . import conf
|
||||
from config import conf
|
||||
from celery import Celery
|
||||
from celery.app.control import Control, Inspect
|
||||
|
||||
|
|
|
@ -14,8 +14,10 @@ from datetime import datetime, timedelta
|
|||
import os
|
||||
import json
|
||||
import sys
|
||||
from .conf import *
|
||||
from config.conf import *
|
||||
from django.core.cache import cache
|
||||
import logging
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
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 = ['*']
|
||||
|
||||
SYS_NAME = '星途工厂综合管理系统'
|
||||
SYS_VERSION = '2.3.0'
|
||||
|
||||
SYS_VERSION = '2.6.2025033109'
|
||||
X_FRAME_OPTIONS = 'SAMEORIGIN'
|
||||
|
||||
# Application definition
|
||||
|
||||
|
@ -270,6 +272,18 @@ LOG_PATH = os.path.join(BASE_DIR, 'log')
|
|||
if not os.path.exists(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 = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
|
@ -293,22 +307,28 @@ LOGGING = {
|
|||
# 默认记录所有日志
|
||||
'default': {
|
||||
'level': 'INFO',
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': os.path.join(LOG_PATH, 'all-{}.log'.format(datetime.now().strftime('%Y-%m-%d'))),
|
||||
'class': 'server.settings.TimedSizeRotatingHandler',
|
||||
'filename': os.path.join(LOG_PATH, 'all.log'),
|
||||
'when': 'midnight', # 每天午夜滚动
|
||||
'interval': 1,
|
||||
'maxBytes': 1024 * 1024 * 2, # 文件大小
|
||||
'backupCount': 10, # 备份数
|
||||
'backupCount': 30, # 备份数
|
||||
'formatter': 'standard', # 输出格式
|
||||
'encoding': 'utf-8', # 设置默认编码,否则打印出来汉字乱码
|
||||
'delay': True, # 延迟打开文件,减少锁定冲突
|
||||
},
|
||||
# 输出错误日志
|
||||
'error': {
|
||||
'level': 'ERROR',
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': os.path.join(LOG_PATH, 'error-{}.log'.format(datetime.now().strftime('%Y-%m-%d'))),
|
||||
'class': 'server.settings.TimedSizeRotatingHandler',
|
||||
'filename': os.path.join(LOG_PATH, 'error.log'),
|
||||
'when': 'midnight',
|
||||
'interval': 1,
|
||||
'maxBytes': 1024 * 1024 * 2, # 文件大小
|
||||
'backupCount': 10, # 备份数
|
||||
'backupCount': 30, # 备份数
|
||||
'formatter': 'standard', # 输出格式
|
||||
'encoding': 'utf-8', # 设置默认编码
|
||||
'delay': True, # 延迟打开文件,减少锁定冲突
|
||||
},
|
||||
# 控制台输出
|
||||
'console': {
|
||||
|
@ -320,12 +340,15 @@ LOGGING = {
|
|||
# 输出info日志
|
||||
'info': {
|
||||
'level': 'INFO',
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': os.path.join(LOG_PATH, 'info-{}.log'.format(datetime.now().strftime('%Y-%m-%d'))),
|
||||
'class': 'server.settings.TimedSizeRotatingHandler',
|
||||
'filename': os.path.join(LOG_PATH, 'info.log'),
|
||||
'when': 'midnight',
|
||||
'interval': 1,
|
||||
'maxBytes': 1024 * 1024 * 2,
|
||||
'backupCount': 10,
|
||||
'backupCount': 30,
|
||||
'formatter': 'standard',
|
||||
'encoding': 'utf-8', # 设置默认编码
|
||||
'delay': True, # 延迟打开文件,减少锁定冲突
|
||||
},
|
||||
},
|
||||
# 配置用哪几种 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):
|
||||
"""获取系统配置可指定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