validate over-return of stock for sales purchase return
This commit is contained in:
parent
0a1ac4090c
commit
87052b366b
@ -66,6 +66,17 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
|||||||
if (this.frm.doc.docstatus==1) {
|
if (this.frm.doc.docstatus==1) {
|
||||||
this.show_stock_ledger();
|
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() {
|
on_submit: function() {
|
||||||
@ -144,6 +155,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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
cur_frm.cscript = new erpnext.stock.StockEntry({frm: cur_frm});
|
cur_frm.cscript = new erpnext.stock.StockEntry({frm: cur_frm});
|
||||||
|
@ -29,6 +29,7 @@ import json
|
|||||||
sql = webnotes.conn.sql
|
sql = webnotes.conn.sql
|
||||||
|
|
||||||
class NotUpdateStockError(webnotes.ValidationError): pass
|
class NotUpdateStockError(webnotes.ValidationError): pass
|
||||||
|
class StockOverReturnError(webnotes.ValidationError): pass
|
||||||
|
|
||||||
from controllers.accounts_controller import AccountsController
|
from controllers.accounts_controller import AccountsController
|
||||||
|
|
||||||
@ -278,32 +279,24 @@ class DocType(AccountsController):
|
|||||||
|
|
||||||
def validate_return_reference_doc(self):
|
def validate_return_reference_doc(self):
|
||||||
"""validate item with reference doc"""
|
"""validate item with reference doc"""
|
||||||
ref_doclist = parentfields = None
|
ref = get_return_reference_details(self.doc.fields)
|
||||||
|
|
||||||
# get ref_doclist
|
if 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:
|
|
||||||
# validate docstatus
|
# validate docstatus
|
||||||
if ref_doclist[0].docstatus != 1:
|
if ref.doclist[0].docstatus != 1:
|
||||||
webnotes.msgprint(_(ref_doclist[0].doctype) + ' "' + ref_doclist[0].name + '": '
|
webnotes.msgprint(_(ref.doclist[0].doctype) + ' "' + ref.doclist[0].name + '": '
|
||||||
+ _("Status should be Submitted"), raise_exception=webnotes.InvalidStatusError)
|
+ _("Status should be Submitted"), raise_exception=webnotes.InvalidStatusError)
|
||||||
|
|
||||||
|
|
||||||
# update stock check
|
# update stock check
|
||||||
if ref_doclist[0].doctype == "Sales Invoice" and (cint(ref_doclist[0].is_pos) != 1 \
|
if ref.doclist[0].doctype == "Sales Invoice" and (cint(ref.doclist[0].is_pos) != 1 \
|
||||||
or cint(ref_doclist[0].update_stock) != 1):
|
or cint(ref.doclist[0].update_stock) != 1):
|
||||||
webnotes.msgprint(_(ref_doclist[0].doctype) + ' "' + ref_doclist[0].name + '": '
|
webnotes.msgprint(_(ref.doclist[0].doctype) + ' "' + ref.doclist[0].name + '": '
|
||||||
+ _("Is POS and Update Stock should be checked."),
|
+ _("Is POS and Update Stock should be checked."),
|
||||||
raise_exception=NotUpdateStockError)
|
raise_exception=NotUpdateStockError)
|
||||||
|
|
||||||
# posting date check
|
# posting date check
|
||||||
ref_posting_datetime = "%s %s" % (cstr(ref_doclist[0].posting_date),
|
ref_posting_datetime = "%s %s" % (cstr(ref.doclist[0].posting_date),
|
||||||
cstr(ref_doclist[0].posting_time))
|
cstr(ref.doclist[0].posting_time))
|
||||||
this_posting_datetime = "%s %s" % (cstr(self.doc.posting_date),
|
this_posting_datetime = "%s %s" % (cstr(self.doc.posting_date),
|
||||||
cstr(self.doc.posting_time))
|
cstr(self.doc.posting_time))
|
||||||
if this_posting_datetime < ref_posting_datetime:
|
if this_posting_datetime < ref_posting_datetime:
|
||||||
@ -312,18 +305,27 @@ class DocType(AccountsController):
|
|||||||
+ ": " + datetime_in_user_format(ref_posting_datetime),
|
+ ": " + datetime_in_user_format(ref_posting_datetime),
|
||||||
raise_exception=True)
|
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"}):
|
for item in self.doclist.get({"parentfield": "mtn_details"}):
|
||||||
# validate if item exists in the ref doclist and that it is a stock item
|
# validate if item exists in the ref doclist and that it is a stock item
|
||||||
if item.item_code not in stock_items:
|
if item.item_code not in stock_items:
|
||||||
msgprint(_("Item") + ': "' + item.item_code + _("\" does not exist in ") +
|
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)
|
raise_exception=webnotes.DoesNotExistError)
|
||||||
|
|
||||||
# validate quantity <= ref item's qty
|
# validate quantity <= ref item's qty - qty already returned
|
||||||
ref_item = ref_doclist.getone({"item_code": item.item_code})
|
ref_item = ref.doclist.getone({"item_code": item.item_code})
|
||||||
self.validate_value("transfer_qty", "<=", ref_item.qty, item)
|
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):
|
def update_serial_no(self, is_submit):
|
||||||
"""Create / Update Serial No"""
|
"""Create / Update Serial No"""
|
||||||
@ -701,15 +703,12 @@ def query_purchase_return_doc(doctype, txt, searchfield, start, page_len, filter
|
|||||||
def query_return_item(doctype, txt, searchfield, start, page_len, filters):
|
def query_return_item(doctype, txt, searchfield, start, page_len, filters):
|
||||||
txt = txt.replace("%", "")
|
txt = txt.replace("%", "")
|
||||||
|
|
||||||
for fieldname, val in return_map[filters["purpose"]].items():
|
ref = get_return_reference_details(filters)
|
||||||
if filters.get(fieldname):
|
|
||||||
ref_doclist = webnotes.get_doclist(val[0], filters[fieldname])
|
|
||||||
parentfields = val[1]
|
|
||||||
|
|
||||||
stock_items = get_stock_items_for_return(ref_doclist, parentfields)
|
stock_items = get_stock_items_for_return(ref.doclist, ref.parentfields)
|
||||||
|
|
||||||
result = []
|
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:
|
if item.item_code in stock_items:
|
||||||
item.item_name = cstr(item.item_name)
|
item.item_name = cstr(item.item_name)
|
||||||
item.description = cstr(item.description)
|
item.description = cstr(item.description)
|
||||||
@ -738,6 +737,20 @@ def get_stock_items_for_return(ref_doclist, parentfields):
|
|||||||
|
|
||||||
return stock_items
|
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 = {
|
return_map = {
|
||||||
"Sales Return": {
|
"Sales Return": {
|
||||||
# [Ref DocType, [Item tables' parentfields]]
|
# [Ref DocType, [Item tables' parentfields]]
|
||||||
@ -748,3 +761,18 @@ return_map = {
|
|||||||
"purchase_receipt_no": ["Purchase Receipt", ["purchase_receipt_details"]]
|
"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
|
@ -312,6 +312,23 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEquals(actual_qty_1 - 5, actual_qty_2)
|
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 = [
|
test_records = [
|
||||||
[
|
[
|
||||||
|
Loading…
x
Reference in New Issue
Block a user