Added salary deductions model / route.

Updated import queries to support deductions.
Added esi_pf expense and payable ledgers to LedgerBase.
This commit is contained in:
Tanshu 2012-11-28 13:28:16 +05:30
parent 517b56431a
commit f60cca97e6
21 changed files with 459 additions and 131 deletions

@ -92,6 +92,8 @@ def main(global_config, **settings):
config.add_route('issue_id', '/Issue/{id}') config.add_route('issue_id', '/Issue/{id}')
config.add_route('issue', '/Issue') config.add_route('issue', '/Issue')
config.add_route('issues_grid', '/Issues/Services/{date}') config.add_route('issues_grid', '/Issues/Services/{date}')
config.add_route('salary_deduction_id', '/SalaryDeduction/{id}')
config.add_route('salary_deduction', '/SalaryDeduction')
config.add_route('voucher', '/Voucher/{id}') config.add_route('voucher', '/Voucher/{id}')
config.add_route('voucher_new', '/Voucher') config.add_route('voucher_new', '/Voucher')

@ -227,6 +227,14 @@ class LedgerBase(Base):
def salary(cls): def salary(cls):
return {'LedgerID': '5c2b54d0-c174-004d-a0d5-92cdaadcefa7', 'Name': 'salary staff'} return {'LedgerID': '5c2b54d0-c174-004d-a0d5-92cdaadcefa7', 'Name': 'salary staff'}
@classmethod
def esi_pf_expense(cls):
return uuid.UUID('d2a1a286-e900-764b-a1a5-9f4b00dbb940')
@classmethod
def esi_pf_payable(cls):
return uuid.UUID('42277912-cc18-854b-b134-9f4b00dba419')
class Employee(LedgerBase): class Employee(LedgerBase):
__tablename__ = 'entities_employees' __tablename__ = 'entities_employees'

@ -6,11 +6,49 @@ from brewman.models.guidtype import GUID
from sqlalchemy import Column, Integer, Boolean, Unicode, DateTime, Numeric, ForeignKey, func from sqlalchemy import Column, Integer, Boolean, Unicode, DateTime, Numeric, ForeignKey, func
from sqlalchemy.orm import relationship, synonym from sqlalchemy.orm import relationship, synonym, backref
from brewman.models import Base, DBSession from brewman.models import Base, DBSession
from brewman.models.master import Product from brewman.models.master import Product
class VoucherType:
def __init__(self, id, name):
self.id = id
self.name = name
@classmethod
def list(cls):
list = []
list.append(VoucherType(1, 'Journal'))
list.append(VoucherType(2, 'Purchase'))
list.append(VoucherType(3, 'Issue'))
list.append(VoucherType(4, 'Payment'))
list.append(VoucherType(5, 'Receipt'))
list.append(VoucherType(6, 'Purchase Return'))
list.append(VoucherType(7, 'Opening Ledgers'))
list.append(VoucherType(8, 'Opening Inventories'))
list.append(VoucherType(9, 'Verification'))
list.append(VoucherType(10, 'Opening Balance'))
list.append(VoucherType(11, 'Closing Balance'))
list.append(VoucherType(12, 'Salary Deductions'))
return list
@classmethod
def by_name(cls, name):
list = cls.list()
for item in list:
if item.name == name:
return item
@classmethod
def by_id(cls, id):
list = cls.list()
for item in list:
if item.id == id:
return item
class Voucher(Base): class Voucher(Base):
__tablename__ = 'entities_vouchers' __tablename__ = 'entities_vouchers'
@ -30,6 +68,8 @@ class Voucher(Base):
journals = relationship('Journal', backref='voucher', cascade="delete, delete-orphan", cascade_backrefs=False) journals = relationship('Journal', backref='voucher', cascade="delete, delete-orphan", cascade_backrefs=False)
inventories = relationship('Inventory', backref='voucher', cascade="delete, delete-orphan", cascade_backrefs=False) inventories = relationship('Inventory', backref='voucher', cascade="delete, delete-orphan", cascade_backrefs=False)
salary_deductions = relationship('SalaryDeduction', backref='voucher', cascade="delete, delete-orphan",
cascade_backrefs=False)
def _get_type(self): def _get_type(self):
return self._type return self._type
@ -50,7 +90,8 @@ class Voucher(Base):
def __name__(self): def __name__(self):
return self.name return self.name
def __init__(self, date=None, reconcilliation_date=None, narration=None, posted=False, creation_date=None, last_edit_date=None, type=None, user_id=None, poster_id=None): def __init__(self, date=None, reconcilliation_date=None, narration='', posted=False, creation_date=None,
last_edit_date=None, type=None, user_id=None, poster_id=None):
self.date = date self.date = date
self.reconcilliation_date = reconcilliation_date self.reconcilliation_date = reconcilliation_date
self.narration = narration self.narration = narration
@ -113,42 +154,34 @@ class Journal(Base):
return DBSession.query(cls) return DBSession.query(cls)
class VoucherType: class SalaryDeduction(Base):
def __init__(self, id, name): __tablename__ = 'entities_salarydeductions'
id = Column('SalaryDeductionID', GUID(), primary_key=True, default=uuid.uuid4)
voucher_id = Column('VoucherID', GUID(), ForeignKey('entities_vouchers.VoucherID'))
journal_id = Column('JournalID', GUID(), ForeignKey('entities_journals.JournalID'))
gross_salary = Column('GrossSalary', Integer)
days_worked = Column('DaysWorked', Integer)
esi_ee = Column('EsiEmployee', Integer)
pf_ee = Column('PfEmployee', Integer)
esi_er = Column('EsiEmployer', Integer)
pf_er = Column('PfEmployer', Integer)
journal = relationship(Journal, backref=backref('salary_deduction', uselist=False), cascade=None,
cascade_backrefs=False)
def __init__(self, id=None, voucher_id=None, journal_id=None, journal=None, gross_salary=None, days_worked=None,
esi_ee=None, pf_ee=None, esi_er=None, pf_er=None):
self.id = id self.id = id
self.name = name self.voucher_id = voucher_id
self.journal_id = journal_id
@classmethod self.gross_salary = gross_salary
def list(cls): self.days_worked = days_worked
list = [] self.esi_ee = esi_ee
list.append(VoucherType(1, 'Journal')) self.pf_ee = pf_ee
list.append(VoucherType(2, 'Purchase')) self.esi_er = esi_er
list.append(VoucherType(3, 'Issue')) self.pf_er = pf_er
list.append(VoucherType(4, 'Payment')) if journal_id is None and journal is not None:
list.append(VoucherType(5, 'Receipt')) self.journal = journal
list.append(VoucherType(6, 'Purchase Return'))
list.append(VoucherType(7, 'Opening Ledgers'))
list.append(VoucherType(8, 'Opening Inventories'))
list.append(VoucherType(9, 'Verification'))
list.append(VoucherType(10, 'Opening Balance'))
list.append(VoucherType(11, 'Closing Balance'))
list.append(VoucherType(12, 'Salary Deductions'))
return list
@classmethod
def by_name(cls, name):
list = cls.list()
for item in list:
if item.name == name:
return item
@classmethod
def by_id(cls, id):
list = cls.list()
for item in list:
if item.id == id:
return item
class Inventory(Base): class Inventory(Base):

