From bb820febff63fe39ce949b905696b0a8c1d722ba Mon Sep 17 00:00:00 2001 From: Tanshu Date: Fri, 9 Nov 2012 00:13:04 +0530 Subject: [PATCH] Added client list, full management pending. Added limiting of clients via cookies. Added Attendence report download and credit salary in employee functions. Renamed get_by_id and get_by_name in User, Group and Role models. Added salary staff account in LedgerBase. Voucher validation to be checked. Added csv renderer for downloading csv files. Added ngCookies module to process ClientID in login form on client side. Modified the datepicker directive to process model properly. Fixed Employee Attendance, was broken. Allowed batch quantity to increase in multiple entries of same product batch in issue. Ledger report would not reload data if ledger and dates were not changed, fixed that. --- brewman/brewman/__init__.py | 17 +- brewman/brewman/models/auth.py | 11 +- brewman/brewman/models/master.py | 4 + brewman/brewman/models/operations.py | 13 +- brewman/brewman/models/voucher.py | 6 +- brewman/brewman/renderers.py | 46 +++++ brewman/brewman/security.py | 2 +- brewman/brewman/static/css/table.css | 39 +--- .../static/js/angular-cookies-1.0.2.js | 171 ++++++++++++++++++ .../brewman/static/partial/account-list.html | 2 +- .../brewman/static/partial/attendance.html | 4 +- brewman/brewman/static/partial/cash-flow.html | 6 +- .../brewman/static/partial/client-list.html | 20 ++ .../brewman/static/partial/closing-stock.html | 4 +- .../static/partial/cost-center-list.html | 2 +- brewman/brewman/static/partial/daybook.html | 8 +- .../static/partial/employee-attendance.html | 16 +- .../static/partial/employee-detail.html | 6 +- .../static/partial/employee-functions.html | 16 +- .../brewman/static/partial/employee-list.html | 2 +- .../brewman/static/partial/group-list.html | 2 +- brewman/brewman/static/partial/issue.html | 6 +- brewman/brewman/static/partial/journal.html | 4 +- brewman/brewman/static/partial/ledger.html | 20 +- brewman/brewman/static/partial/login.html | 15 ++ brewman/brewman/static/partial/payment.html | 6 +- .../static/partial/product-group-list.html | 2 +- .../static/partial/product-ledger.html | 6 +- .../brewman/static/partial/product-list.html | 2 +- .../brewman/static/partial/profit-loss.html | 8 +- .../static/partial/purchase-entries.html | 6 +- .../static/partial/purchase-return.html | 4 +- brewman/brewman/static/partial/purchase.html | 4 +- .../partial/raw-material-cost-detail.html | 8 +- .../static/partial/raw-material-cost.html | 6 +- brewman/brewman/static/partial/receipt.html | 6 +- .../brewman/static/partial/trial-balance.html | 2 +- brewman/brewman/static/partial/unposted.html | 2 +- brewman/brewman/static/partial/user-list.html | 2 +- .../static/scripts/angular_directive.js | 12 +- .../brewman/static/scripts/angular_service.js | 34 +--- brewman/brewman/static/scripts/client.js | 18 ++ .../static/scripts/employee-attendance.js | 23 ++- .../static/scripts/employee-functions.js | 16 +- brewman/brewman/static/scripts/issue.js | 10 + brewman/brewman/static/scripts/ledger.js | 14 +- brewman/brewman/static/scripts/login.js | 20 +- brewman/brewman/static/scripts/overlord.js | 4 +- brewman/brewman/templates/angular_base.mako | 8 +- brewman/brewman/views/__init__.py | 27 +-- brewman/brewman/views/account.py | 42 +++-- brewman/brewman/views/attendance.py | 12 +- brewman/brewman/views/attendance_report.py | 104 +++++++++++ brewman/brewman/views/auth/client.py | 39 ++++ brewman/brewman/views/auth/group.py | 6 +- brewman/brewman/views/auth/user.py | 6 +- brewman/brewman/views/cost_center.py | 26 ++- brewman/brewman/views/employee.py | 2 + brewman/brewman/views/product.py | 53 +++--- brewman/brewman/views/product_group.py | 28 ++- brewman/brewman/views/services/login.py | 101 +++++++++++ brewman/brewman/views/services/session.py | 81 +-------- .../views/services/voucher/__init__.py | 9 +- .../brewman/views/services/voucher/issue.py | 28 ++- .../views/services/voucher/purchase.py | 46 +++-- .../views/services/voucher/purchase_return.py | 14 +- .../views/services/voucher/save_voucher.py | 23 +-- .../views/services/voucher/update_voucher.py | 4 +- 68 files changed, 910 insertions(+), 406 deletions(-) create mode 100644 brewman/brewman/renderers.py create mode 100644 brewman/brewman/static/js/angular-cookies-1.0.2.js create mode 100644 brewman/brewman/static/partial/client-list.html create mode 100644 brewman/brewman/static/scripts/client.js create mode 100644 brewman/brewman/views/attendance_report.py create mode 100644 brewman/brewman/views/auth/client.py create mode 100644 brewman/brewman/views/services/login.py diff --git a/brewman/brewman/__init__.py b/brewman/brewman/__init__.py index 3aea6723..548c04bd 100644 --- a/brewman/brewman/__init__.py +++ b/brewman/brewman/__init__.py @@ -29,7 +29,8 @@ def main(global_config, **settings): ) # This changes in pyramid 1.4 to something similar to "config.add_renderer('myjson', JSON(indent=4))" # http://docs.pylonsproject.org/projects/pyramid/en/master/api/renderers.html#pyramid.renderers.JSON - config.add_renderer('json', factory='brewman.views.my_json_renderer_factory') + config.add_renderer(name='json', factory='brewman.renderers.my_json_renderer_factory') + config.add_renderer(name='csv', factory='brewman.renderers.CSVRenderer') config.add_static_view('icons', 'brewman:static/icons') config.add_static_view('assets', 'brewman:static/assets') @@ -66,6 +67,10 @@ def main(global_config, **settings): config.add_route('group', '/Group') config.add_route('group_list', '/Groups') + config.add_route('client_id', '/Client/{id}') + config.add_route('client', '/Client') + config.add_route('client_list', '/Clients') + config.add_route('product_id', '/Product/{id}') config.add_route('product', '/Product') config.add_route('product_list', '/Products') @@ -88,6 +93,9 @@ def main(global_config, **settings): config.add_route('issue', '/Issue') config.add_route('issues_grid', '/Issues/Services/{date}') + config.add_route('voucher', '/Voucher/{id}') + config.add_route('voucher_new', '/Voucher') + config.add_route('attendance_date', '/Attendance/{date}') config.add_route('attendance', '/Attendance') config.add_route('attendance_types', '/AttendanceTypes') @@ -95,6 +103,10 @@ def main(global_config, **settings): config.add_route('employee_attendance_id', '/EmployeeAttendance/{id}') config.add_route('employee_attendance', '/EmployeeAttendance') + config.add_route('attendance_report', '/AttendanceReport') + config.add_route('credit_salary', '/CreditSalary') + config.add_route('employee_functions', '/EmployeeFunctions') + config.add_route('ledger_id', '/Ledger/{id}') config.add_route('ledger', '/Ledger') @@ -124,9 +136,6 @@ def main(global_config, **settings): config.add_route('account_type_list', '/AccountTypes') - config.add_route('voucher', '/Voucher/{id}') - config.add_route('voucher_new', '/Voucher') - config.add_route('favicon', '/favicon.ico') config.scan() diff --git a/brewman/brewman/models/auth.py b/brewman/brewman/models/auth.py index 8eec623b..2da76140 100644 --- a/brewman/brewman/models/auth.py +++ b/brewman/brewman/models/auth.py @@ -56,7 +56,6 @@ class Client(Base): client_code = random.randint(1000, 9999) otp = random.randint(1000, 9999) name = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(6)) -# name = str(client_code) client = Client(client_code, name, False, otp) DBSession.add(client) return client @@ -108,18 +107,18 @@ class User(Base): self.locked_out = locked_out @classmethod - def get_by_name(cls, name): + def by_name(cls, name): return DBSession.query(cls).filter(cls.name == name).first() @classmethod - def get_by_id(cls, id): + def by_id(cls, id): if not isinstance(id, uuid.UUID): id = uuid.UUID(id) return DBSession.query(cls).filter(cls.id == id).one() @classmethod def auth(cls, name, password): - user = cls.get_by_name(name) + user = cls.by_name(name) if not user: return False, None if user.password != encrypt(password) or user.locked_out: @@ -149,7 +148,7 @@ class Group(Base): self.name = name @classmethod - def get_by_id(cls, id): + def by_id(cls, id): return DBSession.query(cls).filter(cls.id == id).one() @classmethod @@ -173,6 +172,6 @@ class Role(Base): return DBSession.query(cls).order_by(cls.name).all() @classmethod - def get_by_id(cls, id): + def by_id(cls, id): return DBSession.query(cls).filter(cls.id == id).one() diff --git a/brewman/brewman/models/master.py b/brewman/brewman/models/master.py index d8f7b66e..265e28ad 100644 --- a/brewman/brewman/models/master.py +++ b/brewman/brewman/models/master.py @@ -223,6 +223,10 @@ class LedgerBase(Base): def local_purchase(cls): return {'LedgerID': 'd2b75912-505f-2548-9093-466dfff6a0f9', 'Name': 'local purchase'} + @classmethod + def salary(cls): + return {'LedgerID': '5c2b54d0-c174-004d-a0d5-92cdaadcefa7', 'Name': 'salary staff'} + class Employee(LedgerBase): __tablename__ = 'entities_employees' diff --git a/brewman/brewman/models/operations.py b/brewman/brewman/models/operations.py index bdbb28ce..a31ce084 100644 --- a/brewman/brewman/models/operations.py +++ b/brewman/brewman/models/operations.py @@ -34,24 +34,13 @@ def update_journal_voucher(voucher): update_basic(voucher) -def create_purchase_voucher(voucher): +def validate_new_purchase_voucher(voucher): journals_valid(voucher) for item in voucher.inventories: batch = item.batch if not batch.id is None: raise ValidationError("New Purchase cannot have a Batch") - create_basic(voucher) - for item in voucher.journals: - item.voucher_id = voucher.id - DBSession.add(item) - for item in voucher.inventories: - item.voucher_id = voucher.id - DBSession.add(item.batch) - item.batch_id = item.batch.id - DBSession.add(item) - DBSession.flush() - def update_purchase_voucher(voucher): journals_valid(voucher) diff --git a/brewman/brewman/models/voucher.py b/brewman/brewman/models/voucher.py index 23ead4b5..de5bd197 100644 --- a/brewman/brewman/models/voucher.py +++ b/brewman/brewman/models/voucher.py @@ -50,13 +50,13 @@ class Voucher(Base): def __name__(self): return self.name - def __init__(self, date=None, reconcilliation_date=None, narration=None, posted=None, creation_date=None, last_edit_date=None, type=None, user_id=None, poster_id=None): + 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): self.date = date self.reconcilliation_date = reconcilliation_date self.narration = narration self.posted = posted - self.creation_date = creation_date - self.last_edit_date = last_edit_date + self.creation_date = datetime.now() if creation_date is None else creation_date + self.last_edit_date = datetime.now() if last_edit_date is None else last_edit_date self.type = type self.user_id = user_id self.poster_id = poster_id diff --git a/brewman/brewman/renderers.py b/brewman/brewman/renderers.py new file mode 100644 index 00000000..37174284 --- /dev/null +++ b/brewman/brewman/renderers.py @@ -0,0 +1,46 @@ +import csv +from io import StringIO +import json +from brewman.views import DecimalEncoder + +__author__ = 'tanshu' + + +class CSVRenderer(object): + def __init__(self, info): + pass + + def __call__(self, value, system): + csv_data = StringIO() + writer = csv.writer(csv_data, delimiter=',', quoting=csv.QUOTE_MINIMAL) + + if 'header' in value: + writer.writerow(value['header']) + if 'rows' in value: + writer.writerows(value['rows']) + if 'footer' in value: + writer.writerows(value['footer']) + + request = system.get('request') + if request is not None: + response = request.response + ct = response.content_type + if ct == response.default_content_type: + response.content_type = 'text/csv' + filename = value['filename'] if 'filename' in value else 'report.csv' + response.content_disposition = 'attachment;filename="{0}"'.format(filename) + return csv_data.getvalue() + + +class my_json_renderer_factory: + def __init__(self, info): + pass + + def __call__(self, value, system): + request = system.get('request') + if request is not None: + response = request.response + ct = response.content_type + if ct == response.default_content_type: + response.content_type = 'application/json' + return json.dumps(value, cls=DecimalEncoder) diff --git a/brewman/brewman/security.py b/brewman/brewman/security.py index ea53aca3..1a676177 100644 --- a/brewman/brewman/security.py +++ b/brewman/brewman/security.py @@ -5,7 +5,7 @@ def groupfinder(user_id, request): if type(user_id) == str: user_id = uuid.UUID(user_id) perms = [] - user = User.get_by_id(user_id) + user = User.by_id(user_id) for item in user.groups: for perm in item.roles: perms.append(perm.name) diff --git a/brewman/brewman/static/css/table.css b/brewman/brewman/static/css/table.css index 7d2c1cd3..3fe21cb5 100644 --- a/brewman/brewman/static/css/table.css +++ b/brewman/brewman/static/css/table.css @@ -1,41 +1,12 @@ /* Table for Issue */ +.no-wrap { + white-space: nowrap +} .hide { display: none; } -.clean-table -{ - background: #fff; - width: 100%; -} -.clean-table th -{ - color: #039; -} -.clean-table td:not(.posted) -{ - color: #669; -} -.clean-table tbody tr:not(.unposted):hover td -{ - color: #339; - background: #d0dafd; +.table tbody tr.success td { + background-color: #1F7A1F; } -.clean-table tbody tr:nth-child(odd):not(.unposted) -{ - background: #e8edff; -} -.clean-table tfoot tr -{ - color: #039; -} -.unposted -{ - background-color: Pink; -} - -.right -{ - text-align:right; -} diff --git a/brewman/brewman/static/js/angular-cookies-1.0.2.js b/brewman/brewman/static/js/angular-cookies-1.0.2.js new file mode 100644 index 00000000..955715b1 --- /dev/null +++ b/brewman/brewman/static/js/angular-cookies-1.0.2.js @@ -0,0 +1,171 @@ +/** + * @license AngularJS v1.0.2 + * (c) 2010-2012 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular, undefined) { +'use strict'; + +/** + * @ngdoc overview + * @name ngCookies + */ + + +angular.module('ngCookies', ['ng']). + /** + * @ngdoc object + * @name ngCookies.$cookies + * @requires $browser + * + * @description + * Provides read/write access to browser's cookies. + * + * Only a simple Object is exposed and by adding or removing properties to/from + * this object, new cookies are created/deleted at the end of current $eval. + * + * @example + */ + factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) { + var cookies = {}, + lastCookies = {}, + lastBrowserCookies, + runEval = false, + copy = angular.copy, + isUndefined = angular.isUndefined; + + //creates a poller fn that copies all cookies from the $browser to service & inits the service + $browser.addPollFn(function() { + var currentCookies = $browser.cookies(); + if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl + lastBrowserCookies = currentCookies; + copy(currentCookies, lastCookies); + copy(currentCookies, cookies); + if (runEval) $rootScope.$apply(); + } + })(); + + runEval = true; + + //at the end of each eval, push cookies + //TODO: this should happen before the "delayed" watches fire, because if some cookies are not + // strings or browser refuses to store some cookies, we update the model in the push fn. + $rootScope.$watch(push); + + return cookies; + + + /** + * Pushes all the cookies from the service to the browser and verifies if all cookies were stored. + */ + function push() { + var name, + value, + browserCookies, + updated; + + //delete any cookies deleted in $cookies + for (name in lastCookies) { + if (isUndefined(cookies[name])) { + $browser.cookies(name, undefined); + } + } + + //update all cookies updated in $cookies + for(name in cookies) { + value = cookies[name]; + if (!angular.isString(value)) { + if (angular.isDefined(lastCookies[name])) { + cookies[name] = lastCookies[name]; + } else { + delete cookies[name]; + } + } else if (value !== lastCookies[name]) { + $browser.cookies(name, value); + updated = true; + } + } + + //verify what was actually stored + if (updated){ + updated = false; + browserCookies = $browser.cookies(); + + for (name in cookies) { + if (cookies[name] !== browserCookies[name]) { + //delete or reset all cookies that the browser dropped from $cookies + if (isUndefined(browserCookies[name])) { + delete cookies[name]; + } else { + cookies[name] = browserCookies[name]; + } + updated = true; + } + } + } + } + }]). + + + /** + * @ngdoc object + * @name ngCookies.$cookieStore + * @requires $cookies + * + * @description + * Provides a key-value (string-object) storage, that is backed by session cookies. + * Objects put or retrieved from this storage are automatically serialized or + * deserialized by angular's toJson/fromJson. + * @example + */ + factory('$cookieStore', ['$cookies', function($cookies) { + + return { + /** + * @ngdoc method + * @name ngCookies.$cookieStore#get + * @methodOf ngCookies.$cookieStore + * + * @description + * Returns the value of given cookie key + * + * @param {string} key Id to use for lookup. + * @returns {Object} Deserialized cookie value. + */ + get: function(key) { + return angular.fromJson($cookies[key]); + }, + + /** + * @ngdoc method + * @name ngCookies.$cookieStore#put + * @methodOf ngCookies.$cookieStore + * + * @description + * Sets a value for given cookie key + * + * @param {string} key Id for the `value`. + * @param {Object} value Value to be stored. + */ + put: function(key, value) { + $cookies[key] = angular.toJson(value); + }, + + /** + * @ngdoc method + * @name ngCookies.$cookieStore#remove + * @methodOf ngCookies.$cookieStore + * + * @description + * Remove given cookie + * + * @param {string} key Id of the key-value pair to delete. + */ + remove: function(key) { + delete $cookies[key]; + } + }; + + }]); + +})(window, window.angular); diff --git a/brewman/brewman/static/partial/account-list.html b/brewman/brewman/static/partial/account-list.html index 5021c777..8e1eec7c 100644 --- a/brewman/brewman/static/partial/account-list.html +++ b/brewman/brewman/static/partial/account-list.html @@ -1,5 +1,5 @@ Accounts Add - +
diff --git a/brewman/brewman/static/partial/attendance.html b/brewman/brewman/static/partial/attendance.html index 7106fcef..c3981772 100644 --- a/brewman/brewman/static/partial/attendance.html +++ b/brewman/brewman/static/partial/attendance.html @@ -4,7 +4,7 @@
- +
@@ -12,7 +12,7 @@
-
Name
+
diff --git a/brewman/brewman/static/partial/cash-flow.html b/brewman/brewman/static/partial/cash-flow.html index 62c7326c..1e4c4775 100644 --- a/brewman/brewman/static/partial/cash-flow.html +++ b/brewman/brewman/static/partial/cash-flow.html @@ -4,8 +4,8 @@
- - + +
@@ -13,7 +13,7 @@
-
Code
+
diff --git a/brewman/brewman/static/partial/client-list.html b/brewman/brewman/static/partial/client-list.html new file mode 100644 index 00000000..ec7717b0 --- /dev/null +++ b/brewman/brewman/static/partial/client-list.html @@ -0,0 +1,20 @@ +Clients +
Name
+ + + + + + + + + + + + + + + + +
CodeNameEnabledOTP
{{item.Code}}{{item.Name}}{{item.Enabled}}{{item.OTP}}
+ diff --git a/brewman/brewman/static/partial/closing-stock.html b/brewman/brewman/static/partial/closing-stock.html index 563236e6..c4f8f74a 100644 --- a/brewman/brewman/static/partial/closing-stock.html +++ b/brewman/brewman/static/partial/closing-stock.html @@ -4,7 +4,7 @@
- +
@@ -12,7 +12,7 @@
- +
diff --git a/brewman/brewman/static/partial/cost-center-list.html b/brewman/brewman/static/partial/cost-center-list.html index 34ff52fa..8f3d2a0d 100644 --- a/brewman/brewman/static/partial/cost-center-list.html +++ b/brewman/brewman/static/partial/cost-center-list.html @@ -1,5 +1,5 @@ Cost Centers Add -
Product
+
diff --git a/brewman/brewman/static/partial/daybook.html b/brewman/brewman/static/partial/daybook.html index d8d5fb1e..d72ccd2c 100644 --- a/brewman/brewman/static/partial/daybook.html +++ b/brewman/brewman/static/partial/daybook.html @@ -4,8 +4,8 @@
- - + +
@@ -13,7 +13,7 @@
-
Name
+
@@ -26,7 +26,7 @@ - + diff --git a/brewman/brewman/static/partial/employee-attendance.html b/brewman/brewman/static/partial/employee-attendance.html index 6f0e4d1d..e6c69a56 100644 --- a/brewman/brewman/static/partial/employee-attendance.html +++ b/brewman/brewman/static/partial/employee-attendance.html @@ -4,8 +4,8 @@
- - + +
@@ -21,21 +21,27 @@
-
Date
{{item.Date}} {{item.Type}} {{item.Narration}}
+
+ - + +
Date StatusisDirty
{{item.Date}} - + +
diff --git a/brewman/brewman/static/partial/employee-detail.html b/brewman/brewman/static/partial/employee-detail.html index 86da0ed9..d1a0f0e3 100644 --- a/brewman/brewman/static/partial/employee-detail.html +++ b/brewman/brewman/static/partial/employee-detail.html @@ -56,8 +56,7 @@
- +
@@ -65,8 +64,7 @@
- +
diff --git a/brewman/brewman/static/partial/employee-functions.html b/brewman/brewman/static/partial/employee-functions.html index 0d01583a..c7f113c9 100644 --- a/brewman/brewman/static/partial/employee-functions.html +++ b/brewman/brewman/static/partial/employee-functions.html @@ -4,8 +4,18 @@
- - + + +
+ + Attendance Record +
+ + +
+ + + Download
Credit Esi / Pf @@ -13,7 +23,7 @@
- +
diff --git a/brewman/brewman/static/partial/employee-list.html b/brewman/brewman/static/partial/employee-list.html index 9948ca4c..2f65c810 100644 --- a/brewman/brewman/static/partial/employee-list.html +++ b/brewman/brewman/static/partial/employee-list.html @@ -1,6 +1,6 @@ Employees Add - +
diff --git a/brewman/brewman/static/partial/group-list.html b/brewman/brewman/static/partial/group-list.html index a0888be4..27bfa242 100644 --- a/brewman/brewman/static/partial/group-list.html +++ b/brewman/brewman/static/partial/group-list.html @@ -1,5 +1,5 @@ Users Add -
Code
+
diff --git a/brewman/brewman/static/partial/issue.html b/brewman/brewman/static/partial/issue.html index c17041e2..0b724102 100644 --- a/brewman/brewman/static/partial/issue.html +++ b/brewman/brewman/static/partial/issue.html @@ -4,7 +4,7 @@
-
Name
+
@@ -25,7 +25,7 @@
- +
@@ -73,7 +73,7 @@
-
Amount
+
diff --git a/brewman/brewman/static/partial/journal.html b/brewman/brewman/static/partial/journal.html index 2f672dfa..e49afdaf 100644 --- a/brewman/brewman/static/partial/journal.html +++ b/brewman/brewman/static/partial/journal.html @@ -4,7 +4,7 @@
- +
@@ -27,7 +27,7 @@
-
Product
+
diff --git a/brewman/brewman/static/partial/ledger.html b/brewman/brewman/static/partial/ledger.html index 73b66704..5a6d3cb9 100644 --- a/brewman/brewman/static/partial/ledger.html +++ b/brewman/brewman/static/partial/ledger.html @@ -4,8 +4,8 @@
- - + +
@@ -20,7 +20,7 @@
-
Debit
+
@@ -33,19 +33,19 @@ - - + + - + - - - + + + - + diff --git a/brewman/brewman/static/partial/login.html b/brewman/brewman/static/partial/login.html index 753bf476..25de278a 100644 --- a/brewman/brewman/static/partial/login.html +++ b/brewman/brewman/static/partial/login.html @@ -14,6 +14,21 @@ + ClientID {{clientID}} +
+ + +
+ +
+
+
+ + +
+ +
+
diff --git a/brewman/brewman/static/partial/payment.html b/brewman/brewman/static/partial/payment.html index 0eac6fd5..b65d2704 100644 --- a/brewman/brewman/static/partial/payment.html +++ b/brewman/brewman/static/partial/payment.html @@ -3,7 +3,7 @@
- +
@@ -11,7 +11,7 @@
+ ng-options="l.model.LedgerID as l.label for l in ledgers">
₹
-
Date
{{item.Date}}
{{item.Date}} {{item.Name}}{{item.Type}}{{item.Type}} {{item.Narration}}{{item.Debit}}{{item.Credit}}{{item.Running}}{{item.Debit}}{{item.Credit}}{{item.Running}}
{{info.Footer.Date}}{{info.Footer.Date}} {{info.Footer.Name}} {{info.Footer.Type}} {{info.Footer.Narration}}
+
diff --git a/brewman/brewman/static/partial/product-group-list.html b/brewman/brewman/static/partial/product-group-list.html index 895c5977..be5c1182 100644 --- a/brewman/brewman/static/partial/product-group-list.html +++ b/brewman/brewman/static/partial/product-group-list.html @@ -1,5 +1,5 @@ Product Groups Add -
Name
+
diff --git a/brewman/brewman/static/partial/product-ledger.html b/brewman/brewman/static/partial/product-ledger.html index dc60e3d0..6992e462 100644 --- a/brewman/brewman/static/partial/product-ledger.html +++ b/brewman/brewman/static/partial/product-ledger.html @@ -4,8 +4,8 @@
- - + +
@@ -21,7 +21,7 @@
-
Name
+
diff --git a/brewman/brewman/static/partial/product-list.html b/brewman/brewman/static/partial/product-list.html index 913d49a4..99635454 100644 --- a/brewman/brewman/static/partial/product-list.html +++ b/brewman/brewman/static/partial/product-list.html @@ -1,5 +1,5 @@ Products Add -
Date
+
diff --git a/brewman/brewman/static/partial/profit-loss.html b/brewman/brewman/static/partial/profit-loss.html index d7d47ef4..3f5a0013 100644 --- a/brewman/brewman/static/partial/profit-loss.html +++ b/brewman/brewman/static/partial/profit-loss.html @@ -4,8 +4,8 @@
- - + +
@@ -13,7 +13,7 @@
-
Code
+
@@ -45,7 +45,7 @@
-
Group
+
diff --git a/brewman/brewman/static/partial/purchase-entries.html b/brewman/brewman/static/partial/purchase-entries.html index f1dcfa44..d7c97d5a 100644 --- a/brewman/brewman/static/partial/purchase-entries.html +++ b/brewman/brewman/static/partial/purchase-entries.html @@ -4,8 +4,8 @@
- - + +
@@ -13,7 +13,7 @@
-
Group
+
diff --git a/brewman/brewman/static/partial/purchase-return.html b/brewman/brewman/static/partial/purchase-return.html index 4da6e3a4..18e31071 100644 --- a/brewman/brewman/static/partial/purchase-return.html +++ b/brewman/brewman/static/partial/purchase-return.html @@ -4,7 +4,7 @@
- +
@@ -37,7 +37,7 @@
-
Date
+
diff --git a/brewman/brewman/static/partial/purchase.html b/brewman/brewman/static/partial/purchase.html index 10016cb6..d5dd7bbb 100644 --- a/brewman/brewman/static/partial/purchase.html +++ b/brewman/brewman/static/partial/purchase.html @@ -4,7 +4,7 @@
- +
@@ -44,7 +44,7 @@
-
Product
+
diff --git a/brewman/brewman/static/partial/raw-material-cost-detail.html b/brewman/brewman/static/partial/raw-material-cost-detail.html index 3a5aeb5c..a02ebd89 100644 --- a/brewman/brewman/static/partial/raw-material-cost-detail.html +++ b/brewman/brewman/static/partial/raw-material-cost-detail.html @@ -4,8 +4,8 @@
- - + +
@@ -13,7 +13,7 @@
-
Product
+
@@ -24,7 +24,7 @@ - + diff --git a/brewman/brewman/static/partial/raw-material-cost.html b/brewman/brewman/static/partial/raw-material-cost.html index 554e8190..ef656e38 100644 --- a/brewman/brewman/static/partial/raw-material-cost.html +++ b/brewman/brewman/static/partial/raw-material-cost.html @@ -4,8 +4,8 @@
- - + +
@@ -13,7 +13,7 @@
-
Product
{{item.Name}} {{item.Group}} {{item.Quantity}}
+
diff --git a/brewman/brewman/static/partial/receipt.html b/brewman/brewman/static/partial/receipt.html index 733e4a6b..454df69a 100644 --- a/brewman/brewman/static/partial/receipt.html +++ b/brewman/brewman/static/partial/receipt.html @@ -4,7 +4,7 @@
- +
@@ -12,7 +12,7 @@
+ ng-options="l.model.LedgerID as l.label for l in ledgers">
₹
-
Cost Center
+
diff --git a/brewman/brewman/static/partial/trial-balance.html b/brewman/brewman/static/partial/trial-balance.html index 9f1c2e5b..0ece9409 100644 --- a/brewman/brewman/static/partial/trial-balance.html +++ b/brewman/brewman/static/partial/trial-balance.html @@ -12,7 +12,7 @@
-
Name
+
diff --git a/brewman/brewman/static/partial/unposted.html b/brewman/brewman/static/partial/unposted.html index 4fb196b4..c749fae4 100644 --- a/brewman/brewman/static/partial/unposted.html +++ b/brewman/brewman/static/partial/unposted.html @@ -4,7 +4,7 @@
-
Type
+
diff --git a/brewman/brewman/static/partial/user-list.html b/brewman/brewman/static/partial/user-list.html index 26f34aaf..a5a6fdd4 100644 --- a/brewman/brewman/static/partial/user-list.html +++ b/brewman/brewman/static/partial/user-list.html @@ -1,5 +1,5 @@ Users Add -
Select
+
diff --git a/brewman/brewman/static/scripts/angular_directive.js b/brewman/brewman/static/scripts/angular_directive.js index d28db9b6..802d9845 100644 --- a/brewman/brewman/static/scripts/angular_directive.js +++ b/brewman/brewman/static/scripts/angular_directive.js @@ -33,7 +33,7 @@ overlord_directive.directive('ngAutocomplete', function ($q, $parse) { }; }); -overlord_directive.directive('datepicker', function () { +overlord_directive.directive('datepicker', function ($parse) { return { restrict:'E', replace:true, @@ -42,11 +42,13 @@ overlord_directive.directive('datepicker', function () { link:function (scope, element, attrs) { element.datepicker({ dateFormat:'dd-M-yy', + showOtherMonths:true, + selectOtherMonths:true, onSelect:function (dateText, inst) { - if (typeof attrs.model !== 'undefined') { - scope[attrs.model][attrs.prop] = dateText; - scope.$apply(); - } + var model = $parse(attrs.model); + var modelSetter = model.assign; + modelSetter(scope, dateText); + scope.$apply(); } }); } diff --git a/brewman/brewman/static/scripts/angular_service.js b/brewman/brewman/static/scripts/angular_service.js index 08652d65..dc92b3fa 100644 --- a/brewman/brewman/static/scripts/angular_service.js +++ b/brewman/brewman/static/scripts/angular_service.js @@ -2,12 +2,10 @@ var overlord_service = angular.module('overlord.service', ['ngResource']); -// TODO: Replace hardcoded url with route_url overlord_service.factory('IssueGrid', ['$resource', function ($resource) { return $resource('/Issues/Services/:date'); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('Voucher', ['$resource', function ($resource) { return $resource('/Voucher/:id', {id:'@VoucherID'}, { @@ -15,7 +13,6 @@ overlord_service.factory('Voucher', ['$resource', function ($resource) { }); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('Account', ['$resource', function ($resource) { return $resource('/Account/:id', {id:'@LedgerID'}, { @@ -24,7 +21,6 @@ overlord_service.factory('Account', ['$resource', function ($resource) { }); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('Employee', ['$resource', function ($resource) { return $resource('/Employee/:id', {id:'@LedgerID'}, { @@ -33,17 +29,14 @@ overlord_service.factory('Employee', ['$resource', function ($resource) { }); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('Ledger', ['$resource', function ($resource) { return $resource('/Ledger/:id'); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('ProductLedger', ['$resource', function ($resource) { return $resource('/ProductLedger/:id'); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('CostCenter', ['$resource', function ($resource) { return $resource('/CostCenter/:id', {id:'@CostCenterID'}, { @@ -51,12 +44,10 @@ overlord_service.factory('CostCenter', ['$resource', function ($resource) { }); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('AccountType', ['$resource', function ($resource) { return $resource('/AccountTypes'); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('User', ['$resource', function ($resource) { return $resource('/User/:id', {id:'@UserID'}, { @@ -64,7 +55,6 @@ overlord_service.factory('User', ['$resource', function ($resource) { }); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('Group', ['$resource', function ($resource) { return $resource('/Group/:id', {id:'@GroupID'}, { @@ -72,7 +62,13 @@ overlord_service.factory('Group', ['$resource', function ($resource) { }); }]); -// TODO: Replace hardcoded url with route_url +overlord_service.factory('Client', ['$resource', function ($resource) { + return $resource('/Client/:id', + {id:'@ClientID'}, { + query:{method:'GET', params:{list:true}, isArray:true} + }); +}]); + overlord_service.factory('Product', ['$resource', function ($resource) { return $resource('/Product/:id', {id:'@ProductID'}, { @@ -81,14 +77,12 @@ overlord_service.factory('Product', ['$resource', function ($resource) { }); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('Batch', ['$resource', function ($resource) { return $resource('/Batch', {}, { autocomplete:{method:'GET', params:{term:''}, isArray:true} }); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('ProductGroup', ['$resource', function ($resource) { return $resource('/ProductGroup/:id', {id:'@ProductGroupID'}, { @@ -96,7 +90,6 @@ overlord_service.factory('ProductGroup', ['$resource', function ($resource) { }); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('CostCenter', ['$resource', function ($resource) { return $resource('/CostCenter/:id', {id:'@CostCenterID'}, { @@ -104,65 +97,52 @@ overlord_service.factory('CostCenter', ['$resource', function ($resource) { }); }]); - -// TODO: Replace hardcoded url with route_url overlord_service.factory('TrialBalance', ['$resource', function ($resource) { return $resource('/TrialBalance/:date'); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('ClosingStock', ['$resource', function ($resource) { return $resource('/ClosingStock/:date'); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('CashFlow', ['$resource', function ($resource) { return $resource('/CashFlow/:id'); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('Daybook', ['$resource', function ($resource) { return $resource('/Daybook'); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('Unposted', ['$resource', function ($resource) { return $resource('/Unposted'); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('ProfitLoss', ['$resource', function ($resource) { return $resource('/ProfitLoss'); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('PurchaseEntries', ['$resource', function ($resource) { return $resource('/PurchaseEntries'); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('RawMaterialCost', ['$resource', function ($resource) { return $resource('/RawMaterialCost/:id'); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('Auth', ['$resource', function ($resource) { return $resource('/Auth', {}, { login:{method:'GET', params:{list:true}, isArray:true} }); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('Attendance', ['$resource', function ($resource) { return $resource('/Attendance/:date', {date:'@Date'}); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('AttendanceTypes', ['$resource', function ($resource) { return $resource('/AttendanceTypes'); }]); -// TODO: Replace hardcoded url with route_url overlord_service.factory('EmployeeAttendance', ['$resource', function ($resource) { return $resource('/EmployeeAttendance/:id', {id:'@Employee.LedgerID'}); }]); diff --git a/brewman/brewman/static/scripts/client.js b/brewman/brewman/static/scripts/client.js new file mode 100644 index 00000000..f79c6d7d --- /dev/null +++ b/brewman/brewman/static/scripts/client.js @@ -0,0 +1,18 @@ +'use strict'; + +function ClientCtrl($scope, clients) { + $scope.info = clients; +} + +ClientCtrl.resolve = { + clients:function ($q, Client) { + var deferred = $q.defer(); + + var successCb = function (result) { + deferred.resolve(result); + }; + + Client.query({}, successCb); + return deferred.promise; + } +}; diff --git a/brewman/brewman/static/scripts/employee-attendance.js b/brewman/brewman/static/scripts/employee-attendance.js index 0ca71f95..485c0667 100644 --- a/brewman/brewman/static/scripts/employee-attendance.js +++ b/brewman/brewman/static/scripts/employee-attendance.js @@ -4,17 +4,36 @@ function EmployeeAttendanceCtrl($scope, $location, EmployeeAttendance, attendanc $scope.attendance_types = attendance_types; $scope.info = info; $scope.show = function () { - $location.path('/EmployeeAttendance/' + $scope.info.Employee.LedgerID).search({StartDate:$scope.info.StartDate, FinishDate:$scope.info.FinishDate}); + var id = $scope.info.Ledger.LedgerID; + var start_date = $scope.info.StartDate; + var finish_date = $scope.info.FinishDate; + if (id == $routeParams.id && start_date == $routeParams.StartDate && finish_date == $routeParams.FinishDate) { + EmployeeAttendance.get({id:id, StartDate:start_date, FinishDate:finish_date}, function (data) { + $scope.info = data; + }); + } + else { + $location.path('/EmployeeAttendance/' + $scope.info.Employee.LedgerID).search({StartDate:$scope.info.StartDate, FinishDate:$scope.info.FinishDate}); + } }; $scope.save = function () { $scope.info.$save(function (u, putResponseHeaders) { - $scope.toasts.push({Type:'Success', Message:u.Code}); + $scope.toasts.push({Type:'Success', Message:'Saved!'}); }, function (data, status) { $scope.toasts.push({Type:'Error', Message:data.data}); }); }; $('#txtEmployee').focus(); } + +function EmployeeAttendanceSubCtrl($scope) { + $scope.original = {}; + angular.copy($scope.item, $scope.original); + + $scope.isDirty = function(){ + return !angular.equals($scope.original, $scope.item) + } +} EmployeeAttendanceCtrl.resolve = { info:function ($q, $route, EmployeeAttendance) { var deferred = $q.defer(); diff --git a/brewman/brewman/static/scripts/employee-functions.js b/brewman/brewman/static/scripts/employee-functions.js index e5f4fb4b..abd8cb61 100644 --- a/brewman/brewman/static/scripts/employee-functions.js +++ b/brewman/brewman/static/scripts/employee-functions.js @@ -1,12 +1,24 @@ 'use strict'; -function EmployeeFunctionsCtrl($scope) { +function EmployeeFunctionsCtrl($scope, $http) { + $scope.record = {}; $scope.creditSalary = function () { - $scope.toasts.push({Type:'Error', Message:'Not Implemented Yet'}); + $http. + post('/CreditSalary', {Month:$scope.salaryMonth}). + success(function (data) { + $scope.toasts.push({Type:'Success', Message:data.message}); + }). + error(function (errorMessage) { + $scope.toasts.push({Type:'Error', Message:errorMessage}); + }); }; $scope.creditEsiPf = function () { $scope.toasts.push({Type:'Error', Message:'Not Implemented Yet'}); }; + $scope.attendanceRecordUrl = function () { + var url = '/AttendanceReport?StartDate=' + $scope.record.Start + "&FinishDate=" + $scope.record.Finish; + return url; + }; $scope.uploadFingerprints = function () { $scope.toasts.push({Type:'Error', Message:'Not Implemented Yet'}); }; diff --git a/brewman/brewman/static/scripts/issue.js b/brewman/brewman/static/scripts/issue.js index 5dd48674..774f4907 100644 --- a/brewman/brewman/static/scripts/issue.js +++ b/brewman/brewman/static/scripts/issue.js @@ -25,6 +25,16 @@ function IssueCtrl($scope, $routeParams, $location, voucher, cost_centers, issue quantity = Number($scope.quantity), consumption = (getDebitCredit($scope.voucher.Journals, -1) == '7b845f95-dfef-fa4a-897c-f0baf15284a3'); + if (typeof oldInventory !== 'undefined' && + oldInventory.Batch.BatchID === $scope.batch.BatchID && + (!consumption || oldInventory.Quantity + quantity <= $scope.batch.QuantityRemaining)) { + oldInventory.Quantity += quantity; + delete $scope.batch; + delete $scope.quantity; + $('#txtBatch').val(''); + $('#txtBatch').focus(); + } + if (typeof oldInventory === 'undefined' && (!consumption || quantity <= $scope.batch.QuantityRemaining)) { this.voucher.Inventories.push( { diff --git a/brewman/brewman/static/scripts/ledger.js b/brewman/brewman/static/scripts/ledger.js index 826c95fa..7065bca6 100644 --- a/brewman/brewman/static/scripts/ledger.js +++ b/brewman/brewman/static/scripts/ledger.js @@ -1,9 +1,19 @@ 'use strict'; -function LedgerCtrl($scope, $location, ledger) { +function LedgerCtrl($scope, $routeParams, $location, ledger, Ledger) { $scope.info = ledger; $scope.show = function () { - $location.path('/Ledger/' + $scope.info.Ledger.LedgerID).search('StartDate', $scope.info.StartDate).search('FinishDate', $scope.info.FinishDate); + var id = $scope.info.Ledger.LedgerID; + var start_date = $scope.info.StartDate; + var finish_date = $scope.info.FinishDate; + if (id == $routeParams.id && start_date == $routeParams.StartDate && finish_date == $routeParams.FinishDate) { + Ledger.get({id:id, StartDate:start_date, FinishDate:finish_date}, function (data) { + $scope.info = data; + }); + } + else { + $location.path('/Ledger/' + $scope.info.Ledger.LedgerID).search('StartDate', $scope.info.StartDate).search('FinishDate', $scope.info.FinishDate); + } }; $scope.downloadTable = function () { var table = $('#gvGrid'), diff --git a/brewman/brewman/static/scripts/login.js b/brewman/brewman/static/scripts/login.js index ba4d51f2..302e5243 100644 --- a/brewman/brewman/static/scripts/login.js +++ b/brewman/brewman/static/scripts/login.js @@ -1,9 +1,17 @@ 'use strict'; -function LoginCtrl($scope, $http, $location, $routeParams) { +function LoginCtrl($scope, $http, $location, $routeParams, $cookieStore) { + $scope.showOTP = false; $scope.submit = function () { + if ($scope.showOTP) { + var data = {username:$scope.username, password:$scope.password, otp:$scope.otp, ClientName:$scope.name}; + } else { + var data = {username:$scope.username, password:$scope.password}; + } + + $http. - post('/login', {username:$scope.username, password:$scope.password}). + post('/login', data). success(function () { var came_from = $routeParams.came_from; if (typeof came_from === 'undefined' || came_from === "" || came_from === "/login" || came_from === "/logout") { @@ -11,8 +19,12 @@ function LoginCtrl($scope, $http, $location, $routeParams) { } $location.path(came_from).search({}); }). - error(function (errorMessage) { - $scope.toasts.push({Type:'Error', Message:errorMessage}); + error(function (data, status, headers, config) { + if (status === 403 && ['Unknown Client', 'OTP not supplied', 'OTP is wrong'].indexOf(data) !== -1) { + $scope.showOTP = true; + $scope.clientID = $cookieStore.get('ClientID'); + } + $scope.toasts.push({Type:'Error', Message:data}); }); }; $('#username').focus(); diff --git a/brewman/brewman/static/scripts/overlord.js b/brewman/brewman/static/scripts/overlord.js index 2d61a7bf..99e1b952 100644 --- a/brewman/brewman/static/scripts/overlord.js +++ b/brewman/brewman/static/scripts/overlord.js @@ -1,6 +1,6 @@ 'use strict'; -var overlord = angular.module('overlord', ['overlord.directive', 'overlord.filter', 'overlord.service']) +var overlord = angular.module('overlord', ['overlord.directive', 'overlord.filter', 'overlord.service', 'ngCookies']) .config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) { $routeProvider. when('/', {templateUrl:'/partial/home.html'}). @@ -83,6 +83,8 @@ var overlord = angular.module('overlord', ['overlord.directive', 'overlord.filte when('/Group', {templateUrl:'/partial/group-detail.html', controller:GroupCtrl, resolve:GroupCtrl.resolve}). when('/Group/:id', {templateUrl:'/partial/group-detail.html', controller:GroupCtrl, resolve:GroupCtrl.resolve}). + when('/Clients', {templateUrl:'/partial/client-list.html', controller:ClientCtrl, resolve:ClientCtrl.resolve}). + otherwise({templateUrl:'/partial/404.html'}); $locationProvider.html5Mode(true).hashPrefix('!'); }]) diff --git a/brewman/brewman/templates/angular_base.mako b/brewman/brewman/templates/angular_base.mako index e7aed612..4f370a6b 100644 --- a/brewman/brewman/templates/angular_base.mako +++ b/brewman/brewman/templates/angular_base.mako @@ -25,6 +25,7 @@ + @@ -58,6 +59,7 @@ + @@ -135,11 +137,8 @@
  • Employees
  • Attendance
  • Employee Attendance
  • -
  • Attendance Record
  • +
  • Employee Functions
  • Salary Sheet
  • -
  • Credit Salary
  • -
  • Credit Esi Pf
  • -
  • Finger Print
  • Employee Payment Sheet
  • Display Salary Sheet
  • @@ -162,6 +161,7 @@
  • Change Password
  • Users
  • Groups
  • +
  • Clients
  • Split Data
  • diff --git a/brewman/brewman/views/__init__.py b/brewman/brewman/views/__init__.py index ac8dbbf3..c913fc55 100644 --- a/brewman/brewman/views/__init__.py +++ b/brewman/brewman/views/__init__.py @@ -2,7 +2,7 @@ from decimal import Decimal import json import os import uuid -from pyramid.httpexceptions import HTTPForbidden, HTTPFound, exception_response +from pyramid.httpexceptions import HTTPForbidden, HTTPFound from pyramid.response import FileResponse, Response from pyramid.security import forget from pyramid.view import view_config @@ -22,13 +22,6 @@ def forbidden(request): else: return HTTPFound(location=request.route_url('login')) -@view_config(route_name='logout') -def logout(request): - request.session.invalidate() - headers = forget(request) - return HTTPFound(location=request.route_url('home'), headers=headers) - - @view_config(route_name='favicon') def favicon(request): here = os.path.dirname(__file__) @@ -50,21 +43,3 @@ class DecimalEncoder(json.JSONEncoder): return str(obj) return json.JSONEncoder.default(self, obj) -class my_json_renderer_factory: - def __init__(self, info): - """ Constructor: info will be an object having the - following attributes: name (the renderer name), package - (the package that was 'current' at the time the - renderer was registered), type (the renderer type - name), registry (the current application registry) and - settings (the deployment settings dictionary). """ -# print(info) - - def __call__(self, value, system): - request = system.get('request') - if request is not None: - response = request.response - ct = response.content_type - if ct == response.default_content_type: - response.content_type = 'application/json' - return json.dumps(value, cls=DecimalEncoder) diff --git a/brewman/brewman/views/account.py b/brewman/brewman/views/account.py index d5e6c64f..5d5def47 100644 --- a/brewman/brewman/views/account.py +++ b/brewman/brewman/views/account.py @@ -16,25 +16,33 @@ def html(request): return {} -@view_config(request_method='POST', route_name='account_id', renderer='json', xhr=True, permission='Accounts') @view_config(request_method='POST', route_name='account', renderer='json', xhr=True, permission='Accounts') -def save_update(request): +def save(request): try: - id = request.matchdict.get('id', None) - if id is None: - item = Ledger(code=0, name=request.json_body['Name'], type=int(request.json_body['Type']), - is_active=request.json_body['IsActive'], is_reconcilble=request.json_body['IsReconcilable'], - costcenter_id=uuid.UUID(request.json_body['CostCenter']['CostCenterID'])).create() - else: - new_type = int(request.json_body['Type']) - item = Ledger.by_id(uuid.UUID(id)) - if not item.type == new_type: - item.code = Ledger.get_code(new_type) - item.type = new_type - item.name = request.json_body['Name'] - item.is_active = request.json_body['IsActive'] - item.is_reconcilable = request.json_body['IsReconcilable'] - item.costcenter_id = uuid.UUID(request.json_body['CostCenter']['CostCenterID']) + item = Ledger(code=0, name=request.json_body['Name'], type=int(request.json_body['Type']), + is_active=request.json_body['IsActive'], is_reconcilble=request.json_body['IsReconcilable'], + costcenter_id=uuid.UUID(request.json_body['CostCenter']['CostCenterID'])).create() + transaction.commit() + return account_info(item.id) + except ValidationError as ex: + transaction.abort() + response = Response("Failed validation: {0}".format(ex.message)) + response.status_int = 500 + return response + + +@view_config(request_method='POST', route_name='account_id', renderer='json', xhr=True, permission='Accounts') +def update(request): + try: + item = Ledger.by_id(uuid.UUID(request.matchdict['id'])) + new_type = int(request.json_body['Type']) + if not item.type == new_type: + item.code = Ledger.get_code(new_type) + item.type = new_type + item.name = request.json_body['Name'] + item.is_active = request.json_body['IsActive'] + item.is_reconcilable = request.json_body['IsReconcilable'] + item.costcenter_id = uuid.UUID(request.json_body['CostCenter']['CostCenterID']) transaction.commit() return account_info(item.id) except ValidationError as ex: diff --git a/brewman/brewman/views/attendance.py b/brewman/brewman/views/attendance.py index 0a294d3d..dd39e571 100644 --- a/brewman/brewman/views/attendance.py +++ b/brewman/brewman/views/attendance.py @@ -102,7 +102,7 @@ def employee_attendance_report(request): start_date = request.GET.get('StartDate', session_period_start(request)) finish_date = request.GET.get('FinishDate', session_period_finish(request)) info = {'StartDate': start_date, 'FinishDate': finish_date, - 'Employee': {'LedgerID': employee.id, 'Name': employee.name}, 'Body': [], + 'Employee': {'LedgerID': employee.id, 'Name': employee.name}, 'Body': employee_attendance(employee, start_date, finish_date)} return info @@ -129,9 +129,15 @@ def employee_attendance(employee, start_date, finish_date): permission='Attendance') def save_employee_attendance(request): try: + start_date = None + finish_date = None user_id = uuid.UUID(authenticated_userid(request)) employee = Employee.by_id(uuid.UUID(request.matchdict['id'])) for item in request.json_body['Body']: + if start_date is None: + start_date = item['Date'] + finish_date = item['Date'] + attendance_type = item['AttendanceTypeID'] if attendance_type != 0: date = datetime.datetime.strptime(item['Date'], '%d-%b-%Y') @@ -139,7 +145,9 @@ def save_employee_attendance(request): user_id=user_id) attendance.create() transaction.commit() - return employee_attendance(employee, request.json_body['StartDate'], request.json_body['FinishDate']) + return {'StartDate': start_date, 'FinishDate': finish_date, + 'Employee': {'LedgerID': employee.id, 'Name': employee.name}, + 'Body': employee_attendance(employee, start_date, finish_date)} except ValidationError as ex: transaction.abort() response = Response("Failed validation: {0}".format(ex.message)) diff --git a/brewman/brewman/views/attendance_report.py b/brewman/brewman/views/attendance_report.py new file mode 100644 index 00000000..d861a16e --- /dev/null +++ b/brewman/brewman/views/attendance_report.py @@ -0,0 +1,104 @@ +import calendar +import datetime +import uuid +from pyramid.response import Response +from pyramid.security import authenticated_userid +from pyramid.view import view_config +from sqlalchemy import or_ +import transaction +from brewman.models import DBSession +from brewman.models.auth import User +from brewman.models.master import AttendanceType, Employee, Ledger +from brewman.models.validation_exception import ValidationError +from brewman.models.voucher import Attendance, Voucher, VoucherType, Journal +from brewman.views.attendance import daterange +from brewman.views.services.session import session_period_start, session_period_finish, get_last_day, get_first_day + +__author__ = 'tanshu' + +@view_config(route_name='attendance_report', renderer='csv', permission='Attendance') +def get_report(request): + start_date = request.GET.get('StartDate', session_period_start(request)) + finish_date = request.GET.get('FinishDate', session_period_finish(request)) + start_date = datetime.datetime.strptime(start_date, '%d-%b-%Y') + finish_date = datetime.datetime.strptime(finish_date, '%d-%b-%Y') + header, report = attendance_record(start_date, finish_date) + return {'header': header, 'rows': report, 'filename': 'Attendance Record.csv'} + + +def attendance_record(start_date, finish_date): + report = [] + header = ["Code", "Name", "Designation", "Department", "Salary", "SC Points"] + for date in daterange(start_date, finish_date, inclusive=True): + header.append(date.strftime('%d-%b')) + + not_set = AttendanceType.by_id(0) + 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: + row_display = [employee.code, employee.name, employee.designation, employee.costcenter.name, employee.salary, + employee.service_points] + row_value = ['', '', '', '', employee.salary, employee.service_points] + for date in daterange(start_date, finish_date, inclusive=True): + att = DBSession.query(Attendance)\ + .filter(Attendance.employee_id == employee.id)\ + .filter(Attendance.date == date)\ + .filter(Attendance.is_valid == True)\ + .first() + att = not_set if att is None else AttendanceType.by_id(att.attendance_type) + row_display.append(att.name) + row_value.append(att.value) + report.extend([tuple(row_display), tuple(row_value)]) + return header, report + + +@view_config(request_method='POST', route_name='credit_salary', renderer='json', xhr=True, permission='Attendance') +def credit_salary(request): + try: + user = User.by_id(uuid.UUID(authenticated_userid(request))) + month = datetime.datetime.strptime(request.json_body['Month'], '%d-%b-%Y') + + start_date = get_first_day(month) + finish_date = get_last_day(month) + voucher = Voucher(date=finish_date, narration='Auto Generated Salary Entry', user_id=user.id, + type=VoucherType.by_name('Journal'), posted=True, poster_id=user.id) + DBSession.add(voucher) + for item in salary_journals(start_date, finish_date): + voucher.journals.append(item) + DBSession.add(item) + transaction.commit() + return {'message': 'Salary Entry created'} + except ValidationError as ex: + transaction.abort() + response = Response("Failed validation: {0}".format(ex.message)) + response.status_int = 500 + return response + + +def salary_journals(start_date, finish_date): + days = calendar.monthrange(start_date.year, start_date.month)[1] + finish_date = finish_date + datetime.timedelta(1) + amount = 0 + 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: + 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)) + att = round(att * employee.salary / days) + amount += att + journals.append(Journal(amount=att, debit=-1, ledger_id=employee.id, cost_center_id=employee.costcenter_id)) + salary = Ledger.salary() + salary = Ledger.by_id(uuid.UUID(salary['LedgerID'])) + journals.append(Journal(amount=amount, debit=1, ledger_id=salary.id, cost_center_id=salary.costcenter_id)) + return journals + diff --git a/brewman/brewman/views/auth/client.py b/brewman/brewman/views/auth/client.py new file mode 100644 index 00000000..d8c0f39f --- /dev/null +++ b/brewman/brewman/views/auth/client.py @@ -0,0 +1,39 @@ +import uuid +from pyramid.response import Response + +from pyramid.view import view_config +import transaction +from brewman.models.auth import Client + +from brewman.models.validation_exception import ValidationError + + +@view_config(request_method='GET', route_name='client_list', renderer='brewman:templates/angular_base.mako', + xhr=False, permission='Clients') +def html(request): + return {} + + +@view_config(request_method='POST', route_name='client_id', renderer='json', xhr=True, permission='Clients') +def update(request): + try: + item = Client.by_id(uuid.UUID(request.matchdict.get['id'])) + item.enabled = request.json_body['Enabled'] + transaction.commit() + return {} + except ValidationError as ex: + transaction.abort() + response = Response("Failed validation: {0}".format(ex.message)) + response.status_int = 500 + return response + + +@view_config(request_method='GET', route_name='client', request_param='list', renderer='json', xhr=True, + permission='Clients') +def show_list(request): + list = Client.list() + clients = [] + for item in list: + clients.append( + {'ClientID': item.id, 'Code': item.code, 'Name': item.name, 'Enabled': item.enabled, 'OTP': item.otp}) + return clients \ No newline at end of file diff --git a/brewman/brewman/views/auth/group.py b/brewman/brewman/views/auth/group.py index 086eeeb8..0e47347a 100644 --- a/brewman/brewman/views/auth/group.py +++ b/brewman/brewman/views/auth/group.py @@ -34,7 +34,7 @@ def save(request): def update(request): try: id = request.matchdict.get('id', None) - group = Group.get_by_id(uuid.UUID(id)) + group = Group.by_id(uuid.UUID(id)) group.name = request.json_body['Name'] add_permissions(group, request.json_body['Permissions']) transaction.commit() @@ -52,7 +52,7 @@ def add_permissions(group, permissions): gp = [p for p in group.roles if p.id == id] gp = None if len(gp) == 0 else gp[0] if permission['Enabled'] and gp is None: - group.roles.append(Role.get_by_id(id)) + group.roles.append(Role.by_id(id)) elif not permission['Enabled'] and gp: group.roles.remove(gp) @@ -98,7 +98,7 @@ def group_info(id): for item in Role.list(): group['Permissions'].append({'PermissionID': item.id, 'Name': item.name, 'Enabled': False}) else: - group_object = Group.get_by_id(id) + group_object = Group.by_id(id) group = {'GroupID': group_object.id, 'Name': group_object.name, 'Permissions': []} for item in Role.list(): group['Permissions'].append( diff --git a/brewman/brewman/views/auth/user.py b/brewman/brewman/views/auth/user.py index 19f94f35..8e8e814d 100644 --- a/brewman/brewman/views/auth/user.py +++ b/brewman/brewman/views/auth/user.py @@ -36,7 +36,7 @@ def save(request): def update(request): try: id = request.matchdict.get('id', None) - user = User.get_by_id(uuid.UUID(id)) + user = User.by_id(uuid.UUID(id)) user.name = request.json_body['Name'] user.locked_out = request.json_body['LockedOut'] if request.json_body['Password'] != '' and request.json_body['Password'] != user.password: @@ -57,7 +57,7 @@ def add_groups(user, groups): ug = [g for g in user.groups if g.id == id] ug = None if len(ug) == 0 else ug[0] if group['Enabled'] and ug is None: - user.groups.append(Group.get_by_id(id)) + user.groups.append(Group.by_id(id)) elif not group['Enabled'] and ug: user.groups.remove(ug) @@ -104,7 +104,7 @@ def user_info(id): for item in Group.list(): account['Groups'].append({'GroupID': item.id, 'Name': item.name, 'Enabled': False}) else: - user = User.get_by_id(id) + user = User.by_id(id) account = {'UserID': user.id, 'Name': user.name, 'Password': '', 'LockedOut': user.locked_out, 'Groups': []} for item in Group.list(): account['Groups'].append( diff --git a/brewman/brewman/views/cost_center.py b/brewman/brewman/views/cost_center.py index 4cdd28b9..82446b70 100644 --- a/brewman/brewman/views/cost_center.py +++ b/brewman/brewman/views/cost_center.py @@ -18,17 +18,25 @@ def html(request): return {} -@view_config(request_method='POST', route_name='cost_center_id', renderer='json', xhr=True, permission='Cost Centers') @view_config(request_method='POST', route_name='cost_center', renderer='json', xhr=True, permission='Cost Centers') -def save_update(request): +def save(request): try: - id = request.matchdict.get('id', None) - if id is None: - item = CostCenter(request.json_body['Name']) - DBSession.add(item) - else: - item = CostCenter.by_id(uuid.UUID(id)) - item.name = request.json_body['Name'] + item = CostCenter(request.json_body['Name']) + DBSession.add(item) + transaction.commit() + return cost_center_info(item.id) + except ValidationError as ex: + transaction.abort() + response = Response("Failed validation: {0}".format(ex.message)) + response.status_int = 500 + return response + + +@view_config(request_method='POST', route_name='cost_center_id', renderer='json', xhr=True, permission='Cost Centers') +def update(request): + try: + item = CostCenter.by_id(uuid.UUID(request.matchdict.get['id'])) + item.name = request.json_body['Name'] transaction.commit() return cost_center_info(item.id) except ValidationError as ex: diff --git a/brewman/brewman/views/employee.py b/brewman/brewman/views/employee.py index 36bc549d..d46d11e0 100644 --- a/brewman/brewman/views/employee.py +++ b/brewman/brewman/views/employee.py @@ -14,6 +14,8 @@ from brewman.views.account import ledger_list permission='Employees') @view_config(request_method='GET', route_name='employee', renderer='brewman:templates/angular_base.mako', xhr=False, permission='Employees') +@view_config(request_method='GET', route_name='employee_functions', renderer='brewman:templates/angular_base.mako', xhr=False, + permission='Employees') def html(request): return {} diff --git a/brewman/brewman/views/product.py b/brewman/brewman/views/product.py index 8fcc48b6..d68ee90e 100644 --- a/brewman/brewman/views/product.py +++ b/brewman/brewman/views/product.py @@ -5,9 +5,8 @@ from pyramid.response import Response from pyramid.view import view_config import transaction -from brewman.models.master import Product, CostCenter, LedgerType, Ledger, ProductGroup +from brewman.models.master import Product, CostCenter, LedgerType, Ledger from brewman.models.validation_exception import ValidationError -from brewman.models.voucher import Batch @view_config(route_name='product_list', renderer='brewman:templates/angular_base.mako', permission='Authenticated') @view_config(request_method='GET', route_name='product_id', renderer='brewman:templates/angular_base.mako', @@ -18,29 +17,37 @@ def html(request): return {} -@view_config(request_method='POST', route_name='product_id', renderer='json', xhr=True, permission='Products') @view_config(request_method='POST', route_name='product', renderer='json', xhr=True, permission='Products') -def save_update(request): +def save(request): try: - id = request.matchdict.get('id', None) - if id is None: - item = Product(0, request.json_body['Name'], request.json_body['Units'], - Decimal(request.json_body['Fraction']), request.json_body['FractionUnits'], - Decimal(request.json_body['Yeild']), request.json_body['ShowForPurchase'], - uuid.UUID(request.json_body['ProductGroup']['ProductGroupID']), Ledger.all_purchases(), - Decimal(request.json_body['Price']), request.json_body['Discontinued']).create() - else: - item = Product.by_id(uuid.UUID(id)) - item.name = request.json_body['Name'] - item.units = request.json_body['Units'] - item.fraction = Decimal(request.json_body['Fraction']) - item.fraction_units = request.json_body['FractionUnits'] - item.yeild = Decimal(request.json_body['Yeild']) - item.show_for_purchase = request.json_body['ShowForPurchase'] - item.product_group_id = uuid.UUID(request.json_body['ProductGroup']['ProductGroupID']) - item.ledger_id = Ledger.all_purchases() - item.price = Decimal(request.json_body['Price']) - item.discontinued = request.json_body['Discontinued'] + item = Product(0, request.json_body['Name'], request.json_body['Units'], + Decimal(request.json_body['Fraction']), request.json_body['FractionUnits'], + Decimal(request.json_body['Yeild']), request.json_body['ShowForPurchase'], + uuid.UUID(request.json_body['ProductGroup']['ProductGroupID']), Ledger.all_purchases(), + Decimal(request.json_body['Price']), request.json_body['Discontinued']).create() + transaction.commit() + return product_info(item.id) + except ValidationError as ex: + transaction.abort() + response = Response("Failed validation: {0}".format(ex.message)) + response.status_int = 500 + return response + + +@view_config(request_method='POST', route_name='product_id', renderer='json', xhr=True, permission='Products') +def update(request): + try: + item = Product.by_id(uuid.UUID(request.matchdict.get['id'])) + item.name = request.json_body['Name'] + item.units = request.json_body['Units'] + item.fraction = Decimal(request.json_body['Fraction']) + item.fraction_units = request.json_body['FractionUnits'] + item.yeild = Decimal(request.json_body['Yeild']) + item.show_for_purchase = request.json_body['ShowForPurchase'] + item.product_group_id = uuid.UUID(request.json_body['ProductGroup']['ProductGroupID']) + item.ledger_id = Ledger.all_purchases() + item.price = Decimal(request.json_body['Price']) + item.discontinued = request.json_body['Discontinued'] transaction.commit() return product_info(item.id) except ValidationError as ex: diff --git a/brewman/brewman/views/product_group.py b/brewman/brewman/views/product_group.py index 45b16fc2..9d768cda 100644 --- a/brewman/brewman/views/product_group.py +++ b/brewman/brewman/views/product_group.py @@ -5,7 +5,7 @@ from pyramid.view import view_config import transaction from brewman.models import DBSession -from brewman.models.master import CostCenter, ProductGroup +from brewman.models.master import ProductGroup from brewman.models.validation_exception import ValidationError @@ -18,17 +18,25 @@ def html(request): return {} -@view_config(request_method='POST', route_name='product_group_id', renderer='json', xhr=True, permission='Product Groups') @view_config(request_method='POST', route_name='product_group', renderer='json', xhr=True, permission='Product Groups') -def save_update(request): +def save(request): try: - id = request.matchdict.get('id', None) - if id is None: - item = ProductGroup(request.json_body['Name']) - DBSession.add(item) - else: - item = ProductGroup.by_id(uuid.UUID(id)) - item.name = request.json_body['Name'] + item = ProductGroup(request.json_body['Name']) + DBSession.add(item) + transaction.commit() + return product_group_info(item.id) + except ValidationError as ex: + transaction.abort() + response = Response("Failed validation: {0}".format(ex.message)) + response.status_int = 500 + return response + + +@view_config(request_method='POST', route_name='product_group_id', renderer='json', xhr=True, permission='Product Groups') +def update(request): + try: + item = ProductGroup.by_id(uuid.UUID(request.matchdict.get['id'])) + item.name = request.json_body['Name'] transaction.commit() return product_group_info(item.id) except ValidationError as ex: diff --git a/brewman/brewman/views/services/login.py b/brewman/brewman/views/services/login.py new file mode 100644 index 00000000..fe6f118d --- /dev/null +++ b/brewman/brewman/views/services/login.py @@ -0,0 +1,101 @@ +from datetime import date, timedelta +import uuid +from pyramid.httpexceptions import HTTPFound +from pyramid.response import Response +from pyramid.security import authenticated_userid, remember, forget + +from pyramid.view import view_config +import transaction +from brewman import groupfinder +from brewman.models.auth import Role, User, Client + +@view_config(route_name='logout') +def logout(request): + request.session.invalidate() + headers = forget(request) + return HTTPFound(location=request.route_url('home'), headers=headers) + + +@view_config(request_method='POST', route_name='login', renderer='json') +def login(request): + username = request.json_body.get('username', None) + password = request.json_body.get('password', None) + found, user = User.auth(username, password) + + client = request.cookies.get('ClientID', None) + otp = request.json_body.get('otp', None) + client_name = request.json_body.get('ClientName', None) + allowed, response = check_client(client, otp, client_name, user if found else None) + + if found and allowed: + headers = remember(request, str(user.id)) + request.response.headers = headers + if allowed and isinstance(response, Client): + request.response.set_cookie('ClientID', value=str(response.code), max_age=10 * 365 * 24 * 60 * 60) + return request.response + elif not found: + response = Response("Login failed") + response.status_int = 403 + return response + else: + return response + + +def check_client(client, otp, client_name, user): + client = None if client is None else Client.by_code(client) + outside_login_allowed = False if user is None else True if 'Clients' in groupfinder(user.id, None) else False + + if len(Client.enabled_list()) == 0 and outside_login_allowed: + client = Client.create() + client.otp = None + client.name = 'Created on login by ' + user.name + client.enabled = True + transaction.commit() + return True, client + + if client is None: + if outside_login_allowed: + client = Client.create() + transaction.commit() + return True, client + else: + client = Client.create() + response = Response("Unknown Client") + response.status_int = 403 + response.set_cookie('ClientID', value=str(client.code), max_age=10 * 365 * 24 * 60 * 60) + transaction.commit() + return False, response + + if client.enabled or outside_login_allowed: + return True, None + + if client.otp is None: + response = Response("Client is Forbidden") + response.status_int = 403 + return False, response + + if otp is None: + response = Response("OTP not supplied") + response.status_int = 403 + return False, response + elif client.otp != int(otp): + response = Response("OTP is wrong") + response.status_int = 403 + return False, response + else: + client.otp = None + client.enabled = True + client.name = client_name + transaction.commit() + 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) diff --git a/brewman/brewman/views/services/session.py b/brewman/brewman/views/services/session.py index 94e9bd9b..b66baa7c 100644 --- a/brewman/brewman/views/services/session.py +++ b/brewman/brewman/views/services/session.py @@ -1,12 +1,10 @@ from datetime import date, timedelta import uuid -from pyramid.response import Response -from pyramid.security import authenticated_userid, remember +from pyramid.security import authenticated_userid from pyramid.view import view_config -import transaction from brewman import groupfinder -from brewman.models.auth import Role, User, Client +from brewman.models.auth import Role, User def session_current_date(request): session = request.session @@ -14,6 +12,11 @@ def session_current_date(request): session['currentDate'] = date.today().strftime('%d-%b-%Y') return session['currentDate'] +def session_current_date_set(request, date): + session = request.session + session['currentDate'] = date + return session['currentDate'] + def session_period_start(request): session = request.session if 'periodStart' not in session: @@ -34,7 +37,7 @@ def user_permission(request): if user_id is None: auth = {'isAuthenticated': False, 'perms': {}} else: - user = User.get_by_id(uuid.UUID(user_id)) + user = User.by_id(uuid.UUID(user_id)) auth = {'isAuthenticated': True, 'Name': user.name, 'UserID': user.id} session_perms = groupfinder(user.id, request) perms = {} @@ -44,74 +47,6 @@ def user_permission(request): return auth -@view_config(request_method='POST', route_name='login', renderer='json') -def login(request): - username = request.json_body.get('username', None) - password = request.json_body.get('password', None) - found, user = User.auth(username, password) - -# client = request.cookies.get('ClientID', None) -# otp = request.json_body.get('otp', None) -# client_name = request.json_body.get('ClientName', None) -# allowed, response = check_client(client, otp, client_name, user if found else None) - -# if found and allowed: - if found: - headers = remember(request, str(user.id)) - request.response.headers = headers -# if allowed and isinstance(response, Client): -# request.response.set_cookie('ClientID', value=str(response.code), max_age=10 * 365 * 24 * 60 * 60) - return request.response - elif not found: - response = Response("Login failed") - response.status_int = 500 - return response -# else: -# return response - - -def check_client(client, otp, client_name, user): - client = None if client is None else Client.by_code(client) - - if len(Client.enabled_list()) == 0 and user is not None and 'Clients' in groupfinder(user.id, None): - client = Client.create() - client.otp = None - client.name = 'First' - client.enabled = True - transaction.commit() - return True, client - - if client is None: - client = Client.create() - response = Response("Unknown Client") - response.status_int = 401 - response.set_cookie('ClientID', value=str(client.code), max_age=10 * 365 * 24 * 60 * 60) - return False, response - - if client.enabled: - return True, None - - if client.otp is None: - response = Response("Client is Forbidden") - response.status_int = 401 - return False, response - - if otp is None: - response = Response("OTP not supplied") - response.status_int = 401 - return False, response - elif client.otp != int(otp): - response = Response("OTP is wrong") - response.status_int = 401 - return False, response - else: - client.otp = None - client.enabled = True - client.name = client_name - transaction.commit() - 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 diff --git a/brewman/brewman/views/services/voucher/__init__.py b/brewman/brewman/views/services/voucher/__init__.py index 0b88c738..ec4cf42c 100644 --- a/brewman/brewman/views/services/voucher/__init__.py +++ b/brewman/brewman/views/services/voucher/__init__.py @@ -19,7 +19,7 @@ __author__ = 'tanshu' @view_config(request_method='POST', route_name='voucher', request_param='post', renderer='json', xhr=True, permission='Post Vouchers') def voucher_post(request): - user = User.get_by_id(uuid.UUID(authenticated_userid(request))) + user = User.by_id(uuid.UUID(authenticated_userid(request))) try: voucher = Voucher.by_id(uuid.UUID(request.matchdict['id'])) voucher.posted = True @@ -34,7 +34,7 @@ def voucher_post(request): def check_delete_permissions(request, voucher): - user = User.get_by_id(uuid.UUID(authenticated_userid(request))) + user = User.by_id(uuid.UUID(authenticated_userid(request))) permissions = groupfinder(user.id, request) if voucher.posted and not 'Edit Posted Vouchers' in permissions: @@ -53,8 +53,7 @@ def check_delete_permissions(request, voucher): @view_config(request_method='DELETE', route_name='voucher', renderer='json', xhr=True) def delete(request): - id = request.matchdict.get('id', None) - voucher = Voucher.by_id(uuid.UUID(id)) + voucher = Voucher.by_id(uuid.UUID(request.matchdict['id'])) permission = check_delete_permissions(request, voucher) if permission is not None: return permission @@ -110,6 +109,8 @@ def voucher_info(voucher): '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.reconcilliation_date is not None: + json_voucher['ReconcilliationDate'] = voucher.reconcilliation_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}, diff --git a/brewman/brewman/views/services/voucher/issue.py b/brewman/brewman/views/services/voucher/issue.py index dedcedee..e0a16861 100644 --- a/brewman/brewman/views/services/voucher/issue.py +++ b/brewman/brewman/views/services/voucher/issue.py @@ -3,7 +3,6 @@ from decimal import Decimal import uuid from brewman.models import DBSession from brewman.models.master import Product, CostCenter, LedgerBase -from brewman.models.operations import create_basic from brewman.models.validation_exception import ValidationError from brewman.models.voucher import Voucher, VoucherType, Batch, Inventory, Journal @@ -11,10 +10,8 @@ __author__ = 'tanshu' def issue_create_voucher(json, user): dt = datetime.datetime.strptime(json['Date'], '%d-%b-%Y') - voucher = Voucher(date=dt, narration=json['Narration'], user_id=user.id, type=VoucherType.by_name('Issue'), - posted=False) - - create_basic(voucher) + voucher = Voucher(date=dt, narration=json['Narration'], user_id=user.id, type=VoucherType.by_name('Issue')) + DBSession.add(voucher) for item in json['Journals']: if int(item['Debit']) == 1: @@ -60,10 +57,9 @@ def issue_create_journals(inventories, source, destination): amount = 0 for item in inventories: amount += item.amount - - return [Journal(debit=-1, ledger_id=LedgerBase.all_purchases(), amount=round(item.amount, 2), + return [Journal(debit=-1, ledger_id=LedgerBase.all_purchases(), amount=round(amount, 2), cost_center_id=source), - Journal(debit=1, ledger_id=LedgerBase.all_purchases(), amount=round(item.amount, 2), + Journal(debit=1, ledger_id=LedgerBase.all_purchases(), amount=round(amount, 2), cost_center_id=destination)] @@ -107,23 +103,23 @@ def issue_update_inventory(voucher, newInventories, batch_consumed): found = True if item.product_id != product.id: raise ValidationError('Product cannot be changed') - quantity = round(Decimal(i['Quantity']), 2) - item_quantity = round(Decimal(item.quantity), 2) + new_quantity = round(Decimal(i['Quantity']), 2) + old_quantity = round(Decimal(item.quantity), 2) quantity_remaining = round(Decimal(item.batch.quantity_remaining), 2) - if quantity == 0: + if new_quantity <= 0: raise ValidationError("Quantity of {0} cannot be zero".format(item.product.name)) - if batch_consumed == True and quantity - item_quantity > quantity_remaining: + if batch_consumed == True and new_quantity - old_quantity > quantity_remaining: raise ValidationError("Maximum quantity available for {0} is {1}".format(item.product.full_name, - item_quantity + quantity_remaining)) + old_quantity + quantity_remaining)) if batch_consumed is None: pass elif batch_consumed: - item.batch.quantity_remaining -= (quantity - item_quantity) + item.batch.quantity_remaining -= (new_quantity - old_quantity) else: - item.batch.quantity_remaining += (quantity - item_quantity) + item.batch.quantity_remaining += (new_quantity - old_quantity) - item.quantity = quantity + item.quantity = new_quantity newInventories.remove(i) break if not found: diff --git a/brewman/brewman/views/services/voucher/purchase.py b/brewman/brewman/views/services/voucher/purchase.py index 0c054eff..d84c5d82 100644 --- a/brewman/brewman/views/services/voucher/purchase.py +++ b/brewman/brewman/views/services/voucher/purchase.py @@ -3,7 +3,6 @@ from decimal import Decimal import uuid from brewman.models import DBSession from brewman.models.master import Product, LedgerBase -from brewman.models.operations import create_purchase_voucher from brewman.models.validation_exception import ValidationError from brewman.models.voucher import Voucher, VoucherType, Batch, Inventory, Journal @@ -11,17 +10,19 @@ __author__ = 'tanshu' def purchase_create_voucher(json, user): dt = datetime.datetime.strptime(json['Date'], '%d-%b-%Y') - voucher = Voucher(date=dt, narration=json['Narration'], user_id=user.id, - type=VoucherType.by_name(json['Type']), posted=False) + voucher = Voucher(date=dt, narration=json['Narration'], user_id=user.id, type=VoucherType.by_name(json['Type'])) + DBSession.add(voucher) + for item in json['Inventories']: - voucher.inventories.append(purchase_create_inventory(item, json['Date'])) + voucher.inventories.append(purchase_create_inventory(voucher, item, json['Date'])) for item in purchase_create_journals(voucher.inventories, json['Journals'][0]['Ledger']['LedgerID']): + DBSession.add(item) voucher.journals.append(item) - create_purchase_voucher(voucher) + DBSession.flush() return voucher -def purchase_create_inventory(item, date): +def purchase_create_inventory(voucher, item, date): product = Product.by_id(uuid.UUID(item['Product']['ProductID'])) inventory_id = uuid.UUID(item['InventoryID']) if 'InventoryID' in item else None quantity = round(Decimal(item['Quantity']), 2) @@ -30,8 +31,12 @@ def purchase_create_inventory(item, date): discount = round(Decimal(item['Discount']), 5) batch = Batch(name=date, product_id=product.id, quantity_remaining=quantity, rate=rate, tax=tax, discount=discount) - return Inventory(id=inventory_id, product_id=product.id, batch=batch, quantity=quantity, rate=rate, tax=tax, + DBSession.add(batch) + inventory = Inventory(id=inventory_id, product_id=product.id, batch=batch, quantity=quantity, rate=rate, tax=tax, discount=discount) + DBSession.add(inventory) + voucher.inventories.append(item) + def purchase_create_journals(inventories, ledgerID): @@ -74,16 +79,16 @@ def purchase_update_inventory(voucher, newInventories, date): found = True if item.product_id != product.id: raise ValidationError('Product cannot be changed') - quantity = round(Decimal(i['Quantity']), 2) - item_quantity = round(Decimal(item.quantity), 2) + new_quantity = round(Decimal(i['Quantity']), 2) + old_quantity = round(Decimal(item.quantity), 2) quantity_remaining = round(Decimal(item.batch.quantity_remaining), 2) - if quantity == 0: + if new_quantity <= 0: raise ValidationError("Quantity of {0} cannot be zero".format(item.product.name)) - if item_quantity - quantity > 0 and quantity_remaining < (item_quantity - quantity): + if old_quantity > new_quantity and quantity_remaining < (old_quantity - new_quantity): raise ValidationError("{0} has been issued, minimum quantity is".format( - item_quantity - quantity_remaining)) - item.batch.quantity_remaining -= (item_quantity - quantity) - item.quantity = quantity + old_quantity - quantity_remaining)) + item.batch.quantity_remaining -= (old_quantity - new_quantity) + item.quantity = new_quantity rate = round(Decimal(i['Rate']), 2) discount = round(Decimal(i['Discount']), 5) tax = round(Decimal(i['Tax']), 5) @@ -94,8 +99,10 @@ def purchase_update_inventory(voucher, newInventories, date): item.tax = tax item.batch.tax = tax newInventories.remove(i) + #TODO: Update all references of the batch with the new rates break if not found: + #TODO: Check to see if the batch is issued not just quantity remaining is full if item.quantity != item.batch.quantity_remaining: raise ValidationError("{0} has been issued, it cannot be deleted".format(item.product.name)) else: @@ -104,17 +111,17 @@ def purchase_update_inventory(voucher, newInventories, date): voucher.inventories.remove(item) for i in newInventories: product = Product.by_id(uuid.UUID(i['Product']['ProductID'])) - quantity = round(Decimal(i['Quantity']), 2) + new_quantity = round(Decimal(i['Quantity']), 2) rate = round(Decimal(i['Rate']), 2) tax = round(Decimal(i['Tax']), 5) discount = round(Decimal(i['Discount']), 5) - batch = Batch(name=date, product_id=product.id, quantity_remaining=quantity, rate=rate, tax=tax, + batch = Batch(name=date, product_id=product.id, quantity_remaining=new_quantity, rate=rate, tax=tax, discount=discount) - inventory = Inventory(id=None, product_id=product.id, batch=batch, quantity=quantity, rate=rate, tax=tax, + inventory = Inventory(id=None, product_id=product.id, batch=batch, quantity=new_quantity, rate=rate, tax=tax, discount=discount) inventory.voucher_id = voucher.id - DBSession.add(inventory.batch) - inventory.batch_id = inventory.batch.id + DBSession.add(batch) + inventory.batch_id = batch.id DBSession.add(inventory) voucher.inventories.append(inventory) @@ -145,6 +152,7 @@ def purchase_update_journals(voucher, journals): DBSession.delete(item) voucher.journals.remove(item) for item in journals.values(): + item.amount = round(item.amount, 2) DBSession.add(item) voucher.journals.append(item) diff --git a/brewman/brewman/views/services/voucher/purchase_return.py b/brewman/brewman/views/services/voucher/purchase_return.py index 9e338128..b6783923 100644 --- a/brewman/brewman/views/services/voucher/purchase_return.py +++ b/brewman/brewman/views/services/voucher/purchase_return.py @@ -3,7 +3,6 @@ from decimal import Decimal import uuid from brewman.models import DBSession from brewman.models.master import Product, LedgerBase -from brewman.models.operations import create_purchase_voucher, create_basic from brewman.models.validation_exception import ValidationError from brewman.models.voucher import Voucher, VoucherType, Batch, Inventory, Journal @@ -11,10 +10,8 @@ __author__ = 'tanshu' def purchase_return_create_voucher(json, user): dt = datetime.datetime.strptime(json['Date'], '%d-%b-%Y') - voucher = Voucher(date=dt, narration=json['Narration'], user_id=user.id, - type=VoucherType.by_name(json['Type']), posted=False) - - create_basic(voucher) + voucher = Voucher(date=dt, narration=json['Narration'], user_id=user.id, type=VoucherType.by_name(json['Type'])) + DBSession.add(voucher) for item in json['Inventories']: purchase_return_create_inventory(voucher, item) @@ -49,7 +46,7 @@ def purchase_return_create_journals(inventories, ledgerID): amount = 0 for item in inventories: ledger = Product.by_id(item.product_id).ledger - amount += item.amount + amount += round(item.amount, 2) if ledger.id in journals: journals[ledger.id].amount += item.amount else: @@ -83,7 +80,7 @@ def purchase_return_update_inventory(voucher, newInventories, date): found = True if item.product_id != product.id: raise ValidationError('Product cannot be changed') - quantity = Decimal(i['Quantity']) + quantity = round(Decimal(i['Quantity']), 2) if quantity == 0: raise ValidationError("Quantity of {0} cannot be zero".format(item.product.name)) if quantity - item.quantity > item.batch.quantity_remaining: @@ -120,13 +117,14 @@ def purchase_return_update_journals(voucher, journals): item = voucher.journals[i - 1] if item.ledger_id in journals: item.debit = journals[item.ledger_id].debit - item.amount = journals[item.ledger_id].amount + item.amount = round(journals[item.ledger_id].amount, 2) item.cost_center_id = journals[item.ledger_id].cost_center_id del journals[item.ledger_id] else: DBSession.delete(item) voucher.journals.remove(item) for item in journals.values(): + item.amount = round(item.amount, 2) DBSession.add(item) voucher.journals.append(item) diff --git a/brewman/brewman/views/services/voucher/save_voucher.py b/brewman/brewman/views/services/voucher/save_voucher.py index 5aa1e5b9..d0b379d3 100644 --- a/brewman/brewman/views/services/voucher/save_voucher.py +++ b/brewman/brewman/views/services/voucher/save_voucher.py @@ -6,6 +6,7 @@ import transaction from brewman.models.auth import User from brewman.models.validation_exception import ValidationError from brewman.models.voucher import Voucher +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.purchase_return import purchase_return_create_voucher @@ -16,6 +17,8 @@ __author__ = 'tanshu' class save_voucher(object): def __init__(self, request): self.request = request + self.user = User.by_id(uuid.UUID(authenticated_userid(request))) + self.json = request.json_body @view_config(request_param='type=Journal', permission='Journal') def journal(self): @@ -47,19 +50,17 @@ class save_voucher(object): return self.save() def save(self): - request = self.request - user = User.get_by_id(uuid.UUID(authenticated_userid(request))) try: - """ new voucher """ - if request.json_body['Type'] in ['Journal', 'Payment', 'Receipt']: - voucher = journal_create_voucher(request.json_body, user) - elif request.json_body['Type'] in ['Purchase']: - voucher = purchase_create_voucher(request.json_body, user) - elif request.json_body['Type'] in ['Purchase Return']: - voucher = purchase_return_create_voucher(request.json_body, user) - elif request.json_body['Type'] in ['Issue']: - voucher = issue_create_voucher(request.json_body, user) + if self.json['Type'] in ['Journal', 'Payment', 'Receipt']: + voucher = journal_create_voucher(self.json, self.user) + elif self.json['Type'] in ['Purchase']: + voucher = purchase_create_voucher(self.json, self.user) + elif self.json['Type'] in ['Purchase Return']: + voucher = purchase_return_create_voucher(self.json, self.user) + elif self.json['Type'] in ['Issue']: + voucher = issue_create_voucher(self.json, self.user) transaction.commit() + session_current_date_set(self.request,self.json['Date']) return voucher_info(Voucher.by_id(voucher.id)) except ValidationError as ex: transaction.abort() diff --git a/brewman/brewman/views/services/voucher/update_voucher.py b/brewman/brewman/views/services/voucher/update_voucher.py index 5343a52a..4f035bf3 100644 --- a/brewman/brewman/views/services/voucher/update_voucher.py +++ b/brewman/brewman/views/services/voucher/update_voucher.py @@ -7,6 +7,7 @@ from brewman import groupfinder from brewman.models.auth import User from brewman.models.validation_exception import ValidationError from brewman.models.voucher import Voucher +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.purchase_return import purchase_return_update_voucher @@ -17,7 +18,7 @@ __author__ = 'tanshu' class update_voucher(object): def __init__(self, request): self.request = request - self.user = User.get_by_id(uuid.UUID(authenticated_userid(request))) + self.user = User.by_id(uuid.UUID(authenticated_userid(request))) self.voucher = Voucher.by_id(uuid.UUID(request.matchdict.get('id', None))) self.json = request.json_body @@ -76,6 +77,7 @@ class update_voucher(object): elif self.json['Type'] in ['Issue']: voucher = issue_update_voucher(self.voucher, self.json, self.user) transaction.commit() + session_current_date_set(self.request,self.json['Date']) return voucher_info(Voucher.by_id(voucher.id)) except ValidationError as ex: transaction.abort()
    Name