adding arabic translation
This commit is contained in:
parent
bdafaca493
commit
b0192498f4
BIN
accounting/__pycache__/forms.cpython-311.pyc
Normal file
BIN
accounting/__pycache__/forms.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
26
accounting/forms.py
Normal file
26
accounting/forms.py
Normal file
@ -0,0 +1,26 @@
|
||||
from django import forms
|
||||
from .models import Account, JournalEntry, JournalItem
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
class AccountForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Account
|
||||
fields = ['code', 'name_en', 'name_ar', 'account_type', 'description', 'is_active']
|
||||
widgets = {
|
||||
'code': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'name_en': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'name_ar': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'account_type': forms.Select(attrs={'class': 'form-select'}),
|
||||
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
||||
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
||||
}
|
||||
|
||||
class JournalEntryForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = JournalEntry
|
||||
fields = ['date', 'description', 'reference']
|
||||
widgets = {
|
||||
'date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
||||
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}),
|
||||
'reference': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
}
|
||||
67
accounting/templates/accounting/account_form.html
Normal file
67
accounting/templates/accounting/account_form.html
Normal file
@ -0,0 +1,67 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb mb-1">
|
||||
<li class="breadcrumb-item"><a href="{% url 'accounting_dashboard' %}">{% trans "Accounting" %}</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'chart_of_accounts' %}">{% trans "Chart of Accounts" %}</a></li>
|
||||
<li class="breadcrumb-item active">{% if account %}{% trans "Edit Account" %}{% else %}{% trans "Add Account" %}{% endif %}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<h2 class="mb-0">{% if account %}{% trans "Edit Account" %}{% else %}{% trans "Add Account" %}{% endif %}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body p-4">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">{% trans "Account Code" %}</label>
|
||||
{{ form.code }}
|
||||
{% if form.code.errors %}<div class="text-danger small">{{ form.code.errors }}</div>{% endif %}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<label class="form-label">{% trans "Account Type" %}</label>
|
||||
{{ form.account_type }}
|
||||
{% if form.account_type.errors %}<div class="text-danger small">{{ form.account_type.errors }}</div>{% endif %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">{% trans "Name (English)" %}</label>
|
||||
{{ form.name_en }}
|
||||
{% if form.name_en.errors %}<div class="text-danger small">{{ form.name_en.errors }}</div>{% endif %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">{% trans "Name (Arabic)" %}</label>
|
||||
{{ form.name_ar }}
|
||||
{% if form.name_ar.errors %}<div class="text-danger small">{{ form.name_ar.errors }}</div>{% endif %}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">{% trans "Description" %}</label>
|
||||
{{ form.description }}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
{{ form.is_active }}
|
||||
<label class="form-check-label">{% trans "Is Active" %}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 pt-3 border-top d-flex justify-content-end gap-2">
|
||||
<a href="{% url 'chart_of_accounts' %}" class="btn btn-light">{% trans "Cancel" %}</a>
|
||||
<button type="submit" class="btn btn-primary px-4">{% trans "Save Account" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -13,6 +13,9 @@
|
||||
</nav>
|
||||
<h2 class="mb-0">{% trans "Chart of Accounts" %}</h2>
|
||||
</div>
|
||||
<a href="{% url 'account_create' %}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-lg"></i> {% trans "Add Account" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 shadow-sm">
|
||||
@ -43,9 +46,14 @@
|
||||
{{ account.balance|floatformat:global_settings.decimal_places }} {{ global_settings.currency_symbol }}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'account_ledger' account.id %}" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-list-columns"></i> {% trans "Ledger" %}
|
||||
</a>
|
||||
<div class="btn-group">
|
||||
<a href="{% url 'account_ledger' account.id %}" class="btn btn-sm btn-outline-primary" title="{% trans 'Ledger' %}">
|
||||
<i class="bi bi-list-columns"></i>
|
||||
</a>
|
||||
<a href="{% url 'account_edit' account.id %}" class="btn btn-sm btn-outline-secondary" title="{% trans 'Edit' %}">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@ -54,4 +62,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@ -13,6 +13,9 @@
|
||||
</nav>
|
||||
<h2 class="mb-0">{% trans "Journal Entries" %}</h2>
|
||||
</div>
|
||||
<a href="{% url 'manual_journal_entry' %}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-lg"></i> {% trans "New Manual Entry" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% for entry in entries %}
|
||||
@ -63,4 +66,4 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
202
accounting/templates/accounting/journal_entry_form.html
Normal file
202
accounting/templates/accounting/journal_entry_form.html
Normal file
@ -0,0 +1,202 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb mb-1">
|
||||
<li class="breadcrumb-item"><a href="{% url 'accounting_dashboard' %}">{% trans "Accounting" %}</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'journal_entries' %}">{% trans "Journal Entries" %}</a></li>
|
||||
<li class="breadcrumb-item active">{% trans "New Manual Entry" %}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<h2 class="mb-0">{% trans "New Manual Journal Entry" %}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post" id="journal-form">
|
||||
{% csrf_token %}
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-body p-4">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">{% trans "Date" %}</label>
|
||||
{{ form.date }}
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">{% trans "Reference" %}</label>
|
||||
{{ form.reference }}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">{% trans "Description" %}</label>
|
||||
{{ form.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0" id="items-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th style="width: 40%;">{% trans "Account" %}</th>
|
||||
<th style="width: 20%;">{% trans "Type" %}</th>
|
||||
<th style="width: 30%;">{% trans "Amount" %}</th>
|
||||
<th style="width: 10%;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="item-row">
|
||||
<td>
|
||||
<select name="account[]" class="form-select select2-account" required>
|
||||
<option value="">{% trans "Select Account" %}</option>
|
||||
{% for acc in accounts %}
|
||||
<option value="{{ acc.id }}">{{ acc.code }} - {% if LANGUAGE_CODE == 'ar' %}{{ acc.name_ar }}{% else %}{{ acc.name_en }}{% endif %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select name="type[]" class="form-select item-type" required>
|
||||
<option value="debit">{% trans "Debit" %}</option>
|
||||
<option value="credit">{% trans "Credit" %}</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<div class="input-group">
|
||||
<input type="number" name="amount[]" class="form-control item-amount" step="0.001" min="0" required>
|
||||
<span class="input-group-text">{{ global_settings.currency_symbol }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<button type="button" class="btn btn-outline-danger btn-sm remove-row"><i class="bi bi-trash"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="item-row">
|
||||
<td>
|
||||
<select name="account[]" class="form-select select2-account" required>
|
||||
<option value="">{% trans "Select Account" %}</option>
|
||||
{% for acc in accounts %}
|
||||
<option value="{{ acc.id }}">{{ acc.code }} - {% if LANGUAGE_CODE == 'ar' %}{{ acc.name_ar }}{% else %}{{ acc.name_en }}{% endif %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select name="type[]" class="form-select item-type" required>
|
||||
<option value="debit">{% trans "Debit" %}</option>
|
||||
<option value="credit" selected>{% trans "Credit" %}</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<div class="input-group">
|
||||
<input type="number" name="amount[]" class="form-control item-amount" step="0.001" min="0" required>
|
||||
<span class="input-group-text">{{ global_settings.currency_symbol }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<button type="button" class="btn btn-outline-danger btn-sm remove-row"><i class="bi bi-trash"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot class="bg-light">
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm" id="add-row">
|
||||
<i class="bi bi-plus-lg"></i> {% trans "Add Line" %}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="fw-bold">
|
||||
<td class="text-end">{% trans "Totals" %}:</td>
|
||||
<td class="text-end">{% trans "Debit" %}: <span id="total-debit">0.000</span></td>
|
||||
<td class="text-end">{% trans "Credit" %}: <span id="total-credit">0.000</span></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4" class="text-center py-2" id="balance-message">
|
||||
<span class="badge bg-danger">{% trans "Out of Balance" %}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-footer bg-white p-4 border-top d-flex justify-content-end gap-2">
|
||||
<a href="{% url 'journal_entries' %}" class="btn btn-light">{% trans "Cancel" %}</a>
|
||||
<button type="submit" class="btn btn-primary px-4" id="submit-btn" disabled>{% trans "Create Entry" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const table = document.getElementById('items-table').getElementsByTagName('tbody')[0];
|
||||
const addBtn = document.getElementById('add-row');
|
||||
const totalDebitSpan = document.getElementById('total-debit');
|
||||
const totalCreditSpan = document.getElementById('total-credit');
|
||||
const balanceMessage = document.getElementById('balance-message');
|
||||
const submitBtn = document.getElementById('submit-btn');
|
||||
|
||||
function updateTotals() {
|
||||
let totalDebit = 0;
|
||||
let totalCredit = 0;
|
||||
|
||||
document.querySelectorAll('.item-row').forEach(row => {
|
||||
const type = row.querySelector('.item-type').value;
|
||||
const amount = parseFloat(row.querySelector('.item-amount').value) || 0;
|
||||
|
||||
if (type === 'debit') totalDebit += amount;
|
||||
else totalCredit += amount;
|
||||
});
|
||||
|
||||
totalDebitSpan.textContent = totalDebit.toFixed(3);
|
||||
totalCreditSpan.textContent = totalCredit.toFixed(3);
|
||||
|
||||
const balanced = totalDebit > 0 && Math.abs(totalDebit - totalCredit) < 0.001;
|
||||
|
||||
if (balanced) {
|
||||
balanceMessage.innerHTML = '<span class="badge bg-success">{% trans "Balanced" %}</span>';
|
||||
submitBtn.disabled = false;
|
||||
} else {
|
||||
balanceMessage.innerHTML = '<span class="badge bg-danger">{% trans "Out of Balance" %}</span>';
|
||||
submitBtn.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
addBtn.addEventListener('click', function() {
|
||||
const firstRow = document.querySelector('.item-row');
|
||||
const newRow = firstRow.cloneNode(true);
|
||||
newRow.querySelector('.item-amount').value = '';
|
||||
table.appendChild(newRow);
|
||||
|
||||
newRow.querySelector('.remove-row').addEventListener('click', function() {
|
||||
if (document.querySelectorAll('.item-row').length > 2) {
|
||||
newRow.remove();
|
||||
updateTotals();
|
||||
}
|
||||
});
|
||||
|
||||
newRow.querySelector('.item-type').addEventListener('change', updateTotals);
|
||||
newRow.querySelector('.item-amount').addEventListener('input', updateTotals);
|
||||
});
|
||||
|
||||
document.querySelectorAll('.remove-row').forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
if (document.querySelectorAll('.item-row').length > 2) {
|
||||
btn.closest('.item-row').remove();
|
||||
updateTotals();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.item-type').forEach(el => el.addEventListener('change', updateTotals));
|
||||
document.querySelectorAll('.item-amount').forEach(el => el.addEventListener('input', updateTotals));
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@ -4,9 +4,12 @@ from . import views
|
||||
urlpatterns = [
|
||||
path('', views.accounting_dashboard, name='accounting_dashboard'),
|
||||
path('chart-of-accounts/', views.chart_of_accounts, name='chart_of_accounts'),
|
||||
path('chart-of-accounts/add/', views.account_create_update, name='account_create'),
|
||||
path('chart-of-accounts/edit/<int:pk>/', views.account_create_update, name='account_edit'),
|
||||
path('journal-entries/', views.journal_entries, name='journal_entries'),
|
||||
path('journal-entries/manual/', views.manual_journal_entry, name='manual_journal_entry'),
|
||||
path('ledger/<int:account_id>/', views.account_ledger, name='account_ledger'),
|
||||
path('trial-balance/', views.trial_balance, name='trial_balance'),
|
||||
path('balance-sheet/', views.balance_sheet, name='balance_sheet'),
|
||||
path('profit-loss/', views.profit_loss, name='profit_loss'),
|
||||
]
|
||||
]
|
||||
@ -1,9 +1,14 @@
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.utils.translation import gettext as _
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import messages
|
||||
from .models import Account, JournalEntry, JournalItem
|
||||
from .forms import AccountForm, JournalEntryForm
|
||||
from django.db.models import Sum, Q
|
||||
from django.utils import timezone
|
||||
from datetime import datetime
|
||||
from django.db import transaction
|
||||
import json
|
||||
|
||||
@login_required
|
||||
def accounting_dashboard(request):
|
||||
@ -44,11 +49,84 @@ def chart_of_accounts(request):
|
||||
accounts = Account.objects.all().order_by('code')
|
||||
return render(request, 'accounting/chart_of_accounts.html', {'accounts': accounts})
|
||||
|
||||
@login_required
|
||||
def account_create_update(request, pk=None):
|
||||
if pk:
|
||||
account = get_object_or_404(Account, pk=pk)
|
||||
else:
|
||||
account = None
|
||||
|
||||
if request.method == 'POST':
|
||||
form = AccountForm(request.POST, instance=account)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request, _("Account saved successfully."))
|
||||
return redirect('chart_of_accounts')
|
||||
else:
|
||||
form = AccountForm(instance=account)
|
||||
|
||||
return render(request, 'accounting/account_form.html', {'form': form, 'account': account})
|
||||
|
||||
@login_required
|
||||
def journal_entries(request):
|
||||
entries = JournalEntry.objects.all().order_by('-date', '-id')
|
||||
return render(request, 'accounting/journal_entries.html', {'entries': entries})
|
||||
|
||||
@login_required
|
||||
def manual_journal_entry(request):
|
||||
accounts = Account.objects.filter(is_active=True).order_by('code')
|
||||
|
||||
if request.method == 'POST':
|
||||
form = JournalEntryForm(request.POST)
|
||||
# Manual journal entry requires at least two items and they must balance
|
||||
account_ids = request.POST.getlist('account[]')
|
||||
types = request.POST.getlist('type[]')
|
||||
amounts = request.POST.getlist('amount[]')
|
||||
|
||||
if form.is_valid():
|
||||
try:
|
||||
with transaction.atomic():
|
||||
entry = form.save()
|
||||
total_debit = 0
|
||||
total_credit = 0
|
||||
|
||||
for i in range(len(account_ids)):
|
||||
acc_id = account_ids[i]
|
||||
item_type = types[i]
|
||||
amount = float(amounts[i])
|
||||
|
||||
if amount <= 0: continue
|
||||
|
||||
JournalItem.objects.create(
|
||||
entry=entry,
|
||||
account_id=acc_id,
|
||||
type=item_type,
|
||||
amount=amount
|
||||
)
|
||||
|
||||
if item_type == 'debit':
|
||||
total_debit += amount
|
||||
else:
|
||||
total_credit += amount
|
||||
|
||||
if round(total_debit, 3) != round(total_credit, 3):
|
||||
raise Exception(f"Journal entry does not balance. Total Debit: {total_debit}, Total Credit: {total_credit}")
|
||||
|
||||
if total_debit == 0:
|
||||
raise Exception("Journal entry must have at least one debit and one credit.")
|
||||
|
||||
messages.success(request, _("Manual journal entry created successfully."))
|
||||
return redirect('journal_entries')
|
||||
except Exception as e:
|
||||
messages.error(request, str(e))
|
||||
else:
|
||||
form = JournalEntryForm()
|
||||
|
||||
return render(request, 'accounting/journal_entry_form.html', {
|
||||
'form': form,
|
||||
'accounts': accounts
|
||||
})
|
||||
|
||||
@login_required
|
||||
def account_ledger(request, account_id):
|
||||
account = get_object_or_404(Account, id=account_id)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -24,7 +24,8 @@ def global_settings(request):
|
||||
settings = SystemSetting.objects.create()
|
||||
return {
|
||||
'site_settings': settings,
|
||||
'global_settings': settings,
|
||||
'decimal_places': settings.decimal_places if settings else 3
|
||||
}
|
||||
except:
|
||||
return {'decimal_places': 3}
|
||||
return {'decimal_places': 3}
|
||||
|
||||
114
core/views.py
114
core/views.py
@ -1,3 +1,4 @@
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from .utils import number_to_words_en
|
||||
from django.core.paginator import Paginator
|
||||
@ -277,7 +278,7 @@ def add_purchase_payment(request, pk):
|
||||
created_by=request.user
|
||||
)
|
||||
purchase.update_balance()
|
||||
messages.success(request, "Payment added successfully!")
|
||||
messages.success(request, _("Payment added successfully!"))
|
||||
return redirect('purchases')
|
||||
|
||||
@login_required
|
||||
@ -288,7 +289,7 @@ def delete_purchase(request, pk):
|
||||
item.product.save()
|
||||
|
||||
purchase.delete()
|
||||
messages.success(request, "Purchase deleted successfully!")
|
||||
messages.success(request, _("Purchase deleted successfully!"))
|
||||
return redirect('purchases')
|
||||
|
||||
# --- Sale Views ---
|
||||
@ -552,7 +553,7 @@ def add_sale_payment(request, pk):
|
||||
created_by=request.user
|
||||
)
|
||||
sale.update_balance()
|
||||
messages.success(request, "Payment added successfully!")
|
||||
messages.success(request, _("Payment added successfully!"))
|
||||
return redirect('invoices')
|
||||
|
||||
@login_required
|
||||
@ -562,7 +563,7 @@ def delete_sale(request, pk):
|
||||
item.product.stock_quantity += item.quantity
|
||||
item.product.save()
|
||||
sale.delete()
|
||||
messages.success(request, "Sale deleted successfully!")
|
||||
messages.success(request, _("Sale deleted successfully!"))
|
||||
return redirect('invoices')
|
||||
|
||||
# --- Quotation Views ---
|
||||
@ -641,7 +642,7 @@ def create_quotation_api(request):
|
||||
def convert_quotation_to_invoice(request, pk):
|
||||
quotation = get_object_or_404(Quotation, pk=pk)
|
||||
if quotation.status == 'converted':
|
||||
messages.warning(request, "This quotation has already been converted to an invoice.")
|
||||
messages.warning(request, _("This quotation has already been converted to an invoice."))
|
||||
return redirect('invoices')
|
||||
|
||||
# Create Sale from Quotation
|
||||
@ -674,14 +675,14 @@ def convert_quotation_to_invoice(request, pk):
|
||||
quotation.status = 'converted'
|
||||
quotation.save()
|
||||
|
||||
messages.success(request, "Quotation converted to Invoice successfully!")
|
||||
messages.success(request, _("Quotation converted to Invoice successfully!"))
|
||||
return redirect('invoice_detail', pk=sale.pk)
|
||||
|
||||
@login_required
|
||||
def delete_quotation(request, pk):
|
||||
quotation = get_object_or_404(Quotation, pk=pk)
|
||||
quotation.delete()
|
||||
messages.success(request, "Quotation deleted successfully!")
|
||||
messages.success(request, _("Quotation deleted successfully!"))
|
||||
return redirect('quotations')
|
||||
|
||||
# --- Sale Return Views ---
|
||||
@ -770,7 +771,7 @@ def delete_sale_return(request, pk):
|
||||
item.product.stock_quantity -= item.quantity
|
||||
item.product.save()
|
||||
sale_return.delete()
|
||||
messages.success(request, "Sale return deleted successfully!")
|
||||
messages.success(request, _("Sale return deleted successfully!"))
|
||||
return redirect('sales_returns')
|
||||
|
||||
|
||||
@ -860,7 +861,7 @@ def delete_purchase_return(request, pk):
|
||||
item.product.stock_quantity += item.quantity
|
||||
item.product.save()
|
||||
purchase_return.delete()
|
||||
messages.success(request, "Purchase return deleted successfully!")
|
||||
messages.success(request, _("Purchase return deleted successfully!"))
|
||||
return redirect('purchase_returns')
|
||||
|
||||
# --- Other Management Views ---
|
||||
@ -917,7 +918,7 @@ def settings_view(request):
|
||||
settings.logo = request.FILES['logo']
|
||||
|
||||
settings.save()
|
||||
messages.success(request, "Settings updated successfully!")
|
||||
messages.success(request, _("Settings updated successfully!"))
|
||||
return redirect(reverse('settings') + '#profile')
|
||||
|
||||
payment_methods = PaymentMethod.objects.all()
|
||||
@ -936,7 +937,7 @@ def add_payment_method(request):
|
||||
name_ar = request.POST.get('name_ar')
|
||||
is_active = request.POST.get('is_active') == 'on'
|
||||
PaymentMethod.objects.create(name_en=name_en, name_ar=name_ar, is_active=is_active)
|
||||
messages.success(request, "Payment method added successfully!")
|
||||
messages.success(request, _("Payment method added successfully!"))
|
||||
return redirect(reverse('settings') + '#payments')
|
||||
|
||||
@login_required
|
||||
@ -947,14 +948,14 @@ def edit_payment_method(request, pk):
|
||||
pm.name_ar = request.POST.get('name_ar')
|
||||
pm.is_active = request.POST.get('is_active') == 'on'
|
||||
pm.save()
|
||||
messages.success(request, "Payment method updated successfully!")
|
||||
messages.success(request, _("Payment method updated successfully!"))
|
||||
return redirect(reverse('settings') + '#payments')
|
||||
|
||||
@login_required
|
||||
def delete_payment_method(request, pk):
|
||||
pm = get_object_or_404(PaymentMethod, pk=pk)
|
||||
pm.delete()
|
||||
messages.success(request, "Payment method deleted successfully!")
|
||||
messages.success(request, _("Payment method deleted successfully!"))
|
||||
return redirect(reverse('settings') + '#payments')
|
||||
|
||||
@login_required
|
||||
@ -965,7 +966,7 @@ def add_customer(request):
|
||||
email = request.POST.get('email')
|
||||
address = request.POST.get('address')
|
||||
Customer.objects.create(name=name, phone=phone, email=email, address=address)
|
||||
messages.success(request, "Customer added successfully!")
|
||||
messages.success(request, _("Customer added successfully!"))
|
||||
return redirect('customers')
|
||||
|
||||
@login_required
|
||||
@ -977,14 +978,14 @@ def edit_customer(request, pk):
|
||||
customer.email = request.POST.get('email')
|
||||
customer.address = request.POST.get('address')
|
||||
customer.save()
|
||||
messages.success(request, "Customer updated successfully!")
|
||||
messages.success(request, _("Customer updated successfully!"))
|
||||
return redirect('customers')
|
||||
|
||||
@login_required
|
||||
def delete_customer(request, pk):
|
||||
customer = get_object_or_404(Customer, pk=pk)
|
||||
customer.delete()
|
||||
messages.success(request, "Customer deleted successfully!")
|
||||
messages.success(request, _("Customer deleted successfully!"))
|
||||
return redirect('customers')
|
||||
|
||||
@login_required
|
||||
@ -994,7 +995,7 @@ def add_supplier(request):
|
||||
contact_person = request.POST.get('contact_person')
|
||||
phone = request.POST.get('phone')
|
||||
Supplier.objects.create(name=name, contact_person=contact_person, phone=phone)
|
||||
messages.success(request, "Supplier added successfully!")
|
||||
messages.success(request, _("Supplier added successfully!"))
|
||||
return redirect('suppliers')
|
||||
|
||||
@login_required
|
||||
@ -1005,14 +1006,14 @@ def edit_supplier(request, pk):
|
||||
supplier.contact_person = request.POST.get('contact_person')
|
||||
supplier.phone = request.POST.get('phone')
|
||||
supplier.save()
|
||||
messages.success(request, "Supplier updated successfully!")
|
||||
messages.success(request, _("Supplier updated successfully!"))
|
||||
return redirect('suppliers')
|
||||
|
||||
@login_required
|
||||
def delete_supplier(request, pk):
|
||||
supplier = get_object_or_404(Supplier, pk=pk)
|
||||
supplier.delete()
|
||||
messages.success(request, "Supplier deleted successfully!")
|
||||
messages.success(request, _("Supplier deleted successfully!"))
|
||||
return redirect('suppliers')
|
||||
|
||||
|
||||
@ -1071,7 +1072,7 @@ def add_product(request):
|
||||
product.image = request.FILES['image']
|
||||
product.save()
|
||||
|
||||
messages.success(request, "Product added successfully!")
|
||||
messages.success(request, _("Product added successfully!"))
|
||||
return redirect(reverse('inventory') + '#items')
|
||||
|
||||
@login_required
|
||||
@ -1100,7 +1101,7 @@ def edit_product(request, pk):
|
||||
product.image = request.FILES['image']
|
||||
|
||||
product.save()
|
||||
messages.success(request, "Product updated successfully!")
|
||||
messages.success(request, _("Product updated successfully!"))
|
||||
return redirect(reverse('inventory') + '#items')
|
||||
return redirect(reverse('inventory') + '#items')
|
||||
|
||||
@ -1108,7 +1109,7 @@ def edit_product(request, pk):
|
||||
def delete_product(request, pk):
|
||||
product = get_object_or_404(Product, pk=pk)
|
||||
product.delete()
|
||||
messages.success(request, "Product deleted successfully!")
|
||||
messages.success(request, _("Product deleted successfully!"))
|
||||
return redirect(reverse('inventory') + '#items')
|
||||
|
||||
@login_required
|
||||
@ -1118,7 +1119,7 @@ def add_category(request):
|
||||
name_ar = request.POST.get('name_ar')
|
||||
slug = slugify(name_en)
|
||||
Category.objects.create(name_en=name_en, name_ar=name_ar, slug=slug)
|
||||
messages.success(request, "Category added successfully!")
|
||||
messages.success(request, _("Category added successfully!"))
|
||||
return redirect(reverse('inventory') + '#categories-list')
|
||||
|
||||
@login_required
|
||||
@ -1129,14 +1130,14 @@ def edit_category(request, pk):
|
||||
category.name_ar = request.POST.get('name_ar')
|
||||
category.slug = slugify(category.name_en)
|
||||
category.save()
|
||||
messages.success(request, "Category updated successfully!")
|
||||
messages.success(request, _("Category updated successfully!"))
|
||||
return redirect(reverse('inventory') + '#categories-list')
|
||||
|
||||
@login_required
|
||||
def delete_category(request, pk):
|
||||
category = get_object_or_404(Category, pk=pk)
|
||||
category.delete()
|
||||
messages.success(request, "Category deleted successfully!")
|
||||
messages.success(request, _("Category deleted successfully!"))
|
||||
return redirect(reverse('inventory') + '#categories-list')
|
||||
|
||||
@login_required
|
||||
@ -1146,7 +1147,7 @@ def add_unit(request):
|
||||
name_ar = request.POST.get('name_ar')
|
||||
short_name = request.POST.get('short_name')
|
||||
Unit.objects.create(name_en=name_en, name_ar=name_ar, short_name=short_name)
|
||||
messages.success(request, "Unit added successfully!")
|
||||
messages.success(request, _("Unit added successfully!"))
|
||||
return redirect(reverse('inventory') + '#units-list')
|
||||
|
||||
@login_required
|
||||
@ -1157,14 +1158,14 @@ def edit_unit(request, pk):
|
||||
unit.name_ar = request.POST.get('name_ar')
|
||||
unit.short_name = request.POST.get('short_name')
|
||||
unit.save()
|
||||
messages.success(request, "Unit updated successfully!")
|
||||
messages.success(request, _("Unit updated successfully!"))
|
||||
return redirect(reverse('inventory') + '#units-list')
|
||||
|
||||
@login_required
|
||||
def delete_unit(request, pk):
|
||||
unit = get_object_or_404(Unit, pk=pk)
|
||||
unit.delete()
|
||||
messages.success(request, "Unit deleted successfully!")
|
||||
messages.success(request, _("Unit deleted successfully!"))
|
||||
return redirect(reverse('inventory') + '#units-list')
|
||||
|
||||
@login_required
|
||||
@ -1183,7 +1184,7 @@ def import_products(request):
|
||||
excel_file = request.FILES['excel_file']
|
||||
|
||||
if not excel_file.name.endswith('.xlsx'):
|
||||
messages.error(request, "Please upload a valid .xlsx file.")
|
||||
messages.error(request, _("Please upload a valid .xlsx file."))
|
||||
return redirect(reverse('inventory') + '#items')
|
||||
|
||||
try:
|
||||
@ -1326,7 +1327,7 @@ def add_supplier_ajax(request):
|
||||
@login_required
|
||||
def user_management(request):
|
||||
if not (request.user.is_superuser or request.user.groups.filter(name='admin').exists()):
|
||||
messages.error(request, "Access denied.")
|
||||
messages.error(request, _("Access denied."))
|
||||
return redirect('index')
|
||||
|
||||
users_qs = User.objects.all().prefetch_related('groups').order_by('username')
|
||||
@ -1346,7 +1347,7 @@ def user_management(request):
|
||||
group_ids = request.POST.getlist('groups')
|
||||
|
||||
if User.objects.filter(username=username).exists():
|
||||
messages.error(request, "Username already exists.")
|
||||
messages.error(request, _("Username already exists."))
|
||||
else:
|
||||
user = User.objects.create_user(username=username, email=email, password=password)
|
||||
if group_ids:
|
||||
@ -1375,7 +1376,7 @@ def user_management(request):
|
||||
name = request.POST.get('name')
|
||||
permission_ids = request.POST.getlist('permissions')
|
||||
if Group.objects.filter(name=name).exists():
|
||||
messages.error(request, "Group name already exists.")
|
||||
messages.error(request, _("Group name already exists."))
|
||||
else:
|
||||
group = Group.objects.create(name=name)
|
||||
if permission_ids:
|
||||
@ -1397,13 +1398,13 @@ def user_management(request):
|
||||
group_id = request.POST.get('group_id')
|
||||
group = get_object_or_404(Group, id=group_id)
|
||||
group.delete()
|
||||
messages.success(request, "Group deleted.")
|
||||
messages.success(request, _("Group deleted."))
|
||||
|
||||
elif action == 'toggle_status':
|
||||
user_id = request.POST.get('user_id')
|
||||
user = get_object_or_404(User, id=user_id)
|
||||
if user == request.user:
|
||||
messages.error(request, "You cannot deactivate yourself.")
|
||||
messages.error(request, _("You cannot deactivate yourself."))
|
||||
else:
|
||||
user.is_active = not user.is_active
|
||||
user.save()
|
||||
@ -1553,7 +1554,7 @@ def add_loyalty_tier(request):
|
||||
min_points=min_points, point_multiplier=multiplier,
|
||||
discount_percentage=discount, color_code=color
|
||||
)
|
||||
messages.success(request, "Loyalty tier added successfully!")
|
||||
messages.success(request, _("Loyalty tier added successfully!"))
|
||||
return redirect(reverse('settings') + '#loyalty')
|
||||
|
||||
@login_required
|
||||
@ -1567,14 +1568,14 @@ def edit_loyalty_tier(request, pk):
|
||||
tier.discount_percentage = request.POST.get('discount_percentage')
|
||||
tier.color_code = request.POST.get('color_code')
|
||||
tier.save()
|
||||
messages.success(request, "Loyalty tier updated successfully!")
|
||||
messages.success(request, _("Loyalty tier updated successfully!"))
|
||||
return redirect(reverse('settings') + '#loyalty')
|
||||
|
||||
@login_required
|
||||
def delete_loyalty_tier(request, pk):
|
||||
tier = get_object_or_404(LoyaltyTier, pk=pk)
|
||||
tier.delete()
|
||||
messages.success(request, "Loyalty tier deleted successfully!")
|
||||
messages.success(request, _("Loyalty tier deleted successfully!"))
|
||||
return redirect(reverse('settings') + '#loyalty')
|
||||
|
||||
@login_required
|
||||
@ -1631,11 +1632,11 @@ def profile_view(request):
|
||||
user.save()
|
||||
from django.contrib.auth import update_session_auth_hash
|
||||
update_session_auth_hash(request, user)
|
||||
messages.success(request, "Profile and password updated successfully!")
|
||||
messages.success(request, _("Profile and password updated successfully!"))
|
||||
else:
|
||||
messages.error(request, "Passwords do not match.")
|
||||
messages.error(request, _("Passwords do not match."))
|
||||
else:
|
||||
messages.success(request, "Profile updated successfully!")
|
||||
messages.success(request, _("Profile updated successfully!"))
|
||||
|
||||
return redirect('profile')
|
||||
|
||||
@ -1705,7 +1706,7 @@ def expense_create_view(request):
|
||||
attachment=attachment,
|
||||
created_by=request.user
|
||||
)
|
||||
messages.success(request, "Expense recorded successfully!")
|
||||
messages.success(request, _("Expense recorded successfully!"))
|
||||
|
||||
return redirect('expenses')
|
||||
|
||||
@ -1716,7 +1717,7 @@ def expense_delete_view(request, pk):
|
||||
"""
|
||||
expense = get_object_or_404(Expense, pk=pk)
|
||||
expense.delete()
|
||||
messages.success(request, "Expense deleted successfully!")
|
||||
messages.success(request, _("Expense deleted successfully!"))
|
||||
return redirect('expenses')
|
||||
|
||||
@login_required
|
||||
@ -1725,22 +1726,28 @@ def expense_categories_view(request):
|
||||
Manage expense categories
|
||||
"""
|
||||
if request.method == 'POST':
|
||||
category_id = request.POST.get('category_id')
|
||||
name_en = request.POST.get('name_en')
|
||||
name_ar = request.POST.get('name_ar')
|
||||
description = request.POST.get('description', '')
|
||||
|
||||
ExpenseCategory.objects.create(
|
||||
name_en=name_en,
|
||||
name_ar=name_ar,
|
||||
description=description
|
||||
)
|
||||
messages.success(request, "Expense category created successfully!")
|
||||
if category_id:
|
||||
category = get_object_or_404(ExpenseCategory, id=category_id)
|
||||
category.name_en = name_en
|
||||
category.name_ar = name_ar
|
||||
category.description = description
|
||||
category.save()
|
||||
messages.success(request, _("Expense category updated successfully!"))
|
||||
else:
|
||||
ExpenseCategory.objects.create(
|
||||
name_en=name_en,
|
||||
name_ar=name_ar,
|
||||
description=description
|
||||
)
|
||||
messages.success(request, _("Expense category created successfully!"))
|
||||
return redirect('expense_categories')
|
||||
|
||||
paginator = Paginator(expenses, 25)
|
||||
page_number = request.GET.get('page')
|
||||
expenses = paginator.get_page(page_number)
|
||||
categories = ExpenseCategory.objects.all()
|
||||
categories = ExpenseCategory.objects.all().order_by('name_en')
|
||||
return render(request, 'core/expense_categories.html', {'categories': categories})
|
||||
|
||||
@login_required
|
||||
@ -1750,11 +1757,10 @@ def expense_category_delete_view(request, pk):
|
||||
"""
|
||||
category = get_object_or_404(ExpenseCategory, pk=pk)
|
||||
category.delete()
|
||||
messages.success(request, "Expense category deleted successfully!")
|
||||
messages.success(request, _("Expense category deleted successfully!"))
|
||||
return redirect('expense_categories')
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@csrf_exempt
|
||||
def update_sale_api(request, pk):
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
|
||||
BIN
locale/ar/LC_MESSAGES/django.mo
Normal file
BIN
locale/ar/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
3182
locale/ar/LC_MESSAGES/django.po
Normal file
3182
locale/ar/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user