From cc89a548a52969c4a4afedcc8bf23304db243eba Mon Sep 17 00:00:00 2001 From: Amritanshu Date: Mon, 14 Oct 2013 15:03:26 +0530 Subject: [PATCH] Feature: Created stock movement report. --- .gitignore | 2 +- brewman/__init__.py | 3 + brewman/static/partial/stock-movement.html | 52 +++++++++++++ brewman/static/scripts/angular_service.js | 4 + brewman/static/scripts/overlord.js | 1 + brewman/static/scripts/stock-movement.js | 35 +++++++++ brewman/static/scripts/voucher_service.js | 3 - brewman/templates/angular_base.mako | 2 + brewman/views/reports/stock_movement.py | 85 ++++++++++++++++++++++ 9 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 brewman/static/partial/stock-movement.html create mode 100644 brewman/static/scripts/stock-movement.js delete mode 100644 brewman/static/scripts/voucher_service.js create mode 100644 brewman/views/reports/stock_movement.py diff --git a/.gitignore b/.gitignore index 2cb88a62..4d09397e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ env */__pycache__/ */.idea/ .idea/ -*/*.egg-info/ \ No newline at end of file +*.egg-info/ \ No newline at end of file diff --git a/brewman/__init__.py b/brewman/__init__.py index 08ccc082..7d8691b9 100644 --- a/brewman/__init__.py +++ b/brewman/__init__.py @@ -125,6 +125,9 @@ def main(global_config, **settings): config.add_route('api_profit_loss', '/api/ProfitLoss') config.add_route('profit_loss', '/ProfitLoss') + config.add_route('api_stock_movement', '/api/StockMovement') + config.add_route('stock_movement', '/StockMovement') + add_route(config, 'balance_sheet', '/BalanceSheet', has_list=False, variable='date') config.add_route('api_purchase_entries', '/api/PurchaseEntries') diff --git a/brewman/static/partial/stock-movement.html b/brewman/static/partial/stock-movement.html new file mode 100644 index 00000000..548c4e81 --- /dev/null +++ b/brewman/static/partial/stock-movement.html @@ -0,0 +1,52 @@ +
+

Stock Movement

+ +
+ + +
+
+ + + + +
+
+
+
+ + + + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + +
GroupNameOpeningPurchaseIssueClosing
{{item.Group}}{{item.Name}}{{item.Opening | number:2}}{{item.Purchase | number:2}}{{item.Issue | number:2}}{{item.Closing | number:2}}
+
diff --git a/brewman/static/scripts/angular_service.js b/brewman/static/scripts/angular_service.js index 035abba0..2e4241af 100644 --- a/brewman/static/scripts/angular_service.js +++ b/brewman/static/scripts/angular_service.js @@ -126,6 +126,10 @@ overlord_service.factory('ProfitLoss', ['$resource', function ($resource) { return $resource('/api/ProfitLoss'); }]); +overlord_service.factory('StockMovement', ['$resource', function ($resource) { + return $resource('/api/StockMovement'); +}]); + overlord_service.factory('BalanceSheet', ['$resource', function ($resource) { return $resource('/api/BalanceSheet/:date'); }]); diff --git a/brewman/static/scripts/overlord.js b/brewman/static/scripts/overlord.js index a30249c4..4c20acae 100644 --- a/brewman/static/scripts/overlord.js +++ b/brewman/static/scripts/overlord.js @@ -52,6 +52,7 @@ var overlord = angular.module('overlord', ['overlord.directive', 'overlord.filte when('/Daybook', {templateUrl: '/partial/daybook.html', controller: DaybookCtrl, resolve: DaybookCtrl.resolve}). when('/Unposted', {templateUrl: '/partial/unposted.html', controller: UnpostedCtrl, resolve: UnpostedCtrl.resolve}). when('/ProfitLoss', {templateUrl: '/partial/profit-loss.html', controller: ProfitLossCtrl, resolve: ProfitLossCtrl.resolve}). + when('/StockMovement', {templateUrl: '/partial/stock-movement.html', controller: StockMovementCtrl, resolve: StockMovementCtrl.resolve}). when('/NetTransactions', {templateUrl: '/partial/net-transactions.html', controller: NetTransactionsCtrl, resolve: NetTransactionsCtrl.resolve}). when('/PurchaseEntries', {templateUrl: '/partial/purchase-entries.html', controller: PurchaseEntriesCtrl, resolve: PurchaseEntriesCtrl.resolve}). when('/EmployeeFunctions', {templateUrl: '/partial/employee-functions.html', controller: EmployeeFunctionsCtrl}). diff --git a/brewman/static/scripts/stock-movement.js b/brewman/static/scripts/stock-movement.js new file mode 100644 index 00000000..af4af566 --- /dev/null +++ b/brewman/static/scripts/stock-movement.js @@ -0,0 +1,35 @@ +'use strict'; + +var StockMovementCtrl = ['$scope', '$location', 'dateFilter', 'stock_movement', function ($scope, $location, dateFilter, stock_movement) { + $scope.info = stock_movement; + $scope.show = function () { + if (angular.isDate($scope.info.StartDate)) { + $scope.info.StartDate = dateFilter($scope.info.StartDate, 'dd-MMM-yyyy'); + } + if (angular.isDate($scope.info.FinishDate)) { + $scope.info.FinishDate = dateFilter($scope.info.FinishDate, 'dd-MMM-yyyy'); + } + $location.path('/StockMovement').search({StartDate: $scope.info.StartDate, FinishDate: $scope.info.FinishDate}); + }; + $('#txtStartDate').focus(); +}]; + +StockMovementCtrl.resolve = { + stock_movement: ['$q', '$route', 'StockMovement', function ($q, $route, StockMovement) { + var deferred = $q.defer(); + + var start_date = $route.current.params.StartDate; + var finish_date = $route.current.params.FinishDate; + + var successCb = function (result) { + deferred.resolve(result); + }; + + if (typeof start_date === 'undefined' || typeof finish_date === 'undefined') { + StockMovement.get({}, successCb); + } else { + StockMovement.get({StartDate: start_date, FinishDate: finish_date}, successCb); + } + return deferred.promise; + }] +}; \ No newline at end of file diff --git a/brewman/static/scripts/voucher_service.js b/brewman/static/scripts/voucher_service.js deleted file mode 100644 index e168dc62..00000000 --- a/brewman/static/scripts/voucher_service.js +++ /dev/null @@ -1,3 +0,0 @@ -/** - * Created by tanshu on 27/9/13. - */ diff --git a/brewman/templates/angular_base.mako b/brewman/templates/angular_base.mako index 83b7e89c..c1c19b0d 100644 --- a/brewman/templates/angular_base.mako +++ b/brewman/templates/angular_base.mako @@ -77,6 +77,7 @@ + @@ -144,6 +145,7 @@
  • Raw Material Cost
  • Purchase Entries
  • Closing Stock
  • +
  • Stock Movement
  • Trail Balance
  • Profit and Loss
  • diff --git a/brewman/views/reports/stock_movement.py b/brewman/views/reports/stock_movement.py new file mode 100644 index 00000000..0422fb39 --- /dev/null +++ b/brewman/views/reports/stock_movement.py @@ -0,0 +1,85 @@ +import datetime +from sqlalchemy.sql.expression import func + +from pyramid.view import view_config + +from brewman.models import DBSession +from brewman.models.master import Product, CostCenter + +from brewman.models.voucher import Voucher, Journal, VoucherType, Inventory +from brewman.views.services.session import session_period_start, session_period_finish + + +@view_config(request_method='GET', route_name='stock_movement', renderer='brewman:templates/angular_base.mako', + permission='Stock Movement') +def html(request): + return {} + + +@view_config(request_method='GET', route_name='api_stock_movement', renderer='json', permission='Stock Movement') +def get_stock_movement(request): + start_date = request.GET.get('StartDate', None) + finish_date = request.GET.get('FinishDate', None) + if start_date and finish_date: + report = {'StartDate': start_date, 'FinishDate': finish_date} + start_date = datetime.datetime.strptime(start_date, '%d-%b-%Y') + finish_date = datetime.datetime.strptime(finish_date, '%d-%b-%Y') + report['Body'] = build_stock_movement(start_date, finish_date) + return report + else: + return {'StartDate': session_period_start(request), + 'FinishDate': session_period_finish(request), 'Body': []} + + +def build_stock_movement(start_date, finish_date): + dict = {} + quantity_sum = func.sum(Journal.debit * Inventory.quantity).label('Quantity') + openings = DBSession.query(Product, quantity_sum) \ + .join(Product.inventories).join(Inventory.voucher).join(Voucher.journals) \ + .filter(Voucher.date < start_date) \ + .filter(Journal.cost_center_id == CostCenter.cost_center_purchase()) \ + .group_by(Product).all() + for product, quantity in openings: + dict[product.id] = {'ProductID': product.id, 'Name': product.full_name, 'Group': product.product_group.name, + 'Opening': quantity} + purchases = DBSession.query(Product, quantity_sum) \ + .join(Product.inventories).join(Inventory.voucher).join(Voucher.journals) \ + .filter(Voucher.date >= start_date) \ + .filter(Voucher.date <= finish_date) \ + .filter(Voucher.type != VoucherType.by_name('Issue').id) \ + .filter(Journal.cost_center_id == CostCenter.cost_center_purchase()) \ + .group_by(Product).all() + for product, quantity in purchases: + if product.id in dict: + dict[product.id]['Purchase'] = quantity + else: + dict[product.id] = {'ProductID': product.id, 'Name': product.full_name, 'Group': product.product_group.name, + 'Purchase': quantity} + issues = DBSession.query(Product, quantity_sum) \ + .join(Product.inventories).join(Inventory.voucher).join(Voucher.journals) \ + .filter(Voucher.date >= start_date) \ + .filter(Voucher.date <= finish_date) \ + .filter(Voucher.type != VoucherType.by_name('Issue').id) \ + .filter(Journal.cost_center_id == CostCenter.cost_center_purchase()) \ + .group_by(Product).all() + for product, quantity in issues: + if product.id in dict: + dict[product.id]['Issue'] = quantity + else: + dict[product.id] = {'ProductID': product.id, 'Name': product.full_name, 'Group': product.product_group.name, + 'Issue': quantity} + + list = [value for key, value in dict.items()] + list = sorted(list, key=lambda x: x['Name'].lower()) + list = sorted(list, key=lambda x: x['Group'].lower()) + for i in range(len(list), 0, -1): + item = list[i - 1] + opening = item['Opening'] if 'Opening' in item else 0 + purchase = item['Purchase'] if 'Purchase' in item else 0 + issue = item['Issue'] if 'Issue' in item else 0 + closing = opening + purchase - issue + if opening == 0 and purchase == 0 and issue == 0: + list.remove(item) + else: + item['Closing'] = closing + return list \ No newline at end of file