@ -18,15 +18,6 @@
<a class="btn btn-info" ng-href="{{attendanceRecordUrl()}}" target="_self">Download <i class="icon-download-alt icon-white"></i></a> <a class="btn btn-info" ng-href="{{attendanceRecordUrl()}}" target="_self">Download <i class="icon-download-alt icon-white"></i></a>
</div> </div>
</div> </div>
<legend>Credit Esi / Pf</legend>
<div class="control-group">
<label for="txtEsiPfMonth" class="control-label">Month</label>
<div class="controls">
<datepicker id="txtEsiPfMonth" model="esiPfMonth" ng-model="esiPfMonth"></datepicker>
<button class="btn" ng-click="creditEsiPf()">Credit</button>
</div>
</div>
<legend>Upload Fingerprints</legend> <legend>Upload Fingerprints</legend>
<div class="control-group"> <div class="control-group">
<label for="uploadFingerprints" class="control-label"></label> <label for="uploadFingerprints" class="control-label"></label>

@ -1,5 +1,5 @@
<form method="post" autocomplete="off" class="form-horizontal"> <form method="post" autocomplete="off" class="form-horizontal">
<legend>Credit Esi Pf</legend> <legend>Credit Esi / Pf</legend>
<div class="control-group"> <div class="control-group">
<label for="txtDate" class="control-label">Date</label> <label for="txtDate" class="control-label">Date</label>
@ -35,14 +35,13 @@
<th>Esi ER</th> <th>Esi ER</th>
<th>Pf ER</th> <th>Pf ER</th>
<th>Delete</th> <th>Delete</th>
<th>Delete</th>
</tr> </tr>
</thead> </thead>
<tbody id="tbodyMain"> <tbody id="tbodyMain">
<tr ng-repeat="deduction in voucher.SalaryDeductions"> <tr ng-repeat="deduction in voucher.SalaryDeductions">
<td>{{deduction.Employee.Name}}</td> <td>{{deduction.Journal.Ledger.Name}}</td>
<td>{{deduction.Employee.Designation}}</td> <td>{{deduction.Journal.Ledger.Designation}}</td>
<td>{{deduction.Employee.Department}}</td> <td>{{deduction.Journal.Ledger.CostCenter.Name}}</td>
<td>{{deduction.GrossSalary}}</td> <td>{{deduction.GrossSalary}}</td>
<td>{{deduction.DaysWorked}}</td> <td>{{deduction.DaysWorked}}</td>
<td>{{deduction.EsiEmployee}}</td> <td>{{deduction.EsiEmployee}}</td>
@ -61,14 +60,6 @@
</table> </table>
</div> </div>
</div> </div>
<div class="control-group">
<label for="txtNarration" class="control-label">Narration</label>
<div class="controls">
<textarea rows="2" cols="20" id="txtNarration"
class="non-search-box" ng-model="voucher.Narration"></textarea>
</div>
</div>
<div class="form-actions"> <div class="form-actions">
<button class="btn btn-primary" ng-click="save()" <button class="btn btn-primary" ng-click="save()"
ng-disabled="preventAlteration(voucher)">{{voucher.VoucherID | save_button}} ng-disabled="preventAlteration(voucher)">{{voucher.VoucherID | save_button}}

