diff --git a/stock/doctype/stock_entry/stock_entry.js b/stock/doctype/stock_entry/stock_entry.js index 010b27040d..158184486f 100644 --- a/stock/doctype/stock_entry/stock_entry.js +++ b/stock/doctype/stock_entry/stock_entry.js @@ -66,6 +66,17 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ if (this.frm.doc.docstatus==1) { this.show_stock_ledger(); } + + if(this.frm.doc.docstatus === 1 && wn.boot.profile.can_create("Journal Voucher")) { + if(this.frm.doc.purpose === "Sales Return") { + this.frm.add_custom_button("Make Credit Note", this.make_return_jv); + this.add_excise_button(); + } else if(this.frm.doc.purpose === "Purchase Return") { + this.frm.add_custom_button("Make Debit Note", this.make_return_jv); + this.add_excise_button(); + } + } + }, on_submit: function() { @@ -143,6 +154,29 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ } } }, + + add_excise_button: function() { + if(wn.boot.control_panel.country === "India") + this.frm.add_custom_button("Make Excise Invoice", function() { + var excise = wn.model.make_new_doc_and_get_name('Journal Voucher'); + excise = locals['Journal Voucher'][excise]; + excise.voucher_type = 'Excise Voucher'; + loaddoc('Journal Voucher', excise.name); + }); + }, + + make_return_jv: function() { + this.frm.call({ + method: "make_return_jv", + args: { + stock_entry: this.frm.doc.name + }, + callback: function(r) { + console.log(r); + loaddoc("Journal Voucher", r.message); + } + }); + }, }); diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py index 38ab5ba7e4..80756b40ed 100644 --- a/stock/doctype/stock_entry/stock_entry.py +++ b/stock/doctype/stock_entry/stock_entry.py @@ -29,6 +29,7 @@ import json sql = webnotes.conn.sql class NotUpdateStockError(webnotes.ValidationError): pass +class StockOverReturnError(webnotes.ValidationError): pass from controllers.accounts_controller import AccountsController @@ -278,32 +279,24 @@ class DocType(AccountsController): def validate_return_reference_doc(self): """validate item with reference doc""" - ref_doclist = parentfields = None + ref = get_return_reference_details(self.doc.fields) - # get ref_doclist - if self.doc.purpose in return_map: - for fieldname, val in return_map[self.doc.purpose].items(): - if self.doc.fields.get(fieldname): - ref_doclist = webnotes.get_doclist(val[0], self.doc.fields[fieldname]) - parentfields = val[1] - - if ref_doclist: + if ref.doclist: # validate docstatus - if ref_doclist[0].docstatus != 1: - webnotes.msgprint(_(ref_doclist[0].doctype) + ' "' + ref_doclist[0].name + '": ' + if ref.doclist[0].docstatus != 1: + webnotes.msgprint(_(ref.doclist[0].doctype) + ' "' + ref.doclist[0].name + '": ' + _("Status should be Submitted"), raise_exception=webnotes.InvalidStatusError) - # update stock check - if ref_doclist[0].doctype == "Sales Invoice" and (cint(ref_doclist[0].is_pos) != 1 \ - or cint(ref_doclist[0].update_stock) != 1): - webnotes.msgprint(_(ref_doclist[0].doctype) + ' "' + ref_doclist[0].name + '": ' + if ref.doclist[0].doctype == "Sales Invoice" and (cint(ref.doclist[0].is_pos) != 1 \ + or cint(ref.doclist[0].update_stock) != 1): + webnotes.msgprint(_(ref.doclist[0].doctype) + ' "' + ref.doclist[0].name + '": ' + _("Is POS and Update Stock should be checked."), raise_exception=NotUpdateStockError) # posting date check - ref_posting_datetime = "%s %s" % (cstr(ref_doclist[0].posting_date), - cstr(ref_doclist[0].posting_time)) + ref_posting_datetime = "%s %s" % (cstr(ref.doclist[0].posting_date), + cstr(ref.doclist[0].posting_time)) this_posting_datetime = "%s %s" % (cstr(self.doc.posting_date), cstr(self.doc.posting_time)) if this_posting_datetime < ref_posting_datetime: @@ -312,18 +305,27 @@ class DocType(AccountsController): + ": " + datetime_in_user_format(ref_posting_datetime), raise_exception=True) - stock_items = get_stock_items_for_return(ref_doclist, parentfields) + stock_items = get_stock_items_for_return(ref.doclist, ref.parentfields) + already_returned_item_qty = self.get_already_returned_item_qty(ref.fieldname) for item in self.doclist.get({"parentfield": "mtn_details"}): # validate if item exists in the ref doclist and that it is a stock item if item.item_code not in stock_items: msgprint(_("Item") + ': "' + item.item_code + _("\" does not exist in ") + - ref_doclist[0].doctype + ": " + ref_doclist[0].name, + ref.doclist[0].doctype + ": " + ref.doclist[0].name, raise_exception=webnotes.DoesNotExistError) - # validate quantity <= ref item's qty - ref_item = ref_doclist.getone({"item_code": item.item_code}) - self.validate_value("transfer_qty", "<=", ref_item.qty, item) + # validate quantity <= ref item's qty - qty already returned + ref_item = ref.doclist.getone({"item_code": item.item_code}) + returnable_qty = ref_item.qty - flt(already_returned_item_qty.get(item.item_code)) + self.validate_value("transfer_qty", "<=", returnable_qty, item, + raise_exception=StockOverReturnError) + + def get_already_returned_item_qty(self, ref_fieldname): + return dict(webnotes.conn.sql("""select item_code, sum(transfer_qty) as qty + from `tabStock Entry Detail` where parent in ( + select name from `tabStock Entry` where `%s`=%s and docstatus=1) + group by item_code""" % (ref_fieldname, "%s"), (self.doc.fields.get(ref_fieldname),))) def update_serial_no(self, is_submit): """Create / Update Serial No""" @@ -670,7 +672,7 @@ class DocType(AccountsController): + " " + _("Row #") + (" %d %s " % (mreq_item.idx, _("of"))) + _("Material Request") + (" - %s" % item.material_request), raise_exception=webnotes.MappingMismatchError) - + @webnotes.whitelist() def get_production_order_details(production_order): result = webnotes.conn.sql("""select bom_no, @@ -700,16 +702,13 @@ def query_purchase_return_doc(doctype, txt, searchfield, start, page_len, filter def query_return_item(doctype, txt, searchfield, start, page_len, filters): txt = txt.replace("%", "") - - for fieldname, val in return_map[filters["purpose"]].items(): - if filters.get(fieldname): - ref_doclist = webnotes.get_doclist(val[0], filters[fieldname]) - parentfields = val[1] + + ref = get_return_reference_details(filters) - stock_items = get_stock_items_for_return(ref_doclist, parentfields) + stock_items = get_stock_items_for_return(ref.doclist, ref.parentfields) result = [] - for item in ref_doclist.get({"parentfield": ["in", parentfields]}): + for item in ref.doclist.get({"parentfield": ["in", ref.parentfields]}): if item.item_code in stock_items: item.item_name = cstr(item.item_name) item.description = cstr(item.description) @@ -738,6 +737,20 @@ def get_stock_items_for_return(ref_doclist, parentfields): return stock_items +def get_return_reference_details(args): + ref = webnotes._dict() + + # get ref_doclist + if args["purpose"] in return_map: + for fieldname, val in return_map[args["purpose"]].items(): + if args.get(fieldname): + ref.fieldname = fieldname + ref.doclist = webnotes.get_doclist(val[0], args[fieldname]) + ref.parentfields = val[1] + break + + return ref + return_map = { "Sales Return": { # [Ref DocType, [Item tables' parentfields]] @@ -748,3 +761,18 @@ return_map = { "purchase_receipt_no": ["Purchase Receipt", ["purchase_receipt_details"]] } } + +def make_return_jv(stock_entry): + jv = webnotes.bean({ + "doctype": "Journal Voucher", + "__islocal": 1 + }) + + se = webnotes.bean("Stock Entry", stock_entry) + + if not webnotes.response.get("docs"): + webnotes.response["docs"] = [] + + webnotes.response["docs"] = jv.doclist + + return jv.doc.name \ No newline at end of file diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py index caf3291534..c0f8f28240 100644 --- a/stock/doctype/stock_entry/test_stock_entry.py +++ b/stock/doctype/stock_entry/test_stock_entry.py @@ -312,6 +312,23 @@ class TestStockEntry(unittest.TestCase): self.assertEquals(actual_qty_1 - 5, actual_qty_2) + return pr.doc.name + + def test_over_stock_return(self): + from stock.doctype.stock_entry.stock_entry import StockOverReturnError + + # out of 10, 5 gets returned + 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" + se.doc.purchase_receipt_no = pr_docname + se.doc.posting_date = "2013-03-01" + se.doclist[1].qty = se.doclist[1].transfer_qty = 6 + se.doclist[1].s_warehouse = "_Test Warehouse" + + self.assertRaises(StockOverReturnError, se.insert) test_records = [ [