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