From c4ee74857de0e41760d709e7c195baaec329808d Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 24 Jan 2014 21:53:11 +0530 Subject: [PATCH 01/31] BugFix: Production Planning Tool - get_raw_materials --- .../production_planning_tool/production_planning_tool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/manufacturing/doctype/production_planning_tool/production_planning_tool.py index 29232f5f519..90d9dc57cd4 100644 --- a/manufacturing/doctype/production_planning_tool/production_planning_tool.py +++ b/manufacturing/doctype/production_planning_tool/production_planning_tool.py @@ -243,10 +243,10 @@ class DocType: "item_code": [qty_required, description, stock_uom, min_order_qty] } """ - bom_wise_item_details = {} item_list = [] for bom, so_wise_qty in bom_dict.items(): + bom_wise_item_details = {} if self.doc.use_multi_level_bom: # get all raw materials with sub assembly childs for d in webnotes.conn.sql("""select fb.item_code, From 49e8e783e3e1b32f6e1f40dca59c5aef61f9a472 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Mon, 27 Jan 2014 11:12:24 +0530 Subject: [PATCH 02/31] webnotes/erpnext#1353 allow rename for price list --- stock/doctype/price_list/price_list.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stock/doctype/price_list/price_list.txt b/stock/doctype/price_list/price_list.txt index daa89d1cc57..cc70fd897ec 100644 --- a/stock/doctype/price_list/price_list.txt +++ b/stock/doctype/price_list/price_list.txt @@ -2,7 +2,7 @@ { "creation": "2013-01-25 11:35:09", "docstatus": 0, - "modified": "2014-01-17 13:29:39", + "modified": "2014-01-27 11:11:08", "modified_by": "Administrator", "owner": "Administrator" }, @@ -11,6 +11,7 @@ "allow_copy": 0, "allow_email": 1, "allow_print": 1, + "allow_rename": 1, "autoname": "field:price_list_name", "description": "Price List Master", "doctype": "DocType", From fa9fabaa49f749b7ec28611998b51d0ddb25ca50 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 27 Jan 2014 12:46:18 +0530 Subject: [PATCH 03/31] Serial no status fix: patch to set status not available where no sle exists --- .../1401/fix_serial_no_status_and_warehouse.py | 17 +++++++++++++++++ patches/patch_list.py | 1 + stock/doctype/serial_no/serial_no.py | 11 +++++++---- 3 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 patches/1401/fix_serial_no_status_and_warehouse.py diff --git a/patches/1401/fix_serial_no_status_and_warehouse.py b/patches/1401/fix_serial_no_status_and_warehouse.py new file mode 100644 index 00000000000..fe43c282d81 --- /dev/null +++ b/patches/1401/fix_serial_no_status_and_warehouse.py @@ -0,0 +1,17 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import webnotes + +def execute(): + serial_nos = webnotes.conn.sql("""select name from `tabSerial No` where docstatus=0 + and status in ('Available', 'Sales Returned') and ifnull(warehouse, '') = ''""") + for sr in serial_nos: + try: + sr_bean = webnotes.bean("Serial No", sr[0]) + sr_bean.make_controller().via_stock_ledger = True + sr_bean.save() + webnotes.conn.commit() + except: + pass \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index 89e33091bf8..d87d3ad36ff 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -267,4 +267,5 @@ patch_list = [ "patches.1401.p01_make_buying_selling_as_check_box_in_price_list", "patches.1401.update_billing_status_for_zero_value_order", "patches.1401.enable_all_price_list", + "patches.1401.fix_serial_no_status_and_warehouse", ] \ No newline at end of file diff --git a/stock/doctype/serial_no/serial_no.py b/stock/doctype/serial_no/serial_no.py index bd2704d8ac0..e6557b42847 100644 --- a/stock/doctype/serial_no/serial_no.py +++ b/stock/doctype/serial_no/serial_no.py @@ -87,6 +87,8 @@ class DocType(StockController): self.doc.status = "Sales Returned" else: self.doc.status = "Available" + if not self.doc.warehouse: + self.doc.warehouse = last_sle.warehouse else: if document_type == "Purchase Return": self.doc.status = "Purchase Returned" @@ -94,6 +96,8 @@ class DocType(StockController): self.doc.status = "Delivered" else: self.doc.status = "Not Available" + else: + self.doc.status = "Not Available" def set_purchase_details(self, purchase_sle): if purchase_sle: @@ -185,10 +189,9 @@ class DocType(StockController): def on_stock_ledger_entry(self): if self.via_stock_ledger and not self.doc.fields.get("__islocal"): last_sle = self.get_last_sle() - if last_sle: - self.set_status(last_sle.get("last_sle")) - self.set_purchase_details(last_sle.get("purchase_sle")) - self.set_sales_details(last_sle.get("delivery_sle")) + self.set_status(last_sle.get("last_sle")) + self.set_purchase_details(last_sle.get("purchase_sle")) + self.set_sales_details(last_sle.get("delivery_sle")) def on_communication(self): return From 24b26db3271a3c77c853c1f40b3b313b66e97182 Mon Sep 17 00:00:00 2001 From: Thura Hlaing Date: Mon, 27 Jan 2014 14:08:55 +0630 Subject: [PATCH 04/31] initial prototype implementation for issue #856 --- .../doctype/stock_settings/stock_settings.py | 17 +- .../doctype/stock_settings/stock_settings.txt | 182 +++++++++--------- 2 files changed, 105 insertions(+), 94 deletions(-) diff --git a/stock/doctype/stock_settings/stock_settings.py b/stock/doctype/stock_settings/stock_settings.py index 48e1ee16198..a98ed407e7b 100644 --- a/stock/doctype/stock_settings/stock_settings.py +++ b/stock/doctype/stock_settings/stock_settings.py @@ -5,18 +5,23 @@ from __future__ import unicode_literals import webnotes - +from webnotes import _ class DocType: def __init__(self, d, dl): self.doc, self.doclist = d, dl - + def validate(self): - for key in ["item_naming_by", "item_group", "stock_uom", + for key in ["item_naming_by", "item_group", "stock_uom", "allow_negative_stock"]: webnotes.conn.set_default(key, self.doc.fields.get(key, "")) - + from setup.doctype.naming_series.naming_series import set_by_naming_series - set_by_naming_series("Item", "item_code", + set_by_naming_series("Item", "item_code", self.doc.get("item_naming_by")=="Naming Series", hide_name_field=True) - + + stock_frozen_limit = 356 + submitted_stock_frozen = self.doc.fields.get("stock_frozen_upto") + if submitted_stock_frozen > stock_frozen_limit: + self.doc.fields["stock_frozen_upto"] = stock_frozen_limit + webnotes.msgprint (_("Stocks cannot be freezed for days larger than %d.") %stock_frozen_limit) diff --git a/stock/doctype/stock_settings/stock_settings.txt b/stock/doctype/stock_settings/stock_settings.txt index 634ee3a60fb..59710ffc7c6 100644 --- a/stock/doctype/stock_settings/stock_settings.txt +++ b/stock/doctype/stock_settings/stock_settings.txt @@ -1,128 +1,134 @@ [ { - "creation": "2013-06-24 16:37:54", - "docstatus": 0, - "modified": "2013-11-02 19:41:56", - "modified_by": "Administrator", + "creation": "2013-06-24 16:37:54", + "docstatus": 0, + "modified": "2014-01-27 13:29:56", + "modified_by": "Administrator", "owner": "Administrator" - }, + }, { - "description": "Settings", - "doctype": "DocType", - "icon": "icon-cog", - "issingle": 1, - "module": "Stock", + "description": "Settings", + "doctype": "DocType", + "icon": "icon-cog", + "issingle": 1, + "module": "Stock", "name": "__common__" - }, + }, { - "doctype": "DocField", - "name": "__common__", - "parent": "Stock Settings", - "parentfield": "fields", - "parenttype": "DocType", + "doctype": "DocField", + "name": "__common__", + "parent": "Stock Settings", + "parentfield": "fields", + "parenttype": "DocType", "permlevel": 0 - }, + }, { - "create": 1, - "doctype": "DocPerm", - "name": "__common__", - "parent": "Stock Settings", - "parentfield": "permissions", - "parenttype": "DocType", - "permlevel": 0, - "read": 1, - "role": "Material Manager", + "create": 1, + "doctype": "DocPerm", + "name": "__common__", + "parent": "Stock Settings", + "parentfield": "permissions", + "parenttype": "DocType", + "permlevel": 0, + "read": 1, + "role": "Material Manager", "write": 1 - }, + }, { - "doctype": "DocType", + "doctype": "DocType", "name": "Stock Settings" - }, + }, { - "doctype": "DocField", - "fieldname": "item_naming_by", - "fieldtype": "Select", - "label": "Item Naming By", + "doctype": "DocField", + "fieldname": "item_naming_by", + "fieldtype": "Select", + "label": "Item Naming By", "options": "Item Code\nNaming Series" - }, + }, { - "description": "Add / Edit", - "doctype": "DocField", - "fieldname": "item_group", - "fieldtype": "Link", - "label": "Default Item Group", + "description": "Add / Edit", + "doctype": "DocField", + "fieldname": "item_group", + "fieldtype": "Link", + "label": "Default Item Group", "options": "Item Group" - }, + }, { - "doctype": "DocField", - "fieldname": "stock_uom", - "fieldtype": "Link", - "label": "Default Stock UOM", + "doctype": "DocField", + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Default Stock UOM", "options": "UOM" - }, + }, { - "doctype": "DocField", - "fieldname": "column_break_4", + "doctype": "DocField", + "fieldname": "column_break_4", "fieldtype": "Column Break" - }, + }, { - "doctype": "DocField", - "fieldname": "allow_negative_stock", - "fieldtype": "Check", + "doctype": "DocField", + "fieldname": "allow_negative_stock", + "fieldtype": "Check", "label": "Allow Negative Stock" - }, + }, { - "doctype": "DocField", - "fieldname": "valuation_method", - "fieldtype": "Select", - "label": "Default Valuation Method", + "doctype": "DocField", + "fieldname": "valuation_method", + "fieldtype": "Select", + "label": "Default Valuation Method", "options": "FIFO\nMoving Average" - }, + }, { - "description": "Percentage you are allowed to receive or deliver more against the quantity ordered. For example: If you have ordered 100 units. and your Allowance is 10% then you are allowed to receive 110 units.", - "doctype": "DocField", - "fieldname": "tolerance", - "fieldtype": "Float", + "description": "Percentage you are allowed to receive or deliver more against the quantity ordered. For example: If you have ordered 100 units. and your Allowance is 10% then you are allowed to receive 110 units.", + "doctype": "DocField", + "fieldname": "tolerance", + "fieldtype": "Float", "label": "Allowance Percent" - }, + }, { - "doctype": "DocField", - "fieldname": "auto_material_request", - "fieldtype": "Section Break", + "doctype": "DocField", + "fieldname": "auto_material_request", + "fieldtype": "Section Break", "label": "Auto Material Request" - }, + }, { - "doctype": "DocField", - "fieldname": "auto_indent", - "fieldtype": "Check", + "doctype": "DocField", + "fieldname": "auto_indent", + "fieldtype": "Check", "label": "Raise Material Request when stock reaches re-order level" - }, + }, { - "doctype": "DocField", - "fieldname": "reorder_email_notify", - "fieldtype": "Check", + "doctype": "DocField", + "fieldname": "reorder_email_notify", + "fieldtype": "Check", "label": "Notify by Email on creation of automatic Material Request" - }, + }, { - "doctype": "DocField", - "fieldname": "freeze_stock_entries", - "fieldtype": "Section Break", + "doctype": "DocField", + "fieldname": "freeze_stock_entries", + "fieldtype": "Section Break", "label": "Freeze Stock Entries" - }, + }, { - "doctype": "DocField", - "fieldname": "stock_frozen_upto", - "fieldtype": "Date", + "doctype": "DocField", + "fieldname": "stock_frozen_upto", + "fieldtype": "Date", "label": "Stock Frozen Upto" - }, + }, { - "doctype": "DocField", - "fieldname": "stock_auth_role", - "fieldtype": "Link", - "label": "Role Allowed to edit frozen stock", + "doctype": "DocField", + "fieldname": "stock_frozen_upto", + "fieldtype": "Int", + "label": "Stock Frozen Upto" + }, + { + "doctype": "DocField", + "fieldname": "stock_auth_role", + "fieldtype": "Link", + "label": "Role Allowed to edit frozen stock", "options": "Role" - }, + }, { "doctype": "DocPerm" } -] \ No newline at end of file +] From b3d26c08f609f22ca197fd815ccfe0f0f9749b24 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 27 Jan 2014 14:58:55 +0530 Subject: [PATCH 05/31] Fixed planned qty bug and patch to recalculate planned qty --- patches/patch_list.py | 1 + stock/doctype/stock_entry/stock_entry.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/patches/patch_list.py b/patches/patch_list.py index d87d3ad36ff..30617205c8e 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -268,4 +268,5 @@ patch_list = [ "patches.1401.update_billing_status_for_zero_value_order", "patches.1401.enable_all_price_list", "patches.1401.fix_serial_no_status_and_warehouse", + "patches.1401.fix_planned_qty", ] \ No newline at end of file diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py index 7dec8785c4d..c322a1ac45a 100644 --- a/stock/doctype/stock_entry/stock_entry.py +++ b/stock/doctype/stock_entry/stock_entry.py @@ -346,7 +346,8 @@ class DocType(StockController): pro_bean = webnotes.bean("Production Order", self.doc.production_order) _validate_production_order(pro_bean) self.update_produced_qty(pro_bean) - self.update_planned_qty(pro_bean) + if self.doc.purpose == "Manufacture/Repack": + self.update_planned_qty(pro_bean) def update_produced_qty(self, pro_bean): if self.doc.purpose == "Manufacture/Repack": From 44a40b860ed54fc6808d6328a5e6c1acb5702664 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 27 Jan 2014 15:02:30 +0530 Subject: [PATCH 06/31] Fixed planned qty bug and patch to recalculate planned qty --- patches/1401/fix_planned_qty.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 patches/1401/fix_planned_qty.py diff --git a/patches/1401/fix_planned_qty.py b/patches/1401/fix_planned_qty.py new file mode 100644 index 00000000000..96f56fb88ac --- /dev/null +++ b/patches/1401/fix_planned_qty.py @@ -0,0 +1,10 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import webnotes + +def execute(): + from utilities.repost_stock import repost_stock + for d in webnotes.conn.sql("""select distinct production_item, fg_warehouse + from `tabProduction Order` where docstatus>0""", as_dict=1): + repost_stock(d.production_item, d.fg_warehouse) \ No newline at end of file From 9d5566634c100de645a5cab28a17072ad4bb1c06 Mon Sep 17 00:00:00 2001 From: Thura Hlaing Date: Mon, 27 Jan 2014 16:54:02 +0630 Subject: [PATCH 07/31] changed stock_frozen_upto to stock_frozen_upto_days, and added validation for it in stock ledger --- .../stock_ledger_entry/stock_ledger_entry.py | 49 ++++++++++++------- .../doctype/stock_settings/stock_settings.py | 4 +- .../doctype/stock_settings/stock_settings.txt | 6 +-- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index f059451099b..4828ca42bbf 100644 --- a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -1,3 +1,4 @@ + # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt @@ -6,6 +7,7 @@ import webnotes from webnotes import msgprint from webnotes.utils import flt, getdate from webnotes.model.controller import DocListController +from datetime import timedelta, date class DocType(DocListController): def __init__(self, doc, doclist=[]): @@ -19,30 +21,30 @@ class DocType(DocListController): validate_warehouse_user(self.doc.warehouse) validate_warehouse_company(self.doc.warehouse, self.doc.company) self.scrub_posting_time() - + from accounts.utils import validate_fiscal_year validate_fiscal_year(self.doc.posting_date, self.doc.fiscal_year, self.meta.get_label("posting_date")) - + def on_submit(self): self.check_stock_frozen_date() self.actual_amt_check() - + from stock.doctype.serial_no.serial_no import process_serial_no process_serial_no(self.doc) - + #check for item quantity available in stock def actual_amt_check(self): if self.doc.batch_no: - batch_bal_after_transaction = flt(webnotes.conn.sql("""select sum(actual_qty) - from `tabStock Ledger Entry` - where warehouse=%s and item_code=%s and batch_no=%s""", + batch_bal_after_transaction = flt(webnotes.conn.sql("""select sum(actual_qty) + from `tabStock Ledger Entry` + where warehouse=%s and item_code=%s and batch_no=%s""", (self.doc.warehouse, self.doc.item_code, self.doc.batch_no))[0][0]) - + if batch_bal_after_transaction < 0: self.doc.fields.update({ 'batch_bal': batch_bal_after_transaction - self.doc.actual_qty }) - + webnotes.throw("""Not enough quantity (requested: %(actual_qty)s, \ current: %(batch_bal)s in Batch %(batch_no)s for Item \ %(item_code)s at Warehouse %(warehouse)s \ @@ -60,27 +62,27 @@ class DocType(DocListController): msgprint("Warehouse: '%s' does not exist in the system. Please check." % self.doc.fields.get(k), raise_exception = 1) def validate_item(self): - item_det = webnotes.conn.sql("""select name, has_batch_no, docstatus, - is_stock_item, has_serial_no, serial_no_series - from tabItem where name=%s""", + item_det = webnotes.conn.sql("""select name, has_batch_no, docstatus, + is_stock_item, has_serial_no, serial_no_series + from tabItem where name=%s""", self.doc.item_code, as_dict=True)[0] if item_det.is_stock_item != 'Yes': webnotes.throw("""Item: "%s" is not a Stock Item.""" % self.doc.item_code) - + # check if batch number is required if item_det.has_batch_no =='Yes' and self.doc.voucher_type != 'Stock Reconciliation': if not self.doc.batch_no: webnotes.throw("Batch number is mandatory for Item '%s'" % self.doc.item_code) - + # check if batch belongs to item - if not webnotes.conn.sql("""select name from `tabBatch` + if not webnotes.conn.sql("""select name from `tabBatch` where item='%s' and name ='%s' and docstatus != 2""" % (self.doc.item_code, self.doc.batch_no)): webnotes.throw("'%s' is not a valid Batch Number for Item '%s'" % (self.doc.batch_no, self.doc.item_code)) - + if not self.doc.stock_uom: self.doc.stock_uom = item_det.stock_uom - + def check_stock_frozen_date(self): stock_frozen_upto = webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto') or '' if stock_frozen_upto: @@ -88,13 +90,22 @@ class DocType(DocListController): if getdate(self.doc.posting_date) <= getdate(stock_frozen_upto) and not stock_auth_role in webnotes.user.get_roles(): msgprint("You are not authorized to do / modify back dated stock entries before %s" % getdate(stock_frozen_upto).strftime('%d-%m-%Y'), raise_exception=1) + stock_frozen_upto_days = webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto_days') or 0 + if stock_frozen_upto_days: + stock_auth_role = webnotes.conn.get_value('Stock Settings', None,'stock_auth_role') + posting_date = getdate(self.doc.posting_date) + frozen_days = timedelta(days=stock_frozen_upto_days) + if posting_date + frozen_days <= date.today() and not stock_auth_role in webnotes.user.get_roles(): + msgprint("You are not authorized to do / modify back dated stock entries %d ago" %stock_frozen_upto_days, raise_exception=1) + + def scrub_posting_time(self): if not self.doc.posting_time or self.doc.posting_time == '00:0': self.doc.posting_time = '00:00' def on_doctype_update(): - if not webnotes.conn.sql("""show index from `tabStock Ledger Entry` + if not webnotes.conn.sql("""show index from `tabStock Ledger Entry` where Key_name="posting_sort_index" """): webnotes.conn.commit() - webnotes.conn.sql("""alter table `tabStock Ledger Entry` + webnotes.conn.sql("""alter table `tabStock Ledger Entry` add index posting_sort_index(posting_date, posting_time, name)""") \ No newline at end of file diff --git a/stock/doctype/stock_settings/stock_settings.py b/stock/doctype/stock_settings/stock_settings.py index a98ed407e7b..a061aaa4935 100644 --- a/stock/doctype/stock_settings/stock_settings.py +++ b/stock/doctype/stock_settings/stock_settings.py @@ -21,7 +21,7 @@ class DocType: self.doc.get("item_naming_by")=="Naming Series", hide_name_field=True) stock_frozen_limit = 356 - submitted_stock_frozen = self.doc.fields.get("stock_frozen_upto") + submitted_stock_frozen = self.doc.fields.get("stock_frozen_upto_days") if submitted_stock_frozen > stock_frozen_limit: - self.doc.fields["stock_frozen_upto"] = stock_frozen_limit + self.doc.fields["stock_frozen_upto_days"] = stock_frozen_limit webnotes.msgprint (_("Stocks cannot be freezed for days larger than %d.") %stock_frozen_limit) diff --git a/stock/doctype/stock_settings/stock_settings.txt b/stock/doctype/stock_settings/stock_settings.txt index 59710ffc7c6..ca1229619bf 100644 --- a/stock/doctype/stock_settings/stock_settings.txt +++ b/stock/doctype/stock_settings/stock_settings.txt @@ -2,7 +2,7 @@ { "creation": "2013-06-24 16:37:54", "docstatus": 0, - "modified": "2014-01-27 13:29:56", + "modified": "2014-01-27 17:29:56", "modified_by": "Administrator", "owner": "Administrator" }, @@ -117,9 +117,9 @@ }, { "doctype": "DocField", - "fieldname": "stock_frozen_upto", + "fieldname": "stock_frozen_upto_days", "fieldtype": "Int", - "label": "Stock Frozen Upto" + "label": "Stock Frozen Upto [Days Ago]" }, { "doctype": "DocField", From 8821541f33de980d6e42780e42eb15a666b0075b Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Mon, 27 Jan 2014 17:49:45 +0600 Subject: [PATCH 08/31] bumped to version 3.7.0 --- config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index f71ed9b5724..91c1624e137 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,6 @@ { "app_name": "ERPNext", - "app_version": "3.6.6", + "app_version": "3.7.0", "base_template": "app/portal/templates/base.html", "modules": { "Accounts": { @@ -74,5 +74,5 @@ "type": "module" } }, - "requires_framework_version": "==3.7.5" + "requires_framework_version": "==3.8.0" } \ No newline at end of file From 191935bbf59556fd91d3a2041c02b8f33e94e8d3 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 27 Jan 2014 17:37:06 +0530 Subject: [PATCH 09/31] Planned qty patch: auto commit on many writes --- patches/1401/fix_planned_qty.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/patches/1401/fix_planned_qty.py b/patches/1401/fix_planned_qty.py index 96f56fb88ac..979d94985b6 100644 --- a/patches/1401/fix_planned_qty.py +++ b/patches/1401/fix_planned_qty.py @@ -4,7 +4,10 @@ import webnotes def execute(): + webnotes.conn.auto_commit_on_many_writes = 1 from utilities.repost_stock import repost_stock for d in webnotes.conn.sql("""select distinct production_item, fg_warehouse from `tabProduction Order` where docstatus>0""", as_dict=1): - repost_stock(d.production_item, d.fg_warehouse) \ No newline at end of file + repost_stock(d.production_item, d.fg_warehouse) + + webnotes.conn.auto_commit_on_many_writes = 0 \ No newline at end of file From dc540dda4d29df82928c48c946e103e93df17a4b Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Mon, 27 Jan 2014 18:19:50 +0600 Subject: [PATCH 10/31] bumped to version 3.7.1 --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 91c1624e137..cd3e91bda88 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,6 @@ { "app_name": "ERPNext", - "app_version": "3.7.0", + "app_version": "3.7.1", "base_template": "app/portal/templates/base.html", "modules": { "Accounts": { From e31a41854b175fa47eedc257d80d5d784a8cb39a Mon Sep 17 00:00:00 2001 From: Thura Hlaing Date: Mon, 27 Jan 2014 20:14:53 +0630 Subject: [PATCH 11/31] tabify modifications, minor coding style/message updates --- .../stock_ledger_entry/stock_ledger_entry.py | 13 ++++++------- stock/doctype/stock_settings/stock_settings.py | 10 +++++----- stock/doctype/stock_settings/stock_settings.txt | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 4828ca42bbf..d9a8c8c06fc 100644 --- a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -90,13 +90,12 @@ class DocType(DocListController): if getdate(self.doc.posting_date) <= getdate(stock_frozen_upto) and not stock_auth_role in webnotes.user.get_roles(): msgprint("You are not authorized to do / modify back dated stock entries before %s" % getdate(stock_frozen_upto).strftime('%d-%m-%Y'), raise_exception=1) - stock_frozen_upto_days = webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto_days') or 0 - if stock_frozen_upto_days: - stock_auth_role = webnotes.conn.get_value('Stock Settings', None,'stock_auth_role') - posting_date = getdate(self.doc.posting_date) - frozen_days = timedelta(days=stock_frozen_upto_days) - if posting_date + frozen_days <= date.today() and not stock_auth_role in webnotes.user.get_roles(): - msgprint("You are not authorized to do / modify back dated stock entries %d ago" %stock_frozen_upto_days, raise_exception=1) + stock_frozen_upto_days = webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto_days') or 0 + if stock_frozen_upto_days: + stock_auth_role = webnotes.conn.get_value('Stock Settings', None,'stock_auth_role') + older_than_x_days_ago = (add_date(getdate(self.doc.posting_date), stock_frozen_upto_days) <= date.today()) + if older_than_x_days_ago and not stock_auth_role in webnotes.user.get_roles(): + msgprint("You are not authorized to do / modify back dated stock entries older than %d days ago" %stock_frozen_upto_days, raise_exception=1) def scrub_posting_time(self): diff --git a/stock/doctype/stock_settings/stock_settings.py b/stock/doctype/stock_settings/stock_settings.py index a061aaa4935..8592503147f 100644 --- a/stock/doctype/stock_settings/stock_settings.py +++ b/stock/doctype/stock_settings/stock_settings.py @@ -20,8 +20,8 @@ class DocType: set_by_naming_series("Item", "item_code", self.doc.get("item_naming_by")=="Naming Series", hide_name_field=True) - stock_frozen_limit = 356 - submitted_stock_frozen = self.doc.fields.get("stock_frozen_upto_days") - if submitted_stock_frozen > stock_frozen_limit: - self.doc.fields["stock_frozen_upto_days"] = stock_frozen_limit - webnotes.msgprint (_("Stocks cannot be freezed for days larger than %d.") %stock_frozen_limit) + stock_frozen_limit = 356 + submitted_stock_frozen = self.doc.stock_frozen_upto_days + if submitted_stock_frozen > stock_frozen_limit: + self.doc.stock_frozen_upto_days = stock_frozen_limit + webnotes.msgprint (_("`Stocks Freeze Older Than` should be smaller than %d days.") %stock_frozen_limit) diff --git a/stock/doctype/stock_settings/stock_settings.txt b/stock/doctype/stock_settings/stock_settings.txt index ca1229619bf..a1790916168 100644 --- a/stock/doctype/stock_settings/stock_settings.txt +++ b/stock/doctype/stock_settings/stock_settings.txt @@ -2,7 +2,7 @@ { "creation": "2013-06-24 16:37:54", "docstatus": 0, - "modified": "2014-01-27 17:29:56", + "modified": "2014-01-27 20:00:56", "modified_by": "Administrator", "owner": "Administrator" }, @@ -119,7 +119,7 @@ "doctype": "DocField", "fieldname": "stock_frozen_upto_days", "fieldtype": "Int", - "label": "Stock Frozen Upto [Days Ago]" + "label": "Freeze Stocks Older Than [Days]" }, { "doctype": "DocField", From 16f88ba3cd560881e2871d237d570a66aaa6cf38 Mon Sep 17 00:00:00 2001 From: Thura Hlaing Date: Mon, 27 Jan 2014 20:22:08 +0630 Subject: [PATCH 12/31] import add_date, removed timedelta import --- stock/doctype/stock_ledger_entry/stock_ledger_entry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index d9a8c8c06fc..13aaee6b4b2 100644 --- a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -5,9 +5,9 @@ from __future__ import unicode_literals import webnotes from webnotes import msgprint -from webnotes.utils import flt, getdate +from webnotes.utils import flt, getdate, add_date from webnotes.model.controller import DocListController -from datetime import timedelta, date +from datetime import date class DocType(DocListController): def __init__(self, doc, doclist=[]): From 2e67426936c63ef8eaa4256eadcf0c21f1fdac9e Mon Sep 17 00:00:00 2001 From: Thura Hlaing Date: Tue, 28 Jan 2014 09:09:50 +0630 Subject: [PATCH 13/31] label/message update in error reporting --- stock/doctype/stock_settings/stock_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stock/doctype/stock_settings/stock_settings.py b/stock/doctype/stock_settings/stock_settings.py index 8592503147f..87e940d1d56 100644 --- a/stock/doctype/stock_settings/stock_settings.py +++ b/stock/doctype/stock_settings/stock_settings.py @@ -24,4 +24,4 @@ class DocType: submitted_stock_frozen = self.doc.stock_frozen_upto_days if submitted_stock_frozen > stock_frozen_limit: self.doc.stock_frozen_upto_days = stock_frozen_limit - webnotes.msgprint (_("`Stocks Freeze Older Than` should be smaller than %d days.") %stock_frozen_limit) + webnotes.msgprint (_("`Freeze Stocks Older Than` should be smaller than %d days.") %stock_frozen_limit) From a570cd66f771f4f77002d8cd12187edccf4460dc Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Tue, 28 Jan 2014 12:31:12 +0530 Subject: [PATCH 14/31] webnotes/erpnext#1362 allow renaming of sales and purchase taxes and charges master --- .../purchase_taxes_and_charges_master.txt | 3 ++- .../sales_taxes_and_charges_master.txt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.txt b/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.txt index f902ade3d13..dc946338979 100644 --- a/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.txt +++ b/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.txt @@ -2,11 +2,12 @@ { "creation": "2013-01-10 16:34:08", "docstatus": 0, - "modified": "2013-07-22 15:22:25", + "modified": "2014-01-28 12:28:56", "modified_by": "Administrator", "owner": "wasim@webnotestech.com" }, { + "allow_rename": 1, "autoname": "field:title", "description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.", "doctype": "DocType", diff --git a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.txt b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.txt index cddf10ec850..d581f10647b 100644 --- a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.txt +++ b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.txt @@ -2,11 +2,12 @@ { "creation": "2013-01-10 16:34:09", "docstatus": 0, - "modified": "2013-10-31 19:25:09", + "modified": "2014-01-28 12:28:27", "modified_by": "Administrator", "owner": "Administrator" }, { + "allow_rename": 1, "autoname": "field:title", "description": "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.", "doctype": "DocType", From 7c1976950d383b46b67c58f72235034bd9d02bf2 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Tue, 28 Jan 2014 13:03:47 +0530 Subject: [PATCH 15/31] webnotes/erpnext#912 stay updated button fixed in website --- portal/templates/includes/footer.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/portal/templates/includes/footer.html b/portal/templates/includes/footer.html index cd75fd16f63..da58ed04a52 100644 --- a/portal/templates/includes/footer.html +++ b/portal/templates/includes/footer.html @@ -16,11 +16,10 @@ {% endblock %} From d66396abe347be0020f24904b50f9615ef819719 Mon Sep 17 00:00:00 2001 From: Thura Hlaing Date: Wed, 29 Jan 2014 09:49:30 +0630 Subject: [PATCH 16/31] fixed typo add_date -> add_days --- stock/doctype/stock_ledger_entry/stock_ledger_entry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 13aaee6b4b2..d5d4b88a6bc 100644 --- a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import webnotes from webnotes import msgprint -from webnotes.utils import flt, getdate, add_date +from webnotes.utils import flt, getdate, add_days from webnotes.model.controller import DocListController from datetime import date @@ -93,7 +93,7 @@ class DocType(DocListController): stock_frozen_upto_days = webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto_days') or 0 if stock_frozen_upto_days: stock_auth_role = webnotes.conn.get_value('Stock Settings', None,'stock_auth_role') - older_than_x_days_ago = (add_date(getdate(self.doc.posting_date), stock_frozen_upto_days) <= date.today()) + older_than_x_days_ago = (add_days(getdate(self.doc.posting_date), stock_frozen_upto_days) <= date.today()) if older_than_x_days_ago and not stock_auth_role in webnotes.user.get_roles(): msgprint("You are not authorized to do / modify back dated stock entries older than %d days ago" %stock_frozen_upto_days, raise_exception=1) From 258191ab40ffbfa99c17b938ac7c854cf3c0066d Mon Sep 17 00:00:00 2001 From: Thura Hlaing Date: Wed, 29 Jan 2014 11:37:30 +0630 Subject: [PATCH 17/31] fix bug by coercing `stock_frozen_upto_days` value to int --- stock/doctype/stock_ledger_entry/stock_ledger_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index d5d4b88a6bc..38d5b266051 100644 --- a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -90,7 +90,7 @@ class DocType(DocListController): if getdate(self.doc.posting_date) <= getdate(stock_frozen_upto) and not stock_auth_role in webnotes.user.get_roles(): msgprint("You are not authorized to do / modify back dated stock entries before %s" % getdate(stock_frozen_upto).strftime('%d-%m-%Y'), raise_exception=1) - stock_frozen_upto_days = webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto_days') or 0 + stock_frozen_upto_days = int(webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto_days') or 0) if stock_frozen_upto_days: stock_auth_role = webnotes.conn.get_value('Stock Settings', None,'stock_auth_role') older_than_x_days_ago = (add_days(getdate(self.doc.posting_date), stock_frozen_upto_days) <= date.today()) From 3c4bb0c7c96f6ff827508c053f4c65d410e91646 Mon Sep 17 00:00:00 2001 From: Thura Hlaing Date: Wed, 29 Jan 2014 13:28:11 +0630 Subject: [PATCH 18/31] initial tests for freeze stock functionality --- stock/doctype/stock_entry/test_stock_entry.py | 502 +++++++++--------- .../stock_ledger_entry/stock_ledger_entry.py | 4 +- 2 files changed, 261 insertions(+), 245 deletions(-) diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py index d6f34f645a9..78ca0198dbe 100644 --- a/stock/doctype/stock_entry/test_stock_entry.py +++ b/stock/doctype/stock_entry/test_stock_entry.py @@ -18,7 +18,7 @@ class TestStockEntry(unittest.TestCase): webnotes.conn.sql("""delete from `tabMaterial Request Item`""") webnotes.conn.sql("""delete from `tabMaterial Request`""") self._clear_stock_account_balance() - + webnotes.conn.set_value("Stock Settings", None, "auto_indent", True) st1 = webnotes.bean(copy=test_records[0]) @@ -28,15 +28,15 @@ class TestStockEntry(unittest.TestCase): st2 = webnotes.bean(copy=test_records[1]) st2.insert() st2.submit() - + from stock.utils import reorder_item reorder_item() - + mr_name = webnotes.conn.sql("""select parent from `tabMaterial Request Item` where item_code='_Test Item'""") - + self.assertTrue(mr_name) - + webnotes.conn.set_default("company", self.old_default_company) def test_warehouse_company_validation(self): @@ -51,7 +51,7 @@ class TestStockEntry(unittest.TestCase): st1.doclist[1].t_warehouse="_Test Warehouse 2 - _TC1" st1.insert() self.assertRaises(InvalidWarehouseCompany, st1.submit) - + webnotes.session.user = "Administrator" def test_warehouse_user(self): @@ -76,111 +76,111 @@ class TestStockEntry(unittest.TestCase): st1.doclist[1].t_warehouse="_Test Warehouse 2 - _TC1" st1.insert() st1.submit() - + webnotes.session.user = "Administrator" def test_material_receipt_gl_entry(self): self._clear_stock_account_balance() set_perpetual_inventory() - + mr = webnotes.bean(copy=test_records[0]) mr.insert() mr.submit() - - stock_in_hand_account = webnotes.conn.get_value("Account", {"account_type": "Warehouse", + + stock_in_hand_account = webnotes.conn.get_value("Account", {"account_type": "Warehouse", "master_name": mr.doclist[1].t_warehouse}) - - self.check_stock_ledger_entries("Stock Entry", mr.doc.name, + + self.check_stock_ledger_entries("Stock Entry", mr.doc.name, [["_Test Item", "_Test Warehouse - _TC", 50.0]]) - - self.check_gl_entries("Stock Entry", mr.doc.name, + + self.check_gl_entries("Stock Entry", mr.doc.name, sorted([ - [stock_in_hand_account, 5000.0, 0.0], + [stock_in_hand_account, 5000.0, 0.0], ["Stock Adjustment - _TC", 0.0, 5000.0] ]) ) - + mr.cancel() - - self.assertFalse(webnotes.conn.sql("""select * from `tabStock Ledger Entry` - where voucher_type='Stock Entry' and voucher_no=%s""", mr.doc.name)) - - self.assertFalse(webnotes.conn.sql("""select * from `tabGL Entry` + + self.assertFalse(webnotes.conn.sql("""select * from `tabStock Ledger Entry` where voucher_type='Stock Entry' and voucher_no=%s""", mr.doc.name)) - + + self.assertFalse(webnotes.conn.sql("""select * from `tabGL Entry` + where voucher_type='Stock Entry' and voucher_no=%s""", mr.doc.name)) + def test_material_issue_gl_entry(self): self._clear_stock_account_balance() set_perpetual_inventory() - + self._insert_material_receipt() - + mi = webnotes.bean(copy=test_records[1]) mi.insert() mi.submit() - - self.check_stock_ledger_entries("Stock Entry", mi.doc.name, + + self.check_stock_ledger_entries("Stock Entry", mi.doc.name, [["_Test Item", "_Test Warehouse - _TC", -40.0]]) - - stock_in_hand_account = webnotes.conn.get_value("Account", {"account_type": "Warehouse", + + stock_in_hand_account = webnotes.conn.get_value("Account", {"account_type": "Warehouse", "master_name": mi.doclist[1].s_warehouse}) - self.check_gl_entries("Stock Entry", mi.doc.name, + self.check_gl_entries("Stock Entry", mi.doc.name, sorted([ - [stock_in_hand_account, 0.0, 4000.0], + [stock_in_hand_account, 0.0, 4000.0], ["Stock Adjustment - _TC", 4000.0, 0.0] ]) ) - + mi.cancel() - self.assertFalse(webnotes.conn.sql("""select * from `tabStock Ledger Entry` - where voucher_type='Stock Entry' and voucher_no=%s""", mi.doc.name)) - - self.assertFalse(webnotes.conn.sql("""select * from `tabGL Entry` + self.assertFalse(webnotes.conn.sql("""select * from `tabStock Ledger Entry` where voucher_type='Stock Entry' and voucher_no=%s""", mi.doc.name)) - - self.assertEquals(webnotes.conn.get_value("Bin", {"warehouse": mi.doclist[1].s_warehouse, + + self.assertFalse(webnotes.conn.sql("""select * from `tabGL Entry` + where voucher_type='Stock Entry' and voucher_no=%s""", mi.doc.name)) + + self.assertEquals(webnotes.conn.get_value("Bin", {"warehouse": mi.doclist[1].s_warehouse, "item_code": mi.doclist[1].item_code}, "actual_qty"), 50) - - self.assertEquals(webnotes.conn.get_value("Bin", {"warehouse": mi.doclist[1].s_warehouse, + + self.assertEquals(webnotes.conn.get_value("Bin", {"warehouse": mi.doclist[1].s_warehouse, "item_code": mi.doclist[1].item_code}, "stock_value"), 5000) - + def test_material_transfer_gl_entry(self): self._clear_stock_account_balance() set_perpetual_inventory() self._insert_material_receipt() - + mtn = webnotes.bean(copy=test_records[2]) mtn.insert() mtn.submit() - self.check_stock_ledger_entries("Stock Entry", mtn.doc.name, + self.check_stock_ledger_entries("Stock Entry", mtn.doc.name, [["_Test Item", "_Test Warehouse - _TC", -45.0], ["_Test Item", "_Test Warehouse 1 - _TC", 45.0]]) - stock_in_hand_account = webnotes.conn.get_value("Account", {"account_type": "Warehouse", + stock_in_hand_account = webnotes.conn.get_value("Account", {"account_type": "Warehouse", "master_name": mtn.doclist[1].s_warehouse}) - fixed_asset_account = webnotes.conn.get_value("Account", {"account_type": "Warehouse", + fixed_asset_account = webnotes.conn.get_value("Account", {"account_type": "Warehouse", "master_name": mtn.doclist[1].t_warehouse}) - - self.check_gl_entries("Stock Entry", mtn.doc.name, + + self.check_gl_entries("Stock Entry", mtn.doc.name, sorted([ - [stock_in_hand_account, 0.0, 4500.0], + [stock_in_hand_account, 0.0, 4500.0], [fixed_asset_account, 4500.0, 0.0], ]) ) - + mtn.cancel() - self.assertFalse(webnotes.conn.sql("""select * from `tabStock Ledger Entry` - where voucher_type='Stock Entry' and voucher_no=%s""", mtn.doc.name)) - - self.assertFalse(webnotes.conn.sql("""select * from `tabGL Entry` + self.assertFalse(webnotes.conn.sql("""select * from `tabStock Ledger Entry` where voucher_type='Stock Entry' and voucher_no=%s""", mtn.doc.name)) - - + + self.assertFalse(webnotes.conn.sql("""select * from `tabGL Entry` + where voucher_type='Stock Entry' and voucher_no=%s""", mtn.doc.name)) + + def test_repack_no_change_in_valuation(self): self._clear_stock_account_balance() set_perpetual_inventory() @@ -190,47 +190,47 @@ class TestStockEntry(unittest.TestCase): repack = webnotes.bean(copy=test_records[3]) repack.insert() repack.submit() - - self.check_stock_ledger_entries("Stock Entry", repack.doc.name, - [["_Test Item", "_Test Warehouse - _TC", -50.0], + + self.check_stock_ledger_entries("Stock Entry", repack.doc.name, + [["_Test Item", "_Test Warehouse - _TC", -50.0], ["_Test Item Home Desktop 100", "_Test Warehouse - _TC", 1]]) - + gl_entries = webnotes.conn.sql("""select account, debit, credit from `tabGL Entry` where voucher_type='Stock Entry' and voucher_no=%s order by account desc""", repack.doc.name, as_dict=1) self.assertFalse(gl_entries) - + set_perpetual_inventory(0) - + def test_repack_with_change_in_valuation(self): self._clear_stock_account_balance() set_perpetual_inventory() self._insert_material_receipt() - + repack = webnotes.bean(copy=test_records[3]) repack.doclist[2].incoming_rate = 6000 repack.insert() repack.submit() - - stock_in_hand_account = webnotes.conn.get_value("Account", {"account_type": "Warehouse", + + stock_in_hand_account = webnotes.conn.get_value("Account", {"account_type": "Warehouse", "master_name": repack.doclist[2].t_warehouse}) - - self.check_gl_entries("Stock Entry", repack.doc.name, + + self.check_gl_entries("Stock Entry", repack.doc.name, sorted([ - [stock_in_hand_account, 1000.0, 0.0], + [stock_in_hand_account, 1000.0, 0.0], ["Stock Adjustment - _TC", 0.0, 1000.0], ]) ) set_perpetual_inventory(0) - + def check_stock_ledger_entries(self, voucher_type, voucher_no, expected_sle): expected_sle.sort(key=lambda x: x[0]) - + # check stock ledger entries - sle = webnotes.conn.sql("""select item_code, warehouse, actual_qty - from `tabStock Ledger Entry` where voucher_type = %s - and voucher_no = %s order by item_code, warehouse, actual_qty""", + sle = webnotes.conn.sql("""select item_code, warehouse, actual_qty + from `tabStock Ledger Entry` where voucher_type = %s + and voucher_no = %s order by item_code, warehouse, actual_qty""", (voucher_type, voucher_no), as_list=1) self.assertTrue(sle) sle.sort(key=lambda x: x[0]) @@ -239,61 +239,61 @@ class TestStockEntry(unittest.TestCase): self.assertEquals(expected_sle[i][0], sle[0]) self.assertEquals(expected_sle[i][1], sle[1]) self.assertEquals(expected_sle[i][2], sle[2]) - + def check_gl_entries(self, voucher_type, voucher_no, expected_gl_entries): expected_gl_entries.sort(key=lambda x: x[0]) - + gl_entries = webnotes.conn.sql("""select account, debit, credit - from `tabGL Entry` where voucher_type=%s and voucher_no=%s + from `tabGL Entry` where voucher_type=%s and voucher_no=%s order by account asc, debit asc""", (voucher_type, voucher_no), as_list=1) self.assertTrue(gl_entries) gl_entries.sort(key=lambda x: x[0]) - + for i, gle in enumerate(gl_entries): self.assertEquals(expected_gl_entries[i][0], gle[0]) self.assertEquals(expected_gl_entries[i][1], gle[1]) self.assertEquals(expected_gl_entries[i][2], gle[2]) - + def _insert_material_receipt(self): self._clear_stock_account_balance() se1 = webnotes.bean(copy=test_records[0]) se1.insert() se1.submit() - + se2 = webnotes.bean(copy=test_records[0]) se2.doclist[1].item_code = "_Test Item Home Desktop 100" se2.insert() se2.submit() - + webnotes.conn.set_default("company", self.old_default_company) - + def _get_actual_qty(self): - return flt(webnotes.conn.get_value("Bin", {"item_code": "_Test Item", + return flt(webnotes.conn.get_value("Bin", {"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"}, "actual_qty")) - + def _test_sales_invoice_return(self, item_code, delivered_qty, returned_qty): from stock.doctype.stock_entry.stock_entry import NotUpdateStockError - + from accounts.doctype.sales_invoice.test_sales_invoice \ import test_records as sales_invoice_test_records - + # invalid sales invoice as update stock not checked si = webnotes.bean(copy=sales_invoice_test_records[1]) si.insert() si.submit() - + se = webnotes.bean(copy=test_records[0]) se.doc.purpose = "Sales Return" se.doc.sales_invoice_no = si.doc.name se.doclist[1].qty = returned_qty se.doclist[1].transfer_qty = returned_qty self.assertRaises(NotUpdateStockError, se.insert) - + self._insert_material_receipt() - + # check currency available qty in bin actual_qty_0 = self._get_actual_qty() - + # insert a pos invoice with update stock si = webnotes.bean(copy=sales_invoice_test_records[1]) si.doc.is_pos = si.doc.update_stock = 1 @@ -302,12 +302,12 @@ class TestStockEntry(unittest.TestCase): si.doclist[1].qty = 5.0 si.insert() si.submit() - + # check available bin qty after invoice submission actual_qty_1 = self._get_actual_qty() - + self.assertEquals(actual_qty_0 - delivered_qty, actual_qty_1) - + # check if item is validated se = webnotes.bean(copy=test_records[0]) se.doc.purpose = "Sales Return" @@ -317,10 +317,10 @@ class TestStockEntry(unittest.TestCase): se.doclist[1].item_code = "_Test Item Home Desktop 200" se.doclist[1].qty = returned_qty se.doclist[1].transfer_qty = returned_qty - + # check if stock entry gets submitted self.assertRaises(webnotes.DoesNotExistError, se.insert) - + # try again se = webnotes.bean(copy=test_records[0]) se.doc.purpose = "Sales Return" @@ -331,45 +331,45 @@ class TestStockEntry(unittest.TestCase): se.doclist[1].transfer_qty = returned_qty # in both cases item code remains _Test Item when returning se.insert() - + se.submit() - + # check if available qty is increased actual_qty_2 = self._get_actual_qty() - + self.assertEquals(actual_qty_1 + returned_qty, actual_qty_2) - + return se - + def test_sales_invoice_return_of_non_packing_item(self): self._clear_stock_account_balance() self._test_sales_invoice_return("_Test Item", 5, 2) - + def test_sales_invoice_return_of_packing_item(self): self._clear_stock_account_balance() self._test_sales_invoice_return("_Test Sales BOM Item", 25, 20) - + def _test_delivery_note_return(self, item_code, delivered_qty, returned_qty): self._insert_material_receipt() - + from stock.doctype.delivery_note.test_delivery_note \ import test_records as delivery_note_test_records from stock.doctype.delivery_note.delivery_note import make_sales_invoice - + actual_qty_0 = self._get_actual_qty() # make a delivery note based on this invoice dn = webnotes.bean(copy=delivery_note_test_records[0]) dn.doclist[1].item_code = item_code dn.insert() dn.submit() - + actual_qty_1 = self._get_actual_qty() - + self.assertEquals(actual_qty_0 - delivered_qty, actual_qty_1) - + si_doclist = make_sales_invoice(dn.doc.name) - + si = webnotes.bean(si_doclist) si.doc.posting_date = dn.doc.posting_date si.doc.debit_to = "_Test Customer - _TC" @@ -378,7 +378,7 @@ class TestStockEntry(unittest.TestCase): d.cost_center = "_Test Cost Center - _TC" si.insert() si.submit() - + # insert and submit stock entry for sales return se = webnotes.bean(copy=test_records[0]) se.doc.purpose = "Sales Return" @@ -386,60 +386,60 @@ class TestStockEntry(unittest.TestCase): se.doc.posting_date = "2013-03-10" se.doc.fiscal_year = "_Test Fiscal Year 2013" se.doclist[1].qty = se.doclist[1].transfer_qty = returned_qty - + se.insert() se.submit() - + actual_qty_2 = self._get_actual_qty() self.assertEquals(actual_qty_1 + returned_qty, actual_qty_2) - + return se - + def test_delivery_note_return_of_non_packing_item(self): self._clear_stock_account_balance() self._test_delivery_note_return("_Test Item", 5, 2) - + def test_delivery_note_return_of_packing_item(self): self._clear_stock_account_balance() self._test_delivery_note_return("_Test Sales BOM Item", 25, 20) - + def _test_sales_return_jv(self, se): from stock.doctype.stock_entry.stock_entry import make_return_jv jv_list = make_return_jv(se.doc.name) - + self.assertEqual(len(jv_list), 3) self.assertEqual(jv_list[0].get("voucher_type"), "Credit Note") self.assertEqual(jv_list[0].get("posting_date"), se.doc.posting_date) self.assertEqual(jv_list[1].get("account"), "_Test Customer - _TC") self.assertEqual(jv_list[2].get("account"), "Sales - _TC") self.assertTrue(jv_list[1].get("against_invoice")) - + def test_make_return_jv_for_sales_invoice_non_packing_item(self): self._clear_stock_account_balance() se = self._test_sales_invoice_return("_Test Item", 5, 2) self._test_sales_return_jv(se) - + def test_make_return_jv_for_sales_invoice_packing_item(self): self._clear_stock_account_balance() se = self._test_sales_invoice_return("_Test Sales BOM Item", 25, 20) self._test_sales_return_jv(se) - + def test_make_return_jv_for_delivery_note_non_packing_item(self): self._clear_stock_account_balance() se = self._test_delivery_note_return("_Test Item", 5, 2) self._test_sales_return_jv(se) - + se = self._test_delivery_note_return_against_sales_order("_Test Item", 5, 2) self._test_sales_return_jv(se) - + def test_make_return_jv_for_delivery_note_packing_item(self): self._clear_stock_account_balance() se = self._test_delivery_note_return("_Test Sales BOM Item", 25, 20) self._test_sales_return_jv(se) - + se = self._test_delivery_note_return_against_sales_order("_Test Sales BOM Item", 25, 20) self._test_sales_return_jv(se) - + def _test_delivery_note_return_against_sales_order(self, item_code, delivered_qty, returned_qty): self._insert_material_receipt() @@ -447,13 +447,13 @@ class TestStockEntry(unittest.TestCase): from selling.doctype.sales_order.sales_order import make_sales_invoice, make_delivery_note actual_qty_0 = self._get_actual_qty() - + so = webnotes.bean(copy=sales_order_test_records[0]) so.doclist[1].item_code = item_code so.doclist[1].qty = 5.0 so.insert() so.submit() - + dn_doclist = make_delivery_note(so.doc.name) dn = webnotes.bean(dn_doclist) @@ -461,7 +461,7 @@ class TestStockEntry(unittest.TestCase): dn.doc.posting_date = so.doc.delivery_date dn.insert() dn.submit() - + actual_qty_1 = self._get_actual_qty() self.assertEquals(actual_qty_0 - delivered_qty, actual_qty_1) @@ -492,43 +492,43 @@ class TestStockEntry(unittest.TestCase): self.assertEquals(actual_qty_1 + returned_qty, actual_qty_2) return se - + def test_purchase_receipt_return(self): self._clear_stock_account_balance() - + actual_qty_0 = self._get_actual_qty() - + from stock.doctype.purchase_receipt.test_purchase_receipt \ import test_records as purchase_receipt_test_records from stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice - + # submit purchase receipt pr = webnotes.bean(copy=purchase_receipt_test_records[0]) pr.insert() pr.submit() - + actual_qty_1 = self._get_actual_qty() - + self.assertEquals(actual_qty_0 + 5, actual_qty_1) - + pi_doclist = make_purchase_invoice(pr.doc.name) - + pi = webnotes.bean(pi_doclist) pi.doc.posting_date = pr.doc.posting_date pi.doc.credit_to = "_Test Supplier - _TC" for d in pi.doclist.get({"parentfield": "entries"}): d.expense_head = "_Test Account Cost for Goods Sold - _TC" d.cost_center = "_Test Cost Center - _TC" - + for d in pi.doclist.get({"parentfield": "purchase_tax_details"}): d.cost_center = "_Test Cost Center - _TC" - + pi.run_method("calculate_taxes_and_totals") pi.doc.bill_no = "NA" pi.insert() pi.submit() - + # submit purchase return se = webnotes.bean(copy=test_records[0]) se.doc.purpose = "Purchase Return" @@ -539,22 +539,22 @@ class TestStockEntry(unittest.TestCase): se.doclist[1].s_warehouse = "_Test Warehouse - _TC" se.insert() se.submit() - + actual_qty_2 = self._get_actual_qty() - + self.assertEquals(actual_qty_1 - 5, actual_qty_2) - + webnotes.conn.set_default("company", self.old_default_company) - + return se, pr.doc.name - + def test_over_stock_return(self): from stock.doctype.stock_entry.stock_entry import StockOverReturnError self._clear_stock_account_balance() - + # out of 10, 5 gets returned prev_se, pr_docname = self.test_purchase_receipt_return() - + # submit purchase return - return another 6 qtys so that exception is raised se = webnotes.bean(copy=test_records[0]) se.doc.purpose = "Purchase Return" @@ -563,20 +563,20 @@ class TestStockEntry(unittest.TestCase): se.doc.fiscal_year = "_Test Fiscal Year 2013" se.doclist[1].qty = se.doclist[1].transfer_qty = 6 se.doclist[1].s_warehouse = "_Test Warehouse - _TC" - + self.assertRaises(StockOverReturnError, se.insert) - + def _test_purchase_return_jv(self, se): from stock.doctype.stock_entry.stock_entry import make_return_jv jv_list = make_return_jv(se.doc.name) - + self.assertEqual(len(jv_list), 3) self.assertEqual(jv_list[0].get("voucher_type"), "Debit Note") self.assertEqual(jv_list[0].get("posting_date"), se.doc.posting_date) self.assertEqual(jv_list[1].get("account"), "_Test Supplier - _TC") self.assertEqual(jv_list[2].get("account"), "_Test Account Cost for Goods Sold - _TC") self.assertTrue(jv_list[1].get("against_voucher")) - + def test_make_return_jv_for_purchase_receipt(self): self._clear_stock_account_balance() se, pr_name = self.test_purchase_receipt_return() @@ -584,18 +584,18 @@ class TestStockEntry(unittest.TestCase): se, pr_name = self._test_purchase_return_return_against_purchase_order() self._test_purchase_return_jv(se) - + def _test_purchase_return_return_against_purchase_order(self): self._clear_stock_account_balance() - + actual_qty_0 = self._get_actual_qty() - + from buying.doctype.purchase_order.test_purchase_order \ import test_records as purchase_order_test_records - + from buying.doctype.purchase_order.purchase_order import \ make_purchase_receipt, make_purchase_invoice - + # submit purchase receipt po = webnotes.bean(copy=purchase_order_test_records[0]) po.doc.is_subcontracted = None @@ -603,20 +603,20 @@ class TestStockEntry(unittest.TestCase): po.doclist[1].import_rate = 50 po.insert() po.submit() - + pr_doclist = make_purchase_receipt(po.doc.name) - + pr = webnotes.bean(pr_doclist) pr.doc.posting_date = po.doc.transaction_date pr.insert() pr.submit() - + actual_qty_1 = self._get_actual_qty() - + self.assertEquals(actual_qty_0 + 10, actual_qty_1) - + pi_doclist = make_purchase_invoice(po.doc.name) - + pi = webnotes.bean(pi_doclist) pi.doc.posting_date = pr.doc.posting_date pi.doc.credit_to = "_Test Supplier - _TC" @@ -625,12 +625,12 @@ class TestStockEntry(unittest.TestCase): d.cost_center = "_Test Cost Center - _TC" for d in pi.doclist.get({"parentfield": "purchase_tax_details"}): d.cost_center = "_Test Cost Center - _TC" - + pi.run_method("calculate_taxes_and_totals") pi.doc.bill_no = "NA" pi.insert() pi.submit() - + # submit purchase return se = webnotes.bean(copy=test_records[0]) se.doc.purpose = "Purchase Return" @@ -641,23 +641,23 @@ class TestStockEntry(unittest.TestCase): se.doclist[1].s_warehouse = "_Test Warehouse - _TC" se.insert() se.submit() - + actual_qty_2 = self._get_actual_qty() - + self.assertEquals(actual_qty_1 - 5, actual_qty_2) - + webnotes.conn.set_default("company", self.old_default_company) - + return se, pr.doc.name - + def _clear_stock_account_balance(self): webnotes.conn.sql("delete from `tabStock Ledger Entry`") webnotes.conn.sql("""delete from `tabBin`""") webnotes.conn.sql("""delete from `tabGL Entry`""") - + self.old_default_company = webnotes.conn.get_default("company") webnotes.conn.set_default("company", "_Test Company") - + def test_serial_no_not_reqd(self): se = webnotes.bean(copy=test_records[0]) se.doclist[1].serial_no = "ABCD" @@ -689,7 +689,7 @@ class TestStockEntry(unittest.TestCase): se.doclist[1].transfer_qty = 2 se.insert() self.assertRaises(SerialNoQtyError, se.submit) - + def test_serial_no_transfer_in(self): self._clear_stock_account_balance() se = webnotes.bean(copy=test_records[0]) @@ -699,13 +699,13 @@ class TestStockEntry(unittest.TestCase): se.doclist[1].transfer_qty = 2 se.insert() se.submit() - + self.assertTrue(webnotes.conn.exists("Serial No", "ABCD")) self.assertTrue(webnotes.conn.exists("Serial No", "EFGH")) - + se.cancel() self.assertFalse(webnotes.conn.get_value("Serial No", "ABCD", "warehouse")) - + def test_serial_no_not_exists(self): self._clear_stock_account_balance() se = webnotes.bean(copy=test_records[0]) @@ -718,11 +718,11 @@ class TestStockEntry(unittest.TestCase): se.doclist[1].transfer_qty = 2 se.insert() self.assertRaises(SerialNoNotExistsError, se.submit) - + def test_serial_duplicate(self): self._clear_stock_account_balance() self.test_serial_by_series() - + se = webnotes.bean(copy=test_records[0]) se.doclist[1].item_code = "_Test Serialized Item With Series" se.doclist[1].qty = 1 @@ -730,22 +730,22 @@ class TestStockEntry(unittest.TestCase): se.doclist[1].transfer_qty = 1 se.insert() self.assertRaises(SerialNoDuplicateError, se.submit) - + def test_serial_by_series(self): self._clear_stock_account_balance() se = make_serialized_item() serial_nos = get_serial_nos(se.doclist[1].serial_no) - + self.assertTrue(webnotes.conn.exists("Serial No", serial_nos[0])) self.assertTrue(webnotes.conn.exists("Serial No", serial_nos[1])) - + return se def test_serial_item_error(self): self._clear_stock_account_balance() self.test_serial_by_series() - + se = webnotes.bean(copy=test_records[0]) se.doc.purpose = "Material Transfer" se.doclist[1].item_code = "_Test Serialized Item" @@ -761,7 +761,7 @@ class TestStockEntry(unittest.TestCase): self._clear_stock_account_balance() se = make_serialized_item() serial_no = get_serial_nos(se.doclist[1].serial_no)[0] - + se = webnotes.bean(copy=test_records[0]) se.doc.purpose = "Material Transfer" se.doclist[1].item_code = "_Test Serialized Item With Series" @@ -773,14 +773,14 @@ class TestStockEntry(unittest.TestCase): se.insert() se.submit() self.assertTrue(webnotes.conn.get_value("Serial No", serial_no, "warehouse"), "_Test Warehouse 1 - _TC") - + se.cancel() self.assertTrue(webnotes.conn.get_value("Serial No", serial_no, "warehouse"), "_Test Warehouse - _TC") def test_serial_warehouse_error(self): self._clear_stock_account_balance() make_serialized_item() - + se = webnotes.bean(copy=test_records[0]) se.doc.purpose = "Material Transfer" se.doclist[1].item_code = "_Test Serialized Item With Series" @@ -791,15 +791,31 @@ class TestStockEntry(unittest.TestCase): se.doclist[1].t_warehouse = "_Test Warehouse - _TC" se.insert() self.assertRaises(SerialNoWarehouseError, se.submit) - + def test_serial_cancel(self): self._clear_stock_account_balance() se = self.test_serial_by_series() se.cancel() - + serial_no = get_serial_nos(se.doclist[1].serial_no)[0] self.assertFalse(webnotes.conn.get_value("Serial No", serial_no, "warehouse")) + def test_freeze_stocks (self): + self._clear_stock_account_balance() + + # test freeze_stocks_upto + date_newer_than_test_records = add_days(getdate(test_records[0][0]['posting_date']), 5) + webnotes.conn.set_value("Stock Settings", None, "stock_frozen_upto", date_newer_than_test_records) + se = webnotes.bean(copy=test_records[0]).insert() + self.assertRaises (ValidationError, se.submit) + webnotes.conn.set_value("Stock Settings", None, "stock_frozen_upto", '') + + # test freeze_stocks_upto_days + webnotes.conn.set_value("Stock Settings", None, "stock_frozen_upto_days", 7) + se = webnotes.bean(copy=test_records[0]).insert() + self.assertRaises (ValidationError, se.submit) + webnotes.conn.set_value("Stock Settings", None, "stock_frozen_upto_days", 0) + def make_serialized_item(): se = webnotes.bean(copy=test_records[0]) se.doclist[1].item_code = "_Test Serialized Item With Series" @@ -812,70 +828,70 @@ def make_serialized_item(): test_records = [ [ { - "company": "_Test Company", - "doctype": "Stock Entry", - "posting_date": "2013-01-01", - "posting_time": "17:14:24", + "company": "_Test Company", + "doctype": "Stock Entry", + "posting_date": "2013-01-01", + "posting_time": "17:14:24", "purpose": "Material Receipt", - "fiscal_year": "_Test Fiscal Year 2013", - }, + "fiscal_year": "_Test Fiscal Year 2013", + }, { - "conversion_factor": 1.0, - "doctype": "Stock Entry Detail", - "item_code": "_Test Item", - "parentfield": "mtn_details", + "conversion_factor": 1.0, + "doctype": "Stock Entry Detail", + "item_code": "_Test Item", + "parentfield": "mtn_details", "incoming_rate": 100, - "qty": 50.0, - "stock_uom": "_Test UOM", - "transfer_qty": 50.0, + "qty": 50.0, + "stock_uom": "_Test UOM", + "transfer_qty": 50.0, "uom": "_Test UOM", "t_warehouse": "_Test Warehouse - _TC", "expense_account": "Stock Adjustment - _TC", "cost_center": "_Test Cost Center - _TC" - }, + }, ], [ { - "company": "_Test Company", - "doctype": "Stock Entry", - "posting_date": "2013-01-25", - "posting_time": "17:15", + "company": "_Test Company", + "doctype": "Stock Entry", + "posting_date": "2013-01-25", + "posting_time": "17:15", "purpose": "Material Issue", - "fiscal_year": "_Test Fiscal Year 2013", - }, + "fiscal_year": "_Test Fiscal Year 2013", + }, { - "conversion_factor": 1.0, - "doctype": "Stock Entry Detail", - "item_code": "_Test Item", - "parentfield": "mtn_details", + "conversion_factor": 1.0, + "doctype": "Stock Entry Detail", + "item_code": "_Test Item", + "parentfield": "mtn_details", "incoming_rate": 100, - "qty": 40.0, - "stock_uom": "_Test UOM", - "transfer_qty": 40.0, + "qty": 40.0, + "stock_uom": "_Test UOM", + "transfer_qty": 40.0, "uom": "_Test UOM", "s_warehouse": "_Test Warehouse - _TC", "expense_account": "Stock Adjustment - _TC", "cost_center": "_Test Cost Center - _TC" - }, + }, ], [ { - "company": "_Test Company", - "doctype": "Stock Entry", - "posting_date": "2013-01-25", - "posting_time": "17:14:24", + "company": "_Test Company", + "doctype": "Stock Entry", + "posting_date": "2013-01-25", + "posting_time": "17:14:24", "purpose": "Material Transfer", - "fiscal_year": "_Test Fiscal Year 2013", - }, + "fiscal_year": "_Test Fiscal Year 2013", + }, { - "conversion_factor": 1.0, - "doctype": "Stock Entry Detail", - "item_code": "_Test Item", - "parentfield": "mtn_details", + "conversion_factor": 1.0, + "doctype": "Stock Entry Detail", + "item_code": "_Test Item", + "parentfield": "mtn_details", "incoming_rate": 100, - "qty": 45.0, - "stock_uom": "_Test UOM", - "transfer_qty": 45.0, + "qty": 45.0, + "stock_uom": "_Test UOM", + "transfer_qty": 45.0, "uom": "_Test UOM", "s_warehouse": "_Test Warehouse - _TC", "t_warehouse": "_Test Warehouse 1 - _TC", @@ -885,40 +901,40 @@ test_records = [ ], [ { - "company": "_Test Company", - "doctype": "Stock Entry", - "posting_date": "2013-01-25", - "posting_time": "17:14:24", + "company": "_Test Company", + "doctype": "Stock Entry", + "posting_date": "2013-01-25", + "posting_time": "17:14:24", "purpose": "Manufacture/Repack", - "fiscal_year": "_Test Fiscal Year 2013", - }, + "fiscal_year": "_Test Fiscal Year 2013", + }, { - "conversion_factor": 1.0, - "doctype": "Stock Entry Detail", - "item_code": "_Test Item", - "parentfield": "mtn_details", + "conversion_factor": 1.0, + "doctype": "Stock Entry Detail", + "item_code": "_Test Item", + "parentfield": "mtn_details", "incoming_rate": 100, - "qty": 50.0, - "stock_uom": "_Test UOM", - "transfer_qty": 50.0, + "qty": 50.0, + "stock_uom": "_Test UOM", + "transfer_qty": 50.0, "uom": "_Test UOM", "s_warehouse": "_Test Warehouse - _TC", "expense_account": "Stock Adjustment - _TC", "cost_center": "_Test Cost Center - _TC" - }, + }, { - "conversion_factor": 1.0, - "doctype": "Stock Entry Detail", - "item_code": "_Test Item Home Desktop 100", - "parentfield": "mtn_details", + "conversion_factor": 1.0, + "doctype": "Stock Entry Detail", + "item_code": "_Test Item Home Desktop 100", + "parentfield": "mtn_details", "incoming_rate": 5000, - "qty": 1, - "stock_uom": "_Test UOM", - "transfer_qty": 1, + "qty": 1, + "stock_uom": "_Test UOM", + "transfer_qty": 1, "uom": "_Test UOM", "t_warehouse": "_Test Warehouse - _TC", "expense_account": "Stock Adjustment - _TC", "cost_center": "_Test Cost Center - _TC" }, ], -] \ No newline at end of file +] diff --git a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 38d5b266051..232d8dff9d9 100644 --- a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -87,14 +87,14 @@ class DocType(DocListController): stock_frozen_upto = webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto') or '' if stock_frozen_upto: stock_auth_role = webnotes.conn.get_value('Stock Settings', None,'stock_auth_role') - if getdate(self.doc.posting_date) <= getdate(stock_frozen_upto) and not stock_auth_role in webnotes.user.get_roles(): + if getdate(self.doc.posting_date) <= getdate(stock_frozen_upto): # and not stock_auth_role in webnotes.user.get_roles(): msgprint("You are not authorized to do / modify back dated stock entries before %s" % getdate(stock_frozen_upto).strftime('%d-%m-%Y'), raise_exception=1) stock_frozen_upto_days = int(webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto_days') or 0) if stock_frozen_upto_days: stock_auth_role = webnotes.conn.get_value('Stock Settings', None,'stock_auth_role') older_than_x_days_ago = (add_days(getdate(self.doc.posting_date), stock_frozen_upto_days) <= date.today()) - if older_than_x_days_ago and not stock_auth_role in webnotes.user.get_roles(): + if older_than_x_days_ago: # and not stock_auth_role in webnotes.user.get_roles(): msgprint("You are not authorized to do / modify back dated stock entries older than %d days ago" %stock_frozen_upto_days, raise_exception=1) From 990d7c48627015694e9832bff3703aed34d78d14 Mon Sep 17 00:00:00 2001 From: Thura Hlaing Date: Wed, 29 Jan 2014 14:53:21 +0630 Subject: [PATCH 19/31] added StockFreezeError, and uncomment stock_auth_role check --- stock/doctype/stock_entry/test_stock_entry.py | 7 ++++--- stock/doctype/stock_ledger_entry/stock_ledger_entry.py | 10 ++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py index 78ca0198dbe..b88f1c9d18b 100644 --- a/stock/doctype/stock_entry/test_stock_entry.py +++ b/stock/doctype/stock_entry/test_stock_entry.py @@ -6,7 +6,7 @@ import webnotes, unittest from webnotes.utils import flt from stock.doctype.serial_no.serial_no import * from stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory - +from stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError class TestStockEntry(unittest.TestCase): def tearDown(self): @@ -802,18 +802,19 @@ class TestStockEntry(unittest.TestCase): def test_freeze_stocks (self): self._clear_stock_account_balance() + stock_auth_role = webnotes.conn.set_value('Stock Settings', None,'stock_auth_role', '') # test freeze_stocks_upto date_newer_than_test_records = add_days(getdate(test_records[0][0]['posting_date']), 5) webnotes.conn.set_value("Stock Settings", None, "stock_frozen_upto", date_newer_than_test_records) se = webnotes.bean(copy=test_records[0]).insert() - self.assertRaises (ValidationError, se.submit) + self.assertRaises (StockFreezeError, se.submit) webnotes.conn.set_value("Stock Settings", None, "stock_frozen_upto", '') # test freeze_stocks_upto_days webnotes.conn.set_value("Stock Settings", None, "stock_frozen_upto_days", 7) se = webnotes.bean(copy=test_records[0]).insert() - self.assertRaises (ValidationError, se.submit) + self.assertRaises (StockFreezeError, se.submit) webnotes.conn.set_value("Stock Settings", None, "stock_frozen_upto_days", 0) def make_serialized_item(): diff --git a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 232d8dff9d9..277992bcd99 100644 --- a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -9,6 +9,8 @@ from webnotes.utils import flt, getdate, add_days from webnotes.model.controller import DocListController from datetime import date +class StockFreezeError(webnotes.ValidationError): pass + class DocType(DocListController): def __init__(self, doc, doclist=[]): self.doc = doc @@ -87,15 +89,15 @@ class DocType(DocListController): stock_frozen_upto = webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto') or '' if stock_frozen_upto: stock_auth_role = webnotes.conn.get_value('Stock Settings', None,'stock_auth_role') - if getdate(self.doc.posting_date) <= getdate(stock_frozen_upto): # and not stock_auth_role in webnotes.user.get_roles(): - msgprint("You are not authorized to do / modify back dated stock entries before %s" % getdate(stock_frozen_upto).strftime('%d-%m-%Y'), raise_exception=1) + if getdate(self.doc.posting_date) <= getdate(stock_frozen_upto) and not stock_auth_role in webnotes.user.get_roles(): + msgprint("You are not authorized to do / modify back dated stock entries before %s" % getdate(stock_frozen_upto).strftime('%d-%m-%Y'), raise_exception=StockFreezeError) stock_frozen_upto_days = int(webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto_days') or 0) if stock_frozen_upto_days: stock_auth_role = webnotes.conn.get_value('Stock Settings', None,'stock_auth_role') older_than_x_days_ago = (add_days(getdate(self.doc.posting_date), stock_frozen_upto_days) <= date.today()) - if older_than_x_days_ago: # and not stock_auth_role in webnotes.user.get_roles(): - msgprint("You are not authorized to do / modify back dated stock entries older than %d days ago" %stock_frozen_upto_days, raise_exception=1) + if older_than_x_days_ago and not stock_auth_role in webnotes.user.get_roles(): + msgprint("You are not authorized to do / modify back dated stock entries older than %d days ago" %stock_frozen_upto_days, raise_exception=StockFreezeError) def scrub_posting_time(self): From b87e9f2651c423c167fdf2e81c3bec6f98b3137f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 29 Jan 2014 19:51:36 +0530 Subject: [PATCH 20/31] Fixes in fetching party details --- .../sales_invoice/test_sales_invoice.py | 8 ++-- erpnext/public/js/utils/party.js | 37 +++++++++++-------- erpnext/selling/doctype/customer/customer.py | 7 ++-- erpnext/utilities/doctype/contact/contact.py | 3 +- 4 files changed, 31 insertions(+), 24 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 3092477e4f8..2ed9847c07f 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -756,16 +756,18 @@ class TestSalesInvoice(unittest.TestCase): def _test_recurring_invoice(self, base_si, first_and_last_day): from webnotes.utils import add_months, get_last_day - from erpnext.accounts.doctype.sales_invoice.sales_invoice import manage_recurring_invoices + from erpnext.accounts.doctype.sales_invoice.sales_invoice \ + import manage_recurring_invoices, get_next_date no_of_months = ({"Monthly": 1, "Quarterly": 3, "Yearly": 12})[base_si.doc.recurring_type] def _test(i): self.assertEquals(i+1, webnotes.conn.sql("""select count(*) from `tabSales Invoice` where recurring_id=%s and docstatus=1""", base_si.doc.recurring_id)[0][0]) - - next_date = add_months(base_si.doc.posting_date, no_of_months) + next_date = get_next_date(base_si.doc.posting_date, no_of_months, + base_si.doc.repeat_on_day_of_month) + manage_recurring_invoices(next_date=next_date, commit=False) recurred_invoices = webnotes.conn.sql("""select name from `tabSales Invoice` diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index c068ff24173..bda44cea7ea 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -48,24 +48,29 @@ erpnext.utils.get_address_display = function(frm, address_field) { address_field = "supplier_address"; } } - wn.call({ - method: "erpnext.utilities.doctype.address.address.get_address_display", - args: {address: frm.doc[address_field] }, - callback: function(r) { - if(r.message) - frm.set_value("address_display", r.message) - } - }) + if(frm.doc[address_field]) { + wn.call({ + method: "erpnext.utilities.doctype.address.address.get_address_display", + args: {address: frm.doc[address_field] }, + callback: function(r) { + if(r.message) + frm.set_value("address_display", r.message) + } + }) + } } erpnext.utils.get_contact_details = function(frm) { if(frm.updating_party_details) return; - wn.call({ - method: "erpnext.utilities.doctype.contact.contact.get_contact_details", - args: {address: frm.doc.contact_person }, - callback: function(r) { - if(r.message) - frm.set_value(r.message); - } - }) + + if(frm.doc[address_field]) { + wn.call({ + method: "erpnext.utilities.doctype.contact.contact.get_contact_details", + args: {contact: frm.doc.contact_person }, + callback: function(r) { + if(r.message) + frm.set_value(r.message); + } + }) + } } \ No newline at end of file diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index aa28a19d3dd..c182c947a1c 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -196,13 +196,14 @@ def get_customer_details(customer, price_list=None, currency=None): out[f] = customer.get("default_" + f) # price list - out.selling_price_list = webnotes.conn.get_defaults("selling_price_list", webnotes.session.user) + from webnotes.defaults import get_defaults_for + out.selling_price_list = get_defaults_for(webnotes.session.user).get(price_list) if isinstance(out.selling_price_list, list): out.selling_price_list = None out.selling_price_list = out.selling_price_list or customer.price_list \ - or webnotes.conn.get_value("Customer Group", customer.customer_group, "default_price_list") - or price_list + or webnotes.conn.get_value("Customer Group", + customer.customer_group, "default_price_list") or price_list if out.selling_price_list: out.price_list_currency = webnotes.conn.get_value("Price List", out.selling_price_list, "currency") diff --git a/erpnext/utilities/doctype/contact/contact.py b/erpnext/utilities/doctype/contact/contact.py index 9dcb30de331..2abd0dc5506 100644 --- a/erpnext/utilities/doctype/contact/contact.py +++ b/erpnext/utilities/doctype/contact/contact.py @@ -64,5 +64,4 @@ def get_contact_details(contact): "contact_department": contact.get("department") } - return out - \ No newline at end of file + return out \ No newline at end of file From f7f20f624a05d94e43ad830f98878f6259a11c48 Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Wed, 29 Jan 2014 20:13:43 +0530 Subject: [PATCH 21/31] shipping address in quotation fixed for customer validation --- public/js/queries.js | 30 +++++++++------ selling/doctype/opportunity/opportunity.js | 43 ++++++++++------------ selling/doctype/quotation/quotation.js | 22 +++++++++-- selling/doctype/quotation/quotation.py | 4 -- selling/doctype/quotation/quotation.txt | 3 +- utilities/doctype/address/address.py | 6 +-- 6 files changed, 61 insertions(+), 47 deletions(-) diff --git a/public/js/queries.js b/public/js/queries.js index 3c60a9149af..6fa9ef5bc0e 100644 --- a/public/js/queries.js +++ b/public/js/queries.js @@ -7,35 +7,35 @@ $.extend(erpnext.queries, { profile: function() { return { query: "core.doctype.profile.profile.profile_query" }; }, - + lead: function() { return { query: "controllers.queries.lead_query" }; }, - + customer: function() { return { query: "controllers.queries.customer_query" }; }, - + supplier: function() { return { query: "controllers.queries.supplier_query" }; }, - + account: function() { return { query: "controllers.queries.account_query" }; }, - + item: function() { return { query: "controllers.queries.item_query" }; }, - + bom: function() { return { query: "controllers.queries.bom" }; }, - + task: function() { return { query: "projects.utils.query_task" }; }, - + customer_filter: function(doc) { if(!doc.customer) { wn.throw(wn._("Please specify a") + " " + @@ -44,7 +44,7 @@ $.extend(erpnext.queries, { return { filters: { customer: doc.customer } }; }, - + supplier_filter: function(doc) { if(!doc.supplier) { wn.throw(wn._("Please specify a") + " " + @@ -53,9 +53,17 @@ $.extend(erpnext.queries, { return { filters: { supplier: doc.supplier } }; }, - + + lead_filter: function(doc) { + if(!doc.lead) { + wn.throw(wn._("Please specify a") + " " + + wn._(wn.meta.get_label(doc.doctype, "lead", doc.name))); + } + + return { filters: { lead: doc.lead } }; + }, + not_a_group_filter: function() { return { filters: { is_group: "No" } }; }, - }); \ No newline at end of file diff --git a/selling/doctype/opportunity/opportunity.js b/selling/doctype/opportunity/opportunity.js index 05970fc350f..cc093d2f352 100644 --- a/selling/doctype/opportunity/opportunity.js +++ b/selling/doctype/opportunity/opportunity.js @@ -15,13 +15,13 @@ erpnext.selling.Opportunity = wn.ui.form.Controller.extend({ if(!this.frm.doc.enquiry_from) hide_field(['customer', 'customer_address', 'contact_person', 'customer_name','lead', 'address_display', 'contact_display', 'contact_mobile', 'contact_email', 'territory', 'customer_group']); if(!this.frm.doc.status) - set_multiple(cdt,cdn,{status:'Draft'}); + set_multiple(cdt, cdn, { status:'Draft' }); if(!this.frm.doc.date) this.frm.doc.transaction_date = date.obj_to_str(new Date()); if(!this.frm.doc.company && wn.defaults.get_default("company")) - set_multiple(cdt,cdn,{company:wn.defaults.get_default("company")}); - if(!this.frm.doc.fiscal_year && sys_defaults.fiscal_year) - set_multiple(cdt,cdn,{fiscal_year:sys_defaults.fiscal_year}); + set_multiple(cdt, cdn, { company:wn.defaults.get_default("company") }); + if(!this.frm.doc.fiscal_year && sys_defaults.fiscal_year) + set_multiple(cdt, cdn, { fiscal_year:sys_defaults.fiscal_year }); if(this.frm.doc.enquiry_from) { if(this.frm.doc.enquiry_from == 'Customer') { @@ -99,15 +99,15 @@ erpnext.selling.Opportunity = wn.ui.form.Controller.extend({ $.extend(cur_frm.cscript, new erpnext.selling.Opportunity({frm: cur_frm})); -cur_frm.cscript.refresh = function(doc, cdt, cdn){ +cur_frm.cscript.refresh = function(doc, cdt, cdn) { erpnext.hide_naming_series(); cur_frm.clear_custom_buttons(); if(doc.docstatus === 1 && doc.status!=="Lost") { cur_frm.add_custom_button(wn._('Create Quotation'), cur_frm.cscript.create_quotation); - if(doc.status!=="Quotation") { + if(doc.status!=="Quotation") cur_frm.add_custom_button(wn._('Opportunity Lost'), cur_frm.cscript['Declare Opportunity Lost']); - } + cur_frm.add_custom_button(wn._('Send SMS'), cur_frm.cscript.send_sms, "icon-mobile-phone"); } @@ -116,31 +116,29 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn){ } cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) { - if(doc.enquiry_from == 'Lead' && doc.lead) { - cur_frm.cscript.lead(doc,cdt,cdn); - } + if(doc.enquiry_from == 'Lead' && doc.lead) + cur_frm.cscript.lead(doc, cdt, cdn); } cur_frm.cscript.item_code = function(doc, cdt, cdn) { var d = locals[cdt][cdn]; - if (d.item_code) { - return get_server_fields('get_item_details',d.item_code, 'enquiry_details',doc, cdt,cdn,1); - } + if (d.item_code) + return get_server_fields('get_item_details', d.item_code, 'enquiry_details', doc, cdt, cdn, 1); } // hide - unhide fields on basis of enquiry_from lead or customer -cur_frm.cscript.enquiry_from = function(doc,cdt,cdn){ - cur_frm.cscript.lead_cust_show(doc,cdt,cdn); +cur_frm.cscript.enquiry_from = function(doc, cdt, cdn) { + cur_frm.cscript.lead_cust_show(doc, cdt, cdn); } // hide - unhide fields based on lead or customer -cur_frm.cscript.lead_cust_show = function(doc,cdt,cdn){ - if(doc.enquiry_from == 'Lead'){ +cur_frm.cscript.lead_cust_show = function(doc, cdt, cdn) { + if(doc.enquiry_from == 'Lead') { unhide_field(['lead']); hide_field(['customer','customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']); doc.lead = doc.customer = doc.customer_address = doc.contact_person = doc.address_display = doc.contact_display = doc.contact_mobile = doc.contact_email = doc.territory = doc.customer_group = ""; } - else if(doc.enquiry_from == 'Customer'){ + else if(doc.enquiry_from == 'Customer') { unhide_field(['customer']); hide_field(['lead', 'address_display', 'contact_display', 'contact_mobile', 'contact_email', 'territory', 'customer_group']); @@ -164,15 +162,13 @@ cur_frm.cscript.lead = function(doc, cdt, cdn) { wn.model.map_current_doc({ method: "selling.doctype.lead.lead.make_opportunity", source_name: cur_frm.doc.lead - }) + }); unhide_field(['customer_name', 'address_display','contact_mobile', 'customer_address', - 'contact_email', 'territory']); + 'contact_email', 'territory']); } - - -cur_frm.cscript['Declare Opportunity Lost'] = function(){ +cur_frm.cscript['Declare Opportunity Lost'] = function() { var dialog = new wn.ui.Dialog({ title: wn._("Set as Lost"), fields: [ @@ -200,5 +196,4 @@ cur_frm.cscript['Declare Opportunity Lost'] = function(){ }) }); dialog.show(); - } \ No newline at end of file diff --git a/selling/doctype/quotation/quotation.js b/selling/doctype/quotation/quotation.js index c7bf44718fa..c92328e1bb5 100644 --- a/selling/doctype/quotation/quotation.js +++ b/selling/doctype/quotation/quotation.js @@ -15,12 +15,21 @@ wn.require('app/accounts/doctype/sales_invoice/pos.js'); erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ onload: function(doc, dt, dn) { + var me = this; this._super(doc, dt, dn); if(doc.customer && !doc.quotation_to) doc.quotation_to = "Customer"; else if(doc.lead && !doc.quotation_to) doc.quotation_to = "Lead"; - + + // to overwrite the customer_filter trigger from queries.js + if (doc.lead) { + $.each(["customer_address", "shipping_address_name", "contact_person"], + function(i, opts) { + me.frm.set_query(opts, erpnext.queries["lead_filter"]); + } + ); + } }, refresh: function(doc, dt, dn) { this._super(doc, dt, dn); @@ -68,6 +77,12 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ quotation_to: function() { this.frm.toggle_reqd("lead", this.frm.doc.quotation_to == "Lead"); this.frm.toggle_reqd("customer", this.frm.doc.quotation_to == "Customer"); + if (this.frm.doc.quotation_to == "Lead") { + this.frm.set_value("customer", null); + this.frm.set_value("contact_person", null); + } + else if (this.frm.doc.quotation_to == "Customer") + this.frm.set_value("lead", null); }, tc_name: function() { @@ -89,7 +104,7 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ cur_frm.script_manager.make(erpnext.selling.QuotationController); -cur_frm.fields_dict.lead.get_query = function(doc,cdt,cdn) { +cur_frm.fields_dict.lead.get_query = function(doc, cdt, cdn) { return{ query:"controllers.queries.lead_query" } } cur_frm.cscript.lead = function(doc, cdt, cdn) { @@ -152,7 +167,6 @@ cur_frm.cscript['Declare Order Lost'] = function(){ } cur_frm.cscript.on_submit = function(doc, cdt, cdn) { - if(cint(wn.boot.notification_settings.quotation)) { + if(cint(wn.boot.notification_settings.quotation)) cur_frm.email_doc(wn.boot.notification_settings.quotation_message); - } } \ No newline at end of file diff --git a/selling/doctype/quotation/quotation.py b/selling/doctype/quotation/quotation.py index f2546b9cfac..7a68cce1d49 100644 --- a/selling/doctype/quotation/quotation.py +++ b/selling/doctype/quotation/quotation.py @@ -3,14 +3,10 @@ from __future__ import unicode_literals import webnotes - from webnotes.utils import cstr from webnotes.model.bean import getlist from webnotes.model.code import get_obj from webnotes import _, msgprint - - - from controllers.selling_controller import SellingController class DocType(SellingController): diff --git a/selling/doctype/quotation/quotation.txt b/selling/doctype/quotation/quotation.txt index 93346d3029f..2bb1c205fe7 100644 --- a/selling/doctype/quotation/quotation.txt +++ b/selling/doctype/quotation/quotation.txt @@ -2,7 +2,7 @@ { "creation": "2013-05-24 19:29:08", "docstatus": 0, - "modified": "2013-12-14 17:25:46", + "modified": "2014-01-29 19:42:32", "modified_by": "Administrator", "owner": "Administrator" }, @@ -665,6 +665,7 @@ "read_only": 0 }, { + "depends_on": "eval:doc.customer", "doctype": "DocField", "fieldname": "contact_person", "fieldtype": "Link", diff --git a/utilities/doctype/address/address.py b/utilities/doctype/address/address.py index ad6e049b758..e371b515f85 100644 --- a/utilities/doctype/address/address.py +++ b/utilities/doctype/address/address.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import webnotes -from webnotes import msgprint +from webnotes import msgprint, throw, _ from webnotes.utils import cstr, cint class DocType: @@ -16,11 +16,11 @@ class DocType: if not self.doc.address_title: self.doc.address_title = self.doc.customer \ or self.doc.supplier or self.doc.sales_partner or self.doc.lead - + if self.doc.address_title: self.doc.name = cstr(self.doc.address_title).strip() + "-" + cstr(self.doc.address_type).strip() else: - webnotes.msgprint("""Address Title is mandatory.""" + self.doc.customer, raise_exception=True) + throw(_("Address Title is mandatory.")) def validate(self): self.validate_primary_address() From ef0a0e8209411ca614ad6f044e5be1d2f0eab07d Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Wed, 29 Jan 2014 20:18:43 +0530 Subject: [PATCH 22/31] stashed footer page --- portal/templates/includes/footer.html | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/portal/templates/includes/footer.html b/portal/templates/includes/footer.html index da58ed04a52..cd75fd16f63 100644 --- a/portal/templates/includes/footer.html +++ b/portal/templates/includes/footer.html @@ -16,10 +16,11 @@ {% endblock %} From 879b3a0cbc69272dd36da4ef376f0ad82beaf621 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 28 Jan 2014 14:58:31 +0530 Subject: [PATCH 23/31] Set expense account for perpetual inventory only non-opening invoices --- .../doctype/purchase_invoice/purchase_invoice.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py index fcd68465072..9b1ddb77e09 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -32,6 +32,9 @@ class DocType(BuyingController): }] def validate(self): + if not self.doc.is_opening: + self.doc.is_opening = 'No' + super(DocType, self).validate() self.po_required() @@ -45,15 +48,8 @@ class DocType(BuyingController): self.check_for_stopped_status() self.validate_with_previous_doc() self.validate_uom_is_integer("uom", "qty") - - if not self.doc.is_opening: - self.doc.is_opening = 'No' - self.set_aging_date() - - #set against account for credit to self.set_against_expense_account() - self.validate_write_off_account() self.update_raw_material_cost() self.update_valuation_rate("entries") @@ -215,7 +211,8 @@ class DocType(BuyingController): against_accounts = [] stock_items = self.get_stock_items() for item in self.doclist.get({"parentfield": "entries"}): - if auto_accounting_for_stock and item.item_code in stock_items: + if auto_accounting_for_stock and item.item_code in stock_items \ + and self.doc.is_opening == 'No': # in case of auto inventory accounting, against expense account is always # Stock Received But Not Billed for a stock item item.expense_head = stock_not_billed_account From c2a585814333dc8823120b8f4dc0b52b03add895 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 Jan 2014 10:50:35 +0530 Subject: [PATCH 24/31] Minor fix in accounts receivable report --- .../report/accounts_receivable/accounts_receivable.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/accounts/report/accounts_receivable/accounts_receivable.py b/accounts/report/accounts_receivable/accounts_receivable.py index c826fcbd135..1f24c584b60 100644 --- a/accounts/report/accounts_receivable/accounts_receivable.py +++ b/accounts/report/accounts_receivable/accounts_receivable.py @@ -132,8 +132,8 @@ class AccountsReceivableReport(object): if not hasattr(self, "gl_entries"): conditions, values = self.prepare_conditions() self.gl_entries = webnotes.conn.sql("""select * from `tabGL Entry` - where docstatus < 2 {} order by posting_date, account""".format(conditions), - values, as_dict=True) + where docstatus < 2 {0} order by posting_date, account""".format(conditions), + values, as_dict=True, debug=1) return self.gl_entries @@ -153,8 +153,8 @@ class AccountsReceivableReport(object): if not account_map: webnotes.throw(_("No Customer Accounts found.")) else: - accounts_list = ['"{}"'.format(ac) for ac in account_map] - conditions.append("account in ({})".format(", ".join(accounts_list))) + accounts_list = ['"{0}"'.format(ac) for ac in account_map] + conditions.append("account in ({0})".format(", ".join(accounts_list))) return " and ".join(conditions), values From 30aac9bbcff68c50f3b262dee5891a734b502b5b Mon Sep 17 00:00:00 2001 From: Akhilesh Darjee Date: Thu, 30 Jan 2014 11:02:06 +0530 Subject: [PATCH 25/31] removed contact person for lead filter --- selling/doctype/quotation/quotation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selling/doctype/quotation/quotation.js b/selling/doctype/quotation/quotation.js index c92328e1bb5..c8c919e86c5 100644 --- a/selling/doctype/quotation/quotation.js +++ b/selling/doctype/quotation/quotation.js @@ -24,7 +24,7 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ // to overwrite the customer_filter trigger from queries.js if (doc.lead) { - $.each(["customer_address", "shipping_address_name", "contact_person"], + $.each(["customer_address", "shipping_address_name"], function(i, opts) { me.frm.set_query(opts, erpnext.queries["lead_filter"]); } From 60fcce66f763cdf9086e84996b375fd12668f35d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 Jan 2014 12:08:44 +0530 Subject: [PATCH 26/31] Minor fix in accounts receivable report --- accounts/report/accounts_receivable/accounts_receivable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/report/accounts_receivable/accounts_receivable.py b/accounts/report/accounts_receivable/accounts_receivable.py index 1f24c584b60..eb8c91f99f3 100644 --- a/accounts/report/accounts_receivable/accounts_receivable.py +++ b/accounts/report/accounts_receivable/accounts_receivable.py @@ -133,7 +133,7 @@ class AccountsReceivableReport(object): conditions, values = self.prepare_conditions() self.gl_entries = webnotes.conn.sql("""select * from `tabGL Entry` where docstatus < 2 {0} order by posting_date, account""".format(conditions), - values, as_dict=True, debug=1) + values, as_dict=True) return self.gl_entries From 937103f840dd56a7d4d04dd8279c15ed81d81bdb Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 Jan 2014 12:12:26 +0530 Subject: [PATCH 27/31] Update accounts_receivable.py --- accounts/report/accounts_receivable/accounts_receivable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/report/accounts_receivable/accounts_receivable.py b/accounts/report/accounts_receivable/accounts_receivable.py index 1f24c584b60..eb8c91f99f3 100644 --- a/accounts/report/accounts_receivable/accounts_receivable.py +++ b/accounts/report/accounts_receivable/accounts_receivable.py @@ -133,7 +133,7 @@ class AccountsReceivableReport(object): conditions, values = self.prepare_conditions() self.gl_entries = webnotes.conn.sql("""select * from `tabGL Entry` where docstatus < 2 {0} order by posting_date, account""".format(conditions), - values, as_dict=True, debug=1) + values, as_dict=True) return self.gl_entries From 3f657bcf51a6f47948c72eea674ac125018b8f2c Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Thu, 30 Jan 2014 14:29:43 +0600 Subject: [PATCH 28/31] bumped to version 3.8.0 --- config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index cd3e91bda88..be5d2bd65cb 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,6 @@ { "app_name": "ERPNext", - "app_version": "3.7.1", + "app_version": "3.8.0", "base_template": "app/portal/templates/base.html", "modules": { "Accounts": { @@ -74,5 +74,5 @@ "type": "module" } }, - "requires_framework_version": "==3.8.0" + "requires_framework_version": "==3.9.0" } \ No newline at end of file From f2f17959ab5e6865d2e3a14eac58272d865d5c43 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 Jan 2014 14:54:25 +0530 Subject: [PATCH 29/31] Serial no fix --- patches/1401/fix_serial_no_status_and_warehouse.py | 8 +++++--- stock/doctype/serial_no/serial_no.py | 2 -- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/patches/1401/fix_serial_no_status_and_warehouse.py b/patches/1401/fix_serial_no_status_and_warehouse.py index fe43c282d81..9e5579c90c1 100644 --- a/patches/1401/fix_serial_no_status_and_warehouse.py +++ b/patches/1401/fix_serial_no_status_and_warehouse.py @@ -4,14 +4,16 @@ from __future__ import unicode_literals import webnotes + def execute(): serial_nos = webnotes.conn.sql("""select name from `tabSerial No` where docstatus=0 and status in ('Available', 'Sales Returned') and ifnull(warehouse, '') = ''""") for sr in serial_nos: try: - sr_bean = webnotes.bean("Serial No", sr[0]) - sr_bean.make_controller().via_stock_ledger = True - sr_bean.save() + last_sle = webnotes.bean("Serial No", sr[0]).make_controller().get_last_sle() + if last_sle.actual_qty > 0: + webnotes.conn.set_value("Serial No", sr[0], "warehouse", last_sle.warehouse) + webnotes.conn.commit() except: pass \ No newline at end of file diff --git a/stock/doctype/serial_no/serial_no.py b/stock/doctype/serial_no/serial_no.py index e6557b42847..57b3b460b1a 100644 --- a/stock/doctype/serial_no/serial_no.py +++ b/stock/doctype/serial_no/serial_no.py @@ -87,8 +87,6 @@ class DocType(StockController): self.doc.status = "Sales Returned" else: self.doc.status = "Available" - if not self.doc.warehouse: - self.doc.warehouse = last_sle.warehouse else: if document_type == "Purchase Return": self.doc.status = "Purchase Returned" From 5d976ca3acf18a6161954dc606f2b348a2d3033b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 Jan 2014 15:12:39 +0530 Subject: [PATCH 30/31] recurring invoice test fix --- accounts/doctype/sales_invoice/test_sales_invoice.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index e98dfdc2a0c..ccb7d532a54 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -654,7 +654,8 @@ class TestSalesInvoice(unittest.TestCase): def _test_recurring_invoice(self, base_si, first_and_last_day): from webnotes.utils import add_months, get_last_day - from accounts.doctype.sales_invoice.sales_invoice import manage_recurring_invoices + from accounts.doctype.sales_invoice.sales_invoice \ + import manage_recurring_invoices, get_next_date no_of_months = ({"Monthly": 1, "Quarterly": 3, "Yearly": 12})[base_si.doc.recurring_type] @@ -662,7 +663,8 @@ class TestSalesInvoice(unittest.TestCase): self.assertEquals(i+1, webnotes.conn.sql("""select count(*) from `tabSales Invoice` where recurring_id=%s and docstatus=1""", base_si.doc.recurring_id)[0][0]) - next_date = add_months(base_si.doc.posting_date, no_of_months) + next_date = get_next_date(base_si.doc.posting_date, no_of_months, + base_si.doc.repeat_on_day_of_month) manage_recurring_invoices(next_date=next_date, commit=False) From b5be7bab9b95d4b8e0bec6429f99b8726e5b3603 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 30 Jan 2014 18:47:12 +0530 Subject: [PATCH 31/31] Stock Return: fetch customer and supplier details --- .../stock/doctype/stock_entry/stock_entry.js | 74 ++++++++------ .../stock/doctype/stock_entry/stock_entry.py | 64 +++--------- erpnext/utilities/transaction_base.py | 97 +------------------ 3 files changed, 59 insertions(+), 176 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 6bd95648455..5dbcef8a0e1 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -93,9 +93,8 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ if(cint(wn.defaults.get_default("auto_accounting_for_stock"))) { var account_for = "stock_adjustment_account"; - if (this.frm.doc.purpose == "Sales Return") - account_for = "stock_in_hand_account"; - else if (this.frm.doc.purpose == "Purchase Return") + + if (this.frm.doc.purpose == "Purchase Return") account_for = "stock_received_but_not_billed"; return this.frm.call({ @@ -236,7 +235,50 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ mtn_details_on_form_rendered: function(doc, grid_row) { erpnext.setup_serial_no(grid_row) + }, + + customer: function() { + return this.frm.call({ + method: "erpnext.selling.doctype.customer.customer.get_customer_details", + args: { customer: this.frm.doc.customer } + }); + }, + + supplier: function() { + return this.frm.call({ + method: "erpnext.buying.doctype.supplier.supplier.get_supplier_details", + args: { supplier: this.frm.doc.supplier } + }); + }, + + delivery_note_no: function() { + this.get_party_details({ + ref_dt: "Delivery Note", + ref_dn: this.frm.doc.delivery_note_no + }) + }, + + sales_invoice_no: function() { + this.get_party_details({ + ref_dt: "Sales Invoice", + ref_dn: this.frm.doc.sales_invoice_no + }) + }, + + purchase_receipt_no: function() { + this.get_party_details({ + ref_dt: "Purchase Receipt", + ref_dn: this.frm.doc.purchase_receipt_no + }) + }, + + get_party_details: function(args) { + return this.frm.call({ + method: "erpnext.stock.doctype.stock_entry.stock_entry.get_party_details", + args: args, + }) } + }); cur_frm.script_manager.make(erpnext.stock.StockEntry); @@ -265,32 +307,6 @@ cur_frm.cscript.toggle_related_fields = function(doc) { } } -cur_frm.cscript.delivery_note_no = function(doc, cdt, cdn) { - if(doc.delivery_note_no) - return get_server_fields('get_cust_values', '', '', doc, cdt, cdn, 1); -} - -cur_frm.cscript.sales_invoice_no = function(doc, cdt, cdn) { - if(doc.sales_invoice_no) - return get_server_fields('get_cust_values', '', '', doc, cdt, cdn, 1); -} - -cur_frm.cscript.customer = function(doc, cdt, cdn) { - if(doc.customer) - return get_server_fields('get_cust_addr', '', '', doc, cdt, cdn, 1); -} - -cur_frm.cscript.purchase_receipt_no = function(doc, cdt, cdn) { - if(doc.purchase_receipt_no) - return get_server_fields('get_supp_values', '', '', doc, cdt, cdn, 1); -} - -cur_frm.cscript.supplier = function(doc, cdt, cdn) { - if(doc.supplier) - return get_server_fields('get_supp_addr', '', '', doc, cdt, cdn, 1); - -} - cur_frm.fields_dict['production_order'].get_query = function(doc) { return{ filters:[ diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 5c760f4aa02..161a3ad3918 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -590,56 +590,6 @@ class DocType(StockController): # increment idx by 1 idx += 1 return idx - - def get_cust_values(self): - """fetches customer details""" - if self.doc.delivery_note_no: - doctype = "Delivery Note" - name = self.doc.delivery_note_no - else: - doctype = "Sales Invoice" - name = self.doc.sales_invoice_no - - result = webnotes.conn.sql("""select customer, customer_name, - address_display as customer_address - from `tab%s` where name=%s""" % (doctype, "%s"), (name,), as_dict=1) - - return result and result[0] or {} - - def get_cust_addr(self): - from erpnext.utilities.transaction_base import get_default_address, get_address_display - res = webnotes.conn.sql("select customer_name from `tabCustomer` where name = '%s'"%self.doc.customer) - address_display = None - customer_address = get_default_address("customer", self.doc.customer) - if customer_address: - address_display = get_address_display(customer_address) - ret = { - 'customer_name' : res and res[0][0] or '', - 'customer_address' : address_display} - - return ret - - def get_supp_values(self): - result = webnotes.conn.sql("""select supplier, supplier_name, - address_display as supplier_address - from `tabPurchase Receipt` where name=%s""", (self.doc.purchase_receipt_no,), - as_dict=1) - - return result and result[0] or {} - - def get_supp_addr(self): - from erpnext.utilities.transaction_base import get_default_address, get_address_display - res = webnotes.conn.sql("""select supplier_name from `tabSupplier` - where name=%s""", self.doc.supplier) - address_display = None - supplier_address = get_default_address("customer", self.doc.customer) - if supplier_address: - address_display = get_address_display(supplier_address) - - ret = { - 'supplier_name' : res and res[0][0] or '', - 'supplier_address' : address_display } - return ret def validate_with_material_request(self): for item in self.doclist.get({"parentfield": "mtn_details"}): @@ -653,6 +603,17 @@ class DocType(StockController): + _("Material Request") + (" - %s" % item.material_request), raise_exception=webnotes.MappingMismatchError) +@webnotes.whitelist() +def get_party_details(ref_dt, ref_dn): + if ref_dt in ["Delivery Note", "Sales Invoice"]: + res = webnotes.conn.get_value(ref_dt, ref_dn, + ["customer", "customer_name", "address_display as customer_address"], as_dict=1) + else: + res = webnotes.conn.get_value(ref_dt, ref_dn, + ["supplier", "supplier_name", "address_display as supplier_address"], as_dict=1) + print ref_dt, ref_dn, res + return res or {} + @webnotes.whitelist() def get_production_order_details(production_order): result = webnotes.conn.sql("""select bom_no, @@ -965,5 +926,4 @@ def make_return_jv_from_purchase_receipt(se, ref): result = [parent] + [{"account": account} for account in children] - return result - + return result \ No newline at end of file diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index 02416291bde..098a4bd8e9d 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -2,28 +2,14 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import webnotes, json +import webnotes from webnotes import msgprint, _ from webnotes.utils import cstr, flt, now_datetime, cint -from webnotes.model.doc import addchild from erpnext.controllers.status_updater import StatusUpdater -class TransactionBase(StatusUpdater): - def set_address_fields(self): - party_type, party_name = self.get_party_type_and_name() - - if party_type in ("Customer", "Lead"): - if self.doc.customer_address: - self.doc.address_display = get_address_display(self.doc.customer_address) - - if self.doc.shipping_address_name: - self.doc.shipping_address = get_address_display(self.doc.shipping_address_name) - - elif self.doc.supplier_address: - self.doc.address_display = get_address_display(self.doc.supplier_address) - +class TransactionBase(StatusUpdater): def set_contact_fields(self): party_type, party_name = self.get_party_type_and_name() @@ -59,74 +45,6 @@ class TransactionBase(StatusUpdater): def set_lead_defaults(self): self.doc.fields.update(self.get_lead_defaults()) - - # TODO deprecate this - used only in sales_order.js - def get_shipping_address(self, name): - shipping_address = get_default_address("customer", name, is_shipping_address=True) - return { - 'shipping_address_name' : shipping_address, - 'shipping_address' : get_address_display(shipping_address) if shipping_address else None - } - - # Get Supplier Default Primary Address - first load - # ----------------------- - def get_default_supplier_address(self, args): - if isinstance(args, basestring): - args = json.loads(args) - - address_name = get_default_address("supplier", args["supplier"]) - ret = { - 'supplier_address' : address_name, - 'address_display' : get_address_display(address_name), - } - ret.update(map_party_contact_details(None, "supplier", args["supplier"])) - ret.update(self.get_supplier_details(args['supplier'])) - return ret - - # Get Supplier Address - # ----------------------- - def get_supplier_address(self, args): - args = json.loads(args) - ret = { - 'supplier_address' : args['address'], - 'address_display' : get_address_display(args["address"]), - } - ret.update(map_party_contact_details(contact_name=args['contact'])) - return ret - - def set_supplier_address(self, args): - self.doc.fields.update(self.get_supplier_address(args)) - - # Get Supplier Details - # ----------------------- - def get_supplier_details(self, name): - supplier_details = webnotes.conn.sql("""\ - select supplier_name, default_currency - from `tabSupplier` - where name = %s and docstatus < 2""", name, as_dict=1) - if supplier_details: - return { - 'supplier_name': (supplier_details[0]['supplier_name'] - or self.doc.fields.get('supplier_name')), - 'currency': (supplier_details[0]['default_currency'] - or self.doc.fields.get('currency')), - } - else: - return {} - - # Get Sales Person Details of Customer - # ------------------------------------ - def get_sales_person(self, name): - self.doclist = self.doc.clear_table(self.doclist,'sales_team') - idx = 0 - for d in webnotes.conn.sql("select sales_person, allocated_percentage, allocated_amount, incentives from `tabSales Team` where parent = '%s'" % name): - ch = addchild(self.doc, 'sales_team', 'Sales Team', self.doclist) - ch.sales_person = d and cstr(d[0]) or '' - ch.allocated_percentage = d and flt(d[1]) or 0 - ch.allocated_amount = d and flt(d[2]) or 0 - ch.incentives = d and flt(d[3]) or 0 - ch.idx = idx - idx += 1 def load_notification_message(self): dt = self.doc.doctype.lower().replace(" ", "_") @@ -208,17 +126,6 @@ class TransactionBase(StatusUpdater): for field, condition in fields: if prevdoc_values[field] is not None: self.validate_value(field, condition, prevdoc_values[field], doc) - -def get_default_address(party_field, party_name, is_shipping_address=False): - if is_shipping_address: - order_by = "is_shipping_address desc, is_primary_address desc, name asc" - else: - order_by = "is_primary_address desc, name asc" - - address = webnotes.conn.sql("""select name from `tabAddress` where `%s`=%s order by %s - limit 1""" % (party_field, "%s", order_by), party_name) - - return address[0][0] if address else None def get_default_contact(party_field, party_name): contact = webnotes.conn.sql("""select name from `tabContact` where `%s`=%s