396 lines
14 KiB
Python
396 lines
14 KiB
Python
|
|
# forms.py
|
||
|
|
from django import forms
|
||
|
|
from django.contrib.auth import get_user_model # To reference the User model
|
||
|
|
from .models import (
|
||
|
|
Currency, Country, State, Status, CustomerCategory, ContactCategory,
|
||
|
|
Industry, ProductCategory, FreightForwarder, FreightService,
|
||
|
|
Customer, Address, Principle, PrincipleAddress, Contact,
|
||
|
|
Product, ProductAttachment, ProductOptionsCategory, ProductOption,
|
||
|
|
Document, DocPage, Attachment, DocumentAttachment,
|
||
|
|
Enquiry, EnquiryEmailQueue, EnquiryFile, Job, LineItem, Costing,
|
||
|
|
Quote, Invoice, PurchaseOrder, OrderAcknowledgement, PackingList,
|
||
|
|
Email, EmailRecipient, EmailAttachment,
|
||
|
|
Shipment, Box, ShipmentInvoice,
|
||
|
|
# Import other models if forms are needed for them
|
||
|
|
)
|
||
|
|
|
||
|
|
# Get the custom User model if you have one, otherwise the default User
|
||
|
|
User = get_user_model()
|
||
|
|
|
||
|
|
# --- Base Model Forms ---
|
||
|
|
|
||
|
|
class CurrencyForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = Currency
|
||
|
|
fields = ['name', 'code', 'symbol']
|
||
|
|
|
||
|
|
class CountryForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = Country
|
||
|
|
fields = ['name', 'currency']
|
||
|
|
|
||
|
|
class StateForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = State
|
||
|
|
fields = ['name', 'shortform']
|
||
|
|
|
||
|
|
class StatusForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = Status
|
||
|
|
fields = ['name', 'css_class']
|
||
|
|
|
||
|
|
class CustomerCategoryForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = CustomerCategory
|
||
|
|
fields = ['name']
|
||
|
|
|
||
|
|
class ContactCategoryForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = ContactCategory
|
||
|
|
fields = ['name']
|
||
|
|
|
||
|
|
class IndustryForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = Industry
|
||
|
|
fields = ['name', 'parent']
|
||
|
|
|
||
|
|
class ProductCategoryForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = ProductCategory
|
||
|
|
fields = ['name']
|
||
|
|
|
||
|
|
class FreightForwarderForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = FreightForwarder
|
||
|
|
fields = ['name']
|
||
|
|
|
||
|
|
class FreightServiceForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = FreightService
|
||
|
|
fields = ['name', 'freight_forwarder']
|
||
|
|
|
||
|
|
# --- Core Model Forms ---
|
||
|
|
|
||
|
|
class CustomerForm(forms.ModelForm):
|
||
|
|
industries = forms.ModelMultipleChoiceField(
|
||
|
|
queryset=Industry.objects.all(),
|
||
|
|
widget=forms.CheckboxSelectMultiple,
|
||
|
|
required=False
|
||
|
|
)
|
||
|
|
|
||
|
|
class Meta:
|
||
|
|
model = Customer
|
||
|
|
fields = [
|
||
|
|
'name', 'abn', 'customer_category', 'country', 'industries'
|
||
|
|
]
|
||
|
|
widgets = {
|
||
|
|
'abn': forms.TextInput(attrs={'placeholder': 'Enter 11 digit ABN'}),
|
||
|
|
}
|
||
|
|
|
||
|
|
class AddressForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = Address
|
||
|
|
fields = [
|
||
|
|
'customer', 'address_line_1', 'address_line_2', 'city',
|
||
|
|
'postcode', 'state', 'country', 'type'
|
||
|
|
]
|
||
|
|
# You might want to filter 'customer', 'state', 'country' dropdowns
|
||
|
|
# dynamically in the view based on context.
|
||
|
|
|
||
|
|
class PrincipleForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = Principle
|
||
|
|
fields = ['name', 'city', 'country', 'currency']
|
||
|
|
|
||
|
|
class PrincipleAddressForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = PrincipleAddress
|
||
|
|
fields = [
|
||
|
|
'principle', 'address_line_1', 'address_line_2', 'city',
|
||
|
|
'postcode', 'country', 'type'
|
||
|
|
]
|
||
|
|
|
||
|
|
class ContactForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = Contact
|
||
|
|
fields = [
|
||
|
|
'first_name', 'last_name', 'email', 'job_title',
|
||
|
|
'customer', 'contact_category'
|
||
|
|
]
|
||
|
|
# Consider making principle/customer mutually exclusive or adding validation
|
||
|
|
# depending on business logic.
|
||
|
|
|
||
|
|
# --- Product Related Forms ---
|
||
|
|
|
||
|
|
class ProductForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = Product
|
||
|
|
fields = [
|
||
|
|
'principle', 'product_category', 'name', 'description', 'model_number'
|
||
|
|
]
|
||
|
|
widgets = {
|
||
|
|
'description': forms.Textarea(attrs={'rows': 3}),
|
||
|
|
}
|
||
|
|
|
||
|
|
class ProductAttachmentForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = ProductAttachment
|
||
|
|
fields = ['product', 'file', 'description']
|
||
|
|
widgets = {
|
||
|
|
'description': forms.TextInput(attrs={'placeholder': 'Optional description'}),
|
||
|
|
}
|
||
|
|
|
||
|
|
class ProductOptionsCategoryForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = ProductOptionsCategory
|
||
|
|
fields = ['product', 'name', 'location', 'exclusive']
|
||
|
|
|
||
|
|
class ProductOptionForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = ProductOption
|
||
|
|
fields = [
|
||
|
|
'product_options_category', 'name', 'value',
|
||
|
|
'price_adjustment', 'is_default'
|
||
|
|
]
|
||
|
|
|
||
|
|
# --- Document / Enquiry / Job / Order Flow Forms ---
|
||
|
|
|
||
|
|
# Note: Document model is abstract-like. Forms usually handle specific types (Quote, Invoice etc.)
|
||
|
|
|
||
|
|
class EnquiryForm(forms.ModelForm):
|
||
|
|
# Custom fields or overrides can go here
|
||
|
|
send_enquiry_email = forms.BooleanField(
|
||
|
|
required=False,
|
||
|
|
initial=True, # Default to sending email? Adjust as needed.
|
||
|
|
label="Send Confirmation Email to Contact?"
|
||
|
|
)
|
||
|
|
|
||
|
|
class Meta:
|
||
|
|
model = Enquiry
|
||
|
|
fields = [
|
||
|
|
# 'title' is often auto-generated, exclude it from user input forms
|
||
|
|
'user', 'customer', 'contact', 'state', 'country', 'principle',
|
||
|
|
'status', 'billing_address', 'shipping_address', 'gst',
|
||
|
|
'comments',
|
||
|
|
# Add the custom field
|
||
|
|
'send_enquiry_email'
|
||
|
|
]
|
||
|
|
widgets = {
|
||
|
|
'description': forms.Textarea(attrs={'rows': 4}),
|
||
|
|
# Consider using ModelChoiceFields with filtered querysets for addresses
|
||
|
|
# based on the selected customer in the view.
|
||
|
|
'billing_address': forms.Select(),
|
||
|
|
'shipping_address': forms.Select(),
|
||
|
|
}
|
||
|
|
|
||
|
|
def __init__(self, *args, **kwargs):
|
||
|
|
# Optionally filter choices based on passed arguments (e.g., customer)
|
||
|
|
customer = kwargs.pop('customer', None)
|
||
|
|
super().__init__(*args, **kwargs)
|
||
|
|
|
||
|
|
# Filter user choices to only active staff users (example)
|
||
|
|
self.fields['user'].queryset = User.objects.filter(is_active=True, is_staff=True) # Adjust filter as needed
|
||
|
|
|
||
|
|
# Filter contact choices based on the customer (if provided)
|
||
|
|
if customer:
|
||
|
|
self.fields['contact'].queryset = Contact.objects.filter(customer=customer)
|
||
|
|
self.fields['billing_address'].queryset = Address.objects.filter(customer=customer, type__in=['billing', 'physical']) # Example filter
|
||
|
|
self.fields['shipping_address'].queryset = Address.objects.filter(customer=customer, type__in=['shipping', 'physical'])
|
||
|
|
elif self.instance and self.instance.pk and self.instance.customer:
|
||
|
|
# If editing an existing instance, filter based on its customer
|
||
|
|
customer = self.instance.customer
|
||
|
|
self.fields['contact'].queryset = Contact.objects.filter(customer=customer)
|
||
|
|
self.fields['billing_address'].queryset = Address.objects.filter(customer=customer, type__in=['billing', 'physical'])
|
||
|
|
self.fields['shipping_address'].queryset = Address.objects.filter(customer=customer, type__in=['shipping', 'physical'])
|
||
|
|
else:
|
||
|
|
# No customer context, clear or disable these fields initially
|
||
|
|
self.fields['contact'].queryset = Contact.objects.none()
|
||
|
|
self.fields['billing_address'].queryset = Address.objects.none()
|
||
|
|
self.fields['shipping_address'].queryset = Address.objects.none()
|
||
|
|
|
||
|
|
# You might want to filter 'state' based on 'country' using JavaScript
|
||
|
|
# or libraries like django-smart-selects.
|
||
|
|
|
||
|
|
class EnquiryStatusForm(forms.Form):
|
||
|
|
"""Simple form for AJAX status updates."""
|
||
|
|
enquiry_id = forms.IntegerField(widget=forms.HiddenInput())
|
||
|
|
status_id = forms.ModelChoiceField(queryset=Status.objects.all(), empty_label=None)
|
||
|
|
|
||
|
|
class EnquirySearchForm(forms.Form):
|
||
|
|
"""Form for the search functionality."""
|
||
|
|
search_string = forms.CharField(
|
||
|
|
label="Search Enquiries, Customers, Contacts, Jobs",
|
||
|
|
max_length=100,
|
||
|
|
widget=forms.TextInput(attrs={'placeholder': 'Enter name, enquiry #, job #, etc.'})
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class EnquiryFileForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = EnquiryFile
|
||
|
|
fields = ['enquiry', 'file', 'description']
|
||
|
|
|
||
|
|
class JobForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = Job
|
||
|
|
fields = [
|
||
|
|
# 'title' might be auto-generated
|
||
|
|
'enquiry', 'customer', 'contact', 'state', 'currency',
|
||
|
|
'customer_order_number', 'date_order_received',
|
||
|
|
'all_sent', 'all_paid', 'comments'
|
||
|
|
# Add other fields like freight/sale/shipment categories if needed
|
||
|
|
]
|
||
|
|
widgets = {
|
||
|
|
'comments': forms.Textarea(attrs={'rows': 3}),
|
||
|
|
'date_order_received': forms.DateInput(attrs={'type': 'date'}),
|
||
|
|
}
|
||
|
|
# Similar filtering logic for contact based on customer might be needed here
|
||
|
|
|
||
|
|
class LineItemForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = LineItem
|
||
|
|
fields = [
|
||
|
|
'document', 'product', 'item_number', 'description',
|
||
|
|
'quantity', 'unit_price'
|
||
|
|
]
|
||
|
|
widgets = {
|
||
|
|
'description': forms.Textarea(attrs={'rows': 2}),
|
||
|
|
}
|
||
|
|
# Note: 'document' and 'item_number' are often set programmatically
|
||
|
|
# when using formsets.
|
||
|
|
|
||
|
|
class CostingForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = Costing
|
||
|
|
fields = [
|
||
|
|
'line_item', 'product', 'purchase_currency', 'sale_currency',
|
||
|
|
'purchase_price', 'sale_price', 'exchange_rate'
|
||
|
|
# Add other costing fields
|
||
|
|
]
|
||
|
|
# Note: 'line_item' and 'product' often set programmatically.
|
||
|
|
|
||
|
|
class QuoteForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = Quote
|
||
|
|
fields = [
|
||
|
|
# 'document' is the PK, usually not directly editable here
|
||
|
|
'enquiry', 'currency', 'title', 'revision', 'delivery_time',
|
||
|
|
'payment_terms', 'days_valid', 'issue_date'
|
||
|
|
]
|
||
|
|
widgets = {
|
||
|
|
'issue_date': forms.DateInput(attrs={'type': 'date'}),
|
||
|
|
}
|
||
|
|
|
||
|
|
class InvoiceForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = Invoice
|
||
|
|
fields = [
|
||
|
|
# 'document' is PK
|
||
|
|
'enquiry', 'job', 'customer', 'currency', 'title',
|
||
|
|
'issue_date', 'due_date', 'paid', 'date_paid'
|
||
|
|
]
|
||
|
|
widgets = {
|
||
|
|
'issue_date': forms.DateInput(attrs={'type': 'date'}),
|
||
|
|
'due_date': forms.DateInput(attrs={'type': 'date'}),
|
||
|
|
'date_paid': forms.DateInput(attrs={'type': 'date'}),
|
||
|
|
}
|
||
|
|
|
||
|
|
class PurchaseOrderForm(forms.ModelForm):
|
||
|
|
jobs = forms.ModelMultipleChoiceField(
|
||
|
|
queryset=Job.objects.all(), # Filter as needed in view
|
||
|
|
widget=forms.CheckboxSelectMultiple,
|
||
|
|
required=False
|
||
|
|
)
|
||
|
|
class Meta:
|
||
|
|
model = PurchaseOrder
|
||
|
|
fields = [
|
||
|
|
# 'document' is PK
|
||
|
|
'principle', 'currency', 'title', 'issue_date', 'jobs'
|
||
|
|
]
|
||
|
|
widgets = {
|
||
|
|
'issue_date': forms.DateInput(attrs={'type': 'date'}),
|
||
|
|
}
|
||
|
|
|
||
|
|
class OrderAcknowledgementForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = OrderAcknowledgement
|
||
|
|
fields = [
|
||
|
|
# 'document' is PK
|
||
|
|
'enquiry', 'job', 'currency', 'title', 'revision',
|
||
|
|
'delivery_time', 'payment_terms', 'issue_date'
|
||
|
|
]
|
||
|
|
widgets = {
|
||
|
|
'issue_date': forms.DateInput(attrs={'type': 'date'}),
|
||
|
|
}
|
||
|
|
|
||
|
|
class PackingListForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = PackingList
|
||
|
|
fields = [
|
||
|
|
# 'document' is PK
|
||
|
|
'enquiry', 'job', 'customer', 'currency', 'title', 'issue_date'
|
||
|
|
]
|
||
|
|
widgets = {
|
||
|
|
'issue_date': forms.DateInput(attrs={'type': 'date'}),
|
||
|
|
}
|
||
|
|
|
||
|
|
# --- Email Related Forms (Less common for direct user input) ---
|
||
|
|
|
||
|
|
# Forms for Email, EmailRecipient, EmailAttachment are less likely needed
|
||
|
|
# as these are often populated programmatically (e.g., by an email processing script).
|
||
|
|
# If you need forms for manual linking or editing, create them similarly.
|
||
|
|
|
||
|
|
# --- Shipment Related Forms ---
|
||
|
|
|
||
|
|
class ShipmentForm(forms.ModelForm):
|
||
|
|
jobs = forms.ModelMultipleChoiceField(
|
||
|
|
queryset=Job.objects.all(), # Filter as needed
|
||
|
|
widget=forms.CheckboxSelectMultiple,
|
||
|
|
required=False
|
||
|
|
)
|
||
|
|
principles = forms.ModelMultipleChoiceField(
|
||
|
|
queryset=Principle.objects.all(),
|
||
|
|
widget=forms.CheckboxSelectMultiple,
|
||
|
|
required=False
|
||
|
|
)
|
||
|
|
purchase_orders = forms.ModelMultipleChoiceField(
|
||
|
|
queryset=PurchaseOrder.objects.all(), # Filter as needed
|
||
|
|
widget=forms.CheckboxSelectMultiple,
|
||
|
|
required=False
|
||
|
|
)
|
||
|
|
|
||
|
|
class Meta:
|
||
|
|
model = Shipment
|
||
|
|
fields = [
|
||
|
|
'title', 'freight_forwarder', 'freight_service', 'user',
|
||
|
|
'customer', 'address', 'airway_bill', 'ship_date',
|
||
|
|
'expected_delivery_date', 'actual_delivery_date',
|
||
|
|
'jobs', 'principles', 'purchase_orders'
|
||
|
|
]
|
||
|
|
widgets = {
|
||
|
|
'ship_date': forms.DateInput(attrs={'type': 'date'}),
|
||
|
|
'expected_delivery_date': forms.DateInput(attrs={'type': 'date'}),
|
||
|
|
'actual_delivery_date': forms.DateInput(attrs={'type': 'date'}),
|
||
|
|
}
|
||
|
|
# Add filtering for 'address' based on 'customer' in the view/init
|
||
|
|
|
||
|
|
class BoxForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = Box
|
||
|
|
fields = [
|
||
|
|
'shipment', 'box_number', 'length', 'width', 'height', 'weight'
|
||
|
|
]
|
||
|
|
# 'shipment' and 'box_number' often set programmatically in formsets
|
||
|
|
|
||
|
|
class ShipmentInvoiceForm(forms.ModelForm):
|
||
|
|
class Meta:
|
||
|
|
model = ShipmentInvoice
|
||
|
|
fields = [
|
||
|
|
'shipment', 'principle', 'freight_forwarder', 'currency',
|
||
|
|
'invoice_number', 'issue_date', 'amount', 'description'
|
||
|
|
]
|
||
|
|
widgets = {
|
||
|
|
'issue_date': forms.DateInput(attrs={'type': 'date'}),
|
||
|
|
'description': forms.TextInput(attrs={'placeholder': 'Optional description'}),
|
||
|
|
}
|