From a81e851588cb22a4bc5bebee9ed08f8c2cd40117 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 19 Dec 2025 14:02:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20asm=E5=88=9D=E6=AD=A5=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/asm/migrations/0001_initial.py | 86 +++++++++++++++++++++++++++++ apps/asm/models.py | 60 +++++++++++--------- apps/asm/serializers.py | 17 ++++-- apps/asm/urls.py | 5 +- apps/asm/views.py | 45 ++++++++------- 5 files changed, 161 insertions(+), 52 deletions(-) create mode 100644 apps/asm/migrations/0001_initial.py diff --git a/apps/asm/migrations/0001_initial.py b/apps/asm/migrations/0001_initial.py new file mode 100644 index 00000000..05f5d6f5 --- /dev/null +++ b/apps/asm/migrations/0001_initial.py @@ -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, + }, + ), + ] diff --git a/apps/asm/models.py b/apps/asm/models.py index d576f442..7073df42 100644 --- a/apps/asm/models.py +++ b/apps/asm/models.py @@ -1,30 +1,38 @@ -from apps.utils.models import CommonADModel, CommonBDModel +from apps.utils.models import CommonADModel, CommonBDModel, CommonAModel from django.db import models -class AssetAudit(CommonBDModel): - asset_data = models.JSONField(verbose_name='资产信息', blank=True, null=True) - ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单', - on_delete=models.SET_NULL, related_name='asset_ticket', null=True, blank=True, db_constraint=False) - - def __str__(self): - return f"{self.code} - {self.name}" +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(CommonADModel): - code = models.CharField('固定资产编号',max_length=50, unique=True) +class Asset(CommonAModel): + """ + TN:固定资产台账 + """ + card_number = models.CharField('卡片编号',max_length=50, unique=True, blank=True, null=True) name = models.CharField('固定资产名称',max_length=100) - model = models.CharField("资产型号", max_length=100, blank=True, null=True) - quantity = models.IntegerField("数量", default=1) - price = models.DecimalField("资产原值/单价(元)", max_digits=12, decimal_places=2) - net_value = models.DecimalField("净值", max_digits=12, decimal_places=2) - asm_user = models.CharField("使用人", max_length=50, blank=True, null=True) - keeper = models.CharField("使用保管人", max_length=100, blank=True, null=True) - useful_life = models.IntegerField("使用年限(年)", blank=True, null=True) - location = models.CharField("存放地点", max_length=100, blank=True, null=True) - department = models.CharField("存放部门", max_length=100, blank=True, null=True) - vendor = models.CharField("客商", max_length=100, blank=True, null=True) - category = models.CharField("固定资产类别", max_length=100, blank=True, null=True) - start_date = models.DateField("启用日期", blank=True, null=True) - expire_date = models.DateField("到期日期", blank=True, null=True) - unit = models.CharField("计量单位", max_length=50, blank=True, null=True) - remark = models.TextField("备注", blank=True, null=True) \ No newline at end of file + 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) + 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) \ No newline at end of file diff --git a/apps/asm/serializers.py b/apps/asm/serializers.py index bae7d358..30eebe8e 100644 --- a/apps/asm/serializers.py +++ b/apps/asm/serializers.py @@ -1,16 +1,23 @@ -from apps.asm.models import Asset, AssetAudit +from apps.asm.models import Asset, AssetLog, AssetCate from apps.utils.serializers import CustomModelSerializer -from apps.utils.constants import EXCLUDE_FIELDS_DEPT +from apps.utils.constants import EXCLUDE_FIELDS_DEPT, EXCLUDE_FIELDS + + +class AssetCateSerializer(CustomModelSerializer): + class Meta: + model = AssetCate + fields = '__all__' + read_only_fields = EXCLUDE_FIELDS class AssetSerializer(CustomModelSerializer): class Meta: model = Asset fields = '__all__' - read_only_fields = EXCLUDE_FIELDS_DEPT + read_only_fields = EXCLUDE_FIELDS -class AssetlogSerializer(CustomModelSerializer): +class AssetLogSerializer(CustomModelSerializer): class Meta: - model = AssetAudit + model = AssetLog fields = '__all__' read_only_fields = EXCLUDE_FIELDS_DEPT \ No newline at end of file diff --git a/apps/asm/urls.py b/apps/asm/urls.py index 00f732ed..e35b8f61 100644 --- a/apps/asm/urls.py +++ b/apps/asm/urls.py @@ -1,12 +1,13 @@ from django.urls import path, include from rest_framework.routers import DefaultRouter -from apps.asm.views import AssetViewSet, AssetAuditViewSet +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', AssetAuditViewSet, basename='assetlog') +router.register('assetlog', AssetLogViewSet, basename='assetlog') urlpatterns = [ path(API_BASE_URL, include(router.urls)), ] \ No newline at end of file diff --git a/apps/asm/views.py b/apps/asm/views.py index 91a6ed7e..366c9ec7 100644 --- a/apps/asm/views.py +++ b/apps/asm/views.py @@ -1,27 +1,20 @@ from apps.wf.mixins import TicketMixin from apps.utils.viewsets import CustomModelViewSet -from apps.asm.models import Asset +from apps.asm.models import Asset, AssetLog, AssetCate from rest_framework.exceptions import ParseError -from apps.asm.serializers import AssetSerializer, AssetlogSerializer +from apps.asm.serializers import AssetSerializer, AssetLogSerializer, AssetCateSerializer from apps.wf.models import Ticket -class AssetAuditViewSet(TicketMixin, CustomModelViewSet): + +class AssetCateViewSet(CustomModelViewSet): """ - list: 固定资产审核 + list: 固定资产分类 - 固定资产审核 + 固定资产分类 """ - queryset = Asset.objects.all() - serializer_class = AssetSerializer - search_fields = ['assetlog'] - workflow_key = 'wf_asset' - - @staticmethod - def add_asset(ticket:Ticket, transition, new_ticket_data: dict): - asset = Asset.objects.get(ticket=ticket) - if AssetLog.objects.filter(code=asset.code).exists(): - raise ParseError('资产名称已存在') - AssetLog.objects.create(**new_ticket_data) + queryset = AssetCate.objects.all() + serializer_class = AssetCateSerializer + search_fields = ['name', 'code'] class AssetViewSet(CustomModelViewSet): @@ -31,6 +24,20 @@ class AssetViewSet(CustomModelViewSet): 固定资产台账 """ queryset = Asset.objects.all() - serializer_class = AssetlogSerializer - search_fields = ['name', 'code', 'category'] - filterset_fields = ['category'] + 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 + search_fields = ['assetlog'] + workflow_key = 'wf_assetlog' + +