import re with open('expenses/models.py', 'r', encoding='utf-8') as f: lines = f.readlines() new_lines = [] in_project = False in_category = False in_vendor = False in_expense = False in_projectphase = False for line in lines: if line.startswith('class Project(models.Model):'): in_project = True abstracts = """from django.core.exceptions import ValidationError class TimeStampedModel(models.Model): created_at = models.DateTimeField(auto_now_add=True, verbose_name="Fecha de Creación") updated_at = models.DateTimeField(auto_now=True, verbose_name="Última Modificación") class Meta: abstract = True class CatalogModel(TimeStampedModel): code = models.CharField(max_length=50, unique=True, verbose_name="Código") name = models.CharField(max_length=200, verbose_name="Nombre") description = models.TextField(blank=True, verbose_name="Descripción") class Meta: abstract = True """ new_lines.append(abstracts) new_lines.append('class Project(CatalogModel):\n') continue if line.startswith('class Category(models.Model):'): in_project = False in_category = True new_lines.append('class Category(CatalogModel):\n') continue if line.startswith('class Vendor(models.Model):'): in_category = False in_vendor = True new_lines.append('class Vendor(TimeStampedModel):\n') continue if line.startswith('class ProjectPhase(models.Model):'): in_projectphase = True new_lines.append('class ProjectPhase(TimeStampedModel):\n') continue if line.startswith('class ExpenseReport(models.Model):'): in_vendor = False in_projectphase = False if line.startswith('class Expense(models.Model):'): in_expense = True new_lines.append('class Expense(TimeStampedModel):\n') continue if line.startswith('class VehicleLog(models.Model):'): in_expense = False if in_project: if 'code =' in line and 'default=\'PROY-000\'' in line: continue if 'name = models.CharField(max_length=200' in line: continue if 'description = models.TextField(blank=True' in line: continue if 'created_at = models.DateTimeField(auto_now_add=True' in line: continue if 'updated_at = models.DateTimeField(auto_now=True' in line: continue if in_category: if 'code = models.CharField(max_length=10' in line: continue if 'name = models.CharField(max_length=100' in line: continue if 'description = models.TextField(blank=True' in line: continue if in_vendor: if 'created_at = models.DateTimeField(auto_now_add=True)' in line: continue if 'updated_at = models.DateTimeField(auto_now=True)' in line: continue if in_projectphase: if 'created_at = models.DateTimeField(auto_now_add=True)' in line: continue if 'updated_at = models.DateTimeField(auto_now=True)' in line: continue if in_expense: if 'created_at = models.DateTimeField(auto_now_add=True)' in line: continue if 'updated_at = models.DateTimeField(auto_now=True)' in line: continue if ' def save(self, *args, **kwargs):' in line: encap = """ def clean(self): super().clean() if self.amount_clp and self.amount_clp < 0: raise ValidationError({'amount_clp': 'El monto neto no puede ser negativo.'}) amount = Decimal(str(self.amount_clp or '0')) tax = Decimal(str(self.tax_amount_clp or '0')) expected = amount + tax if self.total_amount_clp and abs(self.total_amount_clp - expected) > Decimal('1.0'): raise ValidationError({'total_amount_clp': f'Discrepancia. Total esperado: {expected}'}) def approve_expense(self, user): if self.approval_status == ApprovalStatusEnum.EN_REVISION: self.approval_status = ApprovalStatusEnum.SOLICITADO_FINANZAS self.elevated_by = user self.save() def reject_expense(self, reason): self.approval_status = ApprovalStatusEnum.OBSERVADO_RECHAZADO self.rejection_reason = reason self.save() """ new_lines.append(encap + "\n") new_lines.append(line) proxy = """ class Reimbursement(Expense): class Meta: proxy = True verbose_name = "Rendición Especializada" verbose_name_plural = "Rendiciones Especializadas" def get_approval_flow(self): return "Flujo Acelerado de Reembolso" """ new_lines.append(proxy) with open('expenses/models.py', 'w', encoding='utf-8') as f: f.writelines(new_lines)