Feature: Deletion of active accounts and employees built.
Feature: Many columns made non-nullable to better safeguard data. Fixed: Employee save/update did not work due to misplaced date check.
This commit is contained in:
parent
9c965727df
commit
64c1820867
brewman/brewman
models
static
views
@ -174,7 +174,8 @@ class LedgerBase(Base):
|
||||
def type_object(self):
|
||||
return LedgerType.by_id(self.type)
|
||||
|
||||
def __init__(self, code=None, name=None, type=None, is_active=None, is_reconcilable=False, costcenter_id=None, is_fixture=False):
|
||||
def __init__(self, code=None, name=None, type=None, is_active=None, is_reconcilable=False, costcenter_id=None,
|
||||
is_fixture=False):
|
||||
self.code = code
|
||||
self.name = name
|
||||
self.type = type
|
||||
@ -216,15 +217,13 @@ class LedgerBase(Base):
|
||||
DBSession.add(self)
|
||||
return self
|
||||
|
||||
def can_delete(self):
|
||||
def can_delete(self, advanced_delete):
|
||||
if self.is_fixture:
|
||||
return False, "{0} is a fixture and cannot be edited or deleted.".format(self.name)
|
||||
if self.is_active:
|
||||
return False, 'Account is active'
|
||||
if len(self.journals) > 0:
|
||||
if len(self.journals) > 0 and not advanced_delete:
|
||||
return False, 'Account has journal entries'
|
||||
if len(self.products) > 0:
|
||||
return False, 'Account has products'
|
||||
return True, ''
|
||||
|
||||
@classmethod
|
||||
@ -257,6 +256,10 @@ class LedgerBase(Base):
|
||||
def esi_pf_payable(cls):
|
||||
return uuid.UUID('42277912-cc18-854b-b134-9f4b00dba419')
|
||||
|
||||
@classmethod
|
||||
def suspense(cls):
|
||||
return uuid.UUID('3854e317-6f3b-5142-ab26-9c44d4cddd08')
|
||||
|
||||
|
||||
class Employee(LedgerBase):
|
||||
__tablename__ = 'entities_employees'
|
||||
@ -292,6 +295,9 @@ class Employee(LedgerBase):
|
||||
DBSession.add(self)
|
||||
return self
|
||||
|
||||
def can_delete(self, advanced_delete):
|
||||
return super(Employee, self).can_delete(advanced_delete)
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
return DBSession.query(Employee).order_by(desc(Employee.is_active)).order_by(Ledger.costcenter_id).order_by(
|
||||
@ -305,6 +311,11 @@ class Employee(LedgerBase):
|
||||
class Ledger(LedgerBase):
|
||||
__mapper_args__ = {'polymorphic_identity': ''}
|
||||
|
||||
def can_delete(self, advanced_delete):
|
||||
if len(self.products) > 0:
|
||||
return False, 'Account has products'
|
||||
return super(Ledger, self).can_delete(advanced_delete)
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
return DBSession.query(Ledger).order_by(Ledger.type).order_by(Ledger.name).order_by(Ledger.code).all()
|
||||
|
@ -60,7 +60,7 @@ class Voucher(Base):
|
||||
|
||||
last_edit_date = Column('LastEditDate', DateTime(timezone=True))
|
||||
_type = Column('VoucherType', Integer)
|
||||
user_id = Column('UserID', GUID(), ForeignKey('auth_users.UserID'))
|
||||
user_id = Column('UserID', GUID(), ForeignKey('auth_users.UserID'), nullable=False)
|
||||
poster_id = Column('PosterID', GUID(), ForeignKey('auth_users.UserID'))
|
||||
|
||||
user = relationship('User', primaryjoin="User.id==Voucher.user_id", cascade=None)
|
||||
@ -121,9 +121,9 @@ class Journal(Base):
|
||||
id = Column('JournalID', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
debit = Column('Debit', Integer)
|
||||
amount = Column('Amount', Numeric)
|
||||
voucher_id = Column('VoucherID', GUID(), ForeignKey('entities_vouchers.VoucherID'))
|
||||
ledger_id = Column('LedgerID', GUID(), ForeignKey('entities_ledgers.LedgerID'))
|
||||
cost_center_id = Column('CostCenterID', GUID(), ForeignKey('entities_costcenters.CostCenterID'))
|
||||
voucher_id = Column('VoucherID', GUID(), ForeignKey('entities_vouchers.VoucherID'), nullable=False)
|
||||
ledger_id = Column('LedgerID', GUID(), ForeignKey('entities_ledgers.LedgerID'), nullable=False)
|
||||
cost_center_id = Column('CostCenterID', GUID(), ForeignKey('entities_costcenters.CostCenterID'), nullable=False)
|
||||
|
||||
@hybrid_property
|
||||
def signed_amount(self):
|
||||
@ -157,8 +157,8 @@ class Journal(Base):
|
||||
class SalaryDeduction(Base):
|
||||
__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'))
|
||||
voucher_id = Column('VoucherID', GUID(), ForeignKey('entities_vouchers.VoucherID'), nullable=False)
|
||||
journal_id = Column('JournalID', GUID(), ForeignKey('entities_journals.JournalID'), nullable=False)
|
||||
gross_salary = Column('GrossSalary', Integer)
|
||||
days_worked = Column('DaysWorked', Integer)
|
||||
esi_ee = Column('EsiEmployee', Integer)
|
||||
@ -188,9 +188,9 @@ class Inventory(Base):
|
||||
__tablename__ = 'entities_inventories'
|
||||
# __tableagrs__ = (UniqueConstraint('VoucherID', 'ProductID'))
|
||||
id = Column('InventoryID', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
voucher_id = Column('VoucherID', GUID(), ForeignKey('entities_vouchers.VoucherID'))
|
||||
product_id = Column('ProductID', GUID(), ForeignKey('entities_products.ProductID'))
|
||||
batch_id = Column('BatchID', GUID(), ForeignKey('entities_batches.BatchID'))
|
||||
voucher_id = Column('VoucherID', GUID(), ForeignKey('entities_vouchers.VoucherID'), nullable=False)
|
||||
product_id = Column('ProductID', GUID(), ForeignKey('entities_products.ProductID'), nullable=False)
|
||||
batch_id = Column('BatchID', GUID(), ForeignKey('entities_batches.BatchID'), nullable=False)
|
||||
quantity = Column('Quantity', Numeric)
|
||||
rate = Column('Rate', Numeric)
|
||||
tax = Column('Tax', Numeric)
|
||||
@ -219,7 +219,7 @@ class Batch(Base):
|
||||
|
||||
id = Column('BatchID', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
name = Column('Name', DateTime)
|
||||
product_id = Column('ProductID', GUID(), ForeignKey('entities_products.ProductID'))
|
||||
product_id = Column('ProductID', GUID(), ForeignKey('entities_products.ProductID'), nullable=False)
|
||||
quantity_remaining = Column('QuantityRemaining', Numeric)
|
||||
rate = Column('Rate', Numeric)
|
||||
tax = Column('Tax', Numeric)
|
||||
@ -297,7 +297,7 @@ class Attendance(Base):
|
||||
DBSession.add(self)
|
||||
|
||||
|
||||
class Fingerprint(Base):
|
||||
class Fingerprint(Base):
|
||||
__tablename__ = 'entities_fingerprints'
|
||||
|
||||
id = Column('FingerprintID', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
|
@ -83,6 +83,7 @@
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<button class="btn btn-primary" ng-click="save()">Save</button>
|
||||
<button class="btn btn-danger" ng-show="employee.LedgerID" ng-click="confirm()">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -14,10 +14,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button class="btn btn-primary" ng-click="setLockDate()">Set</button>
|
||||
<button class="btn btn-block btn-primary" ng-click="setLockDate()">Set</button>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button class="btn btn-danger" ng-click="clearLockDate()">Clear</button>
|
||||
<button class="btn btn-block btn-danger" ng-click="clearLockDate()">Clear</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -16,7 +16,7 @@ AccountListCtrl.resolve = {
|
||||
}]
|
||||
};
|
||||
|
||||
var AccountCtrl = ['$scope', '$location', 'account', 'account_types', 'cost_centers', function ($scope, $location, account, account_types, cost_centers) {
|
||||
var AccountCtrl = ['$scope', '$location', '$modal', 'account', 'account_types', 'cost_centers', function ($scope, $location, $modal, account, account_types, cost_centers) {
|
||||
$scope.account = account;
|
||||
|
||||
$scope.account_types = account_types;
|
||||
|
@ -17,19 +17,19 @@ EmployeeListCtrl.resolve = {
|
||||
}]
|
||||
};
|
||||
|
||||
var EmployeeCtrl = ['$scope', '$routeParams', '$location', 'dateFilter', 'employee', 'cost_centers', function ($scope, $routeParams, $location, dateFilter, employee, cost_centers) {
|
||||
var EmployeeCtrl = ['$scope', '$routeParams', '$location', 'dateFilter', '$modal', 'employee', 'cost_centers', function ($scope, $routeParams, $location, dateFilter, $modal, employee, cost_centers) {
|
||||
$scope.employee = employee;
|
||||
|
||||
$scope.cost_centers = cost_centers;
|
||||
|
||||
$scope.save = function () {
|
||||
$scope.employee.$save(function (u, putResponseHeaders) {
|
||||
if (angular.isDate($scope.employee.JoiningDate)) {
|
||||
$scope.info.Date = dateFilter($scope.info.JoiningDate, 'dd-MMM-yyyy');
|
||||
$scope.employee.JoiningDate = dateFilter($scope.employee.JoiningDate, 'dd-MMM-yyyy');
|
||||
}
|
||||
if (angular.isDate($scope.employee.LeavingDate)) {
|
||||
$scope.info.Date = dateFilter($scope.info.LeavingDate, 'dd-MMM-yyyy');
|
||||
$scope.employee.LeavingDate = dateFilter($scope.employee.LeavingDate, 'dd-MMM-yyyy');
|
||||
}
|
||||
$scope.employee.$save(function (u, putResponseHeaders) {
|
||||
$scope.toasts.push({Type: 'Success', Message: u.Code});
|
||||
$location.path('/Employees');
|
||||
}, function (data, status) {
|
||||
@ -45,6 +45,28 @@ var EmployeeCtrl = ['$scope', '$routeParams', '$location', 'dateFilter', 'employ
|
||||
$scope.toasts.push({Type: 'Danger', Message: data.data});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.confirm = function () {
|
||||
var modalInstance = $modal.open({
|
||||
backdrop: true,
|
||||
templateUrl: '/template/modal/confirm.html',
|
||||
controller: ['$scope', '$modalInstance', function ($scope, $modalInstance) {
|
||||
$scope.title = "Delete Account";
|
||||
$scope.body = "Are you sure? This cannot be undone.";
|
||||
$scope.isDelete = true;
|
||||
$scope.ok = function () {
|
||||
$modalInstance.close();
|
||||
};
|
||||
$scope.cancel = function () {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
}]
|
||||
});
|
||||
modalInstance.result.then(function () {
|
||||
$scope.delete();
|
||||
});
|
||||
};
|
||||
|
||||
$('#txtName').focus();
|
||||
}];
|
||||
|
||||
|
@ -197,5 +197,5 @@ var JournalModalCtrl = ['$scope', '$modalInstance', '$q', 'edit', 'Account', fun
|
||||
deferred.resolve(result);
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
}]
|
||||
};
|
||||
}];
|
||||
|
@ -1,12 +1,16 @@
|
||||
import uuid
|
||||
from pyramid.response import Response
|
||||
from pyramid.security import authenticated_userid
|
||||
|
||||
from pyramid.view import view_config
|
||||
from sqlalchemy.orm import joinedload_all
|
||||
import transaction
|
||||
from brewman import groupfinder
|
||||
from brewman.models import DBSession
|
||||
|
||||
from brewman.models.master import CostCenter, Ledger, LedgerType, LedgerBase
|
||||
from brewman.models.validation_exception import ValidationError, TryCatchFunction
|
||||
from brewman.models.voucher import Voucher, Journal, VoucherType
|
||||
|
||||
|
||||
@view_config(route_name='account_list', renderer='brewman:templates/angular_base.mako', permission='Accounts')
|
||||
@ -49,9 +53,9 @@ def update(request):
|
||||
@view_config(request_method='DELETE', route_name='api_account_id', renderer='json', permission='Accounts')
|
||||
def delete(request):
|
||||
account = Ledger.by_id(uuid.UUID(request.matchdict['id']))
|
||||
can_delete, reason = account.can_delete()
|
||||
can_delete, reason = account.can_delete('Advanced Delete' in groupfinder(authenticated_userid(request), request))
|
||||
if can_delete:
|
||||
DBSession.delete(account)
|
||||
delete_with_data(account)
|
||||
transaction.commit()
|
||||
return account_info(None)
|
||||
else:
|
||||
@ -125,3 +129,37 @@ def account_info(id):
|
||||
return account
|
||||
|
||||
|
||||
def delete_with_data(account):
|
||||
suspense_ledger = Ledger.by_id(Ledger.suspense())
|
||||
query = Voucher.query().options(joinedload_all(Voucher.journals, Journal.ledger, innerjoin=True)) \
|
||||
.filter(Voucher.journals.any(Journal.ledger_id == account.id)) \
|
||||
.all()
|
||||
|
||||
for voucher in query:
|
||||
others, sus_jnl, acc_jnl = False, None, None
|
||||
for journal in voucher.journals:
|
||||
if journal.ledger_id == account.id:
|
||||
acc_jnl = journal
|
||||
elif journal.ledger_id == Ledger.suspense():
|
||||
sus_jnl = journal
|
||||
else:
|
||||
others = True
|
||||
if not others:
|
||||
DBSession.delete(voucher)
|
||||
else:
|
||||
if sus_jnl is None:
|
||||
acc_jnl.ledger = suspense_ledger
|
||||
voucher.narration += '\nSuspense \u20B9 {0:,.2f} is {1}'.format(acc_jnl.amount, account.name)
|
||||
else:
|
||||
amount = (sus_jnl.debit * sus_jnl.amount) + (acc_jnl.debit * acc_jnl.amount)
|
||||
DBSession.delete(acc_jnl)
|
||||
if amount == 0:
|
||||
DBSession.delete(sus_jnl)
|
||||
else:
|
||||
sus_jnl.amount = abs(amount)
|
||||
sus_jnl.debit = -1 if amount < 0 else 1
|
||||
voucher.narration += '\nDeleted \u20B9 {0:,.2f} of {1}'.format(acc_jnl.amount * acc_jnl.debit,
|
||||
account.name)
|
||||
if voucher.type in (VoucherType.by_name('Payment').id, VoucherType.by_name('Receipt').id):
|
||||
voucher.type = VoucherType.by_name('Journal')
|
||||
DBSession.delete(account)
|
||||
|
@ -1,21 +1,26 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from pyramid.response import Response
|
||||
from pyramid.security import authenticated_userid
|
||||
|
||||
from pyramid.view import view_config
|
||||
from sqlalchemy.orm import joinedload_all
|
||||
import transaction
|
||||
from brewman import groupfinder
|
||||
from brewman.models import DBSession
|
||||
|
||||
from brewman.models.master import CostCenter, Employee, LedgerBase
|
||||
from brewman.models.master import CostCenter, Employee, LedgerBase, Ledger
|
||||
from brewman.models.validation_exception import ValidationError, TryCatchFunction
|
||||
from brewman.models.voucher import Voucher, Journal, VoucherType
|
||||
|
||||
|
||||
@view_config(route_name='employee_list', renderer='brewman:templates/angular_base.mako', permission='Authenticated')
|
||||
@view_config(request_method='GET', route_name='employee_id', renderer='brewman:templates/angular_base.mako',
|
||||
permission='Employees')
|
||||
permission='Employees')
|
||||
@view_config(request_method='GET', route_name='employee', renderer='brewman:templates/angular_base.mako',
|
||||
permission='Employees')
|
||||
permission='Employees')
|
||||
@view_config(request_method='GET', route_name='employee_functions', renderer='brewman:templates/angular_base.mako',
|
||||
permission='Employees')
|
||||
permission='Employees')
|
||||
def html(request):
|
||||
return {}
|
||||
|
||||
@ -26,13 +31,13 @@ def save(request):
|
||||
is_active = request.json_body['IsActive']
|
||||
joining_date = datetime.datetime.strptime(request.json_body['JoiningDate'], '%d-%b-%Y')
|
||||
leaving_date = None if is_active else datetime.datetime.strptime(request.json_body['LeavingDate'],
|
||||
'%d-%b-%Y')
|
||||
'%d-%b-%Y')
|
||||
|
||||
item = Employee(0, request.json_body['Name'], is_active,
|
||||
uuid.UUID(request.json_body['CostCenter']['CostCenterID']),
|
||||
request.json_body['Designation'], int(request.json_body['Salary']),
|
||||
int(request.json_body['ServicePoints']),
|
||||
joining_date, leaving_date).create()
|
||||
uuid.UUID(request.json_body['CostCenter']['CostCenterID']),
|
||||
request.json_body['Designation'], int(request.json_body['Salary']),
|
||||
int(request.json_body['ServicePoints']),
|
||||
joining_date, leaving_date).create()
|
||||
transaction.commit()
|
||||
return employee_info(item.id)
|
||||
|
||||
@ -51,7 +56,7 @@ def update(request):
|
||||
item.service_points = int(request.json_body['ServicePoints'])
|
||||
item.joining_date = datetime.datetime.strptime(request.json_body['JoiningDate'], '%d-%b-%Y')
|
||||
item.leaving_date = None if item.is_active else datetime.datetime.strptime(request.json_body['LeavingDate'],
|
||||
'%d-%b-%Y')
|
||||
'%d-%b-%Y')
|
||||
transaction.commit()
|
||||
return employee_info(item.id)
|
||||
|
||||
@ -59,9 +64,9 @@ def update(request):
|
||||
@view_config(request_method='DELETE', route_name='api_employee_id', renderer='json', permission='Employees')
|
||||
def delete(request):
|
||||
employee = Employee.by_id(uuid.UUID(request.matchdict['id']))
|
||||
can_delete, reason = employee.can_delete()
|
||||
can_delete, reason = employee.can_delete('Advanced Delete' in groupfinder(authenticated_userid(request), request))
|
||||
if can_delete:
|
||||
DBSession.delete(employee)
|
||||
delete_with_data(employee)
|
||||
transaction.commit()
|
||||
return employee_info(None)
|
||||
else:
|
||||
@ -82,7 +87,7 @@ def show_blank(request):
|
||||
|
||||
|
||||
@view_config(request_method='GET', route_name='api_employee', request_param='list', renderer='json',
|
||||
permission='Authenticated')
|
||||
permission='Authenticated')
|
||||
def show_list(request):
|
||||
list = Employee.list()
|
||||
ledgers = []
|
||||
@ -97,7 +102,7 @@ def show_list(request):
|
||||
|
||||
|
||||
@view_config(request_method='GET', route_name='api_employee', renderer='json', request_param='term',
|
||||
permission='Authenticated')
|
||||
permission='Authenticated')
|
||||
def show_term(request):
|
||||
filter = request.GET.get('term', None)
|
||||
filter = None if filter == '' else filter
|
||||
@ -123,4 +128,46 @@ def employee_info(id):
|
||||
'ServicePoints': employee.service_points, 'JoiningDate': employee.joining_date.strftime('%d-%b-%Y'),
|
||||
'LeavingDate': None if employee.is_active else employee.leaving_date.strftime('%d-%b-%Y'),
|
||||
'CostCenter': {'CostCenterID': employee.costcenter_id, 'Name': employee.costcenter.name}}
|
||||
return employee
|
||||
return employee
|
||||
|
||||
|
||||
def delete_with_data(employee):
|
||||
suspense_ledger = Ledger.by_id(Ledger.suspense())
|
||||
query = Voucher.query().options(joinedload_all(Voucher.journals, Journal.ledger, innerjoin=True))\
|
||||
.filter(Voucher.journals.any(Journal.ledger_id == employee.id))\
|
||||
.all()
|
||||
|
||||
for voucher in query:
|
||||
others, sus_jnl, acc_jnl = False, None, None
|
||||
for journal in voucher.journals:
|
||||
if journal.ledger_id == employee.id:
|
||||
acc_jnl = journal
|
||||
elif journal.ledger_id == Ledger.suspense():
|
||||
sus_jnl = journal
|
||||
else:
|
||||
others = True
|
||||
if not others:
|
||||
DBSession.delete(voucher)
|
||||
else:
|
||||
if sus_jnl is None:
|
||||
acc_jnl.ledger = suspense_ledger
|
||||
voucher.narration += '\nSuspense \u20B9 {0:,.2f} is {1}'.format(acc_jnl.amount, employee.name)
|
||||
else:
|
||||
amount = (sus_jnl.debit * sus_jnl.amount) + (acc_jnl.debit * acc_jnl.amount)
|
||||
if acc_jnl.salary_deduction is not None:
|
||||
DBSession.delete(acc_jnl.salary_deduction)
|
||||
DBSession.delete(acc_jnl)
|
||||
if amount == 0:
|
||||
DBSession.delete(sus_jnl)
|
||||
else:
|
||||
sus_jnl.amount = abs(amount)
|
||||
sus_jnl.debit = -1 if amount < 0 else 1
|
||||
voucher.narration += '\nDeleted \u20B9 {0:,.2f} of {1}'.format(acc_jnl.amount * acc_jnl.debit,
|
||||
employee.name)
|
||||
if voucher.type in (VoucherType.by_name('Payment').id, VoucherType.by_name('Receipt').id):
|
||||
voucher.type = VoucherType.by_name('Journal')
|
||||
for fingerprint in employee.fingerprints:
|
||||
DBSession.delete(fingerprint)
|
||||
for attendance in employee.attendances:
|
||||
DBSession.delete(attendance)
|
||||
DBSession.delete(employee)
|
||||
|
Loading…
x
Reference in New Issue
Block a user