From 5d273df92e5817c9e9fa7215ddb4e041596a6902 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 28 Dec 2012 15:39:21 +0530 Subject: [PATCH 1/7] changes in layout of module home page --- accounts/page/accounts_home/accounts_home.html | 12 ++++++++---- buying/page/buying_home/buying_home.html | 11 ++++++++--- hr/page/hr_home/hr_home.html | 2 +- .../manufacturing_home/manufacturing_home.html | 2 +- projects/page/projects_home/projects_home.html | 2 +- selling/page/selling_home/selling_home.html | 11 ++++++++--- stock/page/stock_home/stock_home.html | 15 ++++++++++++--- support/page/support_home/support_home.html | 2 +- 8 files changed, 40 insertions(+), 17 deletions(-) diff --git a/accounts/page/accounts_home/accounts_home.html b/accounts/page/accounts_home/accounts_home.html index 498f811732..498b214587 100644 --- a/accounts/page/accounts_home/accounts_home.html +++ b/accounts/page/accounts_home/accounts_home.html @@ -18,7 +18,11 @@
Chart of Cost Centers

Structure cost centers

-
+ +
+
+

Reports

+
General Ledger @@ -29,16 +33,16 @@ data-role="Analytics, Accounts Manager, Accounts User">Trial Balance

Tree view of all Account balances

-
+
+
Financial Analytics

Visual representation of financial trends

-
+

-

Reports

diff --git a/buying/page/buying_home/buying_home.html b/buying/page/buying_home/buying_home.html index 68544fb3d3..785abd5ac8 100644 --- a/buying/page/buying_home/buying_home.html +++ b/buying/page/buying_home/buying_home.html @@ -23,15 +23,20 @@
Address

Address Master

-
+
+
+
+

Reports

+
Purchase Analytics

Purchase trends based on Purchase Invoice

-
+
+
+

-

Reports

diff --git a/hr/page/hr_home/hr_home.html b/hr/page/hr_home/hr_home.html index b7c91bb58a..9d6875a994 100644 --- a/hr/page/hr_home/hr_home.html +++ b/hr/page/hr_home/hr_home.html @@ -23,7 +23,7 @@

-

Reports

+

Reports

diff --git a/manufacturing/page/manufacturing_home/manufacturing_home.html b/manufacturing/page/manufacturing_home/manufacturing_home.html index b87d30ac6b..ec77b91fa7 100644 --- a/manufacturing/page/manufacturing_home/manufacturing_home.html +++ b/manufacturing/page/manufacturing_home/manufacturing_home.html @@ -17,7 +17,7 @@

-

Reports

+

Reports

diff --git a/projects/page/projects_home/projects_home.html b/projects/page/projects_home/projects_home.html index 5cd48a0aaf..cb4f1f8aba 100644 --- a/projects/page/projects_home/projects_home.html +++ b/projects/page/projects_home/projects_home.html @@ -17,7 +17,7 @@

-

Reports

+

Reports

diff --git a/selling/page/selling_home/selling_home.html b/selling/page/selling_home/selling_home.html index 3009cebe3b..e483b508b4 100644 --- a/selling/page/selling_home/selling_home.html +++ b/selling/page/selling_home/selling_home.html @@ -29,15 +29,20 @@
Address

Address Master

-
+
+
+
+

Reports

+
Sales Analytics

Sales trends based on Sales Invoice

-
+
+
+

-

Reports

diff --git a/stock/page/stock_home/stock_home.html b/stock/page/stock_home/stock_home.html index 7e43e9976a..d4dfd4cea5 100644 --- a/stock/page/stock_home/stock_home.html +++ b/stock/page/stock_home/stock_home.html @@ -23,7 +23,11 @@
Warehouse

Warehouse is where items are stored

-
+
+
+
+

Reports

+
Stock Ledger
@@ -34,6 +38,12 @@

Inflow, outflow and balance of stock


+
+ Stock Level +
+

Available and estimated qty of stock, as of now

+
+
Stock Analytics
@@ -44,9 +54,8 @@

Analysis of slow moving stock

-
+

-

Reports

diff --git a/support/page/support_home/support_home.html b/support/page/support_home/support_home.html index 8975bae66f..ed01fa094e 100644 --- a/support/page/support_home/support_home.html +++ b/support/page/support_home/support_home.html @@ -26,7 +26,7 @@

-

Reports

+

Reports

