feat:光芯OA 审批系统新增报价单审核

This commit is contained in:
TianyangZhang 2026-01-04 14:16:41 +08:00
parent 90cb2ed7f7
commit 7dc7a78695
7 changed files with 109 additions and 6 deletions

View File

@ -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='归还公里数'),
),
]

View File

@ -60,7 +60,7 @@ class VehicleUse(CommonBDModel):
via = 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) destination = models.CharField('到达地点', null=True, blank=True, max_length=100)
start_km = models.PositiveIntegerField('出发公里数', null=True, blank=True) start_km = models.PositiveIntegerField('出发公里数', null=True, blank=True)
end_km = models.PositiveIntegerField('归还公里数') end_km = models.PositiveIntegerField('归还公里数', default=0)
actual_km = models.PositiveIntegerField('实际行驶公里数', editable=False) actual_km = models.PositiveIntegerField('实际行驶公里数', editable=False)
is_city = models.BooleanField('是否市内用车', default=True) is_city = models.BooleanField('是否市内用车', default=True)
reason = models.CharField('用车事由', max_length=100) reason = models.CharField('用车事由', max_length=100)

View File

@ -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,
},
),
]

View File

@ -107,3 +107,22 @@ class PuPlanItem(CommonBDModel):
null=True, blank=True, related_name='item_puplan') null=True, blank=True, related_name='item_puplan')
pu_order = models.ForeignKey(PuOrder, verbose_name='关联采购订单', pu_order = models.ForeignKey(PuOrder, verbose_name='关联采购订单',
on_delete=models.SET_NULL, null=True, blank=True, related_name='puplan_item_puorder') 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)

View File

@ -3,7 +3,7 @@ from apps.utils.serializers import CustomModelSerializer
from apps.utils.constants import EXCLUDE_FIELDS_DEPT, EXCLUDE_FIELDS_BASE, EXCLUDE_FIELDS from apps.utils.constants import EXCLUDE_FIELDS_DEPT, EXCLUDE_FIELDS_BASE, EXCLUDE_FIELDS
from rest_framework.exceptions import ValidationError, ParseError from rest_framework.exceptions import ValidationError, ParseError
from apps.pum.models import Supplier, PuPlan, PuPlanItem, PuOrder, PuOrderItem, SupplierAudit from apps.pum.models import Supplier, PuPlan, PuPlanItem, PuOrder, PuOrderItem, SupplierAudit, QuotationApply
from apps.mtm.serializers import MaterialSerializer, MaterialSimpleSerializer from apps.mtm.serializers import MaterialSerializer, MaterialSimpleSerializer
from django.db import transaction from django.db import transaction
from .services import PumService from .services import PumService
@ -158,3 +158,10 @@ class SupplierAuditSerializer(CustomModelSerializer):
if Supplier.objects.filter(name=name).exists(): if Supplier.objects.filter(name=name).exists():
raise ParseError('供应商名称已存在') raise ParseError('供应商名称已存在')
return super().create(validated_data) 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

View File

@ -1,6 +1,7 @@
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from apps.pum.views import (SupplierViewSet, PuPlanViewSet, PuPlanItemViewSet, PuOrderViewSet, PuOrderItemViewSet, SupplierAuditViewSet) 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/' API_BASE_URL = 'api/pum/'
HTML_BASE_URL = 'dhtml/pum/' HTML_BASE_URL = 'dhtml/pum/'
@ -12,6 +13,7 @@ router.register('pu_plan', PuPlanViewSet, basename='pu_plan')
router.register('pu_planitem', PuPlanItemViewSet, basename='pu_planitem') router.register('pu_planitem', PuPlanItemViewSet, basename='pu_planitem')
router.register('pu_order', PuOrderViewSet, basename='pu_order') router.register('pu_order', PuOrderViewSet, basename='pu_order')
router.register('pu_orderitem', PuOrderItemViewSet, basename='pu_orderitem') router.register('pu_orderitem', PuOrderItemViewSet, basename='pu_orderitem')
router.register('quotation', QuotationApplyViewSet, basename='quotation')
urlpatterns = [ urlpatterns = [
path(API_BASE_URL, include(router.urls)), path(API_BASE_URL, include(router.urls)),
] ]

View File

@ -1,7 +1,7 @@
from django.shortcuts import render from django.shortcuts import render
from apps.pum.models import Supplier, PuPlan, PuPlanItem, PuOrder, PuOrderItem, SupplierAudit from apps.pum.models import Supplier, PuPlan, PuPlanItem, PuOrder, PuOrderItem, SupplierAudit, QuotationApply
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet, EuModelViewSet from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet, EuModelViewSet
from apps.pum.serializers import (SupplierSerializer, PuPlanSerializer, PuPlanItemSerializer, from apps.pum.serializers import (SupplierSerializer, PuPlanSerializer, PuPlanItemSerializer, QuotationApplySerializer,
PuOrderSerializer, PuOrderItemSerializer, AddSerializer, SupplierAuditSerializer) PuOrderSerializer, PuOrderItemSerializer, AddSerializer, SupplierAuditSerializer)
from rest_framework.exceptions import ParseError, PermissionDenied from rest_framework.exceptions import ParseError, PermissionDenied
from rest_framework.decorators import action from rest_framework.decorators import action
@ -210,3 +210,16 @@ class PuOrderItemViewSet(CustomModelViewSet):
item.pu_order = puorder item.pu_order = puorder
item.save() item.save()
return Response() 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"