1. 修复业绩管理bug
2. 修复组织管理bug
This commit is contained in:
彭森 2024-06-19 16:58:27 +08:00
parent c1cbd6e7d1
commit ab658c482d
8 changed files with 167 additions and 60 deletions

View File

@ -15,9 +15,24 @@ class SecondaryDepartmentSerializer(serializers.ModelSerializer):
class EntityChangeRecordSerializer(serializers.ModelSerializer):
change_date = serializers.DateField(format='%Y-%m-%d')
modified_obj = serializers.SerializerMethodField()
modified_field_verbose = serializers.SerializerMethodField()
class Meta:
model = EntityChangeRecord
fields = '__all__'
fields = ('modified_obj', 'modified_field_verbose', 'content_before_change', 'content_after_change', 'change_date', 'change_executed_by')
def get_modified_obj(self, obj):
company_entity = obj.company_entity
return f"{company_entity.company_name}"
def get_modified_field_verbose(self, obj):
# 获取模型类
model_class = obj.company_entity._meta.model
# 获取字段的 verbose_name
field = model_class._meta.get_field(obj.change_type)
return field.verbose_name
class CompanyBankAccountSerializer(serializers.ModelSerializer):

View File

@ -58,7 +58,7 @@
data-bs-target="#addEditModal">编辑</a>
<a href="#"
id="deleteBtn"
class="edit-btn"
class="delete-btn"
style="color: red"
data-id="{{ item|get_pk_value }}"
data-bs-toggle="modal"
@ -118,11 +118,11 @@
<table class="table">
<thead>
<tr>
<th class="text-center">变更主体</th>
<th class="text-center">变更日期</th>
<th class="text-center">变更类型</th>
<th class="text-center">变更前内容</th>
<th class="text-center">变更后内容</th>
<th class="text-center">变更原因</th>
<th class="text-center">变更执行人</th>
</tr>
</thead>
@ -215,7 +215,7 @@
</div>
</div>
</div>
<div id="deleteBankAModal" class="modal fade" tabindex="-1" role="dialog"
<div id="deleteBankModal" class="modal fade" tabindex="-1" role="dialog"
aria-labelledby="deleteBankModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
@ -231,7 +231,7 @@
<div class="modal-footer">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">删除</button>
<button type="button" class="btn btn-danger" id="DeleteBankBtn">删除</button>
</div>
</div>
</div>
@ -339,11 +339,11 @@
records.forEach(record => {
const row = document.createElement('tr');
row.appendChild(createCell(record.modified_obj));
row.appendChild(createCell(record.change_date));
row.appendChild(createCell(record.change_type));
row.appendChild(createCell(record.modified_field_verbose));
row.appendChild(createCell(record.content_before_change));
row.appendChild(createCell(record.content_after_change));
row.appendChild(createCell(record.change_reason));
row.appendChild(createCell(record.change_executed_by));
tableBody.appendChild(row);
@ -569,6 +569,11 @@
const entityId = document.getElementById('addBankAccountEntityId').value;
showBanksModal(entityId);
showAlert('success', '删除成功');
const deleteBankModal = bootstrap.Modal.getInstance(document.getElementById('deleteBankModal'));
if (deleteBankModal) {
deleteBankModal.hide();
}
} else {
showAlert('warning', '删除失败');
}
@ -576,7 +581,8 @@
.catch(error => console.error('Error deleting bank account:', error));
}
document.getElementById('confirmDeleteBtn').addEventListener('click', () => {
const DeleteBankBtn = document.getElementById('DeleteBankBtn');
DeleteBankBtn.addEventListener('click', () => {
deleteBankAccount(accountIdToDelete);
});

View File

@ -84,9 +84,7 @@ def eir_list_view(request):
{"type": "text", "id": "company_name", "name": "company_name", "label": "公司名称",
"placeholder": "请输入公司名称"},
{"type": "select", "id": "business_status", "name": "business_status", "label": "公司经营状态",
"options": [{"value": "存续", "display": "存续"}, {"value": "注销", "display": "注销"}]},
{"type": "text", "id": "taxpayer_identification_number", "name": "taxpayer_identification_number",
"label": "纳税人识别号", "placeholder": "请输入纳税人识别号"}
"options": [{"value": "存续", "display": "存续"}, {"value": "注销", "display": "注销"}]}
],
"table_exclude_field_name": ['entity_id'],
"excel_upload_config": {
@ -158,7 +156,7 @@ def eir_list_modify(request):
EntityChangeRecord.objects.create(
company_entity=instance,
change_date=now(),
change_type=f"修改 {field}",
change_type=field,
content_before_change=str(original_value),
content_after_change=str(updated_value),
change_reason=request.POST.get('change_reason', '未提供变更原因'),

View File

@ -39,33 +39,29 @@ class GroupBusinessTargetForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(GroupBusinessTargetForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super().clean()
primary_department = cleaned_data.get('primary_department')
year = cleaned_data.get('year')
project_nature = cleaned_data.get('project_nature')
if primary_department and year and project_nature:
if GroupBusinessTarget.objects.filter(primary_department=primary_department, year=year, project_nature=project_nature).exists():
raise forms.ValidationError("该一级部门在同一年已经有一个相同项目性质的业绩目标。")
return cleaned_data
class EmployeePerformanceTargetForm(forms.ModelForm):
current_year = datetime.datetime.now().year
department = forms.ChoiceField(
choices=[('', '---------')] + [(dept.department_name, dept.department_name) for dept in PrimaryDepartment.objects.all()],
widget=forms.Select(attrs={'class': 'form-control'}),
label="一级部门"
)
year = forms.ChoiceField(
choices=[('', '---------')] + [(year, year) for year in range(current_year-1, current_year+2)],
widget=forms.Select(attrs={'class': 'form-control'}),
label="年份",
required=False
)
class Meta:
model = EmployeePerformanceTarget
fields = '__all__'
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
'year': forms.NumberInput(attrs={'class': 'form-control'}),
'project_nature': forms.Select(attrs={'class': 'form-control'}),
'sales_target': forms.NumberInput(attrs={'class': 'form-control'}),
'total_revenue_target': forms.NumberInput(attrs={'class': 'form-control'}),
'new_revenue_target': forms.NumberInput(attrs={'class': 'form-control'}),

View File

@ -1,4 +1,4 @@
from django.db import models
from django.db import models, IntegrityError
from application.org_mgnt.models import PrimaryDepartment
@ -17,13 +17,20 @@ class GroupBusinessTarget(models.Model):
year = models.IntegerField(verbose_name='年份')
project_nature = models.CharField(max_length=255, choices=PROJECT_NATURE_CHOICES, verbose_name='项目性质')
sales = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='销售额(元)', null=True, blank=True)
total_revenue_target = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='收入总目标(元)', null=True, blank=True)
new_revenue_target = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='新增收入目标(元)', null=True, blank=True)
existing_revenue_target = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='存量收入目标(元)', null=True, blank=True)
cost_limit = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='成本限额(元)', null=True, blank=True)
gross_profit = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='毛利润(元)', null=True, blank=True)
expense_limit = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='费用限额(元)', null=True, blank=True)
operating_profit = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='营业利润(元)', null=True, blank=True)
total_revenue_target = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='收入总目标(元)',
null=True, blank=True)
new_revenue_target = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='新增收入目标(元)',
null=True, blank=True)
existing_revenue_target = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='存量收入目标(元)',
null=True, blank=True)
cost_limit = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='成本限额(元)', null=True,
blank=True)
gross_profit = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='毛利润(元)', null=True,
blank=True)
expense_limit = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='费用限额(元)', null=True,
blank=True)
operating_profit = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='营业利润(元)', null=True,
blank=True)
def save(self, *args, **kwargs):
primary_department_name = PrimaryDepartment.objects.filter(department_name=self.primary_department).first()
@ -31,13 +38,20 @@ class GroupBusinessTarget(models.Model):
if not primary_department_name:
raise ValueError("一级部门不存在")
if self.pk is None:
# 新增操作,进行唯一性校验
if GroupBusinessTarget.objects.filter(primary_department=self.primary_department, year=self.year,
project_nature=self.project_nature).exists():
raise IntegrityError("该一级部门在同一年已经有一个相同项目性质的业绩目标。")
super(GroupBusinessTarget, self).save(*args, **kwargs)
class Meta:
verbose_name = '集团经营目标'
verbose_name_plural = '集团经营目标表'
constraints = [
models.UniqueConstraint(fields=['primary_department', 'year', 'project_nature'], name='unique_department_year_nature')
models.UniqueConstraint(fields=['primary_department', 'year', 'project_nature'],
name='unique_department_year_nature')
]
def __str__(self):
@ -49,8 +63,8 @@ class TargetAudit(models.Model):
audit_id = models.AutoField(primary_key=True, verbose_name='记录ID')
target_id = models.ForeignKey(GroupBusinessTarget, on_delete=models.CASCADE, verbose_name='目标ID')
modified_field = models.CharField(max_length=255, verbose_name='修改字段')
old_value = models.CharField(max_length=255, verbose_name='旧值')
new_value = models.CharField(max_length=255, verbose_name='新值')
old_value = models.CharField(max_length=255, verbose_name='旧值', null=True, blank=True)
new_value = models.CharField(max_length=255, verbose_name='新值', null=True, blank=True)
modification_date = models.DateTimeField(auto_now_add=True, verbose_name='修改日期')
modified_by = models.CharField(max_length=255, verbose_name='修改人')
@ -75,12 +89,24 @@ class EmployeePerformanceTarget(models.Model):
name = models.CharField(max_length=255, verbose_name='姓名')
department = models.CharField(max_length=255, verbose_name='一级部门')
year = models.IntegerField(verbose_name='年份')
project_nature = models.CharField(max_length=255, choices=PROJECT_NATURE_CHOICES, verbose_name='项目性质')
sales_target = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='销售额目标(元)')
total_revenue_target = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='收入总目标(元)')
new_revenue_target = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='新增收入目标(元)')
existing_revenue_target = models.DecimalField(max_digits=15, decimal_places=2, verbose_name='存量收入目标(元)')
def save(self, *args, **kwargs):
primary_department_name = PrimaryDepartment.objects.filter(department_name=self.department).first()
if not primary_department_name:
raise ValueError("一级部门不存在")
if self.pk is None:
# 新增操作,进行唯一性校验
if EmployeePerformanceTarget.objects.filter(department=self.department, year=self.year, name=self.name).exists():
raise IntegrityError("该姓名和一级部门在同一年已经有一个相同的业绩目标。")
super(EmployeePerformanceTarget, self).save(*args, **kwargs)
class Meta:
verbose_name = '员工业绩目标'
verbose_name_plural = '员工业绩目标表'
@ -94,8 +120,8 @@ class EmployeeTargetAudit(models.Model):
audit_id = models.AutoField(primary_key=True, verbose_name='记录ID')
target_id = models.ForeignKey(EmployeePerformanceTarget, on_delete=models.CASCADE, verbose_name='目标ID')
modified_field = models.CharField(max_length=255, verbose_name='修改字段')
old_value = models.CharField(max_length=255, verbose_name='旧值')
new_value = models.CharField(max_length=255, verbose_name='新值')
old_value = models.CharField(max_length=255, verbose_name='旧值', null=True, blank=True)
new_value = models.CharField(max_length=255, verbose_name='新值', null=True, blank=True)
modification_date = models.DateTimeField(auto_now_add=True, verbose_name='修改日期')
modified_by = models.CharField(max_length=255, verbose_name='修改人')
@ -104,4 +130,4 @@ class EmployeeTargetAudit(models.Model):
verbose_name_plural = '员工业绩目标修改记录表'
def __str__(self):
return f"{self.target_id} - {self.modified_field} - {self.modification_date}"
return f"{self.target_id} - {self.modified_field} - {self.modification_date}"