@ -1,6 +1,6 @@
'use strict'; 'use strict';
function EmployeeFunctionsCtrl($scope, $http) { var EmployeeFunctionsCtrl = ['$scope', '$http', function ($scope, $http) {
$scope.record = {}; $scope.record = {};
$scope.creditSalary = function () { $scope.creditSalary = function () {
$http. $http.
@ -12,9 +12,6 @@ function EmployeeFunctionsCtrl($scope, $http) {
$scope.toasts.push({Type:'Error', Message:errorMessage}); $scope.toasts.push({Type:'Error', Message:errorMessage});
}); });
}; };
$scope.creditEsiPf = function () {
$scope.toasts.push({Type:'Error', Message:'Not Implemented Yet'});
};
$scope.attendanceRecordUrl = function () { $scope.attendanceRecordUrl = function () {
var url = '/AttendanceReport?StartDate=' + $scope.record.Start + "&FinishDate=" + $scope.record.Finish; var url = '/AttendanceReport?StartDate=' + $scope.record.Start + "&FinishDate=" + $scope.record.Finish;
return url; return url;
@ -23,4 +20,162 @@ function EmployeeFunctionsCtrl($scope, $http) {
$scope.toasts.push({Type:'Error', Message:'Not Implemented Yet'}); $scope.toasts.push({Type:'Error', Message:'Not Implemented Yet'});
}; };
} }]
var SalaryDeductionCtrl = ['$scope', '$location', 'voucher', function ($scope, $location, voucher) {
$scope.voucher = voucher;
$scope.name = '';
function getOldItem(ledgerID, items) {
for (var i = 0, l = items.length; i < l; i++) {
if (items[i].Journal.Ledger.LedgerID === ledgerID) {
return journals[i];
}
}
}
function daysInMonthFunction(date) {
var months = {
Jan:1,
Feb:2,
Mar:3,
Apr:4,
May:5,
Jun:6,
Jul:7,
Aug:8,
Sep:9,
Oct:10,
Nov:11,
Dev:12
}
if (!date.match(/^\d{2}-[\w]{3}-[\d]{4}$/g)) {
return;
}
var parts = date.split("-");
return new Date(parseInt(parts[2]), months[parts[1]], 0).getDate();
}
function getEsi(grossSalary, daysWorked, daysInMonth) {
var limit = 15000,
employee_rate = .0175,
employer_rate = .0475;
var employee = (grossSalary > limit) ? 0 : Math.ceil(employee_rate * grossSalary * daysWorked / daysInMonth)
var employer = (grossSalary > limit) ? 0 : Math.ceil(employer_rate * grossSalary * daysWorked / daysInMonth)
return {ee:employee, er:employer, both:employee + employer};
}
function getPf(grossSalary, daysWorked, daysInMonth) {
var limit = 6500,
employee_rate = .12,
employer_rate = .12 + .011 + .005 + .0001;
var employee = (grossSalary > limit) ? 0 : Math.ceil(employee_rate * grossSalary * daysWorked / daysInMonth)
var employer = (grossSalary > limit) ? 0 : Math.ceil(employer_rate * grossSalary * daysWorked / daysInMonth)
return {ee:employee, er:employer, both:employee + employer};
}
$scope.add = function () {
var oldJournal = getOldItem($scope.employee.LedgerID, this.voucher.SalaryDeductions),
grossSalary = parseInt($scope.grossSalary),
daysWorked = parseInt($scope.daysWorked),
daysInMonth = daysInMonthFunction($scope.voucher.Date),
esi = getEsi(grossSalary, daysWorked, daysInMonth),
pf = getPf(grossSalary, daysWorked, daysInMonth);
if (typeof oldJournal !== 'undefined') {
$scope.toasts.push({Type:'Error', Message:'Employee has already been added!'});
} else {
this.voucher.SalaryDeductions.push(
{
Journal:{Ledger:$scope.employee},
GrossSalary:grossSalary,
DaysWorked:daysWorked,
EsiEmployee:esi.ee,
PfEmployee:pf.ee,
EsiEmployer:esi.er,
PfEmployer:pf.er
}
)
;
}
delete $scope.employee;
delete $scope.grossSalary;
delete $scope.daysWorked;
$('#txtEmployee').val('');
$('#txtEmployee').focus();
};
$scope.remove = function (deduction) {
var index = this.voucher.SalaryDeductions.indexOf(deduction);
this.voucher.SalaryDeductions.splice(index, 1);
};
$scope.preventAlteration = function (voucher) {
if (typeof $scope.perms === 'undefined') {
return false;
} else if (typeof voucher.VoucherID === 'undefined') {
return !$scope.perms['Salary Deduction'];
} else if (voucher.Posted && !$scope.perms['Edit Posted Vouchers']) {
return true;
} else if (voucher.User.UserID != $scope.auth.UserID && !$scope.perms["Edit Other User's Vouchers"]) {
return true;
} else {
return false;
}
};
$scope.get = function (voucherid) {
$scope.voucher = Voucher.get({VoucherID:voucherid}, function (u, putResponseHeaders) {
$scope.toasts.push({Type:'Success', Message:''});
}, function (data, status) {
$scope.toasts.push({Type:'Error', Message:data.data});
});
};
$scope.save = function () {
$scope.voucher.$save({type:'Salary Deduction'}, function (u, putResponseHeaders) {
$scope.toasts.push({Type:'Success', Message:''});
$location.path('/SalaryDeduction/' + u.VoucherID);
}, function (data, status) {
$scope.toasts.push({Type:'Error', Message:data.data});
});
};
$scope.delete = function () {
$scope.voucher.$delete(function (u, putResponseHeaders) {
$scope.toasts.push({Type:'Success', Message:''});
$location.path('/SalaryDeduction').replace();
}, function (data, status) {
$scope.toasts.push({Type:'Error', Message:data.data});
});
};
$scope.post = function () {
$scope.voucher.$post(function (u, putResponseHeaders) {
$scope.toasts.push({Type:'Success', Message:''});
}, function (data, status) {
$scope.toasts.push({Type:'Error', Message:data.data});
});
};
}]
SalaryDeductionCtrl.resolve = {
voucher:['$q', '$route', 'Voucher', function ($q, $route, Voucher) {
var deferred = $q.defer();
var id = $route.current.params.id;
var successCb = function (result) {
deferred.resolve(result);
};
if (typeof id === 'undefined') {
Voucher.get({type:'Salary Deduction'}, successCb);
} else {
Voucher.get({id:id}, successCb);
}
return deferred.promise;
}]
};

