diff --git a/erpnext/__version__.py b/erpnext/__version__.py index 4898e44e7b9..7b6f237255a 100644 --- a/erpnext/__version__.py +++ b/erpnext/__version__.py @@ -1,2 +1,2 @@ from __future__ import unicode_literals -__version__ = '4.24.2' +__version__ = '4.24.3' diff --git a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json b/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json index e3a658e5c7a..42e37fd6c75 100644 --- a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json +++ b/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json @@ -6,12 +6,12 @@ "doctype": "Report", "idx": 1, "is_standard": "Yes", - "modified": "2014-06-03 07:18:17.244501", + "modified": "2015-03-26 11:00:48.720037", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Order Items To Be Billed", "owner": "Administrator", - "query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n `tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order Item`.`project_name` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.base_amount as \"Amount:Currency:100\",\n\t`tabPurchase Order Item`.billed_amt as \"Billed Amount:Currency:100\", \n\t(`tabPurchase Order Item`.base_amount - ifnull(`tabPurchase Order Item`.billed_amt, 0)) as \"Amount to Bill:Currency:100\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status != \"Stopped\"\n\tand ifnull(`tabPurchase Order Item`.billed_amt, 0) < ifnull(`tabPurchase Order Item`.base_amount, 0)\norder by `tabPurchase Order`.transaction_date asc", + "query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n `tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order Item`.`project_name` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.base_amount as \"Amount:Currency:100\",\n\t(`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1)) as \"Billed Amount:Currency:100\", \n\t(`tabPurchase Order Item`.base_amount - (ifnull(`tabPurchase Order Item`.billed_amt, 0) * ifnull(`tabPurchase Order`.conversion_rate, 1))) as \"Amount to Bill:Currency:100\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status != \"Stopped\"\n\tand (ifnull(`tabPurchase Order Item`.billed_amt, 0) * ifnull(`tabPurchase Order`.conversion_rate, 1)) < ifnull(`tabPurchase Order Item`.base_amount, 0)\norder by `tabPurchase Order`.transaction_date asc", "ref_doctype": "Purchase Invoice", "report_name": "Purchase Order Items To Be Billed", "report_type": "Query Report" diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 7fed7367b1d..487f406f77a 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -197,9 +197,10 @@ class StockController(AccountsController): sl_dict.update(args) return sl_dict - def make_sl_entries(self, sl_entries, is_amended=None, allow_negative_stock=False): + def make_sl_entries(self, sl_entries, is_amended=None, allow_negative_stock=False, + via_landed_cost_voucher=False): from erpnext.stock.stock_ledger import make_sl_entries - make_sl_entries(sl_entries, is_amended, allow_negative_stock) + make_sl_entries(sl_entries, is_amended, allow_negative_stock, via_landed_cost_voucher) def make_gl_entries_on_cancel(self): if frappe.db.sql("""select name from `tabGL Entry` where voucher_type=%s diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 00c752b7b8a..ea136702aba 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -5,7 +5,7 @@ app_publisher = "Web Notes Technologies Pvt. Ltd. and Contributors" app_description = "Open Source Enterprise Resource Planning for Small and Midsized Organizations" app_icon = "icon-th" app_color = "#e74c3c" -app_version = "4.24.2" +app_version = "4.24.3" error_report_email = "support@erpnext.com" diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 1fb1e2d1672..242d4faad3e 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, allow_negative_stock=False): + def update_stock(self, args, allow_negative_stock=False, via_landed_cost_voucher=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) + }, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher) 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 16f0f1c82ef..d35fed856d5 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -6,6 +6,7 @@ import frappe from frappe import _ from frappe.utils import flt from frappe.model.document import Document +from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos class LandedCostVoucher(Document): def get_items_from_purchase_receipts(self): @@ -92,13 +93,25 @@ class LandedCostVoucher(Document): # save will update landed_cost_voucher_amount and voucher_amount in PR, # as those fields are allowed to edit after submit pr.save() + + # update latest valuation rate in serial no + self.update_rate_in_serial_no(pr) # update stock & gl entries for cancelled state of PR pr.docstatus = 2 - pr.update_stock_ledger(allow_negative_stock=True) + pr.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True) pr.make_gl_entries_on_cancel() + # update stock & gl entries for submit state of PR pr.docstatus = 1 - pr.update_stock_ledger() + pr.update_stock_ledger(via_landed_cost_voucher=True) pr.make_gl_entries() + + def update_rate_in_serial_no(self, purchase_receipt): + for item in purchase_receipt.get("purchase_receipt_details"): + if item.serial_no: + serial_nos = get_serial_nos(item.serial_no) + if serial_nos: + frappe.db.sql("update `tabSerial No` set purchase_rate=%s where name in ({0})" + .format(", ".join(["%s"]*len(serial_nos))), tuple([item.valuation_rate] + serial_nos)) \ No newline at end of file diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index abc08598290..448581c31be 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, allow_negative_stock=False): + def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False): sl_entries = [] stock_items = self.get_stock_items() @@ -154,7 +154,8 @@ class PurchaseReceipt(BuyingController): })) self.bk_flush_supp_wh(sl_entries) - self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock) + self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock, + via_landed_cost_voucher=via_landed_cost_voucher) def update_ordered_qty(self): po_map = {} diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index b5de2554f98..4d6e6b04854 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -27,8 +27,9 @@ class StockLedgerEntry(Document): self.check_stock_frozen_date() self.actual_amt_check() - from erpnext.stock.doctype.serial_no.serial_no import process_serial_no - process_serial_no(self) + if not self.get("via_landed_cost_voucher"): + from erpnext.stock.doctype.serial_no.serial_no import process_serial_no + process_serial_no(self) #check for item quantity available in stock def actual_amt_check(self): diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 97a1f82157b..65469456705 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, allow_negative_stock=False): +def make_sl_entries(sl_entries, is_amended=None, allow_negative_stock=False, via_landed_cost_voucher=False): if sl_entries: from erpnext.stock.utils import update_bin @@ -28,14 +28,14 @@ def make_sl_entries(sl_entries, is_amended=None, allow_negative_stock=False): sle['actual_qty'] = -flt(sle['actual_qty']) if sle.get("actual_qty") or sle.get("voucher_type")=="Stock Reconciliation": - sle_id = make_entry(sle, allow_negative_stock) + sle_id = make_entry(sle, allow_negative_stock, via_landed_cost_voucher) args = sle.copy() args.update({ "sle_id": sle_id, "is_amended": is_amended }) - update_bin(args, allow_negative_stock) + update_bin(args, allow_negative_stock, via_landed_cost_voucher) if cancel: delete_cancelled_entry(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no')) @@ -46,11 +46,12 @@ def set_as_cancel(voucher_type, voucher_no): where voucher_no=%s and voucher_type=%s""", (now(), frappe.session.user, voucher_type, voucher_no)) -def make_entry(args, allow_negative_stock=False): +def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False): args.update({"doctype": "Stock Ledger Entry"}) sle = frappe.get_doc(args) sle.ignore_permissions = 1 sle.allow_negative_stock=allow_negative_stock + sle.via_landed_cost_voucher = via_landed_cost_voucher sle.insert() sle.submit() return sle.name @@ -59,7 +60,9 @@ 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, allow_negative_stock=False, verbose=1): +def update_entries_after(args, allow_zero_rate=False, allow_negative_stock=False, + via_landed_cost_voucher=False, verbose=1): + """ update valution rate and qty after transaction from the current time-bucket onwards @@ -91,7 +94,7 @@ def update_entries_after(args, allow_zero_rate=False, allow_negative_stock=False stock_value_difference = 0.0 for sle in entries_to_fix: - if sle.serial_no or not allow_negative_stock: + if (sle.serial_no and not via_landed_cost_voucher) 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 b5638c85b90..37326b7d3e4 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -53,11 +53,11 @@ def get_bin(item_code, warehouse): bin_obj.ignore_permissions = True return bin_obj -def update_bin(args, allow_negative_stock=False): +def update_bin(args, allow_negative_stock=False, via_landed_cost_voucher=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, allow_negative_stock) + bin.update_stock(args, allow_negative_stock, via_landed_cost_voucher) return bin else: frappe.msgprint(_("Item {0} ignored since it is not a stock item").format(args.get("item_code"))) diff --git a/setup.py b/setup.py index 5b1dab73b96..217cc84d070 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages import os -version = "4.24.2" +version = "4.24.3" with open("requirements.txt", "r") as f: install_requires = f.readlines()