From 780eb1b35cce1d8b6a763c4f3ab596088bcd820a Mon Sep 17 00:00:00 2001 From: Marwan Alwali Date: Thu, 24 Apr 2025 17:24:20 +0300 Subject: [PATCH] update --- haikalbot/chatbot_logic.py | 30 + haikalbot/models.py | 18 + haikalbot/views.py | 12 + inventory/context_processors.py | 29 + inventory/filters.py | 17 +- inventory/forms.py | 595 ++++++ inventory/middleware.py | 36 + inventory/mixins.py | 20 +- inventory/models.py | 15 + inventory/services.py | 83 +- inventory/signals.py | 288 ++- inventory/tables.py | 39 + inventory/tests.py | 54 + inventory/utilities/sa.py | 11 + inventory/utils.py | 304 ++- inventory/views.py | 3157 ++++++++++++++++++++++++++++++- 16 files changed, 4684 insertions(+), 24 deletions(-) diff --git a/haikalbot/chatbot_logic.py b/haikalbot/chatbot_logic.py index a624046c..15390ce0 100644 --- a/haikalbot/chatbot_logic.py +++ b/haikalbot/chatbot_logic.py @@ -3,6 +3,20 @@ from inventory import models from car_inventory import settings def fetch_data(dealer): + """ + Fetches the total number of cars in the inventory for the specified dealer. If no cars are + found, returns a message indicating that fact. If an error occurs during the operation, + it returns an error message with details. + + :param dealer: The dealer object for which the inventory information is required. + The dealer object must be an instance of a model that includes a + `get_local_name` method for formatting localized dealer names. + :type dealer: Dealer + :return: A string indicating either the total number of cars in the dealer's inventory, + that no cars exist in the inventory, or an error message detailing what went + wrong during the operation. + :rtype: str + """ try: # Annotate total cars by make, model, and trim cars = models.Car.objects.filter(dealer=dealer).count() @@ -17,6 +31,22 @@ def fetch_data(dealer): def get_gpt4_response(user_input, dealer): + """ + Generates a response from the GPT-4 model based on the provided user input + and the dealer's information. The function is tailored to assist with car + inventory management, including queries about inventory, sales processes, + car transfers, invoices, and other features specific to the Haikal system. + + :param user_input: The text input or query provided by the user. + :type user_input: str + :param dealer: Dealer information or identifier used to fetch related car data + or contextual information. + :type dealer: Any + :return: The generated response from the GPT-4 model as a string. + :rtype: str + :raises Exception: In case of an error during the API call to generate the + GPT-4 response. + """ dealer = dealer client = OpenAI(api_key=settings.OPENAI_API_KEY) diff --git a/haikalbot/models.py b/haikalbot/models.py index fd45b56d..1853a4be 100644 --- a/haikalbot/models.py +++ b/haikalbot/models.py @@ -3,6 +3,24 @@ from inventory.models import Dealer class ChatLog(models.Model): + """ + Handles storage and representation of chat logs between users and chatbot. + + This class is designed to store chat logs, which include user messages, chatbot + responses, their associated dealer, and timestamps of when the chat entries are + created. It supports linking chat logs to a specific dealer using a foreign key + relationship. The class provides an easy and structured way to manage and retrieve + historical chat data. + + :ivar dealer: The dealer associated with this chat log. + :type dealer: Dealer + :ivar user_message: The message sent by the user. + :type user_message: str + :ivar chatbot_response: The response generated by the chatbot. + :type chatbot_response: str + :ivar timestamp: The date and time when the chat log entry was created. + :type timestamp: datetime + """ dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='chatlogs') user_message = models.TextField() chatbot_response = models.TextField() diff --git a/haikalbot/views.py b/haikalbot/views.py index 7a0af10a..9420f4a2 100644 --- a/haikalbot/views.py +++ b/haikalbot/views.py @@ -6,6 +6,18 @@ from .chatbot_logic import get_gpt4_response import json class ChatbotView(LoginRequiredMixin, View): + """ + Represents a view for handling chatbot interactions. + + This class handles GET and POST requests for a chatbot interface. It leverages + `LoginRequiredMixin` to ensure that only authenticated users can access it. On GET + requests, it renders the chatbot interface, while on POST requests, it interacts + with a chatbot backend to process user messages and return responses. + + :ivar request: The HTTP request object, providing metadata about the + current session and user. + :type request: HttpRequest + """ def get(self, request): return render(request, "haikalbot/chatbot.html") diff --git a/inventory/context_processors.py b/inventory/context_processors.py index 5c6a34a5..55cff5ef 100644 --- a/inventory/context_processors.py +++ b/inventory/context_processors.py @@ -1,12 +1,41 @@ from django.conf import settings def currency_context(request): + """ + Provides a context dictionary containing the currency setting. This is typically + used for rendering templates with the appropriate currency value. + + :param request: The HTTP request object. This parameter is not used within + the function but is included to maintain compatibility with + context processor signature. + :type request: HttpRequest + + :return: A dictionary with the key 'CURRENCY' containing the value of the + project's CURRENCY setting. + :rtype: dict + """ return { 'CURRENCY': settings.CURRENCY } def breadcrumbs(request): + """ + Generates a breadcrumb trail for the given request path. + + The function processes the request's path, splits it into individual + segments, and creates a list of breadcrumb entries, each representing + a segment of the path. Each breadcrumb entry consists of a 'name' + (key) and its associated 'url' (key). The 'name' is a title-cased + representation of the path segment, and the 'url' is the cumulative URL + path leading to that segment. + + :param request: The request object containing the path to be processed. + :type request: Any + :return: A dictionary containing a list of breadcrumbs, where each + breadcrumb is represented by a dictionary with 'name' and 'url'. + :rtype: dict + """ breadcrumbs = [] path = request.path.strip('/').split('/') for i in range(len(path)): diff --git a/inventory/filters.py b/inventory/filters.py index 0d8b95f5..8fdb0206 100644 --- a/inventory/filters.py +++ b/inventory/filters.py @@ -1,7 +1,22 @@ from django_ledger.models import AccountModel import django_filters -class AccountModelFilter(django_filters.FilterSet): +class AccountModelFilter(django_filters.FilterSet): + """ + Handles filtering functionality for the AccountModel. + + This class is a subclass of `django_filters.FilterSet` and is used to provide + filtering capabilities for the `AccountModel` based on its specified fields such as + `code`, `name`, and `role`. It is designed to integrate seamlessly with Django's + filtering framework to simplify query creation and management for this specific model. + + The Meta class inside defines the model and fields that can be used for filtering. + + :ivar model: The model for which filters are being applied. + :type model: type[AccountModel] + :ivar fields: List of fields defined in the model that can be filtered. + :type fields: list(str) + """ class Meta: model = AccountModel fields = ['code', 'name','role'] \ No newline at end of file diff --git a/inventory/forms.py b/inventory/forms.py index f0b01caf..571d76aa 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -71,6 +71,21 @@ User = get_user_model() class AdditionalServiceForm(forms.ModelForm): + """ + A form used for creating and updating instances of the + `AdditionalServices` model with fields for specifying + service details such as name, price, description, tax + applicability, and unit of measure. + + This form is designed to streamline input validation and + rendering for the `AdditionalServices` model. + + :ivar Meta.model: The model to associate with this form. + :type Meta.model: type + :ivar Meta.fields: List of fields in the `AdditionalServices` + model that this form includes. + :type Meta.fields: list + """ class Meta: model = AdditionalServices fields = ["name", "price", "description", "taxable", "uom"] @@ -84,6 +99,20 @@ class AdditionalServiceForm(forms.ModelForm): class StaffForm(forms.ModelForm): + """ + Represents a form for managing Staff entities, including associated user email updates + and service offerings. + + This class defines a Django ModelForm for the `Staff` model, providing fields + to manage staff details such as name, Arabic name, phone number, staff type, and email. + It allows selection of multiple services offered by staff through a multiple choice + field. The form integrates email management as an additional feature. + + :ivar email: Email address of the user associated with the staff. + :type email: forms.EmailField + :ivar service_offered: Collection of services offered by the staff. + :type service_offered: forms.ModelMultipleChoiceField + """ email = forms.EmailField( required=True, label="Email", @@ -118,6 +147,28 @@ class StaffForm(forms.ModelForm): # Dealer Form class DealerForm(forms.ModelForm): + """ + Represents a Django ModelForm specifically for the `Dealer` model. + + Designed to facilitate the creation, update, and validation of `Dealer` model + instances through form-based data handling. This form exposes specific + fields of the `Dealer` model for interaction. + + :ivar name: Dealer's name. + :type name: str + :ivar arabic_name: Dealer's name in Arabic. + :type arabic_name: str + :ivar crn: Commercial Registration Number for the dealer. + :type crn: str + :ivar vrn: Value-added Tax Registration Number for the dealer. + :type vrn: str + :ivar phone_number: Contact phone number of the dealer. + :type phone_number: str + :ivar address: Physical address of the dealer. + :type address: str + :ivar logo: Logo of the dealer. + :type logo: File + """ class Meta: model = Dealer fields = [ @@ -132,6 +183,37 @@ class DealerForm(forms.ModelForm): class CustomerForm(forms.Form): + """ + Represents a form for collecting customer information. + + This form is used to gather and validate customer details such as name, + email, phone number, national ID, tax registration details, and address. + It includes several fields with validation constraints and specific + requirements. It is designed to handle both required and optional fields, + ensuring the correctness of user inputs. + + :ivar first_name: Customer's first name. + :type first_name: forms.CharField + :ivar last_name: Customer's last name. + :type last_name: forms.CharField + :ivar arabic_name: Customer's name in Arabic. + :type arabic_name: forms.CharField + :ivar email: Customer's email address. + :type email: forms.EmailField + :ivar phone_number: Customer's phone number. Validates the format and + ensures the number is in the Saudi Arabia region. + :type phone_number: PhoneNumberField + :ivar national_id: Customer's national ID. Optional field limited to + a maximum length of 10 characters. + :type national_id: forms.CharField + :ivar crn: Commercial registration number (CRN) of the customer. Optional field. + :type crn: forms.CharField + :ivar vrn: Value-added tax registration number (VRN) of the customer. + Optional field. + :type vrn: forms.CharField + :ivar address: Customer's address. + :type address: forms.CharField + """ first_name = forms.CharField() last_name = forms.CharField() arabic_name = forms.CharField() @@ -158,6 +240,20 @@ class CustomerForm(forms.Form): class OrganizationForm(CustomerForm): + """ + Represents a form for collecting and handling organization-specific details. + + This form extends the `CustomerForm` class and is tailored for organizations, + providing fields for optional input such as the contact person and organization + logo. The form can be used to capture all relevant data pertaining to the + organization during interactions. + + :ivar contact_person: Optional field to specify the name of the contact person + for the organization. + :type contact_person: forms.CharField + :ivar logo: Optional field to upload the logo of the organization. + :type logo: forms.ImageField + """ contact_person = forms.CharField(required=False) logo = forms.ImageField(required=False) @@ -188,6 +284,21 @@ class CarForm( forms.ModelForm, AddClassMixin, ): + """ + A form class for creating and updating `Car` model instances. + + This class extends `forms.ModelForm` and `AddClassMixin` to customize + form fields for the `Car` model. It manages custom field configurations, + such as filtering queryset and setting widgets, and is designed to be used + for handling the form representation of the `Car` model within a user interface. + + :ivar Meta.model: Specifies the model associated with this form. + :type Meta.model: Car + :ivar Meta.fields: Specifies the fields included in the form. + :type Meta.fields: list + :ivar Meta.widgets: Defines the widgets for specific fields. + :type Meta.widgets: dict + """ class Meta: model = Car fields = [ @@ -234,6 +345,23 @@ class CarForm( class CarUpdateForm(forms.ModelForm, AddClassMixin): + """ + Form class used to update Car instances through a Django ModelForm. This class + extends `forms.ModelForm` and applies custom widgets and field configurations + to support specific requirements for the Car model. + + The form is intended for managing Car-related information, allowing inputs + for vendor details, status, stock type, mileage, receiving date, and remarks. + It also provides customization options for field configurations through its + initialization method. + + :ivar Meta.model: The model associated with the form, which is `Car`. + :ivar Meta.fields: Fields of the `Car` model included in the form: "vendor", + "status", "stock_type", "mileage", "receiving_date", "remarks". + :ivar Meta.widgets: Custom widgets for specific fields. The "receiving_date" field + uses a `DateTimeInput` configured with `datetime-local` attributes, while + "remarks" uses a `Textarea` with a specific row configuration. + """ class Meta: model = Car fields = [ @@ -269,6 +397,17 @@ class CarUpdateForm(forms.ModelForm, AddClassMixin): class CarFinanceForm(forms.ModelForm): + """ + Handles the form logic for car finance, including processing + additional financial services selected through the UI. + + This class is used for creating or modifying instances of the CarFinance model. + It provides a mechanism to associate additional financial services with a car + finance application through a multiple choice selection. + + :ivar additional_finances: A field that allows selecting multiple + additional services associated with a car finance application. + """ additional_finances = forms.ModelMultipleChoiceField( queryset=AdditionalServices.objects.all(), widget=forms.CheckboxSelectMultiple(attrs={"class": "form-check-input"}), @@ -300,6 +439,17 @@ class CarFinanceForm(forms.ModelForm): class CarLocationForm(forms.ModelForm): + """ + Represents a Django ModelForm for managing CarLocation instances. + + This form is designed to create or update `CarLocation` model objects. + It provides a user-friendly interface for inputting data, including custom + widget configurations for the `description` field. + + :ivar Meta: Contains metadata for the form, including the associated model, + specified fields, and custom widget configurations. + :type Meta: class + """ class Meta: model = CarLocation fields = ["showroom", "description"] @@ -309,6 +459,20 @@ class CarLocationForm(forms.ModelForm): class CarTransferForm(forms.ModelForm): + """ + Represents a form for transferring a car to a dealer. + + This form is used to facilitate the transfer of a car from one entity + to a dealer. It extends the ModelForm framework to provide functionality + specific to car transfer operations. + + :ivar model: The model associated with this form. + :type model: Type[CarTransfer] + :ivar fields: The fields included in the form. + :type fields: list[str] + :ivar widgets: Custom widgets applied to the form fields. + :type widgets: dict[str, Any] + """ class Meta: model = CarTransfer fields = ["car", "to_dealer", "remarks"] @@ -319,6 +483,20 @@ class CarTransferForm(forms.ModelForm): # Custom Card Form class CustomCardForm(forms.ModelForm): + """ + Representation of a custom card form used for collecting and validating custom card data. + + This form is a subclass of ``forms.ModelForm`` and is specifically tailored for + the ``CustomCard`` model. It provides a user-friendly interface by predefining + the fields and allowing customization of widgets and labels for seamless user + interaction. The form integrates with Django's model validation system to ensure + data integrity before saving to the database. + + :ivar custom_date: The custom date field that accepts user input for a date + value. This field uses a custom widget to render a date input type and + includes a specific label for localized presentation. + :type custom_date: forms.DateTimeField + """ custom_date = forms.DateTimeField( widget=forms.DateInput(attrs={"type": "date"}), label=_("Custom Date"), @@ -331,6 +509,23 @@ class CustomCardForm(forms.ModelForm): # Car Registration Form class CarRegistrationForm(forms.ModelForm): + """ + Represents a Django form for car registration. + + This class is a Django ModelForm specifically designed to handle car + registration data. It is linked to the `CarRegistration` model and + provides fields for entering or managing a license plate number, + additional textual fields, and the registration date. This form also + customizes the widget for the `registration_date` field to use a + datetime-local input. + + :ivar Meta.model: The `CarRegistration` model to associate with this form. + :type Meta.model: Model + :ivar Meta.fields: List of fields to include in the form. + :type Meta.fields: list + :ivar Meta.widgets: Custom widget configurations for specific fields. + :type Meta.widgets: dict + """ class Meta: model = CarRegistration fields = ["plate_number", "text1", "text2", "text3", "registration_date"] @@ -343,6 +538,16 @@ class CarRegistrationForm(forms.ModelForm): # class VendorForm(VendorModelForm): # pass class VendorForm(forms.ModelForm): + """ + Representation of a form used for Vendor model interactions. + + This class extends the functionality of Django's ModelForm to facilitate + CRUD operations for the Vendor model. It includes validation and mapping of + fields defined in the Vendor model to their corresponding form fields. + + :ivar Meta: Inner class to define metadata for the Vendor form. + :type Meta: Type[VendorForm.Meta] + """ class Meta: model = Vendor fields = [ @@ -359,6 +564,21 @@ class VendorForm(forms.ModelForm): class CarColorsForm(forms.ModelForm): + """ + Handles the dynamic customization and validation of color selection forms + for car exterior and interior. + + This class provides a mechanism to dynamically populate querysets and choices + for exterior and interior color fields in the form, as well as enforce + business rules such as ensuring that the user selects values for both + exterior and interior colors. + + :ivar Meta.model: The model associated with this form, which is ``CarColors``. + :type Meta.model: Model + :ivar Meta.fields: List of form fields that correspond to model attributes + to include in the form, namely ``exterior`` and ``interior``. + :type Meta.fields: list + """ class Meta: model = CarColors fields = ["exterior", "interior"] @@ -435,6 +655,20 @@ class CarColorsForm(forms.ModelForm): class RepresentativeForm(forms.ModelForm): + """ + A form for creating or updating instances of the Representative model. + + The RepresentativeForm class inherits from Django's ModelForm and is customized + to work with the Representative model. This class initializes a form instance + with fields relevant to a representative, including their name, contact details, + and associated organization. It supports dynamic customization by passing additional + parameters during initialization. + + :ivar Meta.model: The Django model associated with this form. + :type Meta.model: type + :ivar Meta.fields: The fields from the model to include in the form. + :type Meta.fields: list of str + """ class Meta: model = Representative fields = [ @@ -452,6 +686,20 @@ class RepresentativeForm(forms.ModelForm): class CarSelectionTable(tables.Table): + """ + Creates a table for car selection using Django Tables2. + + This class is used to generate a table with a checkbox column for selecting cars. + It leverages Django Tables2 functionality to display table rows corresponding to + cars, along with metadata for VIN, year, car make, and car model. The table is + rendered using the Bootstrap 4 template for consistent styling. + + :ivar select: Checkbox column for selecting a car, linked to the primary key. + :type select: CheckBoxColumn + :ivar Meta: Metadata containing configuration for the table, including the + model, fields, and template. + :type Meta: django_tables2.tables.Table.Meta + """ select = tables.CheckBoxColumn(accessor="pk", orderable=False) class Meta: @@ -461,6 +709,31 @@ class CarSelectionTable(tables.Table): class WizardForm1(forms.Form): + """ + Represents a form used for the first step of a wizard validation process. + + This class is a Django form designed for a multi-step process and includes + fields for collecting user information such as email, password, and consent + to terms. It provides validation for fields, such as confirming password + matching and preventing duplicate email addresses. The form incorporates user + interaction attributes for frontend handling during the wizard workflow. + + :ivar hx_attrs: Default attributes for htmx library operations, such as\ + defining the request behavior and target elements. + :type hx_attrs: dict + :ivar email: Field for entering the user's email address. Includes validation\ + to ensure uniqueness of the email. + :type email: django.forms.EmailField + :ivar password: Field for entering a password with a minimum length of 8 and\ + appropriate input styling. + :type password: django.forms.CharField + :ivar confirm_password: Field for confirming the password matches the entry\ + in the password field. + :type confirm_password: django.forms.CharField + :ivar terms: Field representing acceptance of the terms and privacy policy.\ + This is a required checkbox input. + :type terms: django.forms.BooleanField + """ hx_attrs = { "hx-post":"", "hx-target": "#wizardValidationForm1", @@ -551,6 +824,27 @@ class WizardForm1(forms.Form): class WizardForm2(forms.Form): + """ + Form for capturing specific user details, including name, Arabic name, + and a phone number. + + This form is primarily designed to capture user information in both + English and Arabic formats along with ensuring the phone number follows + a valid format specifically for the region it is intended for. It makes + use of form controls and placeholder attributes for user-friendly input + handling. + + :ivar name: English name of the user. It is a required field and provides + user-friendly input placeholders and custom error messages. + :type name: forms.CharField + :ivar arabic_name: Arabic name of the user. It is a required field with + user-friendly placeholders and custom error messages. + :type arabic_name: forms.CharField + :ivar phone_number: Contact phone number of the user. Validation is + applied to ensure format compliance, with custom error handling + and region-specific configuration. + :type phone_number: PhoneNumberField + """ name = forms.CharField( label=_("Name"), widget=forms.TextInput( @@ -596,6 +890,24 @@ class WizardForm2(forms.Form): class WizardForm3(forms.Form): + """ + Form for collecting specific business-related information. + + This class defines a Django form for collecting business information, particularly + the Commercial Registration Number (CRN), VAT Registration Number (VRN), and the address. + Each field has custom validation rules, user-friendly error messages, and associated + widgets for user input. + + :ivar crn: Field for the Commercial Registration Number (CRN), with a maximum + length of 10 characters. This field is required. + :type crn: django.forms.fields.CharField + :ivar vrn: Field for the VAT Registration Number (VRN), with a maximum + length of 15 characters. This field is required. + :type vrn: django.forms.fields.CharField + :ivar address: Field for the address input, supporting multi-line input, + with validation enforcing the field to be required. + :type address: django.forms.fields.CharField + """ # CRN field with max length of 10 crn = forms.CharField( label=_("CRN"), @@ -658,6 +970,20 @@ class WizardForm3(forms.Form): class ItemForm(forms.Form): + """ + A form for handling item-related inputs in the application. + + This form is used to manage user-provided data for items, including + the selection of an item and its quantity. It enforces specific rules, + such as a minimum length for the item and requires certain inputs. + + :ivar item: A field representing the selection of an item from a + predefined queryset. Subject to a minimum length validator. + :type item: ModelChoiceField + :ivar quantity: A field for specifying the desired quantity of the + selected item. + :type quantity: DecimalField + """ item = forms.ModelChoiceField( queryset=ledger_models.ItemModel.objects.all(), label="Item", @@ -671,6 +997,25 @@ class ItemForm(forms.Form): class PaymentForm(forms.Form): + """ + Form for handling payment-related data submission and validation. + + This form is designed to manage various payment-related fields, including + invoice and bill associations, payment amount and method, and payment date. + It validates the payment amount against the outstanding dues of the specified + invoice or bill and ensures logical correctness for the provided data. + + :ivar invoice: Reference to an optional invoice for the payment. + :type invoice: ModelChoiceField + :ivar bill: Reference to an optional bill for the payment. + :type bill: ModelChoiceField + :ivar amount: The payment amount being submitted. + :type amount: DecimalField + :ivar payment_method: The method chosen to process the payment. + :type payment_method: ChoiceField + :ivar payment_date: The date on which the payment is made. + :type payment_date: DateField + """ invoice = forms.ModelChoiceField( queryset=ledger_models.InvoiceModel.objects.all(), label="Invoice", @@ -712,6 +1057,24 @@ class PaymentForm(forms.Form): class EmailForm(forms.Form): + """ + Represents an email form for collecting and validating email data. + + This class is designed to handle input related to email communication, + including subject, message body, sender email, and recipient email. It + validates the provided input to ensure they adhere to the specified + constraints. This form is typically used in applications where users + need to send emails through a web interface. + + :ivar subject: The subject line of the email. + :type subject: forms.CharField + :ivar message: The main body content of the email. + :type message: forms.CharField + :ivar from_email: The sender's email address. + :type from_email: forms.EmailField + :ivar to_email: The recipient's email address. + :type to_email: forms.EmailField + """ subject = forms.CharField(max_length=255) message = forms.CharField(widget=forms.Textarea) from_email = forms.EmailField() @@ -719,6 +1082,21 @@ class EmailForm(forms.Form): class LeadForm(forms.ModelForm): + """ + Form class for capturing lead details. + + This class is a Django ModelForm specifically designed for the `Lead` model. + It extends the capabilities of a standard Django form by providing customized + handling for fields related to car make and model. The `id_car_make` field includes + integration with htmx for dynamic model selections. + + :ivar id_car_make: A dropdown field for selecting a car make. This field is + dynamically updated and restricts the queryset to only South African imports. + :type id_car_make: ModelChoiceField + :ivar id_car_model: A dropdown field for selecting a car model. Initially, no + options are displayed until a car make is selected. + :type id_car_model: ModelChoiceField + """ id_car_make = forms.ModelChoiceField( label="Make", queryset=CarMake.objects.filter(is_sa_import=True), @@ -774,6 +1152,18 @@ class LeadForm(forms.ModelForm): class ScheduleForm(forms.ModelForm): + """ + Represents a form for scheduling events, extending ModelForm to bind to the + Schedule model. + + Provides a user interface to create or edit schedule entries with fields for + purpose, type, date/time of scheduling, duration, and additional notes. It + utilizes a custom widget for scheduling date and time in a localized format. + + :ivar scheduled_at: Field to input date and time for scheduling, using a custom + 'datetime-local' HTML widget. + :type scheduled_at: DateTimeField + """ scheduled_at = forms.DateTimeField( widget=DateTimeInput(attrs={"type": "datetime-local"}) ) @@ -783,24 +1173,75 @@ class ScheduleForm(forms.ModelForm): class NoteForm(forms.ModelForm): + """ + Represents a form for creating or editing notes. + + This class is a Django ModelForm for the Notes model. It provides validation + and rendering functionality for handling note data based on the specified + fields. It is used to simplify the creation and management of note-related + forms in Django applications. + + :ivar Meta.model: The model associated with the ModelForm. + :type Meta.model: type + :ivar Meta.fields: The fields to include in the form. + :type Meta.fields: list + """ class Meta: model = Notes fields = ["note"] class ActivityForm(forms.ModelForm): + """ + Represents a form for the Activity model, allowing users to submit or update + data related to an activity instance. + + This form is a subclass of Django's ModelForm, pre-configured to handle + instances of the `Activity` model. It maps to the "activity_type" and "notes" + fields of the model and provides validation and data-binding functionality. + + :ivar Meta: Inner class that defines metadata for the form, including the model + associated with the form and the fields it comprises. + :type Meta: type + """ class Meta: model = Activity fields = ["activity_type", "notes"] class OpportunityForm(forms.ModelForm): + """ + Represents a form for creating or editing Opportunity instances. + + This class is a Django ModelForm designed to simplify the process of + validating and persisting data for Opportunity model instances. It + maps fields from the Opportunity model to form fields, making it + convenient to handle user input for operations such as creating and + updating opportunities. + + :ivar Meta.model: The model associated with the form. + :type Meta.model: type + :ivar Meta.fields: List of fields from the model included in the form. + :type Meta.fields: list + """ class Meta: model = Opportunity fields = ["customer", "car", "stage", "probability", "staff", "closing_date"] class InvoiceModelCreateForm(InvoiceModelCreateFormBase): + """ + Represents a form for creating an Invoice model that inherits from a base + InvoiceModelCreateFormBase. + + This form is intended for handling the creation of invoices with additional + field customizations. It modifies specific field widgets and defines custom + queryset logic for a customer field. + + :ivar fields: A dictionary containing the form fields where keys represent + the field names and values are their respective Field objects. + :type fields: dict + """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -819,6 +1260,18 @@ class InvoiceModelCreateForm(InvoiceModelCreateFormBase): class BillModelCreateForm(BillModelCreateFormBase): + """ + This class represents a form for creating a BillModel, inheriting from + `BillModelCreateFormBase`. + + It customizes the form fields by hiding certain account-related input fields, + such as `cash_account`, `prepaid_account`, and `unearned_account`. This form + is intended for specific use cases where these accounts should not be visible + or interactable by the user. + + :ivar fields: A dictionary of all fields used in the form. + :type fields: dict + """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -831,6 +1284,22 @@ class BillModelCreateForm(BillModelCreateFormBase): class SaleOrderForm(forms.ModelForm): + """ + Represents a form for creating or updating sales orders. + + This class is a Django ModelForm designed for handling the form + representation of the SaleOrder model. It defines specific fields + and their associated widgets to customize the input representation. + + :ivar Meta.model: The model associated with this form. It is required + for linking the form to the SaleOrder model. + :type Meta.model: Type[models.Model] + :ivar Meta.fields: List of fields from the model to be used in the form. + :type Meta.fields: List[str] + :ivar Meta.widgets: Dictionary defining specific widgets to be applied + for fields, such as customizing their appearance. + :type Meta.widgets: Dict[str, Any] + """ class Meta: model = SaleOrder fields = ["estimate", "payment_method", "comments"] @@ -840,6 +1309,32 @@ class SaleOrderForm(forms.ModelForm): class EstimateModelCreateForm(EstimateModelCreateFormBase): + """ + Defines the EstimateModelCreateForm class, which is used to create and manage + forms for EstimateModel. This form handles the rendering and validation + of specific fields, their input widgets, and labels. + + The purpose of this class is to provide a structured way to handle + EstimateModel instances and their related fields, ensuring a user-friendly + form interface with proper field configuration. It facilitates fetching + related data, such as customer queries, based on user-specific parameters. + + :ivar ENTITY_SLUG: A string that represents the entity context in which the + form operates. + :type ENTITY_SLUG: str + :ivar USER_MODEL: The user model that provides methods and objects needed + to filter and query customers. + :type USER_MODEL: Any + :ivar fields: A dictionary representing fields included in the form such as + "title", "customer", and "terms". + :type fields: dict + :ivar widgets: A dictionary defining custom input widgets for form fields, + enabling specific attributes like classes and identifiers for styling or + functionality purposes. + :type widgets: dict + :ivar labels: A dictionary specifying the human-readable labels for form fields. + :type labels: dict + """ class Meta: model = ledger_models.EstimateModel fields = ["title","customer", "terms"] @@ -881,6 +1376,23 @@ class EstimateModelCreateForm(EstimateModelCreateFormBase): class OpportunityStatusForm(forms.Form): + """ + Form used to handle and manage the opportunity status and stage selection. + + This form allows users to select and update the status and stage of an + opportunity. It provides fields with pre-defined choices and custom widgets + for enhanced interactivity within the user interface. + + :ivar status: The selected status of the opportunity, which is managed by a + choice field. It contains a set of predefined options for the status and + applies a custom widget with additional attributes for UI and action + handling. + :type status: ChoiceField + :ivar stage: The selected stage of the opportunity, which is managed by a + choice field. It includes predefined stage options and utilizes a custom + widget configured with attributes to handle UI updates dynamically. + :type stage: ChoiceField + """ status = forms.ChoiceField( label="Status", choices=Status.choices, @@ -914,11 +1426,36 @@ class OpportunityStatusForm(forms.Form): ) class GroupForm(forms.ModelForm): + """ + A form for creating and updating CustomGroup objects. + + This form is used to handle the creation and modification of CustomGroup instances. + It is based on Django's ModelForm framework, which provides validation and transformation + of form data into a model instance. The form is designed to manage only the `name` field + of the `CustomGroup` model. + + :ivar model: The model with which the form is associated, representing the `CustomGroup`. + :type model: Type[CustomGroup] + :ivar fields: List of fields to include in the form. In this case, only the `name` field. + :type fields: List[str] + """ class Meta: model = CustomGroup fields = ["name"] class PermissionForm(forms.ModelForm): + """ + Represents a form for managing permissions using a multiple-choice field. + + This class is a Django ModelForm that is used to handle permission + assignments. It provides a multiple selection widget pre-populated with + permissions based on specific app labels. The form offers a way to submit + and validate permission data for further processing. + + :ivar name: A multiple-choice field that allows users to select permissions + related to specific app labels (`inventory` and `django_ledger`). + :type name: ModelMultipleChoiceField + """ name = forms.ModelMultipleChoiceField( queryset=cache.get('permissions_queryset', Permission.objects.filter(content_type__app_label__in=["inventory","django_ledger"])), widget=forms.CheckboxSelectMultiple(), @@ -933,6 +1470,17 @@ class PermissionForm(forms.ModelForm): fields = ["name"] class UserGroupForm(forms.ModelForm): + """ + Represents a form for selecting user groups. + + This class is a Django ModelForm that is used to facilitate the selection of + multiple user groups. It utilizes a checkbox input for group selection, backed + by a queryset of CustomGroup instances. This form is specifically designed to + handle input related to CustomGroup model objects. + + :ivar name: A field for selecting multiple groups using checkboxes. + :type name: ModelMultipleChoiceField + """ name = forms.ModelMultipleChoiceField( queryset= CustomGroup.objects.all(), widget=forms.CheckboxSelectMultiple(), @@ -943,15 +1491,50 @@ class UserGroupForm(forms.ModelForm): fields = ["name"] class DealerSettingsForm(forms.ModelForm): + """ + Represents a form for managing DealerSettings. + + This class provides a form for creating or updating `DealerSettings` + instances. It automatically includes all fields defined in the + `DealerSettings` model and can be used within a Django web application. + + :ivar model: Associated model for the form. + :type model: DealerSettings + :ivar fields: Fields to include in the form. The value "__all__" indicates + that all model fields should be included. + :type fields: str + """ class Meta: model = DealerSettings fields = "__all__" class LeadTransferForm(forms.Form): + """ + Represents a form for transferring leads between staff members. + + This class defines a Django form used for handling the transfer of leads + to a specific staff member. It provides a mechanism to select the staff + member to whom the lead should be transferred by offering a dropdown + field populated by a queryset of staff objects. + + :ivar transfer_to: Field for selecting the staff to transfer the lead to. + :type transfer_to: ModelChoiceField + """ transfer_to = forms.ModelChoiceField(label="to",queryset=Staff.objects.all()) class DealersMakeForm(forms.Form): + """ + Form for selecting car makes related to a dealer. + + This form allows dealers to select multiple car makes from a predefined + set of car makes that are marked as suitable for SA import. The selected + car makes can then be tied to the dealer. + + :ivar dealer: The dealer instance associated with the form. This instance + is used to filter and save car makes for a specific dealer. + :type dealer: Dealer or None + """ car_makes = forms.ModelMultipleChoiceField( queryset=CarMake.objects.filter(is_sa_import=True), widget=forms.CheckboxSelectMultiple(attrs={"class": "car-makes-grid"}), @@ -972,6 +1555,18 @@ class DealersMakeForm(forms.Form): class JournalEntryModelCreateForm(JournalEntryModelCreateFormBase): + """ + Represents a form model for creating journal entries. + + This class serves as a base for creating a form model specifically designed + for creating new journal entries. It inherits properties and functionalities + from `JournalEntryModelCreateFormBase`. + + :ivar foo: Description of foo attribute. + :type foo: str + :ivar bar: Description of bar attribute. + :type bar: int + """ pass # class LedgerModelCreateForm(LedgerModelCreateFormBase): diff --git a/inventory/middleware.py b/inventory/middleware.py index 7fd90f7b..4e65046a 100644 --- a/inventory/middleware.py +++ b/inventory/middleware.py @@ -14,6 +14,18 @@ logger = logging.getLogger('user_activity') class LogUserActivityMiddleware: + """ + Middleware for logging user activity. + + This middleware logs the activity of authenticated users each time they make a + request. It creates an entry in the UserActivityLog model capturing the user's + ID, the action performed, and the timestamp. It is intended to assist in + tracking user actions across the application for analytics or auditing purposes. + + :ivar get_response: The next middleware or view in the WSGI request-response + chain. + :type get_response: Callable + """ def __init__(self, get_response): self.get_response = get_response @@ -37,6 +49,17 @@ class LogUserActivityMiddleware: class InjectParamsMiddleware: + """ + Middleware to add processed user-related parameters to the request object. + + This middleware processes incoming requests to extract and enhance user + information, specifically linking user context such as `dealer` to the + request. It allows subsequent views and middlewares to access these enriched + request parameters with ease. + + :ivar get_response: The callable to get the next middleware or view response. + :type get_response: Callable + """ def __init__(self, get_response): self.get_response = get_response @@ -51,6 +74,19 @@ class InjectParamsMiddleware: class InjectDealerMiddleware: + """ + Middleware to inject user role attributes into the request object. + + This middleware assigns boolean attributes to the request object to indicate + whether the user is a dealer or a staff member. It checks for the presence of + specific user attributes (`dealer` and `staffmember`) and sets corresponding + flags accordingly. The middleware is designed to support role-based processing + in requests. + + :ivar get_response: The callable provided by the Django framework + to process the next middleware or the view in the request-response cycle. + :type get_response: Callable + """ def __init__(self, get_response): self.get_response = get_response diff --git a/inventory/mixins.py b/inventory/mixins.py index 95c215a8..953e7940 100644 --- a/inventory/mixins.py +++ b/inventory/mixins.py @@ -4,7 +4,13 @@ from django.urls import reverse, reverse_lazy class AddClassMixin: """ - Mixin for adding classes to a model. + Provides functionality for automatically adding CSS classes to form field widgets. + + This mixin is intended to be used in Django forms. It modifies the rendering of form fields + by appending specific CSS classes to their widget attributes, enhancing their default styling. + It distinguishes between fields with ``forms.Select`` widgets and other types of widgets to + apply different CSS classes. + """ def add_class_to_fields(self): """ @@ -22,7 +28,17 @@ class AddClassMixin: class LocalizedNameMixin: """ - Mixin to provide a reusable get_localized_name method. + Provides functionality to retrieve localized names based on the current language. + + This mixin is intended to provide language-specific name access for objects + that support localization. It checks the current language setting and retrieves + the appropriate name attribute (`arabic_name` for Arabic or `name` for other + languages). + + :ivar arabic_name: Localized name for the Arabic language. + :type arabic_name: Optional[str] + :ivar name: Default name used for non-Arabic languages. + :type name: Optional[str] """ def get_local_name(self): """ diff --git a/inventory/models.py b/inventory/models.py index c248bc97..71e0c161 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -55,6 +55,21 @@ class DealerUserManager(UserManager): class DealersMake(models.Model): + """ + Represents the relationship between a car dealer and a car make. + + This model establishes a many-to-many relationship between dealers and + car makes, allowing each dealer to be associated with multiple car makes + and each car make to be associated with multiple dealers. It also keeps + track of the date and time when the relationship was added. + + :ivar dealer: The dealer associated with the car make. + :type dealer: ForeignKey + :ivar car_make: The car make associated with the dealer. + :type car_make: ForeignKey + :ivar added_at: The date and time when the relationship was created. + :type added_at: DateTimeField + """ dealer = models.ForeignKey("Dealer", on_delete=models.CASCADE, related_name="dealer_makes") car_make = models.ForeignKey("CarMake", on_delete=models.CASCADE, related_name="car_dealers") added_at = models.DateTimeField(auto_now_add=True) diff --git a/inventory/services.py b/inventory/services.py index 8023cce4..099e0722 100644 --- a/inventory/services.py +++ b/inventory/services.py @@ -15,7 +15,18 @@ from .models import Car,CarMake,CarModel from inventory.haikalna import decode_vin_haikalna -def get_make(item): +def get_make(item): + """ + Fetches a car make object from the database based on the provided item. The function + first attempts an exact case-insensitive match on the full string. If no match is found, + it splits the input string by spaces and checks for a match with each fragment, stopping + as soon as a match is found. + + :param item: A string representing the name or a part of the name of the car make to search. + :type item: str + :return: The first matching CarMake object found, or None if no match is found. + :rtype: CarMake or None + """ data = CarMake.objects.filter(name__iexact=item).first() if not data: r = item.split(" ") @@ -24,7 +35,18 @@ def get_make(item): break return data -def get_model(item,make): +def get_model(item,make): + """ + Searches for a car model associated with a specific make by performing an + exact case-insensitive match. If no match is found, it attempts to match + based on individual words split from the input item. + + :param item: A string representing the name of the car model to search for. + :param make: An object representing the car manufacturer, which contains the + associated car models. + :return: Returns the first car model object that matches the search criteria, + or None if no match is found. + """ data = make.carmodel_set.filter(name__iexact=item).first() if not data: r = item.split(" ") @@ -34,11 +56,34 @@ def get_model(item,make): return data def normalize_name(name): + """ + Normalizes a given name by removing spaces and hyphens and converting it to lowercase. + + This function is useful for ensuring consistent formatting of names by + removing unwanted characters and applying a uniform casing. The output + is designed to be a simple, standardized string version of the input name. + + :param name: The name string to be normalized. + :type name: str + :return: A normalized string with no spaces or hyphens, all in lowercase. + :rtype: str + """ return name.replace(" ", "").replace("-", "").lower() def decodevin(vin): - + """ + Decodes a Vehicle Identification Number (VIN) using multiple decoding functions + and returns the decoded result. This function attempts to decode the VIN using + three different functions in sequence and returns the first successful result. + If none of the decoding functions produce a valid result, the function returns None. + + :param vin: The Vehicle Identification Number (VIN) to be decoded. + :type vin: str + :return: The decoded result if any decoding function is successful, or None if + all decoding attempts fail. + :rtype: dict | None + """ if result:=decode_vin(vin): return result elif result:=elm(vin): @@ -50,6 +95,21 @@ def decodevin(vin): def decode_vin(vin): + """ + Decodes a given Vehicle Identification Number (VIN) to extract vehicle details such as + maker, model, and model year. + + This function takes a VIN string as input, processes it using the VIN utility, and attempts + to extract essential information about the vehicle. It returns a dictionary containing the + decoded details if all required fields are successfully retrieved; otherwise, it returns None. + + :param vin: The Vehicle Identification Number to be decoded. + :type vin: str + + :return: A dictionary containing decoded vehicle details with keys "maker", "model", and + "modelYear", or None if any of these fields cannot be determined. + :rtype: dict | None + """ v = VIN(vin) data = {} if v: @@ -63,6 +123,23 @@ def decode_vin(vin): def elm(vin): + """ + Fetches vehicle information using the provided VIN (Vehicle Identification Number) + through the ELM API service. + + This function sends a GET request to the ELM API endpoint with the VIN parameter + to retrieve details about a vehicle, including its maker, model, and model year. + The function processes the API's JSON response to extract relevant data and + returns it if all required fields are available. + + :param vin: Vehicle Identification Number required to query the ELM API + service for vehicle information. + :type vin: str + + :return: A dictionary containing the vehicle's `maker`, `model`, and `modelYear` + if all fields are present. Returns None if any of these fields are missing. + :rtype: dict or None + """ headers = { "Content-Type": "application/json", "app-id": settings.ELM_APP_ID, diff --git a/inventory/signals.py b/inventory/signals.py index 1ceb051b..a64da336 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -69,7 +69,19 @@ User = get_user_model() @receiver(post_save, sender=models.Car) def create_car_location(sender, instance, created, **kwargs): """ - Signal to create or update the car's location when a car instance is saved. + Signal handler to create a CarLocation entry when a new Car instance is created. + The function ensures that the associated Car has a dealer before creating its + CarLocation. If the dealer is missing, a ValueError is raised, and an error + message is logged. + + :param sender: The model class that sends the signal, typically `models.Car`. + :type sender: Type[models.Model] + :param instance: The actual instance of the Car model that triggered the signal. + :param created: A boolean value indicating whether a new record was created. + :type created: bool + :param kwargs: Additional keyword arguments provided with the signal. + :type kwargs: dict + :return: None """ try: if created: @@ -91,6 +103,25 @@ def create_car_location(sender, instance, created, **kwargs): # Create Entity @receiver(post_save, sender=models.Dealer) def create_ledger_entity(sender, instance, created, **kwargs): + """ + Signal handler for creating ledger entities and initializing accounts for a new Dealer instance upon creation. + + This signal is triggered when a new Dealer instance is saved to the database. It performs the following actions: + 1. Creates a ledger entity for the Dealer with necessary configurations. + 2. Generates a chart of accounts (COA) for the entity and assigns it as the default. + 3. Creates predefined unit of measures (UOMs) related to the entity. + 4. Initializes and assigns default accounts under various roles (e.g., assets, liabilities) for the entity with their + respective configurations (e.g., account code, balance type). + + This function ensures all necessary financial records and accounts are set up when a new Dealer is added, preparing the + system for future financial transactions and accounting operations. + + :param sender: The model class that sent the signal (in this case, Dealer). + :param instance: The instance of the model being saved. + :param created: A boolean indicating whether a new record was created. + :param kwargs: Additional keyword arguments passed by the signal. + :return: None + """ if created: entity_name = instance.user.dealer.name entity = EntityModel.create_entity( @@ -628,7 +659,19 @@ def create_ledger_entity(sender, instance, created, **kwargs): @receiver(post_save, sender=models.Dealer) def create_dealer_groups(sender, instance, created, **kwargs): + """ + Signal handler to create and assign default groups to a Dealer instance when it + is created. The groups are based on predefined names and assigned specific + permissions. Uses transaction hooks to ensure groups are created only after + successful database commit. + :param sender: The model class that triggered the signal. + :type sender: Type[models.Model] + :param instance: The instance of the model class that caused the signal to fire. + :param created: Boolean indicating whether an instance was newly created. + :param kwargs: Additional keyword arguments passed by the signal. + :type kwargs: dict + """ group_names = ["Inventory", "Accountant", "Sales"] def create_groups(): @@ -642,6 +685,19 @@ def create_dealer_groups(sender, instance, created, **kwargs): # Create Vendor @receiver(post_save, sender=models.Vendor) def create_ledger_vendor(sender, instance, created, **kwargs): + """ + Signal function that listens for the `post_save` event on the Vendor model. + This function is triggered after a Vendor instance is saved. If the instance + is newly created (`created` is True), it will create necessary related entities + such as a Vendor model in the corresponding Entity and an Account for that Vendor. + + :param sender: The model class that triggered the signal event, which is `models.Vendor`. + :param instance: The specific instance of the `models.Vendor` that was saved. + :param created: A boolean indicating whether the instance is newly created (`True`) + or updated (`False`). + :param kwargs: Additional keyword arguments passed by the signal dispatcher. + :return: None + """ if created: entity = EntityModel.objects.filter(name=instance.dealer.name).first() additionals = to_dict(instance) @@ -680,6 +736,19 @@ def create_ledger_vendor(sender, instance, created, **kwargs): @receiver(post_save, sender=models.CustomerModel) def create_customer_user(sender, instance, created, **kwargs): + """ + Connects to the `post_save` signal of the `CustomerModel` to create a user object + associated with the customer instance whenever a new `CustomerModel` instance is + created. Retrieves customer-specific information from `additional_info` to initialize + and configure the associated user object. Ensures the user object created has + unusable passwords by default. + + :param sender: The model class that sends the signal (`CustomerModel`). + :param instance: The instance of `CustomerModel` that triggered the signal. + :param created: A boolean indicating whether a new instance was created. + :param kwargs: Additional keyword arguments passed by the signal. + :return: None + """ if created: try: first_name = instance.additional_info.get("customer_info").get("first_name") @@ -701,6 +770,22 @@ def create_customer_user(sender, instance, created, **kwargs): # Create Item @receiver(post_save, sender=models.Car) def create_item_model(sender, instance, created, **kwargs): + """ + Signal handler that triggers upon saving a `Car` model instance. This function is responsible + for creating or updating an associated product in the related entity's inventory system. The + new product is created only if it does not already exist, and additional information about the + car is added to the product's metadata. + + :param sender: Signal sender, typically the model class triggering the save event. + :type sender: type + :param instance: Instance of the `Car` model that was saved. + :type instance: models.Car + :param created: Flag indicating whether the model instance was newly created. + :type created: bool + :param kwargs: Additional keyword arguments passed by the signal mechanism. + :type kwargs: dict + :return: None + """ entity = instance.dealer.entity if created: coa = entity.get_default_coa() @@ -723,6 +808,18 @@ def create_item_model(sender, instance, created, **kwargs): # # update price - CarFinance @receiver(post_save, sender=models.CarFinance) def update_item_model_cost(sender, instance, created, **kwargs): + """ + Signal handler for updating an inventory item's cost and additional information + when a CarFinance instance is saved. This function updates the corresponding + inventory item of the car dealer's entity associated with the car's VIN by + modifying its default amount and updating additional data fields. + + :param sender: The model class that triggered the signal. + :param instance: The instance of the CarFinance that was saved. + :param created: A boolean indicating whether the model instance was newly created. + :param kwargs: Additional keyword arguments passed during the signal invocation. + :return: None + """ entity = instance.car.dealer.entity product = entity.get_items_all().filter(name=instance.car.vin).first() @@ -759,11 +856,38 @@ def update_item_model_cost(sender, instance, created, **kwargs): @receiver(post_save, sender=models.CarColors) def update_car_when_color_changed(sender, instance, **kwargs): + """ + Signal receiver to handle updates to a car instance when its related + CarColors instance is modified. Triggered by the `post_save` signal + for the `CarColors` model. Ensures that the associated `Car` instance + is saved, propagating changes effectively. + + :param sender: The model class (`CarColors`) that was saved. + :type sender: Type[models.CarColors] + :param instance: The specific instance of `CarColors` that was saved. + :type instance: models.CarColors + :param kwargs: Additional keyword arguments passed by the signal. + :type kwargs: dict + :return: None + """ car = instance.car car.save() @receiver(post_save, sender=models.Opportunity) def notify_staff_on_deal_stage_change(sender, instance, **kwargs): + """ + Notify staff members when the stage of an Opportunity is updated. This function listens to the `post_save` + signal for the Opportunity model and triggers a notification if the stage attribute of the Opportunity + instance has been changed. + + :param sender: The model class that sends the signal. + :type sender: type[models.Opportunity] + :param instance: The actual instance being saved in the Django ORM. + :type instance: models.Opportunity + :param kwargs: Additional keyword arguments passed by the signal. + :type kwargs: dict + :return: None + """ if instance.pk: previous = models.Opportunity.objects.get(pk=instance.pk) if previous.stage != instance.stage: @@ -808,6 +932,18 @@ def notify_staff_on_deal_stage_change(sender, instance, **kwargs): @receiver(post_save, sender=models.AdditionalServices) def create_item_service(sender, instance, created, **kwargs): + """ + Signal handler for creating a service item in the ItemModel when a new + AdditionalServices instance is created. This function listens to the + post_save signal of the AdditionalServices model and sets up the related + ItemModel instance to represent the service. + + :param sender: The model class that sent the signal. + :param instance: The instance of the model being saved. + :param created: Boolean indicating whether a new instance was created. + :param kwargs: Additional keyword arguments provided by the signal. + :return: None + """ if created: entity = instance.dealer.entity uom = entity.get_uom_all().get(unit_abbr=instance.uom) @@ -831,6 +967,25 @@ def create_item_service(sender, instance, created, **kwargs): @receiver(post_save, sender=models.Lead) def track_lead_status_change(sender, instance, **kwargs): + """ + Tracks changes in the status of a Lead instance and logs the transition into the + LeadStatusHistory model. This function is triggered after the `post_save` signal + is emitted for a Lead instance. + + The function compares the `status` of the updated Lead instance with its previous + value. If the `status` has changed, it creates a new entry in the LeadStatusHistory + model, recording the old status, the new status, and the staff responsible for the change. + + :param sender: The model class that sent the signal. + :type sender: Type[models.Model] + :param instance: The actual instance being saved. It represents the Lead instance + whose status is being tracked. + :type instance: models.Lead + :param kwargs: Additional keyword arguments passed by the signal. These can include + flags such as 'created' to indicate if the instance was newly created or updated. + + :return: None + """ if instance.pk: # Ensure the instance is being updated, not created try: old_lead = models.Lead.objects.get(pk=instance.pk) @@ -847,6 +1002,18 @@ def track_lead_status_change(sender, instance, **kwargs): @receiver(post_save, sender=models.Lead) def notify_assigned_staff(sender, instance, created, **kwargs): + """ + Signal handler that sends a notification to the staff member when a new lead is assigned. + This function is triggered when a Lead instance is saved. If the lead has been assigned + to a staff member, it creates a Notification object, notifying the staff member of the + new assignment. + + :param sender: The model class that sent the signal. + :param instance: The instance of the model that was saved. + :param created: A boolean indicating whether a new instance was created. + :param kwargs: Additional keyword arguments. + :return: None + """ if instance.staff: # Check if the lead is assigned models.Notification.objects.create( user=instance.staff.staff_member.user, @@ -857,7 +1024,15 @@ def notify_assigned_staff(sender, instance, created, **kwargs): @receiver(post_save, sender=models.CarReservation) def update_car_status_on_reservation_create(sender, instance, created, **kwargs): """ - Signal to update the car status to 'reserved' when a reservation is created. + Signal handler to update the status of a car upon the creation of a car reservation. + This function is triggered when a new instance of a CarReservation is created and saved + to the database. It modifies the status of the associated car to reflect the RESERVED status. + + :param sender: The model class that sends the signal (CarReservation). + :param instance: The specific instance of the CarReservation that triggered the signal. + :param created: A boolean indicating whether the CarReservation instance was created. + :param kwargs: Additional keyword arguments passed by the signal. + :return: None """ if created: car = instance.car @@ -867,7 +1042,18 @@ def update_car_status_on_reservation_create(sender, instance, created, **kwargs) @receiver(post_delete, sender=models.CarReservation) def update_car_status_on_reservation_delete(sender, instance, **kwargs): """ - Signal to update the car status to 'available' when a reservation is deleted. + Signal handler that updates the status of a car to available when a car reservation + is deleted. If there are no active reservations associated with the car, the car's + status is updated to 'AVAILABLE'. This ensures the car's status accurately reflects + its reservability. + + :param sender: The model class that triggers the signal (should always be + models.CarReservation in this context). + :type sender: type + :param instance: The instance of the deleted reservation. + :type instance: models.CarReservation + :param kwargs: Additional keyword arguments. + :type kwargs: dict """ car = instance.car # Check if there are no active reservations for the car @@ -878,7 +1064,16 @@ def update_car_status_on_reservation_delete(sender, instance, **kwargs): @receiver(post_save, sender=models.CarReservation) def update_car_status_on_reservation_update(sender, instance, **kwargs): """ - Signal to update the car status based on the reservation's active status. + Handles the post-save signal for CarReservation model, updating the associated + car's status based on the reservation's activity status. If the reservation is + active, the car's status is updated to RESERVED. If the reservation is not + active, the car's status is set to AVAILABLE if there are no other active + reservations for the car. + + :param sender: The model class that sent the signal. + :param instance: The CarReservation instance that triggered the signal. + :param kwargs: Additional keyword arguments passed by the signal. + :return: None """ car = instance.car if instance.is_active: @@ -890,6 +1085,23 @@ def update_car_status_on_reservation_update(sender, instance, **kwargs): @receiver(post_save, sender=models.Dealer) def create_dealer_settings(sender, instance, created, **kwargs): + """ + Triggered when a `Dealer` instance is saved. This function creates corresponding + `DealerSettings` for a newly created `Dealer` instance. The function assigns + default accounts for invoices and bills based on the role of the accounts + retrieved from the associated entity of the `Dealer`. + + :param sender: The model class that triggered the signal, specifically `Dealer`. + :type sender: Type + :param instance: The actual instance of the `Dealer` that was saved. + :type instance: models.Dealer + :param created: A boolean indicating whether a new instance was created. + `True` if the instance was newly created; otherwise, `False`. + :type created: bool + :param kwargs: Additional keyword arguments passed by the signal. + :type kwargs: dict + :return: None + """ if created: models.DealerSettings.objects.create( dealer=instance, @@ -930,10 +1142,37 @@ def create_dealer_settings(sender, instance, created, **kwargs): @receiver(post_save, sender=models.Dealer) def create_vat(sender, instance, created, **kwargs): + """ + Signal receiver that listens to the `post_save` signal for the `Dealer` model + and handles the creation of a `VatRate` instance if it does not already exist. + + This function ensures that a default VAT rate is created with a specified rate + and is marked as active. It is connected to the Django signals framework and + automatically executes whenever a `Dealer` instance is saved. + + :param sender: The model class that triggered the signal (in this case, `Dealer`). + :param instance: The instance of the model being saved. + :param created: Boolean indicating whether a new instance was created. + :param kwargs: Additional keyword arguments passed by the signal. + :return: None + """ VatRate.objects.get_or_create(rate=Decimal('0.15'), is_active=True) @receiver(post_save, sender=models.Dealer) def create_make_ledger_accounts(sender, instance, created, **kwargs): + """ + Signal receiver that creates ledger accounts for car makes associated with a dealer when a new dealer instance + is created. This function listens to the `post_save` signal of the `Dealer` model and automatically generates + new ledger accounts for all car makes, associating them with the given dealer's entity. + + :param sender: The model class (`Dealer`) that triggered the signal. + :type sender: Type[models.Dealer] + :param instance: The instance of the `Dealer` model that triggered the signal. + :param created: A boolean indicating whether a new `Dealer` instance was created. + :type created: bool + :param kwargs: Additional keyword arguments passed by the signal. + :return: None + """ if created: entity = instance.entity coa = entity.get_default_coa() @@ -976,6 +1215,27 @@ def create_make_ledger_accounts(sender, instance, created, **kwargs): # ) def save_journal(car_finance,ledger,vendor): + """ + Saves a journal entry pertaining to a car finance transaction for a specific ledger and vendor. + + This function ensures that relevant accounts are updated to record financial transactions. It handles + debiting of the inventory account and crediting of the vendor account to maintain accurate bookkeeping. + Additionally, it creates vendor accounts dynamically if required and ties the created journal entry to + the ledger passed as a parameter. All transactions adhere to the ledger's entity-specific Chart of + Accounts (COA) configuration. + + :param car_finance: Instance of the car finance object containing details about the financed car + and its associated costs. + :type car_finance: `CarFinance` object + :param ledger: Ledger instance to which the journal entry is tied. This ledger must provide + entity-specific details, including its COA and related accounts. + :type ledger: `Ledger` object + :param vendor: Vendor instance representing the supplier or vendor related to the car finance + transaction. This vendor is used to derive or create the vendor account in COA. + :type vendor: `Vendor` object + + :return: None + """ entity = ledger.entity coa = entity.get_default_coa() journal = JournalEntryModel.objects.create( @@ -1027,6 +1287,26 @@ def save_journal(car_finance,ledger,vendor): @receiver(post_save, sender=models.CarFinance) def update_finance_cost(sender, instance, created, **kwargs): + """ + Signal to handle `post_save` functionality for the `CarFinance` model. This function + creates or updates financial records related to a car's finance details in the ledger + associated with the car's vendor and dealer. For newly created instances, a ledger is + created or retrieved, and the `save_journal` function is executed to log the financial + transactions. + + This function also has commented-out logic to handle updates for already created + instances, including journal updates or adjustments to existing financial transactions. + + :param sender: Model class that triggered the signal + :type sender: Model + :param instance: Instance of the `CarFinance` model passed to the signal + :type instance: CarFinance + :param created: Boolean value indicating if the instance was created (`True`) or updated (`False`) + :type created: bool + :param kwargs: Arbitrary keyword arguments passed to the signal + :type kwargs: dict + :return: None + """ if created: entity = instance.car.dealer.entity vendor = instance.car.vendor diff --git a/inventory/tables.py b/inventory/tables.py index 7d9b1b04..33df7b5c 100644 --- a/inventory/tables.py +++ b/inventory/tables.py @@ -8,6 +8,45 @@ from django.utils.html import format_html class CarTable(tables.Table): + """ + Represents a table for displaying information about cars. + + The `CarTable` class is a Django Tables2 table configuration designed for displaying + details of cars, including their stock type, VIN, make, model, year, series, trim, + mileage, price, colors, receiving date, and status. This table renders each column with + customizable settings, including cell attributes and formatting. The class utilizes + custom render methods to format specific columns, such as dates, localized names, and + status badges. + + :ivar stock_type: Specifies whether the car is new or used. + :type stock_type: tables.Column + :ivar vin: Contains the VIN of the car, rendered as a hyperlink to the car detail page. + :type vin: tables.LinkColumn + :ivar id_car_make: Defines the make (brand) of the car. + :type id_car_make: tables.Column + :ivar id_car_model: Defines the model of the car. + :type id_car_model: tables.Column + :ivar year: Specifies the year the car was manufactured. + :type year: tables.Column + :ivar id_car_serie: Refers to the series of the car. + :type id_car_serie: tables.Column + :ivar id_car_trim: Refers to the trim of the car. + :type id_car_trim: tables.Column + :ivar mileage: Indicates the mileage of the car. + :type mileage: tables.Column + :ivar selling_price: Displays the selling price, derived from a related field on the finances object. + :type selling_price: tables.Column + :ivar exterior_color: Displays the exterior color of the car, derived from a related field on the colors object. + :type exterior_color: tables.Column + :ivar interior_color: Displays the interior color of the car, derived from a related field on the colors object. + :type interior_color: tables.Column + :ivar receiving_date: Represents the date the car was received, formatted as the + time elapsed since receiving or as "-" if the date is missing. + :type receiving_date: tables.Column + :ivar status: Shows the status of the car (e.g., available, reserved, sold, transfer) + with badge-style formatting based on the status value. + :type status: tables.Column + """ stock_type = tables.Column(verbose_name=_("Stock Type")) vin = tables.LinkColumn("car_detail", args=[tables.A("pk")], verbose_name=_("VIN"), attrs={"td": {"class": "fw-bold"}}) id_car_make = tables.Column(verbose_name=_("Make")) diff --git a/inventory/tests.py b/inventory/tests.py index f89b37fc..38555717 100644 --- a/inventory/tests.py +++ b/inventory/tests.py @@ -19,6 +19,36 @@ User = get_user_model() # Create your tests here. class ModelTest(TestCase): + """ + Test case class designed to test models in the application. The tests + verify model creation, related object creation, and appropriate + functionality for specific business logic, ensuring correctness and + reliability of the defined models. + + This class tests the following: + - Dealer model, including associated user and entity creation. + - Car model, ensuring product creation and validations. + - Testing of car finances, including finance totals and relationships. + - Creation of additional services, ensuring the generation of corresponding + item services. + + :ivar vat: Vat rate created for testing purposes. + :type vat: VatRate instance + :ivar dealer: Dealer instance created for testing. + :type dealer: Dealer instance + :ivar car_make: Car make created for testing purposes. + :type car_make: CarMake instance + :ivar car_model: Car model created for testing purposes. + :type car_model: CarModel instance + :ivar car_serie: Car series created for testing purposes. + :type car_serie: CarSerie instance + :ivar trim: Car trim created for testing purposes. + :type trim: CarTrim instance + :ivar car: Car object created for testing. + :type car: Car instance + :ivar car_finances: Car finance object for the car under test. + :type car_finances: CarFinance instance + """ def setUp(self): email = "RkzgO@example.com" name = "John Doe" @@ -140,6 +170,16 @@ class ModelTest(TestCase): class AuthenticationTest(TestCase): + """ + Represents a set of test cases for user authentication and signup validation within + a web application. These tests ensure the correctness of authentication functionalities + such as login and signing up with appropriate JSON data handling. + + :ivar client: Django test client used to simulate HTTP requests within the test framework. + :type client: Client + :ivar url: URL for account signup endpoint used in the test cases. + :type url: str + """ def setUp(self): self.client = Client() self.url = reverse("account_signup") @@ -245,6 +285,20 @@ class AuthenticationTest(TestCase): class CarFinanceCalculatorTests(TestCase): + """ + Unit tests for the CarFinanceCalculator class. + + This class contains various test cases to validate the correct functionality + of the CarFinanceCalculator. It includes tests for VAT rate retrieval, + item transactions, car data extraction, calculation of totals, fetching + additional services, and finance-related data processing. + + :ivar mock_model: Mocked model used to simulate interactions with the + CarFinanceCalculator instance. + :type mock_model: unittest.mock.MagicMock + :ivar vat_rate: Active VAT rate used for testing VAT rate retrieval. + :type vat_rate: VatRate + """ def setUp(self): # Common setup for all tests self.mock_model = MagicMock() diff --git a/inventory/utilities/sa.py b/inventory/utilities/sa.py index de5cc29f..195ec5d9 100644 --- a/inventory/utilities/sa.py +++ b/inventory/utilities/sa.py @@ -6,6 +6,17 @@ from plans.taxation import TaxationPolicy class SaudiTaxationPolicy(TaxationPolicy): + """ + Represents the taxation policy specific to Saudi Arabia. + + This class inherits from TaxationPolicy and provides implementations + of methods to retrieve default tax, issuer country code, and tax rates + specific to Saudi Arabia. Typically used in scenarios involving + invoicing or taxation calculation for Saudi Arabia. + + :ivar settings: Configuration settings of the application. + :type settings: module + """ def get_default_tax(self): return getattr(settings, 'PLANS_TAX', None) diff --git a/inventory/utils.py b/inventory/utils.py index 79c0bab1..593354a6 100644 --- a/inventory/utils.py +++ b/inventory/utils.py @@ -32,6 +32,20 @@ from django.utils.translation import get_language def get_jwt_token(): + """ + Fetches a JWT token from an external authentication API. + + This function sends a POST request to the provided authentication API endpoint with + the required headers and payload. It retrieves a JSON Web Token (JWT) if the request + is successful or returns None in case of a failure. + + :raises: This function does not propagate exceptions but catches and logs + ``requests.exceptions.RequestException`` if the request fails. + + :return: A JWT token as a string if the request is successful, or None if + an error occurs during the process. + :rtype: str or None + """ url = "https://carapi.app/api/auth/login" headers = { "accept": "text/plain", @@ -51,6 +65,16 @@ def get_jwt_token(): def localize_some_words(): + """ + Localize predefined words and phrases for display purposes. + + This function is used to localize a set of predefined words or phrases + that may be displayed in a user interface or other contexts. These + localized words or phrases include terms like "success," "error," and + "Forgot Password?". + + :return: None + """ success = _("success") error = _("error") forget = _("Forgot Password?") @@ -59,6 +83,18 @@ def localize_some_words(): def get_calculations(quotation): + """ + Calculates and summarizes financial services data related to the cars in a given + quotation. It aggregates costs, VAT, and total amounts based on the services + available for the cars linked to the quotation. + + :param quotation: The quotation object containing the cars and their related + financial details. + :type quotation: Quotation + :return: A dictionary with computed financial details including the services + data, total service costs, total VAT computed, and the total including VAT. + :rtype: dict + """ context = {} qc_len = quotation.quotation_cars.count() cars = [x.car for x in quotation.quotation_cars.all()] @@ -90,6 +126,23 @@ def get_calculations(quotation): def send_email(from_, to_, subject, message): + """ + Send an email with the specified subject and message. + + This function sends an email from the given sender to the specified + recipient with a subject and a message body. It utilizes the provided + parameters to construct and send the email. + + :param from_: str + The sender's email address. + :param to_: str + The recipient's email address. + :param subject: str + The subject of the email. + :param message: str + The body/content of the email. + :return: None + """ subject = subject message = message from_email = from_ @@ -98,6 +151,22 @@ def send_email(from_, to_, subject, message): def get_user_type(request): + """ + Determine the type of user based on the given request object. + + This function identifies the type of user from the provided request. It + checks if the user is a dealer, staff member, or neither based on the + attributes of the request object, returning the appropriate associated + user type or `None` if the determination cannot be made. + + :param request: The HTTP request object containing user and role + information. + :type request: Any + :return: A dealer object if the user is a dealer, a dealer object + associated with the staff member if the user is a staff member, or + None if the user type cannot be identified. + :rtype: Optional[Any] + """ if request.is_dealer: return request.user.dealer elif request.is_staff: @@ -106,6 +175,18 @@ def get_user_type(request): def get_dealer_from_instance(instance): + """ + Retrieve the dealer object from the given instance. + + This function checks whether the given instance's dealer has an attribute + `staff`. If the attribute exists, it directly returns the dealer object + associated with the instance. + + :param instance: The instance from which the dealer object is retrieved. + :type instance: Any + :return: The dealer object associated with the instance. + :rtype: Any + """ if instance.dealer.staff: return instance.dealer else: @@ -113,6 +194,18 @@ def get_dealer_from_instance(instance): def reserve_car(car, request): + """ + Reserve a car for a user for 24 hours and update its status to reserved. + + The function creates a reservation record for the specified car, sets the + reservation expiration time, updates the car's status, and notifies the user + about the operation outcome through success or error messages. It then redirects + to the car detail page. + + :param car: The car object to be reserved. + :param request: The HTTP request object containing the user making the reservation. + :return: Redirection to the car's detail page. + """ try: reserved_until = timezone.now() + timezone.timedelta(hours=24) models.CarReservation.objects.create( @@ -128,6 +221,17 @@ def reserve_car(car, request): def calculate_vat_amount(amount): + """ + Calculates the VAT (Value Added Tax) amount for a given monetary value. The method retrieves + the first active VAT rate from the database and applies it to the provided amount. If no + active VAT rate is found, the function will return the original amount. + + :param amount: The monetary value to which the VAT should be applied. + :type amount: Decimal + :return: A tuple containing the computed VAT amount and the VAT rate as a decimal, or + the original amount if no VAT rate is active. + :rtype: Tuple[Decimal, Decimal] or Decimal + """ vat = models.VatRate.objects.filter(is_active=True).first() if vat: return ((amount * Decimal(vat.rate)).quantize(Decimal("0.01")), vat.rate) @@ -135,6 +239,19 @@ def calculate_vat_amount(amount): def get_car_finance_data(model): + """ + Fetches car finance data from the provided model instance and calculates + various related details including total prices, VAT, discounts, and + additional services information. + + :param model: The model instance that contains car and finance-related + transaction data. + :type model: Model + :return: A dictionary containing detailed information about the car finance + transactions, including individual car details, total quantity, calculated + totals for prices, VAT, discounts, additional services, and the VAT rate. + :rtype: dict + """ vat = models.VatRate.objects.filter(is_active=True).first() data = model.get_itemtxs_data()[0].all() total = sum( @@ -202,6 +319,29 @@ def get_car_finance_data(model): def get_financial_values(model): + """ + Calculates financial values for a given model, including VAT, discounts, + grand total, and additional services. + + This function processes financial data related to items in the input model + and computes aggregated values like total amounts, VAT amounts, discounts, + and more. It also provides detailed information on cars and items as well + as any applicable additional services. + + :param model: The model instance containing financial and item data. + It should have the necessary methods and attributes to fetch + transactional records and relevant details. + :type model: Any + :return: A dictionary containing calculated financial details including: + - "vat_amount": The computed VAT amount. + - "total": The total value of all items before VAT and discounts. + - "grand_total": The total after applying discounts and adding VAT. + - "discount_amount": The aggregated discount amount. + - "vat": The applicable VAT rate. + - "car_and_item_info": A list of detailed car and item information. + - "additional_services": A list of additional services and their prices. + :rtype: dict + """ vat = models.VatRate.objects.filter(is_active=True).first() if not model.get_itemtxs_data()[0].exists(): @@ -285,6 +425,23 @@ def get_financial_values(model): def set_invoice_payment(dealer, entity, invoice, amount, payment_method): + """ + Processes and applies a payment for a specified invoice. This function calculates + finance details, handles associated account transactions, and updates the invoice + status accordingly. + + :param dealer: Dealer object responsible for processing the payment + :type dealer: Dealer + :param entity: Entity object associated with the invoice and payment + :type entity: Entity + :param invoice: The invoice object for which the payment is being made + :type invoice: Invoice + :param amount: The amount being paid towards the invoice + :type amount: Decimal + :param payment_method: The payment method used for the transaction + :type payment_method: str + :return: None + """ calculator = CarFinanceCalculator(invoice) finance_data = calculator.get_finance_data() @@ -330,6 +487,27 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method): def set_bill_payment(dealer, entity, bill, amount, payment_method): + """ + Sets the payment for a given bill by creating journal entries for the + transaction and updating the respective accounts and the bill's status. + + The function handles the transaction by creating a new journal entry + linked with the specified bill, then records debit and credit + transactions using the entity’s cash and accounts payable accounts. + It finally updates the bill's payment status and persists changes. + + :param dealer: The dealer making or receiving the payment. + :type dealer: Any + :param entity: The business entity involved in the payment transaction. + :type entity: Any + :param bill: The bill object representing the invoice to be paid. + :type bill: Any + :param amount: The amount to be paid for the bill. + :type amount: Decimal + :param payment_method: The method used to make the payment (e.g., cash, credit). + :type payment_method: Any + :return: None + """ total_amount = 0 for x in bill.get_itemtxs_data()[0].all(): total_amount += Decimal(x.unit_cost) * Decimal(x.quantity) @@ -369,6 +547,27 @@ def set_bill_payment(dealer, entity, bill, amount, payment_method): def transfer_to_dealer(request, cars, to_dealer, remarks=None): + """ + Transfers a list of cars from one dealer to another, ensuring that all cars + originate from the same dealer and logging the transfer. + + :param request: The HTTP request object which contains information about + the current user and context. + :type request: HttpRequest + :param cars: A list of car objects to transfer. + :type cars: list[Car] + :param to_dealer: The dealer object representing the destination dealer. + :type to_dealer: Dealer + :param remarks: Optional remarks to include in the transfer log. + Defaults to None. + :type remarks: str, optional + :return: None + :rtype: NoneType + + :raises ValueError: If no cars are selected for transfer. + :raises ValueError: If the cars are not all from the same dealer. + :raises ValueError: If the source and destination dealers are the same. + """ dealer = get_user_type(request) if not cars: @@ -398,6 +597,28 @@ def transfer_to_dealer(request, cars, to_dealer, remarks=None): car.save() class CarTransfer: + """ + Handles the process of transferring a car between dealers, automating associated tasks + such as creating customers, invoices, products, vendors, and bills, as well as reflecting + changes in relevant entities and systems. + + The purpose of this class is to facilitate a smooth transfer of car ownership from one + dealer to another. It ensures that all necessary financial, logistic, and informational + processes are executed and updated within the system. The car's status and related + entities are adjusted accordingly post-transfer. + + :ivar car: Car object being transferred. + :ivar transfer: Represents the transfer details, including source and destination dealers. + :ivar from_dealer: The dealer transferring the car out. + :ivar to_dealer: The dealer receiving the car. + :ivar customer: Customer entity corresponding to the receiving dealer. + :ivar invoice: Invoice entity created for the car transfer. + :ivar ledger: Ledger associated with the created invoice. + :ivar item: The inventory item corresponding to the car being transferred. + :ivar product: The product created in the receiving dealer's ledger. + :ivar vendor: Vendor entity related to the transferring dealer. + :ivar bill: Bill entity created for the receiving dealer. + """ def __init__(self, car, transfer): self.car = car self.transfer = transfer @@ -700,6 +921,18 @@ class CarTransfer: def to_dict(obj): + """ + Convert a Python object's attributes and associated values to a dictionary, + with special handling for specific data types such as datetime and object references. + + :param obj: The Python object to be converted into a dictionary. Object attributes + are dynamically introspected to create the dictionary. For objects containing + references to other objects, specific attributes (e.g., "pk" or "id") may be used + for conversion and representation. + :return: A dictionary containing the key-value pairs for the object's attributes. + Datetime values are formatted as strings in "YYYY-MM-DD HH:MM:SS" format. All other + attributes, including nested object references, are converted to strings. + """ obj_dict = vars(obj).copy() if "_state" in vars(obj): del obj_dict["_state"] @@ -717,6 +950,24 @@ def to_dict(obj): return obj_dict class CarFinanceCalculator: + """ + Class responsible for calculating car financing details. + + This class provides methods and attributes required for calculating various + aspects related to car financing, such as VAT calculation, pricing, discounts, + and additional services. It processes data about cars, computes totals (e.g., + price, VAT, discounts), and aggregates the financial data for reporting or + further processing. + + :ivar model: The data model passed to the calculator for retrieving transaction data. + :type model: Any + :ivar vat_rate: The current active VAT rate retrieved from the database. + :type vat_rate: Decimal + :ivar item_transactions: A collection of item transactions retrieved from the model. + :type item_transactions: list + :ivar additional_services: A list of additional services with details (e.g., name, price, taxable status). + :type additional_services: list + """ VAT_OBJ_NAME = 'vat_rate' CAR_FINANCE_KEY = 'car_finance' CAR_INFO_KEY = 'car_info' @@ -821,6 +1072,16 @@ class CarFinanceCalculator: def get_item_transactions(txs): + """ + Extracts and compiles relevant transaction details from a list of transactions, + including information about cars, financing, estimates, and invoices. The extracted + details for each transaction are stored as dictionaries in a list. + + :param txs: A list of transaction objects from which information is extracted. + :type txs: list + :return: A list of dictionaries, each containing extracted transaction details. + :rtype: list + """ transactions = [] for tx in txs: data = {} @@ -844,7 +1105,21 @@ def get_item_transactions(txs): def get_local_name(self): """ - Returns the localized name based on the current language. + Retrieve the localized name of the instance based on the current language. + + This method returns the name attribute of an instance based on the currently + active language. If the language is Arabic ('ar'), it attempts to return the + value of the attribute `arabic_name`. If the language is not Arabic or the + `arabic_name` attribute is not defined, it defaults to returning the value of + the `name` attribute. + + :param self: + Reference to the instance of the class containing `name` and + optionally `arabic_name` attributes. + :return: + The localized name based on the current language. Returns the value of + `arabic_name` for Arabic ('ar') language, or the value of `name` for any + other language or if `arabic_name` is not defined. """ if get_language() == 'ar': return getattr(self, 'arabic_name', None) @@ -852,6 +1127,21 @@ def get_local_name(self): def handle_account_process(invoice,amount,finance_data): + """ + Processes accounting transactions based on an invoice, financial data, + and related entity accounts configuration. This function handles the + creation of accounts if they do not already exist, and processes journal + entries and transactions. + + :param invoice: The invoice object to process transactions for. + :type invoice: InvoiceModel + :param amount: Total monetary value for the transaction. + :type amount: Decimal + :param finance_data: Dictionary containing financial details such as + 'grand_total', 'total_vat_amount', and other related data. + :type finance_data: dict + :return: None + """ for i in invoice.get_itemtxs_data()[0]: car = models.Car.objects.get(vin=invoice.get_itemtxs_data()[0].first().item_model.name) entity = invoice.ledger.entity @@ -937,6 +1227,18 @@ def handle_account_process(invoice,amount,finance_data): # ) def create_make_accounts(dealer): + """ + Creates accounts for dealer's car makes and associates them with a default + Chart of Accounts (CoA) if they don't already exist. The function iterates + through all car makes associated with the dealer and creates unique inventory + accounts for each car make. Accounts are created with a specified naming + convention and are assigned a new account code if required. + + :param dealer: The dealer for whom to create make accounts. + :type dealer: Dealer + :return: None + :rtype: None + """ entity = dealer.entity coa = entity.get_default_coa() diff --git a/inventory/views.py b/inventory/views.py index 4cb26229..46cb28af 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -160,10 +160,34 @@ logging.basicConfig(level=logging.INFO) class Hash(Func): + """ + Represents a function used to compute a hash value. + + This class serves as a placeholder to specify a particular hash + computation function. It extends the `Func` base class and is used + primarily to work with hash-related operations within the underlying + implementation. The specific hash algorithm can be modified or accessed + through the `function` attribute. + + :ivar function: Specifies the hash computation function. + :type function: str + """ function = "get_hash" def switch_language(request): + """ + Switches the current language context for the user based on a request parameter, modifies the URL path + accordingly, and updates session and cookies with the new language preference. + + :param request: The HTTP request object containing information about the user request, including + the desired language to switch to and the referring URL. + - "GET" dictionary is accessed to retrieve the desired language parameter. + - "META" dictionary is used to extract the referring URL via "HTTP_REFERER". + + :return: A redirect response object pointing to the modified URL with the updated language + preference, if the requested language is valid. Otherwise, redirects to the default URL. + """ language = request.GET.get("language", "en") referer = request.META.get("HTTP_REFERER", "/") parsed_url = urlparse(referer) @@ -200,6 +224,24 @@ def switch_language(request): def dealer_signup(request, *args, **kwargs): + """ + Handles the dealer signup wizard process, including forms validation, user and group + creation, permissions assignment, and dealer data storage. This view supports GET + requests for rendering the signup wizard page, and POST requests for processing + submitted data. The function also differentiates between requests sent with the + "Hx-Request" header for partial form validations in the wizard. + + :param request: The HTTP request object representing the client request. It contains + metadata about the request and the POST data for creating the dealer. + :type request: django.http.HttpRequest + :param args: Optional positional arguments passed to the view during the call. + :type args: tuple + :param kwargs: Optional keyword arguments passed to the view during the call. + :type kwargs: dict + :return: A rendered signup wizard page or a JSON response indicating operation success + or failure. + :rtype: Union[django.http.HttpResponse, django.http.JsonResponse] + """ form1 = forms.WizardForm1() form2 = forms.WizardForm2() form3 = forms.WizardForm3() @@ -290,6 +332,20 @@ def dealer_signup(request, *args, **kwargs): class HomeView(LoginRequiredMixin, TemplateView): + """ + HomeView class responsible for rendering the home page. + + This class ensures that only authenticated users can access the home page. + Unauthenticated users are redirected to the welcome page. It is built on top of + Django's TemplateView and includes additional functionality by inheriting from + LoginRequiredMixin. The purpose of this class is to control the accessibility + of the main index page of the application and manage context data for frontend + rendering. + + :ivar template_name: The path to the template used for rendering the view's + output. + :type template_name: str + """ template_name = "index.html" def dispatch(self, request, *args, **kwargs): @@ -352,10 +408,34 @@ class HomeView(LoginRequiredMixin, TemplateView): class TestView(TemplateView): + """ + Represents a view for displaying a list of cars. + + This class is a Django TemplateView-based view that renders a specific template + to display a list of cars. It uses a pre-defined template file and can be + extended or customized to provide additional functionality for rendering + content in a Django application. + + :ivar template_name: Specifies the path to the HTML template file used for + rendering the cars list view. + :type template_name: str + """ template_name = "inventory/cars_list_api.html" class ManagerDashboard(LoginRequiredMixin, TemplateView): + """ + ManagerDashboard class is a view handling the dashboard for a manager. + + Provides functionality to manage and view various statistics and data specific + to the dealer associated with the authenticated manager. It uses a specific + template and ensures authentication before granting access. The class + aggregates data about cars, leads, financial statistics, and other related + business information for display in the manager's dashboard. + + :ivar template_name: Path to the template used for rendering the manager's dashboard. + :type template_name: str + """ template_name = "dashboards/manager.html" def dispatch(self, request, *args, **kwargs): @@ -451,6 +531,22 @@ class ManagerDashboard(LoginRequiredMixin, TemplateView): class SalesDashboard(LoginRequiredMixin, TemplateView): + """ + SalesDashboard class provides a view for the sales dashboard. + + This class is responsible for generating the context data required to + display various statistics and information on the sales dashboard. It + inherits from `LoginRequiredMixin` and `TemplateView`, ensuring only + authenticated users can access the view. The dashboard includes data + such as the total number of cars, reservations, sold percentages, + reserved percentages, and cars categorized by various statuses. The + purpose of this dashboard is to provide dealership staff an overview + of their inventory and related sales metrics. + + :ivar template_name: The name of the HTML template used for rendering + the sales dashboard. + :type template_name: str + """ template_name = "dashboards/sales.html" def get_context_data(self, **kwargs): @@ -522,6 +618,17 @@ class SalesDashboard(LoginRequiredMixin, TemplateView): class WelcomeView(TemplateView): + """ + Handles the rendering and context data for the Welcome view. + + This class serves as a Django TemplateView for the "welcome.html" template. It + is primarily responsible for providing the necessary context data, including + user-specific information and the list of plans, to the template for rendering + the welcome page. + + :ivar template_name: Path to the template used by the view. + :type template_name: str + """ template_name = "welcome.html" def get_context_data(self, **kwargs): @@ -534,6 +641,22 @@ class WelcomeView(TemplateView): class CarCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): + """ + Manages the creation of a new car entry in the inventory system. + + This class is responsible for handling the creation of a new car, including form + display and data submission. It ensures the user has appropriate permissions and + customizes the available vendors for the user depending on their dealer entity. + + :ivar model: Specifies the Car model that this view interacts with. + :type model: models.Car + :ivar form_class: Defines the form class to be used for creating or editing `Car` instances. + :type form_class: forms.CarForm + :ivar template_name: Name of the template to render the car creation form. + :type template_name: str + :ivar permission_required: Permissions required to add a car. + :type permission_required: list + """ model = models.Car form_class = forms.CarForm template_name = "inventory/car_form.html" @@ -562,6 +685,23 @@ class CarCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): def car_history(request, pk): + """ + Fetch and display the history of activities related to a specific car. + + This view retrieves a car object based on its primary key (pk) and gathers + all related activity records where the content type corresponds to the car + model. The retrieved data is then rendered into a specified HTML template + for presentation. + + :param request: The HTTP request object that contains metadata about the + request made by the client. + :type request: HttpRequest + :param pk: The primary key of the car object to retrieve. + :type pk: int + :return: An HTTP response with the rendered car history HTML template, + including the car and its associated activities as context data. + :rtype: HttpResponse + """ car = get_object_or_404(models.Car, pk=pk) activities = models.Activity.objects.filter( content_type__model="car", object_id=car.id @@ -572,6 +712,16 @@ def car_history(request, pk): class AjaxHandlerView(LoginRequiredMixin, View): + """ + Handles AJAX requests for various car-related operations. + + This class enables dynamic handling of AJAX requests related to vehicle + data, such as retrieving models, series, trims, specifications, and other + related details. It checks the requested action, delegates it to the + corresponding handler method, and returns an appropriate response. + + :ivar request: Django request object containing HTTP request details. + """ def get(self, request, *args, **kwargs): action = request.GET.get("action") handlers = { @@ -750,6 +900,16 @@ class AjaxHandlerView(LoginRequiredMixin, View): @method_decorator(csrf_exempt, name="dispatch") class SearchCodeView(LoginRequiredMixin, View): + """ + View to handle barcode/QR code scanning and car detail retrieval. + + This class is responsible for rendering the form page for scanning and processing + images to decode VIN codes using uploaded images. It handles both GET and POST + requests. Upon successfully decoding a VIN, it redirects to the car detail page. + + :ivar template_name: Path to the template used for the form rendering. + :type template_name: str + """ template_name = "inventory/scan_vin.html" def get(self, request, *args, **kwargs): @@ -783,6 +943,32 @@ class SearchCodeView(LoginRequiredMixin, View): class CarInventory(LoginRequiredMixin, PermissionRequiredMixin, ListView): + """ + Represents the car inventory listing view for a dealership. + + This class is a Django ListView-based implementation to display a list of cars + filtered by specific criteria such as make, model, and trim, while supporting user + authentication and permissions. It includes pagination and ordering features and + integrates search filtering functionality. + + :ivar model: The model associated with the list view. + :type model: models.Car + :ivar home_label: The label of the home section of the application, used for UI + or breadcrumbs. Defined as a translatable string. + :type home_label: str + :ivar template_name: The template path used for rendering the car inventory + list view. + :type template_name: str + :ivar context_object_name: The name of the context variable containing the + car objects in the template. + :type context_object_name: str + :ivar paginate_by: The number of car objects displayed per page. + :type paginate_by: int + :ivar ordering: The default ordering of car objects in the list view. + :type ordering: list + :ivar permission_required: The permission(s) required to access this view. + :type permission_required: list + """ model = models.Car home_label = _("inventory") template_name = "inventory/car_inventory.html" @@ -817,6 +1003,24 @@ class CarInventory(LoginRequiredMixin, PermissionRequiredMixin, ListView): class CarColorCreate(LoginRequiredMixin, PermissionRequiredMixin, CreateView): + """ + View for creating a new car color. + + This view is used to add new colors to a specific car by associating + the color with a car instance. It ensures that the user is logged in + and has the necessary permissions to add car colors. The form used in + this view is populated dynamically with respect to the related car + object that is retrieved based on the provided car primary key. + + :ivar model: Reference to the model representing car colors. + :type model: models.CarColors + :ivar form_class: The form class used for car color creation. + :type form_class: forms.CarColorsForm + :ivar template_name: Path to the template used while rendering the view. + :type template_name: str + :ivar permission_required: List of permissions required by the view. + :type permission_required: list + """ model = models.CarColors form_class = forms.CarColorsForm template_name = "inventory/add_colors.html" @@ -837,6 +1041,28 @@ class CarColorCreate(LoginRequiredMixin, PermissionRequiredMixin, CreateView): class CarListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): + """ + Represents a view for listing and managing a collection of cars. + + This class is used to display a paginated list of cars according to specific + filters and permissions. It ensures that only users with the required + permissions can access the view and handles dynamic filtering of cars based + on user input. The view also populates additional context data, + such as car statistics and related make, model, and year information, + to support detailed customization in templates. + + :ivar model: Specifies the model associated with this view. + :type model: models.Car + :ivar template_name: The name of the template used for this view. + :type template_name: str + :ivar context_object_name: The name of the variable used in the template to + represent the objects being displayed. + :type context_object_name: str + :ivar paginate_by: The number of items to display per page in the paginated list. + :type paginate_by: int + :ivar permission_required: The permission required to access this view. + :type permission_required: str + """ model = models.Car template_name = "inventory/car_list_view.html" context_object_name = "cars" @@ -912,6 +1138,21 @@ class CarListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): @login_required def inventory_stats_view(request): + """ + Handle the inventory stats view for a dealer, displaying detailed information + about the cars, including counts grouped by make, model, and trim. + + The function fetches all cars associated with the authenticated dealer, calculates + the inventory statistics (e.g., total cars, reserved cars, and cars categorized + by make, model, and trim levels), and prepares the data to be rendered in a + template. + + :param request: The HTTP request object from the client. + :type request: HttpRequest + :return: An HTTP response containing structured inventory data rendered in the + "inventory/inventory_stats.html" template. + :rtype: HttpResponse + """ dealer = get_user_type(request) # Base queryset for cars belonging to the dealer @@ -999,6 +1240,27 @@ def inventory_stats_view(request): class CarDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): + """ + Provides a detailed view of a single car instance. + + This class-based view is designed to display detailed information about a + single car instance from the inventory. It utilizes Django's built-in + DetailView along with mixins to enforce authentication and permission + requirements. The view ensures that only authenticated users with the proper + permissions can access car details. + + :ivar model: The model associated with this view, representing the Car model. + :type model: Model + :ivar template_name: The path to the template used to render the detailed + car view. + :type template_name: str + :ivar context_object_name: The name of the context variable that contains + the car object. + :type context_object_name: str + :ivar permission_required: A list of permissions required to access the + view. + :type permission_required: list + """ model = models.Car template_name = "inventory/car_detail.html" context_object_name = "car" @@ -1006,6 +1268,24 @@ class CarDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): class CarFinanceCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): + """ + Handles the creation of car finance records within the inventory system. + + This view provides functionality to create car finance records tied to a + specific car in the inventory. It enforces that the user is logged in and + has the required permissions to add a car finance record. It also customizes + form behavior and context data to associate the finance record with the + corresponding car and populate additional services based on the user's type. + + :ivar model: The database model associated with the view. + :type model: models.CarFinance + :ivar form_class: The form class used to create car finance records. + :type form_class: forms.CarFinanceForm + :ivar template_name: The template used to render the car finance creation page. + :type template_name: str + :ivar permission_required: The list of permissions required to access this view. + :type permission_required: list + """ model = models.CarFinance form_class = forms.CarFinanceForm template_name = "inventory/car_finance_form.html" @@ -1040,6 +1320,26 @@ class CarFinanceCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateVi class CarFinanceUpdateView( LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView ): + """ + Manages the update of car finance details. + + This class-based view provides functionality for updating car finance + information in the system. It enforces login and specific permissions for + access, and it leverages mixins to provide success messages upon completing + updates. It is designed to interact with car finance data, update form + handling, and enforce related business logic. + + :ivar model: The model associated with the view. + :type model: models.CarFinance + :ivar form_class: The form class used by the view. + :type form_class: forms.CarFinanceForm + :ivar template_name: The template used for rendering the form. + :type template_name: str + :ivar success_message: The success message displayed after an update. + :type success_message: str + :ivar permission_required: List of permissions required to access the view. + :type permission_required: list + """ model = models.CarFinance form_class = forms.CarFinanceForm template_name = "inventory/car_finance_form.html" @@ -1074,6 +1374,24 @@ class CarFinanceUpdateView( class CarUpdateView( LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView ): + """ + Represents a Django view dedicated to updating Car model instances. + + This view facilitates secure and authorized updates to car-related information + through a web interface. It ensures permission checks, requires user login for + access, and communicates feedback upon successful updates. + + :ivar model: The model associated with this view. + :type model: models.Model + :ivar form_class: The form class used for updating the model instance. + :type form_class: django.forms.ModelForm + :ivar template_name: The path to the template used for rendering the view. + :type template_name: str + :ivar success_message: Message displayed upon successful update. + :type success_message: str + :ivar permission_required: List of permissions required to access this view. + :type permission_required: list[str] + """ model = models.Car form_class = forms.CarUpdateForm template_name = "inventory/car_edit.html" @@ -1087,6 +1405,22 @@ class CarUpdateView( class CarDeleteView( LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, DeleteView ): + """ + Handles the deletion of Car objects in the inventory application. + + This view ensures that only authorized users can delete a car. It requires users to be logged in + and have the appropriate permissions. Upon successful deletion, a success message is displayed, + and the user is redirected to the inventory statistics page. + + :ivar model: The model associated with this view. + :type model: models.Car + :ivar template_name: The path to the HTML template used for confirming a car deletion. + :type template_name: str + :ivar success_url: The URL to redirect to after successful deletion. + :type success_url: str + :ivar permission_required: A list of permission strings required to access this view. + :type permission_required: list[str] + """ model = models.Car template_name = "inventory/car_confirm_delete.html" success_url = reverse_lazy("inventory_stats") @@ -1098,6 +1432,24 @@ class CarDeleteView( class CarLocationCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): + """ + Handles the creation of new car locations. + + This view allows authenticated and authorized users to add new car locations + to the system. The form allows users to specify details regarding the car's + location. Permissions are required to ensure only authorized users are + permitted to perform this action. Upon successful form submission, the + user is redirected to the car's detail page and a success message is displayed. + + :ivar model: The model associated with this view. + :type model: models.CarLocation + :ivar form_class: The form class used to create or update the model. + :type form_class: forms.CarLocationForm + :ivar template_name: Path to the template used by the view. + :type template_name: str + :ivar permission_required: List of permissions required to create a car location. + :type permission_required: list + """ model = models.CarLocation form_class = forms.CarLocationForm template_name = "inventory/car_location_form.html" @@ -1116,6 +1468,24 @@ class CarLocationCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateV class CarLocationUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): + """ + Provides functionality to update the location of a car. + + This view is a subclass of `LoginRequiredMixin` and `PermissionRequiredMixin`, ensuring + that only authenticated and authorized users with the appropriate permissions can access it. + It provides a form to update car location details and handles the necessary updates to the + location record, associating it with the appropriate car and owner. Upon successful update, + a success message is displayed to the user, and the user is redirected to the car detail page. + + :ivar model: The model associated with this view. Represents car location. + :type model: models.CarLocation + :ivar form_class: The form class used for updating car location. + :type form_class: forms.CarLocationForm + :ivar template_name: The path to the template used to render the view. + :type template_name: str + :ivar permission_required: Permissions required to access this view. + :type permission_required: list + """ model = models.CarLocation form_class = forms.CarLocationForm template_name = "inventory/car_location_form.html" @@ -1139,6 +1509,21 @@ class CarLocationUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateV class CarTransferCreateView(LoginRequiredMixin, CreateView): + """ + A view for creating car transfers. + + This class-based view allows authenticated users to create car transfers. It handles the + selection of cars and dealers involved in the transfer process. The view inherits from + `LoginRequiredMixin` to ensure only logged-in users can access it, and `CreateView` to + simplify the implementation of the creation logic. + + :ivar model: The model class associated with the view. + :type model: models.CarTransfer + :ivar form_class: The form class used to render and validate the input data. + :type form_class: forms.CarTransferForm + :ivar template_name: The path to the template used to render the view. + :type template_name: str + """ model = models.CarTransfer form_class = forms.CarTransferForm template_name = "inventory/car_transfer_form.html" @@ -1167,6 +1552,25 @@ class CarTransferCreateView(LoginRequiredMixin, CreateView): class CarTransferDetailView(LoginRequiredMixin, SuccessMessageMixin, DetailView): + """ + Provides a detailed view of a specific car transfer record. + + This class-based view is used to display details of a car transfer for + authenticated users. It ensures that only authorized users can access this + information and renders the data along with additional context such as + specific actions tied to the transfer. The view is associated with a Django + model for car transfers and uses a specific template to display information. + + :ivar model: The model associated with this view, which represents car + transfer records. + :type model: Type[models.CarTransfer] + :ivar template_name: The path to the template used for rendering the car + transfer details. + :type template_name: str + :ivar context_object_name: The name of the context object used in the template + to reference the car transfer record. + :type context_object_name: str + """ model = models.CarTransfer template_name = "inventory/transfer_details.html" context_object_name = "transfer" @@ -1179,6 +1583,18 @@ class CarTransferDetailView(LoginRequiredMixin, SuccessMessageMixin, DetailView) @login_required def car_transfer_approve(request, car_pk, transfer_pk): + """ + Approves or cancels a car transfer request based on the action parameter. This view + handles the workflow of updating the transfer status and notifying the involved parties + accordingly. If the transfer is canceled, it reverts the car status to "available" and + deactivates the transfer record. If approved, it notifies the recipient dealer and allows + the request to proceed for further actions. + + :param request: The HTTP request object containing metadata and the action parameter. + :param car_pk: Primary key of the car involved in the transfer. + :param transfer_pk: Primary key of the transfer request to approve or cancel. + :return: An HTTP response redirecting to the car detail page of the specified car. + """ car = get_object_or_404(models.Car, pk=car_pk) transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk) action = request.GET.get("action") @@ -1211,6 +1627,19 @@ def car_transfer_approve(request, car_pk, transfer_pk): @login_required def car_transfer_accept_reject(request, car_pk, transfer_pk): + """ + Handles the acceptance or rejection of a car transfer request. Based on the + `status` parameter obtained from the query string, the function updates the + transfer status to either 'accept' or 'reject'. If the transfer is accepted, it + initiates the car transfer process. Appropriate notifications are sent, and + activity records are created for both acceptance and rejection actions. + + :param request: The HTTP request object which contains metadata about + the request made by the user, including session and user information. + :param car_pk: The primary key of the car to be transferred. + :param transfer_pk: The primary key of the car transfer request to be processed. + :return: An HTTP redirect response to the 'inventory_stats' view. + """ car = get_object_or_404(models.Car, pk=car_pk) transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk) status = request.GET.get("status") @@ -1244,6 +1673,24 @@ def car_transfer_accept_reject(request, car_pk, transfer_pk): @login_required def CarTransferPreviewView(request, car_pk, transfer_pk): + """ + Handles the preview of car transfer details and ensures that a user has appropriate + permissions to view the transfer based on their associated dealer. + + This view checks if the car transfer's destination dealer matches the current user's + associated dealer type. If not, the user is redirected to the car detail page. Otherwise, + it renders the transfer preview page with the relevant transfer details. + + :param request: The HTTP request object + :type request: django.http.HttpRequest + :param car_pk: The primary key of the car related to the transfer + :type car_pk: int + :param transfer_pk: The primary key of the car transfer to preview + :type transfer_pk: int + :return: An HTTP response rendering the transfer preview page or redirecting + to the car detail page + :rtype: django.http.HttpResponse + """ transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk) if transfer.to_dealer != get_user_type(request): return redirect("car_detail", pk=car_pk) @@ -1251,6 +1698,20 @@ def CarTransferPreviewView(request, car_pk, transfer_pk): class CustomCardCreateView(LoginRequiredMixin, CreateView): + """ + Represents a view for creating a custom card associated with a specific car. + + This view ensures that the user is authenticated before allowing access and + associates the created custom card with a specific car. It handles form validation, + context data injection, and determines the success URL upon successful form submission. + + :ivar model: The model associated with the view, which is `CustomCard`. + :type model: models.CustomCard + :ivar form_class: The form class used to create a new instance of `CustomCard`. + :type form_class: forms.CustomCardForm + :ivar template_name: The name of the template used to render the view. + :type template_name: str + """ model = models.CustomCard form_class = forms.CustomCardForm template_name = "inventory/add_custom_card.html" @@ -1271,6 +1732,27 @@ class CustomCardCreateView(LoginRequiredMixin, CreateView): class CarRegistrationCreateView(LoginRequiredMixin, CreateView): + """ + Handles the creation of new car registration records. + + This view is responsible for rendering the car registration creation form, + validating input, and saving the data to the database. It ensures that the + current user is authenticated and associates the registration with a specific + car, identified by its primary key. + + Inherits from: + - LoginRequiredMixin: Ensures the user is logged in to access the view. + - CreateView: Provides built-in functionality for creating and saving model + instances. + + :ivar model: The model linked to this view, representing car registrations. + :type model: models.CarRegistration + :ivar form_class: The form class used for creating car registration instances. + :type form_class: forms.CarRegistrationForm + :ivar template_name: The path to the HTML template used for rendering the car + registration form. + :type template_name: str + """ model = models.CarRegistration form_class = forms.CarRegistrationForm template_name = "inventory/car_registration_form.html" @@ -1292,6 +1774,20 @@ class CarRegistrationCreateView(LoginRequiredMixin, CreateView): @login_required() def reserve_car_view(request, car_id): + """ + Handles car reservation requests. This view requires the user to be logged in + and processes only POST requests. When invoked, it checks if the specified car + is already reserved. If not, it proceeds to reserve the car for the user and + sends an appropriate response. If the car is already reserved or if the request + method is invalid, it provides corresponding error messages or responses. + + :param request: The HTTP request object. + :type request: HttpRequest + :param car_id: The unique identifier of the car to be reserved. + :type car_id: int + :return: A response indicating the result of the reservation process. + :rtype: HttpResponse or JsonResponse + """ if request.method == "POST": car = get_object_or_404(models.Car, pk=car_id) if car.is_reserved(): @@ -1306,6 +1802,24 @@ def reserve_car_view(request, car_id): @login_required def manage_reservation(request, reservation_id): + """ + Handles the management of a car reservation, providing options to renew or + cancel an existing reservation associated with the logged-in user. + + Renewing a reservation extends the reservation period by an additional + 24 hours. Canceling a reservation deletes it and updates the car's status + to AVAILABLE. All actions require a valid reservation and are performed + based on the current user's authentication and request type. + + :param request: Django HttpRequest object representing the client's request. + :type request: HttpRequest + :param reservation_id: The unique identifier of the car reservation to manage. + :type reservation_id: int + :return: On POST requests, returns an HTTP redirect or JSON response + based on the action performed. On other request methods, + returns a JSON response with an error message. + :rtype: JsonResponse or HttpResponseRedirect + """ reservation = get_object_or_404( models.CarReservation, pk=reservation_id, reserved_by=request.user ) @@ -1337,6 +1851,21 @@ def manage_reservation(request, reservation_id): class DealerDetailView(LoginRequiredMixin, DetailView): + """ + Represents a detailed view for a Dealer model. + + This class extends Django's `DetailView` to provide a detailed view of a dealer. + It includes additional context data such as the count of staff members, cars + associated with the dealer, available car makes, and dynamically fetched quotas. + The class also ensures that users must be logged in to access the detailed view. + + :ivar model: The model associated with this view (Dealer model). + :type model: django.db.models.Model + :ivar template_name: Path to the template used to render the view. + :type template_name: str + :ivar context_object_name: The name used to refer to the object in the template context. + :type context_object_name: str + """ model = models.Dealer template_name = "dealers/dealer_detail.html" context_object_name = "dealer" @@ -1373,6 +1902,25 @@ class DealerDetailView(LoginRequiredMixin, DetailView): class DealerUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): + """ + Handles the update functionality for the Dealer model. + + This class-based view allows authenticated users to update an existing + Dealer instance using a form. Upon successful update, a success message + is displayed, and the user is redirected to the detail view of the updated + Dealer. + + :ivar model: The model class associated with this view. + :type model: models.Dealer + :ivar form_class: The form class used for this view. + :type form_class: forms.DealerForm + :ivar template_name: The template used to render the form for this view. + :type template_name: str + :ivar success_url: The URL to redirect to after a successful form submission. + :type success_url: str + :ivar success_message: The message displayed upon a successful update. + :type success_message: str + """ model = models.Dealer form_class = forms.DealerForm template_name = "dealers/dealer_form.html" @@ -1384,6 +1932,31 @@ class DealerUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): + """ + View for displaying a list of customers. + + This class-based view is used to display a paginated and searchable list of + customers. It ensures that the user has the required permissions and is + logged in before they can access the view. The view fetches a list of + customers related to the current user's entity and applies any search + filters based on the user's query. The data is rendered using a specified + template. + + :ivar model: The model associated with the view, representing customer data. + :type model: type[CustomerModel] + :ivar home_label: The label used for navigation purposes in the UI. + :type home_label: str + :ivar context_object_name: The name of the context variable in the template. + :type context_object_name: str + :ivar paginate_by: Number of items displayed per page for pagination. + :type paginate_by: int + :ivar template_name: The path of the template used for rendering the view. + :type template_name: str + :ivar ordering: The default ordering applied to the queryset of customers. + :type ordering: list + :ivar permission_required: A list of permissions required to access the view. + :type permission_required: list + """ model = CustomerModel home_label = _("customers") context_object_name = "customers" @@ -1407,6 +1980,22 @@ class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): class CustomerDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): + """ + CustomerDetailView handles retrieving and presenting detailed information about + a specific customer. It ensures that the user is authenticated and has the + necessary permissions before accessing the customer's details. This view + provides context data including estimates and invoices related to the customer. + + :ivar model: The model associated with the view. + :type model: CustomerModel + :ivar template_name: The path to the template used for rendering the view. + :type template_name: str + :ivar context_object_name: The name of the variable in the template context + for the object being viewed. + :type context_object_name: str + :ivar permission_required: The list of permissions required to access this view. + :type permission_required: list[str] + """ model = CustomerModel template_name = "customers/view_customer.html" context_object_name = "customer" @@ -1429,6 +2018,22 @@ class CustomerDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView @login_required def add_note_to_customer(request, customer_id): + """ + This function allows authenticated users to add a note to a specific customer. The + note creation is handled by a form, which is validated after submission. If the form + is valid, the note is saved and associated with the specified customer. On successful + submission, the user is redirected to the customer detail page. If the request method + is not POST, an empty form is rendered. + + :param request: The HTTP request object containing metadata and the method type + (e.g., GET, POST). Should be an HttpRequest instance. + :param customer_id: The unique identifier (UUID) of the customer to whom the note + is to be added. + :return: An HTTP response. In the case of a successful POST request, the function + returns a redirect response to the customer detail page. For a GET or invalid + POST request, it renders the note form template with context including + the form and customer. + """ customer = get_object_or_404(CustomerModel, uuid=customer_id) if request.method == "POST": form = forms.NoteForm(request.POST) @@ -1448,6 +2053,25 @@ def add_note_to_customer(request, customer_id): @login_required def add_activity_to_customer(request, pk): + """ + Adds an activity to a specific customer. + + This function allows adding a new activity to a customer identified by their + primary key (`pk`). It retrieves the customer object, processes the form for + activity creation, and saves it. If the request method is POST, it validates + the form and associates the activity with the respective customer. Upon + successful save, it redirects to the customer detail view. If the request + method is GET, it renders a form for activity submission. + + :param request: The HTTP request object containing metadata about the request. + :type request: HttpRequest + :param pk: The primary key of the customer to which the activity will be added. + :type pk: int + :return: An HTTP response rendered with the activity form in the context of + the customer, or a redirect response to the customer detail view upon + successful activity creation. + :rtype: HttpResponse + """ customer = get_object_or_404(CustomerModel, pk=pk) if request.method == "POST": form = forms.ActivityForm(request.POST) @@ -1467,6 +2091,18 @@ def add_activity_to_customer(request, pk): @login_required @permission_required("django_ledger.add_customermodel", raise_exception=True) def CustomerCreateView(request): + """ + Handles the creation of a new customer within the system. This view ensures that proper permissions + and request methods are utilized. It provides feedback to the user about the success or failure of + the customer creation process. When the form is submitted and valid, it checks for duplicate + customers based on the email provided before proceeding with the customer creation. + + :param request: The HTTP request object containing metadata about the request initiated by the user. + :type request: HttpRequest + :return: The rendered form page or a redirect to the customer list page upon successful creation. + :rtype: HttpResponse + :raises PermissionDenied: If the user does not have the required permissions to access the view. + """ form = forms.CustomerForm() if request.method == "POST": form = forms.CustomerForm(request.POST) @@ -1521,6 +2157,28 @@ def CustomerCreateView(request): @login_required @permission_required("django_ledger.change_customermodel", raise_exception=True) def CustomerUpdateView(request, pk): + """ + Updates the details of an existing customer in the database. This view is + accessible only to logged-in users with the appropriate permissions. It + handles both GET (form rendering with pre-filled customer data) and POST + (submitting updates) requests. Data validation and customer updates are + conducted based on the received form data. + + :param request: The HTTP request object used to determine the request method, + access user session details, and provide request data such as POST content. + Expected to contain the updated customer data if request method is POST. + :type request: HttpRequest + + :param pk: The primary key of the CustomerModel object that is to be updated. + :type pk: int + + :return: A rendered HTML template displaying the customer form pre-filled + with existing data if a GET request is received. On successful form + submission (POST request), redirects to the customer list page + and displays a success message. In case of invalid data or errors, + returns the rendered form template with the validation errors. + :rtype: HttpResponse + """ customer = get_object_or_404(CustomerModel, pk=pk) if request.method == "POST": # form = forms.CustomerForm(request.POST, instance=customer) @@ -1563,6 +2221,21 @@ def CustomerUpdateView(request, pk): @login_required def delete_customer(request, pk): + """ + Deletes a customer from the system and deactivates the corresponding user account. + + This function retrieves a customer object based on the primary key (pk), + sets their active status to False, and deactivates the linked user account + (using the email associated with the customer). After saving these changes, + it displays a success message to the user and redirects to the customer list page. + + :param request: A HttpRequest object containing metadata about the request. + :type request: HttpRequest + :param pk: Primary key of the customer to be deleted. + :type pk: int + :return: A redirect response to the customer list page. + :rtype: HttpResponseRedirect + """ customer = get_object_or_404(models.CustomerModel, pk=pk) user = User.objects.get(email=customer.email) customer.active = False @@ -1575,6 +2248,31 @@ def delete_customer(request, pk): class VendorListView(LoginRequiredMixin, ListView): + """ + Represents a view for displaying a paginated list of vendors, accessible only + to authenticated users. + + This class inherits from ``LoginRequiredMixin`` and ``ListView`` and is + designed to display a paginated list of vendors associated with a user. It + utilizes filters and search capabilities based on user input and is rendered + using a specified template. The list is ordered by the creation date of the + vendors in descending order. + + :ivar model: The model that this view interacts with. + :type model: VendorModel + :ivar context_object_name: The name of the context variable used in the + template for the list of vendors. + :type context_object_name: str + :ivar paginate_by: The number of vendors to display per page in the paginated + view. + :type paginate_by: int + :ivar template_name: The path to the template used to render the list of + vendors. + :type template_name: str + :ivar ordering: The default ordering applied to the queryset. Vendors are + ordered by their creation date in descending order. + :type ordering: list + """ model = VendorModel context_object_name = "vendors" paginate_by = 10 @@ -1590,6 +2288,20 @@ class VendorListView(LoginRequiredMixin, ListView): @login_required def vendorDetailView(request, pk): + """ + Fetches and renders the detail view for a specific vendor. + + This function retrieves a vendor object based on the primary key (pk) + provided in the URL, ensures the user is logged in to access the + view, and renders the vendor detail template with the vendor's context. + + :param request: The HTTP request object. + :type request: HttpRequest + :param pk: The primary key of the vendor to retrieve. + :type pk: int + :return: An HttpResponse object containing the rendered vendor detail page. + :rtype: HttpResponse + """ vendor = get_object_or_404(models.Vendor, pk=pk) return render( request, template_name="vendors/view_vendor.html", context={"vendor": vendor} @@ -1601,6 +2313,24 @@ class VendorCreateView( SuccessMessageMixin, CreateView, ): + """ + Handles the creation of a new vendor. + + This view allows authenticated users to create a new vendor entry. It uses the + Vendor model and its corresponding form for creation. A success message is displayed + upon successful creation, and the user is redirected to the list of vendors. + + :ivar model: The model associated with this view. + :type model: models.Vendor + :ivar form_class: The form class used for creating a vendor. + :type form_class: forms.VendorForm + :ivar template_name: The name of the template used to render the view. + :type template_name: str + :ivar success_url: The URL to redirect to after successful vendor creation. + :type success_url: str + :ivar success_message: The message displayed upon successful creation. + :type success_message: str + """ model = models.Vendor form_class = forms.VendorForm template_name = "vendors/vendor_form.html" @@ -1619,6 +2349,26 @@ class VendorUpdateView( SuccessMessageMixin, UpdateView, ): + """ + View for updating vendor information. + + This class-based view is used to handle the update of vendor information. + It ensures that only authenticated users can access this page, provides + form handling functionality, and includes a success message upon completion. + The form fields are dynamically populated and validated before vendor + information is updated in the database. + + :ivar model: The model that this view is based on. + :type model: models.Vendor + :ivar form_class: The form class used to validate and process vendor data. + :type form_class: forms.VendorForm + :ivar template_name: The path to the HTML template used to render this view. + :type template_name: str + :ivar success_url: The URL to redirect to after successful data submission. + :type success_url: str + :ivar success_message: The message to display upon successful data update. + :type success_message: str + """ model = models.Vendor form_class = forms.VendorForm template_name = "vendors/vendor_form.html" @@ -1648,6 +2398,20 @@ class VendorUpdateView( @login_required def delete_vendor(request, pk): + """ + Deletes an existing vendor record from the database. + + This function allows users with valid authentication to delete a specified + vendor object by its primary key. Upon successful deletion, a success message + is displayed, and the user is redirected to the vendor list page. + + :param request: HttpRequest object containing metadata about the request. + :type request: HttpRequest + :param pk: Primary key of the vendor object to be deleted. + :type pk: int + :return: HttpResponseRedirect object for redirecting to the vendor list page. + :rtype: HttpResponseRedirect + """ vendor = get_object_or_404(models.Vendor, pk=pk) # vendor.active = False vendor.delete() @@ -1657,6 +2421,25 @@ def delete_vendor(request, pk): # group class GroupListView(LoginRequiredMixin, ListView): + """ + Represents a view for listing groups for a logged-in user. + + This view is designed for authenticated users, inheriting functionalities + from `LoginRequiredMixin` to enforce authentication checks and `ListView` + to handle the display of a list of groups. It queries the groups related + to the user type (dealer) and displays the results in a paginated format + using a specified template. + + :ivar model: The model used for retrieving group data. + :type model: type + :ivar context_object_name: The name of the context variable used to contain + the queryset of groups. + :type context_object_name: str + :ivar paginate_by: The number of groups listed on each page. + :type paginate_by: int + :ivar template_name: The path to the template used for rendering the group list. + :type template_name: str + """ model = models.CustomGroup context_object_name = "groups" paginate_by = 10 @@ -1668,6 +2451,23 @@ class GroupListView(LoginRequiredMixin, ListView): class GroupDetailView(LoginRequiredMixin, DetailView): + """ + Represents the detail view for a specific group. + + Handles the display of detailed information about a specific CustomGroup + instance. Requires the user to be logged in to access this view. The class + is designed to fetch a specific group instance and render its details using + the specified template. + + :ivar model: The model that represents the group details being viewed. + :type model: models.CustomGroup + :ivar template_name: The name of the template used to render the group detail + view. + :type template_name: str + :ivar context_object_name: The context variable name under which the group + instance will be available in the template. + :type context_object_name: str + """ model = models.CustomGroup template_name = "groups/group_detail.html" context_object_name = "group" @@ -1678,6 +2478,24 @@ class GroupCreateView( SuccessMessageMixin, CreateView, ): + """ + Represents a view for creating a new group in the application. + + This class provides a form-based interface for authenticated users to create + new group entities. It utilizes mixins to enforce login requirements and display + success messages upon successful group creation. + + :ivar model: The model associated with the view. + :type model: models.CustomGroup + :ivar form_class: The form class used to create a new group. + :type form_class: forms.GroupForm + :ivar template_name: The template used to render the form for creating a group. + :type template_name: str + :ivar success_url: The URL to be redirected to when the group creation is successful. + :type success_url: str + :ivar success_message: A message displayed upon successful creation of a group. + :type success_message: str + """ model = models.CustomGroup form_class = forms.GroupForm template_name = "groups/group_form.html" @@ -1699,6 +2517,27 @@ class GroupUpdateView( SuccessMessageMixin, UpdateView, ): + """ + Handles the update of group objects with permission control and custom behavior. + + This view allows users with login credentials to update existing group + objects. It ensures that changes made to groups adhere to default + permissions and adds necessary naming conventions based on user type. + Upon successful update, it redirects the user to the group list view and + displays a success message. + + :ivar model: Specifies the model to be used for the update operation. + :type model: models.CustomGroup + :ivar form_class: Specifies the form class to be used for validation + and data manipulation. + :type form_class: forms.GroupForm + :ivar template_name: File path to the template that renders the form view. + :type template_name: str + :ivar success_url: URL to redirect upon successful form submission. + :type success_url: str + :ivar success_message: Message displayed upon successful update of a group. + :type success_message: str + """ model = models.CustomGroup form_class = forms.GroupForm template_name = "groups/group_form.html" @@ -1716,6 +2555,18 @@ class GroupUpdateView( @login_required def GroupDeleteview(request, pk): + """ + Handles the deletion of a specific group instance. This view ensures that only + authenticated users can perform the deletion. Upon successful deletion, a + success message is displayed, and the user is redirected to the group list page. + + :param request: The HTTP request object that contains metadata about the + request context and user information. Must be an authenticated user. + :param pk: The primary key of the group instance to be deleted. + It specifies which group to retrieve and delete. + :return: The HTTP response that redirects the user to the group list page + after the group is successfully deleted. + """ group = get_object_or_404(models.CustomGroup, pk=pk) group.delete() messages.success(request, _("Group deleted successfully")) @@ -1724,6 +2575,27 @@ def GroupDeleteview(request, pk): @login_required def GroupPermissionView(request, pk): + """ + Handles the view for adding or modifying permissions of a specific group. This view + fetches the group based on the primary key passed as a parameter, and either displays + a form for editing permissions or processes the submitted permissions. + + If the request method is POST, the permissions of the group are cleared and updated + based on the submitted data. A success message is displayed upon completion, and + the user is redirected to the group's detail page. + + In case of a GET request, the view renders the form pre-filled with the group's + current permissions. + + :param request: The HTTP request object. + :type request: HttpRequest + :param pk: The primary key of the group whose permissions are being modified. + :type pk: int + :return: The HTTP response depending on the request type. For GET requests, renders + the permission form for the specified group. For POST requests, clears and updates + the group's permissions and redirects to the group's detail page. + :rtype: HttpResponse + """ group = get_object_or_404(models.CustomGroup, pk=pk) if request.method == "POST": form = forms.PermissionForm(request.POST) @@ -1742,6 +2614,21 @@ def GroupPermissionView(request, pk): # Users @login_required def UserGroupView(request, pk): + """ + Handles the assignment of user groups to a specific staff member. This view + allows updating the groups a staff member belongs to via a form submission. + It processes both GET and POST requests, ensuring appropriate group + assignments are managed and feedback is provided to the user via messages. + + :param request: HttpRequest object representing the HTTP request. + :type request: HttpRequest + :param pk: Primary key of the staff member whose groups are being updated. + :type pk: int + + :return: Renders the user group form for GET requests or redirects to the + user detail page after successful submission for POST requests. + :rtype: HttpResponse or HttpResponseRedirect + """ staff = get_object_or_404(models.Staff, pk=pk) if request.method == "POST": @@ -1763,6 +2650,24 @@ def UserGroupView(request, pk): class UserListView(LoginRequiredMixin, ListView): + """ + Represents a view for listing users with pagination and filters. + + This class is designed to display a list view of staff users for a specific + dealer. It supports pagination and search filtering functionality. This view + requires the user to be logged in, as it inherits from `LoginRequiredMixin`. + + :ivar model: The model that the view will work with. + :type model: Type[models.Staff] + :ivar context_object_name: The name of the context variable that will contain + the list of users. + :type context_object_name: str + :ivar paginate_by: The number of items to display per page. + :type paginate_by: int + :ivar template_name: The path to the template used for rendering the view's + page. + :type template_name: str + """ model = models.Staff context_object_name = "users" paginate_by = 10 @@ -1776,6 +2681,22 @@ class UserListView(LoginRequiredMixin, ListView): class UserDetailView(LoginRequiredMixin, DetailView): + """ + Represents a detailed view for displaying user-specific information. + + This class-based view is used to display detailed information for + a specific user. It ensures that only logged-in users can access + this view, leveraging the ``LoginRequiredMixin`` for authentication. + The data for the view comes from the ``models.Staff`` model and + is rendered using the specified HTML template. + + :ivar model: The data model associated with this view. + :type model: models.Staff + :ivar template_name: Path to the HTML template used for rendering the view. + :type template_name: str + :ivar context_object_name: Name of the context variable available in the template. + :type context_object_name: str + """ model = models.Staff template_name = "users/user_detail.html" context_object_name = "user_" @@ -1786,6 +2707,28 @@ class UserCreateView( SuccessMessageMixin, CreateView, ): + """ + Manages the creation of a new staff user and their associated details. + + This view is responsible for handling the creation of a new staff user in the + system. It ensures quota limits are adhered to, validates form input, saves user + details, and associates them with services and permissions. + + :ivar model: The database model associated with this view. + :type model: django.db.models.Model + + :ivar form_class: The form class used to validate and create a staff user. + :type form_class: type + + :ivar template_name: The template used to render the user creation form. + :type template_name: str + + :ivar success_url: The URL to redirect to after successfully creating a user. + :type success_url: str + + :ivar success_message: The success message displayed upon user creation. + :type success_message: str + """ model = models.Staff form_class = forms.StaffForm template_name = "users/user_form.html" @@ -1833,6 +2776,26 @@ class UserUpdateView( SuccessMessageMixin, UpdateView, ): + """ + UserUpdateView updates information for a user with specific details. + + This view handles updating user details such as email, name, phone number, and services + offered. It leverages Django's `UpdateView` to manage the update operation. The view + disables editing of email addresses, initializes specific fields with data associated + with the user, and processes input to manage related services. Validation of the form and + saving changes are customized to align with specific business logic. + + :ivar model: The model class associated with the view, used for retrieving the object to update. + :type model: models.Staff + :ivar form_class: The form class used to render and process the form for updating the user. + :type form_class: forms.StaffForm + :ivar template_name: The template used for rendering the form in the UI. + :type template_name: str + :ivar success_url: URL to redirect to after a successful form submission. + :type success_url: str + :ivar success_message: Message displayed to the user after a successful update. + :type success_message: str + """ model = models.Staff form_class = forms.StaffForm template_name = "users/user_form.html" @@ -1876,6 +2839,16 @@ class UserUpdateView( @login_required def UserDeleteview(request, pk): + """ + Deletes a user and its associated staff member from the database and redirects + to the user list page. Displays a success message upon successful deletion + of the user. + + :param request: The HTTP request object representing the incoming request. + :param pk: The primary key (ID) of the staff member to be deleted. + :return: An HTTP redirect to the user list page. + + """ staff = get_object_or_404(models.Staff, pk=pk) staff.staff_member.delete() staff.delete() @@ -1884,6 +2857,24 @@ def UserDeleteview(request, pk): class OrganizationListView(LoginRequiredMixin, ListView): + """ + Represents a view to display a paginated list of organizations for a dealer. + + This class inherits from `LoginRequiredMixin` to ensure that only + authenticated users can access the list, and from `ListView` to provide + a generic implementation to render lists of database objects. It is designed + specifically to show organizations related to a dealer entity and includes + search functionality based on a query parameter. + + :ivar model: Specifies the model to fetch data from. + :type model: type[CustomerModel] + :ivar template_name: The template used to render the organization list page. + :type template_name: str + :ivar context_object_name: The name of the context variable for the organization list. + :type context_object_name: str + :ivar paginate_by: The number of organizations displayed per page. + :type paginate_by: int + """ model = CustomerModel template_name = "organizations/organization_list.html" context_object_name = "organizations" @@ -1900,6 +2891,23 @@ class OrganizationListView(LoginRequiredMixin, ListView): class OrganizationDetailView(LoginRequiredMixin, DetailView): + """ + Handles displaying detailed information about an organization. + + This view is a detailed representation of an individual organization, which + provides the necessary data for templates to render the information. It is + intended to be used for displaying details of a `CustomerModel` instance. + Requires the user to be logged in to access this view. + + :ivar model: Specifies the model that this view will interact with. + :type model: type[CustomerModel] + :ivar template_name: The name of the template to be used for rendering the + organization's detail information. + :type template_name: str + :ivar context_object_name: The context variable name to be used in the + template for accessing the organization's data. + :type context_object_name: str + """ model = CustomerModel template_name = "organizations/organization_detail.html" context_object_name = "organization" @@ -1907,6 +2915,20 @@ class OrganizationDetailView(LoginRequiredMixin, DetailView): @login_required def OrganizationCreateView(request): + """ + Handles the creation of a new organization via a web form. This view allows the + authenticated user to submit data for creating an organization. If a POST request + is received, it validates the data, checks for duplicate organizations, and + creates a customer linked to the organization, including its associated + information such as address, phone number, and logo. Upon success, the user + is redirected to the organization list, and a success message is displayed. + + :param request: The HTTP request object containing data for creating an organization. + :type request: HttpRequest + :return: An HTTP response object rendering the organization create form page or + redirecting the user after a successful creation. + :rtype: HttpResponse + """ if request.method == "POST": form = forms.OrganizationForm(request.POST) if CustomerModel.objects.filter(email=request.POST["email"]).exists(): @@ -1948,6 +2970,22 @@ def OrganizationCreateView(request): @login_required def OrganizationUpdateView(request, pk): + """ + Handles the update of an organization instance. This view fetches the organization + based on the provided primary key (pk) and renders a form for editing the + organization attributes. When a POST request is made, this view validates and + processes the form data, updates the organization instance, and saves the changes. + If the request method is not POST, it initializes the form with existing organization + data for rendering. + + :param request: The HTTP request object. Must be authenticated via login. + :type request: HttpRequest + :param pk: The primary key of the organization to be updated. + :type pk: int + :return: An HTTP response object. Either renders the organization form or redirects + to the organization list upon successful update. + :rtype: HttpResponse + """ organization = get_object_or_404(CustomerModel, pk=pk) if request.method == "POST": form = forms.OrganizationForm(request.POST) @@ -1997,6 +3035,19 @@ def OrganizationUpdateView(request, pk): # success_message = "Organization deleted successfully." @login_required def OrganizationDeleteView(request, pk): + """ + Handles the deletion of an organization based on the provided primary key (pk). Looks up + the organization and its corresponding user by email, attempts to delete both, and provides + appropriate success or error feedback to the user. In case of failure, an error message is shown, + while successful deletion redirects to the organization list. + + :param request: The HTTP request object containing metadata about the request. + :type request: HttpRequest + :param pk: The primary key of the organization to be deleted. + :type pk: int + :return: An HTTP response redirecting to the organization list view. + :rtype: HttpResponseRedirect + """ organization = get_object_or_404(CustomerModel, pk=pk) try: User.objects.get(email=organization.email).delete() @@ -2009,6 +3060,24 @@ def OrganizationDeleteView(request, pk): class RepresentativeListView(LoginRequiredMixin, ListView): + """ + Represents a view for displaying a paginated list of representatives. + + This view handles the functionality of displaying and paginating a list + of representatives for the logged-in user. It utilizes search filters to + allow querying representatives based on the search term provided in the + request. The view restricts access to logged-in users only. + + :ivar model: The model associated with this view. + :type model: models.Representative + :ivar template_name: Name of the template used to render the view. + :type template_name: str + :ivar context_object_name: Name of the context variable used to access + representatives in the template. + :type context_object_name: str + :ivar paginate_by: The number of representatives displayed per page. + :type paginate_by: int + """ model = models.Representative template_name = "representatives/representative_list.html" context_object_name = "representatives" @@ -2022,12 +3091,48 @@ class RepresentativeListView(LoginRequiredMixin, ListView): class RepresentativeDetailView(LoginRequiredMixin, DetailView): + """ + Represents a detailed view for Representative instances. + + This class-based view is used to provide detailed representation for + ``Representative`` model instances. It ensures that only authenticated + users can access the view by utilizing ``LoginRequiredMixin``. The + template used to render the view and the context name for the object + are also specified for use in a Django template. + + :ivar model: The model associated with this view. + :type model: Type of the model (models.Representative) + :ivar template_name: The path to the template used to render this view. + :type template_name: str + :ivar context_object_name: The name of the context variable containing + the object. + :type context_object_name: str + """ model = models.Representative template_name = "representatives/representative_detail.html" context_object_name = "representative" class RepresentativeCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): + """ + Handles the creation of a Representative object. + + This class is a view that provides the interface and functionality to create + a new representative in the application. It is designed to ensure that only + authenticated users with a valid dealer association can create representatives. + A success message is displayed upon successful creation of a representative. + + :ivar model: The model that this view will work with, which is Representative. + :type model: django.db.models.Model + :ivar form_class: The form class used for creating a representative. + :type form_class: django.forms.ModelForm + :ivar template_name: The name of the template used to render the create view. + :type template_name: str + :ivar success_url: The URL to redirect to upon successful form submission. + :type success_url: str + :ivar success_message: The success message displayed after creating a representative. + :type success_message: str + """ model = models.Representative form_class = forms.RepresentativeForm template_name = "representatives/representative_form.html" @@ -2044,6 +3149,32 @@ class RepresentativeCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateVi class RepresentativeUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): + """ + Provides functionality for updating a representative's details. + + This class-based view allows authenticated users, equipped with + the required permissions, to update information for a specific + representative. It uses a predefined form for input, renders the + appropriate update template, and provides success messaging upon + successful completion. It also redirects to a predefined success + URL once the update operation is complete. + + :ivar model: The model representing a representative that is being + updated. + :type model: Type[models.Representative] + :ivar form_class: The form class used for providing input fields to + update representative details. + :type form_class: Type[forms.RepresentativeForm] + :ivar template_name: The template used to render the representative + update page. + :type template_name: str + :ivar success_url: The URL to which the user is redirected following + a successful update. + :type success_url: str + :ivar success_message: The message displayed upon a successful update + operation. + :type success_message: str + """ model = models.Representative form_class = forms.RepresentativeForm template_name = "representatives/representative_form.html" @@ -2052,6 +3183,23 @@ class RepresentativeUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateVi class RepresentativeDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): + """ + Handles the deletion of a representative. + + This view provides functionality to delete a representative from the system. + It ensures that only authenticated users can perform the deletion and displays + a success message upon successful deletion. The deletion is confirmed via a + template, and upon success, the user is redirected to the representative list page. + + :ivar model: The model representing the representative. + :type model: models.Representative + :ivar template_name: The template used to confirm the deletion of a representative. + :type template_name: str + :ivar success_url: The URL to redirect to after successful deletion. + :type success_url: str + :ivar success_message: The success message displayed after a representative is deleted. + :type success_message: str + """ model = models.Representative template_name = "representatives/representative_confirm_delete.html" success_url = reverse_lazy("representative_list") @@ -2060,6 +3208,28 @@ class RepresentativeDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteVi # BANK ACCOUNT class BankAccountListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): + """ + Provides a view for listing bank accounts associated with a specific entity + and applying search filters if provided. Ensures user authentication and + required permissions are implemented. + + This view is used to display the list of bank accounts in a paginated format. + It filters the list of bank accounts based on the associated entity of the + dealer (user type) and applies search filters when needed. It requires the + user to be logged in and have the specified permissions to view the resource. + + :ivar model: The model to fetch data for the bank account listing. + :type model: type[BankAccountModel] + :ivar template_name: The template used to render the bank account list. + :type template_name: str + :ivar context_object_name: The name of the context variable containing the + list of bank accounts. + :type context_object_name: str + :ivar paginate_by: The number of records displayed per page in pagination. + :type paginate_by: int + :ivar permission_required: The required permissions to access the view. + :type permission_required: list[str] + """ model = BankAccountModel template_name = "ledger/bank_accounts/bank_account_list.html" context_object_name = "bank_accounts" @@ -2076,6 +3246,30 @@ class BankAccountListView(LoginRequiredMixin, PermissionRequiredMixin, ListView) class BankAccountCreateView( LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView ): + """ + Represents a view for creating a new bank account. + + This class is a Django CreateView that handles the creation of a new bank account + within the system. It integrates functionalities for login requirements, permission + enforcement, and success messages upon successful creation. The view is initialized + with a specific model, form class, and template to render the form. A success message + and redirection URL are specified for after the account creation. Additionally, it + checks permissions required to access this view. + + :ivar model: The model to be used for the creation of a bank account. + :type model: BankAccountModel + :ivar form_class: The form class to be used for validating and handling bank account + creation. + :type form_class: BankAccountCreateForm + :ivar template_name: The template to render the form for bank account creation. + :type template_name: str + :ivar success_url: The URL to redirect to after successful bank account creation. + :type success_url: str + :ivar success_message: The success message to display upon successful creation. + :type success_message: str + :ivar permission_required: List of permissions required to access the view. + :type permission_required: list + """ model = BankAccountModel form_class = BankAccountCreateForm template_name = "ledger/bank_accounts/bank_account_form.html" @@ -2098,6 +3292,24 @@ class BankAccountCreateView( class BankAccountDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): + """ + Manages the detailed view of a bank account. + + Provides a detailed view for a specific bank account by leveraging Django's + DetailView. This class ensures that only authenticated users with the relevant + permissions can access the view. It is tailored for displaying necessary + details about the bank account. + + :ivar model: The model associated with the view. + :type model: type[BankAccountModel] + :ivar template_name: Path to the HTML template used to render the view. + :type template_name: str + :ivar context_object_name: The name of the context variable used to represent + the specific bank account instance in the template. + :type context_object_name: str + :ivar permission_required: List of permissions required to access the view. + :type permission_required: list[str] + """ model = BankAccountModel template_name = "ledger/bank_accounts/bank_account_detail.html" context_object_name = "bank_account" @@ -2107,6 +3319,28 @@ class BankAccountDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailV class BankAccountUpdateView( LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView ): + """ + Represents a view for updating bank account details in the system. + + This class is responsible for providing functionality to update existing + bank account information within the system. It ensures that only logged-in + users with the appropriate permissions can access and update the data. The + view also provides user feedback regarding successful updates using a + success message and redirects to the bank account list upon completion. + + :ivar model: Defines the model associated with the view. + :type model: BankAccountModel + :ivar form_class: Specifies the form class used for updating bank account information. + :type form_class: BankAccountUpdateForm + :ivar template_name: Path to the template used for rendering the update form. + :type template_name: str + :ivar success_url: URL to redirect to upon successful update of a bank account. + :type success_url: str + :ivar success_message: Message displayed to the user upon successful update. + :type success_message: str + :ivar permission_required: List of permissions required to access the update view. + :type permission_required: list + """ model = BankAccountModel form_class = BankAccountUpdateForm template_name = "ledger/bank_accounts/bank_account_form.html" @@ -2125,6 +3359,25 @@ class BankAccountUpdateView( @login_required def bank_account_delete(request, pk): + """ + Delete a bank account entry from the database. + + This view handles the deletion of a bank account record specified by its + primary key (pk). It renders a deletion confirmation page and processes the + deletion if the request method is POST. Upon successful deletion, the user is + redirected to the list of bank accounts and a success message is displayed. + + :param request: The HTTP request object representing the client's request. + It contains data such as request type (GET or POST) and session + information. + :type request: HttpRequest + :param pk: The primary key of the bank account model instance to be deleted. + :type pk: int + :return: Returns an HttpResponse object. This can be an HTTP redirect to the + bank account list page upon successful deletion, or an HTML response + rendering the confirmation template if accessed via GET. + :rtype: HttpResponse + """ bank_account = get_object_or_404(BankAccountModel, pk=pk) if request.method == "POST": bank_account.delete() @@ -2141,6 +3394,28 @@ def bank_account_delete(request, pk): class AccountListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): + """ + View for displaying a list of accounts. + + AccountListView is responsible for displaying a paginated list of accounts + that users can view based on their permissions. This view is restricted + to users who are logged in and have the required permissions. It also + provides functionality to filter and search the available accounts. + + :ivar model: The Django model used for this view. Determines which + database records are displayed in the account list. + :type model: type[Model] + :ivar template_name: Path to the template file used for rendering the + account list view. + :type template_name: str + :ivar context_object_name: Name of the variable containing the accounts + list passed to the template. + :type context_object_name: str + :ivar paginate_by: Number of accounts to display per page. + :type paginate_by: int + :ivar permission_required: Permissions required to access this view. + :type permission_required: list[str] + """ model = AccountModel template_name = "ledger/coa_accounts/account_list.html" context_object_name = "accounts" @@ -2157,6 +3432,37 @@ class AccountListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): class AccountCreateView( LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView ): + """ + View for creating an account in the ledger system. + + This class provides functionality for rendering a form to create a new account, + validating the form, setting default account properties based on the current + user's entity, and saving the new account. It is designed to ensure that only + authorized users with the required permissions can create accounts. The view + also provides feedback to the user upon successful account creation. + + :ivar model: Defines the model associated with this view. In this case, the model + represents accounts in the ledger system. + :type model: type + + :ivar form_class: Defines the form class used for creating new accounts. This + ensures data validation and captures input for new account creation. + :type form_class: type + + :ivar template_name: Specifies the template used to render the account creation form. + :type template_name: str + + :ivar success_url: URL to which the user is redirected upon successful account creation. + :type success_url: str + + :ivar success_message: Feedback message displayed to the user upon successfully + creating an account. + :type success_message: str + + :ivar permission_required: List of permissions required to access this view. Enforces + the permission checking to prevent unauthorized access. + :type permission_required: list + """ model = AccountModel form_class = AccountModelCreateForm template_name = "ledger/coa_accounts/account_form.html" @@ -2186,6 +3492,29 @@ class AccountCreateView( class AccountDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): + """ + Represents the detailed view for an account with additional context data related to account + transactions and permissions. + + This class provides a detailed view for an account in the system. It includes functionality + for generating contextual data, managing permissions, and customizing rendering templates. + The view calculates total debits, credits, and provides transaction details for the account. + + :ivar model: The Django model class representing the account data. + :type model: Type[AccountModel] + :ivar template_name: The path to the template used to render this view. + :type template_name: str + :ivar context_object_name: The context variable name representing the account object. + :type context_object_name: str + :ivar slug_field: The field in the model used to retrieve the account instance based on a slug. + :type slug_field: str + :ivar DEFAULT_TXS_DAYS: Default number of days to filter transactions. + :type DEFAULT_TXS_DAYS: int + :ivar permission_required: Permissions required to access this view. + :type permission_required: list[str] + :ivar extra_context: Additional context data passed to the template. + :type extra_context: dict + """ model = AccountModel template_name = "ledger/coa_accounts/account_detail.html" context_object_name = "account" @@ -2227,6 +3556,28 @@ class AccountDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView) class AccountUpdateView( LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView ): + """ + Represents a view for updating an existing account. + + This class provides functionality to update an account's details using a form. + The user must be logged in and have the necessary permissions to access this + view. Upon successful update of the account, a success message is displayed + and the user is redirected to the account list page. + + :ivar model: The model associated with this view which represents the account. + :type model: AccountModel + :ivar form_class: The form class used for updating account details. + :type form_class: AccountModelUpdateForm + :ivar template_name: The path to the template used for rendering the update view. + :type template_name: str + :ivar success_url: The URL to redirect to upon success. + :type success_url: str + :ivar success_message: The success message displayed after updating an account + successfully. + :type success_message: str + :ivar permission_required: List of permissions required to access the view. + :type permission_required: list of str + """ model = AccountModel form_class = AccountModelUpdateForm template_name = "ledger/coa_accounts/account_form.html" @@ -2244,6 +3595,18 @@ class AccountUpdateView( @login_required @permission_required("inventory.view_carfinance") def account_delete(request, pk): + """ + Handles the deletion of an account object identified by its primary key (pk). Ensures + that the user has the necessary permissions to perform the deletion. Successfully + deletes the account and redirects to the account list view with a success message. + + :param request: The HTTP request object representing the current user and request data. + :type request: HttpRequest + :param pk: The primary key of the account to be deleted. + :type pk: int + :return: An HTTP redirect response to the account list page. + :rtype: HttpResponse + """ account = get_object_or_404(AccountModel, pk=pk) account.delete() @@ -2255,6 +3618,19 @@ def account_delete(request, pk): @login_required @permission_required("inventory.view_lead", raise_exception=True) def sales_list_view(request): + """ + Handles the retrieval and presentation of a paginated list of item transactions for + sales, specific to the logged-in user's entity. Requires the user to have appropriate + permissions to view the list. + + :param request: The HTTP request object containing metadata about the request, + such as HTTP method, user credentials, and sent data. + :type request: HttpRequest + + :return: An HTTP response with the rendered sales list page containing the paginated + item transactions specific to the user's entity. + :rtype: HttpResponse + """ dealer = get_user_type(request) entity = dealer.entity @@ -2271,6 +3647,26 @@ def sales_list_view(request): # Estimates class EstimateListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): + """ + Handles the display of a paginated list of estimates for a specific entity. + + This class-based view displays estimates related to an entity associated + with the logged-in user. It renders a paginated list of estimates on a + template and allows filtering of estimates based on their status. Access + to this view is restricted to users with the required permissions. + + :ivar model: The database model associated with the view. + :type model: Model + :ivar template_name: The path to the template used for rendering the view. + :type template_name: str + :ivar context_object_name: The name of the context variable representing + the list of estimates. + :type context_object_name: str + :ivar paginate_by: The number of estimates displayed per page. + :type paginate_by: int + :ivar permission_required: List of permissions required to view this page. + :type permission_required: list + """ model = EstimateModel template_name = "sales/estimates/estimate_list.html" context_object_name = "estimates" @@ -2291,6 +3687,25 @@ class EstimateListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): @login_required @permission_required("django_ledger.add_estimatemodel", raise_exception=True) def create_estimate(request, pk=None): + """ + Creates a new estimate based on the provided data and saves it. This function processes + a POST request and expects a JSON payload containing details of the estimate such as + title, customer, terms, items, and quantities. It validates the input data, ensures + availability of stocks, and updates or creates the corresponding estimate in the database. + + If `pk` is provided, it links the created estimate with an existing opportunity. It handles + the reservation of cars and updates the stock information accordingly. + + :param request: The HttpRequest object containing user-specific data and state. + :type request: HttpRequest + :param pk: An optional primary key of the existing opportunity to associate with + the created estimate. + :type pk: int, optional + :return: A JsonResponse object with status and either the created quotation URL + or an error message. If the request method is not POST, it renders the + estimate creation form. + :rtype: JsonResponse or HttpResponse + """ dealer = get_user_type(request) entity = dealer.entity @@ -2470,6 +3885,24 @@ def create_estimate(request, pk=None): class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): + """ + Represents the detailed view for an EstimateModel instance. + + This class provides functionality to display detailed information about + a specific EstimateModel instance. It ensures the user is authenticated + and has the required permissions to access the estimate details. The class + also integrates additional financial and invoice-related data into the + context for more comprehensive display and functionality. + + :ivar model: Specifies the model associated with the view. + :type model: ModelBase + :ivar template_name: Path to the template used for rendering the detailed view. + :type template_name: str + :ivar context_object_name: Name used to refer to the EstimateModel instance in the context. + :type context_object_name: str + :ivar permission_required: List of permissions required to access the view. + :type permission_required: list + """ model = EstimateModel template_name = "sales/estimates/estimate_detail.html" context_object_name = "estimate" @@ -2491,6 +3924,24 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView @login_required @permission_required("inventory.add_saleorder", raise_exception=True) def create_sale_order(request, pk): + """ + Creates a sale order for a given estimate and updates associated item and car data. + + This view is responsible for handling the submission of a sale order form linked to + a specific estimate. It ensures that the estimate is approved if not already, updates + the status of the related car items as sold, and redirects to the estimate's detailed + view upon successful creation of the sale order. If the request method is not POST, it + renders the form for the user to input sale order details, along with other contextual + information like estimate data and car finance details. + + :param request: HTTP request object. + :type request: HttpRequest + :param pk: Primary key of the estimate to create a sale order for. + :type pk: int + :return: An HTTP response rendering the sale order form if the method is GET or invalid + POST data, or redirects to the estimate detail view upon successful creation. + :rtype: HttpResponse + """ estimate = get_object_or_404(EstimateModel, pk=pk) items = estimate.get_itemtxs_data()[0].all() @@ -2526,6 +3977,20 @@ def create_sale_order(request, pk): @login_required def preview_sale_order(request, pk): + """ + Handles rendering of the sale order preview page for a specific estimate. + + This view retrieves an `EstimateModel` object based on the provided primary + key (`pk`), fetches related car finance data, and renders the preview of the + sale order associated with the given estimate. + + :param request: The HTTP request object + :type request: HttpRequest + :param pk: The primary key of the `EstimateModel` to retrieve + :type pk: int + :return: HTTP response containing the rendered sale order preview page + :rtype: HttpResponse + """ estimate = get_object_or_404(EstimateModel, pk=pk) data = get_car_finance_data(estimate) return render( @@ -2536,6 +4001,27 @@ def preview_sale_order(request, pk): class PaymentRequest(LoginRequiredMixin, PermissionRequiredMixin, DetailView): + """ + Represents a detailed view of a payment request for an estimate. + + This class is a Django DetailView that leverages mixins for login + and permission requirements. It displays details of an estimate + and fetches related car data based on the estimate's items. + It ensures only authorized users can access the payment request details. + + :ivar model: The Django model associated with this view. It is used + to fetch and display detailed information for an estimate. + :type model: EstimateModel + :ivar template_name: The template utilized to render the detailed + payment request page for estimates. + :type template_name: str + :ivar context_object_name: The name of the context object to be + accessible in the template for the detailed view. + :type context_object_name: str + :ivar permission_required: Permissions required for accessing + this view. The user must have the specified permissions. + :type permission_required: list + """ model = EstimateModel template_name = "sales/estimates/payment_request_detail.html" context_object_name = "estimate" @@ -2552,6 +4038,24 @@ class PaymentRequest(LoginRequiredMixin, PermissionRequiredMixin, DetailView): class EstimatePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): + """ + Represents a view for previewing an estimate with user-specific permissions and + context data processing. This class provides functionality to render a detailed + view of an estimate while ensuring user authentication and permission verification. + + This view is primarily used in a sales module to preview the detailed financial + breakdown of an estimate, including tax, discount, additional services, and total + amount. + + :ivar model: The model associated with this view. + :type model: Type[models.Model] + :ivar context_object_name: The name of the context variable containing the object. + :type context_object_name: str + :ivar template_name: The path to the template used for rendering this view. + :type template_name: str + :ivar permission_required: List of permissions required to access this view. + :type permission_required: List[str] + """ model = EstimateModel context_object_name = "estimate" template_name = "sales/estimates/estimate_preview.html" @@ -2575,6 +4079,21 @@ class EstimatePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailVie @login_required @permission_required("django_ledger.change_estimatemodel", raise_exception=True) def estimate_mark_as(request, pk): + """ + Marks an estimate with a specified status based on the requested action and + permissions. The marking possibilities include review, approval, rejection, + completion, and cancellation. The function validates whether the estimate + can transition to the desired status before updating it. It also handles + notifications and updates related entities if required, such as car status + changes upon cancellation. + + :param request: The HTTP request object containing metadata about the request. + :type request: HttpRequest + :param pk: The primary key of the estimate to be marked. + :type pk: int + :return: A redirect response to the estimate detail view. + :rtype: HttpResponseRedirect + """ estimate = get_object_or_404(EstimateModel, pk=pk) dealer = get_user_type(request) entity = dealer.entity @@ -2624,6 +4143,26 @@ def estimate_mark_as(request, pk): # Invoice class InvoiceListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): + """ + Handles the display and management of a list of invoices. + + This class-based view provides functionality for displaying a paginated list of invoices + while ensuring that only authenticated and authorized users can access the view. It allows + for applying search filters to the displayed invoices, based on user inputs. The view is + designed to work as part of the Django framework, and utilizes models, templates, and + permissions specific to the application. + + :ivar model: The model representing invoices. + :type model: type + :ivar template_name: Path to the template used to render the view. + :type template_name: str + :ivar context_object_name: Name used to reference the list of invoices in the template. + :type context_object_name: str + :ivar paginate_by: The number of invoices to display per page. + :type paginate_by: int + :ivar permission_required: List of permissions required for accessing this view. + :type permission_required: list + """ model = InvoiceModel template_name = "sales/invoices/invoice_list.html" context_object_name = "invoices" @@ -2638,6 +4177,24 @@ class InvoiceListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): class InvoiceDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): + """ + Handles the detailed view for an invoice. + + This class is responsible for displaying detailed information about a specific + invoice. It uses Django's DetailView to render the details, requires the user + to be logged in, and enforces specific permissions for viewing invoices. The + class also processes and includes additional invoice-specific data into the + context provided to the template. + + :ivar model: Specifies the model to be used for the detail view. + :type model: Type[InvoiceModel] + :ivar template_name: Path to the template used for rendering the invoice details. + :type template_name: str + :ivar context_object_name: The name of the context variable representing the object. + :type context_object_name: str + :ivar permission_required: List of permissions required to access the view. + :type permission_required: list + """ model = InvoiceModel template_name = "sales/invoices/invoice_detail.html" context_object_name = "invoice" @@ -2660,6 +4217,31 @@ class InvoiceDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView) class DraftInvoiceModelUpdateFormView( LoginRequiredMixin, PermissionRequiredMixin, UpdateView ): + """ + Representation of a form view for updating draft invoices. + + This class inherits from Django's login and permission mixins as well as + UpdateView, providing the functionality required to update an existing + instance of `InvoiceModel` using the associated form class. It enforces + that the user logged in has the required permissions to view invoices + and redirects to the invoice list upon successful update. This form + view specifically customizes the form initialization logic to include + additional data based on the requesting user's type and associated + entity. + + :ivar model: The Django model that this view will operate upon. + :type model: Type[InvoiceModel] + :ivar form_class: The form class to be used for updating `InvoiceModel` + instances. + :type form_class: Type[DraftInvoiceModelUpdateForm] + :ivar template_name: The path to the template used to render this view. + :type template_name: str + :ivar success_url: The URL to redirect to upon successful form submission. + :type success_url: str + :ivar permission_required: The list of permissions required to access + this view. + :type permission_required: List[str] + """ model = InvoiceModel form_class = DraftInvoiceModelUpdateForm template_name = "sales/invoices/draft_invoice_update.html" @@ -2677,6 +4259,30 @@ class DraftInvoiceModelUpdateFormView( class ApprovedInvoiceModelUpdateFormView( LoginRequiredMixin, PermissionRequiredMixin, UpdateView ): + """ + Handles the view for updating approved invoice models. + + This class-based view is used to update the details of an approved invoice. It + inherits from ``LoginRequiredMixin``, ``PermissionRequiredMixin``, and + ``UpdateView`` to ensure secured access to the functionality and integrates + with Django's permission system. Users must have the required permission to + access this view. It utilizes a custom update form and is configured with + specific success URLs. + + :ivar model: The model associated with this view, which is ``InvoiceModel``. + :type model: type + :ivar form_class: The form class used for handling updates, which is + ``ApprovedInvoiceModelUpdateForm``. + :type form_class: type + :ivar template_name: The path to the template used for rendering the view. + :type template_name: str + :ivar success_url: URL to redirect upon successful operation. This uses + ``reverse_lazy`` to point to the invoice list by default. + :type success_url: django.urls.reverse_lazy + :ivar permission_required: The permission required to access this view. It is + set to ``django_ledger.view_invoicemodel`` by default. + :type permission_required: list of str + """ model = InvoiceModel form_class = ApprovedInvoiceModelUpdateForm template_name = "sales/invoices/approved_invoice_update.html" @@ -2697,6 +4303,30 @@ class ApprovedInvoiceModelUpdateFormView( class PaidInvoiceModelUpdateFormView( LoginRequiredMixin, PermissionRequiredMixin, UpdateView ): + """ + Handles the update process for paid invoices. + + This view allows updating invoice details for paid invoices within the system. + The user must be logged in and have the necessary permissions for accessing + and modifying invoice-related data. Additionally, the view ensures that any + required validation is performed and only executes updates when the desired + business logic conditions are met. It inherits from `UpdateView` and includes + custom behavior for handling form validation, success URLs, and associated + permissions. + + :ivar model: The model class associated with the view. Represents the invoice + data being managed. + :type model: type + :ivar form_class: The form class used to render and validate the update form. + :type form_class: type + :ivar template_name: Path to the template used to render the update view. + :type template_name: str + :ivar success_url: Default URL to redirect to after a successful update. This + can be overridden in specific cases. + :type success_url: str + :ivar permission_required: List of permissions required to access the view. + :type permission_required: list of str + """ model = InvoiceModel form_class = PaidInvoiceModelUpdateForm template_name = "sales/invoices/paid_invoice_update.html" @@ -2728,6 +4358,22 @@ class PaidInvoiceModelUpdateFormView( @login_required @permission_required("django_ledger.change_invoicemodel", raise_exception=True) def invoice_mark_as(request, pk): + """ + Marks an invoice as approved if it meets the required conditions. + + This view is responsible for marking an invoice as approved based on the provided + `mark` parameter. If the `mark` parameter is specified as "accept" and the invoice + is eligible for approval, it gets approved and saved. Otherwise, an error message + is displayed. The function requires the user to be logged in and to have the + appropriate permission to change the InvoiceModel. + + :param request: The HTTP request object containing metadata about the request. + :type request: django.http.HttpRequest + :param pk: The primary key of the invoice to be processed. + :type pk: int + :return: An HTTP redirect response to the invoice detail page after processing. + :rtype: django.http.HttpResponse + """ invoice = get_object_or_404(InvoiceModel, pk=pk) dealer = get_user_type(request) mark = request.GET.get("mark") @@ -2745,6 +4391,21 @@ def invoice_mark_as(request, pk): @login_required @permission_required("django_ledger.add_invoicemodel", raise_exception=True) def invoice_create(request, pk): + """ + Handles the creation of a new invoice associated with a given estimate. It validates + the submitted data through a form, processes the invoice, updates related models, and + finalizes the estimate. If successful, redirects to the detailed view of the created + invoice. If the submitted data is invalid or the request is not a POST request, renders + the invoice creation form. + + :param request: The HTTP request object. + :type request: HttpRequest + :param pk: The primary key of the estimate associated with the invoice. + :type pk: int + :return: An HTTP response. Redirects to the "invoice_detail" view upon successful invoice + creation or renders the invoice creation form template otherwise. + :rtype: HttpResponse + """ estimate = get_object_or_404(EstimateModel, pk=pk) dealer = get_user_type(request) entity = dealer.entity @@ -2808,6 +4469,24 @@ def invoice_create(request, pk): class InvoicePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): + """ + Represents a detailed view for previewing an invoice. + + This class provides a mechanism to render a preview of an invoice for authorized + users. It utilizes Django's class-based views by extending `DetailView` and includes + necessary mixins for login and permission checks. The purpose of this class is to ensure + secured access to the invoice preview while generating additional context data needed + for rendering finance-related information. + + :ivar model: The Django model class this view will represent. + :type model: InvoiceModel + :ivar context_object_name: Name of the context object used in the template. + :type context_object_name: str + :ivar template_name: Path to the template used for rendering the view. + :type template_name: str + :ivar permission_required: List of permissions required to access the view. + :type permission_required: list + """ model = InvoiceModel context_object_name = "invoice" template_name = "sales/invoices/invoice_preview.html" @@ -2830,6 +4509,28 @@ class InvoicePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailView @login_required @permission_required("django_ledger.add_journalentrymodel", raise_exception=True) def PaymentCreateView(request, pk): + """ + Handles the creation of a payment entry associated with an invoice or bill. Validates + the payment data against the model's current state and reflects the changes in + invoice or bill records. Provides appropriate error messages for invalid conditions + such as exceeding payable amounts or attempting payment for already fully paid models. + + If successfully processed, the payment details are saved, and the model is updated + accordingly. This view regulates payment for dealer-associated entities while + ensuring the model consistency. + + The view renders a form to submit payment details, and pre-populates the form fields + with default data for the associated model if necessary. + + :param request: The HTTP request object containing user request data and session + information. This is required to handle the request and apply the appropriate + processing rules. + :param pk: The primary key of the invoice or bill being processed. It is used to + load the appropriate model instance for payment processing. + :return: An HTTP response object. Depending on the circumstances, the response may + redirect to the detail view of the processed invoice or bill, re-render the + payment form with error messages or indicate success in payment creation. + """ invoice = InvoiceModel.objects.filter(pk=pk).first() bill = BillModel.objects.filter(pk=pk).first() model = invoice if invoice else bill @@ -2883,6 +4584,21 @@ def PaymentCreateView(request, pk): @login_required @permission_required("django_ledger.view_journalentrymodel", raise_exception=True) def PaymentListView(request): + """ + Handles the view for listing payment information associated with the journals of a specific + entity. This view is protected to ensure only authenticated and authorized users can + access it. + + The function retrieves the related dealer object based on the current user session, extracts + the associated entity, and fetches all journal entries linked to the entity. This data is + then passed into the template for rendering. + + :param request: The HTTP request object containing user context. + :type request: HttpRequest + + :return: The rendered HTML response displaying the list of payments. + :rtype: HttpResponse + """ dealer = get_user_type(request) entity = dealer.entity journals = JournalEntryModel.objects.filter(ledger__entity=entity).all() @@ -2892,6 +4608,19 @@ def PaymentListView(request): @login_required @permission_required("django_ledger.view_journalentrymodel", raise_exception=True) def PaymentDetailView(request, pk): + """ + This function handles the detail view for a payment by fetching a journal entry + and its associated transactions. It ensures that the request is authenticated + and the user has permission to view the journal entry model. + + :param request: The HTTP request object. + :type request: HttpRequest + :param pk: The primary key of the journal entry for which details are to be fetched. + :type pk: int + :return: An HTTP response rendering the payment details template with the journal + entry and its associated transactions. + :rtype: HttpResponse + """ journal = JournalEntryModel.objects.filter(pk=pk).first() transactions = ( TransactionModel.objects.filter(journal_entry=journal) @@ -2908,6 +4637,24 @@ def PaymentDetailView(request, pk): @login_required @permission_required("django_ledger.change_journalentrymodel", raise_exception=True) def payment_mark_as_paid(request, pk): + """ + Marks an invoice as paid if it meets the conditions of being fully paid and eligible + for payment. Also ensures that related ledger journal entries are locked and posted + when the payment is marked successfully. + + This function is protected with both `login_required` and + `permission_required` decorators, ensuring that only logged-in users with + appropriate permissions can execute it. + + :param request: HttpRequest object containing metadata about the request. + :type request: HttpRequest + :param pk: Primary key of the invoice to mark as paid. + :type pk: int + :return: Redirect response to the invoice detail page. + :rtype: HttpResponseRedirect + :raises: In case of an exception during the process, an error message is + displayed to the user through Django's messaging framework. + """ invoice = get_object_or_404(InvoiceModel, pk=pk) if request.method == "POST": try: @@ -2937,6 +4684,25 @@ def payment_mark_as_paid(request, pk): # activity log class UserActivityLogListView(LoginRequiredMixin, ListView): + """ + Represents a view to display a paginated list of user activity logs. + + This class is used to display a list of user activity logs in a paginated + manner. It retrieves the logs from the `UserActivityLog` model and allows + basic filtering of logs by user email through the URL query parameters. + The view requires the user to be authenticated and utilizes the + `LoginRequiredMixin` to enforce this. + + :ivar model: The model associated with the view. + :type model: models.UserActivityLog + :ivar template_name: The template used for rendering the view. + :type template_name: str + :ivar context_object_name: The name of the context variable representing + the list of logs. + :type context_object_name: str + :ivar paginate_by: The number of logs displayed per page. + :type paginate_by: int + """ model = models.UserActivityLog template_name = "dealers/activity_log.html" context_object_name = "logs" @@ -2951,6 +4717,26 @@ class UserActivityLogListView(LoginRequiredMixin, ListView): # CRM RELATED VIEWS class LeadListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): + """ + Represents a view to display a paginated list of leads, ensuring user permissions + and type-specific filtering. + + This class is used for rendering a list of leads associated with a logged-in user + based on their type (dealer or staff member). It combines login and permission + controls with pagination to ensure proper access and management of leads + in a CRM context. + + :ivar model: The model to be used for retrieving leads. + :type model: type[Lead] + :ivar template_name: Path to the template for rendering the lead list. + :type template_name: str + :ivar context_object_name: The name of the context variable to contain the leads. + :type context_object_name: str + :ivar paginate_by: Number of items to display per page. + :type paginate_by: int + :ivar permission_required: List of permissions required to access the view. + :type permission_required: list[str] + """ model = models.Lead template_name = "crm/leads/lead_list.html" context_object_name = "leads" @@ -2969,6 +4755,21 @@ class LeadListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): class LeadDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): + """ + View that provides detailed information about a specific lead. + + This class-based view is designed for displaying detailed information associated with a + particular lead, including related notes, emails, activities, status history, and a lead + transfer form. It combines multiple mixins to enforce login and permission requirements + for accessing the data. This view is tailored to the CRM module for managing leads. + + :ivar model: The model associated with this view, which is the Lead model. + :type model: models.Lead + :ivar template_name: Path to the template used to render detailed lead information. + :type template_name: str + :ivar permission_required: List of permissions required to access this view. + :type permission_required: list + """ model = models.Lead template_name = "crm/leads/lead_detail.html" permission_required = ["inventory.view_lead"] @@ -2998,6 +4799,21 @@ class LeadDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): @login_required @permission_required("inventory.add_lead", raise_exception=True) def lead_create(request): + """ + Handles the creation of a new lead in the inventory system. + + This function manages the rendering and processing of a form for creating a new + lead. It filters options for car models in the form based on the selected car + make. For POST requests, it validates and processes the submitted form data + and creates the lead if valid. It also creates a corresponding customer in the + ledger system if one does not exist for the provided email. + + :param request: The HTTP request object containing request data. + :type request: HttpRequest + + :return: An HTTP response object rendering the lead creation form or redirecting to the lead list page upon success. + :rtype: HttpResponse + """ form = forms.LeadForm() make = request.GET.get("id_car_make", None) @@ -3066,6 +4882,28 @@ def lead_create(request): class LeadUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): + """ + Handles the update view for Lead objects. + + This class is used to manage the process of updating Lead objects within the + application. It provides integration with Django's authentication and + permissions systems, ensuring that only authorized users can access this view. + Additionally, it customizes the behavior of the form to dynamically filter + available options for the 'id_car_model' field based on the related + 'id_car_make' field. + + :ivar model: Model class representing a Lead object. + :type model: models.Lead + :ivar form_class: Form class used for the Lead update form. + :type form_class: forms.LeadForm + :ivar template_name: Path to the template used for rendering the Lead update + form. + :type template_name: str + :ivar success_url: URL to redirect to upon successful Lead update. + :type success_url: str + :ivar permission_required: List of permissions required for accessing this view. + :type permission_required: list + """ model = models.Lead form_class = forms.LeadForm template_name = "crm/leads/lead_form.html" @@ -3083,6 +4921,16 @@ class LeadUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): @login_required @permission_required("inventory.delete_lead", raise_exception=True) def LeadDeleteView(request, pk): + """ + Handles the deletion of a Lead along with its associated customer and potentially + a related user account in the system. Ensures proper permissions and login + are required before the operation is performed. Provides a success message + after the deletion is complete. + + :param request: The HTTP request object specific to the current user. + :param pk: The primary key identifier of the Lead to be deleted. + :return: An HTTP redirect response to the lead list page. + """ lead = get_object_or_404(models.Lead, pk=pk) try: User.objects.get(email=lead.customer.email).delete() @@ -3096,6 +4944,21 @@ def LeadDeleteView(request, pk): @login_required def add_note_to_lead(request, pk): + """ + Adds a note to a specific lead. This view is accessible only to authenticated + users. The function handles the POST request to create a new note associated + with a lead. Upon successful submission, the note is linked to the provided lead, + and the user is redirected to the lead's detail page. If the request method is + not POST, it initializes an empty form to add a note. + + :param request: The HTTP request object + :type request: HttpRequest + :param pk: The primary key of the lead to which the note will be added + :type pk: int + :return: HTTP response object. Redirects to the lead detail page on successful + note creation or renders the note form template for GET or invalid POST requests. + :rtype: HttpResponse + """ lead = get_object_or_404(models.Lead, pk=pk) if request.method == "POST": form = forms.NoteForm(request.POST) @@ -3113,6 +4976,19 @@ def add_note_to_lead(request, pk): @login_required def add_note_to_opportunity(request, pk): + """ + Add a note to a specific opportunity identified by its primary key. + + This function handles the addition of a note to an existing opportunity + by processing a POST request that includes the note content. If the note + content is missing, an error message will be displayed. If the operation + is successful, the note is saved, and a success message is returned. + + :param request: The HTTP request object representing the client's request. + :param pk: The primary key of the Opportunity to which the note is to be added. + :type pk: int + :return: A redirect response to the detailed view of the opportunity. + """ opportunity = get_object_or_404(models.Opportunity, pk=pk) if request.method == "POST": notes = request.POST.get("notes") @@ -3126,6 +5002,23 @@ def add_note_to_opportunity(request, pk): @login_required def update_note(request, pk): + """ + Handles the update of a specific note attached to a lead object, ensuring that the user + making the request has permissions over the note. If the request method is `POST`, it + validates and updates the note using the submitted data. If validation is successful, + it redirects to the lead's detail page. Otherwise, a form is rendered for editing. + + :param request: The HTTP request object containing metadata about the request + and the user making the request. + :type request: HttpRequest + :param pk: The primary key of the note to be updated, identifying the specific + note object. + :type pk: int + :return: An HTTP response either rendering the note update form along with the + current note data, or a redirect to the lead detail page upon successful + update. + :rtype: HttpResponse + """ note = get_object_or_404(models.Notes, pk=pk, created_by=request.user) lead_pk = note.content_object.pk @@ -3146,6 +5039,19 @@ def update_note(request, pk): @login_required def delete_note(request, pk): + """ + Deletes a specific note created by the currently logged-in user and redirects + to the lead detail page. If the note does not exist or the user is not the creator, + a 404 error will be raised. A success message is displayed upon successful deletion + of the note. + + :param request: The HTTP request object associated with the current request. + :type request: HttpRequest + :param pk: The primary key of the note to be deleted. + :type pk: int + :return: An HTTP redirection to the lead detail page of the corresponding note's lead. + :rtype: HttpResponseRedirect + """ note = get_object_or_404(models.Notes, pk=pk, created_by=request.user) lead_pk = note.content_object.pk note.delete() @@ -3156,6 +5062,22 @@ def delete_note(request, pk): @login_required @permission_required("inventory.change_lead", raise_exception=True) def lead_convert(request, pk): + """ + Converts a lead into a customer and creates a corresponding opportunity. + + The function ensures that leads are not converted to customers more than once. + If the lead has already been converted, an error is displayed. Otherwise, + the lead is converted into a customer and a new opportunity is created. + Upon successful conversion, the user is redirected to the lead list view + and a success message is displayed. + + :param request: The HTTP request object representing the user's request. + :type request: HttpRequest + :param pk: The primary key of the lead to be converted. + :type pk: int + :return: An HTTP response redirecting to the lead list view. + :rtype: HttpResponse + """ lead = get_object_or_404(models.Lead, pk=pk) dealer = get_user_type(request) if hasattr(lead, "opportunity"): @@ -3170,6 +5092,23 @@ def lead_convert(request, pk): @login_required @permission_required("inventory.add_lead", raise_exception=True) def schedule_lead(request, pk): + """ + Handles the scheduling of a lead for an appointment. + + This function ensures that only staff members with the appropriate permissions + can schedule leads. If the user is not a staff member or does not have the + required inventory permissions, they are redirected with an appropriate error + message. The function creates an appointment request and an associated appointment + record upon successful submission of the scheduling form. + + :param request: The HTTP request object containing metadata about the request. + :type request: HttpRequest + :param pk: The primary key of the lead to be scheduled. + :type pk: int + :return: A rendered template or a redirection to another view based on the request + method and validity of the form submission. + :rtype: HttpResponse + """ if not request.is_staff: messages.error(request, _("You do not have permission to schedule lead")) return redirect("lead_list") @@ -3225,6 +5164,17 @@ def schedule_lead(request, pk): @login_required @permission_required("inventory.change_lead", raise_exception=True) def lead_transfer(request, pk): + """ + Handles the transfer of a lead to a different staff member. This view is accessible + only to authenticated users with the 'inventory.change_lead' permission. If the + request method is POST and the form data is valid, the lead's staff is updated + accordingly, saved, and a success message is displayed. Otherwise, an error message + is shown. In both cases, the user is redirected to the lead listing page. + + :param request: The HTTP request object. + :param pk: The primary key of the lead to be transferred. + :return: An HTTP redirect response to the lead list view. + """ lead = get_object_or_404(models.Lead, pk=pk) if request.method == "POST": form = forms.LeadTransferForm(request.POST) @@ -3239,6 +5189,25 @@ def lead_transfer(request, pk): @login_required def send_lead_email(request, pk, email_pk=None): + """ + Handles sending emails related to a lead. Supports creating drafts, sending emails, and rendering + an email composition page. Changes on the lead or email objects, such as marking a lead as contacted + or an email as sent, are recorded in associated activity logs. + + :param request: The HTTP request object. This contains user information, method type, and data such as + GET or POST payload used to draft or send the email appropriately. + Type: HttpRequest + :param pk: The primary key of the lead to which the email action is associated. It's used to retrieve + the lead object from the database. + Type: int + :param email_pk: Optional parameter representing the primary key of an email template. When provided, + the respective email content is pre-populated into the email composition form. + Defaults to None. + Type: Optional[int] + :return: When successfully sending an email, redirects the user to the lead list page. On draft mode + or email composition rendering, a response object is returned to render the respective page. + Type: HttpResponse + """ lead = get_object_or_404(models.Lead, pk=pk) status = request.GET.get("status") dealer = get_user_type(request) @@ -3316,6 +5285,17 @@ def send_lead_email(request, pk, email_pk=None): @login_required def add_activity_to_lead(request, pk): + """ + Handles the process of adding a new activity to a specific lead. This includes + rendering a form for user input, validating the form submission, and saving the + new activity if the input is valid. If the method is GET, it will simply + render the form. If the method is POST, it checks the form validity, creates + the activity, associates it with the lead, and saves it to the database. + + :param request: The HTTP request object containing metadata about the request made + :param pk: The primary key of the lead to which the activity is to be added + :return: An HTTP response that either renders the form or redirects to the lead detail page + """ lead = get_object_or_404(models.Lead, pk=pk) if request.method == "POST": form = forms.ActivityForm(request.POST) @@ -3331,6 +5311,24 @@ def add_activity_to_lead(request, pk): class OpportunityCreateView(CreateView, LoginRequiredMixin): + """ + Handles the creation of Opportunity instances through a form while enforcing + specific user access control and initial data population. This view ensures + an authenticated user can create opportunities tied to their dealer type and + allows pre-filling data when linked to an existing Lead. + + Allows authenticated users to create a new Opportunity instance, associating it + with the requesting user's dealer type and prefilling some fields based on + existing Lead data, if applicable. + + :ivar model: The database model associated with this view, which is used to + represent Opportunity data. + :type model: models.Opportunity + :ivar form_class: The form class used for creating new Opportunity instances. + :type form_class: forms.OpportunityForm + :ivar template_name: The template used to render the Opportunity creation form. + :type template_name: str + """ model = models.Opportunity form_class = forms.OpportunityForm template_name = "crm/opportunities/opportunity_form.html" @@ -3358,6 +5356,27 @@ class OpportunityCreateView(CreateView, LoginRequiredMixin): class OpportunityUpdateView(LoginRequiredMixin, UpdateView): + """ + Handles the update functionality for Opportunity objects. + + This class-based view is responsible for handling the update of existing + Opportunity instances. It uses a Django form that is specified by the + `form_class` attribute and renders a template to display and process the + update form. Access to this view is restricted to authenticated users, as + it inherits from `LoginRequiredMixin`. + + It defines the model to be updated and the form template to be used. Upon + successful update, it redirects the user to the detail page of the updated + opportunity instance. + + :ivar model: The model associated with this view. Represents the Opportunity model. + :type model: django.db.models.Model + :ivar form_class: The form class used to manage the Opportunity update process. + :type form_class: django.forms.ModelForm + :ivar template_name: The path to the template used to render the opportunity + update form. + :type template_name: str + """ model = models.Opportunity form_class = forms.OpportunityForm template_name = "crm/opportunities/opportunity_form.html" @@ -3367,6 +5386,21 @@ class OpportunityUpdateView(LoginRequiredMixin, UpdateView): class OpportunityDetailView(LoginRequiredMixin, DetailView): + """ + Handles the detailed view of an Opportunity object. + + Displays detailed information about a specific opportunity, including its + status, stage, notes, activities, and related emails. This view utilizes + a form to manage and update the status and stage of the opportunity + through HTTPX requests. Suitable for use in CRM applications. + + :ivar model: The model for which this view is being implemented. + :type model: models.Opportunity + :ivar template_name: The template used to render this view. + :type template_name: str + :ivar context_object_name: The context variable name for the model object in the template. + :type context_object_name: str + """ model = models.Opportunity template_name = "crm/opportunities/opportunity_detail.html" context_object_name = "opportunity" @@ -3397,6 +5431,25 @@ class OpportunityDetailView(LoginRequiredMixin, DetailView): class OpportunityListView(LoginRequiredMixin, ListView): + """ + View for displaying a paginated list of opportunities. + + This class-based view inherits from `LoginRequiredMixin` and `ListView` to + provide a view rendering a list of `Opportunity` objects associated with + the current dealer. It ensures the user is authenticated before providing + access to the opportunity list and adds filtering based on the dealer + associated with the request. + + :ivar model: The model used to retrieve opportunities. + :type model: models.Opportunity + :ivar template_name: The template used to render the opportunities list. + :type template_name: str + :ivar context_object_name: The context variable name for the list of + opportunities in the template. + :type context_object_name: str + :ivar paginate_by: The number of opportunities displayed per page. + :type paginate_by: int + """ model = models.Opportunity template_name = "crm/opportunities/opportunity_list.html" context_object_name = "opportunities" @@ -3409,6 +5462,18 @@ class OpportunityListView(LoginRequiredMixin, ListView): @login_required def delete_opportunity(request, pk): + """ + Deletes an opportunity object from the database and redirects to the opportunity + list view. If the opportunity with the specified primary key is not found, a 404 + error will be raised. Displays a success message after deletion. + + :param request: The HTTP request object containing metadata about the request. + :type request: HttpRequest + :param pk: The primary key of the opportunity object to be deleted. + :type pk: int + :return: An HTTP response redirecting to the opportunity list view. + :rtype: HttpResponse + """ opportunity = get_object_or_404(models.Opportunity, pk=pk) opportunity.delete() messages.success(request, _("Opportunity deleted successfully")) @@ -3417,6 +5482,30 @@ def delete_opportunity(request, pk): @login_required def opportunity_update_status(request, pk): + """ + Update the status and/or stage of a specific Opportunity instance. This is a + view function, which is generally tied to a URL endpoint in a Django application. + The function is accessible only to authenticated users due to the + `@login_required` decorator. + + The function retrieves the `Opportunity` instance based on the primary key + in the URL, updates the status or stage of the instance based on query + parameters in the request, saves the changes, and then redirects to the + detail view of the `Opportunity`. Additionally, a success message is displayed, + and a custom header (`HX-Refresh`) is added to the response. + + :param request: The HTTP request object containing details of the user's + request, such as query parameters for status and stage. This is a + mandatory parameter provided by Django's view function framework. + :type request: HttpRequest + :param pk: The primary key of the `Opportunity` instance to be updated. + :type pk: int + + :return: An HTTP response object that redirects to the updated Opportunity's + detail page, with a success message and the "HX-Refresh" header to trigger + frontend behavior. + :rtype: HttpResponse + """ opportunity = get_object_or_404(models.Opportunity, pk=pk) status = request.GET.get("status") stage = request.GET.get("stage") @@ -3432,6 +5521,26 @@ def opportunity_update_status(request, pk): class NotificationListView(LoginRequiredMixin, ListView): + """ + Handles the display and management of notification history for a user. + + This class provides functionality to display a paginated list of user notifications + sorted by their creation time in descending order. It ensures that the user is logged + in before accessing the notifications and filters the displayed notifications specific + to the logged-in user. + + :ivar model: Specifies the model to retrieve data from. + :type model: models.Notification + :ivar template_name: Specifies the template to render the notifications page. + :type template_name: str + :ivar context_object_name: The name of the variable accessible in the template to + refer to the list of notifications. + :type context_object_name: str + :ivar paginate_by: Defines the number of notifications to display per page. + :type paginate_by: int + :ivar ordering: Defines the default ordering of notifications. + :type ordering: str + """ model = models.Notification template_name = "crm/notifications_history.html" context_object_name = "notifications" @@ -3444,6 +5553,21 @@ class NotificationListView(LoginRequiredMixin, ListView): @login_required def mark_notification_as_read(request, pk): + """ + Marks a user notification as read. + + This view allows an authenticated user to mark a specific notification, + identified by its primary key, as read. Upon successfully marking the + notification, a success message is displayed, and the user is redirected + to their notification history page. + + :param request: The HTTP request object. + :type request: HttpRequest + :param pk: Primary key of the notification to be marked as read. + :type pk: int + :return: An HTTP response redirecting to the notification history page. + :rtype: HttpResponse + """ notification = get_object_or_404(models.Notification, pk=pk, user=request.user) notification.is_read = True notification.save() @@ -3453,6 +5577,18 @@ def mark_notification_as_read(request, pk): @login_required def fetch_notifications(request): + """ + Fetches unread notifications for the currently logged-in user and renders them + to the `notifications.html` template. The notifications are filtered to include + only those belonging to the logged-in user and are sorted by creation date in + descending order. + + :param request: The HTTP request object representing the current user request. + Must include details of the logged-in user. + :return: An HttpResponse object that renders the `notifications.html` template + with the fetched notifications. + + """ notifications = models.Notification.objects.filter( user=request.user, is_read=False ).order_by("-created") @@ -3463,6 +5599,31 @@ def fetch_notifications(request): class ItemServiceCreateView( LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView ): + """ + Manages the creation of additional services with permission checks, success messages, + and validation of VAT and dealer-specific pricing. + + This class-based view facilitates the creation of additional service items within + a custom Django application framework. It includes mixins for login-required access, + permission control, and success message handling. The form is validated to apply VAT + rates to taxable services and associates the service with the dealer determined from + the logged-in user's context. + + :ivar model: The model representing additional services. + :type model: models.AdditionalServices + :ivar form_class: The form class used for creating a service. + :type form_class: forms.AdditionalServiceForm + :ivar template_name: The template used to render the service creation page. + :type template_name: str + :ivar success_url: The redirect URL upon successful form submission. + :type success_url: str + :ivar success_message: The success message displayed upon successful form submission. + :type success_message: str + :ivar context_object_name: The context name used to refer to the service object in templates. + :type context_object_name: str + :ivar permission_required: The permissions required for accessing this view. + :type permission_required: list + """ model = models.AdditionalServices form_class = forms.AdditionalServiceForm template_name = "items/service/service_create.html" @@ -3483,6 +5644,30 @@ class ItemServiceCreateView( class ItemServiceUpdateView( LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView ): + """ + Handles update functionality for additional services in the system. + + This view enables authenticated and authorized users to update instances of + the `AdditionalServices` model. It requires the user to have the necessary + permissions and provides success feedback upon successful update. Additionally, + it calculates and updates the price of the service based on the tax rate if + the service is taxable. + + :ivar model: The model associated with this view. + :type model: models.AdditionalServices + :ivar form_class: The form class used for handling AdditionalService updates. + :type form_class: forms.AdditionalServiceForm + :ivar template_name: The template used for rendering the service update page. + :type template_name: str + :ivar success_url: The URL to redirect after a successful update. + :type success_url: str + :ivar success_message: The success message displayed after a successful update. + :type success_message: str + :ivar context_object_name: The context variable name for the object to be displayed. + :type context_object_name: str + :ivar permission_required: Permissions required for accessing this view. + :type permission_required: list[str] + """ model = models.AdditionalServices form_class = forms.AdditionalServiceForm template_name = "items/service/service_create.html" @@ -3501,6 +5686,27 @@ class ItemServiceUpdateView( class ItemServiceListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): + """ + Handles the listing of additional services for a dealer. + + This class is responsible for displaying a paginated list of additional + services specific to a dealer. It enforces login, permission checking, + and customizes the queryset to return only services associated with the + currently logged-in dealer. + + :ivar model: The model representing the additional services data. + :type model: Model + :ivar template_name: Path to the template used for rendering the view. + :type template_name: str + :ivar context_object_name: The name of the context variable for the + object list. + :type context_object_name: str + :ivar paginate_by: Number of items displayed per page. + :type paginate_by: int + :ivar permission_required: List of permissions required to access the + view. + :type permission_required: list + """ model = models.AdditionalServices template_name = "items/service/service_list.html" context_object_name = "services" @@ -3513,6 +5719,25 @@ class ItemServiceListView(LoginRequiredMixin, PermissionRequiredMixin, ListView) class ItemExpenseCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): + """ + Represents a view for creating item expense entries. + + This class is responsible for handling the creation of expense items in the + application. It integrates with Django's class-based views and includes + customizations specific to the current user and their associated entity. + Permission control ensures only authorized users can create expenses. + + :ivar model: The database model used for this view. + :type model: ItemModel + :ivar form_class: The form class used to render and validate input. + :type form_class: ExpenseItemCreateForm + :ivar template_name: The path to the template used to render the view. + :type template_name: str + :ivar success_url: The URL to redirect to upon successful form submission. + :type success_url: str + :ivar permission_required: A list of permissions required to access the view. + :type permission_required: list + """ model = ItemModel form_class = ExpenseItemCreateForm template_name = "items/expenses/expense_create.html" @@ -3533,6 +5758,25 @@ class ItemExpenseCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateV class ItemExpenseUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): + """ + Manages the update view for item expenses. + + This class enables authenticated users with the necessary permissions to update + information related to item expenses. It integrates form handling and permission + requirements, ensuring entity-specific contextual data is appended to the form + during updates. + + :ivar model: The model associated with the update operation. + :type model: ItemModel + :ivar form_class: The form used to validate and process updates. + :type form_class: ExpenseItemUpdateForm + :ivar template_name: Path to the template used for rendering the update view. + :type template_name: str + :ivar success_url: URL where the user is redirected after a successful update. + :type success_url: str + :ivar permission_required: List of permissions required to access this view. + :type permission_required: list[str] + """ model = ItemModel form_class = ExpenseItemUpdateForm template_name = "items/expenses/expense_update.html" @@ -3553,6 +5797,26 @@ class ItemExpenseUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateV class ItemExpenseListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): + """ + Handles the display of a list of item expenses. + + This class-based view is responsible for displaying a paginated list of item + expenses related to a specific entity. It uses the Django generic `ListView` + class and integrates with permissions and login requirements. It retrieves + data through the `get_queryset` method by invoking a specific function on + the user's associated entity object. + + :ivar model: The model associated with the view. + :type model: type[ItemModel] + :ivar template_name: The template used to render the view. + :type template_name: str + :ivar context_object_name: The context variable name used in the template. + :type context_object_name: str + :ivar paginate_by: The number of items displayed per page. + :type paginate_by: int + :ivar permission_required: The list of required permissions to access the view. + :type permission_required: list[str] + """ model = ItemModel template_name = "items/expenses/expenses_list.html" context_object_name = "expenses" @@ -3565,6 +5829,23 @@ class ItemExpenseListView(LoginRequiredMixin, PermissionRequiredMixin, ListView) class BillListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): + """ + Provides a view for listing bills. + + This class-based view is used to display a list of bills. It ensures that + the user is logged in and has the appropriate permissions to view the list + of bills. The view retrieves the bills associated with the dealer's entity + and displays them using the specified template. + + :ivar model: The model associated with the view. + :type model: django.db.models.Model + :ivar template_name: The template used to render the view. + :type template_name: str + :ivar context_object_name: Name of the context variable containing the list of bills. + :type context_object_name: str + :ivar permission_required: List of permissions required to access the view. + :type permission_required: list[str] + """ model = BillModel template_name = "ledger/bills/bill_list.html" context_object_name = "bills" @@ -3577,6 +5858,22 @@ class BillListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): class BillDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): + """ + Handles the functionality for viewing detailed information about a single bill. + + This class allows logged-in users with the required permissions to view specific bills + along with their attributes and calculated data provided in the context, such as a list + of detailed item transactions and a computed grand total. + + :ivar model: The model associated with the view, which in this case represents bills. + :type model: BillModel + :ivar template_name: The template used for rendering the bill detail view. + :type template_name: str + :ivar context_object_name: The name of the context object passed to the template. + :type context_object_name: str + :ivar permission_required: A list of permissions required to access this view. + :type permission_required: list[str] + """ model = BillModel template_name = "ledger/bills/bill_detail.html" context_object_name = "bill" @@ -3603,6 +5900,30 @@ class BillDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): class InReviewBillView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): + """ + Handles the in-review update functionality for the BillModel. + + This view is responsible for managing the update operations of bills that are in the + "in-review" state. It ensures all updates are performed according to the associated + permissions, and checks the user's type for processing additional context and logic. + The view also enforces that only authorized users with the required permission can + interact with it. + + :ivar model: The model being used for this view, representing the BillModel. + :type model: BillModel + :ivar form_class: The form class used to handle the update of bill data. + :type form_class: InReviewBillModelUpdateForm + :ivar template_name: The template used to render the bill update form. + :type template_name: str + :ivar success_url: The URL to redirect to upon successful bill update. + :type success_url: str + :ivar success_message: A message displayed upon successfully updating the bill. + :type success_message: str + :ivar context_object_name: The context variable name for the view's object. + :type context_object_name: str + :ivar permission_required: The list of permissions required to use this view. + :type permission_required: list + """ model = BillModel form_class = InReviewBillModelUpdateForm template_name = "ledger/bills/bill_update_form.html" @@ -3629,6 +5950,32 @@ class InReviewBillView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): class ApprovedBillModelView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): + """ + Represents a view to update and approve a bill. + + This class provides the functionality to handle the update and + approval process of a bill within the system. It ensures only + authorized users can access and manage the approval. The view + uses specific permissions and form classes to manage this + process. Additionally, it provides feedback upon successful + completion of the operation, such as updating or approving the + bill. + + :ivar model: The Django model associated with this view. + :type model: BillModel + :ivar form_class: The form class to use for handling bill updates. + :type form_class: ApprovedBillModelUpdateForm + :ivar template_name: The path to the HTML template for this view. + :type template_name: str + :ivar success_url: The redirect destination upon successful form submission. + :type success_url: str + :ivar success_message: The success message to display upon successful form submission. + :type success_message: str + :ivar context_object_name: The name of the context object. + :type context_object_name: str + :ivar permission_required: A list of permissions required to access this view. + :type permission_required: list + """ model = BillModel form_class = ApprovedBillModelUpdateForm template_name = "ledger/bills/bill_update_form.html" @@ -3658,6 +6005,18 @@ class ApprovedBillModelView(LoginRequiredMixin, PermissionRequiredMixin, UpdateV @login_required @permission_required("django_ledger.change_billmodel", raise_exception=True) def bill_mark_as_approved(request, pk): + """ + Marks a bill as approved for the given bill ID (primary key) if it is not + already approved. This action can only be completed by an authorized user + with the corresponding permissions. Once the bill is approved, it is saved + and a success message is displayed. If the bill is already approved, an + error message is shown instead. + + :param request: HttpRequest object representing the current request. + :param pk: Primary key of the bill to be marked as approved. + :return: HttpResponseRedirect to the bill detail page after the operation is + completed or an error/success message is set. + """ bill = get_object_or_404(BillModel, pk=pk) if request.method == "POST": dealer = get_user_type(request) @@ -3673,6 +6032,22 @@ def bill_mark_as_approved(request, pk): @login_required @permission_required("django_ledger.change_billmodel", raise_exception=True) def bill_mark_as_paid(request, pk): + """ + Marks a bill as paid if certain conditions are met and updates the ledger accordingly. + + This function is used to mark a specific bill as paid. It verifies whether the bill + is already paid or if the amount paid matches the amount due. If the conditions are + met, it updates the bill's status, locks the journal entries in the associated ledger, + posts them, and saves the changes. Appropriate success or error messages are displayed + to the user, and the user is redirected to the bill details page. + + :param request: The HTTP request object containing details about the request made by the user. + :type request: HttpRequest + :param pk: The primary key of the bill to be marked as paid. + :type pk: int + :return: A redirect response to the bill details page. + :rtype: HttpResponseRedirect + """ bill = get_object_or_404(BillModel, pk=pk) if request.method == "POST": dealer = get_user_type(request) @@ -3695,6 +6070,18 @@ def bill_mark_as_paid(request, pk): @login_required @permission_required("django_ledger.add_billmodel", raise_exception=True) def bill_create(request): + """ + Handles creation of a bill in the system, including the validation of input data, + creation of transactions associated with the bill, and rendering of the appropriate + response or form. Ensures the user creating the bill has the necessary permissions and + correct input parameters for successful bill creation and itemization. + + :param request: Django HttpRequest object containing metadata and data of the HTTP request. + :type request: HttpRequest + :return: JsonResponse with success/error information if the request is processed, + or HttpResponse rendering the form for bill creation. + :rtype: JsonResponse or HttpResponse + """ dealer = get_user_type(request) entity = dealer.entity @@ -3812,6 +6199,19 @@ def bill_create(request): @login_required @permission_required("django_ledger.delete_billmodel", raise_exception=True) def BillDeleteView(request, pk): + """ + Deletes a specific BillModel instance based on the primary key (pk). + This view requires the user to be logged in and have the appropriate + permission to delete a bill. After the delete operation is successful, + the user is redirected to the bill list view. + + :param request: The HTTP request object. + :type request: HttpRequest + :param pk: Primary key of the BillModel instance to be deleted. + :type pk: int + :return: Redirect to the bill list view after successful deletion. + :rtype: HttpResponseRedirect + """ bill = get_object_or_404(BillModel, pk=pk) bill.delete() return redirect("bill_list") @@ -3819,6 +6219,22 @@ def BillDeleteView(request, pk): # orders class OrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): + """ + Represents a view for listing sale orders. + + This class provides the functionality to display a list of sale orders + within a web template. It includes permission-based access control, and + retrieves sale orders corresponding to the entity of the logged-in dealer. + + :ivar model: The model associated with this view. + :type model: models.SaleOrder + :ivar template_name: The name of the template to be rendered. + :type template_name: str + :ivar context_object_name: The context variable name to be used for the object list. + :type context_object_name: str + :ivar permission_required: A list of permissions required to access this view. + :type permission_required: list[str] + """ model = models.SaleOrder template_name = "sales/orders/order_list.html" context_object_name = "orders" @@ -3834,6 +6250,21 @@ class OrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): @login_required @permission_required("django_ledger.view_estimate", raise_exception=True) def send_email_view(request, pk): + """ + View function to send an email for an estimate. This function allows authenticated and + authorized users to send an email containing the estimate details to the customer. + The email includes a link to preview the estimate and a message template in multiple + languages. If the estimate does not have any associated items, the user will receive + an error message. Upon successfully sending the email, the estimate is marked as reviewed. + + :param request: The HttpRequest object containing metadata about the request. + :type request: HttpRequest + :param pk: The primary key of the estimate to be sent via email. + :type pk: int + :return: An HttpResponseRedirect to the estimate detail view. + :rtype: HttpResponseRedirect + :raises Http404: If the estimate with the given primary key does not exist. + """ dealer = get_user_type(request) estimate = get_object_or_404(EstimateModel, pk=pk) @@ -3883,23 +6314,80 @@ def send_email_view(request, pk): # errors def custom_page_not_found_view(request, exception=None): + """ + Custom handler for 404 errors that renders a custom HTML page + upon encountering a Page Not Found error. + + :param request: The HttpRequest object associated with the request. + :type request: HttpRequest + :param exception: The exception that triggered the 404 error, if any. + :type exception: Exception, optional + :return: An HttpResponse object rendering the "errors/404.html" template. + :rtype: HttpResponse + """ return render(request, "errors/404.html", {}) def custom_error_view(request, exception=None): + """ + Handles rendering the custom error page for HTTP 500 errors. + + This function is called when an unhandled exception occurs in the application. It renders + a predefined template for server errors, providing a consistent error page layout + to the users. + + :param request: The HTTP request instance which triggered the error. + :type request: HttpRequest + :param exception: The exception that caused the error to trigger. Defaults to None. + :type exception: Exception, optional + :return: An HttpResponse object representing the rendered error page. + :rtype: HttpResponse + """ return render(request, "errors/500.html", {}) def custom_permission_denied_view(request, exception=None): + """ + Handles the custom view for 403 Forbidden permission denied errors. This + function renders a custom template for the error page when users are denied + permission to access a particular resource or view. + + :param request: Django HttpRequest object containing metadata about the request. + :type request: HttpRequest + :param exception: Optional exception that caused the 403 error. + Defaults to None. + :type exception: Exception | None + :return: HttpResponse object rendering the 403.html error page. + :rtype: HttpResponse + """ return render(request, "errors/403.html", {}) def custom_bad_request_view(request, exception=None): + """ + Handles custom bad request responses by rendering a specified HTML + template for 400 status errors. + + :return: Rendered HTTP response rendering the error page. + :rtype: HttpResponse + """ return render(request, "errors/400.html", {}) # BALANCE SHEET class BaseBalanceSheetRedirectView(RedirectView): + """ + Handles redirection for balance sheet views of entities. + + This class is specifically designed to redirect users to the + appropriate year's balance sheet for an entity. It determines + the URL based on the current year and the provided entity slug + in the request. Additionally, it provides a URL for login redirection + if required. + + :ivar url: The original URL or base URL to redirect users. + :type url: str + """ def get_redirect_url(self, *args, **kwargs): year = get_localdate().year return reverse( @@ -3914,6 +6402,17 @@ class BaseBalanceSheetRedirectView(RedirectView): class FiscalYearBalanceSheetViewBase( FiscalYearBalanceSheetView, DjangoLedgerSecurityMixIn ): + """ + Defines a base view for the fiscal year balance sheet. + + This class serves as a base view for displaying the balance sheet of a fiscal + year. It provides functionality for rendering the associated template and + managing user login requirements. It integrates with Django's security system + to ensure appropriate access control. + + :ivar template_name: The template used for rendering the balance sheet view. + :type template_name: str + """ template_name = "ledger/reports/balance_sheet.html" # AUTHORIZE_SUPERUSER = False # permission_required = [] @@ -3930,7 +6429,22 @@ class QuarterlyBalanceSheetView( FiscalYearBalanceSheetViewBase, QuarterlyReportMixIn, DjangoLedgerSecurityMixIn ): """ - Quarter Balance Sheet View. + Represents a quarterly balance sheet view. + + The purpose of this class is to provide a representation and handling + for the balance sheet data specific to fiscal quarters. It extends from + `FiscalYearBalanceSheetViewBase` to inherit year-level balance sheet + operations and incorporates functionality from `QuarterlyReportMixIn` + to manage quarterly-specific logic. Additionally, the `DjangoLedgerSecurityMixIn` + is used to handle security features related to Django ledger operations. + + :ivar fiscal_year: The fiscal year associated with the quarterly balance sheet. + :type fiscal_year: int + :ivar quarter: The specific quarter (e.g., Q1, Q2, Q3, Q4) for the balance sheet. + :type quarter: int + :ivar balance_data: The data structure representing the detailed financial + information for the balance sheet of the quarter. + :type balance_data: dict """ @@ -3938,7 +6452,22 @@ class MonthlyBalanceSheetView( FiscalYearBalanceSheetViewBase, MonthlyReportMixIn, DjangoLedgerSecurityMixIn ): """ - Monthly Balance Sheet View. + Represents the view for the monthly balance sheet. + + This class is responsible for handling the presentation and processing of + monthly balance sheet data. It incorporates necessary mix-ins to provide + functionality specific to fiscal year balance sheet viewing, monthly reporting, + and security validation within the Django ledger context. + + :ivar fiscal_year: Stores the fiscal year information associated with the + balance sheet. + :type fiscal_year: int + :ivar monthly_reports: Holds a collection of monthly report instances that + pertain to the balance sheet. + :type monthly_reports: List[MonthlyReport] + :ivar user_permissions: Manages user-specific permissions for accessing and + modifying the balance sheet data. + :type user_permissions: Dict[str, Any] """ @@ -3946,7 +6475,26 @@ class DateBalanceSheetView( FiscalYearBalanceSheetViewBase, DateReportMixIn, DjangoLedgerSecurityMixIn ): """ - Date Balance Sheet View. + Represents a balance sheet view for a specific date. + + DateBalanceSheetView extends functionality for displaying balance sheets + specific to certain dates. This class combines features from various mixins + to enforce security, handle date-based reporting, and provide fiscal year + context. It is used in scenarios where financial reports for a particular + date are required to ensure accurate and filtered viewing of accounting + data. + + :ivar fiscal_year: Represents the fiscal year context for the balance sheet. + :type fiscal_year: FiscalYear + :ivar report_date: The specific date for which the balance sheet report + is generated. + :type report_date: datetime.date + :ivar is_authenticated: Indicates whether the user accessing the + balance sheet has been authenticated. + :type is_authenticated: bool + :ivar account_data: Contains processed account data used in the balance sheet + for computations or display purposes. + :type account_data: dict """ @@ -3956,6 +6504,20 @@ class DateBalanceSheetView( class BaseIncomeStatementRedirectViewBase( BaseIncomeStatementRedirectView, DjangoLedgerSecurityMixIn ): + """ + The BaseIncomeStatementRedirectViewBase class provides functionality for handling + redirects in the context of income statements. This class combines features from + both BaseIncomeStatementRedirectView and DjangoLedgerSecurityMixIn to ensure + secure handling of data and user authentication. + + This class operates by calculating the current year, determining the user's dealer + type, and generating a URL to redirect to an income statement view associated with a + specific entity for the calculated year. Additionally, it defines a method to generate + the login URL for unauthenticated users. + + :ivar request: The HTTP request object containing user session and other request-related data. + :type request: HttpRequest + """ def get_redirect_url(self, *args, **kwargs): year = get_localdate().year dealer = get_user_type(self.request) @@ -3970,6 +6532,20 @@ class BaseIncomeStatementRedirectViewBase( class FiscalYearIncomeStatementViewBase( FiscalYearIncomeStatementView, DjangoLedgerSecurityMixIn ): + """ + Represents a base view for fiscal year income statement. + + This class serves as a base view for generating and rendering the fiscal + year income statement within the application. It combines multiple + functionalities, including template rendering and permission handling, + to provide a secure and user-friendly interface. + + :ivar template_name: Path to the HTML template used for rendering the + income statement view. + :type template_name: str + :ivar permission_required: List of permissions required to access the view. + :type permission_required: list[str] + """ template_name = "ledger/reports/income_statement.html" permission_required = ["inventory.view_carfinance"] @@ -3981,7 +6557,28 @@ class QuarterlyIncomeStatementView( FiscalYearIncomeStatementViewBase, QuarterlyReportMixIn, DjangoLedgerSecurityMixIn ): """ - Quarter Income Statement View. + Represents a detailed view for a quarterly income statement. + + This class is responsible for providing a structured view of a company's + quarterly income statement. It combines the functionality of multiple + base classes to deliver a comprehensive representation of quarterly report + data while ensuring integration with Django Ledger security protocols. + + The class serves as a specialized view tied to specific fiscal data, + enabling efficient management and rendering of quarterly financial reports. + + :ivar fiscal_year: The fiscal year associated with the quarterly income + statement. + :type fiscal_year: int + :ivar quarter: The quarter number (e.g., 1 for Q1, 2 for Q2, etc.) of the + income statement. + :type quarter: int + :ivar revenue: The total revenue recorded in the quarterly income statement. + :type revenue: float + :ivar net_income: The net income calculated for the quarter. + :type net_income: float + :ivar expenses: The total expenses incurred during the quarter. + :type expenses: float """ @@ -3989,7 +6586,25 @@ class MonthlyIncomeStatementView( FiscalYearIncomeStatementViewBase, MonthlyReportMixIn, DjangoLedgerSecurityMixIn ): """ - Monthly Income Statement View. + Represents the view for a monthly income statement in the financial + application. + + This class is designed to support the presentation and display of + financial income statement data on a monthly basis. It incorporates + functionality from multiple mixins to include specific fiscal year + operations, monthly report behaviors, and security aspects tailored + to the Django Ledger system. + + :ivar fiscal_year: The fiscal year associated with the income + statement. + :type fiscal_year: int + :ivar month: The month for which the income statement is viewed. + :type month: int + :ivar data: The financial data contained in the income statement. + :type data: dict + :ivar authorized_user: The user authorized to access this income + statement view. + :type authorized_user: str """ @@ -3997,7 +6612,21 @@ class DateModelIncomeStatementView( FiscalYearIncomeStatementViewBase, DateReportMixIn, DjangoLedgerSecurityMixIn ): """ - Date Income Statement View. + Represents a detailed view of an income statement for a fiscal year with additional + capabilities for handling dates and security integrations. + + This class is a specialized combination of views and mixins designed to manage income + statements with added functionality for date-based operations and Django-based ledger + security. It inherits behaviors for fiscal year reports, date handling, and security + enforcements, providing a cohesive interface for managing income statement data. + + :ivar fiscal_year_data: Contains detailed income statement data for a specific fiscal year. + :type fiscal_year_data: dict + :ivar report_date: Represents the date of the associated report. + :type report_date: datetime.date + :ivar user_permissions: Tracks security permissions associated with the income statement for + authenticated users. + :type user_permissions: dict """ @@ -4007,6 +6636,17 @@ class DateModelIncomeStatementView( class BaseCashFlowStatementRedirectViewBase( BaseCashFlowStatementRedirectView, DjangoLedgerSecurityMixIn ): + """ + Base class for handling cash flow statement redirection views. + + This class is designed to manage the redirection of users to the + appropriate cash flow statement view for a particular year and entity. + It incorporates security features and functionalities specific to dealers + or entities by extending relevant mixins. + + :ivar request: The HTTP request object associated with the view. + :type request: HttpRequest + """ def get_redirect_url(self, *args, **kwargs): year = get_localdate().year dealer = get_user_type(self.request) @@ -4021,6 +6661,21 @@ class BaseCashFlowStatementRedirectViewBase( class FiscalYearCashFlowStatementViewBase( FiscalYearCashFlowStatementView, DjangoLedgerSecurityMixIn ): + """ + Represents a base view for fiscal year cash flow statements. + + This class is utilized to handle the rendering and permissions control + for fiscal year cash flow statement views. It inherits functionality + from FiscalYearCashFlowStatementView and DjangoLedgerSecurityMixIn + to integrate with the Django framework's security and reporting + mechanisms. + + :ivar template_name: Template path to render the view. + :type template_name: str + :ivar permission_required: List of permissions required for accessing + the view. + :type permission_required: list + """ template_name = "ledger/reports/cash_flow_statement.html" permission_required = ["inventory.view_carfinance"] @@ -4032,7 +6687,27 @@ class QuarterlyCashFlowStatementView( FiscalYearCashFlowStatementViewBase, QuarterlyReportMixIn, DjangoLedgerSecurityMixIn ): """ - Quarter Cash Flow Statement View. + Represents a view model for quarterly cash flow statements. + + This class is designed to encapsulate and handle data related to + quarterly cash flow statements, extending the functionality from + base classes and mix-ins for specific behaviors. It combines features + from fiscal year cash flow statements, quarterly reporting utilities, + and Django ledger security mechanisms to provide comprehensive support + for managing and displaying financial data for quarterly cash flows. + + :ivar fiscal_year: The fiscal year associated with the cash flow + statement. + :type fiscal_year: int + :ivar quarter: The quarter associated with the cash flow statement, + typically denoted as an integer from 1 to 4. + :type quarter: int + :ivar cash_flow_data: A structure containing detailed cash flow + information for the given quarter and fiscal year. + :type cash_flow_data: dict + :ivar is_audited: Boolean flag indicating whether the quarterly cash + flow statement has been audited. + :type is_audited: bool """ @@ -4040,7 +6715,18 @@ class MonthlyCashFlowStatementView( FiscalYearCashFlowStatementViewBase, MonthlyReportMixIn, DjangoLedgerSecurityMixIn ): """ - Monthly Cash Flow Statement View. + Represents a view for monthly cash flow statements. + + This class combines the functionality of fiscal year cash flow statement views, + monthly reporting capabilities, and security features related to Django Ledger. + It is utilized to generate or retrieve cash flow statements on a monthly basis, + ensuring seamless integration with other components that extend or implement + financial reporting logic. + + :ivar attribute1: Description of attribute1. + :type attribute1: type + :ivar attribute2: Description of attribute2. + :type attribute2: type """ @@ -4048,7 +6734,15 @@ class DateCashFlowStatementView( FiscalYearCashFlowStatementViewBase, DateReportMixIn, DjangoLedgerSecurityMixIn ): """ - Date Cash Flow Statement View. + Representation of a detailed view for a cash flow statement associated with a specific fiscal year, + including reporting capabilities and security features. This class extends functionality from + multiple mix-in classes to provide a comprehensive cash flow statement representation with date-specific + reporting. + + :ivar field1: Description of field1. + :type field1: type + :ivar field2: Description of field2. + :type field2: type """ @@ -4058,6 +6752,20 @@ class DateCashFlowStatementView( class EntityModelDetailHandlerViewBase( EntityModelDetailHandlerView, DjangoLedgerSecurityMixIn ): + """ + Handles detailed views for Entity Models along with redirection logic + customized for different users and units. + + This class extends functionality from `EntityModelDetailHandlerView` and + integrates additional security measures using `DjangoLedgerSecurityMixIn`. + It provides a mechanism to construct redirection URLs based on the specifics + of the current request, such as the user's dealer type, the unit slug, and the + current localized date. Typically used for redirecting to appropriate dashboards + with monthly data filtered by either entity or unit. + + :ivar request: The HTTP request object associated with the current view. + :type request: HttpRequest + """ def get_redirect_url(self, *args, **kwargs): loc_date = get_localdate() dealer = get_user_type(self.request) @@ -4085,6 +6793,21 @@ class EntityModelDetailHandlerViewBase( class EntityModelDetailBaseViewBase( EntityModelDetailBaseView, DjangoLedgerSecurityMixIn ): + """ + Represents a base view that extends functionality for displaying detailed + dashboard information about an entity model. Handles context population for + dynamic elements like charts and titles specific to the entity or an optional + unit. + + This class is designed as part of a Django application, utilizing mixins for + enhanced security and inheriting a basic entity model detail view. It is + customized to render a specific dashboard template and manage context data + for charts and other display elements relevant to the entity. + + :ivar template_name: Path to the HTML template used for rendering the + dashboard. + :type template_name: str + """ template_name = "ledger/reports/dashboard.html" def get_context_data(self, **kwargs): @@ -4127,7 +6850,15 @@ class FiscalYearEntityModelDashboardView( EntityModelDetailBaseViewBase, DjangoLedgerSecurityMixIn ): """ - Entity Fiscal Year Dashboard View. + Represents a dashboard view for fiscal year entity models. + + This class serves as a view to display details related to fiscal year + entity models while enforcing permissions and login requirements. + It integrates security features and provides functionalities specific + to managing fiscal year entity models within the dashboard. + + :ivar permission_required: List of permissions required to access the view. + :type permission_required: list """ permission_required = ["inventory.view_carfinance"] @@ -4140,7 +6871,23 @@ class QuarterlyEntityDashboardView( FiscalYearEntityModelDashboardView, QuarterlyReportMixIn, DjangoLedgerSecurityMixIn ): """ - Entity Quarterly Dashboard View. + Represents a dashboard view for quarterly entities. + + This class integrates the functionalities of `FiscalYearEntityModelDashboardView`, + `QuarterlyReportMixIn`, and `DjangoLedgerSecurityMixIn` to provide a dashboard + view that displays and organizes quarterly financial metrics and reports for a + specific fiscal year entity. It facilitates secure access to financial data and + provides interfaces to analyze quarterly reports. + + :ivar quarters: A list of quarters included in the dashboard view. + :type quarters: list + :ivar entity: The entity for which the dashboard view is generated. + :type entity: object + :ivar fiscal_year: The fiscal year associated with the dashboard. + :type fiscal_year: int + :ivar reporting_data: A collection of data specifically prepared for + quarterly reporting. + :type reporting_data: dict """ @@ -4148,7 +6895,28 @@ class MonthlyEntityDashboardView( FiscalYearEntityModelDashboardView, MonthlyReportMixIn, DjangoLedgerSecurityMixIn ): """ - Monthly Entity Dashboard View. + Represents a dashboard view for a specific entity's monthly report. + + This class combines functionalities from the base dashboard view for + fiscal year entities, as well as specific monthly report and security + features, to provide a comprehensive dashboard handler. It is designed + to work within the Django Ledger system, facilitating secured and + contextual data display tailored for monthly reporting of financial + entities. + + :ivar fiscal_year: Represents the fiscal year associated with the + dashboard view. + :type fiscal_year: int + :ivar entity_id: The unique identifier for the financial entity + being reported on in the dashboard. + :type entity_id: int + :ivar report_month: The specific month for which the report is + generated and displayed. + :type report_month: str + :ivar user_permissions: Permissions associated with the currently + authenticated user to enforce security on + sensitive actions or data. + :type user_permissions: list """ @@ -4156,11 +6924,37 @@ class DateEntityDashboardView( FiscalYearEntityModelDashboardView, DateReportMixIn, DjangoLedgerSecurityMixIn ): """ - Date-specific Entity Dashboard View. + Represents a dashboard view for date-based entity data visualization. + + This class integrates date-based information into the dashboard view, + extending the functionality of `FiscalYearEntityModelDashboardView`. + It utilizes mixins to incorporate report generation and security features + specific to the Django Ledger framework. + + :ivar fiscal_year: The fiscal year associated with the dashboard data. + :type fiscal_year: FiscalYear + :ivar date_report_data: Data pertaining to the date-specific reports displayed. + :type date_report_data: dict + :ivar user_permissions: The security permissions granted to the user for viewing + or interacting with the dashboard. + :type user_permissions: set """ class PayableNetAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View): + """ + Handles the retrieval of net payable data for authenticated users. + + The PayableNetAPIView is designed to process GET requests for retrieving + a summary of unpaid bills, specifically for authenticated users associated + with an entity. This view ensures that only authorized users, based on their + authentication state and type, can access this sensitive financial data. It + leverages the DjangoLedgerSecurityMixIn and EntityUnitMixIn for security + enhancements and entity-specific processing. + + :ivar http_method_names: HTTP methods supported by this view. + :type http_method_names: list[str] + """ http_method_names = ["get"] def get(self, request, *args, **kwargs): @@ -4182,6 +6976,18 @@ class PayableNetAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View): class ReceivableNetAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View): + """ + Handles the retrieval of net receivables summary for authenticated users. + + This view is part of a ledger system and is designed to provide a net summary + of unpaid invoices for an authenticated user. If the user is not authenticated, + the view will return an unauthorized response. The view supports only GET + requests. + + Class Attributes: + :ivar http_method_names: Restricts the allowed HTTP methods for this view. + :type http_method_names: list[str] + """ http_method_names = ["get"] def get(self, request, *args, **kwargs): @@ -4204,6 +7010,17 @@ class ReceivableNetAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View): class PnLAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View): + """ + APIView for handling Profit and Loss (PnL) data retrieval. + + This class provides an endpoint to handle the retrieval of Profit and Loss data + for an entity. It uses authentication to validate the user's access and retrieves + PnL data based on the user's entity and optional query parameters such as dates. + The retrieved data is organized and returned in a JSON format. + + :ivar http_method_names: Restricts HTTP methods that can be used with this view. + :type http_method_names: list[str] + """ http_method_names = ["get"] def get(self, request, *args, **kwargs): @@ -4253,6 +7070,22 @@ class PnLAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View): class EmployeeCalendarView(LoginRequiredMixin, ListView): + """ + Provides a view for displaying the employee's calendar in a list format. + + Displays a list of appointments for logged-in users. This view ensures that + only appointments relevant to the logged-in user as a dealer or staff member + are displayed. Supports search functionality to filter displayed appointments + based on provided query parameters. + + :ivar template_name: Path to the HTML template used to render the view. + :type template_name: str + :ivar model: Model object to interact with appointments data. + :type model: Appointment + :ivar context_object_name: Name of the context variable that contains the + list of appointments in the template. + :type context_object_name: str + """ template_name = "crm/employee_calendar.html" model = Appointment context_object_name = "appointments" @@ -4273,6 +7106,20 @@ class EmployeeCalendarView(LoginRequiredMixin, ListView): def apply_search_filters(queryset, query): + """ + Apply search filters to a queryset based on a query string. + + This function filters the provided queryset by applying a Q object for all + CharField, TextField, or EmailField fields in the model. It checks if the + query exists within these fields using case-insensitive containment (icontains). + If the query string is empty or None, the original queryset is returned + without any modifications. + + :param queryset: The initial queryset to apply the search filters on. + :param query: The search string to match against the model fields. If None + or an empty string, no filtering is applied. + :return: A filtered queryset that satisfies the search condition. + """ if not query: return queryset @@ -4290,6 +7137,23 @@ def apply_search_filters(queryset, query): class CarListViewTable(LoginRequiredMixin, ExportMixin, SingleTableView): + """ + Displays a table view of cars for a user with proper permissions. + + This class-based view leverages functionality for login-required access, data + exporting, and integration with a single table representation. It is designed + to fetch and display a list of car objects related to a specific dealer in + a tabular format. The view also renders a predefined template to display + the data appropriately. + + :ivar model: Specifies the model for the table view (Car). + :type model: models.Car + :ivar table_class: Represents the table class to be used for rendering + the data in tabular format. + :type table_class: tables.CarTable + :ivar template_name: Defines the template to be used for rendering the view. + :type template_name: str + """ model = models.Car table_class = tables.CarTable template_name = "inventory/car_list_table.html" @@ -4303,6 +7167,22 @@ class CarListViewTable(LoginRequiredMixin, ExportMixin, SingleTableView): @login_required def DealerSettingsView(request, pk): + """ + Handles dealer settings view where dealers can update their financial and + payment account settings. This view ensures validation and reassigns form + fields dynamically based on the dealer's account roles. + + :param request: The HTTP request object received from the client. This parameter + contains data including HTTP headers, session, and POST data if applicable. + :type request: HttpRequest + :param pk: Primary key representing the dealer for whom the settings are being + retrieved or modified. This identifier is used to fetch or update dealer- + specific settings from the database. + :type pk: int + :return: An HTTP response rendering the dealer settings form or redirecting + to the dealer detail view after successful form submission. + :rtype: HttpResponse + """ dealer_setting = get_object_or_404(models.DealerSettings, dealer__pk=pk) dealer = get_user_type(request) if request.method == "POST": @@ -4346,6 +7226,19 @@ def DealerSettingsView(request, pk): @login_required def schedule_cancel(request, pk): + """ + Cancel a schedule by updating its status to "Canceled". The function is protected + by a login requirement, ensuring only authenticated users can execute it. It + retrieves the schedule object by its primary key, updates its status, saves the + changes, and returns an HTTP response with status code 200. + + :param request: The HTTP request object representing the user's request. + :type request: HttpRequest + :param pk: The primary key of the schedule to be canceled. + :type pk: int + :return: An HTTP response object with a 200 status code upon successful execution. + :rtype: HttpResponse + """ schedule = get_object_or_404(models.Schedule, pk=pk) schedule.status = "Canceled" schedule.save() @@ -4356,6 +7249,22 @@ def schedule_cancel(request, pk): @login_required def assign_car_makes(request): + """ + Assigns car makes to a dealer. + + This function handles both the display and processing of a form that allows + a dealer to assign or modify their associated car makes. If the request + method is POST, it validates and saves the submitted form data. If the + method is not POST, it displays a form prefilled with the existing car makes + associated with the dealer. + + :param request: The HTTP request object containing information about the + current request. + :type request: HttpRequest + :return: A rendered HTML response for GET requests, or a redirect to the + dealer detail page after successful form submission. + :rtype: HttpResponse + """ dealer = get_user_type(request) if request.method == "POST": form = forms.DealersMakeForm(request.POST, dealer=dealer) @@ -4375,6 +7284,34 @@ def assign_car_makes(request): class LedgerModelListView(LoginRequiredMixin, ListView, ArchiveIndexView): + """ + Provides a view for listing ledger entries in the system. + + This class-based view combines the functionality of LoginRequiredMixin, + ListView, and ArchiveIndexView to display a filtered and ordered + list of LedgerModel entries based on the user's type and entity. It + also allows toggling between different subsets of data, such as + all entries, current entries, or visible entries. + + :ivar model: The model that this view displays. + :type model: type[LedgerModel] + :ivar context_object_name: The name of the context variable containing the list of LedgerModel instances. + :type context_object_name: str + :ivar template_name: The template used to render this view. + :type template_name: str + :ivar date_field: The date field used for ordering and archive functionality. + :type date_field: str + :ivar ordering: Default ordering for the queryset. + :type ordering: str + :ivar show_all: Determines whether all ledger entries should be shown. + :type show_all: bool + :ivar show_current: Determines whether only current ledger entries should be shown. + :type show_current: bool + :ivar show_visible: Determines whether only visible ledger entries should be shown. + :type show_visible: bool + :ivar allow_empty: Allows rendering of the page even if the queryset is empty. + :type allow_empty: bool + """ model = LedgerModel context_object_name = "ledgers" template_name = "ledger/ledger/ledger_list.html" @@ -4408,11 +7345,39 @@ class LedgerModelListView(LoginRequiredMixin, ListView, ArchiveIndexView): # class LedgerModelDetailView(InvoiceModelDetailViewBase): class LedgerModelDetailView(LoginRequiredMixin, DetailView): + """ + This class provides a detailed view of a specific ledger entry. + + The LedgerModelDetailView is implemented as a Django DetailView, enabling it to + retrieve and display detailed information about a single ledger instance. + It ensures that only authenticated users have access to this view by using the + LoginRequiredMixin. + + :ivar model: The model associated with this view, representing ledger entries. + :type model: type[LedgerModel] + :ivar context_object_name: The name of the context variable available in the + template for the detailed ledger. + :type context_object_name: str + :ivar template_name: The path to the template used to render the detailed ledger view. + :type template_name: str + """ model = LedgerModel context_object_name = "ledger" template_name = "ledger/ledger/ledger_detail.html" class LedgerModelCreateView(LedgerModelCreateViewBase): + """ + Handles the creation of LedgerModel entities. + + This class provides the logic to manage the creation process of + LedgerModel instances. It determines the appropriate form for creating + a LedgerModel, validates the submitted form, and redirects the user + upon success. The view is specific to a particular user type (dealer). + + :ivar template_name: Specifies the path to the template used for the + form rendering. + :type template_name: str + """ template_name = "ledger/ledger/ledger_form.html" def get_form(self, form_class=None): @@ -4433,11 +7398,37 @@ class LedgerModelCreateView(LedgerModelCreateViewBase): class LedgerModelModelActionView(LedgerModelModelActionViewBase): + """ + Represents a view for handling actions related to the Ledger model. + + This class extends the LedgerModelModelActionViewBase and is responsible for + providing functionality to determine the redirection URL after performing + specific actions on a Ledger model instance. + + :ivar template_name: Name of the template used for rendering the view. + :type template_name: str + :ivar model: The model associated with this view. + :type model: type[Model] + """ def get_redirect_url(self, *args, **kwargs): return reverse("ledger_list") class LedgerModelDeleteView(LedgerModelDeleteViewBase, SuccessMessageMixin): + """ + Handles the deletion of a Ledger model instance. + + Provides functionality for rendering a confirmation template and deleting a + ledger instance from the system. Extends functionality for managing success + messages and redirections upon successful deletion. + + :ivar template_name: Path to the template used for rendering the delete + confirmation view. + :type template_name: str + :ivar success_message: Success message displayed upon successful deletion + of the ledger instance. + :type success_message: str + """ template_name = "ledger/ledger/ledger_delete.html" success_message = "Ledger deleted" @@ -4467,6 +7458,23 @@ class LedgerModelDeleteView(LedgerModelDeleteViewBase, SuccessMessageMixin): class JournalEntryListView(LoginRequiredMixin, ListView): + """ + Represents a view to list all journal entries for a specific ledger. + + This class inherits from Django's `ListView` and `LoginRequiredMixin` to provide + a secure and paginated list of journal entries associated with a specific ledger. + It ensures the user is authenticated before access to the journal entries is + granted. The view customizes the queryset and context data to include only + entries tied to the given ledger specified by its primary key. + + :ivar model: The model associated with the view. + :type model: JournalEntryModel + :ivar context_object_name: The name of the context variable which will hold the + list of journal entries in the template. + :type context_object_name: str + :ivar template_name: The path to the HTML template that renders the list view. + :type template_name: str + """ model = JournalEntryModel context_object_name = "journal_entries" template_name = "ledger/journal_entry/journal_entry_list.html" @@ -4484,6 +7492,25 @@ class JournalEntryListView(LoginRequiredMixin, ListView): class JournalEntryCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): + """ + View for creating a new journal entry. + + This class-based view allows authenticated users to create new journal entries + associated with a specific ledger. It includes functionality for handling + context data, form initialization, and redirection upon successful submission + of the form. + + :ivar model: The model used for creating the journal entry. + :type model: JournalEntryModel + :ivar template_name: The template used to render the form. + :type template_name: str + :ivar form_class: The form class used to create the journal entry. + :type form_class: forms.JournalEntryModelCreateForm + :ivar ledger_model: The ledger model associated with this journal entry. + :type ledger_model: LedgerModel or None + :ivar success_message: The message displayed upon successfully creating a journal entry. + :type success_message: str + """ model = JournalEntryModel template_name = "ledger/journal_entry/journal_entry_form.html" form_class = forms.JournalEntryModelCreateForm @@ -4510,6 +7537,21 @@ class JournalEntryCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView def JournalEntryDeleteView(request, pk): + """ + Handles the deletion of a specific journal entry. This view facilitates + the deletion of a journal entry identified by its primary key (pk). If the + deletion is successful, the user is redirected to the list of journal entries + for the associated ledger. If the deletion cannot proceed, an error message + is displayed, and the user is redirected back to the journal entry list. + + :param request: The HTTP request object. + :type request: HttpRequest + :param pk: The primary key (pk) of the journal entry to be deleted. + :type pk: int + :return: A rendered HTML response for GET requests or a redirect upon + successful/failed deletion. + :rtype: HttpResponse + """ journal_entry = get_object_or_404(JournalEntryModel, pk=pk) if request.method == "POST": ledger = journal_entry.ledger @@ -4527,6 +7569,19 @@ def JournalEntryDeleteView(request, pk): def JournalEntryTransactionsView(request, pk): + """ + Handles the retrieval and display of journal entry transactions for a specific journal + entry instance. It retrieves the journal entry and its associated transactions, ordering + the transactions by account code. The data is then rendered using the specified template. + + :param request: The HTTP request object. + :type request: django.http.HttpRequest + :param pk: The primary key of the journal entry to be retrieved. + :type pk: int + :return: An HTTP response with the rendered template, including the journal entry and + its transactions. + :rtype: django.http.HttpResponse + """ journal = JournalEntryModel.objects.filter(pk=pk).first() transactions = ( TransactionModel.objects.filter(journal_entry=journal) @@ -4541,10 +7596,37 @@ def JournalEntryTransactionsView(request, pk): class JournalEntryModelTXSDetailView(JournalEntryModelTXSDetailViewBase): + """ + Represents a detailed view of journal entry transactions in the ledger. + + This class is used to render the transactions associated with a specific + journal entry. It extends the base view functionality and is tailored for a + transaction details page. + + :ivar template_name: The path to the template used to render the journal + entry transactions view. + :type template_name: str + """ template_name = "ledger/journal_entry/journal_entry_txs.html" def ledger_lock_all_journals(request, entity_slug, pk): + """ + Locks all journals associated with a specific ledger. If the ledger is already locked, + it will notify the user through an error message. Otherwise, it initiates the locking of + related journal entries, locks the ledger itself, and saves the changes to the database. + After the operation, it redirects the user to the journal entry list associated with the + ledger. + + :param request: HttpRequest object representing the current request. + :type request: HttpRequest + :param entity_slug: The slug identifier of the entity. + :type entity_slug: str + :param pk: The primary key of the ledger to be locked. + :type pk: int + :return: HttpResponse redirecting to the journal entry list page of the locked ledger. + :rtype: HttpResponse + """ ledger = LedgerModel.objects.filter(pk=pk).first() if ledger.is_locked(): messages.error(request, _("Ledger is already locked")) @@ -4556,6 +7638,22 @@ def ledger_lock_all_journals(request, entity_slug, pk): def ledger_unlock_all_journals(request, entity_slug, pk): + """ + Unlocks all journal entries associated with a specific ledger. This function first checks if the + ledger is locked. If it is already unlocked, it shows an error message and redirects the user + to the journal entry list page. If the ledger is locked, it unlocks the ledger, saves changes, + and iterates through all the locked journal entries within the ledger to unlock them as well. + Afterward, it redirects the user to the journal entry list page. + + :param request: The HTTP request object containing user session and request metadata. + :type request: HttpRequest + :param entity_slug: A unique string slug representing the specific entity or organization context. + :type entity_slug: str + :param pk: The primary key of the ledger record to be unlocked. + :type pk: int + :return: A redirection to the journal entry list page for the specified ledger. + :rtype: HttpResponseRedirect + """ ledger = LedgerModel.objects.filter(pk=pk).first() if not ledger.is_locked(): messages.error(request, _("Ledger is already Unlocked")) @@ -4571,6 +7669,20 @@ def ledger_unlock_all_journals(request, entity_slug, pk): def ledger_post_all_journals(request, entity_slug, pk): + """ + Posts all journal entries associated with a ledger. This function updates the ledger's + state to reflect that its journal entries have been posted. If the ledger is already + posted, an error message is displayed and the user is redirected. + + :param request: The HTTP request object used for processing the action. + :type request: HttpRequest + :param entity_slug: A string representing the specific entity slug for the ledger context. + :type entity_slug: str + :param pk: The primary key of the ledger to be posted. + :type pk: int + :return: A redirect to the journal entry list view for the specified ledger. + :rtype: HttpResponseRedirect + """ ledger = LedgerModel.objects.filter(pk=pk).first() if ledger.is_posted(): messages.error(request, _("Ledger is already posted")) @@ -4582,6 +7694,25 @@ def ledger_post_all_journals(request, entity_slug, pk): def ledger_unpost_all_journals(request, entity_slug, pk): + """ + Unposts all journal entries for a specified ledger and marks the ledger as unposted. + + This function identifies a ledger by its primary key (pk) and checks if it is + already posted. If the ledger is not posted, an error message is displayed. + If it is posted, the function iterates through its posted journal entries, + marks them as unposted, and saves the changes. Finally, it marks the ledger itself + as unposted and saves it. + + :param request: The HTTP request object from the client. + :type request: HttpRequest + :param entity_slug: A slug identifying the entity associated with the ledger. + :type entity_slug: str + :param pk: The primary key of the ledger whose journal entries are being unposted. + :type pk: int + :return: An HTTP redirect response object directing the client to the journal entry list + page for the specified ledger. + :rtype: HttpResponseRedirect + """ ledger = LedgerModel.objects.filter(pk=pk).first() if not ledger.is_posted(): messages.error(request, _("Ledger is already Unposted"))