@ -25,6 +25,9 @@ var overlord = angular.module('overlord', ['overlord.directive', 'overlord.filte
when('/Issue', {templateUrl:'/partial/issue.html', controller:IssueCtrl, resolve:IssueCtrl.resolve}). when('/Issue', {templateUrl:'/partial/issue.html', controller:IssueCtrl, resolve:IssueCtrl.resolve}).
when('/Issue/:id', {templateUrl:'/partial/issue.html', controller:IssueCtrl, resolve:IssueCtrl.resolve}). when('/Issue/:id', {templateUrl:'/partial/issue.html', controller:IssueCtrl, resolve:IssueCtrl.resolve}).
when('/SalaryDeduction', {templateUrl:'/partial/salary-deduction.html', controller:SalaryDeductionCtrl, resolve:SalaryDeductionCtrl.resolve}).
when('/SalaryDeduction/:id', {templateUrl:'/partial/salary-deduction.html', controller:SalaryDeductionCtrl, resolve:SalaryDeductionCtrl.resolve}).
when('/Ledger', {templateUrl:'/partial/ledger.html', controller:LedgerCtrl, resolve:LedgerCtrl.resolve}). when('/Ledger', {templateUrl:'/partial/ledger.html', controller:LedgerCtrl, resolve:LedgerCtrl.resolve}).
when('/Ledger/:id', {templateUrl:'/partial/ledger.html', controller:LedgerCtrl, resolve:LedgerCtrl.resolve}). when('/Ledger/:id', {templateUrl:'/partial/ledger.html', controller:LedgerCtrl, resolve:LedgerCtrl.resolve}).

@ -138,6 +138,7 @@
<li><a href="/Attendance">Attendance</a></li> <li><a href="/Attendance">Attendance</a></li>
<li><a href="/EmployeeAttendance">Employee Attendance</a></li> <li><a href="/EmployeeAttendance">Employee Attendance</a></li>
<li><a href="/EmployeeFunctions">Employee Functions</a></li> <li><a href="/EmployeeFunctions">Employee Functions</a></li>
<li><a href="/SalaryDeduction">Credit Esi / Pf</a></li>
<li><a href="/AccountsTesting/Employees/SalarySheet.aspx">Salary Sheet</a></li> <li><a href="/AccountsTesting/Employees/SalarySheet.aspx">Salary Sheet</a></li>
<li><a href="/AccountsTesting/Employees/PaymentSheet.aspx">Employee Payment Sheet</a></li> <li><a href="/AccountsTesting/Employees/PaymentSheet.aspx">Employee Payment Sheet</a></li>
<li><a href="/features/elements.html">Display Salary Sheet</a></li> <li><a href="/features/elements.html">Display Salary Sheet</a></li>

@ -5,7 +5,7 @@ from pyramid.response import Response
from pyramid.view import view_config from pyramid.view import view_config
import transaction import transaction
from brewman.models.master import CostCenter, Employee from brewman.models.master import CostCenter, Employee, LedgerBase
from brewman.models.validation_exception import ValidationError from brewman.models.validation_exception import ValidationError
from brewman.views.account import ledger_list from brewman.views.account import ledger_list
@ -14,7 +14,8 @@ from brewman.views.account import ledger_list
permission='Employees') permission='Employees')
@view_config(request_method='GET', route_name='employee', renderer='brewman:templates/angular_base.mako', xhr=False, @view_config(request_method='GET', route_name='employee', renderer='brewman:templates/angular_base.mako', xhr=False,
permission='Employees') permission='Employees')
@view_config(request_method='GET', route_name='employee_functions', renderer='brewman:templates/angular_base.mako', xhr=False, @view_config(request_method='GET', route_name='employee_functions', renderer='brewman:templates/angular_base.mako',
xhr=False,
permission='Employees') permission='Employees')
def html(request): def html(request):
return {} return {}
@ -98,10 +99,19 @@ def show_list(request):
permission='Authenticated') permission='Authenticated')
def show_term(request): def show_term(request):
filter = request.GET.get('term', None) filter = request.GET.get('term', None)
filter = filter if filter is not None and filter is not '' else None filter = None if filter == '' else filter
count = request.GET.get('count', None) count = request.GET.get('count', None)
count = None if count is None or count == '' else int(count) count = None if count is None or count == '' else int(count)
return ledger_list(10, filter, count)
list = []
for index, item in enumerate(LedgerBase.list(10, filter)):
list.append({'label': item.name,
'model': {'LedgerID': item.id, 'Name': item.name, 'Designation': item.designation,
'CostCenter': {'CostCenterID': item.costcenter.id,
'Name': item.costcenter.name}}})
if count is not None and index == count - 1:
break
return list
def employee_info(id): def employee_info(id):

