增加预算表的显示功能
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,7 +3,7 @@ from django.contrib.admin.actions import delete_selected
|
||||
from django.urls import reverse
|
||||
from django.utils.html import format_html
|
||||
from .models import Branch, EquipmentImage, Drawing, PublicScreen
|
||||
from .models import Activity, Branch, Event, Contact, VideoTerminal
|
||||
from .models import Activity, Branch, Event, Contact, VideoTerminal, Budget, EquipmentBudget, InfrastructureBudget, BudgetTemplate, TemplateEquipmentItem, TemplateInfrastructureItem
|
||||
from django_select2.forms import Select2Widget
|
||||
from unfold.admin import ModelAdmin
|
||||
from django.contrib.admin import AdminSite
|
||||
@@ -192,3 +192,96 @@ class VideoTerminalAdmin(ModelAdmin):
|
||||
list_filter = ('terminal_type', 'branch')
|
||||
search_fields = ('branch__name', 'description')
|
||||
autocomplete_fields = ['branch']
|
||||
|
||||
|
||||
# 预算相关Admin配置
|
||||
|
||||
# 设备预算内联
|
||||
class EquipmentBudgetInline(admin.TabularInline):
|
||||
model = EquipmentBudget
|
||||
extra = 1
|
||||
fields = ('project', 'model', 'unit_price', 'procurement_method', 'quantity', 'subtotal')
|
||||
readonly_fields = ('subtotal',) # 小计自动计算,设为只读
|
||||
|
||||
# 基础设施预算内联
|
||||
class InfrastructureBudgetInline(admin.TabularInline):
|
||||
model = InfrastructureBudget
|
||||
extra = 1
|
||||
fields = ('name', 'remarks', 'unit_price', 'unit', 'quantity', 'subtotal', 'description')
|
||||
readonly_fields = ('subtotal',) # 小计自动计算,设为只读
|
||||
|
||||
# 预算Admin
|
||||
@admin.register(Budget)
|
||||
class BudgetAdmin(ModelAdmin):
|
||||
list_display = ('branch', 'name', 'total_budget', 'created_at')
|
||||
list_filter = ('branch',)
|
||||
search_fields = ('branch__name', 'name')
|
||||
autocomplete_fields = ['branch']
|
||||
readonly_fields = ('total_budget',) # 总预算自动计算,设为只读
|
||||
inlines = [EquipmentBudgetInline, InfrastructureBudgetInline]
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
super().save_model(request, obj, form, change)
|
||||
# 保存后重新计算总预算
|
||||
obj.update_total_budget()
|
||||
|
||||
# 设备预算Admin(如果需要单独管理)
|
||||
@admin.register(EquipmentBudget)
|
||||
class EquipmentBudgetAdmin(ModelAdmin):
|
||||
list_display = ('budget', 'project', 'model', 'unit_price', 'procurement_method', 'quantity', 'subtotal')
|
||||
list_filter = ('budget__branch', 'project', 'procurement_method')
|
||||
search_fields = ('budget__branch__name', 'project', 'model')
|
||||
autocomplete_fields = ['budget']
|
||||
readonly_fields = ('subtotal',)
|
||||
|
||||
# 基础设施预算Admin(如果需要单独管理)
|
||||
@admin.register(InfrastructureBudget)
|
||||
class InfrastructureBudgetAdmin(ModelAdmin):
|
||||
list_display = ('budget', 'name', 'unit_price', 'unit', 'quantity', 'subtotal')
|
||||
list_filter = ('budget__branch', 'name')
|
||||
search_fields = ('budget__branch__name', 'name', 'description')
|
||||
autocomplete_fields = ['budget']
|
||||
readonly_fields = ('subtotal',)
|
||||
|
||||
|
||||
# 预算模板相关Admin配置
|
||||
|
||||
# 模板设备项内联
|
||||
class TemplateEquipmentItemInline(admin.TabularInline):
|
||||
model = TemplateEquipmentItem
|
||||
extra = 1
|
||||
|
||||
# 模板基础设施项内联
|
||||
class TemplateInfrastructureItemInline(admin.TabularInline):
|
||||
model = TemplateInfrastructureItem
|
||||
extra = 1
|
||||
|
||||
# 模板设备项Admin(如果需要单独管理)
|
||||
@admin.register(TemplateEquipmentItem)
|
||||
class TemplateEquipmentItemAdmin(ModelAdmin):
|
||||
list_display = ('template', 'project', 'model', 'unit_price', 'procurement_method')
|
||||
list_filter = ('template', 'project', 'procurement_method')
|
||||
search_fields = ('template__name', 'project', 'model')
|
||||
autocomplete_fields = ['template']
|
||||
|
||||
# 模板基础设施项Admin(如果需要单独管理)
|
||||
@admin.register(TemplateInfrastructureItem)
|
||||
class TemplateInfrastructureItemAdmin(ModelAdmin):
|
||||
list_display = ('template', 'name', 'unit_price', 'unit')
|
||||
list_filter = ('template', 'unit')
|
||||
search_fields = ('template__name', 'name', 'description')
|
||||
autocomplete_fields = ['template']
|
||||
|
||||
# 预算模板Admin
|
||||
@admin.register(BudgetTemplate)
|
||||
class BudgetTemplateAdmin(ModelAdmin):
|
||||
list_display = ('name', 'description', 'is_default', 'created_at')
|
||||
list_filter = ('is_default',)
|
||||
search_fields = ('name', 'description')
|
||||
inlines = [TemplateEquipmentItemInline, TemplateInfrastructureItemInline]
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
# 如果设置为默认模板,将其他模板的默认状态取消
|
||||
if obj.is_default:
|
||||
BudgetTemplate.objects.exclude(id=obj.id).update(is_default=False)
|
||||
super().save_model(request, obj, form, change)
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# Generated by Django 5.0.6 on 2025-12-03 07:44
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('huodong', '0029_alter_activity_end_time_alter_activity_start_time_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Budget',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('total_budget', models.DecimalField(decimal_places=2, default=0, max_digits=15, verbose_name='总预算')),
|
||||
('activity', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='huodong.activity', verbose_name='活动')),
|
||||
('branch', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='huodong.branch', verbose_name='分支机构')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '预算主表',
|
||||
'verbose_name_plural': '预算主表',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EquipmentBudget',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('project', models.CharField(max_length=255, verbose_name='项目')),
|
||||
('model', models.CharField(max_length=255, verbose_name='型号')),
|
||||
('unit_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='单价')),
|
||||
('procurement_method', models.CharField(choices=[('本地询价采购', '本地询价采购'), ('订单采购', '订单采购'), ('按照总部配置要求本地询价采购', '按照总部配置要求本地询价采购'), ('本地询价采购或订单采购', '本地询价采购或订单采购')], max_length=50, verbose_name='采购方式')),
|
||||
('quantity', models.IntegerField(default=1, verbose_name='数量')),
|
||||
('subtotal', models.DecimalField(decimal_places=2, default=0, max_digits=12, verbose_name='小计')),
|
||||
('budget', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='equipment_budgets', to='huodong.budget', verbose_name='预算主表')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '设备预算明细',
|
||||
'verbose_name_plural': '设备预算明细',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='InfrastructureBudget',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='名称')),
|
||||
('remarks', models.TextField(blank=True, verbose_name='备注')),
|
||||
('unit_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='单价')),
|
||||
('unit', models.CharField(max_length=20, verbose_name='单位')),
|
||||
('description', models.TextField(blank=True, verbose_name='说明')),
|
||||
('quantity', models.IntegerField(default=1, verbose_name='数量')),
|
||||
('subtotal', models.DecimalField(decimal_places=2, default=0, max_digits=12, verbose_name='小计')),
|
||||
('budget', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='infrastructure_budgets', to='huodong.budget', verbose_name='预算主表')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '基础设施预算明细',
|
||||
'verbose_name_plural': '基础设施预算明细',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,60 @@
|
||||
# Generated by Django 5.0.6 on 2025-12-03 08:16
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('huodong', '0030_budget_equipmentbudget_infrastructurebudget'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BudgetTemplate',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='模板名称')),
|
||||
('description', models.TextField(blank=True, verbose_name='模板描述')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('is_default', models.BooleanField(default=False, verbose_name='是否默认模板')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '预算模板',
|
||||
'verbose_name_plural': '预算模板',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TemplateEquipmentItem',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('project', models.CharField(max_length=255, verbose_name='项目')),
|
||||
('model', models.CharField(max_length=255, verbose_name='型号')),
|
||||
('unit_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='参考单价')),
|
||||
('procurement_method', models.CharField(choices=[('本地询价采购', '本地询价采购'), ('订单采购', '订单采购'), ('按照总部配置要求本地询价采购', '按照总部配置要求本地询价采购'), ('本地询价采购或订单采购', '本地询价采购或订单采购')], max_length=50, verbose_name='采购方式')),
|
||||
('template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='equipment_items', to='huodong.budgettemplate', verbose_name='模板')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '模板设备项',
|
||||
'verbose_name_plural': '模板设备项',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TemplateInfrastructureItem',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='名称')),
|
||||
('remarks', models.TextField(blank=True, verbose_name='备注')),
|
||||
('unit_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='参考单价')),
|
||||
('unit', models.CharField(max_length=20, verbose_name='单位')),
|
||||
('description', models.TextField(blank=True, verbose_name='说明')),
|
||||
('template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='infrastructure_items', to='huodong.budgettemplate', verbose_name='模板')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '模板基础设施项',
|
||||
'verbose_name_plural': '模板基础设施项',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 5.0.6 on 2025-12-03 09:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('huodong', '0031_budgettemplate_templateequipmentitem_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='budget',
|
||||
name='activity',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='budget',
|
||||
name='name',
|
||||
field=models.CharField(default=1, max_length=255, verbose_name='预算名称'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -180,3 +180,148 @@ class Evaluation(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.activity.name} - {self.branch.name}"
|
||||
|
||||
|
||||
# 预算相关模型
|
||||
class Budget(models.Model):
|
||||
"""预算主表"""
|
||||
branch = models.ForeignKey(Branch, on_delete=models.CASCADE, verbose_name='分支机构')
|
||||
name = models.CharField(max_length=255, verbose_name='预算名称')
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
total_budget = models.DecimalField(max_digits=15, decimal_places=2, default=0, verbose_name='总预算')
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.branch.name} - {self.name} 预算"
|
||||
|
||||
class Meta:
|
||||
verbose_name = '预算主表'
|
||||
verbose_name_plural = '预算主表'
|
||||
|
||||
|
||||
class EquipmentBudget(models.Model):
|
||||
"""设备预算明细"""
|
||||
BUDGET_TYPES = (
|
||||
('本地询价采购', '本地询价采购'),
|
||||
('订单采购', '订单采购'),
|
||||
('按照总部配置要求本地询价采购', '按照总部配置要求本地询价采购'),
|
||||
('本地询价采购或订单采购', '本地询价采购或订单采购')
|
||||
)
|
||||
budget = models.ForeignKey(Budget, related_name='equipment_budgets', on_delete=models.CASCADE, verbose_name='预算主表')
|
||||
project = models.CharField(max_length=255, verbose_name='项目')
|
||||
model = models.CharField(max_length=255, verbose_name='型号')
|
||||
unit_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='单价')
|
||||
procurement_method = models.CharField(max_length=50, choices=BUDGET_TYPES, verbose_name='采购方式')
|
||||
quantity = models.IntegerField(default=1, verbose_name='数量')
|
||||
subtotal = models.DecimalField(max_digits=12, decimal_places=2, default=0, verbose_name='小计')
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.subtotal = self.unit_price * self.quantity
|
||||
super().save(*args, **kwargs)
|
||||
# 更新预算主表的总预算
|
||||
self.update_total_budget()
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
super().delete(*args, **kwargs)
|
||||
# 更新预算主表的总预算
|
||||
self.update_total_budget()
|
||||
|
||||
def update_total_budget(self):
|
||||
"""更新预算主表的总预算"""
|
||||
total = self.budget.equipment_budgets.aggregate(total=models.Sum('subtotal'))['total'] or 0
|
||||
total += self.budget.infrastructure_budgets.aggregate(total=models.Sum('subtotal'))['total'] or 0
|
||||
self.budget.total_budget = total
|
||||
self.budget.save()
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.project} - {self.model}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = '设备预算明细'
|
||||
verbose_name_plural = '设备预算明细'
|
||||
|
||||
|
||||
class InfrastructureBudget(models.Model):
|
||||
"""基础设施预算明细"""
|
||||
budget = models.ForeignKey(Budget, related_name='infrastructure_budgets', on_delete=models.CASCADE, verbose_name='预算主表')
|
||||
name = models.CharField(max_length=255, verbose_name='名称')
|
||||
remarks = models.TextField(blank=True, verbose_name='备注')
|
||||
unit_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='单价')
|
||||
unit = models.CharField(max_length=20, verbose_name='单位')
|
||||
description = models.TextField(blank=True, verbose_name='说明')
|
||||
quantity = models.IntegerField(default=1, verbose_name='数量')
|
||||
subtotal = models.DecimalField(max_digits=12, decimal_places=2, default=0, verbose_name='小计')
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.subtotal = self.unit_price * self.quantity
|
||||
super().save(*args, **kwargs)
|
||||
# 更新预算主表的总预算
|
||||
self.update_total_budget()
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
super().delete(*args, **kwargs)
|
||||
# 更新预算主表的总预算
|
||||
self.update_total_budget()
|
||||
|
||||
def update_total_budget(self):
|
||||
"""更新预算主表的总预算"""
|
||||
total = self.budget.equipment_budgets.aggregate(total=models.Sum('subtotal'))['total'] or 0
|
||||
total += self.budget.infrastructure_budgets.aggregate(total=models.Sum('subtotal'))['total'] or 0
|
||||
self.budget.total_budget = total
|
||||
self.budget.save()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = '基础设施预算明细'
|
||||
verbose_name_plural = '基础设施预算明细'
|
||||
|
||||
|
||||
class BudgetTemplate(models.Model):
|
||||
"""预算模板"""
|
||||
name = models.CharField(max_length=255, verbose_name='模板名称')
|
||||
description = models.TextField(blank=True, verbose_name='模板描述')
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
is_default = models.BooleanField(default=False, verbose_name='是否默认模板')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = '预算模板'
|
||||
verbose_name_plural = '预算模板'
|
||||
|
||||
|
||||
class TemplateEquipmentItem(models.Model):
|
||||
"""模板设备项"""
|
||||
template = models.ForeignKey(BudgetTemplate, related_name='equipment_items', on_delete=models.CASCADE, verbose_name='模板')
|
||||
project = models.CharField(max_length=255, verbose_name='项目')
|
||||
model = models.CharField(max_length=255, verbose_name='型号')
|
||||
unit_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='参考单价')
|
||||
procurement_method = models.CharField(max_length=50, choices=EquipmentBudget.BUDGET_TYPES, verbose_name='采购方式')
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.project} - {self.model}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = '模板设备项'
|
||||
verbose_name_plural = '模板设备项'
|
||||
|
||||
|
||||
class TemplateInfrastructureItem(models.Model):
|
||||
"""模板基础设施项"""
|
||||
template = models.ForeignKey(BudgetTemplate, related_name='infrastructure_items', on_delete=models.CASCADE, verbose_name='模板')
|
||||
name = models.CharField(max_length=255, verbose_name='名称')
|
||||
remarks = models.TextField(blank=True, verbose_name='备注')
|
||||
unit_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='参考单价')
|
||||
unit = models.CharField(max_length=20, verbose_name='单位')
|
||||
description = models.TextField(blank=True, verbose_name='说明')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = '模板基础设施项'
|
||||
verbose_name_plural = '模板基础设施项'
|
||||
|
||||
@@ -11,6 +11,125 @@
|
||||
<span class="flex-grow block border-t border-black" aria-hidden="true" role="presentation"></span>
|
||||
</h2>
|
||||
<!-- Component ends here -->
|
||||
|
||||
<!-- 投入预算表 -->
|
||||
<div class="bg-white rounded-lg shadow-md p-4 mb-6">
|
||||
<h2 class="text-2xl font-bold mb-4">投入预算表</h2>
|
||||
<p class="text-gray-700 mb-4">分支机构项目的预算表,包括设备和基础设施明细</p>
|
||||
|
||||
<!-- 预算模板导入功能 -->
|
||||
{% if budget_templates %} <!-- 只有当有模板时才显示导入功能 -->
|
||||
<div class="mb-6 p-4 bg-blue-50 rounded-lg">
|
||||
<h3 class="text-lg font-semibold mb-2">预算模板导入</h3>
|
||||
<p class="text-sm text-gray-600 mb-3">从模板一键导入预算,快速生成预算表</p>
|
||||
|
||||
<form method="POST" action="{% url 'import-budget-template' branch.id %}">
|
||||
{% csrf_token %}
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label for="template" class="block text-sm font-medium text-gray-700 mb-1">选择模板</label>
|
||||
<select id="template" name="template" class="w-full p-2 border border-gray-300 rounded-md">
|
||||
{% for template in budget_templates %}
|
||||
<option value="{{ template.id }}">{{ template.name }}{% if template.is_default %} (默认){% endif %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="budget_name" class="block text-sm font-medium text-gray-700 mb-1">预算名称</label>
|
||||
<input type="text" id="budget_name" name="budget_name" placeholder="请输入预算名称" class="w-full p-2 border border-gray-300 rounded-md">
|
||||
</div>
|
||||
<div class="flex items-end">
|
||||
<button type="submit" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-md">
|
||||
一键导入
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if budgets %}
|
||||
{% for budget in budgets %}
|
||||
<div class="mb-6">
|
||||
<h3 class="text-lg font-semibold mb-3">{{ budget.activity.name }} - 总预算: ¥{{ budget.total_budget }}</h3>
|
||||
|
||||
<!-- 设备预算部分 -->
|
||||
<div class="mb-4">
|
||||
<h4 class="text-md font-semibold mb-2">设备预算明细</h4>
|
||||
{% if budget.equipment_budgets.all %}
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">项目</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">型号</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">单价</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">采购方式</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">数量</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">小计</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
{% for equipment in budget.equipment_budgets.all %}
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{{ equipment.project }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{{ equipment.model }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">¥{{ equipment.unit_price }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{{ equipment.get_procurement_method_display }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{{ equipment.quantity }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">¥{{ equipment.subtotal }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-gray-500 italic">暂无设备预算明细</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- 基础设施预算部分 -->
|
||||
<div>
|
||||
<h4 class="text-md font-semibold mb-2">基础设施预算明细</h4>
|
||||
{% if budget.infrastructure_budgets.all %}
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">名称</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">备注</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">单价</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">单位</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">数量</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">小计</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
{% for infrastructure in budget.infrastructure_budgets.all %}
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{{ infrastructure.name }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{{ infrastructure.remarks }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">¥{{ infrastructure.unit_price }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{{ infrastructure.unit }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{{ infrastructure.quantity }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">¥{{ infrastructure.subtotal }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{{ infrastructure.description }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-gray-500 italic">暂无基础设施预算明细</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p class="text-gray-500 italic">暂无预算信息</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="bg-white rounded-lg shadow-md p-4 mb-6">
|
||||
<h2 class="text-2xl font-bold mb-4">基本信息</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
|
||||
@@ -11,6 +11,7 @@ urlpatterns = [
|
||||
path('api/', include(router.urls)),
|
||||
path('', views.BranchAll, name='branch-all'),
|
||||
path('branch/<int:branch_id>/', views.branch_detail, name='branch-detail'),
|
||||
path('branch/<int:branch_id>/import-budget/', views.import_budget_template, name='import-budget-template'),
|
||||
path('branch/info/', views.Branchinfo, name='branchinfo'),
|
||||
path('statistics/', views.Statistics, name='statistics'),
|
||||
path('contact/', views.contact_list, name='contact-list'),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from rest_framework import viewsets
|
||||
from .models import Branch, Activity, Evaluation, Event, VideoTerminal
|
||||
from .models import Branch, Activity, Evaluation, Event, VideoTerminal, Budget, EquipmentBudget, InfrastructureBudget, BudgetTemplate, TemplateEquipmentItem, TemplateInfrastructureItem
|
||||
from .serializers import BranchSerializer, ActivitySerializer, EvaluationSerializer
|
||||
from django.shortcuts import render, redirect
|
||||
from .models import PublicScreen
|
||||
@@ -55,6 +55,12 @@ def branch_detail(request, branch_id):
|
||||
equipment_images = branch.equipment_images.all()
|
||||
public_screens = branch.public_screens.all()
|
||||
|
||||
# 获取预算数据
|
||||
budgets = Budget.objects.filter(branch=branch).select_related('activity').prefetch_related('equipment_budgets', 'infrastructure_budgets').order_by('-created_at')
|
||||
|
||||
# 获取预算模板
|
||||
budget_templates = BudgetTemplate.objects.all()
|
||||
|
||||
# 准备上下文数据
|
||||
context = {
|
||||
'branch': branch,
|
||||
@@ -62,10 +68,62 @@ def branch_detail(request, branch_id):
|
||||
'events': events,
|
||||
'equipment_images': equipment_images,
|
||||
'public_screens': public_screens,
|
||||
'budgets': budgets,
|
||||
'activities': activities,
|
||||
'budget_templates': budget_templates,
|
||||
}
|
||||
return render(request, 'branch_detail.html', context)
|
||||
|
||||
|
||||
def import_budget_template(request, branch_id):
|
||||
"""从模板导入预算"""
|
||||
if request.method == 'POST':
|
||||
template_id = request.POST.get('template')
|
||||
budget_name = request.POST.get('budget_name', '导入预算')
|
||||
|
||||
if template_id:
|
||||
try:
|
||||
# 获取模板和分支机构
|
||||
template = BudgetTemplate.objects.get(pk=template_id)
|
||||
branch = Branch.objects.get(pk=branch_id)
|
||||
|
||||
# 创建预算
|
||||
budget = Budget.objects.create(
|
||||
branch=branch,
|
||||
name=budget_name
|
||||
)
|
||||
|
||||
# 导入设备预算项
|
||||
for equipment_item in template.equipment_items.all():
|
||||
EquipmentBudget.objects.create(
|
||||
budget=budget,
|
||||
project=equipment_item.project,
|
||||
model=equipment_item.model,
|
||||
unit_price=equipment_item.unit_price,
|
||||
procurement_method=equipment_item.procurement_method,
|
||||
quantity=1 # 默认数量为1
|
||||
)
|
||||
|
||||
# 导入基础设施预算项
|
||||
for infrastructure_item in template.infrastructure_items.all():
|
||||
InfrastructureBudget.objects.create(
|
||||
budget=budget,
|
||||
name=infrastructure_item.name,
|
||||
remarks=infrastructure_item.remarks,
|
||||
unit_price=infrastructure_item.unit_price,
|
||||
unit=infrastructure_item.unit,
|
||||
description=infrastructure_item.description,
|
||||
quantity=1 # 默认数量为1
|
||||
)
|
||||
|
||||
# 更新总预算
|
||||
budget.update_total_budget()
|
||||
except Exception as e:
|
||||
print(f"导入预算模板失败: {e}")
|
||||
|
||||
return redirect('branch-detail', branch_id=branch_id)
|
||||
|
||||
|
||||
# 在页面上显示所有的branch以及active的数量,首页显示
|
||||
def BranchAll(request):
|
||||
branches = Branch.objects.exclude(activity__isnull=True).order_by('name')
|
||||
@@ -442,3 +500,100 @@ def video_terminal_list(request):
|
||||
'selected_type_name': selected_type_name,
|
||||
}
|
||||
return render(request, 'video_terminals.html', context)
|
||||
|
||||
|
||||
# 预算相关视图
|
||||
|
||||
def create_budget(request, branch_id):
|
||||
branch = Branch.objects.get(pk=branch_id)
|
||||
activities = Activity.objects.filter(branch=branch)
|
||||
|
||||
if request.method == 'POST':
|
||||
activity_id = request.POST.get('activity')
|
||||
if activity_id:
|
||||
activity = Activity.objects.get(pk=activity_id)
|
||||
budget = Budget.objects.create(branch=branch, activity=activity)
|
||||
return redirect('budget_detail', branch_id=branch_id, budget_id=budget.id)
|
||||
|
||||
context = {
|
||||
'branch': branch,
|
||||
'activities': activities,
|
||||
}
|
||||
return render(request, 'create_budget.html', context)
|
||||
|
||||
|
||||
def budget_detail(request, branch_id, budget_id):
|
||||
branch = Branch.objects.get(pk=branch_id)
|
||||
budget = Budget.objects.get(pk=budget_id, branch=branch)
|
||||
|
||||
context = {
|
||||
'branch': branch,
|
||||
'budget': budget,
|
||||
}
|
||||
return render(request, 'budget_detail.html', context)
|
||||
|
||||
|
||||
def add_equipment_budget(request, branch_id, budget_id):
|
||||
branch = Branch.objects.get(pk=branch_id)
|
||||
budget = Budget.objects.get(pk=budget_id, branch=branch)
|
||||
|
||||
if request.method == 'POST':
|
||||
project = request.POST.get('project')
|
||||
model = request.POST.get('model')
|
||||
unit_price = request.POST.get('unit_price')
|
||||
procurement_method = request.POST.get('procurement_method')
|
||||
quantity = request.POST.get('quantity', 1)
|
||||
|
||||
if project and model and unit_price and procurement_method:
|
||||
EquipmentBudget.objects.create(
|
||||
budget=budget,
|
||||
project=project,
|
||||
model=model,
|
||||
unit_price=unit_price,
|
||||
procurement_method=procurement_method,
|
||||
quantity=quantity
|
||||
)
|
||||
|
||||
return redirect('budget_detail', branch_id=branch_id, budget_id=budget_id)
|
||||
|
||||
|
||||
def add_infrastructure_budget(request, branch_id, budget_id):
|
||||
branch = Branch.objects.get(pk=branch_id)
|
||||
budget = Budget.objects.get(pk=budget_id, branch=branch)
|
||||
|
||||
if request.method == 'POST':
|
||||
name = request.POST.get('name')
|
||||
remarks = request.POST.get('remarks')
|
||||
unit_price = request.POST.get('unit_price')
|
||||
unit = request.POST.get('unit')
|
||||
description = request.POST.get('description')
|
||||
quantity = request.POST.get('quantity', 1)
|
||||
|
||||
if name and unit_price and unit:
|
||||
InfrastructureBudget.objects.create(
|
||||
budget=budget,
|
||||
name=name,
|
||||
remarks=remarks,
|
||||
unit_price=unit_price,
|
||||
unit=unit,
|
||||
description=description,
|
||||
quantity=quantity
|
||||
)
|
||||
|
||||
return redirect('budget_detail', branch_id=branch_id, budget_id=budget_id)
|
||||
|
||||
|
||||
def delete_equipment_budget(request, branch_id, budget_id, equipment_budget_id):
|
||||
branch = Branch.objects.get(pk=branch_id)
|
||||
budget = Budget.objects.get(pk=budget_id, branch=branch)
|
||||
equipment_budget = EquipmentBudget.objects.get(pk=equipment_budget_id, budget=budget)
|
||||
equipment_budget.delete()
|
||||
return redirect('budget_detail', branch_id=branch_id, budget_id=budget_id)
|
||||
|
||||
|
||||
def delete_infrastructure_budget(request, branch_id, budget_id, infrastructure_budget_id):
|
||||
branch = Branch.objects.get(pk=branch_id)
|
||||
budget = Budget.objects.get(pk=budget_id, branch=branch)
|
||||
infrastructure_budget = InfrastructureBudget.objects.get(pk=infrastructure_budget_id, budget=budget)
|
||||
infrastructure_budget.delete()
|
||||
return redirect('budget_detail', branch_id=branch_id, budget_id=budget_id)
|
||||
|
||||
Reference in New Issue
Block a user