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>
|
</nav>
|
||||||
<h2 class="mb-0">{% trans "Chart of Accounts" %}</h2>
|
<h2 class="mb-0">{% trans "Chart of Accounts" %}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
<a href="{% url 'account_create' %}" class="btn btn-primary">
|
||||||
|
<i class="bi bi-plus-lg"></i> {% trans "Add Account" %}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card border-0 shadow-sm">
|
<div class="card border-0 shadow-sm">
|
||||||
@ -43,9 +46,14 @@
|
|||||||
{{ account.balance|floatformat:global_settings.decimal_places }} {{ global_settings.currency_symbol }}
|
{{ account.balance|floatformat:global_settings.decimal_places }} {{ global_settings.currency_symbol }}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<a href="{% url 'account_ledger' account.id %}" class="btn btn-sm btn-outline-primary">
|
<div class="btn-group">
|
||||||
<i class="bi bi-list-columns"></i> {% trans "Ledger" %}
|
<a href="{% url 'account_ledger' account.id %}" class="btn btn-sm btn-outline-primary" title="{% trans 'Ledger' %}">
|
||||||
</a>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@ -13,6 +13,9 @@
|
|||||||
</nav>
|
</nav>
|
||||||
<h2 class="mb-0">{% trans "Journal Entries" %}</h2>
|
<h2 class="mb-0">{% trans "Journal Entries" %}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
<a href="{% url 'manual_journal_entry' %}" class="btn btn-primary">
|
||||||
|
<i class="bi bi-plus-lg"></i> {% trans "New Manual Entry" %}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% for entry in entries %}
|
{% for entry in entries %}
|
||||||
|
|||||||
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,7 +4,10 @@ from . import views
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.accounting_dashboard, name='accounting_dashboard'),
|
path('', views.accounting_dashboard, name='accounting_dashboard'),
|
||||||
path('chart-of-accounts/', views.chart_of_accounts, name='chart_of_accounts'),
|
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/', 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('ledger/<int:account_id>/', views.account_ledger, name='account_ledger'),
|
||||||
path('trial-balance/', views.trial_balance, name='trial_balance'),
|
path('trial-balance/', views.trial_balance, name='trial_balance'),
|
||||||
path('balance-sheet/', views.balance_sheet, name='balance_sheet'),
|
path('balance-sheet/', views.balance_sheet, name='balance_sheet'),
|
||||||
|
|||||||
@ -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.auth.decorators import login_required
|
||||||
|
from django.contrib import messages
|
||||||
from .models import Account, JournalEntry, JournalItem
|
from .models import Account, JournalEntry, JournalItem
|
||||||
|
from .forms import AccountForm, JournalEntryForm
|
||||||
from django.db.models import Sum, Q
|
from django.db.models import Sum, Q
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from django.db import transaction
|
||||||
|
import json
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def accounting_dashboard(request):
|
def accounting_dashboard(request):
|
||||||
@ -44,11 +49,84 @@ def chart_of_accounts(request):
|
|||||||
accounts = Account.objects.all().order_by('code')
|
accounts = Account.objects.all().order_by('code')
|
||||||
return render(request, 'accounting/chart_of_accounts.html', {'accounts': accounts})
|
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
|
@login_required
|
||||||
def journal_entries(request):
|
def journal_entries(request):
|
||||||
entries = JournalEntry.objects.all().order_by('-date', '-id')
|
entries = JournalEntry.objects.all().order_by('-date', '-id')
|
||||||
return render(request, 'accounting/journal_entries.html', {'entries': entries})
|
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
|
@login_required
|
||||||
def account_ledger(request, account_id):
|
def account_ledger(request, account_id):
|
||||||
account = get_object_or_404(Account, id=account_id)
|
account = get_object_or_404(Account, id=account_id)
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -24,6 +24,7 @@ def global_settings(request):
|
|||||||
settings = SystemSetting.objects.create()
|
settings = SystemSetting.objects.create()
|
||||||
return {
|
return {
|
||||||
'site_settings': settings,
|
'site_settings': settings,
|
||||||
|
'global_settings': settings,
|
||||||
'decimal_places': settings.decimal_places if settings else 3
|
'decimal_places': settings.decimal_places if settings else 3
|
||||||
}
|
}
|
||||||
except:
|
except:
|
||||||
|
|||||||
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 django.utils.translation import gettext_lazy as _
|
||||||
from .utils import number_to_words_en
|
from .utils import number_to_words_en
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
@ -277,7 +278,7 @@ def add_purchase_payment(request, pk):
|
|||||||
created_by=request.user
|
created_by=request.user
|
||||||
)
|
)
|
||||||
purchase.update_balance()
|
purchase.update_balance()
|
||||||
messages.success(request, "Payment added successfully!")
|
messages.success(request, _("Payment added successfully!"))
|
||||||
return redirect('purchases')
|
return redirect('purchases')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -288,7 +289,7 @@ def delete_purchase(request, pk):
|
|||||||
item.product.save()
|
item.product.save()
|
||||||
|
|
||||||
purchase.delete()
|
purchase.delete()
|
||||||
messages.success(request, "Purchase deleted successfully!")
|
messages.success(request, _("Purchase deleted successfully!"))
|
||||||
return redirect('purchases')
|
return redirect('purchases')
|
||||||
|
|
||||||
# --- Sale Views ---
|
# --- Sale Views ---
|
||||||
@ -552,7 +553,7 @@ def add_sale_payment(request, pk):
|
|||||||
created_by=request.user
|
created_by=request.user
|
||||||
)
|
)
|
||||||
sale.update_balance()
|
sale.update_balance()
|
||||||
messages.success(request, "Payment added successfully!")
|
messages.success(request, _("Payment added successfully!"))
|
||||||
return redirect('invoices')
|
return redirect('invoices')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -562,7 +563,7 @@ def delete_sale(request, pk):
|
|||||||
item.product.stock_quantity += item.quantity
|
item.product.stock_quantity += item.quantity
|
||||||
item.product.save()
|
item.product.save()
|
||||||
sale.delete()
|
sale.delete()
|
||||||
messages.success(request, "Sale deleted successfully!")
|
messages.success(request, _("Sale deleted successfully!"))
|
||||||
return redirect('invoices')
|
return redirect('invoices')
|
||||||
|
|
||||||
# --- Quotation Views ---
|
# --- Quotation Views ---
|
||||||
@ -641,7 +642,7 @@ def create_quotation_api(request):
|
|||||||
def convert_quotation_to_invoice(request, pk):
|
def convert_quotation_to_invoice(request, pk):
|
||||||
quotation = get_object_or_404(Quotation, pk=pk)
|
quotation = get_object_or_404(Quotation, pk=pk)
|
||||||
if quotation.status == 'converted':
|
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')
|
return redirect('invoices')
|
||||||
|
|
||||||
# Create Sale from Quotation
|
# Create Sale from Quotation
|
||||||
@ -674,14 +675,14 @@ def convert_quotation_to_invoice(request, pk):
|
|||||||
quotation.status = 'converted'
|
quotation.status = 'converted'
|
||||||
quotation.save()
|
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)
|
return redirect('invoice_detail', pk=sale.pk)
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def delete_quotation(request, pk):
|
def delete_quotation(request, pk):
|
||||||
quotation = get_object_or_404(Quotation, pk=pk)
|
quotation = get_object_or_404(Quotation, pk=pk)
|
||||||
quotation.delete()
|
quotation.delete()
|
||||||
messages.success(request, "Quotation deleted successfully!")
|
messages.success(request, _("Quotation deleted successfully!"))
|
||||||
return redirect('quotations')
|
return redirect('quotations')
|
||||||
|
|
||||||
# --- Sale Return Views ---
|
# --- Sale Return Views ---
|
||||||
@ -770,7 +771,7 @@ def delete_sale_return(request, pk):
|
|||||||
item.product.stock_quantity -= item.quantity
|
item.product.stock_quantity -= item.quantity
|
||||||
item.product.save()
|
item.product.save()
|
||||||
sale_return.delete()
|
sale_return.delete()
|
||||||
messages.success(request, "Sale return deleted successfully!")
|
messages.success(request, _("Sale return deleted successfully!"))
|
||||||
return redirect('sales_returns')
|
return redirect('sales_returns')
|
||||||
|
|
||||||
|
|
||||||
@ -860,7 +861,7 @@ def delete_purchase_return(request, pk):
|
|||||||
item.product.stock_quantity += item.quantity
|
item.product.stock_quantity += item.quantity
|
||||||
item.product.save()
|
item.product.save()
|
||||||
purchase_return.delete()
|
purchase_return.delete()
|
||||||
messages.success(request, "Purchase return deleted successfully!")
|
messages.success(request, _("Purchase return deleted successfully!"))
|
||||||
return redirect('purchase_returns')
|
return redirect('purchase_returns')
|
||||||
|
|
||||||
# --- Other Management Views ---
|
# --- Other Management Views ---
|
||||||
@ -917,7 +918,7 @@ def settings_view(request):
|
|||||||
settings.logo = request.FILES['logo']
|
settings.logo = request.FILES['logo']
|
||||||
|
|
||||||
settings.save()
|
settings.save()
|
||||||
messages.success(request, "Settings updated successfully!")
|
messages.success(request, _("Settings updated successfully!"))
|
||||||
return redirect(reverse('settings') + '#profile')
|
return redirect(reverse('settings') + '#profile')
|
||||||
|
|
||||||
payment_methods = PaymentMethod.objects.all()
|
payment_methods = PaymentMethod.objects.all()
|
||||||
@ -936,7 +937,7 @@ def add_payment_method(request):
|
|||||||
name_ar = request.POST.get('name_ar')
|
name_ar = request.POST.get('name_ar')
|
||||||
is_active = request.POST.get('is_active') == 'on'
|
is_active = request.POST.get('is_active') == 'on'
|
||||||
PaymentMethod.objects.create(name_en=name_en, name_ar=name_ar, is_active=is_active)
|
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')
|
return redirect(reverse('settings') + '#payments')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -947,14 +948,14 @@ def edit_payment_method(request, pk):
|
|||||||
pm.name_ar = request.POST.get('name_ar')
|
pm.name_ar = request.POST.get('name_ar')
|
||||||
pm.is_active = request.POST.get('is_active') == 'on'
|
pm.is_active = request.POST.get('is_active') == 'on'
|
||||||
pm.save()
|
pm.save()
|
||||||
messages.success(request, "Payment method updated successfully!")
|
messages.success(request, _("Payment method updated successfully!"))
|
||||||
return redirect(reverse('settings') + '#payments')
|
return redirect(reverse('settings') + '#payments')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def delete_payment_method(request, pk):
|
def delete_payment_method(request, pk):
|
||||||
pm = get_object_or_404(PaymentMethod, pk=pk)
|
pm = get_object_or_404(PaymentMethod, pk=pk)
|
||||||
pm.delete()
|
pm.delete()
|
||||||
messages.success(request, "Payment method deleted successfully!")
|
messages.success(request, _("Payment method deleted successfully!"))
|
||||||
return redirect(reverse('settings') + '#payments')
|
return redirect(reverse('settings') + '#payments')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -965,7 +966,7 @@ def add_customer(request):
|
|||||||
email = request.POST.get('email')
|
email = request.POST.get('email')
|
||||||
address = request.POST.get('address')
|
address = request.POST.get('address')
|
||||||
Customer.objects.create(name=name, phone=phone, email=email, address=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')
|
return redirect('customers')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -977,14 +978,14 @@ def edit_customer(request, pk):
|
|||||||
customer.email = request.POST.get('email')
|
customer.email = request.POST.get('email')
|
||||||
customer.address = request.POST.get('address')
|
customer.address = request.POST.get('address')
|
||||||
customer.save()
|
customer.save()
|
||||||
messages.success(request, "Customer updated successfully!")
|
messages.success(request, _("Customer updated successfully!"))
|
||||||
return redirect('customers')
|
return redirect('customers')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def delete_customer(request, pk):
|
def delete_customer(request, pk):
|
||||||
customer = get_object_or_404(Customer, pk=pk)
|
customer = get_object_or_404(Customer, pk=pk)
|
||||||
customer.delete()
|
customer.delete()
|
||||||
messages.success(request, "Customer deleted successfully!")
|
messages.success(request, _("Customer deleted successfully!"))
|
||||||
return redirect('customers')
|
return redirect('customers')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -994,7 +995,7 @@ def add_supplier(request):
|
|||||||
contact_person = request.POST.get('contact_person')
|
contact_person = request.POST.get('contact_person')
|
||||||
phone = request.POST.get('phone')
|
phone = request.POST.get('phone')
|
||||||
Supplier.objects.create(name=name, contact_person=contact_person, phone=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')
|
return redirect('suppliers')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -1005,14 +1006,14 @@ def edit_supplier(request, pk):
|
|||||||
supplier.contact_person = request.POST.get('contact_person')
|
supplier.contact_person = request.POST.get('contact_person')
|
||||||
supplier.phone = request.POST.get('phone')
|
supplier.phone = request.POST.get('phone')
|
||||||
supplier.save()
|
supplier.save()
|
||||||
messages.success(request, "Supplier updated successfully!")
|
messages.success(request, _("Supplier updated successfully!"))
|
||||||
return redirect('suppliers')
|
return redirect('suppliers')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def delete_supplier(request, pk):
|
def delete_supplier(request, pk):
|
||||||
supplier = get_object_or_404(Supplier, pk=pk)
|
supplier = get_object_or_404(Supplier, pk=pk)
|
||||||
supplier.delete()
|
supplier.delete()
|
||||||
messages.success(request, "Supplier deleted successfully!")
|
messages.success(request, _("Supplier deleted successfully!"))
|
||||||
return redirect('suppliers')
|
return redirect('suppliers')
|
||||||
|
|
||||||
|
|
||||||
@ -1071,7 +1072,7 @@ def add_product(request):
|
|||||||
product.image = request.FILES['image']
|
product.image = request.FILES['image']
|
||||||
product.save()
|
product.save()
|
||||||
|
|
||||||
messages.success(request, "Product added successfully!")
|
messages.success(request, _("Product added successfully!"))
|
||||||
return redirect(reverse('inventory') + '#items')
|
return redirect(reverse('inventory') + '#items')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -1100,7 +1101,7 @@ def edit_product(request, pk):
|
|||||||
product.image = request.FILES['image']
|
product.image = request.FILES['image']
|
||||||
|
|
||||||
product.save()
|
product.save()
|
||||||
messages.success(request, "Product updated successfully!")
|
messages.success(request, _("Product updated successfully!"))
|
||||||
return redirect(reverse('inventory') + '#items')
|
return redirect(reverse('inventory') + '#items')
|
||||||
return redirect(reverse('inventory') + '#items')
|
return redirect(reverse('inventory') + '#items')
|
||||||
|
|
||||||
@ -1108,7 +1109,7 @@ def edit_product(request, pk):
|
|||||||
def delete_product(request, pk):
|
def delete_product(request, pk):
|
||||||
product = get_object_or_404(Product, pk=pk)
|
product = get_object_or_404(Product, pk=pk)
|
||||||
product.delete()
|
product.delete()
|
||||||
messages.success(request, "Product deleted successfully!")
|
messages.success(request, _("Product deleted successfully!"))
|
||||||
return redirect(reverse('inventory') + '#items')
|
return redirect(reverse('inventory') + '#items')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -1118,7 +1119,7 @@ def add_category(request):
|
|||||||
name_ar = request.POST.get('name_ar')
|
name_ar = request.POST.get('name_ar')
|
||||||
slug = slugify(name_en)
|
slug = slugify(name_en)
|
||||||
Category.objects.create(name_en=name_en, name_ar=name_ar, slug=slug)
|
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')
|
return redirect(reverse('inventory') + '#categories-list')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -1129,14 +1130,14 @@ def edit_category(request, pk):
|
|||||||
category.name_ar = request.POST.get('name_ar')
|
category.name_ar = request.POST.get('name_ar')
|
||||||
category.slug = slugify(category.name_en)
|
category.slug = slugify(category.name_en)
|
||||||
category.save()
|
category.save()
|
||||||
messages.success(request, "Category updated successfully!")
|
messages.success(request, _("Category updated successfully!"))
|
||||||
return redirect(reverse('inventory') + '#categories-list')
|
return redirect(reverse('inventory') + '#categories-list')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def delete_category(request, pk):
|
def delete_category(request, pk):
|
||||||
category = get_object_or_404(Category, pk=pk)
|
category = get_object_or_404(Category, pk=pk)
|
||||||
category.delete()
|
category.delete()
|
||||||
messages.success(request, "Category deleted successfully!")
|
messages.success(request, _("Category deleted successfully!"))
|
||||||
return redirect(reverse('inventory') + '#categories-list')
|
return redirect(reverse('inventory') + '#categories-list')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -1146,7 +1147,7 @@ def add_unit(request):
|
|||||||
name_ar = request.POST.get('name_ar')
|
name_ar = request.POST.get('name_ar')
|
||||||
short_name = request.POST.get('short_name')
|
short_name = request.POST.get('short_name')
|
||||||
Unit.objects.create(name_en=name_en, name_ar=name_ar, short_name=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')
|
return redirect(reverse('inventory') + '#units-list')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -1157,14 +1158,14 @@ def edit_unit(request, pk):
|
|||||||
unit.name_ar = request.POST.get('name_ar')
|
unit.name_ar = request.POST.get('name_ar')
|
||||||
unit.short_name = request.POST.get('short_name')
|
unit.short_name = request.POST.get('short_name')
|
||||||
unit.save()
|
unit.save()
|
||||||
messages.success(request, "Unit updated successfully!")
|
messages.success(request, _("Unit updated successfully!"))
|
||||||
return redirect(reverse('inventory') + '#units-list')
|
return redirect(reverse('inventory') + '#units-list')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def delete_unit(request, pk):
|
def delete_unit(request, pk):
|
||||||
unit = get_object_or_404(Unit, pk=pk)
|
unit = get_object_or_404(Unit, pk=pk)
|
||||||
unit.delete()
|
unit.delete()
|
||||||
messages.success(request, "Unit deleted successfully!")
|
messages.success(request, _("Unit deleted successfully!"))
|
||||||
return redirect(reverse('inventory') + '#units-list')
|
return redirect(reverse('inventory') + '#units-list')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -1183,7 +1184,7 @@ def import_products(request):
|
|||||||
excel_file = request.FILES['excel_file']
|
excel_file = request.FILES['excel_file']
|
||||||
|
|
||||||
if not excel_file.name.endswith('.xlsx'):
|
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')
|
return redirect(reverse('inventory') + '#items')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -1326,7 +1327,7 @@ def add_supplier_ajax(request):
|
|||||||
@login_required
|
@login_required
|
||||||
def user_management(request):
|
def user_management(request):
|
||||||
if not (request.user.is_superuser or request.user.groups.filter(name='admin').exists()):
|
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')
|
return redirect('index')
|
||||||
|
|
||||||
users_qs = User.objects.all().prefetch_related('groups').order_by('username')
|
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')
|
group_ids = request.POST.getlist('groups')
|
||||||
|
|
||||||
if User.objects.filter(username=username).exists():
|
if User.objects.filter(username=username).exists():
|
||||||
messages.error(request, "Username already exists.")
|
messages.error(request, _("Username already exists."))
|
||||||
else:
|
else:
|
||||||
user = User.objects.create_user(username=username, email=email, password=password)
|
user = User.objects.create_user(username=username, email=email, password=password)
|
||||||
if group_ids:
|
if group_ids:
|
||||||
@ -1375,7 +1376,7 @@ def user_management(request):
|
|||||||
name = request.POST.get('name')
|
name = request.POST.get('name')
|
||||||
permission_ids = request.POST.getlist('permissions')
|
permission_ids = request.POST.getlist('permissions')
|
||||||
if Group.objects.filter(name=name).exists():
|
if Group.objects.filter(name=name).exists():
|
||||||
messages.error(request, "Group name already exists.")
|
messages.error(request, _("Group name already exists."))
|
||||||
else:
|
else:
|
||||||
group = Group.objects.create(name=name)
|
group = Group.objects.create(name=name)
|
||||||
if permission_ids:
|
if permission_ids:
|
||||||
@ -1397,13 +1398,13 @@ def user_management(request):
|
|||||||
group_id = request.POST.get('group_id')
|
group_id = request.POST.get('group_id')
|
||||||
group = get_object_or_404(Group, id=group_id)
|
group = get_object_or_404(Group, id=group_id)
|
||||||
group.delete()
|
group.delete()
|
||||||
messages.success(request, "Group deleted.")
|
messages.success(request, _("Group deleted."))
|
||||||
|
|
||||||
elif action == 'toggle_status':
|
elif action == 'toggle_status':
|
||||||
user_id = request.POST.get('user_id')
|
user_id = request.POST.get('user_id')
|
||||||
user = get_object_or_404(User, id=user_id)
|
user = get_object_or_404(User, id=user_id)
|
||||||
if user == request.user:
|
if user == request.user:
|
||||||
messages.error(request, "You cannot deactivate yourself.")
|
messages.error(request, _("You cannot deactivate yourself."))
|
||||||
else:
|
else:
|
||||||
user.is_active = not user.is_active
|
user.is_active = not user.is_active
|
||||||
user.save()
|
user.save()
|
||||||
@ -1553,7 +1554,7 @@ def add_loyalty_tier(request):
|
|||||||
min_points=min_points, point_multiplier=multiplier,
|
min_points=min_points, point_multiplier=multiplier,
|
||||||
discount_percentage=discount, color_code=color
|
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')
|
return redirect(reverse('settings') + '#loyalty')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -1567,14 +1568,14 @@ def edit_loyalty_tier(request, pk):
|
|||||||
tier.discount_percentage = request.POST.get('discount_percentage')
|
tier.discount_percentage = request.POST.get('discount_percentage')
|
||||||
tier.color_code = request.POST.get('color_code')
|
tier.color_code = request.POST.get('color_code')
|
||||||
tier.save()
|
tier.save()
|
||||||
messages.success(request, "Loyalty tier updated successfully!")
|
messages.success(request, _("Loyalty tier updated successfully!"))
|
||||||
return redirect(reverse('settings') + '#loyalty')
|
return redirect(reverse('settings') + '#loyalty')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def delete_loyalty_tier(request, pk):
|
def delete_loyalty_tier(request, pk):
|
||||||
tier = get_object_or_404(LoyaltyTier, pk=pk)
|
tier = get_object_or_404(LoyaltyTier, pk=pk)
|
||||||
tier.delete()
|
tier.delete()
|
||||||
messages.success(request, "Loyalty tier deleted successfully!")
|
messages.success(request, _("Loyalty tier deleted successfully!"))
|
||||||
return redirect(reverse('settings') + '#loyalty')
|
return redirect(reverse('settings') + '#loyalty')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -1631,11 +1632,11 @@ def profile_view(request):
|
|||||||
user.save()
|
user.save()
|
||||||
from django.contrib.auth import update_session_auth_hash
|
from django.contrib.auth import update_session_auth_hash
|
||||||
update_session_auth_hash(request, user)
|
update_session_auth_hash(request, user)
|
||||||
messages.success(request, "Profile and password updated successfully!")
|
messages.success(request, _("Profile and password updated successfully!"))
|
||||||
else:
|
else:
|
||||||
messages.error(request, "Passwords do not match.")
|
messages.error(request, _("Passwords do not match."))
|
||||||
else:
|
else:
|
||||||
messages.success(request, "Profile updated successfully!")
|
messages.success(request, _("Profile updated successfully!"))
|
||||||
|
|
||||||
return redirect('profile')
|
return redirect('profile')
|
||||||
|
|
||||||
@ -1705,7 +1706,7 @@ def expense_create_view(request):
|
|||||||
attachment=attachment,
|
attachment=attachment,
|
||||||
created_by=request.user
|
created_by=request.user
|
||||||
)
|
)
|
||||||
messages.success(request, "Expense recorded successfully!")
|
messages.success(request, _("Expense recorded successfully!"))
|
||||||
|
|
||||||
return redirect('expenses')
|
return redirect('expenses')
|
||||||
|
|
||||||
@ -1716,7 +1717,7 @@ def expense_delete_view(request, pk):
|
|||||||
"""
|
"""
|
||||||
expense = get_object_or_404(Expense, pk=pk)
|
expense = get_object_or_404(Expense, pk=pk)
|
||||||
expense.delete()
|
expense.delete()
|
||||||
messages.success(request, "Expense deleted successfully!")
|
messages.success(request, _("Expense deleted successfully!"))
|
||||||
return redirect('expenses')
|
return redirect('expenses')
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -1725,22 +1726,28 @@ def expense_categories_view(request):
|
|||||||
Manage expense categories
|
Manage expense categories
|
||||||
"""
|
"""
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
category_id = request.POST.get('category_id')
|
||||||
name_en = request.POST.get('name_en')
|
name_en = request.POST.get('name_en')
|
||||||
name_ar = request.POST.get('name_ar')
|
name_ar = request.POST.get('name_ar')
|
||||||
description = request.POST.get('description', '')
|
description = request.POST.get('description', '')
|
||||||
|
|
||||||
ExpenseCategory.objects.create(
|
if category_id:
|
||||||
name_en=name_en,
|
category = get_object_or_404(ExpenseCategory, id=category_id)
|
||||||
name_ar=name_ar,
|
category.name_en = name_en
|
||||||
description=description
|
category.name_ar = name_ar
|
||||||
)
|
category.description = description
|
||||||
messages.success(request, "Expense category created successfully!")
|
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')
|
return redirect('expense_categories')
|
||||||
|
|
||||||
paginator = Paginator(expenses, 25)
|
categories = ExpenseCategory.objects.all().order_by('name_en')
|
||||||
page_number = request.GET.get('page')
|
|
||||||
expenses = paginator.get_page(page_number)
|
|
||||||
categories = ExpenseCategory.objects.all()
|
|
||||||
return render(request, 'core/expense_categories.html', {'categories': categories})
|
return render(request, 'core/expense_categories.html', {'categories': categories})
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -1750,11 +1757,10 @@ def expense_category_delete_view(request, pk):
|
|||||||
"""
|
"""
|
||||||
category = get_object_or_404(ExpenseCategory, pk=pk)
|
category = get_object_or_404(ExpenseCategory, pk=pk)
|
||||||
category.delete()
|
category.delete()
|
||||||
messages.success(request, "Expense category deleted successfully!")
|
messages.success(request, _("Expense category deleted successfully!"))
|
||||||
return redirect('expense_categories')
|
return redirect('expense_categories')
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@login_required
|
@login_required
|
||||||
@csrf_exempt
|
|
||||||
def update_sale_api(request, pk):
|
def update_sale_api(request, pk):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
try:
|
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