@ -5,7 +5,7 @@ from pyramid.view import view_config
from brewman.models.voucher import Voucher, Journal, VoucherType from brewman.models.voucher import Voucher, Journal, VoucherType
from brewman.views.services.session import session_period_finish, session_period_start from brewman.views.services.session import session_period_finish, session_period_start
from brewman.views.transactions import get_edit_url from brewman.views.services.voucher import get_edit_url
@view_config(request_method='GET', route_name='daybook', renderer='brewman:templates/angular_base.mako', @view_config(request_method='GET', route_name='daybook', renderer='brewman:templates/angular_base.mako',
xhr=False, permission='Daybook') xhr=False, permission='Daybook')

@ -10,7 +10,7 @@ from brewman.models.master import LedgerBase
from brewman.models.voucher import Voucher, Journal, VoucherType from brewman.models.voucher import Voucher, Journal, VoucherType
from brewman.views.services.session import session_period_start, session_period_finish from brewman.views.services.session import session_period_start, session_period_finish
from brewman.views.transactions import get_edit_url from brewman.views.services.voucher import get_edit_url
@view_config(request_method='GET', route_name='ledger_id', renderer='brewman:templates/angular_base.mako', @view_config(request_method='GET', route_name='ledger_id', renderer='brewman:templates/angular_base.mako',
xhr=False, permission='Ledger') xhr=False, permission='Ledger')

@ -10,7 +10,7 @@ from brewman.models.master import Product, CostCenter
from brewman.models.voucher import Voucher, Journal, VoucherType, Inventory from brewman.models.voucher import Voucher, Journal, VoucherType, Inventory
from brewman.views.services.session import session_period_start, session_period_finish from brewman.views.services.session import session_period_start, session_period_finish
from brewman.views.transactions import get_edit_url from brewman.views.services.voucher import get_edit_url
@view_config(request_method='GET', route_name='product_ledger_id', renderer='brewman:templates/angular_base.mako', @view_config(request_method='GET', route_name='product_ledger_id', renderer='brewman:templates/angular_base.mako',
xhr=False, permission='Product Ledger') xhr=False, permission='Product Ledger')

@ -8,7 +8,7 @@ from brewman.models.master import CostCenter, LedgerType, LedgerBase
from brewman.models.voucher import Voucher, Journal, VoucherType, Inventory from brewman.models.voucher import Voucher, Journal, VoucherType, Inventory
from brewman.views.services.session import session_period_start, session_period_finish from brewman.views.services.session import session_period_start, session_period_finish
from brewman.views.transactions import get_edit_url from brewman.views.services.voucher import get_edit_url
@view_config(request_method='GET', route_name='purchase_entries', renderer='brewman:templates/angular_base.mako', @view_config(request_method='GET', route_name='purchase_entries', renderer='brewman:templates/angular_base.mako',
xhr=False, permission='Purchase Entries') xhr=False, permission='Purchase Entries')

@ -3,7 +3,7 @@ from sqlalchemy.orm import joinedload_all
from pyramid.view import view_config from pyramid.view import view_config
from brewman.models.voucher import Voucher, Journal, VoucherType from brewman.models.voucher import Voucher, Journal, VoucherType
from brewman.views.transactions import get_edit_url from brewman.views.services.voucher import get_edit_url
@view_config(request_method='GET', route_name='unposted', renderer='brewman:templates/angular_base.mako', @view_config(request_method='GET', route_name='unposted', renderer='brewman:templates/angular_base.mako',
xhr=False, permission='Post Vouchers') xhr=False, permission='Post Vouchers')

