Feature: Automatic Crediting of Service Points. Plus now service points can be decimals
This commit is contained in:
@ -106,6 +106,7 @@ def main(global_config, **settings):
|
|||||||
|
|
||||||
config.add_route('attendance_report', '/AttendanceReport')
|
config.add_route('attendance_report', '/AttendanceReport')
|
||||||
config.add_route('api_credit_salary', '/api/CreditSalary')
|
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('employee_functions', '/EmployeeFunctions')
|
||||||
config.add_route('api_fingerprint', '/api/Fingerprint')
|
config.add_route('api_fingerprint', '/api/Fingerprint')
|
||||||
|
|
||||||
|
|||||||
@ -108,6 +108,8 @@ def fixtures(engine):
|
|||||||
uuid.UUID('ed2341bb-80b8-9649-90db-f9aaca183bb3'), True),
|
uuid.UUID('ed2341bb-80b8-9649-90db-f9aaca183bb3'), True),
|
||||||
Ledger(1, 'Staff Salary', 7, True, False, uuid.UUID('36f59436-522a-0746-ae94-e0f746bf6c0d'),
|
Ledger(1, 'Staff Salary', 7, True, False, uuid.UUID('36f59436-522a-0746-ae94-e0f746bf6c0d'),
|
||||||
uuid.UUID('5c2b54d0-c174-004d-a0d5-92cdaadcefa7'), True),
|
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'),
|
Ledger(1, 'Suspense', 4, True, False, uuid.UUID('36f59436-522a-0746-ae94-e0f746bf6c0d'),
|
||||||
uuid.UUID('3854e317-6f3b-5142-ab26-9c44d4cddd08'), True)]
|
uuid.UUID('3854e317-6f3b-5142-ab26-9c44d4cddd08'), True)]
|
||||||
|
|
||||||
|
|||||||
@ -347,6 +347,10 @@ class LedgerBase(Base):
|
|||||||
def salary(cls):
|
def salary(cls):
|
||||||
return {'LedgerID': '5c2b54d0-c174-004d-a0d5-92cdaadcefa7', 'Name': 'Staff Salary'}
|
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
|
@classmethod
|
||||||
def esi_pf_expense(cls):
|
def esi_pf_expense(cls):
|
||||||
return uuid.UUID('d2a1a286-e900-764b-a1a5-9f4b00dbb940')
|
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)
|
id = Column('LedgerID', GUID(), ForeignKey(LedgerBase.id), primary_key=True)
|
||||||
designation = Column('Designation', Unicode(255))
|
designation = Column('Designation', Unicode(255))
|
||||||
salary = Column('Salary', Integer)
|
salary = Column('Salary', Integer)
|
||||||
service_points = Column('ServicePoints', Integer)
|
service_points = Column('ServicePoints', Numeric(precision=5, scale=2))
|
||||||
joining_date = Column('JoiningDate', DateTime)
|
joining_date = Column('JoiningDate', DateTime)
|
||||||
leaving_date = Column('LeavingDate', DateTime)
|
leaving_date = Column('LeavingDate', DateTime)
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,37 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<h2>Credit Service Charge</h2>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="txtScMonth" class="col-md-2 control-label">Month</label>
|
||||||
|
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="txtScMonth" type="text" class="form-control" datepicker-popup="dd-MMM-yyyy"
|
||||||
|
datepicker-mode="month" is-open="scmOpen" ng-focus="scmOpen = true" ng-model="sc.Month"/>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button class="btn btn-default"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label for="txtScCreditDate" class="col-md-2 control-label">Credit Date</label>
|
||||||
|
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="txtScCreditDate" type="text" class="form-control" datepicker-popup="dd-MMM-yyyy"
|
||||||
|
is-open="sccdOpen" ng-focus="sccdOpen = true" ng-model="sc.CreditDate"/>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button class="btn btn-default"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<button class="btn btn-block btn-success" tan-click="creditServiceCharge()">Credit <i
|
||||||
|
class="glyphicon glyphicon-screenshot"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<h2>Attendance Record</h2>
|
<h2>Attendance Record</h2>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|||||||
@ -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 () {
|
$scope.attendanceRecordUrl = function () {
|
||||||
if (!$scope.record.StartDate || !$scope.record.FinishDate) {
|
if (!$scope.record.StartDate || !$scope.record.FinishDate) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import uuid
|
import uuid
|
||||||
|
from decimal import Decimal
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
from pyramid.response import Response, FileResponse
|
from pyramid.response import Response, FileResponse
|
||||||
from pyramid.security import authenticated_userid
|
from pyramid.security import authenticated_userid
|
||||||
@ -44,11 +45,11 @@ def save(request):
|
|||||||
raise ValidationError("Salary must be an integer >= 0")
|
raise ValidationError("Salary must be an integer >= 0")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
service_points = int(request.json_body['ServicePoints'])
|
service_points = round(Decimal(request.json_body['ServicePoints']), 2)
|
||||||
if service_points < 0:
|
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):
|
except (ValueError, KeyError):
|
||||||
raise ValidationError("Service Points must be an integer >= 0")
|
raise ValidationError("Service Points must be a decimal >= 0 and < 1000")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
joining_date = datetime.datetime.strptime(request.json_body['JoiningDate'], '%d-%b-%Y')
|
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")
|
raise ValidationError("Salary must be an integer >= 0")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
item.service_points = int(request.json_body['ServicePoints'])
|
item.service_points = round(Decimal(request.json_body['ServicePoints']), 2)
|
||||||
if item.service_points < 0:
|
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):
|
except (ValueError, KeyError):
|
||||||
raise ValidationError("Service Points must be an integer >= 0")
|
raise ValidationError("Service Points must be a decimal >= 0")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
item.joining_date = datetime.datetime.strptime(request.json_body['JoiningDate'], '%d-%b-%Y')
|
item.joining_date = datetime.datetime.strptime(request.json_body['JoiningDate'], '%d-%b-%Y')
|
||||||
|
|||||||
83
brewman/views/services/voucher/credit_service_charge.py
Normal file
83
brewman/views/services/voucher/credit_service_charge.py
Normal file
@ -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
|
||||||
Reference in New Issue
Block a user