From 3f671ea60f29a2544914fdfa8a12149e209f1b78 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 21 Jan 2015 14:07:15 +0530 Subject: [PATCH 1/3] Landed Cost Voucher: Add field and fixed reposting issue --- erpnext/patches.txt | 1 + .../v4_2/update_landed_cost_voucher.py | 10 +++ .../landed_cost_taxes_and_charges.json | 13 +-- .../landed_cost_voucher.js | 89 +++++++++---------- .../landed_cost_voucher.json | 45 ++++++++-- .../landed_cost_voucher.py | 21 ++--- 6 files changed, 100 insertions(+), 79 deletions(-) create mode 100644 erpnext/patches/v4_2/update_landed_cost_voucher.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 7fda960356e..2773d69d78c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -91,3 +91,4 @@ erpnext.patches.v4_2.update_requested_and_ordered_qty execute:frappe.delete_doc("DocType", "Contact Control") erpnext.patches.v4_2.recalculate_bom_costs erpnext.patches.v4_2.discount_amount +erpnext.patches.v4_2.update_landed_cost_voucher diff --git a/erpnext/patches/v4_2/update_landed_cost_voucher.py b/erpnext/patches/v4_2/update_landed_cost_voucher.py new file mode 100644 index 00000000000..6563b7b534a --- /dev/null +++ b/erpnext/patches/v4_2/update_landed_cost_voucher.py @@ -0,0 +1,10 @@ +# 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 frappe + +def execute(): + frappe.reload_doc("stock", "doctype", "landed_cost_voucher") + frappe.db.sql("""update `tabLanded Cost Voucher` set distribute_charges_based_on = 'Amount' + where docstatus=1""") diff --git a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json index f183b331a25..9ea9150fda0 100644 --- a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json +++ b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json @@ -18,17 +18,6 @@ "permlevel": 0, "width": "50%" }, - { - "fieldname": "account", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Account", - "options": "Account", - "permlevel": 0, - "print_hide": 1, - "read_only": 0, - "reqd": 1 - }, { "fieldname": "amount", "fieldtype": "Currency", @@ -40,7 +29,7 @@ } ], "istable": 1, - "modified": "2014-08-08 13:12:02.594698", + "modified": "2015-01-21 11:51:33.964438", "modified_by": "Administrator", "module": "Stock", "name": "Landed Cost Taxes and Charges", diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js index 042011a9911..ea469f04117 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js @@ -5,10 +5,10 @@ frappe.provide("erpnext.stock"); frappe.require("assets/erpnext/js/controllers/stock_controller.js"); -erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ +erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ setup: function() { var me = this; - this.frm.fields_dict.landed_cost_purchase_receipts.grid.get_field('purchase_receipt').get_query = + this.frm.fields_dict.landed_cost_purchase_receipts.grid.get_field('purchase_receipt').get_query = function() { if(!me.frm.doc.company) msgprint(__("Please enter company first")); return { @@ -18,53 +18,44 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ ] } }; - - this.frm.fields_dict.landed_cost_taxes_and_charges.grid.get_field('account').get_query = function() { - if(!me.frm.doc.company) msgprint(__("Please enter company first")); - return { - filters:[ - ['Account', 'group_or_ledger', '=', 'Ledger'], - ['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Expense Account']], - ['Account', 'company', '=', me.frm.doc.company] - ] - } - }; - + this.frm.add_fetch("purchase_receipt", "supplier", "supplier"); this.frm.add_fetch("purchase_receipt", "posting_date", "posting_date"); this.frm.add_fetch("purchase_receipt", "grand_total", "grand_total"); - - }, - + + }, + refresh: function() { - var help_content = ['', - '', - '
', - '

', - __('Notes'), - ':

', - '
    ', - '
  • ', - __("Charges will be distributed proportionately based on item amount"), - '
  • ', - '
  • ', - __("Remove item if charges is not applicable to that item"), - '
  • ', - '
  • ', - __("Charges are updated in Purchase Receipt against each item"), - '
  • ', - '
  • ', - __("Item valuation rate is recalculated considering landed cost voucher amount"), - '
  • ', - '
  • ', - __("Stock Ledger Entries and GL Entries are reposted for the selected Purchase Receipts"), - '
  • ', - '
