Compare commits
7 Commits
86792930f0
...
ee8d8cf287
| Author | SHA1 | Date | |
|---|---|---|---|
| ee8d8cf287 | |||
| a079ceec6d | |||
| 05b18e9da3 | |||
| f0ab5d1888 | |||
| 2fb00fadaf | |||
| 1a647442c4 | |||
| e1364d4f5a |
@ -6,17 +6,64 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def check_create_coa_accounts(task):
|
||||
logger.info("Checking if all accounts are created")
|
||||
instance = task.kwargs["dealer"]
|
||||
entity = instance.entity
|
||||
coa = entity.get_default_coa()
|
||||
"""
|
||||
Hook to verify account creation and handle failures
|
||||
"""
|
||||
if task.success:
|
||||
logger.info("Account creation task completed successfully")
|
||||
return
|
||||
|
||||
for account_data in get_accounts_data():
|
||||
if entity.get_all_accounts().filter(code=account_data["code"]).exists():
|
||||
logger.info(f"Default account already exists: {account_data['code']}")
|
||||
continue
|
||||
logger.info(f"Default account does not exist: {account_data['code']}")
|
||||
create_account(entity, coa, account_data)
|
||||
logger.warning("Account creation task failed, checking status...")
|
||||
|
||||
try:
|
||||
dealer_id = task.kwargs.get('dealer_id')
|
||||
if not dealer_id:
|
||||
logger.error("No dealer_id in task kwargs")
|
||||
return
|
||||
|
||||
from .models import Dealer
|
||||
instance = Dealer.objects.select_related('entity').get(id=dealer_id)
|
||||
entity = instance.entity
|
||||
|
||||
if not entity:
|
||||
logger.error(f"No entity for dealer {dealer_id}")
|
||||
return
|
||||
|
||||
coa = entity.get_default_coa()
|
||||
if not coa:
|
||||
logger.error(f"No COA for entity {entity.id}")
|
||||
return
|
||||
|
||||
# Check which accounts are missing and create them
|
||||
from .utils import get_accounts_data, create_account
|
||||
|
||||
missing_accounts = []
|
||||
for account_data in get_accounts_data():
|
||||
if not entity.get_all_accounts().filter(code=account_data["code"]).exists():
|
||||
missing_accounts.append(account_data)
|
||||
logger.info(f"Missing account: {account_data['code']}")
|
||||
|
||||
if missing_accounts:
|
||||
logger.info(f"Creating {len(missing_accounts)} missing accounts")
|
||||
for account_data in missing_accounts:
|
||||
create_account(entity, coa, account_data)
|
||||
else:
|
||||
logger.info("All accounts are already created")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in check_create_coa_accounts hook: {e}")
|
||||
# def check_create_coa_accounts(task):
|
||||
# logger.info("Checking if all accounts are created")
|
||||
# instance = task.kwargs["dealer"]
|
||||
# entity = instance.entity
|
||||
# coa = entity.get_default_coa()
|
||||
|
||||
# for account_data in get_accounts_data():
|
||||
# if entity.get_all_accounts().filter(code=account_data["code"]).exists():
|
||||
# logger.info(f"Default account already exists: {account_data['code']}")
|
||||
# continue
|
||||
# logger.info(f"Default account does not exist: {account_data['code']}")
|
||||
# create_account(entity, coa, account_data)
|
||||
|
||||
|
||||
def print_results(task):
|
||||
|
||||
@ -685,7 +685,7 @@ class Car(Base):
|
||||
)
|
||||
#
|
||||
additional_services = models.ManyToManyField(
|
||||
AdditionalServices, related_name="additionals", blank=True, null=True
|
||||
AdditionalServices, related_name="additionals"
|
||||
)
|
||||
cost_price = models.DecimalField(
|
||||
max_digits=14,
|
||||
@ -1711,7 +1711,7 @@ class Customer(models.Model):
|
||||
national_id = models.CharField(
|
||||
max_length=10, unique=True, verbose_name=_("National ID"), null=True, blank=True
|
||||
)
|
||||
|
||||
|
||||
phone_number = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_("Phone Number"),
|
||||
@ -3307,6 +3307,7 @@ class CustomGroup(models.Model):
|
||||
"payment",
|
||||
"vendor",
|
||||
"additionalservices",
|
||||
'customer'
|
||||
],
|
||||
other_perms=[
|
||||
"view_car",
|
||||
|
||||
@ -135,55 +135,101 @@ def create_car_location(sender, instance, created, **kwargs):
|
||||
print(f"Failed to create CarLocation for car {instance.vin}: {e}")
|
||||
|
||||
|
||||
# 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(
|
||||
name=entity_name,
|
||||
admin=instance.user,
|
||||
use_accrual_method=True,
|
||||
fy_start_month=1,
|
||||
)
|
||||
try:
|
||||
# Use transaction to ensure atomicity
|
||||
with transaction.atomic():
|
||||
entity_name = instance.user.dealer.name
|
||||
entity = EntityModel.create_entity(
|
||||
name=entity_name,
|
||||
admin=instance.user,
|
||||
use_accrual_method=True,
|
||||
fy_start_month=1,
|
||||
)
|
||||
|
||||
if entity:
|
||||
instance.entity = entity
|
||||
instance.save()
|
||||
coa = entity.create_chart_of_accounts(
|
||||
assign_as_default=True, commit=True, coa_name=_(f"{entity_name}-COA")
|
||||
)
|
||||
if coa:
|
||||
# Create unit of measures
|
||||
entity.create_uom(name="Unit", unit_abbr="unit")
|
||||
for u in models.UnitOfMeasure.choices:
|
||||
entity.create_uom(name=u[1], unit_abbr=u[0])
|
||||
if entity:
|
||||
instance.entity = entity
|
||||
instance.save(update_fields=['entity'])
|
||||
|
||||
# Create COA accounts, background task
|
||||
# Create COA synchronously first
|
||||
coa = entity.create_chart_of_accounts(
|
||||
assign_as_default=True, commit=True,
|
||||
coa_name=_(f"{entity_name}-COA")
|
||||
)
|
||||
|
||||
if coa:
|
||||
# Create essential UOMs synchronously
|
||||
entity.create_uom(name="Unit", unit_abbr="unit")
|
||||
|
||||
# Schedule async task after successful synchronous operations
|
||||
async_task(
|
||||
func="inventory.tasks.create_coa_accounts",
|
||||
dealer=instance,
|
||||
dealer_id=instance.id, # Pass ID instead of object
|
||||
hook="inventory.hooks.check_create_coa_accounts",
|
||||
ack_failure=True, # Ensure task failures are acknowledged
|
||||
sync=False # Explicitly set to async
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create ledger entity for dealer {instance.id}: {e}")
|
||||
# Schedule retry task
|
||||
async_task(
|
||||
func="inventory.tasks.retry_entity_creation",
|
||||
dealer_id=instance.id,
|
||||
retry_count=0
|
||||
)
|
||||
# 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(
|
||||
# name=entity_name,
|
||||
# admin=instance.user,
|
||||
# use_accrual_method=True,
|
||||
# fy_start_month=1,
|
||||
# )
|
||||
|
||||
# if entity:
|
||||
# instance.entity = entity
|
||||
# instance.save()
|
||||
# coa = entity.create_chart_of_accounts(
|
||||
# assign_as_default=True, commit=True, coa_name=_(f"{entity_name}-COA")
|
||||
# )
|
||||
# if coa:
|
||||
# # Create unit of measures
|
||||
# entity.create_uom(name="Unit", unit_abbr="unit")
|
||||
# for u in models.UnitOfMeasure.choices:
|
||||
# entity.create_uom(name=u[1], unit_abbr=u[0])
|
||||
|
||||
# # Create COA accounts, background task
|
||||
# async_task(
|
||||
# func="inventory.tasks.create_coa_accounts",
|
||||
# dealer=instance,
|
||||
# hook="inventory.hooks.check_create_coa_accounts",
|
||||
# )
|
||||
# async_task('inventory.tasks.check_create_coa_accounts', instance, schedule_type='O', schedule_time=timedelta(seconds=20))
|
||||
|
||||
# create_settings(instance.pk)
|
||||
|
||||
@ -62,14 +62,117 @@ def create_settings(pk):
|
||||
)
|
||||
|
||||
|
||||
def create_coa_accounts(**kwargs):
|
||||
logger.info("creating all accounts are created")
|
||||
instance = kwargs.get("dealer")
|
||||
entity = instance.entity
|
||||
coa = entity.get_default_coa()
|
||||
def create_coa_accounts(dealer_id, **kwargs):
|
||||
"""
|
||||
Create COA accounts with retry logic and proper error handling
|
||||
"""
|
||||
from .models import Dealer
|
||||
from .utils import create_account, get_accounts_data
|
||||
|
||||
for account_data in get_accounts_data():
|
||||
create_account(entity, coa, account_data)
|
||||
max_retries = 3
|
||||
retry_delay = 2 # seconds
|
||||
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
logger.info(f"Attempt {attempt + 1} to create accounts for dealer {dealer_id}")
|
||||
|
||||
# Get fresh instance from database
|
||||
instance = Dealer.objects.select_related('entity').get(id=dealer_id)
|
||||
entity = instance.entity
|
||||
|
||||
if not entity:
|
||||
logger.error(f"No entity found for dealer {dealer_id}")
|
||||
return False
|
||||
|
||||
coa = entity.get_default_coa()
|
||||
|
||||
if not coa:
|
||||
logger.error(f"No COA found for entity {entity.id}")
|
||||
return False
|
||||
|
||||
logger.info("Creating default accounts")
|
||||
accounts_created = 0
|
||||
|
||||
with transaction.atomic():
|
||||
for account_data in get_accounts_data():
|
||||
logger.info(f"Creating account: {account_data['code']}")
|
||||
if create_account(entity, coa, account_data):
|
||||
accounts_created += 1
|
||||
|
||||
logger.info(f"Successfully created {accounts_created} accounts")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Attempt {attempt + 1} failed: {e}")
|
||||
|
||||
if attempt < max_retries - 1:
|
||||
logger.info(f"Retrying in {retry_delay} seconds...")
|
||||
time.sleep(retry_delay * (attempt + 1)) # Exponential backoff
|
||||
else:
|
||||
logger.error(f"All {max_retries} attempts failed for dealer {dealer_id}")
|
||||
# Schedule a cleanup or notification task
|
||||
async_task(
|
||||
"inventory.tasks.handle_account_creation_failure",
|
||||
dealer_id=dealer_id,
|
||||
error=str(e)
|
||||
)
|
||||
return False
|
||||
|
||||
def retry_entity_creation(dealer_id, retry_count=0):
|
||||
"""
|
||||
Retry entity creation if initial attempt failed
|
||||
"""
|
||||
from .models import Dealer
|
||||
from yourapp.models import EntityModel
|
||||
|
||||
max_retries = 3
|
||||
|
||||
if retry_count >= max_retries:
|
||||
logger.error(f"Max retries reached for dealer {dealer_id}")
|
||||
return
|
||||
|
||||
try:
|
||||
instance = Dealer.objects.get(id=dealer_id)
|
||||
if not instance.entity:
|
||||
# Retry entity creation
|
||||
entity_name = instance.user.dealer.name
|
||||
entity = EntityModel.create_entity(
|
||||
name=entity_name,
|
||||
admin=instance.user,
|
||||
use_accrual_method=True,
|
||||
fy_start_month=1,
|
||||
)
|
||||
|
||||
if entity:
|
||||
instance.entity = entity
|
||||
instance.save()
|
||||
logger.info(f"Successfully created entity on retry {retry_count + 1}")
|
||||
|
||||
# Now trigger account creation
|
||||
async_task(
|
||||
"inventory.tasks.create_coa_accounts",
|
||||
dealer_id=dealer_id
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Retry {retry_count + 1} failed: {e}")
|
||||
# Schedule another retry
|
||||
async_task(
|
||||
"inventory.tasks.retry_entity_creation",
|
||||
dealer_id=dealer_id,
|
||||
retry_count=retry_count + 1
|
||||
)
|
||||
# def create_coa_accounts(**kwargs):
|
||||
# logger.info("creating all accounts are created")
|
||||
# instance = kwargs.get("dealer")
|
||||
# logger.info(f"Dealer Instance : {instance}")
|
||||
# entity = instance.entity
|
||||
# coa = entity.get_default_coa()
|
||||
|
||||
# logger.info("Creating default accounts")
|
||||
# for account_data in get_accounts_data():
|
||||
# logger.info(f"Creating account: {account_data['code']}")
|
||||
# create_account(entity, coa, account_data)
|
||||
|
||||
|
||||
# def create_coa_accounts1(pk):
|
||||
|
||||
@ -494,8 +494,13 @@ def po_item_formset_table(context, po_model, itemtxs_formset, user):
|
||||
@register.inclusion_tag("bill/tags/bill_item_formset.html", takes_context=True)
|
||||
def bill_item_formset_table(context, item_formset):
|
||||
bill = BillModel.objects.get(uuid=context["view"].kwargs["bill_pk"])
|
||||
for item in item_formset:
|
||||
for form in item_formset.forms:
|
||||
form.fields["item_model"].queryset = form.fields["item_model"].queryset.exclude(
|
||||
item_role="product"
|
||||
)
|
||||
for item in item_formset:
|
||||
if item:
|
||||
print(item.fields["item_model"])
|
||||
item.initial["quantity"] = item.instance.po_quantity
|
||||
item.initial["unit_cost"] = item.instance.po_unit_cost
|
||||
# print(item.instance.po_quantity)
|
||||
|
||||
@ -15,6 +15,7 @@ from django.utils import timezone
|
||||
from django.db import transaction
|
||||
from django_ledger.io import roles
|
||||
from django.contrib import messages
|
||||
from django.db import IntegrityError
|
||||
from django.shortcuts import redirect
|
||||
from django_q.tasks import async_task
|
||||
from django.core.mail import send_mail
|
||||
@ -2386,7 +2387,19 @@ def get_accounts_data():
|
||||
|
||||
|
||||
def create_account(entity, coa, account_data):
|
||||
"""
|
||||
Create account with proper validation and error handling
|
||||
"""
|
||||
try:
|
||||
# Check if account already exists
|
||||
existing_account = entity.get_all_accounts().filter(
|
||||
code=account_data["code"]
|
||||
).first()
|
||||
|
||||
if existing_account:
|
||||
logger.info(f"Account already exists: {account_data['code']}")
|
||||
return True
|
||||
|
||||
account = entity.create_account(
|
||||
coa_model=coa,
|
||||
code=account_data["code"],
|
||||
@ -2395,11 +2408,37 @@ def create_account(entity, coa, account_data):
|
||||
balance_type=_(account_data["balance_type"]),
|
||||
active=True,
|
||||
)
|
||||
account.role_default = account_data["default"]
|
||||
account.save()
|
||||
logger.info(f"Created default account: {account}")
|
||||
|
||||
if account:
|
||||
account.role_default = account_data["default"]
|
||||
account.save()
|
||||
logger.info(f"Successfully created account: {account_data['code']}")
|
||||
return True
|
||||
|
||||
except IntegrityError:
|
||||
logger.warning(f"Account {account_data['code']} already exists (IntegrityError)")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating default account: {account_data['code']}, {e}")
|
||||
logger.error(f"Error creating account {account_data['code']}: {e}")
|
||||
return False
|
||||
|
||||
return False
|
||||
# def create_account(entity, coa, account_data):
|
||||
# try:
|
||||
# account = entity.create_account(
|
||||
# coa_model=coa,
|
||||
# code=account_data["code"],
|
||||
# name=account_data["name"],
|
||||
# role=account_data["role"],
|
||||
# balance_type=_(account_data["balance_type"]),
|
||||
# active=True,
|
||||
# )
|
||||
# logger.info(f"Created account: {account}")
|
||||
# account.role_default = account_data["default"]
|
||||
# account.save()
|
||||
# logger.info(f"Created default account: {account}")
|
||||
# except Exception as e:
|
||||
# logger.error(f"Error creating default account: {account_data['code']}, {e}")
|
||||
|
||||
|
||||
def get_or_generate_car_image(car):
|
||||
@ -2464,9 +2503,9 @@ def force_regenerate_car_image(car):
|
||||
class CarImageAPIClient:
|
||||
"""Simple client to handle authenticated requests to the car image API"""
|
||||
|
||||
BASE_URL = "http://10.10.1.111:8888"
|
||||
USERNAME = "faheed"
|
||||
PASSWORD = "Tenhal@123"
|
||||
BASE_URL = settings.TENHAL_IMAGE_GENERATOR_URL
|
||||
USERNAME = settings.TENHAL_IMAGE_GENERATOR_USERNAME
|
||||
PASSWORD = settings.TENHAL_IMAGE_GENERATOR_PASSWORD
|
||||
|
||||
def __init__(self):
|
||||
self.session = None
|
||||
@ -2529,7 +2568,7 @@ class CarImageAPIClient:
|
||||
"make": payload["make"],
|
||||
"model": payload["model"],
|
||||
"exterior_color": payload["color"],
|
||||
"angle": "3/4 rear",
|
||||
"angle": "front three-quarter",
|
||||
"reference_image": "",
|
||||
}
|
||||
|
||||
|
||||
@ -1,132 +1,156 @@
|
||||
annotated-types
|
||||
anyio
|
||||
arrow
|
||||
asgiref
|
||||
attrs
|
||||
Babel
|
||||
beautifulsoup4
|
||||
blessed
|
||||
cattrs
|
||||
certifi
|
||||
cffi
|
||||
charset-normalizer
|
||||
click
|
||||
colorama
|
||||
crispy-bootstrap5
|
||||
cryptography
|
||||
cssbeautifier
|
||||
defusedxml
|
||||
diff-match-patch
|
||||
distro
|
||||
Django
|
||||
django-allauth
|
||||
django-appconf
|
||||
django-appointment
|
||||
django-background-tasks
|
||||
django-bootstrap5
|
||||
django-ckeditor
|
||||
django-cors-headers
|
||||
django-countries
|
||||
django-crispy-forms
|
||||
django-debug-toolbar
|
||||
django-easy-audit
|
||||
django-extensions
|
||||
django-filter
|
||||
django-imagekit
|
||||
django-import-export
|
||||
django-js-asset
|
||||
django-ledger
|
||||
django-manager-utils
|
||||
django-next-url-mixin
|
||||
django-ordered-model
|
||||
django-phonenumber-field
|
||||
django-picklefield
|
||||
django-plans
|
||||
django-q2
|
||||
django-query-builder
|
||||
django-schema-graph
|
||||
django-sequences
|
||||
django-tables2
|
||||
django-treebeard
|
||||
django-widget-tweaks
|
||||
djangorestframework
|
||||
djhtml
|
||||
djlint
|
||||
docopt
|
||||
EditorConfig
|
||||
Faker
|
||||
fleming
|
||||
fonttools
|
||||
fpdf
|
||||
fpdf2
|
||||
greenlet
|
||||
h11
|
||||
httpcore
|
||||
httpx
|
||||
icalendar
|
||||
idna
|
||||
jiter
|
||||
jsbeautifier
|
||||
json5
|
||||
jsonpatch
|
||||
jsonpointer
|
||||
jwt
|
||||
langchain
|
||||
langchain-core
|
||||
langchain-ollama
|
||||
langchain-text-splitters
|
||||
langsmith
|
||||
luhnchecker
|
||||
Markdown
|
||||
markdown-it-py
|
||||
mdurl
|
||||
num2words
|
||||
numpy
|
||||
ofxtools
|
||||
ollama
|
||||
openai
|
||||
opencv-python
|
||||
orjson
|
||||
packaging
|
||||
pandas
|
||||
pathspec
|
||||
phonenumbers
|
||||
pilkit
|
||||
pillow
|
||||
psycopg2-binary
|
||||
pycparser
|
||||
pydantic
|
||||
pydantic_core
|
||||
Pygments
|
||||
python-dateutil
|
||||
python-slugify
|
||||
python-stdnum
|
||||
pytz
|
||||
pyvin
|
||||
PyYAML
|
||||
pyzbar
|
||||
redis
|
||||
regex
|
||||
requests
|
||||
requests-toolbelt
|
||||
rich
|
||||
ruff
|
||||
setuptools
|
||||
six
|
||||
sniffio
|
||||
soupsieve
|
||||
SQLAlchemy
|
||||
sqlparse
|
||||
suds
|
||||
swapper
|
||||
tablib
|
||||
tenacity
|
||||
text-unidecode
|
||||
tqdm
|
||||
types-python-dateutil
|
||||
typing-inspection
|
||||
typing_extensions
|
||||
tzdata
|
||||
urllib3
|
||||
wcwidth
|
||||
zstandard
|
||||
annotated-types==0.7.0
|
||||
anyio==4.9.0
|
||||
arrow==1.3.0
|
||||
asgiref==3.9.1
|
||||
attrs==25.3.0
|
||||
autobahn==24.4.2
|
||||
Automat==25.4.16
|
||||
Babel==2.15.0
|
||||
beautifulsoup4==4.13.4
|
||||
blessed==1.21.0
|
||||
cattrs==25.1.1
|
||||
certifi==2025.7.9
|
||||
cffi==1.17.1
|
||||
channels==4.2.2
|
||||
charset-normalizer==3.4.2
|
||||
click==8.2.1
|
||||
colorama==0.4.6
|
||||
constantly==23.10.4
|
||||
crispy-bootstrap5==2025.6
|
||||
cryptography==45.0.5
|
||||
cssbeautifier==1.15.4
|
||||
daphne==4.2.1
|
||||
defusedxml==0.7.1
|
||||
diff-match-patch==20241021
|
||||
distro==1.9.0
|
||||
Django==5.2.4
|
||||
django-allauth==65.10.0
|
||||
django-appconf==1.1.0
|
||||
django-appointment==3.8.0
|
||||
django-background-tasks==1.2.8
|
||||
django-bootstrap5==25.1
|
||||
django-ckeditor==6.7.3
|
||||
django-cors-headers==4.7.0
|
||||
django-countries==7.6.1
|
||||
django-crispy-forms==2.4
|
||||
django-debug-toolbar==5.2.0
|
||||
django-easy-audit==1.3.7
|
||||
django-extensions==4.1
|
||||
django-filter==25.1
|
||||
django-imagekit==5.0.0
|
||||
django-import-export==4.3.8
|
||||
django-js-asset==3.1.2
|
||||
django-ledger==0.7.6.1
|
||||
django-manager-utils==3.1.5
|
||||
django-next-url-mixin==0.4.0
|
||||
django-ordered-model==3.7.4
|
||||
django-phonenumber-field==8.0.0
|
||||
django-picklefield==3.3
|
||||
django-plans==2.0.0
|
||||
django-prometheus==2.4.1
|
||||
django-q2==1.8.0
|
||||
django-query-builder==3.2.0
|
||||
django-schema-graph==3.1.0
|
||||
django-sequences==3.0
|
||||
django-tables2==2.7.5
|
||||
django-treebeard==4.7.1
|
||||
django-widget-tweaks==1.5.0
|
||||
djangorestframework==3.16.0
|
||||
djhtml==3.0.8
|
||||
djlint==1.36.4
|
||||
docopt==0.6.2
|
||||
EditorConfig==0.17.1
|
||||
Faker==37.4.0
|
||||
fleming==0.7.0
|
||||
fonttools==4.58.5
|
||||
# fpdf==1.7.2
|
||||
fpdf2==2.8.3
|
||||
greenlet==3.2.3
|
||||
gunicorn==23.0.0
|
||||
h11==0.16.0
|
||||
h2==4.2.0
|
||||
hpack==4.1.0
|
||||
httpcore==1.0.9
|
||||
httpx==0.28.1
|
||||
hyperframe==6.1.0
|
||||
hyperlink==21.0.0
|
||||
icalendar==6.3.1
|
||||
idna==3.10
|
||||
incremental==24.7.2
|
||||
jiter==0.10.0
|
||||
jsbeautifier==1.15.4
|
||||
json5==0.12.0
|
||||
jsonpatch==1.33
|
||||
jsonpointer==3.0.0
|
||||
jwt==1.4.0
|
||||
langchain==0.3.26
|
||||
langchain-core==0.3.68
|
||||
langchain-ollama==0.3.4
|
||||
langchain-text-splitters==0.3.8
|
||||
langsmith==0.4.4
|
||||
luhnchecker==0.0.12
|
||||
Markdown==3.8.2
|
||||
markdown-it-py==3.0.0
|
||||
mdurl==0.1.2
|
||||
num2words==0.5.14
|
||||
numpy==2.3.1
|
||||
ofxtools==0.9.5
|
||||
ollama==0.5.1
|
||||
openai==1.93.3
|
||||
opencv-python==4.11.0.86
|
||||
orjson==3.10.18
|
||||
packaging==24.2
|
||||
pandas==2.3.1
|
||||
pathspec==0.12.1
|
||||
phonenumbers==8.13.42
|
||||
pilkit==3.0
|
||||
pillow==10.4.0
|
||||
priority==1.3.0
|
||||
prometheus_client==0.22.1
|
||||
psycopg2-binary==2.9.10
|
||||
pyasn1==0.6.1
|
||||
pyasn1_modules==0.4.2
|
||||
pycparser==2.22
|
||||
pydantic==2.11.7
|
||||
pydantic_core==2.33.2
|
||||
Pygments==2.19.2
|
||||
pyOpenSSL==25.1.0
|
||||
python-dateutil==2.9.0.post0
|
||||
python-dotenv==1.1.1
|
||||
python-slugify==8.0.4
|
||||
python-stdnum==2.1
|
||||
pytz==2025.2
|
||||
pyvin==0.0.2
|
||||
PyYAML==6.0.2
|
||||
pyzbar==0.1.9
|
||||
redis==6.2.0
|
||||
regex==2024.11.6
|
||||
requests==2.32.4
|
||||
requests-toolbelt==1.0.0
|
||||
rich==14.0.0
|
||||
ruff==0.12.2
|
||||
service-identity==24.2.0
|
||||
setuptools==80.9.0
|
||||
six==1.17.0
|
||||
sniffio==1.3.1
|
||||
soupsieve==2.7
|
||||
SQLAlchemy==2.0.41
|
||||
sqlparse==0.5.3
|
||||
suds==1.2.0
|
||||
swapper==1.3.0
|
||||
tablib==3.8.0
|
||||
tenacity==9.1.2
|
||||
text-unidecode==1.3
|
||||
tqdm==4.67.1
|
||||
Twisted==25.5.0
|
||||
txaio==25.6.1
|
||||
types-python-dateutil==2.9.0.20250708
|
||||
typing-inspection==0.4.1
|
||||
typing_extensions==4.14.1
|
||||
tzdata==2025.2
|
||||
urllib3==2.5.0
|
||||
uvicorn==0.35.0
|
||||
uvicorn-worker==0.3.0
|
||||
wcwidth==0.2.13
|
||||
zope.interface==7.2
|
||||
zstandard==0.23.0
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 493 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 470 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 418 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 450 KiB |
@ -2,19 +2,9 @@
|
||||
{% load static %}
|
||||
{% load django_ledger %}
|
||||
{% load widget_tweaks %}
|
||||
{% if bill.get_itemtxs_data.1.total_amount__sum > 0 %}
|
||||
<form id="bill-update-form"
|
||||
action="{% url 'bill-update-items' dealer_slug=dealer_slug entity_slug=entity_slug bill_pk=bill_pk %}"
|
||||
method="post">
|
||||
{% else %}
|
||||
<form id="bill-update-form"
|
||||
hx-trigger="load delay:300ms"
|
||||
hx-swap="outerHTML"
|
||||
hx-target="#bill-update-form"
|
||||
hx-select="#bill-update-form"
|
||||
hx-post="{% url 'bill-update-items' dealer_slug=dealer_slug entity_slug=entity_slug bill_pk=bill_pk %}"
|
||||
method="post">
|
||||
{% endif %}
|
||||
<div class="container-fluid py-4">
|
||||
<!-- Page Header -->
|
||||
<div class="row mb-4">
|
||||
@ -125,13 +115,13 @@
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-start gap-2">
|
||||
{% if not item_formset.has_po %}
|
||||
{% comment %} {% if not item_formset.has_po %}
|
||||
<a href="{% url 'django_ledger:product-create' entity_slug=entity_slug %}"
|
||||
class="btn btn-phoenix-primary">
|
||||
<i class="fas fa-plus me-1"></i>
|
||||
{% trans 'New Item' %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %} {% endcomment %}
|
||||
<button type="submit" class="btn btn-phoenix-primary">
|
||||
<i class="fas fa-save me-1"></i>
|
||||
{% trans 'Save Changes' %}
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
hx-swap="outerHTML"
|
||||
hx-select-oob="#toast-container"
|
||||
hx-indicator="#spinner">
|
||||
|
||||
|
||||
<li class="nav-item">
|
||||
{% comment %} <p class="navbar-vertical-label text-primary fs-8 text-truncate">{{request.dealer|default:"Apps"}}</p>
|
||||
<hr class="navbar-vertical-line"> {% endcomment %}
|
||||
@ -440,7 +440,7 @@
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
</div> {% endcomment %}
|
||||
{% endif %}
|
||||
@ -450,10 +450,10 @@
|
||||
<a class="nav-link ps-2" href="{% if request.is_dealer%}{% url 'ticket_list' request.dealer.slug %} {% else %}#{%endif%}">
|
||||
<div class="d-flex align-items-center">
|
||||
{% if user.is_authenticated%}
|
||||
|
||||
|
||||
<span class="nav-link-icon"><span class="fa-solid fa-gear me-1 fs-7"></span></span>
|
||||
<span class="nav-link-text">{{ request.dealer.user.username }}</span>
|
||||
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@ -50,15 +50,15 @@
|
||||
{% trans 'Filters' %} <i class="fas fa-sliders-h ms-2"></i>
|
||||
</h2>
|
||||
<form method="GET" class="row g-3 align-items-end">
|
||||
<div class="col-md-3">
|
||||
<div class="col-md-4">
|
||||
<label for="start_date" class="form-label">{% trans 'Start Date' %}</label>
|
||||
<input type="date" class="form-control" id="start_date" name="start_date" value="{{ start_date|default_if_none:'' }}">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="col-md-4">
|
||||
<label for="end_date" class="form-label">{% trans 'End Date' %}</label>
|
||||
<input type="date" class="form-control" id="end_date" name="end_date" value="{{ end_date|default_if_none:'' }}">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="col-md-4">
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="fas fa-filter me-2"></i>{% trans 'Filter' %}
|
||||
</button>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user