From 7c54e4b0c762a8bda3c4de16f35cbbbd51b01331 Mon Sep 17 00:00:00 2001 From: tanshu Date: Thu, 25 Dec 2014 16:40:47 +0530 Subject: [PATCH] Feature: Automatic Crediting of Service Points. Plus now service points can be decimals --- brewman/__init__.py | 1 + brewman/models/__init__.py | 2 + brewman/models/master.py | 6 +- .../static/partial/employee-functions.html | 31 +++++++ brewman/static/scripts/employee-functions.js | 19 +++++ brewman/views/employee.py | 13 +-- .../services/voucher/credit_service_charge.py | 83 +++++++++++++++++++ 7 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 brewman/views/services/voucher/credit_service_charge.py diff --git a/brewman/__init__.py b/brewman/__init__.py index eb2d53d4..70e00b6a 100644 --- a/brewman/__init__.py +++ b/brewman/__init__.py @@ -106,6 +106,7 @@ def main(global_config, **settings): config.add_route('attendance_report', '/AttendanceReport') config.add_route('api_credit_salary', '/api/CreditSalary') + config.add_route('api_credit_service_charge', '/api/CreditServiceCharge') config.add_route('employee_functions', '/EmployeeFunctions') config.add_route('api_fingerprint', '/api/Fingerprint') diff --git a/brewman/models/__init__.py b/brewman/models/__init__.py index 279eb45e..1e6162e2 100644 --- a/brewman/models/__init__.py +++ b/brewman/models/__init__.py @@ -108,6 +108,8 @@ def fixtures(engine): uuid.UUID('ed2341bb-80b8-9649-90db-f9aaca183bb3'), True), Ledger(1, 'Staff Salary', 7, True, False, uuid.UUID('36f59436-522a-0746-ae94-e0f746bf6c0d'), uuid.UUID('5c2b54d0-c174-004d-a0d5-92cdaadcefa7'), True), + Ledger(2, 'Service Charges', 11, True, False, uuid.UUID('36f59436-522a-0746-ae94-e0f746bf6c0d'), + uuid.UUID('b7eff754-e8ba-e047-ab06-9132c15c7640'), True), Ledger(1, 'Suspense', 4, True, False, uuid.UUID('36f59436-522a-0746-ae94-e0f746bf6c0d'), uuid.UUID('3854e317-6f3b-5142-ab26-9c44d4cddd08'), True)] diff --git a/brewman/models/master.py b/brewman/models/master.py index 467f4d32..eee8d1c6 100644 --- a/brewman/models/master.py +++ b/brewman/models/master.py @@ -347,6 +347,10 @@ class LedgerBase(Base): def salary(cls): return {'LedgerID': '5c2b54d0-c174-004d-a0d5-92cdaadcefa7', 'Name': 'Staff Salary'} + @classmethod + def service_charge(cls): + return {'LedgerID': 'b7eff754-e8ba-e047-ab06-9132c15c7640', 'Name': 'Service Charges'} + @classmethod def esi_pf_expense(cls): return uuid.UUID('d2a1a286-e900-764b-a1a5-9f4b00dbb940') @@ -367,7 +371,7 @@ class Employee(LedgerBase): id = Column('LedgerID', GUID(), ForeignKey(LedgerBase.id), primary_key=True) designation = Column('Designation', Unicode(255)) salary = Column('Salary', Integer) - service_points = Column('ServicePoints', Integer) + service_points = Column('ServicePoints', Numeric(precision=5, scale=2)) joining_date = Column('JoiningDate', DateTime) leaving_date = Column('LeavingDate', DateTime) diff --git a/brewman/static/partial/employee-functions.html b/brewman/static/partial/employee-functions.html index 63e4ae2c..ca4a6b6e 100644 --- a/brewman/static/partial/employee-functions.html +++ b/brewman/static/partial/employee-functions.html @@ -19,6 +19,37 @@ +

Credit Service Charge

+ +
+ + +
+
+ + + + +
+
+ + +
+
+ + + + +
+
+
+ +
+

Attendance Record