View File

@ -17,24 +17,41 @@ class EmployeePerformanceTargetSerializer(serializers.ModelSerializer):
class TargetAuditSerializer(serializers.ModelSerializer):
modification_date = serializers.DateTimeField(format='%Y-%m-%d %H:%M')
modified_obj = serializers.SerializerMethodField()
modified_field_verbose = serializers.SerializerMethodField()
class Meta:
model = TargetAudit
fields = ('modified_obj', 'modified_field', 'old_value', 'new_value', 'modification_date', 'modified_by')
fields = ('modified_obj', 'modified_field_verbose', 'old_value', 'new_value', 'modification_date', 'modified_by')
def get_modified_obj(self, obj):
target = obj.target_id
return f"{target.primary_department}{target.year}年度目标"
return f"{target.year}-{target.primary_department}-{target.project_nature}"
def get_modified_field_verbose(self, obj):
# 获取模型类
model_class = obj.target_id._meta.model
# 获取字段的 verbose_name
field = model_class._meta.get_field(obj.modified_field)
return field.verbose_name
class EmployeeTargetAuditSerializer(serializers.ModelSerializer):
modification_date = serializers.DateTimeField(format='%Y-%m-%d %H:%M')
modified_obj = serializers.SerializerMethodField()
modified_field_verbose = serializers.SerializerMethodField()
class Meta:
model = TargetAudit
fields = ('modified_obj', 'modified_field', 'old_value', 'new_value', 'modification_date', 'modified_by')
fields = ('modified_obj', 'modified_field_verbose', 'old_value', 'new_value', 'modification_date', 'modified_by')
def get_modified_obj(self, obj):
target = obj.target_id
return f"{target.department}{target.year}年度目标"
return f"{target.department}-{target.name}-{target.year}"
def get_modified_field_verbose(self, obj):
# 获取模型类
model_class = obj.target_id._meta.model
# 获取字段的 verbose_name
field = model_class._meta.get_field(obj.modified_field)
return field.verbose_name