@ -88,14 +88,3 @@ def check_client(client, otp, client_name, user):
client.name = client_name client.name = client_name
transaction.commit() transaction.commit()
return True, None return True, None
def get_first_day(dt, d_years=0, d_months=0):
# d_years, d_months are "deltas" to apply to dt
y, m = dt.year + d_years, dt.month + d_months
a, m = divmod(m - 1, 12)
return date(y + a, m + 1, 1)
def get_last_day(dt):
return get_first_day(dt, 0, 1) + timedelta(-1)

@ -1,4 +1,5 @@
import uuid import uuid
from datetime import date
from pyramid.response import Response from pyramid.response import Response
from pyramid.security import authenticated_userid from pyramid.security import authenticated_userid
from pyramid.view import view_config from pyramid.view import view_config
@ -15,6 +16,37 @@ from brewman.views.services.voucher.purchase import purchase_create_voucher, pur
__author__ = 'tanshu' __author__ = 'tanshu'
@view_config(request_method='GET', route_name='journal_id', renderer='brewman:templates/angular_base.mako',
permission='Journal')
@view_config(request_method='GET', route_name='journal', renderer='brewman:templates/angular_base.mako',
permission='Journal')
@view_config(request_method='GET', route_name='payment_id', renderer='brewman:templates/angular_base.mako',
permission='Payment')
@view_config(request_method='GET', route_name='payment', renderer='brewman:templates/angular_base.mako',
permission='Payment')
@view_config(request_method='GET', route_name='receipt_id', renderer='brewman:templates/angular_base.mako',
permission='Receipt')
@view_config(request_method='GET', route_name='receipt', renderer='brewman:templates/angular_base.mako',
permission='Receipt')
@view_config(request_method='GET', route_name='purchase_id', renderer='brewman:templates/angular_base.mako',
permission='Purchase')
@view_config(request_method='GET', route_name='purchase', renderer='brewman:templates/angular_base.mako',
permission='Purchase')
@view_config(request_method='GET', route_name='purchase_return_id', renderer='brewman:templates/angular_base.mako',
permission='Purchase Return')
@view_config(request_method='GET', route_name='purchase_return', renderer='brewman:templates/angular_base.mako',
permission='Purchase Return')
@view_config(request_method='GET', route_name='issue_id', renderer='brewman:templates/angular_base.mako',
permission='Issue')
@view_config(request_method='GET', route_name='issue', renderer='brewman:templates/angular_base.mako',
permission='Issue')
@view_config(request_method='GET', route_name='salary_deduction_id', renderer='brewman:templates/angular_base.mako',
permission='Issue')
@view_config(request_method='GET', route_name='salary_deduction', renderer='brewman:templates/angular_base.mako',
permission='Issue')
def journal_get(request):
return {}
@view_config(request_method='POST', route_name='voucher', request_param='post', renderer='json', xhr=True, @view_config(request_method='POST', route_name='voucher', request_param='post', renderer='json', xhr=True,
permission='Post Vouchers') permission='Post Vouchers')
@ -115,6 +147,20 @@ def voucher_info(voucher):
json_voucher['Journals'].append({'JournalID': item.id, 'Debit': item.debit, 'Amount': item.amount, json_voucher['Journals'].append({'JournalID': item.id, 'Debit': item.debit, 'Amount': item.amount,
'Ledger': {'LedgerID': item.ledger.id, 'Name': item.ledger.name}, 'Ledger': {'LedgerID': item.ledger.id, 'Name': item.ledger.name},
'CostCenter': {'CostCenterID': item.cost_center_id}}) 'CostCenter': {'CostCenterID': item.cost_center_id}})
for item in voucher.salary_deductions:
json_voucher['SalaryDeductions'] = []
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,
'CostCenter': {'CostCenterID': item.journal.ledger.costcenter.id,
'Name': item.journal.ledger.costcenter.name}}}})
for item in voucher.inventories: for item in voucher.inventories:
json_voucher['Inventories'].append( json_voucher['Inventories'].append(
{'InventoryID': item.id, 'Quantity': item.quantity, 'Rate': item.rate, {'InventoryID': item.id, 'Quantity': item.quantity, 'Rate': item.rate,
@ -172,6 +218,24 @@ def blank_voucher(type=None, date=None, additionalInfo=None):
'Debit': item['Debit'], 'Debit': item['Debit'],
'CostCenter': {'CostCenterID': item['CostCenter']['CostCenterID']}}) 'CostCenter': {'CostCenterID': item['CostCenter']['CostCenterID']}})
elif type == 'Salary Deduction':
json_voucher['SalaryDeductions'] = []
else: else:
raise ValidationError("Voucher of type \"{0}\" does not exist.".format(type)) raise ValidationError("Voucher of type \"{0}\" does not exist.".format(type))
return json_voucher return json_voucher
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)
else:
return '#'

@ -35,6 +35,11 @@ class blank_voucher_view(object):
return self.get_blank() return self.get_blank()
@view_config(request_param='type=Salary Deduction', permission='Purchase Return')
def purchase_return(self):
return self.get_blank()
@view_config(request_param='type=Issue', permission='Issue') @view_config(request_param='type=Issue', permission='Issue')
def issue(self): def issue(self):
voucher_type = self.request.GET.get('type', None) voucher_type = self.request.GET.get('type', None)

