forked from erp-dev/erp
fix: business.object_id maybe null, missing parameters when plate-Order cloned
This commit is contained in:
3
stateflow/management/__init__.py
Normal file
3
stateflow/management/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# Package marker for Django management commands.
|
||||
|
||||
|
||||
3
stateflow/management/commands/__init__.py
Normal file
3
stateflow/management/commands/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# Package marker for Django management commands.
|
||||
|
||||
|
||||
124
stateflow/management/commands/repair_business_object_bindings.py
Normal file
124
stateflow/management/commands/repair_business_object_bindings.py
Normal file
@@ -0,0 +1,124 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import transaction
|
||||
|
||||
from printing import models as printing_models
|
||||
from stateflow import services as stateflow_services
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = (
|
||||
"修复 printing 相关模型(PrintingJob/PlateOrder)所关联的 BusinessObject 的 "
|
||||
"content_type/object_id/name 绑定(用于历史数据回填与一致性修复)。"
|
||||
)
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--dry-run',
|
||||
action='store_true',
|
||||
help='只输出统计,不写入数据库',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--limit',
|
||||
type=int,
|
||||
default=None,
|
||||
help='最多处理多少条记录(用于分批回填)',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--only',
|
||||
choices=['printingjob', 'plateorder', 'all'],
|
||||
default='all',
|
||||
help='仅修复指定类型(默认 all)',
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
dry_run: bool = options['dry_run']
|
||||
limit: int | None = options['limit']
|
||||
only: str = options['only']
|
||||
|
||||
# 提前确保 ContentType 可用(也用于输出校验信息)
|
||||
ct_job = ContentType.objects.get_for_model(printing_models.PrintingJob)
|
||||
ct_po = ContentType.objects.get_for_model(printing_models.PlateOrder)
|
||||
|
||||
self.stdout.write(f"dry_run={dry_run} only={only} limit={limit}")
|
||||
self.stdout.write(f"ContentType: PrintingJob={ct_job.id}, PlateOrder={ct_po.id}")
|
||||
|
||||
stats = {
|
||||
'printingjob_total': 0,
|
||||
'printingjob_updated': 0,
|
||||
'plateorder_total': 0,
|
||||
'plateorder_updated': 0,
|
||||
}
|
||||
|
||||
def _iter_qs(qs):
|
||||
if limit:
|
||||
return qs.order_by('id')[:limit]
|
||||
return qs.order_by('id')
|
||||
|
||||
# 用事务包住,避免部分更新(dry-run 仍使用事务,最后标记回滚)
|
||||
with transaction.atomic():
|
||||
if only in ('printingjob', 'all'):
|
||||
qs = (
|
||||
printing_models.PrintingJob.objects
|
||||
.select_related('business_object')
|
||||
.filter(business_object__isnull=False)
|
||||
)
|
||||
for job in _iter_qs(qs):
|
||||
stats['printingjob_total'] += 1
|
||||
bo = job.business_object
|
||||
should_fix = (
|
||||
bo.content_type_id != ct_job.id
|
||||
or bo.object_id != job.id
|
||||
or not (bo.name or '').strip()
|
||||
)
|
||||
if not should_fix:
|
||||
continue
|
||||
|
||||
if dry_run:
|
||||
stats['printingjob_updated'] += 1
|
||||
continue
|
||||
|
||||
if stateflow_services.ensure_business_object_bound_to_instance(
|
||||
bo,
|
||||
job,
|
||||
default_name=f"PrintingJob-{job.id}",
|
||||
):
|
||||
stats['printingjob_updated'] += 1
|
||||
|
||||
if only in ('plateorder', 'all'):
|
||||
qs = (
|
||||
printing_models.PlateOrder.objects
|
||||
.select_related('business_object')
|
||||
.filter(business_object__isnull=False)
|
||||
)
|
||||
for po in _iter_qs(qs):
|
||||
stats['plateorder_total'] += 1
|
||||
bo = po.business_object
|
||||
should_fix = (
|
||||
bo.content_type_id != ct_po.id
|
||||
or bo.object_id != po.id
|
||||
or not (bo.name or '').strip()
|
||||
)
|
||||
if not should_fix:
|
||||
continue
|
||||
|
||||
if dry_run:
|
||||
stats['plateorder_updated'] += 1
|
||||
continue
|
||||
|
||||
if stateflow_services.ensure_business_object_bound_to_instance(
|
||||
bo,
|
||||
po,
|
||||
default_name=f"PlateOrder-{po.id}",
|
||||
):
|
||||
stats['plateorder_updated'] += 1
|
||||
|
||||
if dry_run:
|
||||
# dry-run:显式回滚(即便未来误加了写逻辑,也不会落库)
|
||||
transaction.set_rollback(True)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS("Done."))
|
||||
for k in sorted(stats.keys()):
|
||||
self.stdout.write(f"{k}={stats[k]}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user