View File

@ -111,7 +111,10 @@ def gbo_list_view(request):
"add_url": reverse("gbo_list_add"),
"delete_url": reverse("gbo_list_delete"),
"add_button": True,
"import_excel_button": True
"import_excel_button": True,
"report_excel_button": True,
"modify_records_button": True,
"modify_records_url": reverse("gbo_audit_record_list")
}
return render(request, 'items_list.html', context)
@ -147,11 +150,38 @@ def gbo_list_modify(request):
if 'id' in request.POST:
instance = GroupBusinessTarget.objects.get(target_id=request.POST['id'])
form = GroupBusinessTargetForm(request.POST, instance=instance)
old_data = instance.__dict__.copy() # 保存旧数据
else:
form = GroupBusinessTargetForm(request.POST)
old_data = None
if form.is_valid():
if old_data:
old_data.pop('_state', None) # 删除旧数据中的不可序列化对象
old_data.pop('id', None) # 删除旧数据中的主键
form.save()
new_data = form.instance.__dict__.copy() # 保存新数据
new_data.pop('_state', None) # 删除新数据中的不可序列化对象
new_data.pop('id', None) # 删除新数据中的主键
modified_by = request.user.username # 获取修改人
target_instance = form.instance
# 对比新旧数据,找出变化的字段
if old_data:
for field, old_value in old_data.items():
new_value = new_data.get(field)
if old_value != new_value:
# 为每个变化的字段新增一条修改记录
TargetAudit.objects.create(
target_id=target_instance,
modified_field=field,
old_value=old_value,
new_value=new_value,
modified_by=modified_by
)
return JsonResponse({"message": "保存成功"})
else:
form_html = render_to_string('form_partial.html', {'form': form}, request)
@ -302,18 +332,6 @@ def emt_list_view(request):
{"value": "2021", "display": "2021"},
{"value": "2020", "display": "2020"}
]
},
{
"type": "select",
"id": "project_nature",
"name": "project_nature",
"label": "项目性质",
"options": [
{"value": "新增", "display": "新增"},
{"value": "存续", "display": "存续"},
{"value": "新增及存续", "display": "新增及存续"},
{"value": "老客户新业务", "display": "老客户新业务"}
]
}
],
"table_exclude_field_name": ['target_id'],
@ -329,7 +347,10 @@ def emt_list_view(request):
"add_url": reverse("emt_list_add"),
"delete_url": reverse("emt_list_delete"),
"add_button": True,
"import_excel_button": True
"import_excel_button": True,
"report_excel_button": True,
"modify_records_button": True,
"modify_records_url": reverse("emt_audit_record_list")
}
return render(request, 'items_list.html', context)
@ -365,11 +386,38 @@ def emt_list_modify(request):
if 'id' in request.POST:
instance = EmployeePerformanceTarget.objects.get(target_id=request.POST['id'])
form = EmployeePerformanceTargetForm(request.POST, instance=instance)
old_data = instance.__dict__.copy() # 保存旧数据
else:
form = EmployeePerformanceTargetForm(request.POST)
old_data = None
if form.is_valid():
if old_data:
old_data.pop('_state', None) # 删除旧数据中的不可序列化对象
old_data.pop('id', None) # 删除旧数据中的主键
form.save()
new_data = form.instance.__dict__.copy() # 保存新数据
new_data.pop('_state', None) # 删除新数据中的不可序列化对象
new_data.pop('id', None) # 删除新数据中的主键
modified_by = request.user.username # 获取修改人
target_instance = form.instance
# 对比新旧数据,找出变化的字段
if old_data:
for field, old_value in old_data.items():
new_value = new_data.get(field)
if old_value != new_value:
# 为每个变化的字段新增一条修改记录
EmployeeTargetAudit.objects.create(
target_id=target_instance,
modified_field=field,
old_value=old_value,
new_value=new_value,
modified_by=modified_by
)
return JsonResponse({"message": "保存成功"})
else:
form_html = render_to_string('form_partial.html', {'form': form}, request)

View File

@ -34,11 +34,12 @@
function fetchAuditRecords(page) {
$.ajax({
type: 'GET',
url: '{{ url.modify_record }}',
url: '{{ modify_records_url }}',
data: {
page: page
},
success: function (response) {
debugger
if (response.modal_title) {
$('#modalTitle').text(response.modal_title);
@ -102,7 +103,7 @@
}
// 页面加载完毕后,第一次获取数据
$(document).ready(function () {
$('#modifyRecordBtn').click(function () {
fetchAuditRecords(1); // 默认加载第一页
});