diff --git a/brewman/static/scripts/employee-functions.js b/brewman/static/scripts/employee-functions.js index 3163568d..ef5e9e40 100644 --- a/brewman/static/scripts/employee-functions.js +++ b/brewman/static/scripts/employee-functions.js @@ -17,6 +17,25 @@ var EmployeeFunctionsController = ['$scope', '$http', 'asDateFilter', function ( }); }; + $scope.creditServiceCharge = function () { + if (!angular.isDate($scope.sc.Month)) { + return; + } + var ServiceChargeMonth = asDate($scope.sc.Month); + if (!angular.isDate($scope.sc.CreditDate)) { + return; + } + var CreditDate = asDate($scope.sc.CreditDate); + return $http. + post('/api/CreditServiceCharge', {Month: ServiceChargeMonth, CreditDate: CreditDate}). + success(function (data) { + $scope.toasts.push({Type: 'Success', Message: data.message}); + }). + error(function (errorMessage) { + $scope.toasts.push({Type: 'Danger', Message: errorMessage}); + }); + }; + $scope.attendanceRecordUrl = function () { if (!$scope.record.StartDate || !$scope.record.FinishDate) { return; diff --git a/brewman/views/employee.py b/brewman/views/employee.py index 1f3830e8..477f6cad 100644 --- a/brewman/views/employee.py +++ b/brewman/views/employee.py @@ -1,5 +1,6 @@ import datetime import uuid +from decimal import Decimal import pkg_resources from pyramid.response import Response, FileResponse from pyramid.security import authenticated_userid @@ -44,11 +45,11 @@ def save(request): raise ValidationError("Salary must be an integer >= 0") try: - service_points = int(request.json_body['ServicePoints']) + service_points = round(Decimal(request.json_body['ServicePoints']), 2) if service_points < 0: - raise ValidationError("Service Points must be an integer >= 0") + raise ValidationError("Service Points must be a decimal >= 0 and < 1000") except (ValueError, KeyError): - raise ValidationError("Service Points must be an integer >= 0") + raise ValidationError("Service Points must be a decimal >= 0 and < 1000") try: joining_date = datetime.datetime.strptime(request.json_body['JoiningDate'], '%d-%b-%Y') @@ -94,11 +95,11 @@ def update(request): raise ValidationError("Salary must be an integer >= 0") try: - item.service_points = int(request.json_body['ServicePoints']) + item.service_points = round(Decimal(request.json_body['ServicePoints']), 2) if item.service_points < 0: - raise ValidationError("Service Points must be an integer >= 0") + raise ValidationError("Service Points must be a decimal >= 0") except (ValueError, KeyError): - raise ValidationError("Service Points must be an integer >= 0") + raise ValidationError("Service Points must be a decimal >= 0") try: item.joining_date = datetime.datetime.strptime(request.json_body['JoiningDate'], '%d-%b-%Y') diff --git a/brewman/views/services/voucher/credit_service_charge.py b/brewman/views/services/voucher/credit_service_charge.py new file mode 100644 index 00000000..044d6ddc --- /dev/null +++ b/brewman/views/services/voucher/credit_service_charge.py @@ -0,0 +1,83 @@ +import datetime +import uuid +from decimal import Decimal +from pyramid.security import authenticated_userid +from pyramid.view import view_config +from sqlalchemy import or_, func +import transaction +from ....models import DBSession +from ....models.auth import User +from ....models.master import Employee, AttendanceType, Ledger +from ....models.validation_exception import TryCatchFunction +from ....models.voucher import Voucher, VoucherType, Attendance, Journal +from ..session import get_first_day, get_last_day + +__author__ = 'tanshu' + + +@view_config(request_method='POST', route_name='api_credit_service_charge', renderer='json', permission='Attendance') +@TryCatchFunction +def credit_sc(request): + user = User.by_id(uuid.UUID(authenticated_userid(request))) + month = datetime.datetime.strptime(request.json_body['Month'], '%d-%b-%Y') + credit_date = datetime.datetime.strptime(request.json_body['CreditDate'], '%d-%b-%Y') + + amount = balance(month) + start_date = get_first_day(month) + finish_date = get_last_day(month) + voucher = Voucher(date=credit_date, narration='Auto Generated Service Charge Entry', user_id=user.id, + type=VoucherType.by_name('Journal'), posted=True, poster_id=user.id) + DBSession.add(voucher) + for item in service_charge_journals(amount, start_date, finish_date): + voucher.journals.append(item) + DBSession.add(item) + transaction.commit() + return {'message': 'Salary Entry created'} + + +def service_charge_journals(amount, start_date, finish_date): + amount = amount * Decimal(.7) * Decimal(.9) + total_points = 0 + details = [] + finish_date = finish_date + datetime.timedelta(1) + journals = [] + employees = DBSession().query(Employee) \ + .filter(Employee.joining_date <= finish_date) \ + .filter(or_(Employee.is_active, Employee.leaving_date >= start_date)) \ + .order_by(Employee.costcenter_id).order_by(Employee.designation).order_by(Employee.name).all() + for employee in employees: + if employee.service_points != 0: + 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)) + points = Decimal(att) * employee.service_points + if points != 0: + total_points += points + details.append({'Employee': employee, 'Points': points}) + if total_points == 0: + raise ValueError("There is no data to credit service charges") + point_value = round(amount / total_points, 2) + amount = 0 + for item in details: + ee = item['Employee'] + employee_amount = round(point_value * item['Points']) + journal = Journal(amount=employee_amount, debit=-1, ledger_id=ee.id, cost_center_id=ee.costcenter_id) + journals.append(journal) + amount += employee_amount + sc = Ledger.by_id(uuid.UUID(Ledger.service_charge()['LedgerID'])) + journals.append(Journal(amount=amount, debit=1, ledger_id=sc.id, cost_center_id=sc.costcenter_id)) + return journals + + +def balance(date): + amount = DBSession.query(func.sum(Journal.amount * Journal.debit)) \ + .join(Journal.voucher) \ + .filter(Voucher.date < date + datetime.timedelta(1)) \ + .filter(Voucher.type != VoucherType.by_name('Issue').id) \ + .filter(Journal.ledger_id == uuid.UUID(Ledger.service_charge()['LedgerID'])) \ + .scalar() + return 0 if amount is None else amount * -1