Files
brewman/brewman/views/services/voucher/__init__.py
T
2018-05-15 14:38:30 +05:30

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 '#'