333 lines
18 KiB
Python
333 lines
18 KiB
Python
import datetime
|
|
import pkg_resources
|
|
import transaction
|
|
import uuid
|
|
|
|
from decimal import Decimal
|
|
from pyramid.response import Response, FileResponse
|
|
from pyramid.view import view_config
|
|
from sqlalchemy import func, or_
|
|
|
|
from brewman.models.auth import User
|
|
from brewman.models.master import LedgerBase, CostCentre, Employee, AttendanceType, Ledger
|
|
from brewman.models.validation_exception import ValidationError
|
|
from brewman.models.voucher import Voucher, VoucherType, Inventory, DbImage, Attendance, Journal
|
|
from brewman.views import get_lock_info
|
|
from .issue import issue_create_voucher, issue_update_voucher
|
|
from .journal import journal_update_voucher, journal_create_voucher
|
|
from .purchase import purchase_create_voucher, purchase_update_voucher
|
|
from .service_charge import service_charge_create_voucher, service_charge_update_voucher
|
|
from ..session import get_first_day
|
|
|
|
|
|
@view_config(request_method='GET', route_name='journal_id', permission='Journal')
|
|
@view_config(request_method='GET', route_name='journal', permission='Journal')
|
|
@view_config(request_method='GET', route_name='payment_id', permission='Payment')
|
|
@view_config(request_method='GET', route_name='payment', permission='Payment')
|
|
@view_config(request_method='GET', route_name='receipt_id', permission='Receipt')
|
|
@view_config(request_method='GET', route_name='receipt', permission='Receipt')
|
|
@view_config(request_method='GET', route_name='purchase_id', permission='Purchase')
|
|
@view_config(request_method='GET', route_name='purchase', permission='Purchase')
|
|
@view_config(request_method='GET', route_name='purchase_return_id', permission='Purchase Return')
|
|
@view_config(request_method='GET', route_name='purchase_return', permission='Purchase Return')
|
|
@view_config(request_method='GET', route_name='issue_id', permission='Issue')
|
|
@view_config(request_method='GET', route_name='issue', permission='Issue')
|
|
@view_config(request_method='GET', route_name='salary_deduction_id', permission='Issue')
|
|
@view_config(request_method='GET', route_name='salary_deduction', permission='Issue')
|
|
@view_config(request_method='GET', route_name='service_charge_id', permission='Service Charge')
|
|
@view_config(request_method='GET', route_name='service_charge', permission='Service Charge')
|
|
def journal_get(request):
|
|
package, resource = 'brewman:static/index.html'.split(':', 1)
|
|
file = pkg_resources.resource_filename(package, resource)
|
|
return FileResponse(file, request=request)
|
|
|
|
|
|
@view_config(request_method='POST', route_name='api_voucher_id', request_param='post', renderer='json',
|
|
permission='Post Vouchers', trans=True)
|
|
def voucher_post(request):
|
|
user = request.dbsession.query(User).filter(User.id == uuid.UUID(request.authenticated_userid)).one()
|
|
voucher = request.dbsession.query(Voucher).filter(Voucher.id == uuid.UUID(request.matchdict['id'])).first()
|
|
start, finish = get_lock_info(request.dbsession)
|
|
if start is not None and start > voucher.date:
|
|
raise ValidationError("Vouchers before {0} have been locked.".format(start.strftime('%d-%b-%Y')))
|
|
elif finish is not None and finish < voucher.date:
|
|
raise ValidationError("Vouchers after {0} have been locked.".format(finish.strftime('%d-%b-%Y')))
|
|
voucher.posted = True
|
|
voucher.poster_id = user.id
|
|
transaction.commit()
|
|
new_voucher = request.dbsession.query(Voucher).filter(Voucher.id == voucher.id).first()
|
|
return voucher_info(new_voucher, request)
|
|
|
|
|
|
def check_delete_permissions(request, voucher):
|
|
user = request.dbsession.query(User).filter(User.id == uuid.UUID(request.authenticated_userid)).one()
|
|
|
|
if voucher.posted and not request.has_permission('Edit Posted Vouchers'):
|
|
response = Response("You are not allowed to edit posted vouchers")
|
|
response.status_int = 403
|
|
return response
|
|
elif voucher.user_id != user.id and not request.has_permission("Edit Other User's Vouchers"):
|
|
response = Response("You are not allowed to edit other user's vouchers")
|
|
response.status_int = 403
|
|
return response
|
|
elif not request.has_permission(VoucherType.by_id(voucher.type).name):
|
|
response = Response("You are not allowed (0) vouchers".format(VoucherType.by_id(voucher.type).name))
|
|
response.status_int = 403
|
|
return response
|
|
start, finish = get_lock_info(request.dbsession)
|
|
if start is not None and start > voucher.date:
|
|
response = Response("Vouchers before {0} have been locked.".format(start.strftime('%d-%b-%Y')))
|
|
response.status_int = 403
|
|
return response
|
|
elif finish is not None and finish < voucher.date:
|
|
response = Response("Vouchers after {0} have been locked.".format(finish.strftime('%d-%b-%Y')))
|
|
response.status_int = 403
|
|
return response
|
|
|
|
|
|
@view_config(request_method='DELETE', route_name='api_voucher_id', renderer='json', trans=True)
|
|
def delete(request):
|
|
voucher = request.dbsession.query(Voucher).filter(Voucher.id == uuid.UUID(request.matchdict['id'])).first()
|
|
images = request.dbsession.query(DbImage).filter(DbImage.resource_id == voucher.id).all()
|
|
permission = check_delete_permissions(request, voucher)
|
|
if permission is not None:
|
|
return permission
|
|
json_voucher = voucher_info(voucher, request)
|
|
batches_to_delete = []
|
|
if voucher.type == VoucherType.by_name('Issue').id:
|
|
for item in voucher.journals:
|
|
if item.debit == 1:
|
|
destination = item.cost_centre_id
|
|
else:
|
|
source = item.cost_centre_id
|
|
if source == CostCentre.cost_centre_purchase():
|
|
batch_consumed = True
|
|
elif destination == CostCentre.cost_centre_purchase():
|
|
batch_consumed = False
|
|
else:
|
|
batch_consumed = None
|
|
|
|
if batch_consumed is None:
|
|
pass
|
|
elif batch_consumed:
|
|
for item in voucher.inventories:
|
|
item.batch.quantity_remaining += item.quantity
|
|
else:
|
|
for item in voucher.inventories:
|
|
if item.batch.quantity_remaining < item.batch.quantity_remaining:
|
|
raise ValueError(
|
|
'Quantity remaining for {0} is less than issued, hence cannot be deleted'.format(
|
|
item.product.name))
|
|
item.batch.quantity_remaining -= item.quantity
|
|
elif voucher.type == VoucherType.by_name('Purchase').id:
|
|
for item in voucher.inventories:
|
|
uses = request.dbsession.query(func.count(Inventory.id)) \
|
|
.filter(Inventory.batch_id == item.batch.id) \
|
|
.filter(Inventory.id != item.id).scalar()
|
|
if uses > 0:
|
|
raise ValueError('{0} has been issued and cannot be deleted'.format(item.product.name))
|
|
batches_to_delete.append(item.batch)
|
|
elif voucher.type == VoucherType.by_name('Purchase Return').id:
|
|
for item in voucher.inventories:
|
|
item.batch.quantity_remaining += item.quantity
|
|
for b in batches_to_delete:
|
|
request.dbsession.delete(b)
|
|
request.dbsession.delete(voucher)
|
|
for image in images:
|
|
request.dbsession.delete(image)
|
|
transaction.commit()
|
|
return blank_voucher(info=json_voucher, dbsession=request.dbsession)
|
|
|
|
|
|
@view_config(request_method='GET', route_name='api_voucher_id', renderer='json', trans=True)
|
|
def get_old(request):
|
|
id = request.matchdict.get('id', None)
|
|
voucher = request.dbsession.query(Voucher).filter(Voucher.id == uuid.UUID(id)).first()
|
|
return voucher_info(voucher, request)
|
|
|
|
|
|
def voucher_info(voucher, request):
|
|
json_voucher = {'VoucherID': voucher.id,
|
|
'Date': voucher.date.strftime('%d-%b-%Y'),
|
|
'Type': VoucherType.by_id(voucher.type).name,
|
|
'Posted': voucher.posted,
|
|
'Narration': voucher.narration,
|
|
'Journals': [],
|
|
'Inventories': [],
|
|
'SalaryDeductions': [],
|
|
'ServiceCharges': [],
|
|
'Files': [],
|
|
'CreationDate': voucher.creation_date.strftime('%d-%b-%Y %H:%M'),
|
|
'LastEditDate': voucher.last_edit_date.strftime('%d-%b-%Y %H:%M'),
|
|
'User': {'UserID': voucher.user.id, 'Name': voucher.user.name},
|
|
'Poster': voucher.poster.name if voucher.posted else ''}
|
|
if voucher.reconcile_date is not None:
|
|
json_voucher['ReconcileDate'] = voucher.reconcile_date.strftime('%d-%b-%Y %H:%M')
|
|
for item in voucher.journals:
|
|
json_voucher['Journals'].append({'JournalID': item.id, 'Debit': item.debit, 'Amount': item.amount,
|
|
'Ledger': {'LedgerID': item.ledger.id, 'Name': item.ledger.name},
|
|
'CostCentre': {'CostCentreID': item.cost_centre_id}})
|
|
for item in voucher.salary_deductions:
|
|
json_voucher['SalaryDeductions'].append(
|
|
{'GrossSalary': item.gross_salary,
|
|
'DaysWorked': item.days_worked,
|
|
'EsiEmployee': item.esi_ee,
|
|
'PfEmployee': item.pf_ee,
|
|
'EsiEmployer': item.esi_er,
|
|
'PfEmployer': item.pf_er,
|
|
'Journal': {'JournalID': item.journal.id,
|
|
'Ledger': {'LedgerID': item.journal.ledger.id, 'Name': item.journal.ledger.name,
|
|
'Designation': item.journal.ledger.designation,
|
|
'CostCentre': {'CostCentreID': item.journal.ledger.cost_centre.id,
|
|
'Name': item.journal.ledger.cost_centre.name}}}})
|
|
for item in voucher.service_charges:
|
|
employee = request.dbsession.query(Employee).filter(Employee.id == item.journal.ledger_id).first()
|
|
json_voucher['ServiceCharges'].append(
|
|
{'EmployeeID': item.journal.ledger_id, 'Name': item.journal.ledger.name,
|
|
'Designation': employee.designation,
|
|
'Department': item.journal.ledger.cost_centre.name, 'DaysWorked': item.days_worked, 'Points': item.points})
|
|
if len(json_voucher['ServiceCharges']) > 0:
|
|
json_voucher['ServiceCharge'] = [x.amount for x in voucher.journals if
|
|
x.ledger_id == Ledger.service_charge_id()][0]
|
|
for item in voucher.inventories:
|
|
text = "{0} ({1}) {2:.2f}@{3:.2f} from {4}".format(item.product.name, item.product.units,
|
|
item.batch.quantity_remaining, item.batch.rate,
|
|
item.batch.name.strftime('%d-%b-%Y'))
|
|
json_voucher['Inventories'].append(
|
|
{'InventoryID': item.id, 'Quantity': item.quantity, 'Rate': item.rate,
|
|
'Tax': item.tax, 'Discount': item.discount, 'Amount': item.amount,
|
|
'Product': {'ProductID': item.product.id, 'Name': item.product.full_name, 'Units': item.product.units,
|
|
'Price': item.rate},
|
|
'Batch': {'BatchID': item.batch.id, 'Name': text,
|
|
'QuantityRemaining': item.batch.quantity_remaining, 'Tax': item.batch.tax,
|
|
'Discount': item.batch.discount, 'Rate': item.batch.rate,
|
|
'Product': {'ProductID': item.batch.product.id, 'Name': item.batch.product.full_name}}})
|
|
images = request.dbsession.query(DbImage).filter(DbImage.resource_id == voucher.id).all()
|
|
for image in images:
|
|
resized = request.route_url('db_image', id=image.id, type='resized')
|
|
thumbnail = request.route_url('db_image', id=image.id, type='thumbnail')
|
|
json_voucher['Files'].append({'ID': image.id, 'Resized': resized, 'Thumbnail': thumbnail})
|
|
return json_voucher
|
|
|
|
|
|
def blank_voucher(info, dbsession):
|
|
if 'Type' not in info:
|
|
raise ValidationError('Voucher Type is null')
|
|
type = info['Type']
|
|
if 'Date' not in info:
|
|
raise ValidationError('Date cannot be null')
|
|
json_voucher = {'Type': type, 'Date': info['Date'], 'Posted': False, 'Narration': "", 'Journals': [],
|
|
'Inventories': []}
|
|
if type == 'Journal':
|
|
pass
|
|
elif type == 'Payment':
|
|
ledger = None
|
|
if info is not None and 'Account' in info and info['Account'] is not None:
|
|
ledger = dbsession.query(LedgerBase).filter(LedgerBase.id == uuid.UUID(info['Account'])).first()
|
|
if ledger is not None:
|
|
ledger = {'LedgerID': ledger.id, 'Name': ledger.name}
|
|
else:
|
|
ledger = LedgerBase.cash_in_hand()
|
|
json_voucher['Journals'].append({'Ledger': ledger, 'Amount': 0, 'Debit': -1})
|
|
elif type == 'Receipt':
|
|
ledger = None
|
|
if info is not None and 'Account' in info and info['Account'] is not None:
|
|
ledger = dbsession.query(LedgerBase).filter(LedgerBase.id == uuid.UUID(info['Account'])).first()
|
|
if ledger is not None:
|
|
ledger = {'LedgerID': ledger.id, 'Name': ledger.name}
|
|
else:
|
|
ledger = LedgerBase.cash_in_hand()
|
|
json_voucher['Journals'].append({'Ledger': ledger, 'Amount': 0, 'Debit': 1})
|
|
elif type == 'Purchase':
|
|
json_voucher['Journals'].append({'Ledger': LedgerBase.local_purchase(), 'Amount': 0, 'Debit': -1})
|
|
elif type == 'Purchase Return':
|
|
json_voucher['Journals'].append({'Ledger': LedgerBase.local_purchase(), 'Amount': 0, 'Debit': 1})
|
|
elif type == 'Issue':
|
|
if 'Source' in info and 'Destination' in info:
|
|
json_voucher['Journals'].append({'Ledger': {'LedgerID': LedgerBase.all_purchases()},
|
|
'Amount': 0,
|
|
'Debit': -1,
|
|
'CostCentre': {'CostCentreID': info['Source']}})
|
|
json_voucher['Journals'].append({'Ledger': {'LedgerID': LedgerBase.all_purchases()},
|
|
'Amount': 0,
|
|
'Debit': 1,
|
|
'CostCentre': {'CostCentreID': info['Destination']}})
|
|
elif 'Journals' not in info:
|
|
json_voucher['Journals'].append({'Ledger': {'LedgerID': LedgerBase.all_purchases()},
|
|
'Amount': 0,
|
|
'Debit': -1,
|
|
'CostCentre': {'CostCentreID': CostCentre.cost_centre_purchase()}})
|
|
json_voucher['Journals'].append({'Ledger': {'LedgerID': LedgerBase.all_purchases()},
|
|
'Amount': 0,
|
|
'Debit': 1,
|
|
'CostCentre': {'CostCentreID': CostCentre.cost_centre_kitchen()}})
|
|
else:
|
|
json_voucher['Date'] = info['Date']
|
|
for item in info['Journals']:
|
|
json_voucher['Journals'].append({'Ledger': {'LedgerID': item['Ledger']['LedgerID']},
|
|
'Amount': 0,
|
|
'Debit': item['Debit'],
|
|
'CostCentre': {'CostCentreID': item['CostCentre']['CostCentreID']}})
|
|
|
|
elif type == 'Salary Deduction':
|
|
json_voucher['SalaryDeductions'] = []
|
|
elif type == 'Service Charge':
|
|
json_voucher['ServiceCharges'], json_voucher['ServiceCharge'] = service_charge_employees(
|
|
info['Date'], dbsession
|
|
)
|
|
else:
|
|
raise ValidationError("Voucher of type \"{0}\" does not exist.".format(type))
|
|
json_voucher['Files'] = []
|
|
return json_voucher
|
|
|
|
|
|
def service_charge_employees(date, dbsession):
|
|
date = datetime.datetime.strptime(date, '%d-%b-%Y')
|
|
start_date = get_first_day(date)
|
|
finish_date = date + datetime.timedelta(1)
|
|
details = []
|
|
employees = dbsession.query(Employee) \
|
|
.filter(Employee.joining_date <= finish_date) \
|
|
.filter(or_(Employee.is_active, Employee.leaving_date >= start_date)) \
|
|
.order_by(Employee.cost_centre_id).order_by(Employee.designation).order_by(Employee.name).all()
|
|
for employee in employees:
|
|
att = dbsession.query(Attendance) \
|
|
.filter(Attendance.employee_id == employee.id) \
|
|
.filter(Attendance.date >= start_date) \
|
|
.filter(Attendance.date < finish_date) \
|
|
.filter(Attendance.is_valid == True) \
|
|
.all()
|
|
att = sum(map(lambda x: AttendanceType.by_id(x.attendance_type).value, att))
|
|
details.append({'EmployeeID': employee.id, 'Name': employee.name, 'Designation': employee.designation,
|
|
'Department': employee.cost_centre.name, 'DaysWorked': att, 'Points': employee.service_points})
|
|
|
|
amount = dbsession.query(func.sum(Journal.amount * Journal.debit)) \
|
|
.join(Journal.voucher) \
|
|
.filter(Voucher.date < finish_date) \
|
|
.filter(Voucher.type != VoucherType.by_name('Issue').id) \
|
|
.filter(Journal.ledger_id == Ledger.service_charge_id()) \
|
|
.scalar()
|
|
amount = 0 if amount is None else amount * Decimal(.7) * Decimal(.9) * -1
|
|
return details, amount
|
|
|
|
|
|
def get_edit_url(request, voucher):
|
|
if voucher.type == 1:
|
|
return request.route_url('journal_id', id=voucher.id)
|
|
elif voucher.type == 2:
|
|
return request.route_url('purchase_id', id=voucher.id)
|
|
elif voucher.type == 3:
|
|
return request.route_url('issue_id', id=voucher.id)
|
|
elif voucher.type == 4:
|
|
return request.route_url('payment_id', id=voucher.id)
|
|
elif voucher.type == 5:
|
|
return request.route_url('receipt_id', id=voucher.id)
|
|
elif voucher.type == 6:
|
|
return request.route_url('purchase_return_id', id=voucher.id)
|
|
elif voucher.type == 12:
|
|
return request.route_url('salary_deduction_id', id=voucher.id)
|
|
elif voucher.type == 13:
|
|
return request.route_url('service_charge_id', id=voucher.id)
|
|
else:
|
|
return '#'
|