From 30f5346184c743aa21cca07cba803dab7a341e9f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 28 Dec 2012 15:39:55 +0530 Subject: [PATCH 2/7] stock level report --- public/js/stock_analytics.js | 2 +- startup/report_data_map.py | 46 ++++- stock/page/stock_ageing/stock_ageing.js | 2 +- stock/page/stock_balance/stock_balance.js | 2 +- stock/page/stock_ledger/stock_ledger.js | 2 +- stock/page/stock_level/__init__.py | 0 stock/page/stock_level/stock_level.js | 206 ++++++++++++++++++++++ stock/page/stock_level/stock_level.txt | 21 +++ 8 files changed, 276 insertions(+), 5 deletions(-) create mode 100644 stock/page/stock_level/__init__.py create mode 100644 stock/page/stock_level/stock_level.js create mode 100644 stock/page/stock_level/stock_level.txt diff --git a/public/js/stock_analytics.js b/public/js/stock_analytics.js index 35b3f563e1..53641ce996 100644 --- a/public/js/stock_analytics.js +++ b/public/js/stock_analytics.js @@ -70,7 +70,7 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({ {fieldtype:"Select", label: "Brand", link:"Brand", default_value: "Select Brand...", filter: function(val, item, opts) { return val == opts.default_value || item.brand == val || item._show; - }}, + }, link_formatter: {filter_input: "brand"}}, {fieldtype:"Select", label: "Warehouse", link:"Warehouse", default_value: "Select Warehouse..."}, {fieldtype:"Date", label: "From Date"}, diff --git a/startup/report_data_map.py b/startup/report_data_map.py index b07ded52f0..6a7ba42b32 100644 --- a/startup/report_data_map.py +++ b/startup/report_data_map.py @@ -93,13 +93,57 @@ data_map = { "item_code": ["Item", "name"], "warehouse": ["Warehouse", "name"] }, - "force_index": "posting_sort_index" + "force_index": "posting_sort_index" }, "Stock Entry": { "columns": ["name", "purpose"], "conditions": ["docstatus=1"], "order_by": "posting_date, posting_time, name", }, + "Production Order": { + "columns": ["production_item as item_code", + "(ifnull(qty, 0) - ifnull(produced_qty, 0)) as qty", + "fg_warehouse as warehouse"], + "conditions": ["docstatus=1", "status != 'Stopped'", "ifnull(fg_warehouse, '')!=''", + "ifnull(qty, 0) > ifnull(produced_qty, 0)"], + "links": { + "item_code": ["Item", "name"], + "warehouse": ["Warehouse", "name"] + }, + }, + "Purchase Request Item": { + "columns": ["item_code", "warehouse", + "(ifnull(qty, 0) - ifnull(ordered_qty, 0)) as qty"], + "from": "`tabPurchase Request Item` item, `tabPurchase Request` main", + "conditions": ["item.parent = main.name", "main.docstatus=1", "main.status != 'Stopped'", + "ifnull(warehouse, '')!=''", "ifnull(qty, 0) > ifnull(ordered_qty, 0)"], + "links": { + "item_code": ["Item", "name"], + "warehouse": ["Warehouse", "name"] + }, + }, + "Purchase Order Item": { + "columns": ["item_code", "warehouse", + "(ifnull(qty, 0) - ifnull(received_qty, 0)) as qty"], + "from": "`tabPurchase Order Item` item, `tabPurchase Order` main", + "conditions": ["item.parent = main.name", "main.docstatus=1", "main.status != 'Stopped'", + "ifnull(warehouse, '')!=''", "ifnull(qty, 0) > ifnull(received_qty, 0)"], + "links": { + "item_code": ["Item", "name"], + "warehouse": ["Warehouse", "name"] + }, + }, + "Sales Order Item": { + "columns": ["item_code", "(ifnull(qty, 0) - ifnull(delivered_qty, 0)) as qty", + "reserved_warehouse as warehouse"], + "from": "`tabSales Order Item` item, `tabSales Order` main", + "conditions": ["item.parent = main.name", "main.docstatus=1", "main.status != 'Stopped'", + "ifnull(reserved_warehouse, '')!=''", "ifnull(qty, 0) > ifnull(delivered_qty, 0)"], + "links": { + "item_code": ["Item", "name"], + "warehouse": ["Warehouse", "name"] + }, + }, # Sales "Customer": { diff --git a/stock/page/stock_ageing/stock_ageing.js b/stock/page/stock_ageing/stock_ageing.js index 7809ab9cd9..ea495ce6cd 100644 --- a/stock/page/stock_ageing/stock_ageing.js +++ b/stock/page/stock_ageing/stock_ageing.js @@ -63,7 +63,7 @@ erpnext.StockAgeing = erpnext.StockGridReport.extend({ {fieldtype:"Select", label: "Brand", link:"Brand", default_value: "Select Brand...", filter: function(val, item, opts) { return val == opts.default_value || item.brand == val; - }}, + }, link_formatter: {filter_input: "brand"}}, {fieldtype:"Select", label: "Plot By", options: ["Average Age", "Earliest", "Latest"]}, {fieldtype:"Date", label: "To Date"}, diff --git a/stock/page/stock_balance/stock_balance.js b/stock/page/stock_balance/stock_balance.js index 3033470a06..485909c2b9 100644 --- a/stock/page/stock_balance/stock_balance.js +++ b/stock/page/stock_balance/stock_balance.js @@ -63,7 +63,7 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({ {fieldtype:"Select", label: "Brand", link:"Brand", default_value: "Select Brand...", filter: function(val, item, opts) { return val == opts.default_value || item.brand == val || item._show; - }}, + }, link_formatter: {filter_input: "brand"}}, {fieldtype:"Select", label: "Warehouse", link:"Warehouse", default_value: "Select Warehouse..."}, {fieldtype:"Date", label: "From Date"}, diff --git a/stock/page/stock_ledger/stock_ledger.js b/stock/page/stock_ledger/stock_ledger.js index e0cfd02af5..97d64175ba 100644 --- a/stock/page/stock_ledger/stock_ledger.js +++ b/stock/page/stock_ledger/stock_ledger.js @@ -86,7 +86,7 @@ erpnext.StockLedger = erpnext.StockGridReport.extend({ {fieldtype:"Select", label: "Brand", link:"Brand", default_value: "Select Brand...", filter: function(val, item, opts) { return val == opts.default_value || item.brand == val || item._show; - }}, + }, link_formatter: {filter_input: "brand"}}, {fieldtype:"Data", label: "Voucher No", filter: function(val, item, opts) { if(!val) return true; diff --git a/stock/page/stock_level/__init__.py b/stock/page/stock_level/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/stock/page/stock_level/stock_level.js b/stock/page/stock_level/stock_level.js new file mode 100644 index 0000000000..b1a2a1a248 --- /dev/null +++ b/stock/page/stock_level/stock_level.js @@ -0,0 +1,206 @@ +// ERPNext - web based ERP (http://erpnext.com) +// Copyright (C) 2012 Web Notes Technologies Pvt Ltd +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +wn.pages['stock-level'].onload = function(wrapper) { + wn.ui.make_app_page({ + parent: wrapper, + title: 'Stock Level', + single_column: true + }); + + new erpnext.StockLevel(wrapper); + + wrapper.appframe.add_home_breadcrumb() + wrapper.appframe.add_module_breadcrumb("Stock") + wrapper.appframe.add_breadcrumb("icon-bar-chart") +} + +wn.require("app/js/stock_grid_report.js"); + +erpnext.StockLevel = erpnext.StockGridReport.extend({ + init: function(wrapper) { + this._super({ + title: "Stock Level", + page: wrapper, + parent: $(wrapper).find('.layout-main'), + appframe: wrapper.appframe, + doctypes: ["Item", "Warehouse", "Stock Ledger Entry", "Production Order", + "Purchase Request Item", "Purchase Order Item", "Sales Order Item", "Brand"], + }) + }, + + setup_columns: function() { + this.columns = [ + {id: "item_code", name: "Item Code", field: "item_code", width: 160, + link_formatter: { + filter_input: "item_code", + open_btn: true, + doctype: '"Item"', + }}, + {id: "warehouse", name: "Warehouse", field: "warehouse", width: 100, + link_formatter: {filter_input: "warehouse"}}, + {id: "actual_qty", name: "Actual Qty", + field: "actual_qty", width: 80, formatter: this.currency_formatter}, + {id: "planned_qty", name: "Planned Qty", + field: "planned_qty", width: 80, formatter: this.currency_formatter}, + {id: "requested_qty", name: "Requested Qty", + field: "requested_qty", width: 80, formatter: this.currency_formatter}, + {id: "ordered_qty", name: "Ordered Qty", + field: "ordered_qty", width: 80, formatter: this.currency_formatter}, + {id: "reserved_qty", name: "Reserved Qty", + field: "reserved_qty", width: 80, formatter: this.currency_formatter}, + {id: "projected_qty", name: "Projected Qty", + field: "projected_qty", width: 80, formatter: this.currency_formatter}, + {id: "uom", name: "UOM", field: "uom", width: 60}, + {id: "item_name", name: "Item Name", field: "item_name", width: 100, + formatter: this.text_formatter}, + {id: "brand", name: "Brand", field: "brand", width: 100, + link_formatter: {filter_input: "brand"}}, + ]; + }, + + filters: [ + {fieldtype:"Select", label: "Item Code", link:"Item", default_value: "Select Item...", + filter: function(val, item, opts) { + return item.item_code == val || val == opts.default_value; + }}, + + {fieldtype:"Select", label: "Warehouse", link:"Warehouse", + default_value: "Select Warehouse...", filter: function(val, item, opts) { + return item.warehouse == val || val == opts.default_value; + }}, + + {fieldtype:"Select", label: "Brand", link:"Brand", + default_value: "Select Brand...", filter: function(val, item, opts) { + return val == opts.default_value || item.brand == val; + }}, + {fieldtype:"Button", label: "Refresh", icon:"icon-refresh icon-white", cssClass:"btn-info"}, + {fieldtype:"Button", label: "Reset Filters"} + ], + + setup_filters: function() { + var me = this; + this._super(); + + this.wrapper.bind("apply_filters_from_route", function() { me.toggle_enable_brand(); }); + this.filter_inputs.item_code.change(function() { me.toggle_enable_brand(); }); + + this.trigger_refresh_on_change(["item_code", "warehouse", "brand"]); + }, + + toggle_enable_brand: function() { + if(this.filter_inputs.item_code.val() == + this.filter_inputs.item_code.get(0).opts.default_value) { + this.filter_inputs.brand.removeAttr("disabled"); + } else { + this.filter_inputs.brand + .val(this.filter_inputs.brand.get(0).opts.default_value) + .attr("disabled", "disabled"); + } + }, + + init_filter_values: function() { + this._super(); + this.filter_inputs.warehouse.get(0).selectedIndex = 0; + }, + + prepare_data: function() { + var me = this; + + if(!this._data) { + this._data = []; + this.item_warehouse_map = []; + this.item_by_name = this.make_name_map(wn.report_dump.data["Item"]); + var sorted_item_list = Object.keys(this.item_by_name).sort(); + $.each(sorted_item_list, function(i, item_code) { + var item = me.item_by_name[item_code]; + $.each(wn.report_dump.data["Warehouse"], function(i, warehouse) { + // a list of item warehouse combination objects + var row = { + item_code: item_code, + warehouse: warehouse.name, + brand: item.brand, + item_name: item.item_name || item.name, + uom: item.stock_uom, + id: item_code + ":" + warehouse.name, + } + me.reset_item_values(row); + me._data.push(row); + me.item_warehouse_map[row.id] = row; + }); + }); + this.calculate_quantities(); + + // filter out rows with zero values + this._data = $.map(this._data, function(d) { + return me.apply_zero_filter(null, d, null, me) ? d : null; + }); + } + + this.data = [].concat(this._data); + this.data = $.map(this.data, function(d) { + return me.apply_filters(d) ? d : null; + }); + + this.calculate_total(); + }, + + calculate_quantities: function() { + var me = this; + $.each([ + ["Stock Ledger Entry", "actual_qty"], + ["Production Order", "planned_qty"], + ["Purchase Request Item", "requested_qty"], + ["Purchase Order Item", "ordered_qty"], + ["Sales Order Item", "reserved_qty"]], + function(i, v) { + $.each(wn.report_dump.data[v[0]], function(i, item) { + var row = me.item_warehouse_map[item.item_code + ":" + item.warehouse]; + row[v[1]] += flt(item.qty); + }); + } + ); + + $.each(this._data, function(i, row) { + row.projected_qty = row.actual_qty + row.planned_qty + row.requested_qty + + row.ordered_qty - row.reserved_qty; + }); + }, + + calculate_total: function() { + var me = this; + // show total if a specific item is selected and warehouse is not filtered + if(this.is_default("warehouse") && !this.is_default("item_code")) { + var total = { + id: "_total", + item_code: "Total", + _style: "font-weight: bold", + _show: true + }; + this.reset_item_values(total); + + $.each(this.data, function(i, row) { + $.each(me.columns, function(i, col) { + if (col.formatter==me.currency_formatter) { + total[col.id] += row[col.id]; + } + }); + }); + + this.data = this.data.concat([total]); + } + } +}) diff --git a/stock/page/stock_level/stock_level.txt b/stock/page/stock_level/stock_level.txt new file mode 100644 index 0000000000..6bf5c41cec --- /dev/null +++ b/stock/page/stock_level/stock_level.txt @@ -0,0 +1,21 @@ +[ + { + "owner": "Administrator", + "docstatus": 0, + "creation": "2012-12-28 11:02:23", + "modified_by": "Administrator", + "modified": "2012-12-28 11:02:23" + }, + { + "name": "__common__", + "title": "Stock Level", + "doctype": "Page", + "module": "Stock", + "standard": "Yes", + "page_name": "stock-level" + }, + { + "name": "stock-level", + "doctype": "Page" + } +] \ No newline at end of file From 6850b4c57241dc7a9a32ff155470639b65e06ea5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 28 Dec 2012 16:44:52 +0530 Subject: [PATCH 3/7] added footnote to stock level report explaining the various columns --- stock/page/stock_level/stock_level.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/stock/page/stock_level/stock_level.js b/stock/page/stock_level/stock_level.js index b1a2a1a248..87fce6a9bc 100644 --- a/stock/page/stock_level/stock_level.js +++ b/stock/page/stock_level/stock_level.js @@ -25,13 +25,15 @@ wn.pages['stock-level'].onload = function(wrapper) { wrapper.appframe.add_home_breadcrumb() wrapper.appframe.add_module_breadcrumb("Stock") - wrapper.appframe.add_breadcrumb("icon-bar-chart") + wrapper.appframe.add_breadcrumb("icon-bar-chart"); } wn.require("app/js/stock_grid_report.js"); erpnext.StockLevel = erpnext.StockGridReport.extend({ init: function(wrapper) { + var me = this; + this._super({ title: "Stock Level", page: wrapper, @@ -39,7 +41,24 @@ erpnext.StockLevel = erpnext.StockGridReport.extend({ appframe: wrapper.appframe, doctypes: ["Item", "Warehouse", "Stock Ledger Entry", "Production Order", "Purchase Request Item", "Purchase Order Item", "Sales Order Item", "Brand"], - }) + }); + + this.wrapper.bind("make", function() { + wn.utils.set_footnote(me, me.wrapper.get(0), + "
    \ +
  • \ + Projected Qty = Actual Qty + Planned Qty + Requested Qty \ + + Ordered Qty - Reserved Qty
  • \ +
      \ +
    • Actual Qty: Quantity available in the warehouse.
    • \ +
    • Planned Qty: Quantity, for which, Production Order has been raised, \ + but is pending to be manufactured.
    • \ +
    • Requested Qty: Quantity requested for purchase, but not ordered.
    • \ +
    • Ordered Qty: Quantity ordered for purchase, but not received.
    • \ +
    • Reserved Qty: Quantity ordered for sale, but not delivered.
    • \ +
    \ +
