diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py index fd80a52680..ca13458f3e 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -75,6 +75,7 @@ class DocType(BuyingController): self.validate_write_off_account() self.update_raw_material_cost() self.update_valuation_rate("entries") + self.validate_multiple_billing("Purchase Receipt", "pr_detail", "import_amount") def get_credit_to(self): acc_head = sql("""select name, credit_days from `tabAccount` @@ -206,17 +207,17 @@ class DocType(BuyingController): }, "Purchase Order Item": { "ref_dn_field": "po_detail", - "compare_fields": [["export_rate", "="], ["project_name", "="], ["item_code", "="], + "compare_fields": [["import_rate", "="], ["project_name", "="], ["item_code", "="], ["uom", "="]], "is_child_table": True }, "Purchase Receipt": { "ref_dn_field": "purchase_receipt", - "compare_fields": [["customer", "="], ["company", "="], ["currency", "="]], + "compare_fields": [["supplier", "="], ["company", "="], ["currency", "="]], }, "Purchase Receipt Item": { "ref_dn_field": "pr_detail", - "compare_fields": [["export_rate", "="], ["project_name", "="], ["item_code", "="], + "compare_fields": [["import_rate", "="], ["project_name", "="], ["item_code", "="], ["uom", "="]], "is_child_table": True } diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 23fe75eaff..e0147b12d3 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -93,6 +93,7 @@ class DocType(SellingController): self.validate_c_form() self.validate_time_logs_are_submitted() self.validate_recurring_invoice() + self.validate_multiple_billing("Delivered Note", "dn_detail", "export_amount") def on_submit(self): if cint(self.doc.update_stock) == 1: diff --git a/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js index b6070ba95e..1b7dcf0e47 100644 --- a/buying/doctype/purchase_common/purchase_common.js +++ b/buying/doctype/purchase_common/purchase_common.js @@ -107,7 +107,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ plc_conversion_rate: me.frm.doc.plc_conversion_rate, is_subcontracted: me.frm.doc.is_subcontracted, company: me.frm.doc.company, - currency: me.frm.doc.currency + currency: me.frm.doc.currency, + transaction_date: me.frm.doc.transaction_date } }, callback: function(r) { diff --git a/buying/doctype/purchase_order/purchase_order.py b/buying/doctype/purchase_order/purchase_order.py index 50eef5d76c..22cecf6467 100644 --- a/buying/doctype/purchase_order/purchase_order.py +++ b/buying/doctype/purchase_order/purchase_order.py @@ -64,7 +64,7 @@ class DocType(BuyingController): }, "Supplier Quotation Item": { "ref_dn_field": "supplier_quotation_item", - "compare_fields": [["export_rate", "="], ["project_name", "="], ["item_code", "="], + "compare_fields": [["import_rate", "="], ["project_name", "="], ["item_code", "="], ["uom", "="]], "is_child_table": True } diff --git a/buying/doctype/purchase_order_item/purchase_order_item.txt b/buying/doctype/purchase_order_item/purchase_order_item.txt index 927f6e1218..dac9a7be05 100755 --- a/buying/doctype/purchase_order_item/purchase_order_item.txt +++ b/buying/doctype/purchase_order_item/purchase_order_item.txt @@ -2,7 +2,7 @@ { "creation": "2013-05-24 19:29:06", "docstatus": 0, - "modified": "2013-07-09 12:16:19", + "modified": "2013-07-09 11:45:00", "modified_by": "Administrator", "owner": "Administrator" }, @@ -25,6 +25,20 @@ "doctype": "DocType", "name": "Purchase Order Item" }, + { + "doctype": "DocField", + "fieldname": "item_code", + "fieldtype": "Link", + "in_filter": 1, + "label": "Item Code", + "oldfieldname": "item_code", + "oldfieldtype": "Link", + "options": "Item", + "print_hide": 0, + "read_only": 0, + "reqd": 1, + "search_index": 1 + }, { "doctype": "DocField", "fieldname": "schedule_date", @@ -40,20 +54,6 @@ "reqd": 1, "search_index": 1 }, - { - "doctype": "DocField", - "fieldname": "item_code", - "fieldtype": "Link", - "in_filter": 1, - "label": "Item Code", - "oldfieldname": "item_code", - "oldfieldtype": "Link", - "options": "Item", - "print_hide": 0, - "read_only": 0, - "reqd": 1, - "search_index": 1 - }, { "description": "If Supplier Part Number exists for given Item, it gets stored here", "doctype": "DocField", diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py index 957958cddc..41b033bb0f 100644 --- a/controllers/accounts_controller.py +++ b/controllers/accounts_controller.py @@ -354,6 +354,22 @@ class AccountsController(TransactionBase): "advance_amount": flt(d.amount), "allocate_amount": 0 }) + + def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on): + for item in self.doclist.get({"parentfield": "entries"}): + if item.fields.get(item_ref_dn): + already_billed = webnotes.conn.sql("""select sum(%s) from `tab%s` + where %s=%s and docstatus=1""" % (based_on, self.tname, item_ref_dn, '%s'), + item.fields[item_ref_dn])[0][0] + if already_billed: + max_allowed_amt = webnotes.conn.get_value(ref_dt + " Item", + item.fields[item_ref_dn], based_on) + + if flt(already_billed) + flt(item.fields[based_on]) > max_allowed_amt: + webnotes.msgprint(_("Row ")+ item.idx + ": " + item.item_code + + _(" will be over-billed against mentioned ") + ref_dt + + _(". Max allowed " + based_on + ": " + max_allowed_amt), + raise_exception=1) def get_company_default(self, fieldname): from accounts.utils import get_company_default diff --git a/patches/july_2013/p02_copy_shipping_address.py b/patches/july_2013/p02_copy_shipping_address.py new file mode 100644 index 0000000000..c73f93db2a --- /dev/null +++ b/patches/july_2013/p02_copy_shipping_address.py @@ -0,0 +1,5 @@ +def execute(): + import webnotes + webnotes.reload_doc("stock", "doctype", "delivery_note") + webnotes.conn.sql("""update `tabDelivery Note` set shipping_address_name = customer_address, + shipping_address = address_display""") \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index f3aaa156c7..eee00fe855 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -248,4 +248,5 @@ patch_list = [ "patches.july_2013.p01_remove_doctype_mappers", "execute:webnotes.delete_doc('Report', 'Delivered Items To Be Billed')", "execute:webnotes.delete_doc('Report', 'Received Items To Be Billed')", + "patches.july_2013.p02_copy_shipping_address", ] \ No newline at end of file diff --git a/public/js/controllers/stock_controller.js b/public/js/controllers/stock_controller.js index 3021d756b9..ccbca4e9c0 100644 --- a/public/js/controllers/stock_controller.js +++ b/public/js/controllers/stock_controller.js @@ -19,7 +19,7 @@ wn.provide("erpnext.stock"); erpnext.stock.StockController = wn.ui.form.Controller.extend({ show_stock_ledger: function() { var me = this; - this.frm.add_custom_button("Show Stock Ledger", function() { + this.frm.add_custom_button("Stock Ledger", function() { wn.route_options = { voucher_no: me.frm.doc.name, from_date: cur_frm.doc.posting_date, @@ -27,5 +27,17 @@ erpnext.stock.StockController = wn.ui.form.Controller.extend({ }; wn.set_route('stock-ledger'); }, "icon-bar-chart"); + }, + show_general_ledger: function() { + if(doc.docstatus==1) { + cur_frm.add_custom_button('Accounting Ledger', function() { + wn.route_options = { + "voucher_no": doc.name, + "from_date": doc.posting_date, + "to_date": doc.posting_date, + }; + wn.set_route("general-ledger"); + }); + } } }); \ No newline at end of file diff --git a/selling/doctype/sales_order/test_sales_order.py b/selling/doctype/sales_order/test_sales_order.py index 23046ede3e..f1579327e7 100644 --- a/selling/doctype/sales_order/test_sales_order.py +++ b/selling/doctype/sales_order/test_sales_order.py @@ -158,7 +158,7 @@ class TestSalesOrder(unittest.TestCase): self.check_reserved_qty(so.doclist[1].item_code, so.doclist[1].reserved_warehouse, 10.0) def test_reserved_qty_for_so_with_packing_list(self): - from stock.doctype.sales_bom.test_sales_bom import test_records as sbom_test_records + from selling.doctype.sales_bom.test_sales_bom import test_records as sbom_test_records # change item in test so record test_record = test_records[0][:] @@ -185,7 +185,7 @@ class TestSalesOrder(unittest.TestCase): so.doclist[1].reserved_warehouse, 0.0) def test_reserved_qty_for_partial_delivery_with_packing_list(self): - from stock.doctype.sales_bom.test_sales_bom import test_records as sbom_test_records + from selling.doctype.sales_bom.test_sales_bom import test_records as sbom_test_records # change item in test so record @@ -235,7 +235,7 @@ class TestSalesOrder(unittest.TestCase): so.doclist[1].reserved_warehouse, 20.0) def test_reserved_qty_for_over_delivery_with_packing_list(self): - from stock.doctype.sales_bom.test_sales_bom import test_records as sbom_test_records + from selling.doctype.sales_bom.test_sales_bom import test_records as sbom_test_records # change item in test so record test_record = webnotes.copy_doclist(test_records[0]) diff --git a/stock/doctype/delivery_note/delivery_note.js b/stock/doctype/delivery_note/delivery_note.js index 117e77eacb..936ca9eb5e 100644 --- a/stock/doctype/delivery_note/delivery_note.js +++ b/stock/doctype/delivery_note/delivery_note.js @@ -34,7 +34,9 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend( if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1) cur_frm.add_custom_button('Make Installation Note', this.make_installation_note); - if (doc.docstatus==1) cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); + if (doc.docstatus==1) { + cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); + } if(doc.docstatus==0 && !doc.__islocal) { cur_frm.add_custom_button('Make Packing Slip', cur_frm.cscript['Make Packing Slip']); diff --git a/stock/doctype/delivery_note/test_delivery_note.py b/stock/doctype/delivery_note/test_delivery_note.py index a8ff87ecc4..e7e66ed30a 100644 --- a/stock/doctype/delivery_note/test_delivery_note.py +++ b/stock/doctype/delivery_note/test_delivery_note.py @@ -28,6 +28,25 @@ class TestDeliveryNote(unittest.TestCase): pr.run_method("calculate_taxes_and_totals") pr.insert() pr.submit() + + def test_over_billing_against_dn(self): + from stock.doctype.delivery_note.delivery_note import make_sales_invoice + + dn = webnotes.bean(copy=test_records[0]).insert() + + self.assertRaises(webnotes.ValidationError, make_sales_invoice, + dn.doc.name) + + dn = webnotes.bean("Delivery Note", dn.doc.name) + dn.submit() + si = make_sales_invoice(dn.doc.name) + + self.assertEquals(len(si), len(dn.doclist)) + + # modify export_amount + si[1].export_rate = 200 + self.assertRaises(webnotes.ValidationError, webnotes.bean(si).submit) + def test_delivery_note_no_gl_entry(self): webnotes.conn.sql("""delete from `tabBin`""") diff --git a/stock/doctype/material_request/test_material_request.py b/stock/doctype/material_request/test_material_request.py index 5d221680bf..2dbc748ec8 100644 --- a/stock/doctype/material_request/test_material_request.py +++ b/stock/doctype/material_request/test_material_request.py @@ -124,6 +124,9 @@ class TestMaterialRequest(unittest.TestCase): po_doclist[0].supplier = "_Test Supplier" po_doclist[1].qty = 27.0 po_doclist[2].qty = 1.5 + po_doclist[1].schedule_date = "2013-07-09" + po_doclist[2].schedule_date = "2013-07-09" + # check for stopped status of Material Request po = webnotes.bean(copy=po_doclist) diff --git a/stock/doctype/material_request_item/material_request_item.txt b/stock/doctype/material_request_item/material_request_item.txt index 7d9a417211..992b4cf239 100644 --- a/stock/doctype/material_request_item/material_request_item.txt +++ b/stock/doctype/material_request_item/material_request_item.txt @@ -2,7 +2,7 @@ { "creation": "2013-02-22 01:28:02", "docstatus": 0, - "modified": "2013-03-07 07:03:25", + "modified": "2013-07-09 11:37:12", "modified_by": "Administrator", "owner": "Administrator" }, @@ -25,20 +25,6 @@ "doctype": "DocType", "name": "Material Request Item" }, - { - "allow_on_submit": 0, - "doctype": "DocField", - "fieldname": "schedule_date", - "fieldtype": "Date", - "label": "Required Date", - "no_copy": 1, - "oldfieldname": "schedule_date", - "oldfieldtype": "Date", - "print_hide": 0, - "print_width": "100px", - "reqd": 1, - "width": "100px" - }, { "doctype": "DocField", "fieldname": "item_code", @@ -53,6 +39,20 @@ "search_index": 1, "width": "100px" }, + { + "allow_on_submit": 0, + "doctype": "DocField", + "fieldname": "schedule_date", + "fieldtype": "Date", + "label": "Required Date", + "no_copy": 1, + "oldfieldname": "schedule_date", + "oldfieldtype": "Date", + "print_hide": 0, + "print_width": "100px", + "reqd": 1, + "width": "100px" + }, { "doctype": "DocField", "fieldname": "description", diff --git a/stock/doctype/purchase_receipt/purchase_receipt.py b/stock/doctype/purchase_receipt/purchase_receipt.py index 9bb4ee1990..59c6c32d32 100644 --- a/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/stock/doctype/purchase_receipt/purchase_receipt.py @@ -103,7 +103,7 @@ class DocType(BuyingController): }, "Purchase Order Item": { "ref_dn_field": "prevdoc_detail_docname", - "compare_fields": [["export_rate", "="], ["project_name", "="], ["warehouse", "="], + "compare_fields": [["import_rate", "="], ["project_name", "="], ["warehouse", "="], ["uom", "="], ["item_code", "="]], "is_child_table": True } diff --git a/stock/doctype/purchase_receipt/test_purchase_receipt.py b/stock/doctype/purchase_receipt/test_purchase_receipt.py index e754e69836..377f91d831 100644 --- a/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -22,7 +22,7 @@ import webnotes.defaults from webnotes.utils import cint class TestPurchaseReceipt(unittest.TestCase): - def test_make_purchase_invocie(self): + def test_make_purchase_invoice(self): from stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice pr = webnotes.bean(copy=test_records[0]).insert() @@ -37,6 +37,10 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEquals(pi[0]["doctype"], "Purchase Invoice") self.assertEquals(len(pi), len(pr.doclist)) + # modify import_rate + pi[1].import_rate = 200 + self.assertRaises(webnotes.ValidationError, webnotes.bean(pi).submit) + def test_purchase_receipt_no_gl_entry(self): pr = webnotes.bean(copy=test_records[0]) pr.run_method("calculate_taxes_and_totals") diff --git a/stock/doctype/stock_entry/stock_entry.js b/stock/doctype/stock_entry/stock_entry.js index 1f4aafa9e5..468d3d7a96 100644 --- a/stock/doctype/stock_entry/stock_entry.js +++ b/stock/doctype/stock_entry/stock_entry.js @@ -103,6 +103,8 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ this.toggle_enable_bom(); if (this.frm.doc.docstatus==1) { this.show_stock_ledger(); + if(wn.boot.auto_inventory_accounting) + this.show_general_ledger(); } if(this.frm.doc.docstatus === 1 && diff --git a/stock/report/batch_wise_balance_history/batch_wise_balance_history.js b/stock/report/batch_wise_balance_history/batch_wise_balance_history.js index 0ba1938a59..98293e45f7 100644 --- a/stock/report/batch_wise_balance_history/batch_wise_balance_history.js +++ b/stock/report/batch_wise_balance_history/batch_wise_balance_history.js @@ -1,26 +1,5 @@ wn.query_reports["Batch-Wise Balance History"] = { "filters": [ - { - "fieldname":"item_code", - "label": "Item", - "fieldtype": "Link", - "options": "Item", - "width": "80" - }, - { - "fieldname":"warehouse", - "label": "Warehouse", - "fieldtype": "Link", - "options": "Warehouse", - "width": "80" - }, - { - "fieldname":"batch_no", - "label": "Batch", - "fieldtype": "Link", - "options": "Batch", - "width": "80" - }, { "fieldname":"from_date", "label": "From Date", diff --git a/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/stock/report/batch_wise_balance_history/batch_wise_balance_history.py index ca3e775f72..530465e9ae 100644 --- a/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +++ b/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -49,15 +49,6 @@ def get_columns(filters): def get_conditions(filters): conditions = "" - if filters.get("item_code"): - conditions += " and item_code='%s'" % filters["item_code"] - - if filters.get("warehouse"): - conditions += " and warehouse='%s'" % filters["warehouse"] - - if filters.get("batch_no"): - conditions += " and batch_no='%s'" % filters["batch_no"] - if not filters.get("from_date"): webnotes.msgprint("Please enter From Date", raise_exception=1) @@ -100,8 +91,6 @@ def get_item_warehouse_batch_map(filters): return iwb_map def get_item_details(filters): - if filters.get("item_code"): - conditions = " and name = '%s'" % filters["item_code"] item_map = {} for d in webnotes.conn.sql("select name, item_name, description from tabItem", as_dict=1): item_map.setdefault(d.name, d) diff --git a/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js index 5e1eb3a7b2..2ce5b4b4ab 100644 --- a/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js +++ b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js @@ -1,19 +1,5 @@ wn.query_reports["Warehouse-Wise Stock Balance"] = { "filters": [ - { - "fieldname":"item_code", - "label": "Item", - "fieldtype": "Link", - "options": "Item", - "width": "80" - }, - { - "fieldname":"warehouse", - "label": "Warehouse", - "fieldtype": "Link", - "options": "Warehouse", - "width": "80" - }, { "fieldname":"from_date", "label": "From Date", diff --git a/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py index 324bbe3e8c..4389aa59e4 100644 --- a/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py +++ b/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py @@ -26,34 +26,29 @@ def execute(filters=None): iwb_map = get_item_warehouse_map(filters) data = [] - for item in sorted(iwb_map): - for wh in sorted(iwb_map[item]): - qty_dict = iwb_map[item][wh] - data.append([item, item_map[item]["item_name"], - item_map[item]["description"], wh, - qty_dict.opening_qty, qty_dict.in_qty, - qty_dict.out_qty, qty_dict.bal_qty - ]) + for company in sorted(iwb_map): + for item in sorted(iwb_map[company]): + for wh in sorted(iwb_map[company][item]): + qty_dict = iwb_map[company][item][wh] + data.append([item, item_map[item]["item_name"], + item_map[item]["description"], wh, + qty_dict.opening_qty, qty_dict.in_qty, + qty_dict.out_qty, qty_dict.bal_qty, company + ]) return columns, data def get_columns(filters): """return columns based on filters""" - columns = ["Item:Link/Item:100"] + ["Item Name::150"] + ["Description::150"] + \ - ["Warehouse:Link/Warehouse:100"] + ["Opening Qty::90"] + \ - ["In Qty::80"] + ["Out Qty::80"] + ["Balance Qty::90"] + columns = ["Item:Link/Item:100", "Item Name::150", "Description::150", \ + "Warehouse:Link/Warehouse:100", "Opening Qty::90", \ + "In Qty::80", "Out Qty::80", "Balance Qty::90", "Company:Link/Company:100"] return columns def get_conditions(filters): conditions = "" - if filters.get("item_code"): - conditions += " and item_code='%s'" % filters["item_code"] - - if filters.get("warehouse"): - conditions += " and warehouse='%s'" % filters["warehouse"] - if not filters.get("from_date"): webnotes.msgprint("Please enter From Date", raise_exception=1) @@ -68,7 +63,7 @@ def get_conditions(filters): def get_stock_ledger_entries(filters): conditions = get_conditions(filters) return webnotes.conn.sql("""select item_code, warehouse, - posting_date, actual_qty + posting_date, actual_qty, company from `tabStock Ledger Entry` where ifnull(is_cancelled, 'No') = 'No' %s order by item_code, warehouse""" % conditions, as_dict=1) @@ -78,10 +73,11 @@ def get_item_warehouse_map(filters): iwb_map = {} for d in sle: - iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, webnotes._dict({\ + iwb_map.setdefault(d.company, {}).setdefault(d.item_code, {}).\ + setdefault(d.warehouse, webnotes._dict({\ "opening_qty": 0.0, "in_qty": 0.0, "out_qty": 0.0, "bal_qty": 0.0 })) - qty_dict = iwb_map[d.item_code][d.warehouse] + qty_dict = iwb_map[d.company][d.item_code][d.warehouse] if d.posting_date < filters["from_date"]: qty_dict.opening_qty += flt(d.actual_qty) elif d.posting_date >= filters["from_date"] and d.posting_date <= filters["to_date"]: @@ -95,8 +91,6 @@ def get_item_warehouse_map(filters): return iwb_map def get_item_details(filters): - if filters.get("item_code"): - conditions = " and name = '%s'" % filters["item_code"] item_map = {} for d in webnotes.conn.sql("select name, item_name, description from tabItem", as_dict=1): item_map.setdefault(d.name, d) diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py index 5994ae5afa..30a40da7ba 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -303,7 +303,7 @@ class TransactionBase(StatusUpdater): for key, val in ref.items(): ref_doc = {} for d in self.doclist.get({"doctype": source_dt}): - if d.fields[val["ref_dn_field"]]: + if d.fields.get(val["ref_dn_field"]): ref_doc.setdefault(key, d.fields[val["ref_dn_field"]]) if val.get("is_child_table"):