Files

135 lines
7.5 KiB
Python

from django import forms
from .models import ItemCatalog, PurchaseRequisition, RequisitionItem, PurchaseOrder, PurchaseOrderItem, GoodsReceipt, ThreeWayMatch
from expenses.models import Project, Vendor, Category
from decimal import Decimal
class ItemCatalogForm(forms.ModelForm):
class Meta:
model = ItemCatalog
fields = ['code', 'name', 'category', 'unit', 'reference_price_clp', 'reference_price_usd', 'sla_days', 'preferred_vendor', 'specifications', 'is_active']
widgets = {
'code': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'CAT-XXX-01', 'required': 'required', 'pattern': 'CAT-[A-Z0-9]+-[0-9]+'}),
'name': forms.TextInput(attrs={'class': 'form-control', 'required': 'required', 'minlength': '3'}),
'category': forms.Select(attrs={'class': 'form-control', 'required': 'required'}),
'unit': forms.Select(attrs={'class': 'form-control', 'required': 'required'}),
'reference_price_clp': forms.NumberInput(attrs={'class': 'form-control', 'step': '1000', 'required': 'required', 'min': '0'}),
'reference_price_usd': forms.NumberInput(attrs={'class': 'form-control', 'step': '1', 'min': '0'}),
'sla_days': forms.NumberInput(attrs={'class': 'form-control', 'required': 'required', 'min': '0'}),
'preferred_vendor': forms.Select(attrs={'class': 'form-control'}),
'specifications': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
}
def clean_reference_price_clp(self):
val = self.cleaned_data.get('reference_price_clp')
if val is not None and val < 0:
raise forms.ValidationError("El precio no puede ser negativo.")
return val
class PurchaseRequisitionForm(forms.ModelForm):
required_date = forms.DateField(
widget=forms.DateInput(attrs={'type': 'date', 'class': 'form-control', 'required': 'required'}),
label="Fecha Requerida en Sitio / Faena"
)
class Meta:
model = PurchaseRequisition
fields = ['project', 'purpose', 'priority', 'required_date']
widgets = {
'project': forms.Select(attrs={'class': 'form-control', 'required': 'required'}),
'purpose': forms.Textarea(attrs={'class': 'form-control', 'rows': 3, 'placeholder': 'Describa el objetivo técnico y la necesidad para el proyecto...', 'required': 'required', 'minlength': '10'}),
'priority': forms.Select(attrs={'class': 'form-control', 'required': 'required'}),
}
class PurchaseOrderForm(forms.ModelForm):
issue_date = forms.DateField(
widget=forms.DateInput(attrs={'type': 'date', 'class': 'form-control', 'required': 'required'}),
label="Fecha de Emisión"
)
expected_delivery_date = forms.DateField(
widget=forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}),
label="Fecha Estimada de Entrega",
required=False
)
class Meta:
model = PurchaseOrder
fields = ['requisition', 'vendor', 'project', 'issue_date', 'expected_delivery_date', 'payment_terms', 'delivery_address', 'terms_and_conditions', 'notes']
widgets = {
'requisition': forms.Select(attrs={'class': 'form-control', 'required': 'required'}),
'vendor': forms.Select(attrs={'class': 'form-control', 'required': 'required'}),
'project': forms.Select(attrs={'class': 'form-control', 'required': 'required'}),
'payment_terms': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Ej: 30 Días Contra Factura', 'required': 'required'}),
'delivery_address': forms.TextInput(attrs={'class': 'form-control', 'id': 'id_delivery_address', 'required': 'required'}),
'terms_and_conditions': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}),
}
def clean(self):
cleaned_data = super().clean()
issue = cleaned_data.get('issue_date')
expected = cleaned_data.get('expected_delivery_date')
if issue and expected and expected < issue:
raise forms.ValidationError("La fecha de entrega esperada no puede ser anterior a la fecha de emisión.")
return cleaned_data
class GoodsReceiptForm(forms.ModelForm):
receipt_date = forms.DateField(
widget=forms.DateInput(attrs={'type': 'date', 'class': 'form-control', 'required': 'required'}),
label="Fecha de Recepción"
)
class Meta:
model = GoodsReceipt
fields = ['purchase_order', 'dispatch_guide_number', 'receipt_date', 'technical_conformity', 'notes']
widgets = {
'purchase_order': forms.Select(attrs={'class': 'form-control', 'required': 'required'}),
'dispatch_guide_number': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'N° Guía de Despacho o Acta Terreno', 'required': 'required'}),
'technical_conformity': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3, 'placeholder': 'Observaciones de estado físico, embalaje o pruebas de funcionamiento...'}),
}
class ThreeWayMatchForm(forms.ModelForm):
dte_invoice_date = forms.DateField(
widget=forms.DateInput(attrs={'type': 'date', 'class': 'form-control', 'required': 'required'}),
label="Fecha Emisión Factura SII"
)
class Meta:
model = ThreeWayMatch
fields = [
'purchase_order', 'goods_receipt', 'dte_invoice_number', 'dte_invoice_rut',
'dte_invoice_date', 'dte_net_amount', 'dte_tax_amount', 'dte_total_amount',
'resolution_notes'
]
widgets = {
'purchase_order': forms.Select(attrs={'class': 'form-control', 'id': 'id_po_select', 'required': 'required'}),
'goods_receipt': forms.Select(attrs={'class': 'form-control', 'required': 'required'}),
'dte_invoice_number': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Ej: 45290', 'required': 'required', 'pattern': r'^[0-9A-Za-z\-]+$'}),
'dte_invoice_rut': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Ej: 76.543.210-K', 'required': 'required', 'pattern': r'^\d{1,2}\.\d{3}\.\d{3}-[\dkK]$'}),
'dte_net_amount': forms.NumberInput(attrs={'class': 'form-control', 'step': '1', 'id': 'id_dte_net', 'required': 'required', 'min': '0'}),
'dte_tax_amount': forms.NumberInput(attrs={'class': 'form-control', 'step': '1', 'id': 'id_dte_tax', 'required': 'required', 'min': '0'}),
'dte_total_amount': forms.NumberInput(attrs={'class': 'form-control', 'step': '1', 'id': 'id_dte_total', 'required': 'required', 'min': '0'}),
'resolution_notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 2, 'placeholder': 'Comentarios de auditoría o justificación de diferencias...'}),
}
def clean_dte_invoice_rut(self):
rut = self.cleaned_data.get('dte_invoice_rut')
if not rut:
raise forms.ValidationError("Este campo es obligatorio.")
return rut.upper()
def clean(self):
cleaned_data = super().clean()
net = cleaned_data.get('dte_net_amount')
tax = cleaned_data.get('dte_tax_amount')
total = cleaned_data.get('dte_total_amount')
if net is not None and tax is not None and total is not None:
if net + tax != total:
raise forms.ValidationError("El Monto Total debe ser igual a la suma del Monto Neto y el Monto IVA.")
return cleaned_data