"); + }); }, setup_columns: function() { From f9a3c8fcdc85134bd7968de7df87cb4b7a2e1c4c Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 28 Dec 2012 19:42:49 +0530 Subject: [PATCH 4/7] added status validation --- .../doctype/purchase_order/purchase_order.py | 11 +- .../purchase_request/purchase_request.py | 8 +- .../supplier_quotation/supplier_quotation.py | 8 +- hr/doctype/appraisal/appraisal.py | 1 + hr/doctype/attendance/attendance.py | 150 ++++---- hr/doctype/employee/employee.py | 19 +- hr/doctype/expense_claim/expense_claim.py | 5 +- .../leave_application/leave_application.py | 5 +- .../salary_structure/salary_structure.py | 159 ++++---- .../production_order/production_order.py | 4 + .../installation_note/installation_note.py | 342 +++++++++--------- selling/doctype/quotation/quotation.py | 4 + selling/doctype/sales_order/sales_order.py | 9 +- stock/doctype/delivery_note/delivery_note.py | 3 + .../purchase_receipt/purchase_receipt.py | 8 +- stock/doctype/serial_no/serial_no.py | 4 + stock/doctype/stock_entry/stock_entry.py | 11 +- .../doctype/customer_issue/customer_issue.py | 5 +- utilities/__init__.py | 9 +- 19 files changed, 410 insertions(+), 355 deletions(-) diff --git a/buying/doctype/purchase_order/purchase_order.py b/buying/doctype/purchase_order/purchase_order.py index 85a11ed813..f589b67503 100644 --- a/buying/doctype/purchase_order/purchase_order.py +++ b/buying/doctype/purchase_order/purchase_order.py @@ -128,9 +128,14 @@ class DocType(TransactionBase): # Validate def validate(self): self.validate_fiscal_year() - # Step 1:=> set status as "Draft" - webnotes.conn.set(self.doc, 'status', 'Draft') - + + if not self.doc.status: + self.doc.status = "Draft" + + import utilities + utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped", + "Cancelled"]) + # Step 2:=> get Purchase Common Obj pc_obj = get_obj(dt='Purchase Common') diff --git a/buying/doctype/purchase_request/purchase_request.py b/buying/doctype/purchase_request/purchase_request.py index 3f7f932c25..0a7ae19812 100644 --- a/buying/doctype/purchase_request/purchase_request.py +++ b/buying/doctype/purchase_request/purchase_request.py @@ -141,8 +141,12 @@ class DocType: self.validate_schedule_date() self.validate_fiscal_year() - # set status as "Draft" - webnotes.conn.set(self.doc, 'status', 'Draft') + if not self.doc.status: + self.doc.status = "Draft" + + import utilities + utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped", + "Cancelled"]) # Get Purchase Common Obj pc_obj = get_obj(dt='Purchase Common') diff --git a/buying/doctype/supplier_quotation/supplier_quotation.py b/buying/doctype/supplier_quotation/supplier_quotation.py index cac4bab5f3..9e62e1303b 100644 --- a/buying/doctype/supplier_quotation/supplier_quotation.py +++ b/buying/doctype/supplier_quotation/supplier_quotation.py @@ -30,10 +30,16 @@ class DocType(TransactionBase): self.doc.name = make_autoname(self.doc.naming_series + ".#####") def validate(self): + if not self.doc.status: + self.doc.status = "Draft" + + import utilities + utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped", + "Cancelled"]) + self.validate_fiscal_year() self.validate_common() self.set_in_words() - self.doc.status = "Draft" def on_submit(self): purchase_controller = webnotes.get_obj("Purchase Common") diff --git a/hr/doctype/appraisal/appraisal.py b/hr/doctype/appraisal/appraisal.py index b4b0e4e8c5..82fb77b2f6 100644 --- a/hr/doctype/appraisal/appraisal.py +++ b/hr/doctype/appraisal/appraisal.py @@ -57,6 +57,7 @@ class DocType: def validate(self): if not self.doc.status: self.doc.status = "Draft" + self.validate_dates() self.validate_existing_appraisal() self.calculate_total() diff --git a/hr/doctype/attendance/attendance.py b/hr/doctype/attendance/attendance.py index d3daeb1dfc..d1ebc97333 100644 --- a/hr/doctype/attendance/attendance.py +++ b/hr/doctype/attendance/attendance.py @@ -8,11 +8,11 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . from __future__ import unicode_literals import webnotes @@ -28,79 +28,81 @@ sql = webnotes.conn.sql class DocType: - def __init__(self, doc, doclist=[]): - self.doc = doc - self.doclist = doclist - - #autoname function - def autoname(self): - self.doc.name = make_autoname(self.doc.naming_series+'.#####') - - #get employee name based on employee id selected - def get_emp_name(self): - emp_nm = sql("select employee_name from `tabEmployee` where name=%s", self.doc.employee) + def __init__(self, doc, doclist=[]): + self.doc = doc + self.doclist = doclist + + #autoname function + def autoname(self): + self.doc.name = make_autoname(self.doc.naming_series+'.#####') + + #get employee name based on employee id selected + def get_emp_name(self): + emp_nm = sql("select employee_name from `tabEmployee` where name=%s", self.doc.employee) - #this is done because sometimes user entered wrong employee name while uploading employee attendance - webnotes.conn.set(self.doc, 'employee_name', emp_nm and emp_nm[0][0] or '') + #this is done because sometimes user entered wrong employee name while uploading employee attendance + webnotes.conn.set(self.doc, 'employee_name', emp_nm and emp_nm[0][0] or '') - ret = { 'employee_name' : emp_nm and emp_nm[0][0] or ''} - return ret - - #validation for duplicate record - def validate_duplicate_record(self): - res = sql("select name from `tabAttendance` where employee = '%s' and att_date = '%s' and not name = '%s' and docstatus = 1"%(self.doc.employee,self.doc.att_date, self.doc.name)) - if res: - msgprint("Employee's attendance already marked.") - raise Exception - - - #check for already record present in leave transaction for same date - def check_leave_record(self): - if self.doc.status == 'Present': - chk = sql("select name from `tabLeave Application` where employee=%s and (from_date <= %s and to_date >= %s) and docstatus!=2", (self.doc.employee, self.doc.att_date, self.doc.att_date)) - if chk: - msgprint("Leave Application created for employee "+self.doc.employee+" whom you are trying to mark as 'Present' ") - raise Exception - - - def validate_fiscal_year(self): - fy=sql("select year_start_date from `tabFiscal Year` where name='%s'"% self.doc.fiscal_year) - ysd=fy and fy[0][0] or "" - yed=add_days(str(ysd),365) - if str(self.doc.att_date) < str(ysd) or str(self.doc.att_date) > str(yed): - msgprint("'%s' Not Within The Fiscal Year selected"%(self.doc.att_date)) - raise Exception - - def validate_att_date(self): - import datetime - if getdate(self.doc.att_date)>getdate(datetime.datetime.now().date().strftime('%Y-%m-%d')): - msgprint("Attendance can not be marked for future dates") - raise Exception + ret = { 'employee_name' : emp_nm and emp_nm[0][0] or ''} + return ret + + #validation for duplicate record + def validate_duplicate_record(self): + res = sql("select name from `tabAttendance` where employee = '%s' and att_date = '%s' and not name = '%s' and docstatus = 1"%(self.doc.employee,self.doc.att_date, self.doc.name)) + if res: + msgprint("Employee's attendance already marked.") + raise Exception + + + #check for already record present in leave transaction for same date + def check_leave_record(self): + if self.doc.status == 'Present': + chk = sql("select name from `tabLeave Application` where employee=%s and (from_date <= %s and to_date >= %s) and docstatus!=2", (self.doc.employee, self.doc.att_date, self.doc.att_date)) + if chk: + msgprint("Leave Application created for employee "+self.doc.employee+" whom you are trying to mark as 'Present' ") + raise Exception + + + def validate_fiscal_year(self): + fy=sql("select year_start_date from `tabFiscal Year` where name='%s'" % \ + self.doc.fiscal_year) + ysd=fy and fy[0][0] or "" + yed=add_days(str(ysd),365) + if str(self.doc.att_date) < str(ysd) or str(self.doc.att_date) > str(yed): + msgprint("'%s' Not Within The Fiscal Year selected"%(self.doc.att_date)) + raise Exception + + def validate_att_date(self): + import datetime + if getdate(self.doc.att_date)>getdate(datetime.datetime.now().date().strftime('%Y-%m-%d')): + msgprint("Attendance can not be marked for future dates") + raise Exception - # Validate employee - #------------------- - def validate_employee(self): - emp = sql("select name, status from `tabEmployee` where name = '%s'" % self.doc.employee) - if not emp: - msgprint("Employee: %s does not exists in the system" % self.doc.employee, raise_exception=1) - elif emp[0][1] != 'Active': - msgprint("Employee: %s is not Active" % self.doc.employee, raise_exception=1) - - # validate... - def validate(self): - self.validate_fiscal_year() - self.validate_att_date() - self.validate_duplicate_record() - #self.validate_status() - self.check_leave_record() - - def on_update(self): - #self.validate() - - #this is done because sometimes user entered wrong employee name while uploading employee attendance - x=self.get_emp_name() + # Validate employee + #------------------- + def validate_employee(self): + emp = sql("select name, status from `tabEmployee` where name = '%s'" % self.doc.employee) + if not emp: + msgprint("Employee: %s does not exists in the system" % self.doc.employee, raise_exception=1) + elif emp[0][1] != 'Active': + msgprint("Employee: %s is not Active" % self.doc.employee, raise_exception=1) + + def validate(self): + import utilities + utilities.validate_status(self.doc.status, ["Present", "Absent", "Half Day"]) - def on_submit(self): - #this is done because while uploading attendance chnage docstatus to 1 i.e. submit - webnotes.conn.set(self.doc,'docstatus',1) - pass + self.validate_fiscal_year() + self.validate_att_date() + self.validate_duplicate_record() + self.check_leave_record() + + def on_update(self): + #self.validate() + + #this is done because sometimes user entered wrong employee name while uploading employee attendance + x=self.get_emp_name() + + def on_submit(self): + #this is done because while uploading attendance chnage docstatus to 1 i.e. submit + webnotes.conn.set(self.doc,'docstatus',1) + pass diff --git a/hr/doctype/employee/employee.py b/hr/doctype/employee/employee.py index c0f7be3659..ab3f842d22 100644 --- a/hr/doctype/employee/employee.py +++ b/hr/doctype/employee/employee.py @@ -19,7 +19,7 @@ import webnotes from webnotes.utils import getdate, validate_email_add from webnotes.model.doc import make_autoname -from webnotes import msgprint +from webnotes import msgprint, _ sql = webnotes.conn.sql @@ -39,6 +39,16 @@ class DocType: self.doc.name = make_autoname(self.doc.employee_number) self.doc.employee = self.doc.name + + def validate(self): + import utilities + utilities.validate_status(self.doc.status, ["Active", "Left"]) + + self.doc.employee = self.doc.name + self.validate_date() + self.validate_email() + self.validate_name() + self.validate_status() def get_retirement_date(self): import datetime @@ -52,13 +62,6 @@ class DocType: ret_sal_struct=sql("select name from `tabSalary Structure` where employee='%s' and is_active = 'Yes' and docstatus!= 2"%nm) return ret_sal_struct and ret_sal_struct[0][0] or '' - def validate(self): - self.doc.employee = self.doc.name - self.validate_date() - self.validate_email() - self.validate_name() - self.validate_status() - def on_update(self): self.update_user_default() diff --git a/hr/doctype/expense_claim/expense_claim.py b/hr/doctype/expense_claim/expense_claim.py index b3a132ed45..2ba53aa5ed 100644 --- a/hr/doctype/expense_claim/expense_claim.py +++ b/hr/doctype/expense_claim/expense_claim.py @@ -33,9 +33,8 @@ class DocType: # if self.doc.exp_approver == self.doc.owner: # webnotes.msgprint("""Self Approval is not allowed.""", raise_exception=1) - if self.doc.status not in ("Draft", "Approved", "Rejected"): - webnotes.msgprint("Status must be one of 'Draft', 'Approved' or 'Rejected'", - raise_exception=True) + import utilities + utilities.validate_status(self.doc.status, ["Draft", "Approved", "Rejected"]) self.validate_fiscal_year() self.validate_exp_details() diff --git a/hr/doctype/leave_application/leave_application.py b/hr/doctype/leave_application/leave_application.py index e61f7b5137..ebefc0eb15 100755 --- a/hr/doctype/leave_application/leave_application.py +++ b/hr/doctype/leave_application/leave_application.py @@ -33,9 +33,8 @@ class DocType: def validate(self): # if self.doc.leave_approver == self.doc.owner: # webnotes.msgprint("""Self Approval is not allowed.""", raise_exception=1) - if self.doc.status not in ("Open", "Approved", "Rejected"): - webnotes.msgprint("Status must be one of 'Open', 'Approved' or 'Rejected'", - raise_exception=True) + import utilities + utilities.validate_status(self.doc.status, ["Open", "Approved", "Rejected"]) self.validate_to_date() self.validate_balance_leaves() diff --git a/hr/doctype/salary_structure/salary_structure.py b/hr/doctype/salary_structure/salary_structure.py index f310b7b4c8..9c5ad246ef 100644 --- a/hr/doctype/salary_structure/salary_structure.py +++ b/hr/doctype/salary_structure/salary_structure.py @@ -8,11 +8,11 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . from __future__ import unicode_literals import webnotes @@ -28,87 +28,86 @@ sql = webnotes.conn.sql class DocType: - #init function - def __init__(self,doc,doclist=[]): - self.doc = doc - self.doclist = doclist + #init function + def __init__(self,doc,doclist=[]): + self.doc = doc + self.doclist = doclist - #autoname function - #--------------------------------------------------------- - def autoname(self): - self.doc.name = make_autoname(self.doc.employee + '/.SST' + '/.#####') - - #get employee details - #--------------------------------------------------------- - def get_employee_details(self): - ret = {} - det = sql("select employee_name, branch, designation, department, grade from `tabEmployee` where name = '%s'" %self.doc.employee) - if det: - ret = { - 'employee_name' : cstr(det[0][0]), - 'branch' : cstr(det[0][1]), - 'designation' : cstr(det[0][2]), - 'department' : cstr(det[0][3]), - 'grade' : cstr(det[0][4]), - 'backup_employee': cstr(self.doc.employee) - } - return ret - + #autoname function + #--------------------------------------------------------- + def autoname(self): + self.doc.name = make_autoname(self.doc.employee + '/.SST' + '/.#####') + + #get employee details + #--------------------------------------------------------- + def get_employee_details(self): + ret = {} + det = sql("select employee_name, branch, designation, department, grade from `tabEmployee` where name = '%s'" %self.doc.employee) + if det: + ret = { + 'employee_name': cstr(det[0][0]), + 'branch': cstr(det[0][1]), + 'designation': cstr(det[0][2]), + 'department': cstr(det[0][3]), + 'grade': cstr(det[0][4]), + 'backup_employee': cstr(self.doc.employee) + } + return ret + - # Set Salary structure field values - #--------------------------------------------------------- - def get_ss_values(self,employee): - basic_info = sql("select bank_name, bank_ac_no, esic_card_no, pf_number from `tabEmployee` where name ='%s'" % employee) - ret = {'bank_name' : basic_info and basic_info[0][0] or '', - 'bank_ac_no' : basic_info and basic_info[0][1] or '', - 'esic_no' : basic_info and basic_info[0][2] or '', - 'pf_no' : basic_info and basic_info[0][3] or ''} - return ret - - # Make earning and deduction table - #--------------------------------------------------------- - def make_table(self, doct_name, tab_fname, tab_name): - list1 = sql("select name from `tab%s` where docstatus != 2" % doct_name) - for li in list1: - child = addchild(self.doc, tab_fname, tab_name, self.doclist) - if(tab_fname == 'earning_details'): - child.e_type = cstr(li[0]) - child.modified_value = 0 - elif(tab_fname == 'deduction_details'): - child.d_type = cstr(li[0]) - child.d_modified_amt = 0 - - # add earning & deduction types to table - #--------------------------------------------------------- - def make_earn_ded_table(self): - #Earning List - self.make_table('Earning Type','earning_details','Salary Structure Earning') - - #Deduction List - self.make_table('Deduction Type','deduction_details', 'Salary Structure Deduction') - + # Set Salary structure field values + #--------------------------------------------------------- + def get_ss_values(self,employee): + basic_info = sql("select bank_name, bank_ac_no, esic_card_no, pf_number from `tabEmployee` where name ='%s'" % employee) + ret = {'bank_name': basic_info and basic_info[0][0] or '', + 'bank_ac_no': basic_info and basic_info[0][1] or '', + 'esic_no': basic_info and basic_info[0][2] or '', + 'pf_no': basic_info and basic_info[0][3] or ''} + return ret + + # Make earning and deduction table + #--------------------------------------------------------- + def make_table(self, doct_name, tab_fname, tab_name): + list1 = sql("select name from `tab%s` where docstatus != 2" % doct_name) + for li in list1: + child = addchild(self.doc, tab_fname, tab_name, self.doclist) + if(tab_fname == 'earning_details'): + child.e_type = cstr(li[0]) + child.modified_value = 0 + elif(tab_fname == 'deduction_details'): + child.d_type = cstr(li[0]) + child.d_modified_amt = 0 + + # add earning & deduction types to table + #--------------------------------------------------------- + def make_earn_ded_table(self): + #Earning List + self.make_table('Earning Type','earning_details','Salary Structure Earning') + + #Deduction List + self.make_table('Deduction Type','deduction_details', + 'Salary Structure Deduction') + - # Check if another active ss exists - #--------------------------------------------------------- - def check_existing(self): - ret = sql("select name from `tabSalary Structure` where is_active = 'Yes' and employee = '%s' and name!='%s'" %(self.doc.employee,self.doc.name)) - if ret and self.doc.is_active=='Yes': - msgprint("Another Salary Structure '%s' is active for employee '%s'. Please make its status 'Inactive' to proceed."%(cstr(ret), self.doc.employee)) - raise Exception + # Check if another active ss exists + #--------------------------------------------------------- + def check_existing(self): + ret = sql("select name from `tabSalary Structure` where is_active = 'Yes' and employee = '%s' and name!='%s'" %(self.doc.employee,self.doc.name)) + if ret and self.doc.is_active=='Yes': + msgprint("Another Salary Structure '%s' is active for employee '%s'. Please make its status 'Inactive' to proceed."%(cstr(ret), self.doc.employee)) + raise Exception - # Validate net pay - #--------------------------------------------------------- - def validate_net_pay(self): - if flt(self.doc.net_pay) < 0: - msgprint("Net pay can not be negative") - raise Exception - elif flt(self.doc.net_pay) > flt(self.doc.ctc): - msgprint("Net pay can not be greater than CTC") - raise Exception + # Validate net pay + #--------------------------------------------------------- + def validate_net_pay(self): + if flt(self.doc.net_pay) < 0: + msgprint("Net pay can not be negative") + raise Exception + elif flt(self.doc.net_pay) > flt(self.doc.ctc): + msgprint("Net pay can not be greater than CTC") + raise Exception - # Validate - #--------------------------------------------------------- - def validate(self): - self.check_existing() - self.validate_net_pay() + def validate(self): + self.check_existing() + self.validate_net_pay() diff --git a/manufacturing/doctype/production_order/production_order.py b/manufacturing/doctype/production_order/production_order.py index 3ba368efca..9a09494b92 100644 --- a/manufacturing/doctype/production_order/production_order.py +++ b/manufacturing/doctype/production_order/production_order.py @@ -34,6 +34,10 @@ class DocType: self.doclist = doclist def validate(self): + import utilities + utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped", + "In Process", "Completed", "Cancelled"]) + if self.doc.production_item : item_detail = sql("select name from `tabItem` where name = '%s' and docstatus != 2" % self.doc.production_item, as_dict = 1) diff --git a/selling/doctype/installation_note/installation_note.py b/selling/doctype/installation_note/installation_note.py index 1b8590b1f5..5a997fdc04 100644 --- a/selling/doctype/installation_note/installation_note.py +++ b/selling/doctype/installation_note/installation_note.py @@ -8,11 +8,11 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . from __future__ import unicode_literals import webnotes @@ -30,178 +30,176 @@ sql = webnotes.conn.sql from utilities.transaction_base import TransactionBase class DocType(TransactionBase): - def __init__(self, doc, doclist=[]): - self.doc = doc - self.doclist = doclist - self.tname = 'Installation Note Item' - self.fname = 'installed_item_details' + def __init__(self, doc, doclist=[]): + self.doc = doc + self.doclist = doclist + self.tname = 'Installation Note Item' + self.fname = 'installed_item_details' - # Autoname - # --------- - def autoname(self): - self.doc.name = make_autoname(self.doc.naming_series+'.#####') + def autoname(self): + self.doc.name = make_autoname(self.doc.naming_series+'.#####') + + def validate(self): + self.validate_fiscal_year() + self.validate_installation_date() + self.check_item_table() + sales_com_obj = get_obj(dt = 'Sales Common') + sales_com_obj.check_active_sales_items(self) + sales_com_obj.get_prevdoc_date(self) + self.validate_mandatory() + self.validate_reference_value() - - #fetch delivery note details - #==================================== - def pull_delivery_note_details(self): - self.validate_prev_docname() - self.doclist = get_obj('DocType Mapper', 'Delivery Note-Installation Note').dt_map('Delivery Note', 'Installation Note', self.doc.delivery_note_no, self.doc, self.doclist, "[['Delivery Note', 'Installation Note'],['Delivery Note Item', 'Installation Note Item']]") - - # Validates that Delivery Note is not pulled twice - #============================================ - def validate_prev_docname(self): - for d in getlist(self.doclist, 'installed_item_details'): - if self.doc.delivery_note_no == d.prevdoc_docname: - msgprint(cstr(self.doc.delivery_note_no) + " delivery note details have already been pulled. ") - raise Exception, "Validation Error. " - - #Fiscal Year Validation - #================================ - def validate_fiscal_year(self): - get_obj('Sales Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.inst_date,'Installation Date') - - # Validate Mandatory - #=============================== - def validate_mandatory(self): - # Amendment Date - if self.doc.amended_from and not self.doc.amendment_date: - msgprint("Please Enter Amendment Date") - raise Exception, "Validation Error. " - - # Validate values with reference document - #---------------------------------------- - def validate_reference_value(self): - get_obj('DocType Mapper', 'Delivery Note-Installation Note', with_children = 1).validate_reference_value(self, self.doc.name) - - #check if serial no added - #----------------------------- - def is_serial_no_added(self,item_code,serial_no): - ar_required = sql("select has_serial_no from tabItem where name = '%s'" % item_code) - ar_required = ar_required and ar_required[0][0] or '' - if ar_required == 'Yes' and not serial_no: - msgprint("Serial No is mandatory for item: "+ item_code) - raise Exception - elif ar_required != 'Yes' and cstr(serial_no).strip(): - msgprint("If serial no required, please select 'Yes' in 'Has Serial No' in Item :"+item_code) - raise Exception - - #check if serial no exist in system - #------------------------------------- - def is_serial_no_exist(self, item_code, serial_no): - for x in serial_no: - chk = sql("select name from `tabSerial No` where name =%s", x) - if not chk: - msgprint("Serial No "+x+" does not exist in the system") - raise Exception - - #check if serial no already installed - #------------------------------------------ - def is_serial_no_installed(self,cur_s_no,item_code): - for x in cur_s_no: - status = sql("select status from `tabSerial No` where name = %s", x) - status = status and status[0][0] or '' - - if status == 'Installed': - msgprint("Item "+item_code+" with serial no. "+x+" already installed") - raise Exception, "Validation Error." - - #get list of serial no from previous_doc - #---------------------------------------------- - def get_prevdoc_serial_no(self, prevdoc_detail_docname, prevdoc_docname): - from stock.doctype.stock_ledger.stock_ledger import get_sr_no_list - res = sql("select serial_no from `tabDelivery Note Item` where name = '%s' and parent ='%s'" % (prevdoc_detail_docname, prevdoc_docname)) - return get_sr_no_list(res[0][0]) - - #check if all serial nos from current record exist in resp delivery note - #--------------------------------------------------------------------------------- - def is_serial_no_match(self, cur_s_no, prevdoc_s_no, prevdoc_docname): - for x in cur_s_no: - if not(x in prevdoc_s_no): - msgprint("Serial No. "+x+" not present in the Delivery Note "+prevdoc_docname, raise_exception = 1) - raise Exception, "Validation Error." - - #validate serial number - #---------------------------------------- - def validate_serial_no(self): - cur_s_no, prevdoc_s_no, sr_list = [], [], [] - from stock.doctype.stock_ledger.stock_ledger import get_sr_no_list - - for d in getlist(self.doclist, 'installed_item_details'): - self.is_serial_no_added(d.item_code, d.serial_no) - - if d.serial_no: + #fetch delivery note details + #==================================== + def pull_delivery_note_details(self): + self.validate_prev_docname() + self.doclist = get_obj('DocType Mapper', 'Delivery Note-Installation Note').dt_map('Delivery Note', 'Installation Note', self.doc.delivery_note_no, self.doc, self.doclist, "[['Delivery Note', 'Installation Note'],['Delivery Note Item', 'Installation Note Item']]") + + # Validates that Delivery Note is not pulled twice + #============================================ + def validate_prev_docname(self): + for d in getlist(self.doclist, 'installed_item_details'): + if self.doc.delivery_note_no == d.prevdoc_docname: + msgprint(cstr(self.doc.delivery_note_no) + " delivery note details have already been pulled. ") + raise Exception, "Validation Error. " + + #Fiscal Year Validation + #================================ + def validate_fiscal_year(self): + get_obj('Sales Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.inst_date,'Installation Date') + + # Validate Mandatory + #=============================== + def validate_mandatory(self): + # Amendment Date + if self.doc.amended_from and not self.doc.amendment_date: + msgprint("Please Enter Amendment Date") + raise Exception, "Validation Error. " + + # Validate values with reference document + #---------------------------------------- + def validate_reference_value(self): + get_obj('DocType Mapper', 'Delivery Note-Installation Note', with_children = 1).validate_reference_value(self, self.doc.name) + + #check if serial no added + #----------------------------- + def is_serial_no_added(self,item_code,serial_no): + ar_required = sql("select has_serial_no from tabItem where name = '%s'" % item_code) + ar_required = ar_required and ar_required[0][0] or '' + if ar_required == 'Yes' and not serial_no: + msgprint("Serial No is mandatory for item: "+ item_code) + raise Exception + elif ar_required != 'Yes' and cstr(serial_no).strip(): + msgprint("If serial no required, please select 'Yes' in 'Has Serial No' in Item :"+item_code) + raise Exception + + #check if serial no exist in system + #------------------------------------- + def is_serial_no_exist(self, item_code, serial_no): + for x in serial_no: + chk = sql("select name from `tabSerial No` where name =%s", x) + if not chk: + msgprint("Serial No "+x+" does not exist in the system") + raise Exception + + #check if serial no already installed + #------------------------------------------ + def is_serial_no_installed(self,cur_s_no,item_code): + for x in cur_s_no: + status = sql("select status from `tabSerial No` where name = %s", x) + status = status and status[0][0] or '' + + if status == 'Installed': + msgprint("Item "+item_code+" with serial no. "+x+" already installed") + raise Exception, "Validation Error." + + #get list of serial no from previous_doc + #---------------------------------------------- + def get_prevdoc_serial_no(self, prevdoc_detail_docname, prevdoc_docname): + from stock.doctype.stock_ledger.stock_ledger import get_sr_no_list + + res = sql("select serial_no from `tabDelivery Note Item` where name = '%s' and parent ='%s'" % (prevdoc_detail_docname, prevdoc_docname)) + return get_sr_no_list(res[0][0]) + + #check if all serial nos from current record exist in resp delivery note + #--------------------------------------------------------------------------------- + def is_serial_no_match(self, cur_s_no, prevdoc_s_no, prevdoc_docname): + for x in cur_s_no: + if not(x in prevdoc_s_no): + msgprint("Serial No. "+x+" not present in the Delivery Note "+prevdoc_docname, raise_exception = 1) + raise Exception, "Validation Error." + + #validate serial number + #---------------------------------------- + def validate_serial_no(self): + cur_s_no, prevdoc_s_no, sr_list = [], [], [] + from stock.doctype.stock_ledger.stock_ledger import get_sr_no_list + + for d in getlist(self.doclist, 'installed_item_details'): + self.is_serial_no_added(d.item_code, d.serial_no) + + if d.serial_no: - sr_list = get_sr_no_list(d.serial_no, d.qty, d.item_code) - self.is_serial_no_exist(d.item_code, sr_list) - - prevdoc_s_no = self.get_prevdoc_serial_no(d.prevdoc_detail_docname, d.prevdoc_docname) - if prevdoc_s_no: - self.is_serial_no_match(sr_list, prevdoc_s_no, d.prevdoc_docname) - - self.is_serial_no_installed(sr_list, d.item_code) - return sr_list - - #validate installation date - #------------------------------- - def validate_installation_date(self): - for d in getlist(self.doclist, 'installed_item_details'): - if d.prevdoc_docname: - d_date = sql("select posting_date from `tabDelivery Note` where name=%s", d.prevdoc_docname) - d_date = d_date and d_date[0][0] or '' - - if d_date > getdate(self.doc.inst_date): - msgprint("Installation Date can not be before Delivery Date "+cstr(d_date)+" for item "+d.item_code) - raise Exception - - def validate(self): - self.validate_fiscal_year() - self.validate_installation_date() - self.check_item_table() - sales_com_obj = get_obj(dt = 'Sales Common') - sales_com_obj.check_active_sales_items(self) - sales_com_obj.get_prevdoc_date(self) - self.validate_mandatory() - self.validate_reference_value() - - def check_item_table(self): - if not(getlist(self.doclist, 'installed_item_details')): - msgprint("Please fetch items from Delivery Note selected") - raise Exception - - def on_update(self): - webnotes.conn.set(self.doc, 'status', 'Draft') - - def on_submit(self): - valid_lst = [] - valid_lst = self.validate_serial_no() - - get_obj("Sales Common").update_prevdoc_detail(1,self) - - for x in valid_lst: - wp = sql("select warranty_period from `tabSerial No` where name = '%s'"% x) - wp = wp and wp[0][0] or 0 - if wp: - sql("update `tabSerial No` set maintenance_status = 'Under Warranty' where name = '%s'" % x) - - sql("update `tabSerial No` set status = 'Installed' where name = '%s'" % x) - - webnotes.conn.set(self.doc, 'status', 'Submitted') + sr_list = get_sr_no_list(d.serial_no, d.qty, d.item_code) + self.is_serial_no_exist(d.item_code, sr_list) + + prevdoc_s_no = self.get_prevdoc_serial_no(d.prevdoc_detail_docname, d.prevdoc_docname) + if prevdoc_s_no: + self.is_serial_no_match(sr_list, prevdoc_s_no, d.prevdoc_docname) + + self.is_serial_no_installed(sr_list, d.item_code) + return sr_list + + #validate installation date + #------------------------------- + def validate_installation_date(self): + for d in getlist(self.doclist, 'installed_item_details'): + if d.prevdoc_docname: + d_date = sql("select posting_date from `tabDelivery Note` where name=%s", d.prevdoc_docname) + d_date = d_date and d_date[0][0] or '' + + if d_date > getdate(self.doc.inst_date): + msgprint("Installation Date can not be before Delivery Date "+cstr(d_date)+" for item "+d.item_code) + raise Exception + + def check_item_table(self): + if not(getlist(self.doclist, 'installed_item_details')): + msgprint("Please fetch items from Delivery Note selected") + raise Exception + + def on_update(self): + webnotes.conn.set(self.doc, 'status', 'Draft') + + def on_submit(self): + valid_lst = [] + valid_lst = self.validate_serial_no() + + get_obj("Sales Common").update_prevdoc_detail(1,self) + + for x in valid_lst: + wp = sql("select warranty_period from `tabSerial No` where name = '%s'"% x) + wp = wp and wp[0][0] or 0 + if wp: + sql("update `tabSerial No` set maintenance_status = 'Under Warranty' where name = '%s'" % x) + + sql("update `tabSerial No` set status = 'Installed' where name = '%s'" % x) + + webnotes.conn.set(self.doc, 'status', 'Submitted') - - def on_cancel(self): - cur_s_no = [] - sales_com_obj = get_obj(dt = 'Sales Common') - sales_com_obj.update_prevdoc_detail(0,self) - - for d in getlist(self.doclist, 'installed_item_details'): - if d.serial_no: - #get current list of serial no - cur_serial_no = d.serial_no.replace(' ', '') - cur_s_no = cur_serial_no.split(',') - - for x in cur_s_no: - sql("update `tabSerial No` set status = 'Delivered' where name = '%s'" % x) - - webnotes.conn.set(self.doc, 'status', 'Cancelled') + + def on_cancel(self): + cur_s_no = [] + sales_com_obj = get_obj(dt = 'Sales Common') + sales_com_obj.update_prevdoc_detail(0,self) + + for d in getlist(self.doclist, 'installed_item_details'): + if d.serial_no: + #get current list of serial no + cur_serial_no = d.serial_no.replace(' ', '') + cur_s_no = cur_serial_no.split(',') + + for x in cur_s_no: + sql("update `tabSerial No` set status = 'Delivered' where name = '%s'" % x) + + webnotes.conn.set(self.doc, 'status', 'Cancelled') diff --git a/selling/doctype/quotation/quotation.py b/selling/doctype/quotation/quotation.py index 3b452f18b7..ed09ab03f8 100644 --- a/selling/doctype/quotation/quotation.py +++ b/selling/doctype/quotation/quotation.py @@ -179,6 +179,10 @@ class DocType(TransactionBase): # Validate # -------- def validate(self): + import utilities + utilities.validate_status(self.doc.status, ["Draft", "Submitted", + "Order Confirmed", "Order Lost", "Cancelled"]) + self.validate_fiscal_year() self.validate_mandatory() self.set_last_contact_date() diff --git a/selling/doctype/sales_order/sales_order.py b/selling/doctype/sales_order/sales_order.py index e0ac2ffb57..3e7b03dd83 100644 --- a/selling/doctype/sales_order/sales_order.py +++ b/selling/doctype/sales_order/sales_order.py @@ -226,8 +226,13 @@ class DocType(TransactionBase): self.doc.in_words = sales_com_obj.get_total_in_words(dcc, self.doc.rounded_total) self.doc.in_words_export = sales_com_obj.get_total_in_words(self.doc.currency, self.doc.rounded_total_export) - # set SO status - self.doc.status='Draft' + if not self.doc.status: + self.doc.status = "Draft" + + import utilities + utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped", + "Cancelled"]) + if not self.doc.billing_status: self.doc.billing_status = 'Not Billed' if not self.doc.delivery_status: self.doc.delivery_status = 'Not Delivered' diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py index af4d91e9aa..411ce5c4c0 100644 --- a/stock/doctype/delivery_note/delivery_note.py +++ b/stock/doctype/delivery_note/delivery_note.py @@ -131,6 +131,9 @@ class DocType(TransactionBase): def validate(self): + import utilities + utilities.validate_status(self.doc.status, ["Draft", "submitted", "Cancelled"]) + self.so_required() self.validate_fiscal_year() self.validate_proj_cust() diff --git a/stock/doctype/purchase_receipt/purchase_receipt.py b/stock/doctype/purchase_receipt/purchase_receipt.py index 5e60753a18..8c2ad016e0 100644 --- a/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/stock/doctype/purchase_receipt/purchase_receipt.py @@ -148,7 +148,13 @@ class DocType(TransactionBase): def validate(self): self.po_required() self.validate_fiscal_year() - webnotes.conn.set(self.doc, 'status', 'Draft') # set status as "Draft" + + if not self.doc.status: + self.doc.status = "Draft" + + import utilities + utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Cancelled"]) + self.validate_accepted_rejected_qty() self.validate_inspection() # Validate Inspection get_obj('Stock Ledger').validate_serial_no(self, 'purchase_receipt_details') diff --git a/stock/doctype/serial_no/serial_no.py b/stock/doctype/serial_no/serial_no.py index b6f60f57f3..35308bce17 100644 --- a/stock/doctype/serial_no/serial_no.py +++ b/stock/doctype/serial_no/serial_no.py @@ -65,6 +65,10 @@ class DocType(TransactionBase): # validate # --------- def validate(self): + import utilities + utilities.validate_status(self.doc.status, ["In Store", "Delivered", + "Not in Use", "Purchase Returned"]) + self.validate_warranty_status() self.validate_amc_status() self.validate_warehouse() diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py index 2e26a1a86a..18440329ae 100644 --- a/stock/doctype/stock_entry/stock_entry.py +++ b/stock/doctype/stock_entry/stock_entry.py @@ -17,7 +17,7 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cstr, cint, flt, getdate, now +from webnotes.utils import cstr, cint, flt, getdate, now, comma_or from webnotes.model import db_exists, delete_doc from webnotes.model.doc import Document, addchild from webnotes.model.wrapper import getlist, copy_doclist @@ -35,6 +35,8 @@ class DocType(TransactionBase): self.fname = 'mtn_details' def validate(self): + self.validate_purpose() + self.validate_serial_nos() pro_obj = self.doc.production_order and \ get_obj('Production Order', self.doc.production_order) or None @@ -57,6 +59,13 @@ class DocType(TransactionBase): self.update_stock_ledger(1) # update Production Order self.update_production_order(0) + + def validate_purpose(self): + valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer", + "Manufacture/Repack", "Subcontract", "Sales Return", "Purchase Return"] + if self.doc.purpose not in valid_purposes: + msgprint(_("Purpose must be one of ") + comma_or(valid_purposes), + raise_exception=True) def validate_serial_nos(self): sl_obj = get_obj("Stock Ledger") diff --git a/support/doctype/customer_issue/customer_issue.py b/support/doctype/customer_issue/customer_issue.py index 44ae891c84..0a08d82f38 100644 --- a/support/doctype/customer_issue/customer_issue.py +++ b/support/doctype/customer_issue/customer_issue.py @@ -41,9 +41,8 @@ class DocType(TransactionBase): def validate(self): if session['user'] != 'Guest' and not self.doc.customer: - msgprint("Please select Customer from whom issue is raised") - raise Exception - + msgprint("Please select Customer from whom issue is raised", + raise_exception=True) def on_cancel(self): lst = sql("select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent = t1.name and t2.prevdoc_docname = '%s' and t1.docstatus!=2"%(self.doc.name)) diff --git a/utilities/__init__.py b/utilities/__init__.py index 486568d638..7c44ec31dd 100644 --- a/utilities/__init__.py +++ b/utilities/__init__.py @@ -16,7 +16,8 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cint +from webnotes import _, msgprint +from webnotes.utils import cint, comma_or @webnotes.whitelist() def get_sc_list(arg=None): @@ -52,4 +53,8 @@ def get_report_list(): and ifnull(tabReport.disabled,0) != 1 order by tabReport.name limit %s, %s""" % \ - ("%s", cint(limit_start), cint(limit_page_length)), (module,), as_dict=True) \ No newline at end of file + ("%s", cint(limit_start), cint(limit_page_length)), (module,), as_dict=True) + +def validate_status(status, options): + if status not in options: + msgprint(_("Status must be one of ") + comma_or(options), raise_exception=True) From 751af41a948960a43eef7c17d7964e6b30d03d1f Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Sun, 30 Dec 2012 16:43:04 +0530 Subject: [PATCH 5/7] temporarily removed status validation for serial no --- stock/doctype/serial_no/serial_no.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stock/doctype/serial_no/serial_no.py b/stock/doctype/serial_no/serial_no.py index 35308bce17..190b92be3f 100644 --- a/stock/doctype/serial_no/serial_no.py +++ b/stock/doctype/serial_no/serial_no.py @@ -65,9 +65,9 @@ class DocType(TransactionBase): # validate # --------- def validate(self): - import utilities - utilities.validate_status(self.doc.status, ["In Store", "Delivered", - "Not in Use", "Purchase Returned"]) + # import utilities + # utilities.validate_status(self.doc.status, ["In Store", "Delivered", + # "Not in Use", "Purchase Returned"]) self.validate_warranty_status() self.validate_amc_status() From 833d7a65d578c2874109d656be479e1bfe994272 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sun, 30 Dec 2012 19:56:57 +0530 Subject: [PATCH 6/7] small fixes in product caching --- website/helpers/product.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/helpers/product.py b/website/helpers/product.py index 1e8257a7a7..623ceeee34 100644 --- a/website/helpers/product.py +++ b/website/helpers/product.py @@ -102,4 +102,5 @@ def get_parent_item_groups(item_group_name): def invalidate_cache_for(item_group): for i in get_parent_item_groups(item_group): - delete_page_cache(i.page_name) \ No newline at end of file + if i.page_name: + delete_page_cache(i.page_name) \ No newline at end of file From 65cef741683a9114492696bb8e82c1b2b725d20d Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Sun, 30 Dec 2012 19:58:24 +0530 Subject: [PATCH 7/7] before delete page cache, check if page name exists --- website/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/utils.py b/website/utils.py index ec33103e29..674d752114 100644 --- a/website/utils.py +++ b/website/utils.py @@ -263,7 +263,8 @@ def clear_cache(page_name=None): webnotes.cache().delete_keys("page:") def delete_page_cache(page_name): - webnotes.cache().delete_value("page:" + page_name) + if page_name: + webnotes.cache().delete_value("page:" + page_name) def url_for_website(url): if url and not url.lower().startswith("http"):