@ -0,0 +1,109 @@
import datetime
from decimal import Decimal
from math import ceil
import uuid
from brewman.models import DBSession
from brewman.models.master import LedgerBase, Employee
from brewman.models.operations import journals_valid
from brewman.models.validation_exception import ValidationError
from brewman.models.voucher import Journal, Voucher, VoucherType, SalaryDeduction
from brewman.views.services.session import get_last_day
__author__ = 'tanshu'
def salary_deduction_create_voucher(json, user):
dt = get_last_day(datetime.datetime.strptime(json['Date'], '%d-%b-%Y'))
days_in_month = dt.day
voucher = Voucher(date=dt, user_id=user.id, type=VoucherType.by_id(12))
DBSession.add(voucher)
exp, total = 0, 0
for item in json['SalaryDeductions']:
item_exp, item_total = add_salary_deduction(item, days_in_month, voucher)
exp += item_exp
total += item_total
ledger = LedgerBase.by_id(LedgerBase.esi_pf_expense())
journal = Journal(amount=exp, debit=1, ledger_id=ledger.id, cost_center_id=ledger.costcenter_id)
DBSession.add(journal)
voucher.journals.append(journal)
ledger = LedgerBase.by_id(LedgerBase.esi_pf_payable())
journal = Journal(amount=total, debit=-1, ledger_id=ledger.id, cost_center_id=ledger.costcenter_id)
DBSession.add(journal)
voucher.journals.append(journal)
journals_valid(voucher)
return voucher
def salary_deduction_update_voucher(voucher, json, user):
dt = get_last_day(datetime.datetime.strptime(json['Date'], '%d-%b-%Y'))
if dt != voucher.date:
raise ValidationError("Date Cannot be changed for Salary Deduction voucher!")
days_in_month = voucher.date.day
voucher.user_id = user.id
voucher.posted = False
voucher.last_edit_date = datetime.datetime.now()
newDeductions = json['SalaryDeductions']
exp, total, journals = 0, 0, []
for i in range(len(voucher.salary_deductions), 0, -1):
item = voucher.salary_deductions[i - 1]
found = False
for i in range(len(newDeductions), 0, -1):
j = newDeductions[i - 1]
if 'SalaryDeductionID' in j and item.id == uuid.UUID(j['SalaryDeductionID']):
journals.append(item.journal.id)
exp += item.esi_er + item.pf_er
total += item.esi_ee + item.pf_ee + item.esi_er + item.pf_er
newDeductions.remove(j)
break
if not found:
voucher.salary_deductions.remove(item)
voucher.journals.remove(item.journal)
for j in newDeductions:
item_exp, item_total = add_salary_deduction(j, days_in_month, voucher)
exp += item_exp
total += item_total
journal = [i for i in voucher.journals if i.ledger_id == LedgerBase.esi_pf_expense()]
journal = journal[0]
journal.amount = exp
journal = [i for i in voucher.journals if i.ledger_id == LedgerBase.esi_pf_payable()]
journal = journal[0]
journal.amount = total
journals_valid(voucher)
return voucher
def add_salary_deduction(item, days_in_month, voucher):
ledger = Employee.by_id(uuid.UUID(item['Journal']['Ledger']['LedgerID']))
gross_salary = int(item['GrossSalary'])
days_worked = int(item['DaysWorked'])
esi_ee, esi_er, esi_both = esi_contribution(gross_salary, days_worked, days_in_month)
pf_ee, pf_er, pf_both = pf_contribution(gross_salary, days_worked, days_in_month)
journal = Journal(amount=esi_ee + pf_ee, debit=1, ledger_id=ledger.id, cost_center_id=ledger.costcenter_id)
sd = SalaryDeduction(journal=journal, gross_salary=gross_salary, days_worked=days_worked, esi_ee=esi_ee,
pf_ee=pf_ee, esi_er=esi_er, pf_er=pf_er)
voucher.journals.append(journal)
voucher.salary_deductions.append(sd)
DBSession.add(journal)
DBSession.add(sd)
return esi_er + pf_er, esi_both + pf_both
def esi_contribution(gross_salary, days_worked, days_in_month):
limit = 15000
employee_rate = .0175
employer_rate = .0475
employee = 0 if gross_salary > limit else ceil(employee_rate * gross_salary * days_worked / days_in_month)
employer = 0 if gross_salary > limit else ceil(employer_rate * gross_salary * days_worked / days_in_month)
return employee, employer, employee + employer
def pf_contribution(gross_salary, days_worked, days_in_month):
limit = 6500
employee_rate = .12
employer_rate = .12 + .011 + .005 + .0001
employee = 0 if gross_salary > limit else ceil(employee_rate * gross_salary * days_worked / days_in_month)
employer = 0 if gross_salary > limit else ceil(employer_rate * gross_salary * days_worked / days_in_month)
return employee, employer, employee + employer

