284 lines
10 KiB
Python
284 lines
10 KiB
Python
from django import forms
|
|
from django.contrib.auth.models import User, Group
|
|
from .models import Expense, Project, Category, SystemSetting, UserProfile, Document, CurrencyEnum, ExpenseTypeEnum, Vendor, VehicleOwnershipEnum
|
|
from .chile_regions import REGION_CHOICES, COMMUNE_CHOICES
|
|
|
|
|
|
from decimal import Decimal
|
|
import os
|
|
|
|
class MultipleFileInput(forms.FileInput):
|
|
allow_multiple_selected = True
|
|
|
|
class MultipleFileField(forms.FileField):
|
|
def __init__(self, *args, **kwargs):
|
|
kwargs.setdefault("widget", MultipleFileInput(attrs={'multiple': True, 'accept': 'image/*,.pdf'}))
|
|
super().__init__(*args, **kwargs)
|
|
|
|
def clean(self, data, initial=None):
|
|
single_file_clean = super().clean
|
|
if isinstance(data, (list, tuple)):
|
|
result = [single_file_clean(d, initial) for d in data]
|
|
else:
|
|
result = single_file_clean(data, initial)
|
|
return result
|
|
|
|
class ExpenseForm(forms.ModelForm):
|
|
date = forms.DateField(
|
|
widget=forms.DateInput(attrs={'type': 'date'}),
|
|
label='Fecha del Gasto'
|
|
)
|
|
|
|
amount_clp = forms.CharField(
|
|
label='Monto Neto (CLP)',
|
|
required=True,
|
|
widget=forms.TextInput(attrs={'placeholder': 'Ej: 1.450.000'})
|
|
)
|
|
|
|
receipt = MultipleFileField(
|
|
label='Adjuntar Evidencia(s) (Boleta / Factura)',
|
|
required=False
|
|
)
|
|
|
|
region = forms.ChoiceField(
|
|
choices=[('', '--- Seleccione Región ---')] + REGION_CHOICES,
|
|
label='Región Operativa',
|
|
required=False
|
|
)
|
|
|
|
comuna = forms.ChoiceField(
|
|
choices=[('', '--- Seleccione Comuna ---')] + COMMUNE_CHOICES,
|
|
label='Comuna',
|
|
required=False
|
|
)
|
|
|
|
vehicle_ownership = forms.ChoiceField(
|
|
choices=[('', '--- Seleccione Modalidad ---')] + VehicleOwnershipEnum.choices,
|
|
label='Modalidad / Vehículo',
|
|
required=False
|
|
)
|
|
|
|
tax_amount_clp = forms.CharField(
|
|
label='Monto Impuesto (CLP)',
|
|
required=False,
|
|
widget=forms.TextInput(attrs={'readonly': 'readonly'})
|
|
)
|
|
|
|
total_amount_clp = forms.CharField(
|
|
label='Monto Total (CLP)',
|
|
required=False,
|
|
widget=forms.TextInput(attrs={'readonly': 'readonly'})
|
|
)
|
|
|
|
new_vendor_rut = forms.CharField(
|
|
label="RUT Nuevo Proveedor",
|
|
required=False,
|
|
widget=forms.TextInput(attrs={'placeholder': 'Ej: 76.543.210-K', 'id': 'id_new_vendor_rut'})
|
|
)
|
|
|
|
new_vendor_name = forms.CharField(
|
|
label="Nombre / Razón Social Nuevo Proveedor",
|
|
required=False,
|
|
widget=forms.TextInput(attrs={'placeholder': 'Ej: IMPORTADORA SPA', 'id': 'id_new_vendor_name'})
|
|
)
|
|
|
|
class Meta:
|
|
model = Expense
|
|
fields = [
|
|
'project', 'category', 'amount_clp', 'tax_type', 'tax_amount_clp', 'total_amount_clp', 'currency', 'responsible_person', 'date',
|
|
'description', 'vendor', 'region', 'comuna', 'vehicle_ownership'
|
|
]
|
|
widgets = {
|
|
'description': forms.Textarea(attrs={'rows': 3, 'placeholder': 'Ej: Carga combustible Copec / Pasajes bus terreno'}),
|
|
}
|
|
|
|
def clean_amount_clp(self):
|
|
data = self.cleaned_data.get('amount_clp', '')
|
|
if not data:
|
|
raise forms.ValidationError("Este campo es obligatorio.")
|
|
data_clean = str(data).replace('.', '').replace(',', '.')
|
|
try:
|
|
return Decimal(data_clean)
|
|
except Exception:
|
|
raise forms.ValidationError("Ingrese un monto válido (ej: 1.450.000).")
|
|
|
|
def clean_tax_amount_clp(self):
|
|
data = self.cleaned_data.get('tax_amount_clp', '')
|
|
if not data:
|
|
return Decimal('0.00')
|
|
data_clean = str(data).replace('.', '').replace(',', '.')
|
|
try:
|
|
return Decimal(data_clean)
|
|
except Exception:
|
|
return Decimal('0.00')
|
|
|
|
def clean_total_amount_clp(self):
|
|
data = self.cleaned_data.get('total_amount_clp', '')
|
|
if not data:
|
|
return Decimal('0.00')
|
|
data_clean = str(data).replace('.', '').replace(',', '.')
|
|
try:
|
|
return Decimal(data_clean)
|
|
except Exception:
|
|
return Decimal('0.00')
|
|
|
|
def clean_receipt(self):
|
|
files = self.files.getlist('receipt')
|
|
for f in files:
|
|
ext = os.path.splitext(f.name)[1].lower()
|
|
if ext not in ['.pdf', '.png', '.jpg', '.jpeg']:
|
|
raise forms.ValidationError(f"El archivo '{f.name}' no tiene una extensión permitida (PDF, PNG, JPG, JPEG).")
|
|
return self.cleaned_data.get('receipt')
|
|
|
|
def clean(self):
|
|
cleaned_data = super().clean()
|
|
new_name = cleaned_data.get('new_vendor_name')
|
|
new_rut = cleaned_data.get('new_vendor_rut')
|
|
|
|
if (new_name and not new_rut) or (new_rut and not new_name):
|
|
raise forms.ValidationError("Si desea registrar un nuevo proveedor, debe ingresar tanto el RUT como la Razón Social.")
|
|
return cleaned_data
|
|
|
|
def save(self, commit=True):
|
|
instance = super().save(commit=False)
|
|
new_name = self.cleaned_data.get('new_vendor_name')
|
|
new_rut = self.cleaned_data.get('new_vendor_rut')
|
|
if new_name and new_rut:
|
|
from .models import Vendor
|
|
vendor, _ = Vendor.objects.get_or_create(
|
|
rut=new_rut.strip(),
|
|
defaults={'name': new_name.strip()}
|
|
)
|
|
instance.vendor = vendor
|
|
if commit:
|
|
instance.save()
|
|
return instance
|
|
|
|
|
|
class ProjectForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Project
|
|
fields = ['code', 'name', 'parent', 'category', 'status', 'type', 'description', 'manager', 'assigned_budget_clp']
|
|
widgets = {
|
|
'description': forms.Textarea(attrs={'rows': 3}),
|
|
}
|
|
|
|
|
|
class CategoryForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Category
|
|
fields = ['code', 'name', 'parent', 'description']
|
|
widgets = {
|
|
'description': forms.Textarea(attrs={'rows': 3}),
|
|
}
|
|
|
|
|
|
class VendorForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Vendor
|
|
fields = ['rut', 'name', 'business_line', 'bank', 'account_type', 'account_number', 'email', 'payment_terms']
|
|
|
|
|
|
class SystemSettingForm(forms.ModelForm):
|
|
class Meta:
|
|
model = SystemSetting
|
|
fields = ['global_budget', 'usd_exchange_rate']
|
|
|
|
|
|
class ResponsableForm(forms.Form):
|
|
username = forms.CharField(max_length=150, label="Nombre de Usuario")
|
|
password = forms.CharField(widget=forms.PasswordInput(), label="Contraseña", required=False)
|
|
email = forms.EmailField(label="Correo Electrónico", required=False)
|
|
role = forms.ModelChoiceField(queryset=Group.objects.all(), label="Rol / Grupo")
|
|
|
|
# Perfil Fields
|
|
rut = forms.CharField(max_length=20, label="RUT", required=False)
|
|
job_title = forms.CharField(max_length=150, label="Cargo", required=False)
|
|
description = forms.CharField(widget=forms.Textarea(attrs={'rows': 3}), label="Descripción / Bio", required=False)
|
|
specialties = forms.ModelMultipleChoiceField(
|
|
queryset=Category.objects.all(),
|
|
required=False,
|
|
widget=forms.CheckboxSelectMultiple(),
|
|
label="Especialidades Técnicas (Categorías)"
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.instance = kwargs.pop('instance', None)
|
|
super().__init__(*args, **kwargs)
|
|
if self.instance:
|
|
self.fields['username'].initial = self.instance.username
|
|
self.fields['email'].initial = self.instance.email
|
|
# Set role
|
|
groups = self.instance.groups.all()
|
|
if groups.exists():
|
|
self.fields['role'].initial = groups.first()
|
|
# Set profile info
|
|
try:
|
|
self.fields['description'].initial = self.instance.profile.description
|
|
self.fields['rut'].initial = self.instance.profile.rut
|
|
self.fields['job_title'].initial = self.instance.profile.job_title
|
|
self.fields['specialties'].initial = self.instance.profile.specialties.all()
|
|
except Exception:
|
|
pass
|
|
# Password is optional for edit
|
|
self.fields['password'].required = False
|
|
self.fields['password'].help_text = "Dejar en blanco si no desea cambiarla."
|
|
|
|
def clean_username(self):
|
|
username = self.cleaned_data.get('username')
|
|
qs = User.objects.filter(username=username)
|
|
if self.instance:
|
|
qs = qs.exclude(pk=self.instance.pk)
|
|
if qs.exists():
|
|
raise forms.ValidationError("Este nombre de usuario ya está registrado.")
|
|
return username
|
|
|
|
def save(self):
|
|
username = self.cleaned_data.get('username')
|
|
password = self.cleaned_data.get('password')
|
|
email = self.cleaned_data.get('email')
|
|
role = self.cleaned_data.get('role')
|
|
description = self.cleaned_data.get('description')
|
|
rut = self.cleaned_data.get('rut')
|
|
job_title = self.cleaned_data.get('job_title')
|
|
|
|
if self.instance:
|
|
user = self.instance
|
|
user.username = username
|
|
user.email = email
|
|
if password:
|
|
user.set_password(password)
|
|
else:
|
|
user = User.objects.create_user(username=username, email=email, password=password)
|
|
|
|
if role.name == 'Administrador' or role.name == 'Administrador Finanzas':
|
|
user.is_staff = True
|
|
user.is_superuser = True
|
|
else:
|
|
user.is_staff = False
|
|
user.is_superuser = False
|
|
|
|
user.save()
|
|
|
|
user.groups.clear()
|
|
user.groups.add(role)
|
|
|
|
profile, _ = UserProfile.objects.get_or_create(user=user)
|
|
profile.description = description
|
|
profile.rut = rut
|
|
profile.job_title = job_title
|
|
profile.save()
|
|
|
|
# Save specialties
|
|
specialties = self.cleaned_data.get('specialties')
|
|
if specialties is not None:
|
|
profile.specialties.set(specialties)
|
|
|
|
return user
|
|
|
|
|
|
class DocumentForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Document
|
|
fields = ['file', 'title']
|