', - '
'].join("\n"); + var help_content = [ + '

', + '', + '', + '
', + '

', + __('Notes'), + ':

', + '
    ', + '
  • ', + __("Charges will be distributed proportionately based on item qty or amount, as per your selection"), + '
  • ', + '
  • ', + __("Remove item if charges is not applicable to that item"), + '
  • ', + '
  • ', + __("Charges are updated in Purchase Receipt against each item"), + '
  • ', + '
  • ', + __("Item valuation rate is recalculated considering landed cost voucher amount"), + '
  • ', + '
  • ', + __("Stock Ledger Entries and GL Entries are reposted for the selected Purchase Receipts"), + '
  • ', + '
', + '
'].join("\n"); set_field_options("landed_cost_help", help_content); }, - + get_items_from_purchase_receipts: function() { var me = this; if(!this.frm.doc.landed_cost_purchase_receipts.length) { @@ -75,13 +66,13 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ method: "get_items_from_purchase_receipts" }); } - }, - + }, + amount: function() { this.set_total_taxes_and_charges(); this.set_applicable_charges_for_item(); }, - + set_total_taxes_and_charges: function() { total_taxes_and_charges = 0.0; $.each(this.frm.doc.landed_cost_taxes_and_charges, function(i, d) { @@ -89,7 +80,7 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ }); cur_frm.set_value("total_taxes_and_charges", total_taxes_and_charges); }, - + set_applicable_charges_for_item: function() { var me = this; if(this.frm.doc.landed_cost_taxes_and_charges.length) { @@ -97,14 +88,14 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ $.each(this.frm.doc.landed_cost_items, function(i, d) { total_item_cost += flt(d.amount) }); - + $.each(this.frm.doc.landed_cost_items, function(i, item) { item.applicable_charges = flt(item.amount) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost) }); refresh_field("landed_cost_items"); } } - + }); -cur_frm.script_manager.make(erpnext.stock.LandedCostVoucher); \ No newline at end of file +cur_frm.script_manager.make(erpnext.stock.LandedCostVoucher); diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json index 682a16bfd13..3425d9dc712 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json @@ -44,6 +44,13 @@ "options": "Landed Cost Taxes and Charges", "permlevel": 0 }, + { + "fieldname": "sec_break1", + "fieldtype": "Section Break", + "options": "Simple", + "permlevel": 0, + "precision": "" + }, { "fieldname": "total_taxes_and_charges", "fieldtype": "Currency", @@ -53,13 +60,6 @@ "read_only": 1, "reqd": 1 }, - { - "fieldname": "landed_cost_help", - "fieldtype": "HTML", - "label": "Landed Cost Help", - "options": "", - "permlevel": 0 - }, { "fieldname": "amended_from", "fieldtype": "Link", @@ -69,11 +69,40 @@ "permlevel": 0, "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "col_break1", + "fieldtype": "Column Break", + "permlevel": 0, + "precision": "" + }, + { + "default": "Amount", + "fieldname": "distribute_charges_based_on", + "fieldtype": "Select", + "label": "Distribute Charges Based On", + "options": "\nQty\nAmount", + "permlevel": 0, + "precision": "", + "reqd": 1 + }, + { + "fieldname": "sec_break2", + "fieldtype": "Section Break", + "permlevel": 0, + "precision": "" + }, + { + "fieldname": "landed_cost_help", + "fieldtype": "HTML", + "label": "Landed Cost Help", + "options": "", + "permlevel": 0 } ], "icon": "icon-usd", "is_submittable": 1, - "modified": "2014-09-01 12:05:46.834513", + "modified": "2015-01-21 11:56:37.698326", "modified_by": "Administrator", "module": "Stock", "name": "Landed Cost Voucher", diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index 3046c5e9211..7bafcf60584 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -7,9 +7,6 @@ from frappe import _ from frappe.utils import flt from frappe.model.document import Document -from erpnext.stock.utils import get_valuation_method -from erpnext.stock.stock_ledger import get_previous_sle - class LandedCostVoucher(Document): def get_items_from_purchase_receipts(self): self.set("landed_cost_items", []) @@ -69,10 +66,11 @@ class LandedCostVoucher(Document): self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("landed_cost_taxes_and_charges")]) def set_applicable_charges_for_item(self): - total_item_cost = sum([flt(d.amount) for d in self.get("landed_cost_items")]) + based_on = self.distribute_charges_based_on.lower() + total = sum([flt(d.get(based_on)) for d in self.get("landed_cost_items")]) for item in self.get("landed_cost_items"): - item.applicable_charges = flt(item.amount) * flt(self.total_taxes_and_charges) / flt(total_item_cost) + item.applicable_charges = flt(item.get(based_on)) * flt(self.total_taxes_and_charges) / flt(total) def on_submit(self): self.update_landed_cost() @@ -92,13 +90,16 @@ class LandedCostVoucher(Document): pr.update_valuation_rate("purchase_receipt_details") # save will update landed_cost_voucher_amount and voucher_amount in PR, - # as those fields are ellowed to edit after submit + # as those fields are allowed to edit after submit pr.save() - # update stock & gl entries for cancelled state of PR - pr.docstatus = 2 - pr.update_stock_ledger() - pr.make_gl_entries_on_cancel() + # delete stock ledger entries & gl entries for cancelled state of PR + + frappe.db.sql("""delete from `tabStock Ledger Entry` + where voucher_type='Purchase Receipt' and voucher_no=%s""", pr.name) + + frappe.db.sql("""delete from `tabGL Entry` + where voucher_type='Purchase Receipt' and voucher_no=%s""", pr.name) # update stock & gl entries for submit state of PR pr.docstatus = 1 From ea61046e8dc8616fc765487847659a142bdc1f2c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 21 Jan 2015 14:07:30 +0530 Subject: [PATCH 2/3] Updated Quotation Status --- erpnext/selling/doctype/quotation/quotation.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 9af5029e799..9029670b3ab 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -723,7 +723,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "Draft\nSubmitted\nOrdered\nLost\nCancelled", + "options": "Draft\nSubmitted\nOrdered\nLost\nCancelled\nOpen\nReplied", "permlevel": 0, "print_hide": 1, "read_only": 1, @@ -842,7 +842,7 @@ "idx": 1, "is_submittable": 1, "max_attachments": 1, - "modified": "2015-01-12 16:57:14.706270", + "modified": "2015-01-21 11:24:08.210880", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", From 9c47efb5920b6a79f4feef81f6ba818434d03699 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 21 Jan 2015 16:22:45 +0530 Subject: [PATCH 3/3] Landed cost voucher: allow negative stock while doing cancellation entry for purchase receipts --- erpnext/controllers/stock_controller.py | 4 ++-- erpnext/stock/doctype/bin/bin.py | 4 ++-- .../landed_cost_voucher/landed_cost_voucher.py | 11 ++++------- .../doctype/purchase_receipt/purchase_receipt.py | 4 ++-- erpnext/stock/stock_ledger.py | 11 +++++++---- erpnext/stock/utils.py | 4 ++-- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 15355753ffb..7fed7367b1d 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -197,9 +197,9 @@ class StockController(AccountsController): sl_dict.update(args) return sl_dict - def make_sl_entries(self, sl_entries, is_amended=None): + def make_sl_entries(self, sl_entries, is_amended=None, allow_negative_stock=False): from erpnext.stock.stock_ledger import make_sl_entries - make_sl_entries(sl_entries, is_amended) + make_sl_entries(sl_entries, is_amended, allow_negative_stock) def make_gl_entries_on_cancel(self): if frappe.db.sql("""select name from `tabGL Entry` where voucher_type=%s diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index e3269e87e2c..1fb1e2d1672 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -23,7 +23,7 @@ class Bin(Document): if (not getattr(self, f, None)) or (not self.get(f)): self.set(f, 0.0) - def update_stock(self, args): + def update_stock(self, args, allow_negative_stock=False): self.update_qty(args) if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation": @@ -38,7 +38,7 @@ class Bin(Document): "warehouse": self.warehouse, "posting_date": args.get("posting_date"), "posting_time": args.get("posting_time") - }) + }, allow_negative_stock=allow_negative_stock) def update_qty(self, args): # update the stock values (for current quantities) diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index 7bafcf60584..16f0f1c82ef 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -93,13 +93,10 @@ class LandedCostVoucher(Document): # as those fields are allowed to edit after submit pr.save() - # delete stock ledger entries & gl entries for cancelled state of PR - - frappe.db.sql("""delete from `tabStock Ledger Entry` - where voucher_type='Purchase Receipt' and voucher_no=%s""", pr.name) - - frappe.db.sql("""delete from `tabGL Entry` - where voucher_type='Purchase Receipt' and voucher_no=%s""", pr.name) + # update stock & gl entries for cancelled state of PR + pr.docstatus = 2 + pr.update_stock_ledger(allow_negative_stock=True) + pr.make_gl_entries_on_cancel() # update stock & gl entries for submit state of PR pr.docstatus = 1 diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index f38ee5d5deb..e04abbb2bc7 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -129,7 +129,7 @@ class PurchaseReceipt(BuyingController): if not d.prevdoc_docname: frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code)) - def update_stock_ledger(self): + def update_stock_ledger(self, allow_negative_stock=False): sl_entries = [] stock_items = self.get_stock_items() @@ -153,7 +153,7 @@ class PurchaseReceipt(BuyingController): })) self.bk_flush_supp_wh(sl_entries) - self.make_sl_entries(sl_entries) + self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock) def update_ordered_qty(self): po_map = {} diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index eae1bf68bf7..7bbf8fc3c5e 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -14,7 +14,7 @@ class NegativeStockError(frappe.ValidationError): pass _exceptions = frappe.local('stockledger_exceptions') # _exceptions = [] -def make_sl_entries(sl_entries, is_amended=None): +def make_sl_entries(sl_entries, is_amended=None, allow_negative_stock=False): if sl_entries: from erpnext.stock.utils import update_bin @@ -35,7 +35,7 @@ def make_sl_entries(sl_entries, is_amended=None): "sle_id": sle_id, "is_amended": is_amended }) - update_bin(args) + update_bin(args, allow_negative_stock) if cancel: delete_cancelled_entry(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no')) @@ -58,7 +58,7 @@ def delete_cancelled_entry(voucher_type, voucher_no): frappe.db.sql("""delete from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no)) -def update_entries_after(args, allow_zero_rate=False, verbose=1): +def update_entries_after(args, allow_zero_rate=False, allow_negative_stock=False, verbose=1): """ update valution rate and qty after transaction from the current time-bucket onwards @@ -73,6 +73,9 @@ def update_entries_after(args, allow_zero_rate=False, verbose=1): if not _exceptions: frappe.local.stockledger_exceptions = [] + if not allow_negative_stock: + allow_negative_stock = cint(frappe.db.get_default("allow_negative_stock")) + previous_sle = get_sle_before_datetime(args) qty_after_transaction = flt(previous_sle.get("qty_after_transaction")) @@ -87,7 +90,7 @@ def update_entries_after(args, allow_zero_rate=False, verbose=1): stock_value_difference = 0.0 for sle in entries_to_fix: - if sle.serial_no or not cint(frappe.db.get_default("allow_negative_stock")): + if sle.serial_no or not allow_negative_stock: # validate negative stock for serialized items, fifo valuation # or when negative stock is not allowed for moving average if not validate_negative_stock(qty_after_transaction, sle): diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 889c30ced34..c08ed7da965 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -52,11 +52,11 @@ def get_bin(item_code, warehouse): bin_obj.ignore_permissions = True return bin_obj -def update_bin(args): +def update_bin(args, allow_negative_stock=False): is_stock_item = frappe.db.get_value('Item', args.get("item_code"), 'is_stock_item') if is_stock_item == 'Yes': bin = get_bin(args.get("item_code"), args.get("warehouse")) - bin.update_stock(args) + bin.update_stock(args, allow_negative_stock) return bin else: frappe.msgprint(_("Item {0} ignored since it is not a stock item").format(args.get("item_code")))