@ -9,6 +9,7 @@ from brewman.models.voucher import Voucher
from brewman.views.services.session import session_current_date_set from brewman.views.services.session import session_current_date_set
from brewman.views.services.voucher import voucher_info, journal_create_voucher, purchase_create_voucher, issue_create_voucher from brewman.views.services.voucher import voucher_info, journal_create_voucher, purchase_create_voucher, issue_create_voucher
from brewman.views.services.voucher.purchase_return import purchase_return_create_voucher from brewman.views.services.voucher.purchase_return import purchase_return_create_voucher
from brewman.views.services.voucher.salary_deduction import salary_deduction_create_voucher
__author__ = 'tanshu' __author__ = 'tanshu'
@ -49,6 +50,10 @@ class save_voucher(object):
def issue(self): def issue(self):
return self.save() return self.save()
@view_config(request_param='type=Salary Deduction', permission='Salary Deduction')
def salary_deduction(self):
return self.save()
def save(self): def save(self):
try: try:
if self.json['Type'] in ['Journal', 'Payment', 'Receipt']: if self.json['Type'] in ['Journal', 'Payment', 'Receipt']:
@ -59,6 +64,8 @@ class save_voucher(object):
voucher = purchase_return_create_voucher(self.json, self.user) voucher = purchase_return_create_voucher(self.json, self.user)
elif self.json['Type'] in ['Issue']: elif self.json['Type'] in ['Issue']:
voucher = issue_create_voucher(self.json, self.user) voucher = issue_create_voucher(self.json, self.user)
elif self.json['Type'] in ['Salary Deduction']:
voucher = salary_deduction_create_voucher(self.json, self.user)
transaction.commit() transaction.commit()
session_current_date_set(self.request,self.json['Date']) session_current_date_set(self.request,self.json['Date'])
return voucher_info(Voucher.by_id(voucher.id)) return voucher_info(Voucher.by_id(voucher.id))

@ -10,6 +10,7 @@ from brewman.models.voucher import Voucher
from brewman.views.services.session import session_current_date_set from brewman.views.services.session import session_current_date_set
from brewman.views.services.voucher import voucher_info, issue_update_voucher, purchase_update_voucher, journal_update_voucher from brewman.views.services.voucher import voucher_info, issue_update_voucher, purchase_update_voucher, journal_update_voucher
from brewman.views.services.voucher.purchase_return import purchase_return_update_voucher from brewman.views.services.voucher.purchase_return import purchase_return_update_voucher
from brewman.views.services.voucher.salary_deduction import salary_deduction_update_voucher
__author__ = 'tanshu' __author__ = 'tanshu'
@ -64,6 +65,10 @@ class update_voucher(object):
def issue(self): def issue(self):
return self.update() return self.update()
@view_config(request_param='type=Salary Deduction', permission='Salary Deduction')
def salary_deduction(self):
return self.update()
def update(self): def update(self):
if self.error is not None: if self.error is not None:
return self.error return self.error
@ -76,6 +81,8 @@ class update_voucher(object):
voucher = purchase_return_update_voucher(self.voucher, self.json, self.user) voucher = purchase_return_update_voucher(self.voucher, self.json, self.user)
elif self.json['Type'] in ['Issue']: elif self.json['Type'] in ['Issue']:
voucher = issue_update_voucher(self.voucher, self.json, self.user) voucher = issue_update_voucher(self.voucher, self.json, self.user)
elif self.json['Type'] in ['Salary Deduction']:
voucher = salary_deduction_update_voucher(self.voucher, self.json, self.user)
transaction.commit() transaction.commit()
session_current_date_set(self.request,self.json['Date']) session_current_date_set(self.request,self.json['Date'])
return voucher_info(Voucher.by_id(voucher.id)) return voucher_info(Voucher.by_id(voucher.id))

@ -1,47 +0,0 @@
from datetime import date
from pyramid.view import view_config
__author__ = 'tanshu'
@view_config(request_method='GET', route_name='journal_id', renderer='brewman:templates/angular_base.mako',
permission='Journal')
@view_config(request_method='GET', route_name='journal', renderer='brewman:templates/angular_base.mako',
permission='Journal')
@view_config(request_method='GET', route_name='payment_id', renderer='brewman:templates/angular_base.mako',
permission='Payment')
@view_config(request_method='GET', route_name='payment', renderer='brewman:templates/angular_base.mako',
permission='Payment')
@view_config(request_method='GET', route_name='receipt_id', renderer='brewman:templates/angular_base.mako',
permission='Receipt')
@view_config(request_method='GET', route_name='receipt', renderer='brewman:templates/angular_base.mako',
permission='Receipt')
@view_config(request_method='GET', route_name='purchase_id', renderer='brewman:templates/angular_base.mako',
permission='Purchase')
@view_config(request_method='GET', route_name='purchase', renderer='brewman:templates/angular_base.mako',
permission='Purchase')
@view_config(request_method='GET', route_name='purchase_return_id', renderer='brewman:templates/angular_base.mako',
permission='Purchase Return')
@view_config(request_method='GET', route_name='purchase_return', renderer='brewman:templates/angular_base.mako',
permission='Purchase Return')
@view_config(request_method='GET', route_name='issue_id', renderer='brewman:templates/angular_base.mako',
permission='Issue')
@view_config(request_method='GET', route_name='issue', renderer='brewman:templates/angular_base.mako',
permission='Issue')
def journal_get(request):
return {}
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)
else:
return '#'