Compare commits
258 Commits
3.0.202511
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
d29fcce935 | |
|
|
a534bde086 | |
|
|
63002f27c8 | |
|
|
4bbae8b7df | |
|
|
dc26c7cc46 | |
|
|
0d80e182cd | |
|
|
2759114ede | |
|
|
80f832aa85 | |
|
|
70e49eb27e | |
|
|
e99b2ecbbc | |
|
|
146e842642 | |
|
|
47b1887c4b | |
|
|
1ffbe0cc44 | |
|
|
3e173f7a72 | |
|
|
fce66da1d9 | |
|
|
feb8bd6770 | |
|
|
43f5f11ca8 | |
|
|
d5ea72a021 | |
|
|
143d9cb719 | |
|
|
cf6633592a | |
|
|
b39b0e7923 | |
|
|
70563a6c02 | |
|
|
def22f6b18 | |
|
|
f9eee5a523 | |
|
|
2ecaeadff7 | |
|
|
6eee0e1e53 | |
|
|
3417515e72 | |
|
|
43abcbaa48 | |
|
|
e2a92b6faa | |
|
|
02e3265133 | |
|
|
65cdeb0e7c | |
|
|
238d1dd074 | |
|
|
f7a78431c5 | |
|
|
5b60318bfb | |
|
|
0127e2a149 | |
|
|
f5b1b13a63 | |
|
|
f7b09ab1df | |
|
|
b362fc3b89 | |
|
|
8f791ac8de | |
|
|
afa3b8b9ad | |
|
|
3d6fcfac8a | |
|
|
db910f0804 | |
|
|
7dc7a78695 | |
|
|
952cdb1bc7 | |
|
|
9ed78f8d32 | |
|
|
52ebac68a0 | |
|
|
81770e89fa | |
|
|
6ac7c020bd | |
|
|
e385a558e9 | |
|
|
c37e71d60f | |
|
|
c3c7675ac5 | |
|
|
29f4e2f76a | |
|
|
ec13b8b166 | |
|
|
c56f908b42 | |
|
|
a8ae8ee32a | |
|
|
3f183583c8 | |
|
|
09a7c64b3c | |
|
|
aa07c041fb | |
|
|
29f4edccb8 | |
|
|
ca67a1479f | |
|
|
90cb2ed7f7 | |
|
|
79e957bd83 | |
|
|
00f2918d17 | |
|
|
f84ceaa95e | |
|
|
da1f587036 | |
|
|
55c94a83ce | |
|
|
aad329061b | |
|
|
16a04b43f0 | |
|
|
4536c136bb | |
|
|
7400202209 | |
|
|
d7c3e92f9e | |
|
|
a408b2db92 | |
|
|
b250b9b078 | |
|
|
725f7d1764 | |
|
|
75c7f26448 | |
|
|
dec8650af1 | |
|
|
a81e851588 | |
|
|
4996b0e4a3 | |
|
|
79c8115445 | |
|
|
21424a61bd | |
|
|
b5fc176cf2 | |
|
|
ba521b1107 | |
|
|
81d2cbce8c | |
|
|
45f9f27cbe | |
|
|
c440707612 | |
|
|
5dfb903c4a | |
|
|
7fc995c7c9 | |
|
|
07134f32bb | |
|
|
041474411e | |
|
|
07b2df1fd0 | |
|
|
57459cc7dc | |
|
|
ec7b0cd987 | |
|
|
5711791a1f | |
|
|
4b7e648b13 | |
|
|
0c5034683c | |
|
|
c35041f72b | |
|
|
71fe281a90 | |
|
|
54f4aed729 | |
|
|
d3c5358d2c | |
|
|
190f2f047d | |
|
|
3dcb26f14a | |
|
|
01615b4b27 | |
|
|
be4dfe85ee | |
|
|
a9aac76328 | |
|
|
16a513f331 | |
|
|
de0149dce4 | |
|
|
1af6555e26 | |
|
|
320da10e61 | |
|
|
7f34d45160 | |
|
|
8c18bbf6ae | |
|
|
636042e9c3 | |
|
|
e607dfd51b | |
|
|
ddd75d408a | |
|
|
6584334d00 | |
|
|
d36edaffe1 | |
|
|
33f6c3982d | |
|
|
984f875a6c | |
|
|
8ee48f77fc | |
|
|
4bea3d973e | |
|
|
18c66eee7a | |
|
|
77c7811476 | |
|
|
d104ee90a4 | |
|
|
09c3bec66a | |
|
|
75b2a420b7 | |
|
|
7e09b872cf | |
|
|
53a56ace1f | |
|
|
9388eada7d | |
|
|
81aa49d339 | |
|
|
adee19eb5b | |
|
|
0519facc9e | |
|
|
fadf1e979d | |
|
|
a78b3a98a0 | |
|
|
45fc29e9fe | |
|
|
c60424e946 | |
|
|
cad2a7e50a | |
|
|
39066f2124 | |
|
|
8211a33b58 | |
|
|
a51494aea9 | |
|
|
2fcfae518d | |
|
|
2bd43f0465 | |
|
|
4ef834570e | |
|
|
0c1a7a4f60 | |
|
|
36df498e08 | |
|
|
5be3f6fbe5 | |
|
|
f3e8e8d3c2 | |
|
|
df1f6f07a1 | |
|
|
700f238a8e | |
|
|
72f8637e19 | |
|
|
8af5be4429 | |
|
|
1e391f36ec | |
|
|
e494b659d6 | |
|
|
6112ac81f5 | |
|
|
e98027776c | |
|
|
ff1e60fc77 | |
|
|
9ccfcbec03 | |
|
|
f66e5bd9df | |
|
|
004833292d | |
|
|
e07a1fdc4c | |
|
|
8970d5b04e | |
|
|
57330e6ac6 | |
|
|
1074fd35bd | |
|
|
d0493eb87b | |
|
|
355be71775 | |
|
|
95604372a4 | |
|
|
ae7863871a | |
|
|
3f2db2a4af | |
|
|
5db4a99611 | |
|
|
f70605129c | |
|
|
873e4bd80e | |
|
|
2dbd7da2f8 | |
|
|
e9d8402cda | |
|
|
0776ff0054 | |
|
|
b939fcbef2 | |
|
|
dc17c01aaf | |
|
|
146756d1b8 | |
|
|
c6dc424b33 | |
|
|
54196b2a64 | |
|
|
dcd41792fa | |
|
|
a58d95843f | |
|
|
0817589758 | |
|
|
99df0166f8 | |
|
|
5fa4881614 | |
|
|
5d7c681983 | |
|
|
5ed3b7c483 | |
|
|
706cfd502b | |
|
|
29f1a96c3b | |
|
|
cbd458a8a5 | |
|
|
caf1bba050 | |
|
|
01d93d7814 | |
|
|
5e81a3d3dc | |
|
|
f8db7adab5 | |
|
|
a81d03e3ca | |
|
|
3ff6072ae1 | |
|
|
ab7b08b6f2 | |
|
|
f3daa9fe91 | |
|
|
362497af37 | |
|
|
f290138c40 | |
|
|
e2129cc799 | |
|
|
4157dd7aa6 | |
|
|
0b2b7f2fa2 | |
|
|
8267f8dda6 | |
|
|
fb87fead4f | |
|
|
62d0ce87ea | |
|
|
50c21292a9 | |
|
|
4ba952d705 | |
|
|
c6ec4f6c56 | |
|
|
e093498d3c | |
|
|
168d917232 | |
|
|
8b74d0b121 | |
|
|
e7ebdb0e8e | |
|
|
6d030e2c06 | |
|
|
c739fcfd79 | |
|
|
0141e539e7 | |
|
|
75305bd1fd | |
|
|
7c3e63668e | |
|
|
c21a39b52d | |
|
|
53db1cdc35 | |
|
|
6c24017905 | |
|
|
509fdb3656 | |
|
|
af09d35177 | |
|
|
230b71f0aa | |
|
|
1d74171988 | |
|
|
7a10da7e2b | |
|
|
164f3f16bb | |
|
|
2799e85605 | |
|
|
097adb063e | |
|
|
8943443e24 | |
|
|
1b88386193 | |
|
|
eca63e993d | |
|
|
d16801c365 | |
|
|
5109d6aa22 | |
|
|
2300d5bfad | |
|
|
55292558d1 | |
|
|
aa62b04fcf | |
|
|
acf09d0056 | |
|
|
e001de9bb6 | |
|
|
3ccf7a87b0 | |
|
|
1ecc6a9f67 | |
|
|
afd6e60cbb | |
|
|
6e5db774b7 | |
|
|
87ffde0945 | |
|
|
9acb5eb98a | |
|
|
2d81424009 | |
|
|
32b39ee423 | |
|
|
5d85abfad1 | |
|
|
5b4a8445ea | |
|
|
3cc9b0b117 | |
|
|
9408dbd6e5 | |
|
|
f62bc532de | |
|
|
0710696aa4 | |
|
|
058141151e | |
|
|
5e05582d91 | |
|
|
01305eeb08 | |
|
|
030f3c62de | |
|
|
0c97939165 | |
|
|
4b377e115c | |
|
|
590357468d | |
|
|
66d523b711 |
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AsmConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'apps.asm'
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
# Generated by Django 3.2.12 on 2025-12-18 08:36
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('system', '0006_auto_20241213_1249'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('pum', '0009_supplieraudit'),
|
||||
('wf', '0006_auto_20251215_1645'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AssetLog',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('type', models.CharField(help_text='入库/出库', max_length=50, verbose_name='流水类型')),
|
||||
('start_date', models.DateField(blank=True, null=True, verbose_name='启用日期')),
|
||||
('items', models.JSONField(blank=True, default=list, null=True, verbose_name='资产明细')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assetlog_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('keep_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='system.dept', verbose_name='保管部门')),
|
||||
('ticket', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='assetlog_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assetlog_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AssetCate',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('name', models.CharField(max_length=50, unique=True, verbose_name='类别名称')),
|
||||
('code', models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='类别编码')),
|
||||
('default_unit', models.CharField(default='台', max_length=20, verbose_name='默认单位')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assetcate_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assetcate_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Asset',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('card_number', models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='卡片编号')),
|
||||
('name', models.CharField(max_length=100, verbose_name='固定资产名称')),
|
||||
('specification', models.CharField(blank=True, max_length=100, null=True, verbose_name='规格型号')),
|
||||
('quantity', models.PositiveIntegerField(default=1, verbose_name='数量')),
|
||||
('start_date', models.DateField(verbose_name='启用日期')),
|
||||
('canuse_year', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='可用年限')),
|
||||
('original_value', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='资产原值')),
|
||||
('storage_location', models.CharField(blank=True, max_length=100, null=True, verbose_name='存放地点')),
|
||||
('state', models.CharField(help_text='在用/闲置', max_length=50, verbose_name='使用状态')),
|
||||
('unit', models.CharField(default='台', max_length=50, verbose_name='计量单位')),
|
||||
('note', models.TextField(blank=True, null=True, verbose_name='备注')),
|
||||
('cate', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='asm.assetcate', verbose_name='资产类别')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='asset_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('keep_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='system.dept', verbose_name='保管部门')),
|
||||
('keeper', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='保管人')),
|
||||
('supplier', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='pum.supplier', verbose_name='供应商')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='asset_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 3.2.12 on 2025-12-24 06:53
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('asm', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='assetlog',
|
||||
name='keeper',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='保管人'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
from apps.utils.models import CommonADModel, CommonBDModel, CommonAModel
|
||||
from django.db import models
|
||||
|
||||
class AssetCate(CommonAModel):
|
||||
name = models.CharField('类别名称',max_length=50, unique=True)
|
||||
code = models.CharField('类别编码',max_length=50, unique=True, null=True, blank=True)
|
||||
default_unit = models.CharField('默认单位', max_length=20, default='台')
|
||||
|
||||
class Asset(CommonAModel):
|
||||
"""
|
||||
TN:固定资产台账
|
||||
"""
|
||||
card_number = models.CharField('卡片编号',max_length=50, unique=True, blank=True, null=True)
|
||||
name = models.CharField('固定资产名称',max_length=100)
|
||||
specification = models.CharField("规格型号", max_length=100, blank=True, null=True)
|
||||
cate = models.ForeignKey(AssetCate, verbose_name='资产类别', on_delete=models.PROTECT)
|
||||
quantity = models.PositiveIntegerField("数量", default=1)
|
||||
start_date = models.DateField("启用日期")
|
||||
canuse_year = models.PositiveSmallIntegerField("可用年限", null=True, blank=True)
|
||||
original_value = models.DecimalField('资产原值', max_digits=15, decimal_places=2)
|
||||
storage_location = models.CharField("存放地点", max_length=100, blank=True, null=True)
|
||||
keeper = models.ForeignKey('system.user', verbose_name='保管人', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
keep_dept = models.ForeignKey('system.dept', verbose_name='保管部门', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
state = models.CharField("使用状态", max_length=50, help_text="在用/闲置")
|
||||
supplier = models.ForeignKey('pum.supplier', verbose_name='供应商', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
unit = models.CharField("计量单位", max_length=50, default='台')
|
||||
note = models.TextField("备注", blank=True, null=True)
|
||||
|
||||
class AssetLog(CommonADModel):
|
||||
"""
|
||||
TN:资产操作日志
|
||||
"""
|
||||
type = models.CharField("流水类型", max_length=50, help_text="入库/出库")
|
||||
keep_dept = models.ForeignKey('system.dept', verbose_name='保管部门', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
keeper = models.ForeignKey('system.user', verbose_name='保管人', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
start_date = models.DateField("启用日期", null=True, blank=True)
|
||||
items = models.JSONField(verbose_name='资产明细', default=list, null=True, blank=True)
|
||||
ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单',
|
||||
on_delete=models.PROTECT, related_name='assetlog_ticket', null=True, blank=True)
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
from apps.asm.models import Asset, AssetLog, AssetCate
|
||||
from apps.utils.serializers import CustomModelSerializer
|
||||
from apps.utils.constants import EXCLUDE_FIELDS_DEPT, EXCLUDE_FIELDS
|
||||
from rest_framework import serializers
|
||||
from apps.system.models import User
|
||||
from apps.wf.serializers import TicketSimpleSerializer
|
||||
|
||||
class AssetCateSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
model = AssetCate
|
||||
fields = '__all__'
|
||||
read_only_fields = EXCLUDE_FIELDS
|
||||
|
||||
class AssetSerializer(CustomModelSerializer):
|
||||
keep_dept_name = serializers.CharField(source="keep_dept.name", read_only=True)
|
||||
keeper_name = serializers.CharField(source="keeper.name", read_only=True)
|
||||
cate_name = serializers.CharField(source="cate.name", read_only=True)
|
||||
class Meta:
|
||||
model = Asset
|
||||
fields = '__all__'
|
||||
read_only_fields = EXCLUDE_FIELDS
|
||||
|
||||
class AssetCreateSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
model = Asset
|
||||
fields = '__all__'
|
||||
read_only_fields = EXCLUDE_FIELDS
|
||||
|
||||
|
||||
class AssetLogSerializer(CustomModelSerializer):
|
||||
keeper_name = serializers.CharField(source="keeper.name", read_only=True)
|
||||
keep_dept_name = serializers.CharField(source="keep_dept.name", read_only=True)
|
||||
ticket_ = TicketSimpleSerializer(source="ticket", read_only=True)
|
||||
class Meta:
|
||||
model = AssetLog
|
||||
fields = '__all__'
|
||||
read_only_fields = EXCLUDE_FIELDS_DEPT
|
||||
|
||||
class AssetLogCreateSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
model = AssetLog
|
||||
fields = ["keep_dept", "start_date", "items", "keeper", "type"]
|
||||
extra_kwargs = {"start_date": {"required": True}, "keep_dept": {"required": True}}
|
||||
|
||||
def to_internal_value(self, data):
|
||||
for item in data.get("items", []):
|
||||
if item.get("keeper", None) is None:
|
||||
item["keeper"] = data.get("keeper", None)
|
||||
if item.get("keep_dept", None) is None:
|
||||
item["keep_dept"] = data["keep_dept"]
|
||||
if item.get("start_date", None) is None:
|
||||
item["start_date"] = data["start_date"]
|
||||
item["state"] = "在用"
|
||||
return super().to_internal_value(data)
|
||||
|
||||
def validate_items(self, value):
|
||||
AssetCreateSerializer(data=value, many=True).is_valid(raise_exception=True)
|
||||
return value
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from apps.asm.views import AssetViewSet, AssetLogViewSet, AssetCateViewSet
|
||||
|
||||
API_BASE_URL = 'api/asm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('assetcate', AssetCateViewSet, basename='assetcate')
|
||||
router.register('asset', AssetViewSet, basename='asset')
|
||||
router.register('assetlog', AssetLogViewSet, basename='assetlog')
|
||||
urlpatterns = [
|
||||
path(API_BASE_URL, include(router.urls)),
|
||||
]
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
from apps.wf.mixins import TicketMixin
|
||||
from apps.utils.viewsets import CustomModelViewSet
|
||||
from apps.asm.models import Asset, AssetLog, AssetCate
|
||||
from rest_framework.exceptions import ParseError
|
||||
from apps.asm.serializers import AssetSerializer, AssetLogSerializer, AssetCateSerializer, AssetLogCreateSerializer
|
||||
from apps.wf.models import Ticket
|
||||
from apps.pum.models import Supplier
|
||||
|
||||
class AssetCateViewSet(CustomModelViewSet):
|
||||
"""
|
||||
list: 固定资产分类
|
||||
|
||||
固定资产分类
|
||||
"""
|
||||
queryset = AssetCate.objects.all()
|
||||
serializer_class = AssetCateSerializer
|
||||
search_fields = ['name', 'code']
|
||||
|
||||
|
||||
class AssetViewSet(CustomModelViewSet):
|
||||
"""
|
||||
list: 固定资产台账
|
||||
|
||||
固定资产台账
|
||||
"""
|
||||
queryset = Asset.objects.all()
|
||||
serializer_class = AssetSerializer
|
||||
search_fields = ['name', 'card_number', 'specification']
|
||||
filterset_fields = ['keep_dept', 'state']
|
||||
|
||||
|
||||
class AssetLogViewSet(TicketMixin, CustomModelViewSet):
|
||||
"""
|
||||
list: 固定资产操作
|
||||
|
||||
固定资产操作
|
||||
"""
|
||||
queryset = AssetLog.objects.all()
|
||||
serializer_class = AssetLogSerializer
|
||||
create_serializer_class = AssetLogCreateSerializer
|
||||
|
||||
def add_info_for_list(self, data):
|
||||
supplierIds = []
|
||||
cateIds = []
|
||||
for dataitem in data:
|
||||
supplierIds.extend([item["supplier"] for item in dataitem["items"]])
|
||||
cateIds.extend([item["cate"] for item in dataitem["items"]])
|
||||
supplier_dict = dict(Supplier.objects.filter(id__in=supplierIds).values_list("id", "name"))
|
||||
assetcate_dict = dict(AssetCate.objects.filter(id__in=cateIds).values_list("id", "name"))
|
||||
for dataitem in data:
|
||||
for item in dataitem["items"]:
|
||||
item["supplier_name"] = supplier_dict.get(item["supplier"], None)
|
||||
item["cate_name"] = assetcate_dict.get(item["cate"], None)
|
||||
return data
|
||||
|
||||
def get_workflow_key(self, instance:AssetLog):
|
||||
if instance.type == "入库":
|
||||
return "wf_assetlogin"
|
||||
raise ParseError("不支持的流程类型")
|
||||
|
||||
def gen_other_ticket_data(self, instance:AssetLog):
|
||||
return {"keep_dept_name": instance.keep_dept.name}
|
||||
|
||||
@staticmethod
|
||||
def apply(ticket: Ticket, transition, new_ticket_data: dict):
|
||||
assetlog:AssetLog = ticket.assetlog_ticket
|
||||
items = assetlog.items
|
||||
sr = AssetSerializer(data=items, many=True)
|
||||
sr.is_valid(raise_exception=True)
|
||||
sr.save(create_by=ticket.create_by)
|
||||
|
||||
|
|
@ -4,6 +4,7 @@ from jinja2 import Template
|
|||
from apps.bi.models import Dataset
|
||||
import concurrent
|
||||
from apps.utils.sql import execute_raw_sql, format_sqldata
|
||||
from apps.utils.tools import MyJSONEncoder
|
||||
|
||||
forbidden_keywords = ["UPDATE", "DELETE", "DROP", "TRUNCATE", "INSERT", "CREATE", "ALTER", "GRANT", "REVOKE", "EXEC", "EXECUTE"]
|
||||
|
||||
|
|
@ -25,7 +26,7 @@ def format_json_with_placeholders(json_str, **kwargs):
|
|||
|
||||
# 遍历关键字参数,将占位符替换为对应的值
|
||||
for key, value in kwargs.items():
|
||||
formatted_json = formatted_json.replace("{" + key + "}", json.dumps(value))
|
||||
formatted_json = formatted_json.replace("{" + key + "}", json.dumps(value, cls=MyJSONEncoder))
|
||||
|
||||
# 格式化后的字符串依然是 JSON 字符串,没有使用 json.loads()
|
||||
return formatted_json
|
||||
|
|
|
|||
|
|
@ -146,7 +146,9 @@ class DatasetRecordViewSet(ListModelMixin, CustomGenericViewSet):
|
|||
queryset = DatasetRecord.objects.all()
|
||||
serializer_class = DatasetRecordSerializer
|
||||
filterset_fields = {
|
||||
"timex": ["year", "month", "day"]
|
||||
"timex": ["year", "month", "day"],
|
||||
"dataset": ["exact"],
|
||||
"dataset__code": ["exact"]
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -670,7 +670,6 @@ class TestViewSet(CustomGenericViewSet):
|
|||
Handover.objects.all().delete()
|
||||
Ftest.objects.all().delete()
|
||||
FtestWork.objects.all().delete()
|
||||
Material.objects.get_queryset(all=True).update(count=0, count_mb=0, count_wm=0)
|
||||
BatchSt.objects.all().delete()
|
||||
Ticket.objects.get_queryset(all=True).delete()
|
||||
return Response()
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ router.register('question', QuestionViewSet, basename='question')
|
|||
router.register('paper', PaperViewSet, basename='paper')
|
||||
router.register('exam', ExamViewSet, basename='exam')
|
||||
router.register('examrecord', ExamRecordViewSet, basename='examrecord')
|
||||
router.register('training', TrainRecordViewSet, basename='examrecord')
|
||||
router.register('training', TrainRecordViewSet, basename='training')
|
||||
urlpatterns = [
|
||||
path(API_BASE_URL, include(router.urls)),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
# Generated by Django 3.2.12 on 2025-12-15 07:16
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('wf', '0005_workflow_cate'),
|
||||
('em', '0022_equipment_cd_req_addr'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Repair',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('fault_description', models.TextField(verbose_name='故障描述')),
|
||||
('fault_cate', models.TextField(blank=True, null=True, verbose_name='故障类别')),
|
||||
('repair_start_time', models.DateTimeField(blank=True, null=True, verbose_name='维修开始时间')),
|
||||
('repair_duration', models.DecimalField(decimal_places=1, default=0.0, max_digits=5, verbose_name='维修工时(小时)')),
|
||||
('repair_description', models.TextField(blank=True, null=True, verbose_name='维修描述')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='repair_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('equipment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='repaire_equip', to='em.equipment', verbose_name='关联设备')),
|
||||
('repair_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='维修人')),
|
||||
('ticket', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='repair_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='repair_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from django.db import models
|
||||
from apps.utils.models import CommonBModel, CommonADModel
|
||||
from apps.utils.models import CommonBModel, CommonADModel, CommonBDModel
|
||||
from apps.system.models import User
|
||||
|
||||
# Create your models here.
|
||||
|
|
@ -156,3 +156,20 @@ class EInspect(CommonADModel):
|
|||
inspect_time = models.DateTimeField("巡检时间")
|
||||
result = models.CharField(max_length=20, choices=INSPECT_RESULTS, verbose_name="巡检结果", help_text=str(INSPECT_RESULTS))
|
||||
note = models.TextField("备注", null=True, blank=True)
|
||||
|
||||
|
||||
class Repair(CommonADModel):
|
||||
"""
|
||||
TN:维修申请
|
||||
"""
|
||||
equipment = models.ForeignKey(Equipment, verbose_name="关联设备", on_delete=models.CASCADE, related_name="repaire_equip")
|
||||
fault_description = models.TextField("故障描述")
|
||||
fault_cate = models.TextField("故障类别", null=True, blank=True)
|
||||
repair_user = models.ForeignKey("system.user", verbose_name="维修人", on_delete=models.CASCADE, null=True, blank=True)
|
||||
repair_start_time = models.DateTimeField("维修开始时间", null=True, blank=True)
|
||||
repair_duration = models.DecimalField("维修工时(小时)", max_digits=5, decimal_places=1, default=0.0)
|
||||
repair_description = models.TextField("维修描述", null=True, blank=True)
|
||||
ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单',
|
||||
on_delete=models.PROTECT, related_name='repair_ticket', null=True, blank=True)
|
||||
|
||||
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
from apps.utils.serializers import CustomModelSerializer
|
||||
from apps.em.models import Equipment, EcheckRecord, EInspect, Ecate
|
||||
from apps.em.models import Equipment, EcheckRecord, EInspect, Ecate, Repair
|
||||
from apps.system.models import Dept
|
||||
from apps.utils.constants import EXCLUDE_FIELDS, EXCLUDE_FIELDS_BASE
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.exceptions import ParseError
|
||||
from apps.wf.serializers import TicketSimpleSerializer
|
||||
|
||||
|
||||
class EcateSerializer(CustomModelSerializer):
|
||||
|
|
@ -75,4 +76,20 @@ class EInspectSerializer(CustomModelSerializer):
|
|||
|
||||
|
||||
class CdSerializer(serializers.Serializer):
|
||||
method = serializers.CharField(label="方法名")
|
||||
method = serializers.CharField(label="方法名")
|
||||
|
||||
|
||||
class RepairCreateSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
model = Repair
|
||||
fields = ["id", "equipment", "fault_description", "fault_cate"]
|
||||
|
||||
class RepairListSerializer(CustomModelSerializer):
|
||||
equipment_fullname = serializers.StringRelatedField(source="equipment", read_only=True)
|
||||
repair_user_name = serializers.CharField(source="repair_user.name", read_only=True)
|
||||
ticket_ = TicketSimpleSerializer(source="ticket", read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Repair
|
||||
fields = "__all__"
|
||||
read_only_fields = EXCLUDE_FIELDS
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from django.urls import path, include
|
||||
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, RepairViewSet
|
||||
|
||||
API_BASE_URL = 'api/em/'
|
||||
HTML_BASE_URL = 'dhtml/em/'
|
||||
|
|
@ -10,6 +10,7 @@ router.register('equipment', EquipmentViewSet, basename='equipment')
|
|||
router.register('echeckrecord', EcheckRecordViewSet, basename='echeckrecord')
|
||||
router.register('einspect', EInspectViewSet, basename='einspect')
|
||||
router.register('ecate', EcateViewSet, basename='ecate')
|
||||
router.register('repair', RepairViewSet, basename='repair')
|
||||
urlpatterns = [
|
||||
path(API_BASE_URL, include(router.urls)),
|
||||
path(API_BASE_URL + 'cd/', CdView.as_view()),
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ from django.shortcuts import render
|
|||
from drf_yasg.utils import swagger_auto_schema
|
||||
from drf_yasg import openapi
|
||||
from django.utils import timezone
|
||||
from apps.em.models import Equipment, EcheckRecord, EInspect, Ecate
|
||||
from apps.em.models import Equipment, EcheckRecord, EInspect, Ecate, Repair
|
||||
from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet
|
||||
from apps.em.serializers import EquipmentSerializer, EcheckRecordSerializer, EInspectSerializer, EcateSerializer, CdSerializer
|
||||
from apps.em.serializers import (EquipmentSerializer, EcheckRecordSerializer,
|
||||
EInspectSerializer, EcateSerializer, CdSerializer, RepairCreateSerializer, RepairListSerializer)
|
||||
from apps.em.filters import EquipFilterSet
|
||||
from rest_framework.exceptions import ParseError
|
||||
from rest_framework.mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin
|
||||
|
|
@ -20,6 +21,9 @@ from apps.enp.services import get_last_envdata
|
|||
from rest_framework.views import APIView
|
||||
from apps.utils.mixins import MyLoggingMixin
|
||||
import importlib
|
||||
from apps.wf.mixins import TicketMixin
|
||||
from apps.wf.models import Ticket
|
||||
from apps.system.models import User
|
||||
|
||||
# Create your views here.
|
||||
|
||||
|
|
@ -188,4 +192,49 @@ class CdView(MyLoggingMixin, APIView):
|
|||
module, func = m.rsplit(".", 1)
|
||||
m = importlib.import_module(module)
|
||||
f = getattr(m, func)
|
||||
return Response(f(*args))
|
||||
return Response(f(*args))
|
||||
|
||||
|
||||
class RepairViewSet(TicketMixin, CustomModelViewSet):
|
||||
"""
|
||||
list:维修申请
|
||||
|
||||
维修申请
|
||||
"""
|
||||
queryset = Repair.objects.all()
|
||||
serializer_class = RepairCreateSerializer
|
||||
list_serializer_class = RepairListSerializer
|
||||
retrieve_serializer_class = RepairListSerializer
|
||||
select_related_fields = ["equipment", "repair_user", "create_by"]
|
||||
filterset_fields = ["equipment", "repair_user", "ticket__state__type"]
|
||||
search_fields = ["equipment__name", "equipment__number", "fault_description", "fault_cate", "repair_description"]
|
||||
workflow_key = "wf_repair"
|
||||
|
||||
def gen_other_ticket_data(self, instance):
|
||||
return {"equipment_fullname": str(instance.equipment)}
|
||||
|
||||
@staticmethod
|
||||
def assgin(ticket: Ticket, transition, new_ticket_data: dict):
|
||||
repair_user = new_ticket_data.get("repair_user", None)
|
||||
fault_cate = new_ticket_data.get("fault_cate", None)
|
||||
if repair_user and fault_cate:
|
||||
repair = Repair.objects.get(ticket=ticket)
|
||||
repair.repair_user = User.objects.get(id=repair_user)
|
||||
repair.fault_cate = fault_cate
|
||||
repair.save()
|
||||
else:
|
||||
raise ParseError("请分派维修人")
|
||||
|
||||
@staticmethod
|
||||
def repair_completed(ticket: Ticket, transition, new_ticket_data: dict):
|
||||
repair_start_time = new_ticket_data.get("repair_start_time", None)
|
||||
repair_duration = new_ticket_data.get("repair_duration", None)
|
||||
repair_description = new_ticket_data.get("repair_description", None)
|
||||
if repair_start_time and repair_duration:
|
||||
repair = Repair.objects.get(ticket=ticket)
|
||||
repair.repair_start_time = repair_start_time
|
||||
repair.repair_duration = repair_duration
|
||||
repair.repair_description = repair_description
|
||||
repair.save()
|
||||
else:
|
||||
raise ParseError("请填写维修开始时间和维修工时")
|
||||
|
|
@ -35,7 +35,11 @@ def translate_eval_formula(exp_str: str, year: int, month: int, day: int, hour:
|
|||
for match in matches:
|
||||
mpst = MpointStat.objects.filter(mpoint__code=match, type="hour", year=year, month=month, day=day, hour=hour).first()
|
||||
if mpst is None:
|
||||
mpoint = Mpoint.objects.get(code=match)
|
||||
try:
|
||||
mpoint = Mpoint.objects.get(code=match)
|
||||
except Exception as e:
|
||||
myLogger.error(f"找不到该测点: exp_str: {exp_str} code: {match} {e}")
|
||||
raise
|
||||
mpst, _ = MpointStat.objects.get_or_create(mpoint=mpoint, type="hour", year=year, month=month, day=day, hour=hour, defaults={"val": 0})
|
||||
myLogger.error(f"找不到该测点的时间线数据: {match}, {year}, {month}, {day}, {hour}, 赋予0值")
|
||||
if mpst:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 3.2.12 on 2025-11-24 08:15
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wf', '0005_workflow_cate'),
|
||||
('hrm', '0020_auto_20251106_0942'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='resignation',
|
||||
name='ticket',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='resignation_ticket', to='wf.ticket', verbose_name='关联工单'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# Generated by Django 3.2.12 on 2025-12-12 08:48
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('system', '0006_auto_20241213_1249'),
|
||||
('wf', '0005_workflow_cate'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('hrm', '0021_alter_resignation_ticket'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EmpNeed',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('post_need', models.TextField(verbose_name='需求岗位')),
|
||||
('count_need', models.PositiveIntegerField(verbose_name='需求人数')),
|
||||
('salary', models.PositiveIntegerField(verbose_name='工资报酬')),
|
||||
('arrival_date', models.DateField(verbose_name='到岗日期')),
|
||||
('reason', models.TextField(help_text='新增人员/该岗原人员离职或辞职或辞退需补充/其他原因', verbose_name='申请理由')),
|
||||
('duty', models.TextField(verbose_name='岗位人员职责描述')),
|
||||
('gender', models.CharField(help_text='男/女/不限', max_length=10, verbose_name='性别要求')),
|
||||
('education', models.CharField(help_text='高中/大专/不限', max_length=50, verbose_name='学历要求')),
|
||||
('professional_requirement', models.TextField(blank=True, null=True, verbose_name='相关专业及技能要求')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='empneed_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('dept_need', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='system.dept', verbose_name='需求部门')),
|
||||
('ticket', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='empneed_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='empneed_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
# Generated by Django 3.2.12 on 2025-12-22 05:40
|
||||
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('system', '0006_auto_20241213_1249'),
|
||||
('wf', '0006_auto_20251215_1645'),
|
||||
('hrm', '0022_empneed'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EmpPersonInfo',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('name', models.CharField(max_length=20, verbose_name='姓名')),
|
||||
('gender', models.CharField(default='男', max_length=10, verbose_name='性别')),
|
||||
('IDcard', models.CharField(max_length=20, verbose_name='身份证号')),
|
||||
('phone', models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.RegexValidator('^1[3456789]\\d{9}$', '手机号码格式不正确')], verbose_name='手机号')),
|
||||
('note', models.TextField(blank=True, null=True, verbose_name='备注')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='emppersoninfo_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='emppersoninfo_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EmpJoin',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('person', models.JSONField(default=list, verbose_name='人员信息')),
|
||||
('join_date', models.DateField(verbose_name='入职日期')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='empjoin_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('dept_need', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='system.dept', verbose_name='入职部门')),
|
||||
('ticket', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='empjoin_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='empjoin_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.12 on 2025-12-23 03:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hrm', '0023_empjoin_emppersoninfo'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='emppersoninfo',
|
||||
name='post',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='岗位'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# Generated by Django 3.2.12 on 2026-01-07 01:18
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wf', '0006_auto_20251215_1645'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('hrm', '0024_emppersoninfo_post'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Leave',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('start_date', models.DateTimeField(verbose_name='开始日期')),
|
||||
('end_date', models.DateTimeField(verbose_name='结束日期')),
|
||||
('leave_type', models.PositiveSmallIntegerField(blank=True, choices=[(10, '事假'), (20, '病假'), (30, '婚假'), (40, '丧假'), (50, '公假'), (60, '工伤'), (70, '产假'), (80, '护理假'), (90, '其他')], null=True, verbose_name='请假类型')),
|
||||
('reason', models.TextField(verbose_name='请假事由')),
|
||||
('hour', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='请假时长')),
|
||||
('file', models.TextField(blank=True, null=True, verbose_name='证明')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='leave_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('employee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hrm.employee', verbose_name='员工')),
|
||||
('ticket', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='leave_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='leave_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -4,8 +4,9 @@ from apps.system.models import Post, User
|
|||
from apps.utils.models import BaseModel, CommonADModel, CommonAModel, CommonBModel
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
from django.core.validators import RegexValidator
|
||||
|
||||
|
||||
PHONE_VALIDATOR = RegexValidator(r'^1[3456789]\d{9}$', '手机号码格式不正确')
|
||||
class Employee(CommonBModel):
|
||||
"""
|
||||
TN:员工信息
|
||||
|
|
@ -202,4 +203,71 @@ class Resignation(CommonADModel):
|
|||
end_date = models.DateField('离职日期')
|
||||
reason = models.TextField('离职原因')
|
||||
handle_date = models.DateField('办理离职交接日期', null=True, blank=True)
|
||||
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.SET_NULL, null=True, blank=True, related_name='resignation_ticket')
|
||||
|
||||
|
||||
class EmpNeed(CommonADModel):
|
||||
"""
|
||||
TN:员工需求
|
||||
"""
|
||||
dept_need = models.ForeignKey('system.Dept', verbose_name='需求部门', on_delete=models.CASCADE)
|
||||
post_need = models.TextField("需求岗位")
|
||||
count_need = models.PositiveIntegerField('需求人数')
|
||||
salary = models.PositiveIntegerField("工资报酬")
|
||||
arrival_date = models.DateField('到岗日期')
|
||||
reason = models.TextField('申请理由', help_text="新增人员/该岗原人员离职或辞职或辞退需补充/其他原因")
|
||||
duty = models.TextField("岗位人员职责描述")
|
||||
gender = models.CharField('性别要求', max_length=10, help_text="男/女/不限")
|
||||
education = models.CharField('学历要求', max_length=50, help_text='高中/大专/不限')
|
||||
professional_requirement = models.TextField(verbose_name="相关专业及技能要求", null=True, blank=True)
|
||||
ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单',
|
||||
on_delete=models.CASCADE, related_name='empneed_ticket', null=True, blank=True)
|
||||
|
||||
|
||||
class EmpJoin(CommonADModel):
|
||||
"""
|
||||
TN:员工入职
|
||||
"""
|
||||
dept_need = models.ForeignKey('system.Dept', verbose_name='入职部门', on_delete=models.CASCADE)
|
||||
ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单',
|
||||
on_delete=models.CASCADE, related_name='empjoin_ticket', null=True, blank=True)
|
||||
person = models.JSONField('人员信息', default=list)
|
||||
join_date = models.DateField('入职日期')
|
||||
|
||||
|
||||
class EmpPersonInfo(CommonADModel):
|
||||
"""
|
||||
TN:入职人员信息
|
||||
"""
|
||||
name = models.CharField('姓名', max_length=20)
|
||||
gender = models.CharField('性别', max_length=10, default='男')
|
||||
IDcard = models.CharField('身份证号', max_length=20)
|
||||
phone = models.CharField('手机号', max_length=20,validators=[PHONE_VALIDATOR], null=True, blank=True)
|
||||
post = models.CharField('岗位', max_length=20, null=True, blank=True)
|
||||
note = models.TextField('备注', null=True, blank=True)
|
||||
|
||||
|
||||
class Leave(CommonADModel):
|
||||
"""
|
||||
TN:员工请假
|
||||
"""
|
||||
E_TYPE_CHOISE = (
|
||||
(10, '事假'),
|
||||
(20, '病假'),
|
||||
(30, '婚假'),
|
||||
(40, '丧假'),
|
||||
(50, '公假'),
|
||||
(60, '工伤'),
|
||||
(70, '产假'),
|
||||
(80, '护理假'),
|
||||
(90, '其他'),
|
||||
)
|
||||
employee = models.ForeignKey(Employee, verbose_name='员工', on_delete=models.CASCADE)
|
||||
start_date = models.DateTimeField('开始日期')
|
||||
end_date = models.DateTimeField('结束日期')
|
||||
leave_type = models.PositiveSmallIntegerField('请假类型', choices=E_TYPE_CHOISE, null=True, blank=True)
|
||||
reason = models.TextField('请假事由')
|
||||
hour = models.PositiveSmallIntegerField('请假时长', null=True, blank=True)
|
||||
file = models.TextField('证明', null=True, blank=True)
|
||||
ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单',
|
||||
on_delete=models.CASCADE, related_name='leave_ticket', null=True, blank=True)
|
||||
|
|
@ -9,7 +9,7 @@ from django.utils import timezone
|
|||
from apps.utils.serializers import CustomModelSerializer
|
||||
from apps.utils.constants import EXCLUDE_FIELDS
|
||||
from apps.hrm.models import (Certificate, ClockRecord, Employee,
|
||||
NotWorkRemark, Attendance, Resignation)
|
||||
NotWorkRemark, Attendance, Resignation, EmpNeed, EmpJoin, EmpPersonInfo, Leave)
|
||||
from apps.system.serializers import DeptSimpleSerializer, UserSimpleSerializer
|
||||
from django.db import transaction
|
||||
from django.core.cache import cache
|
||||
|
|
@ -17,6 +17,7 @@ from apps.utils.tools import check_id_number_strict, get_info_from_id
|
|||
from rest_framework.exceptions import ParseError
|
||||
from django.conf import settings
|
||||
import datetime
|
||||
from apps.wf.serializers import TicketSimpleSerializer
|
||||
|
||||
|
||||
class EmployeeShortSerializer(CustomModelSerializer):
|
||||
|
|
@ -319,6 +320,60 @@ class AttendanceSerializer(CustomModelSerializer):
|
|||
class ResignationSerializer(CustomModelSerializer):
|
||||
belong_dept_name = serializers.CharField(source="employee.belong_dept.name", read_only=True)
|
||||
post_name = serializers.CharField(source="employee.post.name", read_only=True)
|
||||
ticket_ = TicketSimpleSerializer(source="ticket", read_only=True)
|
||||
employee_name = serializers.CharField(source="employee.name", read_only=True)
|
||||
employee_id_number = serializers.CharField(source="employee.id_number", read_only=True)
|
||||
class Meta:
|
||||
model = Resignation
|
||||
fields = '__all__'
|
||||
fields = '__all__'
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
validated_data.pop('employee')
|
||||
return super().update(instance, validated_data)
|
||||
|
||||
def create(self, validated_data):
|
||||
employee:Employee = validated_data['employee']
|
||||
if employee.job_state == Employee.JOB_ON:
|
||||
pass
|
||||
else:
|
||||
raise ParseError('员工不在职,不可创建申请')
|
||||
if Resignation.objects.filter(employee=employee).exists():
|
||||
raise ParseError('该员工已存在离职申请')
|
||||
return super().create(validated_data)
|
||||
|
||||
|
||||
class EmpNeedSerializer(CustomModelSerializer):
|
||||
dept_need_name = serializers.CharField(source='dept_need.name', read_only=True)
|
||||
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
|
||||
class Meta:
|
||||
model = EmpNeed
|
||||
fields = '__all__'
|
||||
|
||||
class EmpJoinSerializer(CustomModelSerializer):
|
||||
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
|
||||
dept_name = serializers.CharField(source='dept_need.name', read_only=True)
|
||||
class Meta:
|
||||
model = EmpJoin
|
||||
fields = '__all__'
|
||||
|
||||
class EmpPersonInfoSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
model = EmpPersonInfo
|
||||
fields = (
|
||||
'name',
|
||||
'gender',
|
||||
'IDcard',
|
||||
'phone',
|
||||
'post',
|
||||
'note',
|
||||
)
|
||||
|
||||
class LeaveSerializer(CustomModelSerializer):
|
||||
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
|
||||
employee_name = serializers.CharField(source='employee.name', read_only=True)
|
||||
post_name = serializers.CharField(source="employee.post.name", read_only=True)
|
||||
belong_dept_name = serializers.CharField(source='employee.belong_dept.name', read_only=True)
|
||||
class Meta:
|
||||
model = Leave
|
||||
fields = '__all__'
|
||||
|
||||
|
|
|
|||
|
|
@ -498,9 +498,3 @@ class HrmService:
|
|||
|
||||
|
||||
|
||||
def bind_resignation(ticket: Ticket, transition: Transition, new_ticket_data: dict):
|
||||
ins = Resignation.objects.get(id=new_ticket_data['rpj'])
|
||||
if ins.ticket and ins.ticket.id != ticket.id:
|
||||
raise ParseError('重复创建工单')
|
||||
ticket.create_by = ins.create_by
|
||||
ticket.save()
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from apps.hrm.views import (CertificateViewSet, ClockRecordViewSet, EmployeeViewSet, NotWorkRemarkViewSet,
|
||||
AttendanceViewSet, ResignationViewSet)
|
||||
from apps.hrm.views import (CertificateViewSet, ClockRecordViewSet, EmployeeViewSet, NotWorkRemarkViewSet, EmpNeedViewSet,
|
||||
AttendanceViewSet, ResignationViewSet, EmpJoinViewSet, LeaveViewSet)
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
|
|
@ -14,6 +14,9 @@ router.register('not_work_remark', NotWorkRemarkViewSet,
|
|||
router.register('certificate', CertificateViewSet, basename='certificate')
|
||||
router.register('attendance', AttendanceViewSet, basename='attendance')
|
||||
router.register('resignation', ResignationViewSet, basename='resignation')
|
||||
router.register('empneed', EmpNeedViewSet, basename='empneed')
|
||||
router.register('empjoin', EmpJoinViewSet, basename='empjoin')
|
||||
router.register('leave', LeaveViewSet, basename='leave')
|
||||
urlpatterns = [
|
||||
path(API_BASE_URL, include(router.urls)),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -12,23 +12,24 @@ from rest_framework.response import Response
|
|||
from apps.hrm.errors import NO_NEED_LEVEL_REMARK
|
||||
from apps.hrm.filters import (CertificateFilterSet, ClockRecordFilterSet, EmployeeFilterSet,
|
||||
NotWorkRemarkFilterSet)
|
||||
from apps.hrm.models import Certificate, ClockRecord, Employee, NotWorkRemark, Attendance, Resignation
|
||||
from apps.hrm.serializers import (CertificateCreateUpdateSerializer, CertificateSerializer, ChannelAuthoritySerializer,
|
||||
from apps.hrm.models import Certificate, ClockRecord, Employee, NotWorkRemark, Attendance, Resignation, EmpNeed, EmpJoin, Leave
|
||||
from apps.hrm.serializers import (CertificateCreateUpdateSerializer, CertificateSerializer, ChannelAuthoritySerializer, EmpJoinSerializer,
|
||||
ClockRecordListSerializer,
|
||||
EmployeeCreateUpdateSerializer, EmployeeDetailSerializer, EmployeeImproveSerializer,
|
||||
EmployeeNotWorkRemarkSerializer,
|
||||
EmployeeNotWorkRemarkSerializer,EmpPersonInfoSerializer,
|
||||
EmployeeSerializer,
|
||||
ClockRecordSimpleSerializer, ClockRecordCreateSerializer,
|
||||
NotWorkRemarkListSerializer, CorrectSerializer, AttendanceSerializer,
|
||||
ResignationSerializer)
|
||||
ResignationSerializer, EmpNeedSerializer, LeaveSerializer)
|
||||
from apps.hrm.services import HrmService
|
||||
|
||||
from apps.third.dahua import dhClient
|
||||
from apps.third.tapis import dhapis
|
||||
from apps.utils.export import export_excel
|
||||
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
||||
from apps.utils.mixins import BulkCreateModelMixin, BulkDestroyModelMixin, CustomListModelMixin
|
||||
|
||||
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet, EuModelViewSet
|
||||
from apps.utils.mixins import BulkCreateModelMixin, BulkDestroyModelMixin, CustomListModelMixin, RetrieveModelMixin
|
||||
from apps.wf.models import Ticket
|
||||
from apps.wf.mixins import TicketMixin
|
||||
epTypeOptions = {'employee': '正式员工', 'remployee': '相关方',
|
||||
'visitor': '访客', 'driver': '货车司机'}
|
||||
epStateOptions = {10: '在职', 20: '离职', 30: '退休'}
|
||||
|
|
@ -393,9 +394,78 @@ class CertificateViewSet(CustomModelViewSet):
|
|||
ins.get_state(need_update=True)
|
||||
|
||||
|
||||
class ResignationViewSet(CustomListModelMixin, BulkCreateModelMixin, CustomGenericViewSet):
|
||||
perms_map = {"get": "*", "post": "resignation.create"}
|
||||
class ResignationViewSet(TicketMixin, EuModelViewSet):
|
||||
select_related_fields = ['employee', 'employee__belong_dept', 'employee__post']
|
||||
queryset = Resignation.objects.all()
|
||||
serializer_class = ResignationSerializer
|
||||
search_fields = ["employee__name"]
|
||||
search_fields = ["employee__name"]
|
||||
workflow_key = "wf_resignation"
|
||||
|
||||
def gen_other_ticket_data(self, instance):
|
||||
return {"employee_name": instance.employee.name}
|
||||
|
||||
@staticmethod
|
||||
def update_handle_date(ticket: Ticket, transition, new_ticket_data: dict):
|
||||
handle_date = new_ticket_data.get("handle_date", None)
|
||||
if handle_date:
|
||||
resignation = Resignation.objects.get(ticket=ticket)
|
||||
resignation.handle_date = handle_date
|
||||
resignation.save()
|
||||
else:
|
||||
raise ParseError("请填写办理离职的交接日期")
|
||||
|
||||
@staticmethod
|
||||
def make_job_off(ticket: Ticket, transition, new_ticket_data: dict):
|
||||
resignation = Resignation.objects.get(ticket=ticket)
|
||||
emp = resignation.employee
|
||||
emp.job_state = Employee.JOB_OFF
|
||||
emp.save(update_fields=['job_state'])
|
||||
user = emp.user
|
||||
user.is_deleted = True
|
||||
user.save(update_fields=['is_deleted'])
|
||||
|
||||
|
||||
class EmpNeedViewSet(TicketMixin, EuModelViewSet):
|
||||
queryset = EmpNeed.objects.all()
|
||||
serializer_class = EmpNeedSerializer
|
||||
filterset_fields = ['dept_need']
|
||||
search_fields = ["dept_need__name", "post_need"]
|
||||
workflow_key = "wf_empneed"
|
||||
|
||||
def gen_other_ticket_data(self, instance):
|
||||
return {"post_need": instance.post_need}
|
||||
|
||||
|
||||
class EmpJoinViewSet(TicketMixin, EuModelViewSet):
|
||||
queryset = EmpJoin.objects.all()
|
||||
serializer_class = EmpJoinSerializer
|
||||
workflow_key = "wf_empjoin"
|
||||
|
||||
def gen_other_ticket_data(self, instance):
|
||||
return {"dept_name": instance.dept_need.name if instance.dept_need else None}
|
||||
|
||||
@staticmethod
|
||||
def approve(ticket: Ticket, transition, new_ticket_data: dict):
|
||||
person = new_ticket_data.get("person", None)
|
||||
EmpJoin.objects.filter(ticket=ticket).update(person=person)
|
||||
if not person:
|
||||
raise ParseError("请选择人员")
|
||||
serializer = EmpPersonInfoSerializer(data=person, many=True)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
|
||||
|
||||
class LeaveViewSet(TicketMixin, EuModelViewSet):
|
||||
select_related_fields = [
|
||||
'employee',
|
||||
'employee__belong_dept',
|
||||
'ticket',
|
||||
]
|
||||
queryset = Leave.objects.all()
|
||||
serializer_class = LeaveSerializer
|
||||
filterset_fields = ['leave_type', 'employee__belong_dept']
|
||||
search_fields = ["employee__name", "leave_type"]
|
||||
workflow_key = "wf_leave"
|
||||
|
||||
def gen_other_ticket_data(self, instance):
|
||||
return {"hour": instance.hour if instance.hour else None}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.12 on 2025-11-17 06:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inm', '0037_alter_materialbatch_material'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='mioitem',
|
||||
name='count_send',
|
||||
field=models.DecimalField(blank=True, decimal_places=3, max_digits=12, null=True, verbose_name='发出数量'),
|
||||
),
|
||||
]
|
||||
|
|
@ -161,6 +161,7 @@ class MIOItem(BaseModel):
|
|||
batch = models.TextField('批次号', db_index=True)
|
||||
unit_price = models.DecimalField('单价', max_digits=14, decimal_places=2, null=True, blank=True)
|
||||
count = models.DecimalField('出入数量', max_digits=12, decimal_places=3)
|
||||
count_send = models.DecimalField('发出数量', max_digits=12, decimal_places=3, null=True, blank=True)
|
||||
count_tested = models.PositiveIntegerField('已检数', null=True, blank=True)
|
||||
test_date = models.DateField('检验日期', null=True, blank=True)
|
||||
test_user = models.ForeignKey(
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ class MIOItemCreateSerializer(CustomModelSerializer):
|
|||
class Meta:
|
||||
model = MIOItem
|
||||
fields = ['mio', 'warehouse', 'material',
|
||||
'batch', 'count', 'assemb', 'is_testok', 'mioitemw', 'mb', 'wm', 'unit_price', 'note', "pack_index"]
|
||||
'batch', 'count', 'assemb', 'is_testok', 'mioitemw', 'mb', 'wm', 'unit_price', 'note', "pack_index", "count_send"]
|
||||
extra_kwargs = {
|
||||
'mio': {'required': True}, 'warehouse': {'required': False},
|
||||
'material': {'required': False}, 'batch': {'required': False, "allow_null": True, "allow_blank": True}}
|
||||
|
|
@ -156,6 +156,8 @@ class MIOItemCreateSerializer(CustomModelSerializer):
|
|||
batch = validated_data.get("batch", None)
|
||||
if not batch:
|
||||
batch = "无"
|
||||
if batch != '无' and len(batch) < 5:
|
||||
raise ParseError('批次号格式错误')
|
||||
if material.is_hidden:
|
||||
raise ParseError('隐式物料不可出入库')
|
||||
if mio.type in [MIO.MIO_TYPE_RETURN_IN, MIO.MIO_TYPE_BORROW_OUT]:
|
||||
|
|
@ -212,11 +214,18 @@ class MIOItemCreateSerializer(CustomModelSerializer):
|
|||
mio_type = mio.type
|
||||
if mio_type != "pur_in" and mio_type != "other_in":
|
||||
wprIds = [i["wpr"].id for i in mioitemw]
|
||||
mb_ids = list(Wpr.objects.filter(id__in=wprIds).values_list("mb__id", flat=True).distinct())
|
||||
if len(mb_ids) == 1 and mb_ids[0] == instance.mb.id:
|
||||
pass
|
||||
else:
|
||||
raise ParseError(f'{batch}物料明细中存在{len(mb_ids)}个不同物料批次')
|
||||
if instance.mb:
|
||||
mb_ids = list(Wpr.objects.filter(id__in=wprIds).values_list("mb__id", flat=True).distinct())
|
||||
if len(mb_ids) == 1 and mb_ids[0] == instance.mb.id:
|
||||
pass
|
||||
else:
|
||||
raise ParseError(f'{batch}物料明细中存在{len(mb_ids)}个不同物料批次')
|
||||
elif instance.wm:
|
||||
wm_ids = list(Wpr.objects.filter(id__in=wprIds).values_list("wm__id", flat=True).distinct())
|
||||
if len(wm_ids) == 1 and wm_ids[0] == instance.wm.id:
|
||||
pass
|
||||
else:
|
||||
raise ParseError(f'{batch}物料明细中存在{len(wm_ids)}个不同物料批次')
|
||||
for item in mioitemw:
|
||||
if item.get("wpr", None) is None and mio_type != "pur_in" and mio_type != "other_in":
|
||||
raise ParseError(f'{item["number"]}_请提供产品明细ID')
|
||||
|
|
@ -238,6 +247,15 @@ class MIOItemAListSerializer(CustomModelSerializer):
|
|||
read_only_fields = EXCLUDE_FIELDS_BASE
|
||||
|
||||
|
||||
class MIOItemListSimpleSerializer(CustomModelSerializer):
|
||||
warehouse_name = serializers.CharField(source='warehouse.name', read_only=True)
|
||||
material_name = serializers.StringRelatedField(
|
||||
source='material', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = MIOItem
|
||||
fields = ["id", "mio", "material", "warehouse", "material_name", "warehouse_name", "batch", "count", "test_date", "count_notok"]
|
||||
|
||||
class MIOItemSerializer(CustomModelSerializer):
|
||||
warehouse_name = serializers.CharField(source='warehouse.name', read_only=True)
|
||||
material_ = MaterialSerializer(source='material', read_only=True)
|
||||
|
|
|
|||
|
|
@ -37,11 +37,12 @@ def do_out(item: MIOItem, is_reverse: bool = False):
|
|||
is_zhj = False # 是否组合件领料
|
||||
if mias.exists():
|
||||
is_zhj = True
|
||||
mias_list = list(mias.values_list('material', 'batch', 'rate'))
|
||||
for i in range(len(mias_list)):
|
||||
material, batch, rate = mias_list[i]
|
||||
new_count = rate * item.count # 假设 item.count 存在
|
||||
action_list.append([material, batch, new_count, None])
|
||||
for itema in mias:
|
||||
material = itema.material
|
||||
batch = itema.batch
|
||||
rate = itema.rate
|
||||
new_count = rate * item.count
|
||||
action_list.append([material, batch, new_count, None, None])
|
||||
else:
|
||||
action_list = [[item.material, item.batch, item.count, defect]]
|
||||
|
||||
|
|
@ -88,14 +89,14 @@ def do_out(item: MIOItem, is_reverse: bool = False):
|
|||
defect=defect
|
||||
)
|
||||
except (MaterialBatch.DoesNotExist, MaterialBatch.MultipleObjectsReturned) as e:
|
||||
raise ParseError(f"批次错误!{e}")
|
||||
raise ParseError(f"{str(xmaterial)}批次{xbatch}错误!{e}")
|
||||
mb.count = mb.count - xcount
|
||||
if mb.count < 0:
|
||||
raise ParseError(f"{mb.batch}-{str(mb.material)}-批次库存不足,操作失败")
|
||||
else:
|
||||
mb.save()
|
||||
|
||||
if material.into_wm:
|
||||
if xmaterial.into_wm:
|
||||
# 领到车间库存(或工段)
|
||||
wm, new_create = WMaterial.objects.get_or_create(
|
||||
batch=xbatch, material=xmaterial,
|
||||
|
|
@ -156,13 +157,14 @@ def do_in(item: MIOItem):
|
|||
|
||||
if mias.exists():
|
||||
is_zhj = True
|
||||
mias_list = mias.values_list('material', 'batch', 'rate')
|
||||
for i in mias_list:
|
||||
material, batch, rate = i
|
||||
new_count = rate * item.count # 假设 item.count 存在
|
||||
action_list.append([material, batch, new_count, None])
|
||||
for itema in mias:
|
||||
material = itema.material
|
||||
batch = itema.batch
|
||||
rate = itema.rate
|
||||
new_count = rate * item.count
|
||||
action_list.append([material, batch, new_count, None, None])
|
||||
else:
|
||||
action_list = [[item.material, item.batch, item.count, defect]]
|
||||
action_list = [[item.material, item.batch, item.count, defect, item.wm]]
|
||||
|
||||
production_dept = None
|
||||
|
||||
|
|
@ -170,28 +172,31 @@ def do_in(item: MIOItem):
|
|||
if is_zhj:
|
||||
xbatchs = [item.batch]
|
||||
for al in action_list:
|
||||
xmaterial, xbatch, xcount, defect = al
|
||||
xmaterial, xbatch, xcount, defect, xwm = al
|
||||
if xcount <= 0:
|
||||
raise ParseError("存在非正数!")
|
||||
|
||||
xbatchs.append(xbatch)
|
||||
if material.into_wm:
|
||||
wm_qs = WMaterial.objects.filter(
|
||||
batch=xbatch,
|
||||
material=xmaterial,
|
||||
belong_dept=belong_dept,
|
||||
mgroup=mgroup,
|
||||
defect=defect,
|
||||
state=WMaterial.WM_OK)
|
||||
count_x = wm_qs.count()
|
||||
if count_x == 1:
|
||||
wm = wm_qs.first()
|
||||
elif count_x == 0:
|
||||
raise ParseError(
|
||||
f'{str(xmaterial)}-{xbatch}-批次库存不存在!')
|
||||
if xmaterial.into_wm:
|
||||
if xwm:
|
||||
wm = xwm
|
||||
else:
|
||||
raise ParseError(
|
||||
f'{str(xmaterial)}-{xbatch}-存在多个相同批次!')
|
||||
wm_qs = WMaterial.objects.filter(
|
||||
batch=xbatch,
|
||||
material=xmaterial,
|
||||
belong_dept=belong_dept,
|
||||
mgroup=mgroup,
|
||||
defect=defect,
|
||||
state=WMaterial.WM_OK)
|
||||
count_x = wm_qs.count()
|
||||
if count_x == 1:
|
||||
wm = wm_qs.first()
|
||||
elif count_x == 0:
|
||||
raise ParseError(
|
||||
f'{str(xmaterial)}-{xbatch}-批次库存不存在!')
|
||||
else:
|
||||
raise ParseError(
|
||||
f'{str(xmaterial)}-{xbatch}-存在多个相同批次!')
|
||||
|
||||
# 扣减车间库存
|
||||
new_count = wm.count - xcount
|
||||
|
|
@ -272,7 +277,7 @@ class InmService:
|
|||
"""
|
||||
更新库存, 支持反向操作
|
||||
"""
|
||||
if not MIOItem.objects.filter(mio=instance).exists():
|
||||
if is_reverse is False and not MIOItem.objects.filter(mio=instance).exists():
|
||||
raise ParseError("出入库记录缺失明细,无法操作")
|
||||
|
||||
if instance.type == MIO.MIO_TYPE_PUR_IN: # 需要更新订单
|
||||
|
|
@ -347,8 +352,6 @@ class InmService:
|
|||
out = -1
|
||||
"""
|
||||
mioitems = MIOItem.objects.filter(mio=instance)
|
||||
if not mioitems.exists():
|
||||
raise ParseError("未填写物料明细")
|
||||
for i in mioitems:
|
||||
cls.update_mb_item(i, in_or_out, 'count')
|
||||
|
||||
|
|
@ -370,7 +373,10 @@ class InmService:
|
|||
ddict["material_ofrom"] = i.material
|
||||
|
||||
if field == "count":
|
||||
count_change = i.count - i.count_notok
|
||||
if i.test_date is not None:
|
||||
count_change = i.count - i.count_notok
|
||||
else:
|
||||
count_change = i.count
|
||||
elif field == "count_notok":
|
||||
count_change = getattr(i, field)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -122,7 +122,13 @@ def daoru_mioitem_test(path:str, mioitem:MIOItem):
|
|||
FtestItem.objects.bulk_create(ftestitems)
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
mioitem.test_date = test_date
|
||||
mioitem.test_user = test_user
|
||||
mioitem.count = MIOItemw.objects.filter(mioitem=mioitem).count()
|
||||
mioitem.count_tested = MIOItemw.objects.filter(mioitem=mioitem, ftest__isnull=False).count()
|
||||
mioitem.count_notok = MIOItemw.objects.filter(mioitem=mioitem, ftest__is_ok=False).count()
|
||||
mioitem.save()
|
||||
|
||||
def daoru_mioitems(path:str, mio:MIO):
|
||||
from apps.utils.snowflake import idWorker
|
||||
|
|
@ -136,19 +142,30 @@ def daoru_mioitems(path:str, mio:MIO):
|
|||
|
||||
mioitems = []
|
||||
ind = 2
|
||||
while sheet[f"a{ind}"].value:
|
||||
batch = sheet[f"b{ind}"].value
|
||||
while sheet[f"a{ind}"].value or sheet[f"b{ind}"].value:
|
||||
batch = sheet[f"e{ind}"].value
|
||||
material_number = sheet[f"a{ind}"].value
|
||||
try:
|
||||
material = Material.objects.get(number=material_number)
|
||||
except Exception as e:
|
||||
raise ParseError(f"未找到物料:{material_number} {e}")
|
||||
if material_number:
|
||||
try:
|
||||
material = Material.objects.get(number=material_number)
|
||||
except Exception as e:
|
||||
raise ParseError(f"未找到物料:{material_number} {e}")
|
||||
else:
|
||||
material_name = sheet[f"b{ind}"].value
|
||||
material_model = sheet[f"d{ind}"].value
|
||||
material_specification = sheet[f"c{ind}"].value
|
||||
try:
|
||||
material = Material.objects.get(name=material_name, model=material_model, specification=material_specification)
|
||||
except Exception as e:
|
||||
raise ParseError(f"未找到物料:{material_name} {material_model} {material_specification} {e}")
|
||||
if batch:
|
||||
pass
|
||||
else:
|
||||
batch = "无"
|
||||
count = sheet[f"c{ind}"].value
|
||||
warehouse_name = sheet[f"d{ind}"].value
|
||||
if batch != '无' and len(batch) < 5:
|
||||
raise ParseError(f'第{ind}行批次号{batch}:格式错误')
|
||||
count = sheet[f"f{ind}"].value
|
||||
warehouse_name = sheet[f"g{ind}"].value
|
||||
try:
|
||||
warehouse = WareHouse.objects.get(name=warehouse_name)
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ router.register('warehouse', WarehouseVIewSet, basename='warehouse')
|
|||
router.register('materialbatch', MaterialBatchViewSet,
|
||||
basename='materialbatch')
|
||||
router.register('mio', MIOViewSet, basename='mio')
|
||||
router.register('mio/do', MioDoViewSet)
|
||||
router.register('mio/sale', MioSaleViewSet)
|
||||
router.register('mio/pur', MioPurViewSet)
|
||||
router.register('mio/other', MioOtherViewSet)
|
||||
router.register('mio/do', MioDoViewSet, basename='mio_do')
|
||||
router.register('mio/sale', MioSaleViewSet, basename='mio_sale')
|
||||
router.register('mio/pur', MioPurViewSet, basename='mio_pur')
|
||||
router.register('mio/other', MioOtherViewSet, basename='mio_other')
|
||||
router.register('mioitem', MIOItemViewSet, basename='mioitem')
|
||||
router.register('mioitemw', MIOItemwViewSet, basename='mioitemw')
|
||||
# router.register('pack', PackViewSet, basename='pack')
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from apps.inm.serializers import (
|
|||
MaterialBatchSerializer, WareHourseSerializer, MIOListSerializer, MIOItemSerializer, MioItemAnaSerializer,
|
||||
MIODoSerializer, MIOSaleSerializer, MIOPurSerializer, MIOOtherSerializer, MIOItemCreateSerializer,
|
||||
MaterialBatchDetailSerializer, MIODetailSerializer, MIOItemTestSerializer, MIOItemPurInTestSerializer,
|
||||
MIOItemwSerializer, MioItemDetailSerializer, PackSerializer, PackMioSerializer)
|
||||
MIOItemwSerializer, MioItemDetailSerializer, PackSerializer, PackMioSerializer, MIOItemListSimpleSerializer)
|
||||
from apps.inm.serializers2 import MIOItemwCreateUpdateSerializer
|
||||
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
||||
from apps.inm.services import InmService
|
||||
|
|
@ -28,6 +28,7 @@ from apps.mtm.models import Material
|
|||
from drf_yasg.utils import swagger_auto_schema
|
||||
from drf_yasg import openapi
|
||||
from django.db import connection
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# Create your views here.
|
||||
|
|
@ -162,20 +163,39 @@ class MIOViewSet(CustomModelViewSet):
|
|||
return mio
|
||||
|
||||
def add_info_for_list(self, data):
|
||||
# 获取检验状态
|
||||
mio_dict = {}
|
||||
# 1. 收集所有mio的ID
|
||||
mio_ids = [item['id'] for item in data]
|
||||
|
||||
# 2. 预初始化mio字典和items列表
|
||||
mio_dict = {item['id']: {
|
||||
**item,
|
||||
'is_tested': False, # 默认值设为False
|
||||
'mioitems': []
|
||||
} for item in data}
|
||||
|
||||
# 3. 批量查询MIOItem数据
|
||||
if mio_ids: # 避免空查询
|
||||
mioitems = MIOItemListSimpleSerializer(
|
||||
instance=MIOItem.objects.filter(
|
||||
mio__id__in=mio_ids
|
||||
).select_related("warehouse", "material"),
|
||||
many=True
|
||||
).data
|
||||
|
||||
# 4. 单次循环处理所有item
|
||||
for item in mioitems:
|
||||
mio_id = item['mio']
|
||||
if mio_id in mio_dict:
|
||||
mio_dict[mio_id]['mioitems'].append(item)
|
||||
# 更新is_tested状态(只要有一个item有test_date就为True)
|
||||
if item.get('test_date'):
|
||||
mio_dict[mio_id]['is_tested'] = True
|
||||
|
||||
# 5. 直接返回原始data列表,避免额外转换
|
||||
for item in data:
|
||||
item['is_tested'] = None
|
||||
mio_dict[item['id']] = item
|
||||
mioitems = list(MIOItem.objects.filter(mio__id__in=mio_dict.keys()).values_list("mio__id", "test_date"))
|
||||
for item in mioitems:
|
||||
mioId, test_date = item
|
||||
is_tested = False
|
||||
if test_date:
|
||||
is_tested = True
|
||||
mio_dict[mioId]['is_tested'] = is_tested
|
||||
datax = [mio_dict[key] for key in mio_dict.keys()]
|
||||
return datax
|
||||
item.update(mio_dict[item['id']])
|
||||
|
||||
return data
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ['create', 'update', 'partial_update']:
|
||||
|
|
@ -482,13 +502,25 @@ class MIOItemwViewSet(CustomModelViewSet):
|
|||
perms_map = {'get': '*', 'post': 'mio.update', 'put': 'mio.update', 'delete': 'mio.update'}
|
||||
queryset = MIOItemw.objects.all()
|
||||
serializer_class = MIOItemwCreateUpdateSerializer
|
||||
filterset_fields = ['mioitem', 'wpr']
|
||||
filterset_fields = {
|
||||
'mioitem': ['exact'],
|
||||
'mioitem__material__type': ['exact'],
|
||||
"wpr": ['exact'],
|
||||
"number": ["exact"],
|
||||
"ftest": ["isnull"],
|
||||
"mioitem__mio__state": ["exact"]
|
||||
}
|
||||
select_related_fields = ["ftest"]
|
||||
ordering = ["number", "create_time"]
|
||||
ordering_fields = ["number", "create_time"]
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
if not self.detail and not self.request.query_params.get('mioitem', None):
|
||||
raise ParseError('请指定所属出入库记录明细')
|
||||
if not self.detail:
|
||||
if not self.request.query_params.get('mioitem', None):
|
||||
if "ftest__isnull" in self.request.query_params:
|
||||
pass
|
||||
else:
|
||||
raise ParseError('请指定所属出入库记录明细')
|
||||
return super().filter_queryset(queryset)
|
||||
|
||||
def cal_mioitem_count(self, mioitem:MIOItem):
|
||||
|
|
@ -496,6 +528,10 @@ class MIOItemwViewSet(CustomModelViewSet):
|
|||
mioitem.count = count
|
||||
mioitem.count_tested = MIOItemw.objects.filter(mioitem=mioitem, ftest__isnull=False).count()
|
||||
mioitem.count_notok = MIOItemw.objects.filter(mioitem=mioitem, ftest__is_ok=False).count()
|
||||
if mioitem.test_date is None:
|
||||
mioitem.test_date = datetime.now()
|
||||
if mioitem.test_user is None:
|
||||
mioitem.test_user = self.request.user
|
||||
mioitem.save()
|
||||
|
||||
def perform_create(self, serializer):
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ from django_filters import rest_framework as filters
|
|||
from apps.mtm.models import Goal, Material, Route, RoutePack
|
||||
from django.db.models.expressions import F
|
||||
from rest_framework.exceptions import ParseError
|
||||
|
||||
from django.db.models import Sum, Q, Value, F, ExpressionWrapper, DecimalField
|
||||
from django.db.models.functions import Coalesce
|
||||
|
||||
class MaterialFilter(filters.FilterSet):
|
||||
tag = filters.CharFilter(method='filter_tag', label="low_inm:库存不足")
|
||||
|
|
@ -27,7 +28,7 @@ class MaterialFilter(filters.FilterSet):
|
|||
|
||||
def filter_tag(self, queryset, name, value):
|
||||
if value == 'low_inm':
|
||||
queryset = queryset.exclude(count_safe=None).exclude(count_safe__lte=0).filter(
|
||||
queryset = Material.annotate_count(queryset.exclude(count_safe=None).exclude(count_safe__lte=0)).filter(
|
||||
count__lte=F('count_safe'))
|
||||
return queryset
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ from collections import defaultdict, deque
|
|||
from django.db.models import Q
|
||||
from datetime import datetime, timedelta
|
||||
from django.utils import timezone
|
||||
from django.db.models import Sum, Q, Value, F, ExpressionWrapper, DecimalField
|
||||
from django.db.models.functions import Coalesce
|
||||
|
||||
class Process(CommonBModel):
|
||||
"""
|
||||
|
|
@ -119,7 +121,30 @@ class Material(CommonAModel):
|
|||
def __str__(self):
|
||||
return f'{self.name}|{self.specification if self.specification else ""}|{self.model if self.model else ""}|{self.process.name if self.process else ""}'
|
||||
|
||||
@property
|
||||
def fname(self):
|
||||
return f'{self.name}|{self.specification if self.specification else ""}|{self.model if self.model else ""}|{self.process.name if self.process else ""}'
|
||||
|
||||
@staticmethod
|
||||
def annotate_count(qs):
|
||||
return qs.annotate(
|
||||
count_mb=Coalesce(
|
||||
Sum('mb_m__count', filter=Q(mb_m__state=10, mb_m__count__gt=0)),
|
||||
Value(0),
|
||||
output_field=DecimalField()
|
||||
),
|
||||
count_wm=Coalesce(
|
||||
Sum('wm_m__count', filter=Q(wm_m__state=10, wm_m__count__gt=0)),
|
||||
Value(0),
|
||||
output_field=DecimalField()
|
||||
)
|
||||
).annotate(
|
||||
count=ExpressionWrapper(
|
||||
F('count_wm') + F('count_mb'),
|
||||
output_field=DecimalField()
|
||||
)
|
||||
)
|
||||
|
||||
class Shift(CommonBModel):
|
||||
"""TN:班次
|
||||
"""
|
||||
|
|
@ -204,7 +229,7 @@ class Mgroup(CommonBModel):
|
|||
# 如果当前时间在结束时间之前,属于前一天
|
||||
else:
|
||||
return (w_s_time - timedelta(days=1)).date(), shift
|
||||
# return w_s_time.date(), None
|
||||
raise ParseError(f"工段{self.name}的班次规则未覆盖时间点{w_s_time.strftime('%H:%M:%S')}")
|
||||
|
||||
|
||||
class TeamMember(BaseModel):
|
||||
|
|
@ -451,7 +476,7 @@ class Route(CommonADModel):
|
|||
return rqs
|
||||
|
||||
@classmethod
|
||||
def validate_dag(cls, final_material_out:Material, rqs):
|
||||
def validate_dag(cls, final_material_out:Material, rqs, check_final=True):
|
||||
"""
|
||||
TN:校验工艺路线是否正确:
|
||||
- 所有 Route 必须有 material_in 和 material_out
|
||||
|
|
@ -482,7 +507,7 @@ class Route(CommonADModel):
|
|||
|
||||
# 3. 检查final_material_out是否是终点
|
||||
final_id = final_material_out.id
|
||||
if final_id in reverse_graph:
|
||||
if check_final and final_id in reverse_graph:
|
||||
# raise ParseError(
|
||||
# f"最终物料 {final_material_out.name}(ID:{final_id}) 不能作为任何Route的输入"
|
||||
# )
|
||||
|
|
@ -502,6 +527,8 @@ class Route(CommonADModel):
|
|||
|
||||
# 5. 检查未到达的物料
|
||||
unreachable_ids = all_material_ids - visited
|
||||
if check_final is False:
|
||||
unreachable_ids.discard(final_id)
|
||||
if unreachable_ids:
|
||||
# unreachable_materials = Material.objects.filter(id__in=unreachable_ids).values_list('name', flat=True)
|
||||
# raise ParseError(
|
||||
|
|
|
|||
|
|
@ -38,13 +38,15 @@ class MaterialSerializer(CustomModelSerializer):
|
|||
count = serializers.SerializerMethodField()
|
||||
|
||||
def get_count_mb(self, obj):
|
||||
return getattr(obj, 'count_mb', 0)
|
||||
return getattr(obj, 'count_mb', None)
|
||||
|
||||
def get_count_wm(self, obj):
|
||||
return getattr(obj, 'count_wm', 0)
|
||||
return getattr(obj, 'count_wm', None)
|
||||
|
||||
def get_count(self, obj):
|
||||
return getattr(obj, 'count_mb', 0) + getattr(obj, 'count_wm', 0)
|
||||
if hasattr(obj, 'count_mb') and hasattr(obj, 'count_wm'):
|
||||
return getattr(obj, 'count_mb', 0) + getattr(obj, 'count_wm', 0)
|
||||
return None
|
||||
|
||||
class Meta:
|
||||
model = Material
|
||||
|
|
@ -209,7 +211,10 @@ class RouteSerializer(CustomModelSerializer):
|
|||
"""
|
||||
material = instance.material
|
||||
process = instance.process
|
||||
material_out: Material = Material.objects.get_queryset(all=True).filter(type=Material.MA_TYPE_HALFGOOD, parent=material, process=process).first()
|
||||
|
||||
material_out: Material = Material.objects.get_queryset(all=True).filter(
|
||||
type__in=[Material.MA_TYPE_HALFGOOD, Material.MA_TYPE_GOOD],
|
||||
parent=material, process=process).order_by("create_time").first()
|
||||
if material_out:
|
||||
material_out.is_deleted = False
|
||||
if material_out.parent == material:
|
||||
|
|
@ -220,7 +225,10 @@ class RouteSerializer(CustomModelSerializer):
|
|||
material_out.tracking = material_out_tracking
|
||||
material_out.save()
|
||||
return material_out
|
||||
material_out = Material.objects.get_queryset(all=True).filter(name=material.name, model=material.model, process=process, specification=material.specification).first()
|
||||
|
||||
material_out = Material.objects.get_queryset(all=True).filter(name=material.name,
|
||||
model=material.model, process=process,
|
||||
specification=material.specification).order_by("create_time", "-is_hidden").first()
|
||||
if material_out:
|
||||
material_out.is_deleted = False
|
||||
if material_out.parent is None:
|
||||
|
|
@ -230,6 +238,7 @@ class RouteSerializer(CustomModelSerializer):
|
|||
material_out.tracking = material_out_tracking
|
||||
material_out.save()
|
||||
return material_out
|
||||
|
||||
material_out = Material.objects.create(**{'parent': instance.material, 'process': instance.process,
|
||||
'is_hidden': True, 'name': material.name,
|
||||
'number': material.number,
|
||||
|
|
|
|||
|
|
@ -44,26 +44,10 @@ class MaterialViewSet(CustomModelViewSet):
|
|||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
if self.action in ["list", "retrieve"]:
|
||||
return qs.annotate(
|
||||
count_wm=Coalesce(
|
||||
Sum('mb_m__count', filter=Q(mb_m__state=10)),
|
||||
Value(0),
|
||||
output_field=DecimalField()
|
||||
),
|
||||
count_mb=Coalesce(
|
||||
Sum('wm_m__count', filter=Q(wm_m__state=10)),
|
||||
Value(0),
|
||||
output_field=DecimalField()
|
||||
)
|
||||
).annotate(
|
||||
count=ExpressionWrapper(
|
||||
F('count_wm') + F('count_mb'),
|
||||
output_field=DecimalField()
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if self.action in ["list", "retrieve"] and self.request.query_params.get('wiith_count', None) == 'yes':
|
||||
return Material.annotate_count(qs)
|
||||
return qs
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
from apps.inm.models import MaterialBatch
|
||||
if MaterialBatch.objects.filter(material=instance).exists():
|
||||
|
|
@ -265,9 +249,12 @@ class RoutePackViewSet(CustomModelViewSet):
|
|||
|
||||
@transaction.atomic
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
from apps.wpm.models import Mlog
|
||||
obj: RoutePack = self.get_object()
|
||||
if obj.state != RoutePack.RP_S_CREATE:
|
||||
raise ParseError('该状态下不可删除')
|
||||
if Mlog.objects.filter(route__routepack=obj).exists():
|
||||
raise ParseError('该工艺路线包有生产记录,不可删除')
|
||||
obj.delete()
|
||||
Ticket.objects.filter(ticket_data__t_id=obj.id, ticket_data__t_model='routepack').delete()
|
||||
return Response(status=204)
|
||||
|
|
@ -395,6 +382,16 @@ class RouteViewSet(CustomModelViewSet):
|
|||
raise ParseError('该工艺步骤被其他步骤引用,无法删除')
|
||||
return super().perform_destroy(instance)
|
||||
|
||||
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=Serializer)
|
||||
def dag(self, request, *args, **kwargs):
|
||||
"""获取总图
|
||||
|
||||
获取总图
|
||||
"""
|
||||
materialId = self.request.data.get('product', None)
|
||||
if materialId is None:
|
||||
raise ParseError('缺少参数product')
|
||||
return Response(Route.get_dag(rqs=Route.objects.filter(material__id=materialId)))
|
||||
|
||||
class SruleViewSet(CustomModelViewSet):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from django_filters import rest_framework as filters
|
||||
from apps.ofm.models import MroomBooking, BorrowRecord
|
||||
from apps.ofm.models import MroomBooking, BorrowRecord, VehicleUse
|
||||
|
||||
from .models import LendingSeal
|
||||
from apps.utils.filters import MyJsonListFilter
|
||||
|
|
@ -15,6 +15,16 @@ class MroomBookingFilterset(filters.FilterSet):
|
|||
"id": ["exact"]
|
||||
}
|
||||
|
||||
class VehicleFilterset(filters.FilterSet):
|
||||
class Meta:
|
||||
model = VehicleUse
|
||||
fields = {
|
||||
'slot_vehicle__vehreg': ['exact', 'in'],
|
||||
'slot_vehicle__vdate': ['exact', 'gte', 'lte'],
|
||||
'create_by': ['exact'],
|
||||
"id": ["exact"]
|
||||
}
|
||||
|
||||
|
||||
class SealFilter(filters.FilterSet):
|
||||
seal = MyJsonListFilter(label='按印章名称查询', field_name="seal")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# Generated by Django 3.2.12 on 2025-06-25 09:29
|
||||
# Generated by Django 3.2.12 on 2025-11-18 03:08
|
||||
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
|
@ -11,7 +12,9 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('system', '0006_auto_20241213_1249'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('wf', '0005_workflow_cate'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
@ -40,13 +43,114 @@ class Migration(migrations.Migration):
|
|||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('title', models.CharField(max_length=100, verbose_name='会议主题')),
|
||||
('note', models.TextField(blank=True, null=True, verbose_name='备注')),
|
||||
('participant_count', models.PositiveIntegerField(default=0, verbose_name='参会人数')),
|
||||
('key_participants', models.TextField(blank=True, null=True, verbose_name='主要参会领导')),
|
||||
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mroombooking_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mroombooking_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('ticket', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mrooms_ticket', to='wf.ticket', verbose_name='关联会议室')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mroombooking_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='VehicleReg',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('name', models.CharField(max_length=50, verbose_name='车辆名称')),
|
||||
('brand', models.CharField(blank=True, max_length=50, null=True, verbose_name='品牌')),
|
||||
('plate', models.CharField(max_length=50, verbose_name='车牌号')),
|
||||
('km', models.PositiveIntegerField(blank=True, null=True, verbose_name='行驶里程')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vehiclereg_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vehiclereg_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='VehicleUse',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('location', models.CharField(blank=True, max_length=100, null=True, verbose_name='出发地点')),
|
||||
('via', models.CharField(blank=True, max_length=100, null=True, verbose_name='途经地点')),
|
||||
('destination', models.CharField(blank=True, max_length=100, null=True, verbose_name='到达地点')),
|
||||
('start_km', models.PositiveIntegerField(verbose_name='出发公里数')),
|
||||
('end_km', models.PositiveIntegerField(blank=True, null=True, verbose_name='归还公里数')),
|
||||
('actual_km', models.PositiveIntegerField(editable=False, verbose_name='实际行驶公里数')),
|
||||
('is_city', models.BooleanField(default=True, verbose_name='是否市内用车')),
|
||||
('reason', models.CharField(max_length=100, verbose_name='用车事由')),
|
||||
('reception', models.CharField(blank=True, max_length=50, null=True, verbose_name='接待人')),
|
||||
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vehicleuse_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vehicleuse_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('ticket', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vehicle_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vehicleuse_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
('vehiclereg', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='vehicle_record', to='ofm.vehiclereg')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='VehicleSlot',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('vdate', models.DateField(db_index=True, verbose_name='使用日期')),
|
||||
('slot', models.PositiveIntegerField(help_text='0-47', verbose_name='时段')),
|
||||
('is_inuse', models.BooleanField(default=True, verbose_name='是否占用')),
|
||||
('vehicle_use', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='slot_vehicle', to='ofm.vehicleuse')),
|
||||
('vehreg', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='slot_record', to='ofm.vehiclereg')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Publicity',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('number', models.CharField(blank=True, max_length=50, null=True, verbose_name='记录编号')),
|
||||
('title', models.CharField(max_length=100, verbose_name='送审稿件标题')),
|
||||
('participants', models.CharField(max_length=50, verbose_name='所有撰稿人')),
|
||||
('pub_dept', models.CharField(blank=True, max_length=50, null=True, verbose_name='部室/研究院')),
|
||||
('pfile', models.CharField(blank=True, max_length=100, null=True, verbose_name='稿件路径')),
|
||||
('level', models.JSONField(default=list, help_text="['重要', '一般', '非涉密']", verbose_name='涉密等级')),
|
||||
('content', models.JSONField(default=list, help_text="['武器装备科研生产综合事项', '其它']", verbose_name='稿件内容涉及')),
|
||||
('other_content', models.CharField(blank=True, max_length=100, null=True, verbose_name='其它内容')),
|
||||
('report_purpose', models.CharField(blank=True, max_length=100, null=True, verbose_name='宣传报道目的')),
|
||||
('channel', models.JSONField(default=list, help_text="['互联网', '信息平台', '官微', '公开发行物', '其它']", verbose_name='发布渠道')),
|
||||
('other_channel', models.CharField(blank=True, max_length=50, null=True, verbose_name='其它渠道')),
|
||||
('report_name', models.CharField(blank=True, max_length=50, null=True, verbose_name='报道名称')),
|
||||
('review', models.JSONField(blank=True, default=list, help_text="['内容不涉及国家秘密和商业秘密,申请公开', '内容涉及国家秘密,申请按涉密渠道发布']", null=True, verbose_name='第一撰稿人自审')),
|
||||
('dept_opinion', models.CharField(blank=True, max_length=100, null=True, verbose_name='部门负责人意见')),
|
||||
('disposal_method', models.CharField(blank=True, max_length=50, null=True, verbose_name='处理方式')),
|
||||
('secret_level', models.CharField(blank=True, max_length=50, null=True, verbose_name='秘密等级')),
|
||||
('secret_period', models.CharField(blank=True, max_length=50, null=True, verbose_name='秘密期限')),
|
||||
('dept_opinion_review', models.CharField(blank=True, max_length=100, null=True, verbose_name='部门审查意见')),
|
||||
('publicity_opinion', models.CharField(blank=True, max_length=100, null=True, verbose_name='宣传报道意见')),
|
||||
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='publicity_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='publicity_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('ticket', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='publicity_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='publicity_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MroomSlot',
|
||||
fields=[
|
||||
|
|
@ -56,11 +160,83 @@ class Migration(migrations.Migration):
|
|||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('mdate', models.DateField(db_index=True, verbose_name='会议日期')),
|
||||
('slot', models.PositiveIntegerField(help_text='0-47', verbose_name='时段')),
|
||||
('is_inuse', models.BooleanField(default=True, verbose_name='是否占用')),
|
||||
('booking', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='slot_b', to='ofm.mroombooking')),
|
||||
('mroom', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='slot_m', to='ofm.mroom')),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('mroom', 'mdate', 'slot')},
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='LendingSeal',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('seal', models.JSONField(default=list, help_text='[公章,法人章,财务章,合同章,业务章,其他章]', verbose_name='印章信息')),
|
||||
('seal_other', models.CharField(blank=True, max_length=50, null=True, verbose_name='其他印章')),
|
||||
('filename', models.TextField(verbose_name='文件名称')),
|
||||
('file', models.TextField(verbose_name='文件内容')),
|
||||
('file_count', models.PositiveIntegerField(verbose_name='用印份数')),
|
||||
('is_lending', models.BooleanField(default=False, verbose_name='是否借出')),
|
||||
('contacts', models.CharField(blank=True, max_length=50, null=True, validators=[django.core.validators.RegexValidator('^1[3456789]\\d{9}$', '手机号码格式不正确')], verbose_name='联系方式')),
|
||||
('lending_date', models.DateField(blank=True, null=True, verbose_name='借出日期')),
|
||||
('return_date', models.DateField(blank=True, null=True, verbose_name='拟归还日期')),
|
||||
('actual_return_date', models.DateField(blank=True, null=True, verbose_name='实际归还日期')),
|
||||
('reason', models.CharField(blank=True, max_length=100, null=True, verbose_name='借用理由')),
|
||||
('note', models.TextField(blank=True, null=True, verbose_name='备注')),
|
||||
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='lendingseal_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='lendingseal_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('ticket', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='seal_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='lendingseal_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='FileRecord',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('name', models.CharField(max_length=100, verbose_name='资料名称')),
|
||||
('number', models.CharField(blank=True, max_length=50, null=True, verbose_name='档案编号')),
|
||||
('counts', models.CharField(blank=True, max_length=10, null=True, verbose_name='文件份数')),
|
||||
('location', models.CharField(blank=True, max_length=100, null=True, verbose_name='存放位置')),
|
||||
('contacts', models.CharField(blank=True, max_length=50, null=True, validators=[django.core.validators.RegexValidator('^1[3456789]\\d{9}$', '手机号码格式不正确')], verbose_name='存档人电话')),
|
||||
('reciver', models.CharField(blank=True, max_length=50, null=True, verbose_name='接收人(综合办)')),
|
||||
('remark', models.TextField(blank=True, max_length=200, null=True, verbose_name='备注')),
|
||||
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='filerecord_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='filerecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='filerecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='BorrowRecord',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('borrow_date', models.DateField(blank=True, null=True, verbose_name='借阅日期')),
|
||||
('return_date', models.DateField(blank=True, null=True, verbose_name='归还日期')),
|
||||
('contacts', models.CharField(blank=True, max_length=50, null=True, validators=[django.core.validators.RegexValidator('^1[3456789]\\d{9}$', '手机号码格式不正确')], verbose_name='借阅人电话')),
|
||||
('remark', models.JSONField(default=list, help_text="['借阅', '复印', '查阅']", verbose_name='用途')),
|
||||
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='borrowrecord_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||
('borrow_file', models.ManyToManyField(related_name='borrow_records', to='ofm.FileRecord')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='borrowrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('ticket', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='borrow_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='borrowrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-05 03:07
|
||||
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wf', '0002_alter_state_filter_dept'),
|
||||
('system', '0006_auto_20241213_1249'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('ofm', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='LendingSeal',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('seal', models.JSONField(default=list, help_text='{"seal_name": "印章名称"}', verbose_name='印章信息')),
|
||||
('filename', models.TextField(verbose_name='文件名称')),
|
||||
('file', models.TextField(verbose_name='文件内容')),
|
||||
('file_count', models.PositiveIntegerField(verbose_name='用印份数')),
|
||||
('is_lending', models.BooleanField(default=False, verbose_name='是否借出')),
|
||||
('contacts', models.CharField(blank=True, max_length=50, null=True, validators=[django.core.validators.RegexValidator('^1[3456789]\\d{9}$', '手机号码格式不正确')], verbose_name='联系方式')),
|
||||
('lending_date', models.DateField(blank=True, null=True, verbose_name='借出日期')),
|
||||
('return_date', models.DateField(blank=True, null=True, verbose_name='拟归还日期')),
|
||||
('actual_return_date', models.DateField(blank=True, null=True, verbose_name='实际归还日期')),
|
||||
('reason', models.CharField(blank=True, max_length=100, null=True, verbose_name='借用理由')),
|
||||
('note', models.TextField(blank=True, null=True, verbose_name='备注')),
|
||||
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='lendingseal_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='lendingseal_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('submit_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='seal_submit_user', to=settings.AUTH_USER_MODEL, verbose_name='提交人')),
|
||||
('ticket', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='seal_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='lendingseal_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-08 03:11
|
||||
# Generated by Django 3.2.12 on 2025-11-18 07:39
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
|
@ -6,12 +6,12 @@ from django.db import migrations
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0002_lendingseal'),
|
||||
('ofm', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='lendingseal',
|
||||
name='submit_user',
|
||||
model_name='vehicleuse',
|
||||
name='vehiclereg',
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.12 on 2025-11-19 02:22
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0002_remove_vehicleuse_vehiclereg'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='borrowrecord',
|
||||
name='count',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='借阅份数'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 3.2.12 on 2025-12-18 08:36
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0003_borrowrecord_count'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='vehiclereg',
|
||||
name='km',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='行驶里程'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vehicleuse',
|
||||
name='start_km',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='出发公里数'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-10 06:26
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('wf', '0002_alter_state_filter_dept'),
|
||||
('ofm', '0003_remove_lendingseal_submit_user'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Vehicle',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('start_time', models.DateField(blank=True, null=True, verbose_name='出车时间')),
|
||||
('end_time', models.DateField(blank=True, null=True, verbose_name='还车时间')),
|
||||
('location', models.CharField(blank=True, max_length=100, null=True, verbose_name='出发地点')),
|
||||
('destination', models.CharField(blank=True, max_length=100, null=True, verbose_name='到达地点')),
|
||||
('start_km', models.PositiveIntegerField(verbose_name='出发公里数')),
|
||||
('end_km', models.PositiveIntegerField(verbose_name='归还公里数')),
|
||||
('actual_km', models.PositiveIntegerField(editable=False, verbose_name='实际行驶公里数')),
|
||||
('is_city', models.BooleanField(default=True, verbose_name='是否市内用车')),
|
||||
('reason', models.CharField(max_length=100, verbose_name='用车事由')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vehicle_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('ticket', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vehicle_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vehicle_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.12 on 2026-01-04 06:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0004_auto_20251218_1636'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='vehicleuse',
|
||||
name='end_km',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='归还公里数'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-10 06:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0004_vehicle'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='vehicle',
|
||||
name='via',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='途经地点'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-11 01:53
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('system', '0006_auto_20241213_1249'),
|
||||
('ofm', '0005_vehicle_via'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='vehicle',
|
||||
name='belong_dept',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vehicle_belong_dept', to='system.dept', verbose_name='所属部门'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-11 06:41
|
||||
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('system', '0006_auto_20241213_1249'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('ofm', '0006_vehicle_belong_dept'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='lendingseal',
|
||||
name='seal',
|
||||
field=models.JSONField(default=list, help_text='[公章,法人章,财务章,合同章,业务章,其他章]', verbose_name='印章信息'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='FileRecord',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('name', models.CharField(max_length=100, verbose_name='资料名称')),
|
||||
('number', models.CharField(blank=True, max_length=50, null=True, verbose_name='档案编号')),
|
||||
('counts', models.CharField(blank=True, max_length=10, null=True, verbose_name='文件份数')),
|
||||
('location', models.CharField(blank=True, max_length=100, null=True, verbose_name='存放位置')),
|
||||
('contacts', models.CharField(blank=True, max_length=50, null=True, validators=[django.core.validators.RegexValidator('^1[3456789]\\d{9}$', '手机号码格式不正确')], verbose_name='存档人电话')),
|
||||
('reciver', models.CharField(blank=True, max_length=50, null=True, verbose_name='接收人(综合办)')),
|
||||
('remark', models.TextField(blank=True, max_length=200, null=True, verbose_name='备注')),
|
||||
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='filerecord_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='filerecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='filerecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='BorrowRecord',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('borrow_date', models.DateField(blank=True, null=True, verbose_name='借阅日期')),
|
||||
('return_date', models.DateField(blank=True, null=True, verbose_name='归还日期')),
|
||||
('contacts', models.CharField(blank=True, max_length=50, null=True, validators=[django.core.validators.RegexValidator('^1[3456789]\\d{9}$', '手机号码格式不正确')], verbose_name='借阅人电话')),
|
||||
('remark', models.JSONField(default=list, help_text=['借阅', '复印', '查阅'], verbose_name='用途')),
|
||||
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='borrowrecord_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||
('borrow_file', models.ManyToManyField(related_name='borrow_records', to='ofm.FileRecord')),
|
||||
('borrow_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='borrow_user', to=settings.AUTH_USER_MODEL)),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='borrowrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='borrowrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-12 06:42
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0007_auto_20250911_1441'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='borrowrecord',
|
||||
name='borrow_user',
|
||||
),
|
||||
]
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-12 07:00
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wf', '0002_alter_state_filter_dept'),
|
||||
('ofm', '0008_remove_borrowrecord_borrow_user'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='borrowrecord',
|
||||
name='ticket',
|
||||
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='borrow_ticket', to='wf.ticket', verbose_name='关联工单'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-19 01:21
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('system', '0006_auto_20241213_1249'),
|
||||
('ofm', '0009_borrowrecord_ticket'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='lendingseal',
|
||||
name='seal_other',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='其他印章'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Publicity',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('number', models.CharField(max_length=50, verbose_name='记录编号')),
|
||||
('title', models.CharField(max_length=100, verbose_name='送审稿件标题')),
|
||||
('participants', models.CharField(max_length=50, verbose_name='所有撰稿人')),
|
||||
('level', models.JSONField(default=list, help_text=['重要', '一般', '非涉密'], verbose_name='用途')),
|
||||
('content', models.JSONField(default=list, help_text=['武器装备科研生产综合事项', '其它'], verbose_name='稿件内容涉及')),
|
||||
('other_content', models.CharField(blank=True, max_length=100, null=True, verbose_name='其它内容')),
|
||||
('report_purpose', models.CharField(blank=True, max_length=100, null=True, verbose_name='宣传报道目的')),
|
||||
('channel', models.JSONField(default=list, help_text=['互联网', '信息平台', '官微', '公开发行物', '其它'], verbose_name='发布渠道')),
|
||||
('channel_other', models.CharField(blank=True, max_length=50, null=True, verbose_name='其它渠道')),
|
||||
('other_channel', models.CharField(blank=True, max_length=50, null=True, verbose_name='其它渠道')),
|
||||
('report_name', models.CharField(blank=True, max_length=50, null=True, verbose_name='报道名称')),
|
||||
('review', models.JSONField(default=list, help_text=['内容不涉及国家秘密和商业秘密,申请公开', '内容涉及国家秘密,申请按涉密渠道发布'], verbose_name='第一撰稿人自审')),
|
||||
('dept_opinion', models.JSONField(default=list, help_text=['同意', '不同意'], verbose_name='部门负责人意见')),
|
||||
('dept_opinion_review', models.CharField(blank=True, max_length=100, null=True, verbose_name='部门审查意见')),
|
||||
('publicity_opinion', models.JSONField(default=list, help_text=['同意公开宣传报道', '不同意任何渠道的宣传报道'], verbose_name='宣传统战部审查意见')),
|
||||
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='publicity_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='publicity_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='publicity_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-24 05:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0010_auto_20250919_0921'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='publicity',
|
||||
name='channel_other',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='publicity',
|
||||
name='pfile',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='稿件路径'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='publicity',
|
||||
name='pub_dept',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='部室/研究院'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-24 06:07
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wf', '0003_workflow_view_path'),
|
||||
('ofm', '0011_auto_20250924_1359'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='publicity',
|
||||
name='ticket',
|
||||
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='publicity_ticket', to='wf.ticket', verbose_name='关联工单'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-25 07:41
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wf', '0003_workflow_view_path'),
|
||||
('ofm', '0012_publicity_ticket'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='mroomslot',
|
||||
name='ticket',
|
||||
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mrooms_ticket', to='wf.ticket', verbose_name='关联会议室'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-28 02:23
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wf', '0003_workflow_view_path'),
|
||||
('ofm', '0013_mroomslot_ticket'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='mroombooking',
|
||||
name='ticket',
|
||||
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mrooms_ticket', to='wf.ticket', verbose_name='关联会议室'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='mroomslot',
|
||||
name='is_inuse',
|
||||
field=models.BooleanField(default=True, verbose_name='是否占用'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='mroomslot',
|
||||
unique_together={('mroom', 'mdate', 'slot', 'is_inuse')},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='mroomslot',
|
||||
name='ticket',
|
||||
),
|
||||
]
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-28 06:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0014_auto_20250928_1023'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='vehicle',
|
||||
name='end_km',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='归还公里数'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-09-29 07:51
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('system', '0006_auto_20241213_1249'),
|
||||
('ofm', '0015_alter_vehicle_end_km'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='mroombooking',
|
||||
name='belong_dept',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mroombooking_belong_dept', to='system.dept', verbose_name='所属部门'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='mroombooking',
|
||||
name='key_participants',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='主要参会领导'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='mroombooking',
|
||||
name='note',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='备注'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='mroombooking',
|
||||
name='participant_count',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='参会人数'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-10-10 08:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0016_auto_20250929_1551'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='publicity',
|
||||
name='secret_period',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='秘密期限'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='level',
|
||||
field=models.JSONField(default=list, help_text=['重要', '一般', '非涉密'], verbose_name='涉密等级'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-10-11 01:22
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0017_auto_20251010_1631'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='dept_opinion',
|
||||
field=models.JSONField(blank=True, default=list, help_text=['同意', '不同意'], null=True, verbose_name='部门负责人意见'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='number',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='记录编号'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='publicity_opinion',
|
||||
field=models.JSONField(blank=True, default=list, help_text=['同意公开宣传报道', '不同意任何渠道的宣传报道'], null=True, verbose_name='宣传统战部审查意见'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='review',
|
||||
field=models.JSONField(blank=True, default=list, help_text=['内容不涉及国家秘密和商业秘密,申请公开', '内容涉及国家秘密,申请按涉密渠道发布'], null=True, verbose_name='第一撰稿人自审'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-10-11 03:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0018_auto_20251011_0922'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='dept_opinion',
|
||||
field=models.JSONField(default=list, help_text=['同意', '不同意'], verbose_name='部门负责人意见'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='publicity_opinion',
|
||||
field=models.JSONField(blank=True, default=list, help_text=['同意公开宣传报道', '不同意任何渠道的宣传报道'], verbose_name='宣传统战部审查意见'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='review',
|
||||
field=models.JSONField(blank=True, default=list, help_text=['内容不涉及国家秘密和商业秘密,申请公开', '内容涉及国家秘密,申请按涉密渠道发布'], verbose_name='第一撰稿人自审'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-10-11 06:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0019_auto_20251011_1128'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='dept_opinion',
|
||||
field=models.JSONField(blank=True, default=list, help_text=['同意', '不同意'], null=True, verbose_name='部门负责人意见'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='publicity_opinion',
|
||||
field=models.JSONField(blank=True, default=list, help_text=['同意公开宣传报道', '不同意任何渠道的宣传报道'], null=True, verbose_name='宣传统战部审查意见'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='review',
|
||||
field=models.JSONField(blank=True, default=list, help_text=['内容不涉及国家秘密和商业秘密,申请公开', '内容涉及国家秘密,申请按涉密渠道发布'], null=True, verbose_name='第一撰稿人自审'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-10-17 06:50
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0021_alter_publicity_publicity_opinion'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='mroomslot',
|
||||
unique_together=set(),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-10-21 06:08
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wf', '0004_workflow_view_path2'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('system', '0006_auto_20241213_1249'),
|
||||
('ofm', '0022_alter_mroomslot_unique_together'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PatentInfo',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('name', models.CharField(max_length=100, verbose_name='拟申请专利名称')),
|
||||
('author', models.CharField(max_length=100, verbose_name='发明人(设计人)')),
|
||||
('type', models.CharField(choices=[('invention', '发明专利'), ('utility', '实用新型专利'), ('design', '外观设计专利')], default='invention', max_length=50, verbose_name='专利类型')),
|
||||
('is_public', models.BooleanField(default=False, verbose_name='是否公开')),
|
||||
('area', models.CharField(choices=[('Domestic', '国内申请'), ('Foreign', '国外申请'), (' PCT', 'PCT申请')], default='Domestic', max_length=50, verbose_name='拟申请地域')),
|
||||
('identified', models.BooleanField(default=False, verbose_name='是否进行过科技成果鉴定')),
|
||||
('published_article', models.BooleanField(default=False, verbose_name='是否发表过文章')),
|
||||
('exhibited', models.BooleanField(default=False, verbose_name='是否参与过展会展出')),
|
||||
('applied_to_production', models.BooleanField(default=False, verbose_name='是否参与应用于生产/销售')),
|
||||
('participated_in_exchange', models.BooleanField(default=False, verbose_name='是否参与过技术交流')),
|
||||
('tech_background_pages', models.PositiveIntegerField(blank=True, null=True, verbose_name='技术背景材料页数')),
|
||||
('tech_disclosure_pages', models.PositiveIntegerField(blank=True, null=True, verbose_name='技术交底材料页数')),
|
||||
('novelty_report_pages', models.PositiveIntegerField(blank=True, null=True, verbose_name='查新检索报告页数')),
|
||||
('diagrams_or_photos_pages', models.PositiveIntegerField(blank=True, null=True, verbose_name='图/照片页数或张数')),
|
||||
('belong_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='patentinfo_belong_dept', to='system.dept', verbose_name='所属部门')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='patentinfo_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('ticket', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='patentInfo_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='patentinfo_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-10-22 02:05
|
||||
|
||||
import apps.ofm.models
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0023_patentinfo'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='patentinfo',
|
||||
name='applied_to_production',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='patentinfo',
|
||||
name='diagrams_or_photos_pages',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='patentinfo',
|
||||
name='exhibited',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='patentinfo',
|
||||
name='identified',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='patentinfo',
|
||||
name='novelty_report_pages',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='patentinfo',
|
||||
name='participated_in_exchange',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='patentinfo',
|
||||
name='published_article',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='patentinfo',
|
||||
name='tech_background_pages',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='patentinfo',
|
||||
name='tech_disclosure_pages',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='patentinfo',
|
||||
name='other_area',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='其它申请地域'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='patentinfo',
|
||||
name='tech_file',
|
||||
field=models.JSONField(default=list, help_text='技术文件信息列表,每个条目包含name(名称)page(页数)字段', verbose_name='技术文件'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='patentinfo',
|
||||
name='tech_status',
|
||||
field=models.JSONField(blank=True, default=list, help_text='技术状态信息列表,每个条目包含name(名称)、status(状态)、file(文件)字段', verbose_name='技术状态'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='borrowrecord',
|
||||
name='remark',
|
||||
field=models.JSONField(default=list, help_text="['借阅', '复印', '查阅']", verbose_name='用途'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='channel',
|
||||
field=models.JSONField(default=list, help_text="['互联网', '信息平台', '官微', '公开发行物', '其它']", verbose_name='发布渠道'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='content',
|
||||
field=models.JSONField(default=list, help_text="['武器装备科研生产综合事项', '其它']", verbose_name='稿件内容涉及'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='dept_opinion',
|
||||
field=models.JSONField(blank=True, default=list, help_text="['同意', '不同意']", null=True, verbose_name='部门负责人意见'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='level',
|
||||
field=models.JSONField(default=list, help_text="['重要', '一般', '非涉密']", verbose_name='涉密等级'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='review',
|
||||
field=models.JSONField(blank=True, default=list, help_text="['内容不涉及国家秘密和商业秘密,申请公开', '内容涉及国家秘密,申请按涉密渠道发布']", null=True, verbose_name='第一撰稿人自审'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-10-24 05:46
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0024_auto_20251022_1005'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='patentinfo',
|
||||
name='area',
|
||||
field=models.CharField(choices=[('Domestic', '国内申请'), ('Foreign', '国外申请'), ('PCT', 'PCT申请')], default='Domestic', max_length=50, verbose_name='拟申请地域'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-10-29 03:13
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('wf', '0004_workflow_view_path2'),
|
||||
('ofm', '0025_alter_patentinfo_area'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PaperSe',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('paper_name', models.CharField(max_length=100, verbose_name='拟发表论文名称')),
|
||||
('publication_name', models.CharField(max_length=100, verbose_name='拟投期刊名称')),
|
||||
('author', models.CharField(max_length=100, verbose_name='作者')),
|
||||
('paper_type', models.CharField(max_length=100, verbose_name='拟发表文章类型')),
|
||||
('is_chinese_core', models.BooleanField(default=False, verbose_name='是否为中文核心')),
|
||||
('is_sci', models.BooleanField(default=False, verbose_name='是否被SCI/EI收录')),
|
||||
('tech_status', models.JSONField(blank=True, default=list, help_text='技术状态信息列表,每个条目包含name(名称)、status(状态)、file(文件)字段', verbose_name='技术状态')),
|
||||
('tech_file', models.JSONField(default=list, help_text='技术文件信息列表,每个条目包含name(名称)page(页数)字段', verbose_name='技术文件')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='paperse_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('ticket', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='paperse_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='paperse_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-10-29 06:26
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('wf', '0004_workflow_view_path2'),
|
||||
('ofm', '0026_paperse'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Papersecret',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('paper_name', models.CharField(max_length=100, verbose_name='拟发表论文名称')),
|
||||
('publication_name', models.CharField(max_length=100, verbose_name='拟投期刊名称')),
|
||||
('author', models.CharField(max_length=100, verbose_name='作者')),
|
||||
('paper_type', models.CharField(max_length=100, verbose_name='拟发表文章类型')),
|
||||
('is_chinese_core', models.BooleanField(default=False, verbose_name='是否为中文核心')),
|
||||
('is_sci', models.BooleanField(default=False, verbose_name='是否被SCI/EI收录')),
|
||||
('tech_status', models.JSONField(blank=True, default=list, help_text='技术状态信息列表,每个条目包含name(名称)、status(状态)、file(文件)字段', verbose_name='技术状态')),
|
||||
('tech_file', models.JSONField(default=list, help_text='技术文件信息列表,每个条目包含name(名称)page(页数)字段', verbose_name='技术文件')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='papersecret_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('ticket', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='paperse_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='papersecret_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='PaperSe',
|
||||
),
|
||||
]
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-10-30 05:55
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('system', '0006_auto_20241213_1249'),
|
||||
('ofm', '0027_auto_20251029_1426'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='papersecret',
|
||||
name='belong_dept',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='papersecret_belong_dept', to='system.dept', verbose_name='所属部门'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-11-03 01:39
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0028_papersecret_belong_dept'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='patentinfo',
|
||||
name='belong_dept',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='patentinfo',
|
||||
name='create_by',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='patentinfo',
|
||||
name='ticket',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='patentinfo',
|
||||
name='update_by',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Papersecret',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='PatentInfo',
|
||||
),
|
||||
]
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-11-05 09:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0029_auto_20251103_0939'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='vehicle',
|
||||
name='end_time',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='还车时间'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vehicle',
|
||||
name='start_time',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='出车时间'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
# Generated by Django 3.2.12 on 2025-11-06 08:08
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ofm', '0030_auto_20251105_1715'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='publicity',
|
||||
name='disposal_method',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='处理方式'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='publicity',
|
||||
name='secret_level',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='秘密等级'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='publicity',
|
||||
name='dept_opinion',
|
||||
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='部门负责人意见'),
|
||||
),
|
||||
]
|
||||
|
|
@ -26,6 +26,7 @@ class Mroom(CommonADModel):
|
|||
location = models.CharField('位置', max_length=100)
|
||||
capacity = models.PositiveIntegerField('容纳人数')
|
||||
|
||||
|
||||
class MroomBooking(CommonBDModel):
|
||||
"""TN: 会议室预定信息"""
|
||||
# belong_dept 是预定部门
|
||||
|
|
@ -46,10 +47,45 @@ class MroomSlot(BaseModel):
|
|||
is_inuse = models.BooleanField('是否占用', default=True)
|
||||
|
||||
|
||||
# class Seal(BaseModel):
|
||||
# """TN: 印章类型"""
|
||||
# name = models.CharField('印章名称', max_length=50, unique=True)
|
||||
class VehicleReg(CommonADModel):
|
||||
"""TN: 车辆台账"""
|
||||
name = models.CharField('车辆名称', max_length=50)
|
||||
brand = models.CharField('品牌', max_length=50, null=True, blank=True)
|
||||
plate = models.CharField('车牌号', max_length=50)
|
||||
km = models.PositiveIntegerField('行驶里程', default=0)
|
||||
|
||||
class VehicleUse(CommonBDModel):
|
||||
"""TN: 用车记录"""
|
||||
location = models.CharField('出发地点', null=True, blank=True, max_length=100)
|
||||
via = models.CharField('途经地点', null=True, blank=True, max_length=100)
|
||||
destination = models.CharField('到达地点', null=True, blank=True, max_length=100)
|
||||
start_km = models.PositiveIntegerField('出发公里数', null=True, blank=True)
|
||||
end_km = models.PositiveIntegerField('归还公里数', default=0)
|
||||
actual_km = models.PositiveIntegerField('实际行驶公里数', editable=False)
|
||||
is_city = models.BooleanField('是否市内用车', default=True)
|
||||
reason = models.CharField('用车事由', max_length=100)
|
||||
reception = models.CharField('接待人', max_length=50, blank=True, null=True)
|
||||
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单',
|
||||
on_delete=models.SET_NULL, related_name='vehicle_ticket', null=True, blank=True, db_constraint=False)
|
||||
def save(self, *args, **kwargs):
|
||||
if self.start_km is not None:
|
||||
self.start_km = int(self.start_km)
|
||||
if self.end_km is not None:
|
||||
self.end_km = int(self.end_km)
|
||||
if self.start_km <= self.end_km:
|
||||
self.actual_km = self.end_km - self.start_km
|
||||
else:
|
||||
raise ParseError(f'归还公里数不能小于出发公里数, 出发公里数组为 {self.start_km} km')
|
||||
else:
|
||||
self.actual_km = 0
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
class VehicleSlot(BaseModel):
|
||||
vehreg = models.ForeignKey(VehicleReg, on_delete=models.CASCADE, related_name="slot_record")
|
||||
vehicle_use = models.ForeignKey(VehicleUse, on_delete=models.CASCADE, related_name="slot_vehicle")
|
||||
vdate = models.DateField('使用日期', db_index=True)
|
||||
slot = models.PositiveIntegerField('时段', help_text='0-47')
|
||||
is_inuse = models.BooleanField('是否占用', default=True)
|
||||
|
||||
class LendingSeal(CommonBDModel):
|
||||
"""TN: 印章外出用印信息"""
|
||||
|
|
@ -69,32 +105,6 @@ class LendingSeal(CommonBDModel):
|
|||
on_delete=models.SET_NULL, related_name='seal_ticket', null=True, blank=True, db_constraint=False)
|
||||
note = models.TextField('备注', null=True, blank=True)
|
||||
|
||||
|
||||
class Vehicle(CommonBDModel):
|
||||
"""TN: 用车申请"""
|
||||
start_time = models.DateTimeField('出车时间', blank=True, null=True)
|
||||
end_time = models.DateTimeField('还车时间', blank=True, null=True)
|
||||
location = models.CharField('出发地点', null=True, blank=True, max_length=100)
|
||||
via = models.CharField('途经地点', null=True, blank=True, max_length=100)
|
||||
destination = models.CharField('到达地点', null=True, blank=True, max_length=100)
|
||||
start_km = models.PositiveIntegerField('出发公里数')
|
||||
end_km = models.PositiveIntegerField('归还公里数', null=True, blank=True)
|
||||
actual_km = models.PositiveIntegerField('实际行驶公里数', editable=False)
|
||||
is_city = models.BooleanField('是否市内用车', default=True)
|
||||
reason = models.CharField('用车事由', max_length=100)
|
||||
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单',
|
||||
on_delete=models.SET_NULL, related_name='vehicle_ticket', null=True, blank=True, db_constraint=False)
|
||||
def save(self, *args, **kwargs):
|
||||
if self.end_km:
|
||||
if self.start_km <= self.end_km:
|
||||
self.actual_km = self.end_km - self.start_km
|
||||
else:
|
||||
raise ParseError('归还公里数不能小于出发公里数')
|
||||
else:
|
||||
self.actual_km = 0
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class FileRecord(CommonBDModel):
|
||||
"""TN: 档案台账"""
|
||||
name = models.CharField('资料名称', max_length=100)
|
||||
|
|
@ -105,12 +115,12 @@ class FileRecord(CommonBDModel):
|
|||
reciver = models.CharField('接收人(综合办)', max_length=50, null=True, blank=True)
|
||||
remark = models.TextField('备注', max_length=200, null=True, blank=True)
|
||||
|
||||
|
||||
class BorrowRecord(CommonBDModel):
|
||||
"""TN: 借阅、复印、查阅记录"""
|
||||
borrow_file = models.ManyToManyField(FileRecord, related_name="borrow_records")
|
||||
borrow_date = models.DateField('借阅日期', null=True, blank=True)
|
||||
return_date = models.DateField('归还日期', null=True, blank=True)
|
||||
count = models.PositiveIntegerField('借阅份数', null=True, blank=True)
|
||||
contacts = models.CharField('借阅人电话', max_length=50, validators=[phone_validator], null=True, blank=True)
|
||||
remark = models.JSONField('用途', default=list, help_text=str(['借阅', '复印', '查阅']))
|
||||
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from .models import (Mroom, MroomBooking, MroomSlot, LendingSeal, Vehicle, FileRecord, BorrowRecord, Publicity)
|
||||
from .models import (Mroom, MroomBooking, MroomSlot, LendingSeal, VehicleUse, FileRecord, BorrowRecord, Publicity, VehicleReg, VehicleSlot)
|
||||
from apps.utils.serializers import CustomModelSerializer
|
||||
from rest_framework import serializers
|
||||
from django.db import transaction
|
||||
|
|
@ -33,14 +33,17 @@ class MroomBookingSerializer(CustomModelSerializer):
|
|||
mdate = validated_data.pop('mdate')
|
||||
booking = super().create(validated_data)
|
||||
MroomSlot.objects.filter(booking=booking).delete()
|
||||
for slot in slots:
|
||||
if slot < 0 or slot > 47:
|
||||
raise ParseError("时段索引超出范围")
|
||||
ms_exists = MroomSlot.objects.filter(mroom=mroom, mdate=mdate, slot=slot, is_inuse=True).exists()
|
||||
if ms_exists:
|
||||
raise ParseError("时段已预订,请刷新重选")
|
||||
MroomSlot.objects.create(booking=booking, slot=slot, mdate=mdate, mroom=mroom, is_inuse=True)
|
||||
return booking
|
||||
if slots:
|
||||
for slot in slots:
|
||||
if slot < 0 or slot > 47:
|
||||
raise ParseError("时段索引超出范围")
|
||||
ms_exists = MroomSlot.objects.filter(mroom=mroom, mdate=mdate, slot=slot, is_inuse=True).exists()
|
||||
if ms_exists:
|
||||
raise ParseError("时段已预订,请刷新重选")
|
||||
MroomSlot.objects.create(booking=booking, slot=slot, mdate=mdate, mroom=mroom, is_inuse=True)
|
||||
return booking
|
||||
else:
|
||||
raise ParseError("请选择时段")
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
mroom = validated_data.pop('mroom')
|
||||
|
|
@ -48,16 +51,17 @@ class MroomBookingSerializer(CustomModelSerializer):
|
|||
mdate = validated_data.pop('mdate')
|
||||
booking = super().update(instance, validated_data)
|
||||
MroomSlot.objects.filter(booking=instance).delete()
|
||||
for slot in slots:
|
||||
if slot < 0 or slot > 47:
|
||||
raise ParseError("时段索引超出范围")
|
||||
ms_exists = MroomSlot.objects.filter(mroom=mroom, mdate=mdate, slot=slot, is_inuse=True).exists()
|
||||
if ms_exists:
|
||||
raise ParseError("时段已预订,请刷新重选")
|
||||
MroomSlot.objects.create(booking=booking, slot=slot, mdate=mdate, mroom=mroom, is_inuse=True)
|
||||
return booking
|
||||
|
||||
|
||||
if slots:
|
||||
for slot in slots:
|
||||
if slot < 0 or slot > 47:
|
||||
raise ParseError("时段索引超出范围")
|
||||
ms_exists = MroomSlot.objects.filter(mroom=mroom, mdate=mdate, slot=slot, is_inuse=True).exists()
|
||||
if ms_exists:
|
||||
raise ParseError("时段已预订,请刷新重选")
|
||||
MroomSlot.objects.create(booking=booking, slot=slot, mdate=mdate, mroom=mroom, is_inuse=True)
|
||||
return booking
|
||||
else:
|
||||
raise ParseError("请选择时段")
|
||||
class MroomSlotSerializer(CustomModelSerializer):
|
||||
booking_title = serializers.CharField(source='booking.title', read_only=True)
|
||||
class Meta:
|
||||
|
|
@ -65,6 +69,72 @@ class MroomSlotSerializer(CustomModelSerializer):
|
|||
fields = '__all__'
|
||||
|
||||
|
||||
class VehicleRecordSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
model = VehicleReg
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class VehicleUseSerializer(CustomModelSerializer):
|
||||
vehreg = serializers.PrimaryKeyRelatedField(queryset=VehicleReg.objects.all(), write_only=True, label="车辆信息")
|
||||
vdate = serializers.DateField(write_only=True, label="预订日期")
|
||||
slots = serializers.ListField(child=serializers.IntegerField(), write_only=True, label="时段索引")
|
||||
create_by_name = serializers.CharField(source='create_by.username', read_only=True)
|
||||
create_by_phone = serializers.CharField(source='create_by.phone', read_only=True)
|
||||
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
|
||||
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
|
||||
class Meta:
|
||||
model = VehicleUse
|
||||
fields = '__all__'
|
||||
read_only_fields = EXCLUDE_FIELDS + ['actual_km']
|
||||
extra_kwargs = {'belong_dept': {'required': True}}
|
||||
|
||||
def create(self, validated_data):
|
||||
vehreg = validated_data.pop('vehreg')
|
||||
slots = validated_data.pop('slots')
|
||||
vdate = validated_data.pop('vdate')
|
||||
validated_data['start_km'] = vehreg.km
|
||||
if validated_data.get('end_km', None):
|
||||
vehreg.km= validated_data['end_km']
|
||||
vehicle_use = super().create(validated_data)
|
||||
VehicleSlot.objects.filter(vehicle_use=vehicle_use).delete()
|
||||
if slots:
|
||||
for slot in slots:
|
||||
if slot < 0 or slot > 47:
|
||||
raise ParseError("时段索引超出范围")
|
||||
ms_exists = VehicleSlot.objects.filter(vehreg=vehreg, vdate=vdate, slot=slot, is_inuse=True).exists()
|
||||
if ms_exists:
|
||||
raise ParseError("时段已预订,请刷新重选")
|
||||
VehicleSlot.objects.create(vehicle_use=vehicle_use, slot=slot, vdate=vdate, vehreg=vehreg, is_inuse=True)
|
||||
return vehicle_use
|
||||
else:
|
||||
raise ParseError("请选择时段")
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
vehreg = validated_data.pop('vehreg')
|
||||
slots = validated_data.pop('slots')
|
||||
vdate = validated_data.pop('vdate')
|
||||
vehicle_use = super().update(instance, validated_data)
|
||||
VehicleSlot.objects.filter(vehicle_use=vehicle_use).delete()
|
||||
if slots:
|
||||
for slot in slots:
|
||||
if slot < 0 or slot > 47:
|
||||
raise ParseError("时段索引超出范围")
|
||||
ms_exists = VehicleSlot.objects.filter(vehreg=vehreg, vdate=vdate, slot=slot, is_inuse=True).exists()
|
||||
if ms_exists:
|
||||
raise ParseError("时段已预订,请刷新重选")
|
||||
VehicleSlot.objects.create(vehicle_use=vehicle_use, slot=slot, vdate=vdate, vehreg=vehreg, is_inuse=True)
|
||||
return vehicle_use
|
||||
else:
|
||||
raise ParseError("请选择时段")
|
||||
|
||||
|
||||
class VehSlotSerializer(CustomModelSerializer):
|
||||
veh_name = serializers.CharField(source='vehreg.name', read_only=True)
|
||||
class Meta:
|
||||
model = VehicleSlot
|
||||
fields = '__all__'
|
||||
|
||||
class LendingSealSerializer(CustomModelSerializer):
|
||||
create_by_name = serializers.CharField(source='create_by.name', read_only=True)
|
||||
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
|
||||
|
|
@ -74,17 +144,6 @@ class LendingSealSerializer(CustomModelSerializer):
|
|||
fields = '__all__'
|
||||
read_only_fields = EXCLUDE_FIELDS
|
||||
|
||||
|
||||
class VehicleSerializer(CustomModelSerializer):
|
||||
create_by_name = serializers.CharField(source='create_by.name', read_only=True)
|
||||
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
|
||||
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
|
||||
class Meta:
|
||||
model = Vehicle
|
||||
fields = '__all__'
|
||||
read_only_fields = EXCLUDE_FIELDS + ['actual_km']
|
||||
|
||||
|
||||
class FileRecordSerializer(CustomModelSerializer):
|
||||
create_by_name = serializers.CharField(source='create_by.name', read_only=True)
|
||||
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
from apps.wf.models import Ticket
|
||||
# TicketFlow, Transition, Workflow, CustomField, State,
|
||||
from apps.ofm.models import LendingSeal, Vehicle, BorrowRecord, Publicity, MroomBooking, MroomSlot
|
||||
from apps.ofm.models import LendingSeal, VehicleUse, BorrowRecord, Publicity, MroomBooking, MroomSlot
|
||||
from rest_framework.exceptions import ParseError
|
||||
|
||||
|
||||
|
|
@ -29,10 +29,11 @@ def mroombooking_reject(ticket: Ticket, transition, new_ticket_data: dict):
|
|||
ins = MroomBooking.objects.get(id=new_ticket_data['t_id'])
|
||||
MroomSlot.objects.filter(booking=ins).update(is_inuse=False)
|
||||
|
||||
|
||||
def bind_lendingseal(ticket: Ticket, transition, new_ticket_data: dict):
|
||||
ticket_data = ticket.ticket_data
|
||||
ins = LendingSeal.objects.get(id=new_ticket_data['t_id'])
|
||||
ins.actual_return_date = None
|
||||
ticket_data = ticket.ticket_data
|
||||
ticket_data.update({
|
||||
't_model': 'LendingSeal',
|
||||
't_id': ins.id,
|
||||
|
|
@ -59,7 +60,7 @@ def lending_save_ticket_data(ticket: Ticket, new_ticket_data: dict, **kwargs):
|
|||
|
||||
|
||||
def bind_vehicle(ticket: Ticket, transition, new_ticket_data: dict):
|
||||
ins = Vehicle.objects.get(id=new_ticket_data['t_id'])
|
||||
ins = VehicleUse.objects.get(id=new_ticket_data['t_id'])
|
||||
ticket_data = ticket.ticket_data
|
||||
ticket_data.update({
|
||||
't_model': 'Vehicle',
|
||||
|
|
@ -67,7 +68,6 @@ def bind_vehicle(ticket: Ticket, transition, new_ticket_data: dict):
|
|||
'is_city': ins.is_city
|
||||
})
|
||||
ins.actual_km = None
|
||||
ins.end_time = None
|
||||
ticket.ticket_data = ticket_data
|
||||
ticket.create_by = ins.create_by
|
||||
ticket.save()
|
||||
|
|
@ -77,8 +77,8 @@ def bind_vehicle(ticket: Ticket, transition, new_ticket_data: dict):
|
|||
|
||||
def vehicle_save_ticket_data(ticket: Ticket, new_ticket_data: dict, **kwargs):
|
||||
try:
|
||||
obj = Vehicle.objects.get(id=new_ticket_data['t_id'])
|
||||
except Vehicle.DoesNotExist:
|
||||
obj = VehicleUse.objects.get(id=new_ticket_data['t_id'])
|
||||
except VehicleUse.DoesNotExist:
|
||||
raise ParseError("Publicity t_id 不存在")
|
||||
data_save = {k: v for k, v in new_ticket_data.items() if k not in ['t_model', 't_id']}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from apps.ofm.views import (MroomViewSet, MroomBookingViewSet, MroomSlotViewSet,LendingSealViewSet, VehicleViewSet, FilerecordViewSet, FileborrowViewSet, PublicityViewSet)
|
||||
from apps.ofm.views import (MroomViewSet, MroomBookingViewSet, MroomSlotViewSet,LendingSealViewSet, VehicleUseViewSet,
|
||||
VehicleRegViewSet, VehicleSlotViewSet, FilerecordViewSet, FileborrowViewSet, PublicityViewSet)
|
||||
|
||||
API_BASE_URL = 'api/ofm/'
|
||||
HTML_BASE_URL = 'dhtml/ofm/'
|
||||
|
|
@ -11,7 +12,9 @@ router.register('mroombooking', MroomBookingViewSet, basename='mroombooking')
|
|||
router.register('mroomslot', MroomSlotViewSet, basename='mroomslot')
|
||||
# router.register('sealmanage', SealManageViewSet, basename='sealmanage')
|
||||
router.register('lendingseal', LendingSealViewSet, basename='lendingseal')
|
||||
router.register('vehicle', VehicleViewSet, basename='vehicle')
|
||||
router.register('vehicle-use', VehicleUseViewSet, basename='vehicle_use')
|
||||
router.register('vsolt', VehicleSlotViewSet, basename='vslot')
|
||||
router.register('vehicle', VehicleRegViewSet, basename='vehicle')
|
||||
router.register('filerecord', FilerecordViewSet, basename='filerecord')
|
||||
router.register('fileborrow', FileborrowViewSet, basename='fileborrow')
|
||||
router.register('publicity', PublicityViewSet, basename='publicity')
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
from django.shortcuts import render
|
||||
from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet
|
||||
from .models import Mroom, MroomBooking, MroomSlot, LendingSeal, Vehicle, FileRecord, BorrowRecord, Publicity
|
||||
from .models import Mroom, MroomBooking, MroomSlot, LendingSeal, VehicleUse, VehicleSlot, VehicleReg, FileRecord, BorrowRecord, Publicity
|
||||
|
||||
from .serializers import (MroomSerializer, MroomBookingSerializer, MroomSlotSerializer, LendingSealSerializer,
|
||||
VehicleSerializer, FileRecordSerializer, BorrowRecordSerializer, PublicitySerializer)
|
||||
VehicleUseSerializer, VehicleRecordSerializer, VehSlotSerializer, FileRecordSerializer, BorrowRecordSerializer, PublicitySerializer)
|
||||
from apps.utils.mixins import CustomListModelMixin
|
||||
from rest_framework.exceptions import ParseError
|
||||
from apps.ofm.filters import MroomBookingFilterset, SealFilter, BorrowRecordFilter
|
||||
from apps.ofm.filters import MroomBookingFilterset, SealFilter, BorrowRecordFilter, VehicleFilterset
|
||||
from rest_framework.response import Response
|
||||
|
||||
class MroomViewSet(CustomModelViewSet):
|
||||
|
|
@ -115,8 +115,6 @@ class LendingSealViewSet(CustomModelViewSet):
|
|||
|
||||
印章外出
|
||||
"""
|
||||
perms_map = {'get': '*', 'post': 'seal.update',
|
||||
'put': 'seal.update', 'delete': 'seal.delete'}
|
||||
queryset = LendingSeal.objects.all()
|
||||
serializer_class = LendingSealSerializer
|
||||
filterset_class = SealFilter
|
||||
|
|
@ -124,15 +122,106 @@ class LendingSealViewSet(CustomModelViewSet):
|
|||
data_filter = True
|
||||
|
||||
|
||||
class VehicleViewSet(CustomModelViewSet):
|
||||
class VehicleRegViewSet(CustomModelViewSet):
|
||||
"""list: 车辆
|
||||
|
||||
车辆
|
||||
"""
|
||||
queryset = Vehicle.objects.all()
|
||||
serializer_class = VehicleSerializer
|
||||
queryset = VehicleReg.objects.all()
|
||||
serializer_class = VehicleRecordSerializer
|
||||
ordering = ["-create_time"]
|
||||
|
||||
class VehicleUseViewSet(CustomModelViewSet):
|
||||
"""list: 车辆使用
|
||||
|
||||
车辆使用
|
||||
"""
|
||||
queryset = VehicleUse.objects.all()
|
||||
serializer_class = VehicleUseSerializer
|
||||
select_related_fields = ["create_by", "ticket", "belong_dept"]
|
||||
filterset_class = VehicleFilterset
|
||||
|
||||
def add_info_for_list(self, data):
|
||||
vehicle_ids = [d["id"] for d in data]
|
||||
slots = VehicleSlot.objects.filter(vehicle_use__in=vehicle_ids).order_by("vehreg", "vehicle_use", "vdate", "slot")
|
||||
vehicle_info = {}
|
||||
for slot in slots:
|
||||
vehicle_id = slot.vehicle_use.id
|
||||
|
||||
if vehicle_id not in vehicle_info:
|
||||
vehicle_info[vehicle_id] = {
|
||||
"vdate": slot.vdate.strftime("%Y-%m-%d"), # 格式化日期
|
||||
"vehreg": slot.vehreg.id,
|
||||
"vehreg_name": slot.vehreg.name, # 会议室名称
|
||||
"time_ranges": [], # 存储时间段(如 ["8:00-9:00", "10:00-11:30"])
|
||||
"current_slots": [], # 临时存储连续的slot(用于合并)
|
||||
}
|
||||
|
||||
# 检查是否连续(当前slot是否紧接上一个slot)
|
||||
current_slots = vehicle_info[vehicle_id]["current_slots"]
|
||||
if not current_slots or slot.slot == current_slots[-1] + 1:
|
||||
current_slots.append(slot.slot)
|
||||
else:
|
||||
# 如果不连续,先把当前连续的slot转换成时间段
|
||||
if current_slots:
|
||||
start_time = self._slot_to_time(current_slots[0])
|
||||
end_time = self._slot_to_time(current_slots[-1] + 1)
|
||||
vehicle_info[vehicle_id]["time_ranges"].append(f"{start_time}-{end_time}")
|
||||
current_slots.clear()
|
||||
current_slots.append(slot.slot)
|
||||
|
||||
# 处理最后剩余的连续slot
|
||||
for info in vehicle_info.values():
|
||||
if info["current_slots"]:
|
||||
start_time = self._slot_to_time(info["current_slots"][0])
|
||||
end_time = self._slot_to_time(info["current_slots"][-1] + 1)
|
||||
info["time_ranges"].append(f"{start_time}-{end_time}")
|
||||
info["slots"] = info.pop("current_slots") # 清理临时数据
|
||||
|
||||
for item in data:
|
||||
item.update(vehicle_info.get(item["id"], {}))
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _slot_to_time(slot):
|
||||
"""将slot (0-47) 转换为 HH:MM 格式的时间字符串"""
|
||||
hours = slot // 2
|
||||
minutes = (slot % 2) * 30
|
||||
return f"{hours:02d}:{minutes:02d}"
|
||||
|
||||
def perform_update(self, serializer):
|
||||
ins:VehicleUse = self.get_object()
|
||||
ticket = ins.ticket
|
||||
if ticket is None or ticket.state.type == 1:
|
||||
pass
|
||||
else:
|
||||
raise ParseError("存在审批单,不允许修改")
|
||||
if ins.create_by and ins.create_by != self.request.user:
|
||||
raise ParseError("只允许创建者修改")
|
||||
return super().perform_update(serializer)
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
if instance.create_by and instance.create_by != self.request.user:
|
||||
raise ParseError("只允许创建者删除")
|
||||
ticket = instance.ticket
|
||||
if ticket is None or ticket.state.type == 1:
|
||||
pass
|
||||
else:
|
||||
raise ParseError("存在审批单,不允许删除")
|
||||
if ticket:
|
||||
ticket.delete()
|
||||
instance.delete()
|
||||
|
||||
|
||||
class VehicleSlotViewSet(CustomListModelMixin, CustomGenericViewSet):
|
||||
"""list:
|
||||
|
||||
会议室预订时段
|
||||
"""
|
||||
queryset = VehicleSlot.objects.all()
|
||||
serializer_class = VehSlotSerializer
|
||||
filterset_fields = ["vehreg", "vehicle_use", "vdate", "is_inuse"]
|
||||
|
||||
|
||||
class FilerecordViewSet(CustomModelViewSet):
|
||||
"""list: 文件
|
||||
|
|
@ -167,120 +256,3 @@ class PublicityViewSet(CustomModelViewSet):
|
|||
ordering = ["-create_time", "number"]
|
||||
|
||||
|
||||
# class PatentInfoViewSet(CustomModelViewSet):
|
||||
# """list: 专利
|
||||
|
||||
# 专利
|
||||
# """
|
||||
# queryset = PatentInfo.objects.all()
|
||||
# serializer_class = PatentInfoSerializer
|
||||
# filterset_fields = ["name", "author", "type"]
|
||||
# ordering = ["-create_time", "name", "author", "type"]
|
||||
|
||||
|
||||
# class PapersecretViewSet(CustomModelViewSet):
|
||||
# """list: 论文申密审批
|
||||
|
||||
# 论文申密审批
|
||||
# """
|
||||
# queryset = Papersecret.objects.all()
|
||||
# serializer_class = PaperSeSerializer
|
||||
# filterset_fields = ["paper_name", "author"]
|
||||
# ordering = ["-create_time", "paper_name"]
|
||||
|
||||
|
||||
# class PatentRecordViewSet(CustomModelViewSet):
|
||||
# """list: 专利台账登记
|
||||
|
||||
# 专利台账登记
|
||||
# """
|
||||
# queryset = PatentRecord.objects.all()
|
||||
# serializer_class = PatentRecordSerializer
|
||||
# select_related_fields = ["patent"]
|
||||
# filterset_fields = ["title", "volume_number","inventors"]
|
||||
# ordering = ["-create_time", "title", "type"]
|
||||
# search_fields = ["title", "volume_number", "inventors"]
|
||||
|
||||
# def get_queryset(self):
|
||||
# qs = super().get_queryset()
|
||||
# patent_type = self.request.query_params.get('patent_type', None)
|
||||
# if patent_type:
|
||||
# qs = qs.filter(patent__type=patent_type)
|
||||
# return qs
|
||||
|
||||
|
||||
# @action(detail=False, methods=['get'])
|
||||
# def patent_name(self, request):
|
||||
# """获取专利列表"""
|
||||
# search = request.query_params.get('search', '')
|
||||
# queryset = PatentInfo.objects.all()
|
||||
# if search:
|
||||
# queryset = queryset.filter(name__icontains=search)
|
||||
# patents = [{'id': patent.id, 'name': patent.name} for patent in queryset]
|
||||
# return Response(patents)
|
||||
|
||||
|
||||
# class PlatformViewSet(CustomModelViewSet):
|
||||
# """list: 平台
|
||||
|
||||
# 平台
|
||||
# """
|
||||
# queryset = Platform.objects.all()
|
||||
# serializer_class = PlatformSerializer
|
||||
# filterset_fields = ["name"]
|
||||
# ordering = ["create_time", "name"]
|
||||
|
||||
|
||||
# class ProjectViewSet(CustomModelViewSet):
|
||||
# """list: 项目
|
||||
|
||||
# 项目
|
||||
# """
|
||||
# queryset = Project.objects.all()
|
||||
# serializer_class = ProjectSerializer
|
||||
# filterset_fields = ["name"]
|
||||
# ordering = ["create_time", "name"]
|
||||
|
||||
|
||||
# class PatentRecordViewSet(CustomModelViewSet):
|
||||
# """list: 专利台账登记
|
||||
|
||||
# 专利台账登记
|
||||
# """
|
||||
# queryset = PatentRecord.objects.all()
|
||||
# serializer_class = ProjectMemberSerializer
|
||||
# filterset_fields = ["patent", "type"]
|
||||
# ordering = ["create_time", "patent", "type"]
|
||||
|
||||
|
||||
# class PaperRecordViewSet(CustomModelViewSet):
|
||||
# """list: 论文台账登记
|
||||
|
||||
# 论文台账登记
|
||||
# """
|
||||
# queryset = PaperRecord.objects.all()
|
||||
# serializer_class = ProjectMemberSerializer
|
||||
# filterset_fields = ["index", "title", "paper_code","paper_type", "authors"]
|
||||
# ordering = ["create_time", "paper", "type"]
|
||||
|
||||
|
||||
# class ProjectApprovalViewSet(CustomModelViewSet):
|
||||
# """list: 立项审批表
|
||||
|
||||
# 立项审批表
|
||||
# """
|
||||
# queryset = ProjectApproval.objects.all()
|
||||
# serializer_class = ProjectApprovalSerializer
|
||||
# filterset_fields = ["project_start_date"]
|
||||
# ordering = ["project_start_date"]
|
||||
|
||||
|
||||
# class ProjectInfoViewSet(CustomModelViewSet):
|
||||
# """list: 项目信息
|
||||
|
||||
# 项目信息
|
||||
# """
|
||||
# queryset = ProjectInfo.objects.all()
|
||||
# serializer_class = ProjectInfoSerializer
|
||||
# filterset_fields = ["serial_number", "name", "platform", "project_source"]
|
||||
# ordering = ["serial_number", "name"]
|
||||
|
|
|
|||
|
|
@ -82,8 +82,8 @@ class PmService:
|
|||
queue.append(input_material_id)
|
||||
|
||||
|
||||
# 返回各Route的任务数量
|
||||
return {route.id: step_production.get(route.id, 0) for route in route_qs}
|
||||
# 返回各Route的任务数量/默认在输入物料时使用target_quantity
|
||||
return {route.id: step_production.get(route.id, target_quantity if route.material_in.id == target_materialId else 0) for route in route_qs}
|
||||
|
||||
@classmethod
|
||||
def cal_real_task_count(cls, count: int, rate_list):
|
||||
|
|
@ -211,14 +211,18 @@ class PmService:
|
|||
if not gjson_item:
|
||||
raise ParseError("缺少该产品的生产子图")
|
||||
rqs = Route.get_routes(routeIds=gjson_item["routes"])
|
||||
if not rqs.exists():
|
||||
raise ParseError('未配置工艺路线')
|
||||
Route.validate_dag(product, rqs)
|
||||
else:
|
||||
rqs = Route.get_routes(material=product)
|
||||
if not rqs.exists():
|
||||
raise ParseError('未配置工艺路线')
|
||||
Route.validate_dag(product, rqs, check_final=False)
|
||||
|
||||
if not rqs.exists():
|
||||
raise ParseError('未配置工艺路线')
|
||||
|
||||
|
||||
# 通过出材率校正任务数, out_rate 默认为 100
|
||||
Route.validate_dag(product, rqs)
|
||||
r_count_dict = cls.cal_x_task_count(product.id, count, rqs)
|
||||
|
||||
# 创建小任务
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
# Generated by Django 3.2.12 on 2025-11-10 02:00
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('system', '0006_auto_20241213_1249'),
|
||||
('wf', '0004_workflow_view_path2'),
|
||||
('pum', '0008_auto_20240731_1829'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SupplierAudit',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('name', models.CharField(max_length=100, verbose_name='供应商名称')),
|
||||
('material_name', models.CharField(max_length=100, verbose_name='物料名称')),
|
||||
('material_cate', models.CharField(max_length=100, verbose_name='物料类别')),
|
||||
('business_license', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='supplier_audit_business_license', to='system.file', verbose_name='营业执照')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='supplieraudit_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('quality_certificate', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='supplier_audit_quality_certificate', to='system.file', verbose_name='质量证书')),
|
||||
('survery_form', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='supplier_audit_survey_form', to='system.file', verbose_name='调查表')),
|
||||
('ticket', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='supplieraudit_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# Generated by Django 3.2.12 on 2025-12-26 07:29
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('wf', '0006_auto_20251215_1645'),
|
||||
('pum', '0009_supplieraudit'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='QuotationApply',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('customer_name', models.CharField(max_length=50, verbose_name='客户名称')),
|
||||
('product_name', models.CharField(max_length=50, verbose_name='产品名称')),
|
||||
('contact_person', models.CharField(max_length=50, verbose_name='联系人')),
|
||||
('contact_phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='联系电话')),
|
||||
('receive_address', models.CharField(blank=True, max_length=100, null=True, verbose_name='收件地址')),
|
||||
('product_spec_quantity', models.TextField(blank=True, null=True, verbose_name='产品规格/数量')),
|
||||
('quotation_basis', models.TextField(blank=True, null=True, verbose_name='报价依据')),
|
||||
('suggested_price_calc', models.TextField(blank=True, null=True, verbose_name='建议价格及计算方式')),
|
||||
('quotation_range', models.CharField(blank=True, max_length=100, null=True, verbose_name='报价区间')),
|
||||
('quoter', models.CharField(blank=True, max_length=50, null=True, verbose_name='报价人')),
|
||||
('apply_date', models.DateField(auto_now_add=True, verbose_name='申请日期')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quotationapply_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('ticket', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='quo_ticket', to='wf.ticket', verbose_name='关联工单')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quotationapply_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from django.db import models
|
||||
from apps.utils.models import CommonBModel, BaseModel, CommonBDModel
|
||||
from apps.utils.models import CommonBModel, BaseModel, CommonBDModel, CommonADModel
|
||||
from apps.mtm.models import Material
|
||||
from apps.wf.models import Ticket
|
||||
|
||||
|
||||
# Create your models here.
|
||||
|
|
@ -15,6 +16,15 @@ class Supplier(CommonBModel):
|
|||
address = models.CharField('地址', max_length=200, default='', blank=True)
|
||||
can_outsource = models.BooleanField('是否可外协', default=False)
|
||||
|
||||
class SupplierAudit(CommonADModel):
|
||||
name = models.CharField('供应商名称', max_length=100)
|
||||
material_name = models.CharField('物料名称', max_length=100)
|
||||
material_cate = models.CharField('物料类别', max_length=100)
|
||||
survery_form = models.ForeignKey('system.file', verbose_name='调查表', on_delete=models.SET_NULL, null=True, blank=True, related_name='supplier_audit_survey_form')
|
||||
business_license = models.ForeignKey('system.file', verbose_name='营业执照', on_delete=models.SET_NULL, null=True, blank=True, related_name='supplier_audit_business_license')
|
||||
quality_certificate = models.ForeignKey('system.file', verbose_name='质量证书', on_delete=models.SET_NULL, null=True, blank=True, related_name='supplier_audit_quality_certificate')
|
||||
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
|
||||
|
||||
class PuPlan(CommonBModel):
|
||||
"""
|
||||
|
|
@ -97,3 +107,22 @@ class PuPlanItem(CommonBDModel):
|
|||
null=True, blank=True, related_name='item_puplan')
|
||||
pu_order = models.ForeignKey(PuOrder, verbose_name='关联采购订单',
|
||||
on_delete=models.SET_NULL, null=True, blank=True, related_name='puplan_item_puorder')
|
||||
|
||||
|
||||
class QuotationApply(CommonADModel):
|
||||
"""
|
||||
TN:报价申请表
|
||||
"""
|
||||
customer_name = models.CharField(max_length=50,verbose_name="客户名称")
|
||||
product_name = models.CharField(max_length=50,verbose_name="产品名称")
|
||||
contact_person = models.CharField(max_length=50,verbose_name="联系人")
|
||||
contact_phone = models.CharField(max_length=20,verbose_name="联系电话", null=True, blank=True)
|
||||
receive_address = models.CharField(max_length=100,verbose_name="收件地址", null=True, blank=True)
|
||||
product_spec_quantity = models.TextField(verbose_name="产品规格/数量", null=True, blank=True)
|
||||
quotation_basis = models.TextField(verbose_name="报价依据", null=True, blank=True)
|
||||
suggested_price_calc = models.TextField(verbose_name="建议价格及计算方式", null=True, blank=True)
|
||||
quotation_range = models.CharField(max_length=100,verbose_name="报价区间", null=True, blank=True)
|
||||
quoter = models.CharField(max_length=50,verbose_name="报价人", null=True, blank=True)
|
||||
apply_date = models.DateField(verbose_name="申请日期",auto_now_add=True)
|
||||
ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单',
|
||||
on_delete=models.CASCADE, related_name='quo_ticket', null=True, blank=True)
|
||||
|
|
@ -1,12 +1,14 @@
|
|||
from rest_framework import serializers
|
||||
from apps.utils.serializers import CustomModelSerializer
|
||||
from apps.utils.constants import EXCLUDE_FIELDS_DEPT, EXCLUDE_FIELDS_BASE, EXCLUDE_FIELDS
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.exceptions import ValidationError, ParseError
|
||||
|
||||
from apps.pum.models import Supplier, PuPlan, PuPlanItem, PuOrder, PuOrderItem
|
||||
from apps.pum.models import Supplier, PuPlan, PuPlanItem, PuOrder, PuOrderItem, SupplierAudit, QuotationApply
|
||||
from apps.mtm.serializers import MaterialSerializer, MaterialSimpleSerializer
|
||||
from django.db import transaction
|
||||
from .services import PumService
|
||||
from apps.wf.serializers import TicketSimpleSerializer
|
||||
from apps.system.serializers import FileSerializer
|
||||
|
||||
|
||||
class SupplierSerializer(CustomModelSerializer):
|
||||
|
|
@ -139,3 +141,27 @@ class AddSerializer(serializers.Serializer):
|
|||
label='采购订单ID', queryset=PuOrder.objects.all())
|
||||
pu_planitems = serializers.PrimaryKeyRelatedField(
|
||||
label='计划明细ID', queryset=PuPlanItem.objects.all(), many=True)
|
||||
|
||||
|
||||
class SupplierAuditSerializer(CustomModelSerializer):
|
||||
survery_form_ = FileSerializer(source="survery_form", read_only=True)
|
||||
business_license_ = FileSerializer(source="business_license", read_only=True)
|
||||
quality_certificate_ = FileSerializer(source="quality_certificate", read_only=True)
|
||||
ticket_ = TicketSimpleSerializer(source="ticket", read_only=True)
|
||||
class Meta:
|
||||
model = SupplierAudit
|
||||
fields = "__all__"
|
||||
read_only_fields = EXCLUDE_FIELDS_BASE + ['ticket']
|
||||
|
||||
def create(self, validated_data):
|
||||
name = validated_data["name"]
|
||||
if Supplier.objects.filter(name=name).exists():
|
||||
raise ParseError('供应商名称已存在')
|
||||
return super().create(validated_data)
|
||||
|
||||
class QuotationApplySerializer(CustomModelSerializer):
|
||||
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
|
||||
class Meta:
|
||||
model = QuotationApply
|
||||
fields = "__all__"
|
||||
read_only_fields = EXCLUDE_FIELDS
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
from rest_framework.exceptions import ValidationError
|
||||
from apps.pum.models import PuOrderItem, PuPlan, PuPlanItem, PuOrder
|
||||
from apps.pum.models import PuOrderItem, PuPlan, PuPlanItem, PuOrder, SupplierAudit
|
||||
from django.db.models import F, Sum
|
||||
from apps.inm.models import MIO, MIOItem
|
||||
from rest_framework.exceptions import ParseError
|
||||
|
||||
from apps.wf.models import Ticket, Transition
|
||||
|
||||
class PumService:
|
||||
|
||||
|
|
@ -95,3 +95,18 @@ class PumService:
|
|||
puplan.state = PuPlan.PUPLAN_DONE
|
||||
puplan.save()
|
||||
|
||||
def bind_supplieraudit(ticket: Ticket, transition: Transition, new_ticket_data: dict):
|
||||
ins = SupplierAudit.objects.get(id=new_ticket_data['t_id'])
|
||||
if ins.ticket and ins.ticket.id != ticket.id:
|
||||
raise ParseError('重复创建工单')
|
||||
ticket_data = ticket.ticket_data
|
||||
ticket_data.update({
|
||||
't_model': 'supplier_audit',
|
||||
't_id': ins.id,
|
||||
})
|
||||
ticket.ticket_data = ticket_data
|
||||
ticket.create_by = ins.create_by
|
||||
ticket.save()
|
||||
if ins.ticket is None:
|
||||
ins.ticket = ticket
|
||||
ins.save()
|
||||
|
|
@ -1,16 +1,19 @@
|
|||
from django.urls import path, include
|
||||
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, SupplierAuditViewSet, QuotationApplyViewSet)
|
||||
# from apps.pum.views import SupplierViewSet, PuPlanViewSet, PuPlanItemViewSet, PuOrderViewSet, PuOrderItemViewSet
|
||||
|
||||
API_BASE_URL = 'api/pum/'
|
||||
HTML_BASE_URL = 'dhtml/pum/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('supplier', SupplierViewSet, basename='supplier')
|
||||
router.register('supplieraudit', SupplierAuditViewSet, basename='supplieraudit')
|
||||
router.register('pu_plan', PuPlanViewSet, basename='pu_plan')
|
||||
router.register('pu_planitem', PuPlanItemViewSet, basename='pu_planitem')
|
||||
router.register('pu_order', PuOrderViewSet, basename='pu_order')
|
||||
router.register('pu_orderitem', PuOrderItemViewSet, basename='pu_orderitem')
|
||||
router.register('quotation', QuotationApplyViewSet, basename='quotation')
|
||||
urlpatterns = [
|
||||
path(API_BASE_URL, include(router.urls)),
|
||||
]
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
from django.shortcuts import render
|
||||
from apps.pum.models import Supplier, PuPlan, PuPlanItem, PuOrder, PuOrderItem
|
||||
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
||||
from apps.pum.serializers import (SupplierSerializer, PuPlanSerializer, PuPlanItemSerializer,
|
||||
PuOrderSerializer, PuOrderItemSerializer, AddSerializer)
|
||||
from apps.pum.models import Supplier, PuPlan, PuPlanItem, PuOrder, PuOrderItem, SupplierAudit, QuotationApply
|
||||
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet, EuModelViewSet
|
||||
from apps.pum.serializers import (SupplierSerializer, PuPlanSerializer, PuPlanItemSerializer, QuotationApplySerializer,
|
||||
PuOrderSerializer, PuOrderItemSerializer, AddSerializer, SupplierAuditSerializer)
|
||||
from rest_framework.exceptions import ParseError, PermissionDenied
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework import serializers
|
||||
|
|
@ -11,6 +11,8 @@ from django.db import transaction
|
|||
from rest_framework.response import Response
|
||||
from django.utils import timezone
|
||||
from apps.pum.services import PumService
|
||||
from apps.wf.mixins import TicketMixin
|
||||
from apps.wf.models import Ticket
|
||||
# Create your views here.
|
||||
|
||||
|
||||
|
|
@ -30,6 +32,23 @@ class SupplierViewSet(CustomModelViewSet):
|
|||
raise ParseError('该供应商存在采购订单不可删除')
|
||||
instance.delete()
|
||||
|
||||
class SupplierAuditViewSet(TicketMixin, CustomModelViewSet):
|
||||
"""
|
||||
list: 供应商审核
|
||||
|
||||
供应商审核
|
||||
"""
|
||||
queryset = SupplierAudit.objects.all()
|
||||
serializer_class = SupplierAuditSerializer
|
||||
search_fields = ['name', 'material_name', 'material_cate']
|
||||
workflow_key = "wf_supplieraudit"
|
||||
|
||||
@staticmethod
|
||||
def add_supplier(ticket: Ticket, transition, new_ticket_data: dict):
|
||||
supplieraudit = SupplierAudit.objects.get(ticket=ticket)
|
||||
if Supplier.objects.filter(name=supplieraudit.name).exists():
|
||||
raise ParseError('供应商名称已存在')
|
||||
Supplier.objects.create(name=supplieraudit.name)
|
||||
|
||||
class PuPlanViewSet(CustomModelViewSet):
|
||||
"""
|
||||
|
|
@ -191,3 +210,16 @@ class PuOrderItemViewSet(CustomModelViewSet):
|
|||
item.pu_order = puorder
|
||||
item.save()
|
||||
return Response()
|
||||
|
||||
class QuotationApplyViewSet(TicketMixin, CustomModelViewSet):
|
||||
"""
|
||||
list: 报价申请
|
||||
|
||||
报价申请
|
||||
"""
|
||||
queryset = QuotationApply.objects.all()
|
||||
serializer_class = QuotationApplySerializer
|
||||
filterset_fields = ['product_name', 'customer_name','apply_date', 'quoter']
|
||||
search_fields = ['product_name', 'customer_name','contact_person']
|
||||
ordering = ['create_time']
|
||||
workflow_key = "wf_quotation"
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 3.2.12 on 2025-11-26 01:53
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('qm', '0054_alter_ptest_val_xj'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ftestitem',
|
||||
name='ftest',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items_ftest', to='qm.ftest', verbose_name='关联检验'),
|
||||
),
|
||||
]
|
||||
|
|
@ -385,7 +385,7 @@ class FtestItem(BaseModel):
|
|||
TN:检测明细
|
||||
"""
|
||||
ftest = models.ForeignKey(
|
||||
Ftest, verbose_name='关联检验', on_delete=models.CASCADE)
|
||||
Ftest, verbose_name='关联检验', on_delete=models.CASCADE, related_name='items_ftest')
|
||||
testitem = models.ForeignKey(
|
||||
TestItem, verbose_name='质检项目', on_delete=models.CASCADE)
|
||||
test_equip = models.ForeignKey(Equipment, verbose_name='检测设备', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
|
|
|
|||
|
|
@ -347,8 +347,8 @@ class FtestItemProcessSerializer(CustomModelSerializer):
|
|||
class FtestProcessSerializer(CustomModelSerializer):
|
||||
test_user_name = serializers.CharField(
|
||||
source='test_user.name', read_only=True)
|
||||
ftestitems = FtestItemProcessSerializer(label='检验明细', many=True)
|
||||
ftestdefects = FtestDefectSerializer(label='缺陷明细', many=True)
|
||||
ftestitems = FtestItemProcessSerializer(many=True)
|
||||
ftestdefects = FtestDefectSerializer(many=True)
|
||||
|
||||
class Meta:
|
||||
model = Ftest
|
||||
|
|
@ -430,4 +430,8 @@ class FtestProcessSerializer(CustomModelSerializer):
|
|||
instance.defect_main = defect_main
|
||||
instance.is_ok = is_ok
|
||||
instance.save()
|
||||
return instance
|
||||
return instance
|
||||
|
||||
class FtestProcessListSerializer(FtestProcessSerializer):
|
||||
ftestitems = FtestItemProcessSerializer(source='items_ftest', many=True)
|
||||
ftestdefects = FtestDefectSerializer(source='defects_ftest', many=True)
|
||||
|
|
@ -157,6 +157,43 @@ def ftestwork_submit(ins:FtestWork, user: User):
|
|||
# 触发批次统计分析
|
||||
ana_batch_thread(xbatchs=[ins.batch])
|
||||
|
||||
|
||||
def ftestwork_revert(ins: FtestWork):
|
||||
wm:WMaterial = ins.wm
|
||||
if wm and ins.need_update_wm:
|
||||
fwd_qs = FtestworkDefect.objects.filter(ftestwork=ins)
|
||||
for item in fwd_qs:
|
||||
item:FtestworkDefect = item
|
||||
if item.count > 0:
|
||||
wm.count = wm.count + item.count
|
||||
wm.save()
|
||||
wmstate = WMaterial.WM_OK
|
||||
if item.defect.okcate == Defect.DEFECT_NOTOK:
|
||||
wmstate = WMaterial.WM_NOTOK
|
||||
try:
|
||||
wmx = WMaterial.objects.get(
|
||||
material=wm.material,
|
||||
batch=wm.batch,
|
||||
mgroup=wm.mgroup,
|
||||
belong_dept=wm.belong_dept,
|
||||
state=wmstate,
|
||||
notok_sign=None,
|
||||
defect=item.defect,
|
||||
)
|
||||
except Exception as e:
|
||||
raise ParseError(f'找不到{item.defect.name}的车间库存: {str(e)}')
|
||||
wmx.count = wmx.count - item.count
|
||||
if wmx.count < 0:
|
||||
raise ParseError("数量不足,撤回失败")
|
||||
wmx.save()
|
||||
else:
|
||||
raise ParseError("该检验工作不支持撤回")
|
||||
ins.submit_user = None
|
||||
ins.submit_time = None
|
||||
ins.save()
|
||||
# 触发批次统计分析
|
||||
ana_batch_thread(xbatchs=[ins.batch])
|
||||
|
||||
def bind_ftestwork(ticket: Ticket, transition, new_ticket_data: dict):
|
||||
ins = FtestWork.objects.get(id=new_ticket_data['t_id'])
|
||||
if ins.submit_time is not None:
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
|||
from apps.wpm.models import SfLog
|
||||
from apps.qm.filters import QuaStatFilter, TestItemFilter, FtestWorkFilter, QctFilter, FtestFilter
|
||||
from django.db import transaction
|
||||
from apps.qm.services import ftestwork_submit
|
||||
from apps.qm.services import ftestwork_submit, ftestwork_revert
|
||||
from apps.wpm.services_2 import ana_batch_thread
|
||||
from apps.wf.models import State
|
||||
# Create your views here.
|
||||
|
|
@ -327,4 +327,20 @@ class FtestWorkViewSet(CustomModelViewSet):
|
|||
ftestwork_submit(ins, request.user)
|
||||
else:
|
||||
raise ParseError('该检验工作已提交')
|
||||
return Response()
|
||||
|
||||
@action(methods=['post'], detail=True, perms_map={'post': 'ftestwork.submit'}, serializer_class=Serializer)
|
||||
@transaction.atomic
|
||||
def revert(self, request, *args, **kwargs):
|
||||
"""撤回检验工作
|
||||
|
||||
撤回检验工作
|
||||
"""
|
||||
ins:FtestWork = self.get_object()
|
||||
if ins.submit_time:
|
||||
if self.request.user != ins.submit_user:
|
||||
raise ParseError('只能由提交人撤回')
|
||||
ftestwork_revert(ins)
|
||||
else:
|
||||
raise ParseError('该检验工作未提交')
|
||||
return Response()
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class RemConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'apps.rem'
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# Generated by Django 3.2.12 on 2026-01-06 01:49
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('system', '0006_auto_20241213_1249'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Project',
|
||||
fields=[
|
||||
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('name', models.TextField(verbose_name='项目名称')),
|
||||
('description', models.TextField(verbose_name='项目介绍')),
|
||||
('start_date', models.DateField(verbose_name='开始日期')),
|
||||
('end_date', models.DateField(verbose_name='结束日期')),
|
||||
('participants', models.TextField(blank=True, null=True, verbose_name='项目成员')),
|
||||
('note', models.TextField(blank=True, null=True, verbose_name='备注')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='project_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('files', models.ManyToManyField(blank=True, to='system.File', verbose_name='附件')),
|
||||
('leader', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='项目负责人')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='project_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
from django.db import models
|
||||
from apps.utils.models import CommonADModel
|
||||
from apps.system.models import User, File
|
||||
# Create your models here.
|
||||
|
||||
class Project(CommonADModel):
|
||||
name = models.TextField("项目名称")
|
||||
description = models.TextField("项目介绍")
|
||||
start_date = models.DateField("开始日期")
|
||||
end_date = models.DateField("结束日期")
|
||||
leader = models.ForeignKey(User, verbose_name="项目负责人", on_delete=models.CASCADE)
|
||||
participants = models.TextField("项目成员", blank=True, null=True)
|
||||
files = models.ManyToManyField(File, verbose_name="附件", blank=True)
|
||||
note = models.TextField("备注", blank=True, null=True)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue