This commit is contained in:
Marwan Alwali 2025-04-24 17:24:20 +03:00
parent 916c1e8b16
commit 780eb1b35c
16 changed files with 4684 additions and 24 deletions

View File

@ -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)

View File

@ -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()

View File

@ -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")

View File

@ -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)):

View File

@ -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']

View File

@ -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):

View File

@ -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

View File

@ -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):
"""

View File

@ -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)

View File

@ -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,

View File

@ -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

View File

@ -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"))

View File

@ -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()

View File

@ -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)

View File

@ -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 entitys 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()

File diff suppressed because it is too large Load Diff