From 4436157da14c02596115854dbbfe0daa780c8b92 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Sat, 12 Jul 2014 23:24:10 +0530 Subject: [PATCH 01/22] Landed cost redesign initial commit #1921 --- .../landed_cost_item/landed_cost_item.json | 85 ++++++++++++---- .../landed_cost_purchase_receipt.json | 40 +++++++- .../landed_cost_taxes_and_charges/__init__.py | 0 .../landed_cost_taxes_and_charges.json | 51 ++++++++++ .../landed_cost_taxes_and_charges.py | 9 ++ .../doctype/landed_cost_voucher/__init__.py | 0 .../landed_cost_voucher.js | 82 ++++++++++++++++ .../landed_cost_voucher.json | 97 +++++++++++++++++++ .../landed_cost_voucher.py | 83 ++++++++++++++++ 9 files changed, 426 insertions(+), 21 deletions(-) create mode 100644 erpnext/stock/doctype/landed_cost_taxes_and_charges/__init__.py create mode 100644 erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json create mode 100644 erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.py create mode 100644 erpnext/stock/doctype/landed_cost_voucher/__init__.py create mode 100644 erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js create mode 100644 erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json create mode 100644 erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py diff --git a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json index f761ebabb1..c5887d47f4 100644 --- a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json +++ b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json @@ -1,27 +1,18 @@ { - "creation": "2013-02-22 01:28:02.000000", + "creation": "2013-02-22 01:28:02", "docstatus": 0, "doctype": "DocType", "fields": [ { - "fieldname": "account_head", + "fieldname": "item_code", "fieldtype": "Link", "in_list_view": 1, - "label": "Account Head", - "oldfieldname": "account_head", - "oldfieldtype": "Link", - "options": "Account", + "label": "Item Code", + "options": "Item", "permlevel": 0, + "read_only": 1, "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "options": "Cost Center", - "permlevel": 0, - "reqd": 1 + "width": "100px" }, { "fieldname": "description", @@ -32,8 +23,46 @@ "oldfieldtype": "Data", "permlevel": 0, "print_width": "300px", + "read_only": 1, "reqd": 1, - "width": "300px" + "width": "120px" + }, + { + "fieldname": "purchase_receipt", + "fieldtype": "Link", + "hidden": 0, + "label": "Purchase Receipt", + "options": "Purchase Receipt", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "hidden": 0, + "label": "Warehouse", + "options": "Warehouse", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "col_break2", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "fieldname": "qty", + "fieldtype": "Float", + "label": "Qty", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "rate", + "fieldtype": "Currency", + "label": "Rate", + "permlevel": 0, + "read_only": 1 }, { "fieldname": "amount", @@ -44,14 +73,34 @@ "oldfieldtype": "Currency", "options": "currency", "permlevel": 0, + "read_only": 1, "reqd": 1 + }, + { + "fieldname": "old_valuation_rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Old Valuation Rate", + "permlevel": 0, + "read_only": 1, + "width": "" + }, + { + "fieldname": "new_valuation_rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "New Valuation Rate", + "permlevel": 0, + "read_only": 1, + "width": "" } ], "idx": 1, "istable": 1, - "modified": "2013-12-20 19:23:18.000000", + "modified": "2014-07-11 15:20:53.714633", "modified_by": "Administrator", "module": "Stock", "name": "Landed Cost Item", - "owner": "wasim@webnotestech.com" + "owner": "wasim@webnotestech.com", + "permissions": [] } \ No newline at end of file diff --git a/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json b/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json index b50c148292..651f28834b 100644 --- a/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json +++ b/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json @@ -1,5 +1,5 @@ { - "creation": "2013-02-22 01:28:02.000000", + "creation": "2013-02-22 01:28:02", "docstatus": 0, "doctype": "DocType", "fields": [ @@ -13,14 +13,48 @@ "options": "Purchase Receipt", "permlevel": 0, "print_width": "220px", + "read_only": 0, + "reqd": 1, "width": "220px" + }, + { + "fieldname": "supplier", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Supplier", + "options": "Supplier", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "col_break1", + "fieldtype": "Column Break", + "permlevel": 0, + "width": "50%" + }, + { + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "permlevel": 0, + "read_only": 1 + }, + { + "fieldname": "grand_total", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Grand Total", + "permlevel": 0, + "read_only": 1 } ], "idx": 1, "istable": 1, - "modified": "2013-12-20 19:23:18.000000", + "modified": "2014-07-11 12:12:08.572263", "modified_by": "Administrator", "module": "Stock", "name": "Landed Cost Purchase Receipt", - "owner": "wasim@webnotestech.com" + "owner": "wasim@webnotestech.com", + "permissions": [] } \ No newline at end of file diff --git a/erpnext/stock/doctype/landed_cost_taxes_and_charges/__init__.py b/erpnext/stock/doctype/landed_cost_taxes_and_charges/__init__.py new file mode 100644 index 0000000000..e69de29bb2 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 new file mode 100644 index 0000000000..d077dbc31d --- /dev/null +++ b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json @@ -0,0 +1,51 @@ +{ + "creation": "2014-07-11 11:51:00.453717", + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "fieldname": "description", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Description", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "col_break3", + "fieldtype": "Column Break", + "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", + "in_list_view": 1, + "label": "Amount", + "permlevel": 0, + "reqd": 1 + } + ], + "istable": 1, + "modified": "2014-07-11 13:00:14.770284", + "modified_by": "Administrator", + "module": "Stock", + "name": "Landed Cost Taxes and Charges", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.py b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.py new file mode 100644 index 0000000000..2e1f5a31f3 --- /dev/null +++ b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.py @@ -0,0 +1,9 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class LandedCostTaxesandCharges(Document): + pass \ No newline at end of file diff --git a/erpnext/stock/doctype/landed_cost_voucher/__init__.py b/erpnext/stock/doctype/landed_cost_voucher/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js new file mode 100644 index 0000000000..47ba0e1c40 --- /dev/null +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js @@ -0,0 +1,82 @@ +// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + + +frappe.provide("erpnext.stock"); +frappe.require("assets/erpnext/js/controllers/stock_controller.js"); + +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 = + function() { + if(!me.frm.doc.company) msgprint(__("Please enter company first")); + return { + filters:[ + ['Purchase Receipt', 'docstatus', '=', '1'], + ['Purchase Receipt', 'company', '=', me.frm.doc.company], + ] + } + }; + + 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']], + ['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"); + + }, + + get_items_from_purchase_receipts: function() { + var me = this; + if(!this.frm.doc.landed_cost_purchase_receipts.length) { + msgprint(__("Please enter Purchase Receipt first")); + } else { + return this.frm.call({ + doc: me.frm.doc, + method: "get_items_from_purchase_receipts" + }); + } + }, + + amount: function() { + this.set_total_taxes_and_charges(); + this.set_new_valuation_rate(); + }, + + set_total_taxes_and_charges: function() { + total_taxes_and_charges = 0.0; + $.each(this.frm.doc.landed_cost_taxes_and_charges, function(i, d) { + total_taxes_and_charges += flt(d.amount) + }); + cur_frm.set_value("total_taxes_and_charges", total_taxes_and_charges); + }, + + set_new_valuation_rate: function() { + var me = this; + if(this.frm.doc.landed_cost_taxes_and_charges.length) { + var total_item_cost = 0.0; + $.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) { + var charges_for_item = flt(item.amount) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost) + item.new_valuation_rate = flt(item.old_valuation_rate) + charges_for_item + }); + refresh_field("landed_cost_items"); + } + } + +}); + +cur_frm.script_manager.make(erpnext.stock.LandedCostVoucher); \ No newline at end of file diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json new file mode 100644 index 0000000000..11425c27b7 --- /dev/null +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json @@ -0,0 +1,97 @@ +{ + "allow_import": 0, + "autoname": "LCV.####", + "creation": "2014-07-11 11:33:42.547339", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Transaction", + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "permlevel": 0, + "reqd": 1 + }, + { + "fieldname": "landed_cost_purchase_receipts", + "fieldtype": "Table", + "label": "Purchase Receipts", + "options": "Landed Cost Purchase Receipt", + "permlevel": 0 + }, + { + "fieldname": "get_items_from_purchase_receipts", + "fieldtype": "Button", + "label": "Get Items From Purchase Receipts", + "permlevel": 0 + }, + { + "fieldname": "landed_cost_items", + "fieldtype": "Table", + "label": "Purchase Receipt Items", + "no_copy": 1, + "options": "Landed Cost Item", + "permlevel": 0, + "read_only": 0 + }, + { + "fieldname": "landed_cost_taxes_and_charges", + "fieldtype": "Table", + "label": "Taxes and Charges", + "options": "Landed Cost Taxes and Charges", + "permlevel": 0 + }, + { + "fieldname": "total_taxes_and_charges", + "fieldtype": "Currency", + "label": "Total Taxes and Charges", + "permlevel": 0, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "landed_cost_help", + "fieldtype": "HTML", + "label": "Landed Cost Help", + "options": "", + "permlevel": 0 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Landed Cost Voucher", + "permlevel": 0, + "print_hide": 1, + "read_only": 1 + } + ], + "is_submittable": 1, + "modified": "2014-07-11 15:34:51.306164", + "modified_by": "Administrator", + "module": "Stock", + "name": "Landed Cost Voucher", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "export": 1, + "permlevel": 0, + "read": 1, + "report": 1, + "role": "Material Manager", + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py new file mode 100644 index 0000000000..7ebdb0be24 --- /dev/null +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -0,0 +1,83 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.utils import flt +from frappe.model.document import Document + +class LandedCostVoucher(Document): + def get_items_from_purchase_receipts(self): + self.set("landed_cost_items", []) + for pr in self.get("landed_cost_purchase_receipts"): + pr_items = frappe.db.sql("""select item_code, description, qty, rate, amount, valuation_rate, warehouse + from `tabPurchase Receipt Item` where parent = %s""", pr.purchase_receipt, as_dict=True) + for d in pr_items: + item = self.append("landed_cost_items") + item.item_code = d.item_code + item.description = d.description + item.qty = d.qty + item.rate = d.rate + item.amount = d.amount + item.old_valuation_rate = d.valuation_rate + item.purchase_receipt = pr.purchase_receipt + item.warehouse = d.warehouse + + if self.total_taxes_and_charges: + self.set_new_valuation_rate() + + def validate(self): + self.check_mandatory() + self.validate_purchase_receipts() + self.set_total_taxes_and_charges() + if not self.get("landed_cost_items"): + self.get_items_from_purchase_receipts() + else: + self.set_new_valuation_rate() + + def check_mandatory(self): + if not self.get("landed_cost_purchase_receipts"): + frappe.throw(_("Please enter Purchase Receipts")) + + if not self.get("landed_cost_taxes_and_charges"): + frappe.throw(_("Please enter Taxes and Charges")) + + def validate_purchase_receipts(self): + purchase_receipts = [] + for d in self.get("landed_cost_purchase_receipts"): + if frappe.db.get_value("Purchase Receipt", d.purchase_receipt, "docstatus") != 1: + frappe.throw(_("Purchase Receipt must be submitted")) + else: + purchase_receipts.append(d.purchase_receipt) + + for item in self.get("landed_cost_items"): + if not item.purchase_receipt: + frappe.throw(_("Item must be added using 'Get Items from Purchase Receipts' button")) + elif item.purchase_receipt not in purchase_receipts: + frappe.throw(_("Item Row {0}: Purchase Reciept {1} does not exist in above 'Purchase Receipts' table") + .format(item.idx, item.purchase_receipt)) + + def set_total_taxes_and_charges(self): + total_taxes_and_charges = 0.0 + for d in self.get("landed_cost_taxes_and_charges"): + total_taxes_and_charges += flt(d.amount) + + self.total_taxes_and_charges = total_taxes_and_charges + + def set_new_valuation_rate(self): + total_item_cost = sum([flt(d.amount) for d in self.get("landed_cost_items")]) + + for item in self.get("landed_cost_items"): + charges_for_item = flt(item.amount) * flt(self.total_taxes_and_charges) / flt(total_item_cost) + item.new_valuation_rate = flt(item.old_valuation_rate) + charges_for_item + + def on_submit(self): + self.make_stock_ledger_entries() + self.make_gl_entries() + + def make_stock_ledger_entries(self): + pass + + def make_gl_entries(self): + pass \ No newline at end of file From f6ea21c8aed752c8533c5de41f34b62e86f3bdcb Mon Sep 17 00:00:00 2001 From: nabinhait Date: Wed, 16 Jul 2014 14:39:35 +0530 Subject: [PATCH 02/22] landed cost voucher design changed #1921 --- .../landed_cost_item/landed_cost_item.json | 31 +++++--------- .../landed_cost_voucher.js | 7 ++-- .../landed_cost_voucher.py | 40 +++++++++---------- 3 files changed, 31 insertions(+), 47 deletions(-) diff --git a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json index c5887d47f4..3b77d0f18d 100644 --- a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json +++ b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json @@ -36,15 +36,6 @@ "permlevel": 0, "read_only": 1 }, - { - "fieldname": "warehouse", - "fieldtype": "Link", - "hidden": 0, - "label": "Warehouse", - "options": "Warehouse", - "permlevel": 0, - "read_only": 1 - }, { "fieldname": "col_break2", "fieldtype": "Column Break", @@ -53,6 +44,7 @@ { "fieldname": "qty", "fieldtype": "Float", + "in_list_view": 1, "label": "Qty", "permlevel": 0, "read_only": 1 @@ -60,6 +52,7 @@ { "fieldname": "rate", "fieldtype": "Currency", + "in_list_view": 0, "label": "Rate", "permlevel": 0, "read_only": 1 @@ -77,27 +70,23 @@ "reqd": 1 }, { - "fieldname": "old_valuation_rate", + "fieldname": "applicable_charges", "fieldtype": "Currency", "in_list_view": 1, - "label": "Old Valuation Rate", + "label": "Applicable Charges", "permlevel": 0, - "read_only": 1, - "width": "" + "read_only": 1 }, { - "fieldname": "new_valuation_rate", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "New Valuation Rate", - "permlevel": 0, - "read_only": 1, - "width": "" + "fieldname": "pr_item_row_id", + "fieldtype": "Data", + "label": "PR Item Row Id", + "permlevel": 0 } ], "idx": 1, "istable": 1, - "modified": "2014-07-11 15:20:53.714633", + "modified": "2014-07-15 18:01:26.152422", "modified_by": "Administrator", "module": "Stock", "name": "Landed Cost Item", 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 47ba0e1c40..207b12b910 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js @@ -50,7 +50,7 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ amount: function() { this.set_total_taxes_and_charges(); - this.set_new_valuation_rate(); + this.set_applicable_charges_for_item(); }, set_total_taxes_and_charges: function() { @@ -61,7 +61,7 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ cur_frm.set_value("total_taxes_and_charges", total_taxes_and_charges); }, - set_new_valuation_rate: function() { + set_applicable_charges_for_item: function() { var me = this; if(this.frm.doc.landed_cost_taxes_and_charges.length) { var total_item_cost = 0.0; @@ -70,8 +70,7 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ }); $.each(this.frm.doc.landed_cost_items, function(i, item) { - var charges_for_item = flt(item.amount) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost) - item.new_valuation_rate = flt(item.old_valuation_rate) + charges_for_item + item.applicable_charges = flt(item.amount) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost) }); refresh_field("landed_cost_items"); } 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 7ebdb0be24..79e25b3749 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -7,11 +7,15 @@ 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", []) for pr in self.get("landed_cost_purchase_receipts"): - pr_items = frappe.db.sql("""select item_code, description, qty, rate, amount, valuation_rate, warehouse + pr_items = frappe.db.sql("""select pr_tem.item_code, pr_tem.description, + pr_tem.qty, pr_tem.rate, pr_tem.amount, pr_tem.name from `tabPurchase Receipt Item` where parent = %s""", pr.purchase_receipt, as_dict=True) for d in pr_items: item = self.append("landed_cost_items") @@ -20,12 +24,12 @@ class LandedCostVoucher(Document): item.qty = d.qty item.rate = d.rate item.amount = d.amount - item.old_valuation_rate = d.valuation_rate item.purchase_receipt = pr.purchase_receipt - item.warehouse = d.warehouse + item.pr_item_row_id = d.name + + if self.get("landed_cost_taxes_and_charges"): + self.set_applicable_charges_for_item() - if self.total_taxes_and_charges: - self.set_new_valuation_rate() def validate(self): self.check_mandatory() @@ -34,7 +38,7 @@ class LandedCostVoucher(Document): if not self.get("landed_cost_items"): self.get_items_from_purchase_receipts() else: - self.set_new_valuation_rate() + self.set_applicable_charges_for_item() def check_mandatory(self): if not self.get("landed_cost_purchase_receipts"): @@ -55,29 +59,21 @@ class LandedCostVoucher(Document): if not item.purchase_receipt: frappe.throw(_("Item must be added using 'Get Items from Purchase Receipts' button")) elif item.purchase_receipt not in purchase_receipts: - frappe.throw(_("Item Row {0}: Purchase Reciept {1} does not exist in above 'Purchase Receipts' table") + frappe.throw(_("Item Row {0}: Purchase Receipt {1} does not exist in above 'Purchase Receipts' table") .format(item.idx, item.purchase_receipt)) def set_total_taxes_and_charges(self): - total_taxes_and_charges = 0.0 - for d in self.get("landed_cost_taxes_and_charges"): - total_taxes_and_charges += flt(d.amount) - - self.total_taxes_and_charges = total_taxes_and_charges + self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("landed_cost_taxes_and_charges")]) - def set_new_valuation_rate(self): + def set_applicable_charges_for_item(self): total_item_cost = sum([flt(d.amount) for d in self.get("landed_cost_items")]) for item in self.get("landed_cost_items"): - charges_for_item = flt(item.amount) * flt(self.total_taxes_and_charges) / flt(total_item_cost) - item.new_valuation_rate = flt(item.old_valuation_rate) + charges_for_item + item.applicable_charges = flt(item.amount) * flt(self.total_taxes_and_charges) / flt(total_item_cost) def on_submit(self): - self.make_stock_ledger_entries() - self.make_gl_entries() + purchase_receipts = list(set([d.purchase_receipt for d in self.get("landed_cost_items")])) - def make_stock_ledger_entries(self): - pass - - def make_gl_entries(self): - pass \ No newline at end of file + for purchase_receipt in purchase_receipts: + pr = frappe.get_doc("Purchase Receipt", purchase_receipt) + \ No newline at end of file From 87f2401c1e8c921e1c8afcd371c0b29c32e9e55b Mon Sep 17 00:00:00 2001 From: nabinhait Date: Wed, 16 Jul 2014 19:48:29 +0530 Subject: [PATCH 03/22] Landed Cost on_submit and gl entries for PR --- .../purchase_invoice/purchase_invoice.py | 2 +- .../doctype/sales_invoice/sales_invoice.py | 2 +- erpnext/controllers/buying_controller.py | 10 +- erpnext/controllers/stock_controller.py | 2 +- .../doctype/delivery_note/delivery_note.py | 2 +- .../landed_cost_voucher.py | 16 ++- .../purchase_receipt/purchase_receipt.py | 97 +++++++++++++++++-- .../purchase_receipt_item.json | 7 ++ .../stock/doctype/stock_entry/stock_entry.py | 2 +- .../stock_reconciliation.py | 2 +- 10 files changed, 124 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 55e3247636..3b6ad82cdc 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -387,7 +387,7 @@ class PurchaseInvoice(BuyingController): self.update_prevdoc_status() self.update_billing_status_for_zero_amount_refdoc("Purchase Order") - self.make_cancel_gl_entries() + self.make_gl_entries_on_cancel() def on_update(self): pass diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 1205646cbf..481ae098b3 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -121,7 +121,7 @@ class SalesInvoice(SellingController): self.update_prevdoc_status() self.update_billing_status_for_zero_amount_refdoc("Sales Order") - self.make_cancel_gl_entries() + self.make_gl_entries_on_cancel() def update_status_updater_args(self): if cint(self.update_stock): diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 85d7a9ee60..9f2b78e022 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -193,9 +193,13 @@ class BuyingController(StockController): "UOM Conversion Detail", {"parent": item.item_code, "uom": item.uom}, "conversion_factor")) or 1 qty_in_stock_uom = flt(item.qty * item.conversion_factor) - rm_supp_cost = item.rm_supp_cost if self.doctype=="Purchase Receipt" else 0.0 - item.valuation_rate = ((item.base_amount + item.item_tax_amount + rm_supp_cost) - / qty_in_stock_uom) + rm_supp_cost = flt(item.rm_supp_cost) if self.doctype=="Purchase Receipt" else 0.0 + + landed_cost_voucher_amount = flt(item.landed_cost_voucher_amount) \ + if self.doctype == "Purchase Receipt" else 0.0 + + item.valuation_rate = ((item.base_amount + item.item_tax_amount + rm_supp_cost + + landed_cost_voucher_amount) / qty_in_stock_uom) else: item.valuation_rate = 0.0 diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 27437a396a..a1d9a5b00d 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -274,7 +274,7 @@ class StockController(AccountsController): from erpnext.stock.stock_ledger import make_sl_entries make_sl_entries(sl_entries, is_amended) - def make_cancel_gl_entries(self): + def make_gl_entries_on_cancel(self): if frappe.db.sql("""select name from `tabGL Entry` where voucher_type=%s and voucher_no=%s""", (self.doctype, self.name)): self.make_gl_entries() diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index e831c47fe7..864329d3ea 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -196,7 +196,7 @@ class DeliveryNote(SellingController): frappe.db.set(self, 'status', 'Cancelled') self.cancel_packing_slips() - self.make_cancel_gl_entries() + self.make_gl_entries_on_cancel() def validate_packed_qty(self): """ 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 79e25b3749..9653242cfa 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -16,7 +16,10 @@ class LandedCostVoucher(Document): for pr in self.get("landed_cost_purchase_receipts"): pr_items = frappe.db.sql("""select pr_tem.item_code, pr_tem.description, pr_tem.qty, pr_tem.rate, pr_tem.amount, pr_tem.name - from `tabPurchase Receipt Item` where parent = %s""", pr.purchase_receipt, as_dict=True) + from `tabPurchase Receipt Item` pr_item where parent = %s + and (select name form tabItem where name = pr_item.item_code and is_stock_item = 'Yes')""", + pr.purchase_receipt, as_dict=True) + for d in pr_items: item = self.append("landed_cost_items") item.item_code = d.item_code @@ -73,7 +76,14 @@ class LandedCostVoucher(Document): def on_submit(self): purchase_receipts = list(set([d.purchase_receipt for d in self.get("landed_cost_items")])) - + self.delete_sle_and_gle(purchase_receipts) for purchase_receipt in purchase_receipts: pr = frappe.get_doc("Purchase Receipt", purchase_receipt) - \ No newline at end of file + pr.update_valuation_rate() + pr.update_stock() + pr.make_gl_entries() + + def delete_sle_and_gle(self, purchase_receipts): + for doctype in ["Stock Ledger Entry", "GL Entry"]: + frappe.db.sql("""delete from `tab%s` where voucher_type='Purchase Receipt' + and voucher_no in (%s)""" % (doctype, ', '.join(['%s']*len(purchase_receipts))), purchase_receipts) \ 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 5f56149607..d627386e61 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -67,9 +67,16 @@ class PurchaseReceipt(BuyingController): # sub-contracting self.validate_for_subcontracting() self.create_raw_materials_supplied("pr_raw_material_details") - + self.set_landed_cost_voucher_amount() self.update_valuation_rate("purchase_receipt_details") - + + def set_landed_cost_voucher_amount(self, voucher_detail): + for d in self.get("purchase_receipt_details"): + lc_voucher_amount = frappe.db.sql("""select sum(ifnull(applicable_charges)) + from `tabLanded Cost Item` + where docstatus = 1 and pr_item_row_id = %s""", voucher_detail) + d.landed_cost_voucher_amount = lc_voucher_amount[0][0] if lc_voucher_amount else 0.0 + def validate_rejected_warehouse(self): for d in self.get("purchase_receipt_details"): if flt(d.rejected_qty) and not d.rejected_warehouse: @@ -265,7 +272,7 @@ class PurchaseReceipt(BuyingController): self.update_prevdoc_status() pc_obj.update_last_purchase_rate(self, 0) - self.make_cancel_gl_entries() + self.make_gl_entries_on_cancel() def get_current_stock(self): for d in self.get('pr_raw_material_details'): @@ -277,10 +284,88 @@ class PurchaseReceipt(BuyingController): return frappe.get_doc('Purchase Common').get_rate(arg,self) def get_gl_entries(self, warehouse_account=None): - against_stock_account = self.get_company_default("stock_received_but_not_billed") + from erpnext.accounts.general_ledger import process_gl_map + + stock_rbnb = self.get_company_default("stock_received_but_not_billed") + expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") + default_cost_center = self.get_company_default("cost_center") + against_expense_account = None + + gl_entries = [] + warehouse_with_no_account = [] + stock_items = self.get_stock_items() + for d in self.get("purchase_receipt_details"): + if d.item_code in stock_items and flt(d.valuation_rate): + if warehouse_account.get(d.warehouse) or warehouse_account.get(d.rejected_warehouse): + self.check_expense_account(d) + + # warehouse account + if flt(d.qty): + gl_list.append(self.get_gl_dict({ + "account": warehouse_account[d.warehouse], + "against": against_expense_account, + "cost_center": default_cost_center, + "remarks": self.get("remarks") or "Accounting Entry for Stock", + "debit": flt(d.valuation_rate) * flt(d.qty) * flt(d.conversion_factor) + })) + + # rejected warehouse + if flt(d.rejected_qty): + gl_list.append(self.get_gl_dict({ + "account": warehouse_account[d.rejected_warehouse], + "against": against_expense_account, + "cost_center": default_cost_center, + "remarks": self.get("remarks") or "Accounting Entry for Stock", + "debit": flt(d.valuation_rate) * flt(d.rejected_qty) * flt(d.conversion_factor) + })) - gl_entries = super(PurchaseReceipt, self).get_gl_entries(warehouse_account, against_stock_account) - return gl_entries + # stock received but not billed + gl_list.append(self.get_gl_dict({ + "account": stock_rbnb, + "against": warehouse_account[d.warehouse], + "cost_center": default_cost_center, + "remarks": self.get("remarks") or "Accounting Entry for Stock", + "credit": flt(d.base_amount, 2) + })) + + if flt(d.landed_cost_voucher_amount): + gl_list.append(self.get_gl_dict({ + "account": expenses_included_in_valuation, + "against": warehouse_account[d.warehouse], + "cost_center": default_cost_center, + "remarks": self.get("remarks") or "Accounting Entry for Stock", + "credit": flt(d.landed_cost_voucher_amount) + })) + + elif d.warehouse not in warehouse_with_no_account or \ + d.rejected_warehouse not in warehouse_with_no_account: + warehouse_with_no_account.append(d.warehouse) + + if warehouse_with_no_account: + msgprint(_("No accounting entries for the following warehouses") + ": \n" + + "\n".join(warehouse_with_no_account)) + + valuation_tax = {} + for tax in self.get("other_charges"): + if tax.category in ("Valuation", "Valuation and Total") and flt(tax.tax_amount): + if not tax.cost_center: + frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category))) + valuation_tax.setdefault(tax.cost_center, 0) + valuation_tax[tax.cost_center] += \ + (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount) + + for cost_center, amount in valuation_tax.items(): + gl_entries.append( + self.get_gl_dict({ + "account": expenses_included_in_valuation, + "cost_center": cost_center, + # "against": , + "credit": amount, + "remarks": self.remarks or "Accounting Entry for Stock" + }) + ) + + return process_gl_map(gl_entries) @frappe.whitelist() diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 8c5e73297b..76d62945b2 100755 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -501,6 +501,13 @@ "search_index": 0, "width": "150px" }, + { + "fieldname": "landed_cost_voucher_amount", + "fieldtype": "Currency", + "label": "Landed Cost Voucher Amount", + "permlevel": 0, + "read_only": 1 + }, { "fieldname": "valuation_rate", "fieldtype": "Currency", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 1da2ad53d0..bdd760ba4a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -65,7 +65,7 @@ class StockEntry(StockController): def on_cancel(self): self.update_stock_ledger() self.update_production_order() - self.make_cancel_gl_entries() + self.make_gl_entries_on_cancel() def validate_fiscal_year(self): from erpnext.accounts.utils import validate_fiscal_year diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index bb1e3e2873..8b32211177 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -27,7 +27,7 @@ class StockReconciliation(StockController): def on_cancel(self): self.delete_and_repost_sle() - self.make_cancel_gl_entries() + self.make_gl_entries_on_cancel() def validate_data(self): if not self.reconciliation_json: From cc0692d71458f010fd871c734c6bcc4d8909f39d Mon Sep 17 00:00:00 2001 From: nabinhait Date: Thu, 17 Jul 2014 19:16:38 +0530 Subject: [PATCH 04/22] landed cost fix --- erpnext/controllers/buying_controller.py | 2 +- .../landed_cost_voucher.py | 18 ++++++++--- .../purchase_receipt/purchase_receipt.py | 32 ++++++++++--------- .../purchase_receipt_item.json | 2 ++ 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 9f2b78e022..8bc0c9d576 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -194,7 +194,7 @@ class BuyingController(StockController): "conversion_factor")) or 1 qty_in_stock_uom = flt(item.qty * item.conversion_factor) rm_supp_cost = flt(item.rm_supp_cost) if self.doctype=="Purchase Receipt" else 0.0 - + landed_cost_voucher_amount = flt(item.landed_cost_voucher_amount) \ if self.doctype == "Purchase Receipt" else 0.0 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 9653242cfa..dd3fd9070c 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -14,12 +14,12 @@ class LandedCostVoucher(Document): def get_items_from_purchase_receipts(self): self.set("landed_cost_items", []) for pr in self.get("landed_cost_purchase_receipts"): - pr_items = frappe.db.sql("""select pr_tem.item_code, pr_tem.description, - pr_tem.qty, pr_tem.rate, pr_tem.amount, pr_tem.name + pr_items = frappe.db.sql("""select pr_item.item_code, pr_item.description, + pr_item.qty, pr_item.rate, pr_item.amount, pr_item.name from `tabPurchase Receipt Item` pr_item where parent = %s - and (select name form tabItem where name = pr_item.item_code and is_stock_item = 'Yes')""", + and exists(select name from tabItem where name = pr_item.item_code and is_stock_item = 'Yes')""", pr.purchase_receipt, as_dict=True) - + for d in pr_items: item = self.append("landed_cost_items") item.item_code = d.item_code @@ -75,11 +75,19 @@ class LandedCostVoucher(Document): item.applicable_charges = flt(item.amount) * flt(self.total_taxes_and_charges) / flt(total_item_cost) def on_submit(self): + self.update_landed_cost() + + def on_cancel(self): + self.update_landed_cost() + + def update_landed_cost(self): purchase_receipts = list(set([d.purchase_receipt for d in self.get("landed_cost_items")])) self.delete_sle_and_gle(purchase_receipts) for purchase_receipt in purchase_receipts: pr = frappe.get_doc("Purchase Receipt", purchase_receipt) - pr.update_valuation_rate() + pr.set_landed_cost_voucher_amount() + pr.update_valuation_rate("purchase_receipt_details") + pr.save() pr.update_stock() pr.make_gl_entries() diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index d627386e61..adcc255c34 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -70,11 +70,11 @@ class PurchaseReceipt(BuyingController): self.set_landed_cost_voucher_amount() self.update_valuation_rate("purchase_receipt_details") - def set_landed_cost_voucher_amount(self, voucher_detail): + def set_landed_cost_voucher_amount(self): for d in self.get("purchase_receipt_details"): - lc_voucher_amount = frappe.db.sql("""select sum(ifnull(applicable_charges)) + lc_voucher_amount = frappe.db.sql("""select sum(ifnull(applicable_charges, 0)) from `tabLanded Cost Item` - where docstatus = 1 and pr_item_row_id = %s""", voucher_detail) + where docstatus = 1 and pr_item_row_id = %s""", d.name) d.landed_cost_voucher_amount = lc_voucher_amount[0][0] if lc_voucher_amount else 0.0 def validate_rejected_warehouse(self): @@ -297,42 +297,41 @@ class PurchaseReceipt(BuyingController): for d in self.get("purchase_receipt_details"): if d.item_code in stock_items and flt(d.valuation_rate): if warehouse_account.get(d.warehouse) or warehouse_account.get(d.rejected_warehouse): - self.check_expense_account(d) - + # warehouse account if flt(d.qty): - gl_list.append(self.get_gl_dict({ + gl_entries.append(self.get_gl_dict({ "account": warehouse_account[d.warehouse], "against": against_expense_account, - "cost_center": default_cost_center, + "cost_center": d.cost_center, "remarks": self.get("remarks") or "Accounting Entry for Stock", "debit": flt(d.valuation_rate) * flt(d.qty) * flt(d.conversion_factor) })) # rejected warehouse if flt(d.rejected_qty): - gl_list.append(self.get_gl_dict({ + gl_entries.append(self.get_gl_dict({ "account": warehouse_account[d.rejected_warehouse], "against": against_expense_account, - "cost_center": default_cost_center, + "cost_center": d.cost_center, "remarks": self.get("remarks") or "Accounting Entry for Stock", "debit": flt(d.valuation_rate) * flt(d.rejected_qty) * flt(d.conversion_factor) })) # stock received but not billed - gl_list.append(self.get_gl_dict({ + gl_entries.append(self.get_gl_dict({ "account": stock_rbnb, "against": warehouse_account[d.warehouse], - "cost_center": default_cost_center, + "cost_center": d.cost_center, "remarks": self.get("remarks") or "Accounting Entry for Stock", "credit": flt(d.base_amount, 2) })) if flt(d.landed_cost_voucher_amount): - gl_list.append(self.get_gl_dict({ + gl_entries.append(self.get_gl_dict({ "account": expenses_included_in_valuation, "against": warehouse_account[d.warehouse], - "cost_center": default_cost_center, + "cost_center": d.cost_center, "remarks": self.get("remarks") or "Accounting Entry for Stock", "credit": flt(d.landed_cost_voucher_amount) })) @@ -353,7 +352,10 @@ class PurchaseReceipt(BuyingController): valuation_tax.setdefault(tax.cost_center, 0) valuation_tax[tax.cost_center] += \ (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount) - + + if frappe.db.get_value("Purchase Invoice Item", {"purchase_receipt": self.name, "docstatus": 1}): + expenses_included_in_valuation = stock_rbnb + for cost_center, amount in valuation_tax.items(): gl_entries.append( self.get_gl_dict({ @@ -364,7 +366,7 @@ class PurchaseReceipt(BuyingController): "remarks": self.remarks or "Accounting Entry for Stock" }) ) - + return process_gl_map(gl_entries) diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 76d62945b2..3b45ca2756 100755 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -502,6 +502,7 @@ "width": "150px" }, { + "allow_on_submit": 1, "fieldname": "landed_cost_voucher_amount", "fieldtype": "Currency", "label": "Landed Cost Voucher Amount", @@ -509,6 +510,7 @@ "read_only": 1 }, { + "allow_on_submit": 1, "fieldname": "valuation_rate", "fieldtype": "Currency", "hidden": 1, From 5e1b0014c250d0960b8ad31c4135ba1297149b74 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Mon, 21 Jul 2014 15:26:39 +0530 Subject: [PATCH 05/22] GL entries for sub-contracting and rejected qty --- .../purchase_receipt/purchase_receipt.py | 56 ++++++++++--------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index adcc255c34..57075a9ec6 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -150,7 +150,7 @@ class PurchaseReceipt(BuyingController): "warehouse": d.rejected_warehouse, "actual_qty": flt(d.rejected_qty) * flt(d.conversion_factor), "serial_no": cstr(d.rejected_serial_no).strip(), - "incoming_rate": d.valuation_rate + "incoming_rate": 0.0 })) self.bk_flush_supp_wh(sl_entries) @@ -296,27 +296,16 @@ class PurchaseReceipt(BuyingController): stock_items = self.get_stock_items() for d in self.get("purchase_receipt_details"): if d.item_code in stock_items and flt(d.valuation_rate): - if warehouse_account.get(d.warehouse) or warehouse_account.get(d.rejected_warehouse): + if warehouse_account.get(d.warehouse) and flt(d.qty) and flt(d.valuation_rate): # warehouse account - if flt(d.qty): - gl_entries.append(self.get_gl_dict({ - "account": warehouse_account[d.warehouse], - "against": against_expense_account, - "cost_center": d.cost_center, - "remarks": self.get("remarks") or "Accounting Entry for Stock", - "debit": flt(d.valuation_rate) * flt(d.qty) * flt(d.conversion_factor) - })) - - # rejected warehouse - if flt(d.rejected_qty): - gl_entries.append(self.get_gl_dict({ - "account": warehouse_account[d.rejected_warehouse], - "against": against_expense_account, - "cost_center": d.cost_center, - "remarks": self.get("remarks") or "Accounting Entry for Stock", - "debit": flt(d.valuation_rate) * flt(d.rejected_qty) * flt(d.conversion_factor) - })) + gl_entries.append(self.get_gl_dict({ + "account": warehouse_account[d.warehouse], + "against": against_expense_account, + "cost_center": d.cost_center, + "remarks": self.get("remarks") or "Accounting Entry for Stock", + "debit": flt(d.valuation_rate) * flt(d.qty) * flt(d.conversion_factor) + })) # stock received but not billed gl_entries.append(self.get_gl_dict({ @@ -327,6 +316,7 @@ class PurchaseReceipt(BuyingController): "credit": flt(d.base_amount, 2) })) + # Amount added through landed-cost-voucher if flt(d.landed_cost_voucher_amount): gl_entries.append(self.get_gl_dict({ "account": expenses_included_in_valuation, @@ -335,15 +325,22 @@ class PurchaseReceipt(BuyingController): "remarks": self.get("remarks") or "Accounting Entry for Stock", "credit": flt(d.landed_cost_voucher_amount) })) + + # sub-contracting warehouse + if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse): + gl_entries.append(self.get_gl_dict({ + "account": warehouse_account[self.supplier_warehouse], + "against": warehouse_account[d.warehouse], + "cost_center": d.cost_center, + "remarks": self.get("remarks") or "Accounting Entry for Stock", + "credit": flt(d.rm_supp_cost) + })) elif d.warehouse not in warehouse_with_no_account or \ d.rejected_warehouse not in warehouse_with_no_account: warehouse_with_no_account.append(d.warehouse) - - if warehouse_with_no_account: - msgprint(_("No accounting entries for the following warehouses") + ": \n" + - "\n".join(warehouse_with_no_account)) - + + # Cost center-wise amount breakup for other charges included for valuation valuation_tax = {} for tax in self.get("other_charges"): if tax.category in ("Valuation", "Valuation and Total") and flt(tax.tax_amount): @@ -353,9 +350,14 @@ class PurchaseReceipt(BuyingController): valuation_tax[tax.cost_center] += \ (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount) + # Backward compatibility: + # If PI exists and charges added via Landed Cost Voucher, + # post valuation related charges on "Stock Received But Not Billed" + # as that account has been debited in PI if frappe.db.get_value("Purchase Invoice Item", {"purchase_receipt": self.name, "docstatus": 1}): expenses_included_in_valuation = stock_rbnb + # Expense included in valuation for cost_center, amount in valuation_tax.items(): gl_entries.append( self.get_gl_dict({ @@ -367,6 +369,10 @@ class PurchaseReceipt(BuyingController): }) ) + if warehouse_with_no_account: + msgprint(_("No accounting entries for the following warehouses") + ": \n" + + "\n".join(warehouse_with_no_account)) + return process_gl_map(gl_entries) From b21b6598f1fa0236974dadf2bf29b4a9eff7288a Mon Sep 17 00:00:00 2001 From: nabinhait Date: Tue, 22 Jul 2014 17:48:10 +0530 Subject: [PATCH 06/22] Test case for landed cost voucher --- .../accounts/doctype/account/test_account.py | 1 + .../test_landed_cost_voucher.py | 68 +++++++++++++++++++ .../purchase_receipt/purchase_receipt.py | 34 +--------- 3 files changed, 71 insertions(+), 32 deletions(-) create mode 100644 erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py index 37746299e6..bb9102f1f6 100644 --- a/erpnext/accounts/doctype/account/test_account.py +++ b/erpnext/accounts/doctype/account/test_account.py @@ -14,6 +14,7 @@ def _make_test_records(verbose): ["_Test Account Stock Expenses", "Direct Expenses", "Group", None], ["_Test Account Shipping Charges", "_Test Account Stock Expenses", "Ledger", "Chargeable"], ["_Test Account Customs Duty", "_Test Account Stock Expenses", "Ledger", "Tax"], + ["_Test Account Insurance Charges", "_Test Account Stock Expenses", "Ledger", "Chargeable"], ["_Test Account Tax Assets", "Current Assets", "Group", None], diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py new file mode 100644 index 0000000000..6abf2f5d53 --- /dev/null +++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py @@ -0,0 +1,68 @@ +# 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 unittest +import frappe +from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \ + import set_perpetual_inventory, get_gl_entries, test_records as pr_test_records + + +class TestLandedCostVoucher(unittest.TestCase): + def test_landed_cost_voucher(self): + frappe.db.set_default("cost_center", "Main - _TC") + set_perpetual_inventory(1) + pr = self.submit_pr() + self.submit_landed_cost_voucher(pr) + + pr_lc_value = frappe.db.get_value("Purchase Receipt Item", {"parent": pr.name}, "landed_cost_voucher_amount") + self.assertEquals(pr_lc_value, 25.0) + + gl_entries = get_gl_entries("Purchase Receipt", pr.name) + + self.assertTrue(gl_entries) + + stock_in_hand_account = pr.get("purchase_receipt_details")[0].warehouse + fixed_asset_account = pr.get("purchase_receipt_details")[1].warehouse + + + expected_values = { + stock_in_hand_account: [400.0, 0.0], + fixed_asset_account: [400.0, 0.0], + "Stock Received But Not Billed - _TC": [0.0, 750.0], + "Expenses Included In Valuation - _TC": [0.0, 50.0] + } + + for gle in gl_entries: + self.assertEquals(expected_values[gle.account][0], gle.debit) + self.assertEquals(expected_values[gle.account][1], gle.credit) + + set_perpetual_inventory(0) + + def submit_landed_cost_voucher(self, pr): + lcv = frappe.new_doc("Landed Cost Voucher") + lcv.company = "_Test Company" + lcv.set("landed_cost_purchase_receipts", [{ + "purchase_receipt": pr.name, + "supplier": pr.supplier, + "posting_date": pr.posting_date, + "grand_total": pr.grand_total + }]) + lcv.set("landed_cost_taxes_and_charges", [{ + "description": "Insurance Charges", + "account": "_Test Account Insurance Charges - _TC", + "amount": 50.0 + }]) + + lcv.insert() + lcv.submit() + + def submit_pr(self): + pr = frappe.copy_doc(pr_test_records[0]) + pr.submit() + return pr + + + +test_records = frappe.get_test_records('Landed Cost Voucher') \ 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 57075a9ec6..0166de5a40 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -289,7 +289,6 @@ class PurchaseReceipt(BuyingController): stock_rbnb = self.get_company_default("stock_received_but_not_billed") expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") default_cost_center = self.get_company_default("cost_center") - against_expense_account = None gl_entries = [] warehouse_with_no_account = [] @@ -301,7 +300,7 @@ class PurchaseReceipt(BuyingController): # warehouse account gl_entries.append(self.get_gl_dict({ "account": warehouse_account[d.warehouse], - "against": against_expense_account, + "against": stock_rbnb, "cost_center": d.cost_center, "remarks": self.get("remarks") or "Accounting Entry for Stock", "debit": flt(d.valuation_rate) * flt(d.qty) * flt(d.conversion_factor) @@ -313,7 +312,7 @@ class PurchaseReceipt(BuyingController): "against": warehouse_account[d.warehouse], "cost_center": d.cost_center, "remarks": self.get("remarks") or "Accounting Entry for Stock", - "credit": flt(d.base_amount, 2) + "credit": flt(d.base_amount + d.item_tax_amount, self.precision("base_amount", d)) })) # Amount added through landed-cost-voucher @@ -339,35 +338,6 @@ class PurchaseReceipt(BuyingController): elif d.warehouse not in warehouse_with_no_account or \ d.rejected_warehouse not in warehouse_with_no_account: warehouse_with_no_account.append(d.warehouse) - - # Cost center-wise amount breakup for other charges included for valuation - valuation_tax = {} - for tax in self.get("other_charges"): - if tax.category in ("Valuation", "Valuation and Total") and flt(tax.tax_amount): - if not tax.cost_center: - frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category))) - valuation_tax.setdefault(tax.cost_center, 0) - valuation_tax[tax.cost_center] += \ - (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount) - - # Backward compatibility: - # If PI exists and charges added via Landed Cost Voucher, - # post valuation related charges on "Stock Received But Not Billed" - # as that account has been debited in PI - if frappe.db.get_value("Purchase Invoice Item", {"purchase_receipt": self.name, "docstatus": 1}): - expenses_included_in_valuation = stock_rbnb - - # Expense included in valuation - for cost_center, amount in valuation_tax.items(): - gl_entries.append( - self.get_gl_dict({ - "account": expenses_included_in_valuation, - "cost_center": cost_center, - # "against": , - "credit": amount, - "remarks": self.remarks or "Accounting Entry for Stock" - }) - ) if warehouse_with_no_account: msgprint(_("No accounting entries for the following warehouses") + ": \n" + From fe39442c48a1f331b50ce0d6eee1159092467ff4 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Tue, 22 Jul 2014 18:04:13 +0530 Subject: [PATCH 07/22] Removed old landed cost wizard --- erpnext/patches.txt | 1 + .../doctype/landed_cost_wizard/README.md | 1 - .../doctype/landed_cost_wizard/__init__.py | 1 - .../landed_cost_wizard/landed_cost_wizard.js | 46 --------- .../landed_cost_wizard.json | 83 ----------------- .../landed_cost_wizard/landed_cost_wizard.py | 93 ------------------- 6 files changed, 1 insertion(+), 224 deletions(-) delete mode 100644 erpnext/stock/doctype/landed_cost_wizard/README.md delete mode 100644 erpnext/stock/doctype/landed_cost_wizard/__init__.py delete mode 100644 erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.js delete mode 100644 erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.json delete mode 100644 erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1d4e91313a..1799e6b410 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -76,3 +76,4 @@ erpnext.patches.v4_2.toggle_rounded_total #2014-07-30 erpnext.patches.v4_2.fix_account_master_type erpnext.patches.v4_2.update_project_milestones erpnext.patches.v4_2.add_currency_turkish_lira #2014-08-08 +execute:frappe.delete_doc("DocType", "Landed Cost Wizard") diff --git a/erpnext/stock/doctype/landed_cost_wizard/README.md b/erpnext/stock/doctype/landed_cost_wizard/README.md deleted file mode 100644 index 0d2c20d93f..0000000000 --- a/erpnext/stock/doctype/landed_cost_wizard/README.md +++ /dev/null @@ -1 +0,0 @@ -Tool to distribute costs as part of Item value after the Item has been received. This is typically in case where bills related to Items are received much later and for multiple Item. (specially Custom Duty) \ No newline at end of file diff --git a/erpnext/stock/doctype/landed_cost_wizard/__init__.py b/erpnext/stock/doctype/landed_cost_wizard/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/stock/doctype/landed_cost_wizard/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.js b/erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.js deleted file mode 100644 index 38e8636f8e..0000000000 --- a/erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.js +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - - -frappe.provide("erpnext.stock"); -frappe.require("assets/erpnext/js/controllers/stock_controller.js"); - -erpnext.stock.LandedCostWizard = erpnext.stock.StockController.extend({ - setup: function() { - var me = this; - this.frm.fields_dict.lc_pr_details.grid.get_field('purchase_receipt').get_query = - function() { - if(!me.frm.doc.company) msgprint(__("Please enter company first")); - return { - filters:[ - ['Purchase Receipt', 'docstatus', '=', '1'], - ['Purchase Receipt', 'company', '=', me.frm.doc.company], - ] - } - }; - - this.frm.fields_dict.landed_cost_details.grid.get_field('account_head').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'], - ['Account', 'company', '=', me.frm.doc.company] - ] - } - }, - - this.frm.fields_dict.landed_cost_details.grid.get_field('cost_center').get_query = - function() { - if(!me.frm.doc.company) msgprint(__("Please enter company first")); - return { - filters:[ - ['Cost Center', 'group_or_ledger', '=', 'Ledger'], - ['Cost Center', 'company', '=', me.frm.doc.company] - ] - } - } - } -}); - -cur_frm.script_manager.make(erpnext.stock.LandedCostWizard); \ No newline at end of file diff --git a/erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.json b/erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.json deleted file mode 100644 index 59c9478125..0000000000 --- a/erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "creation": "2013-01-22 16:50:39.000000", - "docstatus": 0, - "doctype": "DocType", - "fields": [ - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "permlevel": 0, - "reqd": 1 - }, - { - "fieldname": "section_break0", - "fieldtype": "Section Break", - "label": "Select Purchase Receipts", - "options": "Simple", - "permlevel": 0 - }, - { - "fieldname": "lc_pr_details", - "fieldtype": "Table", - "label": "Landed Cost Purchase Receipts", - "options": "Landed Cost Purchase Receipt", - "permlevel": 0 - }, - { - "fieldname": "section_break1", - "fieldtype": "Section Break", - "label": "Add Taxes and Charges", - "permlevel": 0 - }, - { - "fieldname": "landed_cost_details", - "fieldtype": "Table", - "label": "Landed Cost Items", - "options": "Landed Cost Item", - "permlevel": 0 - }, - { - "fieldname": "update_landed_cost", - "fieldtype": "Button", - "label": "Update Landed Cost", - "options": "update_landed_cost", - "permlevel": 0 - } - ], - "icon": "icon-magic", - "idx": 1, - "issingle": 1, - "modified": "2013-12-20 19:23:18.000000", - "modified_by": "Administrator", - "module": "Stock", - "name": "Landed Cost Wizard", - "owner": "wasim@webnotestech.com", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Purchase Manager", - "submit": 0, - "write": 1 - }, - { - "create": 1, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Purchase User", - "submit": 0, - "write": 1 - } - ] -} \ No newline at end of file diff --git a/erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.py b/erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.py deleted file mode 100644 index c105ecbce4..0000000000 --- a/erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.py +++ /dev/null @@ -1,93 +0,0 @@ -# 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 -from frappe.utils import flt -from frappe import msgprint, _ - -from frappe.model.document import Document - -class LandedCostWizard(Document): - - def update_landed_cost(self): - """ - Add extra cost and recalculate all values in pr, - Recalculate valuation rate in all sle after pr posting date - """ - purchase_receipts = [row.purchase_receipt for row in - self.get("lc_pr_details")] - - self.validate_purchase_receipts(purchase_receipts) - self.cancel_pr(purchase_receipts) - self.add_charges_in_pr(purchase_receipts) - self.submit_pr(purchase_receipts) - msgprint(_("Landed Cost updated successfully")) - - def validate_purchase_receipts(self, purchase_receipts): - for pr in purchase_receipts: - if frappe.db.get_value("Purchase Receipt", pr, "docstatus") != 1: - frappe.throw(_("Purchase Receipt {0} is not submitted").format(pr)) - - def add_charges_in_pr(self, purchase_receipts): - """ Add additional charges in selected pr proportionately""" - total_amt = self.get_total_pr_amt(purchase_receipts) - - for pr in purchase_receipts: - pr_doc = frappe.get_doc('Purchase Receipt', pr) - pr_items = pr_doc.get("purchase_tax_details") - - for lc in self.get("landed_cost_details"): - amt = flt(lc.amount) * flt(pr_doc.net_total)/ flt(total_amt) - - matched_row = pr_doc.get("other_charges", { - "category": "Valuation", - "add_deduct_tax": "Add", - "charge_type": "Actual", - "account_head": lc.account_head - }) - - if not matched_row: # add if not exists - ch = pr_doc.append("other_charges") - ch.category = 'Valuation' - ch.add_deduct_tax = 'Add' - ch.charge_type = 'Actual' - ch.description = lc.description - ch.account_head = lc.account_head - ch.cost_center = lc.cost_center - ch.rate = amt - ch.tax_amount = amt - ch.docstatus = 1 - ch.db_insert() - else: # overwrite if exists - matched_row[0].rate = amt - matched_row[0].tax_amount = amt - matched_row[0].cost_center = lc.cost_center - - pr_doc.run_method("validate") - pr_doc._validate_mandatory() - for d in pr_doc.get_all_children(): - d.db_update() - - def get_total_pr_amt(self, purchase_receipts): - return frappe.db.sql("""SELECT SUM(net_total) FROM `tabPurchase Receipt` - WHERE name in (%s)""" % ', '.join(['%s']*len(purchase_receipts)), - tuple(purchase_receipts))[0][0] - - def cancel_pr(self, purchase_receipts): - for pr in purchase_receipts: - pr_doc = frappe.get_doc("Purchase Receipt", pr) - - pr_doc.run_method("update_ordered_qty") - - frappe.db.sql("""delete from `tabStock Ledger Entry` - where voucher_type='Purchase Receipt' and voucher_no=%s""", pr) - frappe.db.sql("""delete from `tabGL Entry` where voucher_type='Purchase Receipt' - and voucher_no=%s""", pr) - - def submit_pr(self, purchase_receipts): - for pr in purchase_receipts: - pr_doc = frappe.get_doc("Purchase Receipt", pr) - pr_doc.run_method("update_ordered_qty") - pr_doc.run_method("update_stock") - pr_doc.run_method("make_gl_entries") From e2c3f7ab14c3043891f62cbf9f75ca99b235bbcd Mon Sep 17 00:00:00 2001 From: nabinhait Date: Tue, 22 Jul 2014 18:16:21 +0530 Subject: [PATCH 08/22] Added help in landed cost voucher --- .../landed_cost_voucher.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) 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 207b12b910..6101074282 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js @@ -36,6 +36,35 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ }, + 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"); + + 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) { From 509aa52efc1a348b4ab01dd0ad0304ad6365b548 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Tue, 22 Jul 2014 18:23:06 +0530 Subject: [PATCH 09/22] minor fix --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 0166de5a40..4eb355351d 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -340,7 +340,7 @@ class PurchaseReceipt(BuyingController): warehouse_with_no_account.append(d.warehouse) if warehouse_with_no_account: - msgprint(_("No accounting entries for the following warehouses") + ": \n" + + frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" + "\n".join(warehouse_with_no_account)) return process_gl_map(gl_entries) From 11594c79272a51cac697e177dee9e1285c7add88 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Thu, 24 Jul 2014 10:39:23 +0530 Subject: [PATCH 10/22] purchase receipt gl entries --- .../purchase_invoice/purchase_invoice.py | 24 +++++++++---- .../purchase_receipt/purchase_receipt.py | 36 ++++++++++++++++++- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 3b6ad82cdc..27f2064bef 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -316,16 +316,25 @@ class PurchaseInvoice(BuyingController): stock_item_and_auto_accounting_for_stock = False stock_items = self.get_stock_items() for item in self.get("entries"): - if auto_accounting_for_stock and item.item_code in stock_items: - if flt(item.valuation_rate): + if flt(item.base_amount): + gl_entries.append( + self.get_gl_dict({ + "account": item.expense_account, + "against": self.credit_to, + "debit": item.base_amount, + "remarks": self.remarks, + "cost_center": item.cost_center + }) + ) + + if auto_accounting_for_stock and item.item_code in stock_items and item.valuation_rate: # if auto inventory accounting enabled and stock item, # then do stock related gl entries # expense will be booked in sales invoice stock_item_and_auto_accounting_for_stock = True - valuation_amt = flt(item.base_amount + item.item_tax_amount, - self.precision("base_amount", item)) - + + gl_entries.append( self.get_gl_dict({ "account": item.expense_account, @@ -352,7 +361,10 @@ class PurchaseInvoice(BuyingController): # this will balance out valuation amount included in cost of goods sold expenses_included_in_valuation = \ self.get_company_default("expenses_included_in_valuation") - + + # Backward compatibility: + # Post expenses_included_in_valuation only if it is not booked in Purchase Receipt + for cost_center, amount in valuation_tax.items(): gl_entries.append( self.get_gl_dict({ diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 4eb355351d..1f02f361b8 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -312,7 +312,7 @@ class PurchaseReceipt(BuyingController): "against": warehouse_account[d.warehouse], "cost_center": d.cost_center, "remarks": self.get("remarks") or "Accounting Entry for Stock", - "credit": flt(d.base_amount + d.item_tax_amount, self.precision("base_amount", d)) + "credit": flt(d.base_amount, self.precision("base_amount", d)) })) # Amount added through landed-cost-voucher @@ -338,6 +338,40 @@ class PurchaseReceipt(BuyingController): elif d.warehouse not in warehouse_with_no_account or \ d.rejected_warehouse not in warehouse_with_no_account: warehouse_with_no_account.append(d.warehouse) + + # Cost center-wise amount breakup for other charges included for valuation + valuation_tax = {} + for tax in self.get("other_charges"): + if tax.category in ("Valuation", "Valuation and Total") and flt(tax.tax_amount): + if not tax.cost_center: + frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category))) + valuation_tax.setdefault(tax.cost_center, 0) + valuation_tax[tax.cost_center] += \ + (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount) + + # Backward compatibility: + # If expenses_included_in_valuation account has been credited in against PI + # and charges added via Landed Cost Voucher, + # post valuation related charges on "Stock Received But Not Billed" + + pi_exists = frappe.db.sql("""select name from `tabPurchase Invoice Item` pi + where docstatus = 1 and purchase_receipt=%s + and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' + and voucher_no=pi.parent and account=%s)""", (self.name, expenses_included_in_valuation)) + + if pi_exists: + expenses_included_in_valuation = stock_rbnb + + # Expense included in valuation + for cost_center, amount in valuation_tax.items(): + gl_entries.append( + self.get_gl_dict({ + "account": expenses_included_in_valuation, + "cost_center": cost_center, + "credit": amount, + "remarks": self.remarks or "Accounting Entry for Stock" + }) + ) if warehouse_with_no_account: frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" + From f807cda375c0a238c694a702524c9f0281c0a846 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Thu, 24 Jul 2014 17:42:52 +0530 Subject: [PATCH 11/22] PR and PI gl entries fixes for landed cost --- .../purchase_invoice/purchase_invoice.py | 69 ++++++++++--------- .../purchase_invoice/test_purchase_invoice.py | 35 +++++++++- .../purchase_invoice/test_records.json | 2 + .../test_landed_cost_voucher.py | 5 +- .../purchase_receipt/purchase_receipt.py | 6 +- .../purchase_receipt/test_purchase_receipt.py | 4 +- .../purchase_receipt/test_records.json | 18 +++-- .../utilities/doctype/address/test_address.py | 3 + 8 files changed, 93 insertions(+), 49 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 27f2064bef..e465e2c973 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -273,6 +273,8 @@ class PurchaseInvoice(BuyingController): def make_gl_entries(self): auto_accounting_for_stock = \ cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) + + stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed") gl_entries = [] @@ -313,7 +315,7 @@ class PurchaseInvoice(BuyingController): (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount) # item gl entries - stock_item_and_auto_accounting_for_stock = False + negative_expense_to_be_booked = 0.0 stock_items = self.get_stock_items() for item in self.get("entries"): if flt(item.base_amount): @@ -327,54 +329,53 @@ class PurchaseInvoice(BuyingController): }) ) - if auto_accounting_for_stock and item.item_code in stock_items and item.valuation_rate: - # if auto inventory accounting enabled and stock item, - # then do stock related gl entries - # expense will be booked in sales invoice - stock_item_and_auto_accounting_for_stock = True - + if auto_accounting_for_stock and item.item_code in stock_items and item.item_tax_amount: + # Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt + stock_rbnb_booked_in_pr = None + if item.purchase_receipt: + stock_rbnb_booked_in_pr = frappe.db.sql("""select name from `tabGL Entry` + where voucher_type='Purchase Receipt' and voucher_no=%s and account=%s""", + (item.purchase_receipt, stock_received_but_not_billed)) - - gl_entries.append( - self.get_gl_dict({ - "account": item.expense_account, - "against": self.credit_to, - "debit": valuation_amt, - "remarks": self.remarks or "Accounting Entry for Stock" - }) - ) + if stock_rbnb_booked_in_pr: + gl_entries.append( + self.get_gl_dict({ + "account": stock_received_but_not_billed, + "against": self.credit_to, + "debit": flt(item.item_tax_amount), + "remarks": self.remarks or "Accounting Entry for Stock" + }) + ) + + negative_expense_to_be_booked += flt(item.item_tax_amount) - elif flt(item.base_amount): - # if not a stock item or auto inventory accounting disabled, book the expense - gl_entries.append( - self.get_gl_dict({ - "account": item.expense_account, - "against": self.credit_to, - "debit": item.base_amount, - "remarks": self.remarks, - "cost_center": item.cost_center - }) - ) - if stock_item_and_auto_accounting_for_stock and valuation_tax: + if negative_expense_to_be_booked and valuation_tax: # credit valuation tax amount in "Expenses Included In Valuation" # this will balance out valuation amount included in cost of goods sold - expenses_included_in_valuation = \ - self.get_company_default("expenses_included_in_valuation") - - # Backward compatibility: - # Post expenses_included_in_valuation only if it is not booked in Purchase Receipt + expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") + total_valuation_amount = sum(valuation_tax.values()) + amount_including_divisional_loss = negative_expense_to_be_booked + i = 1 for cost_center, amount in valuation_tax.items(): + if i == len(valuation_tax): + applicable_amount = amount_including_divisional_loss + else: + applicable_amount = negative_expense_to_be_booked * (amount / total_valuation_amount) + amount_including_divisional_loss -= applicable_amount + gl_entries.append( self.get_gl_dict({ "account": expenses_included_in_valuation, "cost_center": cost_center, "against": self.credit_to, - "credit": amount, + "credit": applicable_amount, "remarks": self.remarks or "Accounting Entry for Stock" }) ) + + i += 1 # writeoff account includes petty difference in the invoice amount # and the amount that is paid diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index ca73518bd9..5eb5f0103b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -9,7 +9,8 @@ import frappe.model import json from frappe.utils import cint import frappe.defaults -from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory +from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \ + test_records as pr_test_records test_dependencies = ["Item", "Cost Center"] test_ignore = ["Serial No"] @@ -54,6 +55,38 @@ class TestPurchaseInvoice(unittest.TestCase): order by account asc""", pi.name, as_dict=1) self.assertTrue(gl_entries) + expected_values = sorted([ + ["_Test Supplier - _TC", 0, 720], + ["Stock Received But Not Billed - _TC", 500.0, 0], + ["_Test Account Shipping Charges - _TC", 100.0, 0], + ["_Test Account VAT - _TC", 120.0, 0], + ]) + + for i, gle in enumerate(gl_entries): + self.assertEquals(expected_values[i][0], gle.account) + self.assertEquals(expected_values[i][1], gle.debit) + self.assertEquals(expected_values[i][2], gle.credit) + + set_perpetual_inventory(0) + + def test_gl_entries_with_auto_accounting_for_stock_against_pr(self): + set_perpetual_inventory(1) + self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1) + + pr = frappe.copy_doc(pr_test_records[0]) + pr.submit() + + pi = frappe.copy_doc(test_records[1]) + for d in pi.get("entries"): + d.purchase_receipt = pr.name + pi.insert() + pi.submit() + + gl_entries = frappe.db.sql("""select account, debit, credit + from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s + order by account asc""", pi.name, as_dict=1) + self.assertTrue(gl_entries) + expected_values = sorted([ ["_Test Supplier - _TC", 0, 720], ["Stock Received But Not Billed - _TC", 750.0, 0], diff --git a/erpnext/accounts/doctype/purchase_invoice/test_records.json b/erpnext/accounts/doctype/purchase_invoice/test_records.json index 48fb45d4d0..3ddbcc76e1 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_records.json +++ b/erpnext/accounts/doctype/purchase_invoice/test_records.json @@ -138,6 +138,7 @@ } ], "posting_date": "2013-02-03", + "supplier": "_Test Supplier", "supplier_name": "_Test Supplier" }, { @@ -201,6 +202,7 @@ } ], "posting_date": "2013-02-03", + "supplier": "_Test Supplier", "supplier_name": "_Test Supplier" } ] diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py index 6abf2f5d53..ca16b4493b 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py @@ -11,7 +11,6 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \ class TestLandedCostVoucher(unittest.TestCase): def test_landed_cost_voucher(self): - frappe.db.set_default("cost_center", "Main - _TC") set_perpetual_inventory(1) pr = self.submit_pr() self.submit_landed_cost_voucher(pr) @@ -30,8 +29,8 @@ class TestLandedCostVoucher(unittest.TestCase): expected_values = { stock_in_hand_account: [400.0, 0.0], fixed_asset_account: [400.0, 0.0], - "Stock Received But Not Billed - _TC": [0.0, 750.0], - "Expenses Included In Valuation - _TC": [0.0, 50.0] + "Stock Received But Not Billed - _TC": [0.0, 500.0], + "Expenses Included In Valuation - _TC": [0.0, 300.0] } for gle in gl_entries: diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 1f02f361b8..cb24c8e211 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -354,12 +354,12 @@ class PurchaseReceipt(BuyingController): # and charges added via Landed Cost Voucher, # post valuation related charges on "Stock Received But Not Billed" - pi_exists = frappe.db.sql("""select name from `tabPurchase Invoice Item` pi + stock_rbnb_booked_in_pi = frappe.db.sql("""select name from `tabPurchase Invoice Item` pi where docstatus = 1 and purchase_receipt=%s and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' - and voucher_no=pi.parent and account=%s)""", (self.name, expenses_included_in_valuation)) + and voucher_no=pi.parent and account=%s)""", (self.name, stock_rbnb)) - if pi_exists: + if stock_rbnb_booked_in_pi: expenses_included_in_valuation = stock_rbnb # Expense included in valuation diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 77de44d021..9b76a14381 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -55,7 +55,6 @@ class TestPurchaseReceipt(unittest.TestCase): set_perpetual_inventory() self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1) - pr = frappe.copy_doc(test_records[0]) pr.insert() pr.submit() @@ -72,7 +71,8 @@ class TestPurchaseReceipt(unittest.TestCase): expected_values = { stock_in_hand_account: [375.0, 0.0], fixed_asset_account: [375.0, 0.0], - "Stock Received But Not Billed - _TC": [0.0, 750.0] + "Stock Received But Not Billed - _TC": [0.0, 500.0], + "Expenses Included In Valuation - _TC": [0.0, 250.0] } for gle in gl_entries: diff --git a/erpnext/stock/doctype/purchase_receipt/test_records.json b/erpnext/stock/doctype/purchase_receipt/test_records.json index 7dd4f7f739..4b9b3aecf2 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_records.json +++ b/erpnext/stock/doctype/purchase_receipt/test_records.json @@ -19,7 +19,8 @@ "doctype": "Purchase Taxes and Charges", "parentfield": "other_charges", "rate": 100.0, - "tax_amount": 100.0 + "tax_amount": 100.0, + "cost_center": "Main - _TC" }, { "account_head": "_Test Account VAT - _TC", @@ -30,7 +31,8 @@ "doctype": "Purchase Taxes and Charges", "parentfield": "other_charges", "rate": 120.0, - "tax_amount": 120.0 + "tax_amount": 120.0, + "cost_center": "Main - _TC" }, { "account_head": "_Test Account Customs Duty - _TC", @@ -41,7 +43,8 @@ "doctype": "Purchase Taxes and Charges", "parentfield": "other_charges", "rate": 150.0, - "tax_amount": 150.0 + "tax_amount": 150.0, + "cost_center": "Main - _TC" } ], "posting_date": "2013-02-12", @@ -61,7 +64,8 @@ "rejected_qty": 0.0, "stock_uom": "Nos", "uom": "_Test UOM", - "warehouse": "_Test Warehouse - _TC" + "warehouse": "_Test Warehouse - _TC", + "cost_center": "Main - _TC" }, { "base_amount": 250.0, @@ -77,7 +81,8 @@ "rejected_qty": 0.0, "stock_uom": "Nos", "uom": "_Test UOM", - "warehouse": "_Test Warehouse 1 - _TC" + "warehouse": "_Test Warehouse 1 - _TC", + "cost_center": "Main - _TC" } ], "supplier": "_Test Supplier" @@ -109,7 +114,8 @@ "rejected_qty": 0.0, "stock_uom": "Nos", "uom": "_Test UOM", - "warehouse": "_Test Warehouse - _TC" + "warehouse": "_Test Warehouse - _TC", + "cost_center": "Main - _TC" } ], "supplier": "_Test Supplier", diff --git a/erpnext/utilities/doctype/address/test_address.py b/erpnext/utilities/doctype/address/test_address.py index 1e36a4438c..11f67b17e4 100644 --- a/erpnext/utilities/doctype/address/test_address.py +++ b/erpnext/utilities/doctype/address/test_address.py @@ -16,3 +16,6 @@ class TestAddress(unittest.TestCase): address = frappe.get_list("Address")[0].name display = get_address_display(frappe.get_doc("Address", address).as_dict()) self.assertTrue(display) + + +test_dependencies = ["Address Template"] From cfd1b10980be7fa37a5b665e748b755794c8447c Mon Sep 17 00:00:00 2001 From: nabinhait Date: Fri, 25 Jul 2014 12:02:36 +0530 Subject: [PATCH 12/22] Purchase receipt gl entries if there is warehouse without account --- .../sales_invoice/test_sales_invoice.py | 3 +- .../purchase_receipt/purchase_receipt.py | 58 ++++++++++++------- 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 1d22e0930a..ab361d83ad 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -502,7 +502,8 @@ class TestSalesInvoice(unittest.TestCase): "warehouse": "_Test Warehouse - _TC" }) - pos_setting.insert() + if not frappe.db.exists("POS Setting", "_Test POS Setting"): + pos_setting.insert() def test_si_gl_entry_with_aii_and_update_stock_with_warehouse_but_no_account(self): self.clear_stock_account_balance() diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index cb24c8e211..206d407890 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -292,11 +292,12 @@ class PurchaseReceipt(BuyingController): gl_entries = [] warehouse_with_no_account = [] + negative_expense_to_be_booked = 0.0 stock_items = self.get_stock_items() for d in self.get("purchase_receipt_details"): if d.item_code in stock_items and flt(d.valuation_rate): if warehouse_account.get(d.warehouse) and flt(d.qty) and flt(d.valuation_rate): - + # warehouse account gl_entries.append(self.get_gl_dict({ "account": warehouse_account[d.warehouse], @@ -315,6 +316,8 @@ class PurchaseReceipt(BuyingController): "credit": flt(d.base_amount, self.precision("base_amount", d)) })) + negative_expense_to_be_booked += flt(d.item_tax_amount) + # Amount added through landed-cost-voucher if flt(d.landed_cost_voucher_amount): gl_entries.append(self.get_gl_dict({ @@ -349,29 +352,42 @@ class PurchaseReceipt(BuyingController): valuation_tax[tax.cost_center] += \ (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount) - # Backward compatibility: - # If expenses_included_in_valuation account has been credited in against PI - # and charges added via Landed Cost Voucher, - # post valuation related charges on "Stock Received But Not Billed" + if negative_expense_to_be_booked and valuation_tax: + # Backward compatibility: + # If expenses_included_in_valuation account has been credited in against PI + # and charges added via Landed Cost Voucher, + # post valuation related charges on "Stock Received But Not Billed" - stock_rbnb_booked_in_pi = frappe.db.sql("""select name from `tabPurchase Invoice Item` pi - where docstatus = 1 and purchase_receipt=%s - and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' - and voucher_no=pi.parent and account=%s)""", (self.name, stock_rbnb)) + stock_rbnb_booked_in_pi = frappe.db.sql("""select name from `tabPurchase Invoice Item` pi + where docstatus = 1 and purchase_receipt=%s + and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' + and voucher_no=pi.parent and account=%s)""", (self.name, stock_rbnb)) - if stock_rbnb_booked_in_pi: - expenses_included_in_valuation = stock_rbnb + if stock_rbnb_booked_in_pi: + expenses_included_in_valuation = stock_rbnb - # Expense included in valuation - for cost_center, amount in valuation_tax.items(): - gl_entries.append( - self.get_gl_dict({ - "account": expenses_included_in_valuation, - "cost_center": cost_center, - "credit": amount, - "remarks": self.remarks or "Accounting Entry for Stock" - }) - ) + against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0]) + total_valuation_amount = sum(valuation_tax.values()) + amount_including_divisional_loss = negative_expense_to_be_booked + i = 1 + for cost_center, amount in valuation_tax.items(): + if i == len(valuation_tax): + applicable_amount = amount_including_divisional_loss + else: + applicable_amount = negative_expense_to_be_booked * (amount / total_valuation_amount) + amount_including_divisional_loss -= applicable_amount + + gl_entries.append( + self.get_gl_dict({ + "account": expenses_included_in_valuation, + "cost_center": cost_center, + "credit": applicable_amount, + "remarks": self.remarks or "Accounting Entry for Stock", + "against": against_account + }) + ) + + i += 1 if warehouse_with_no_account: frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" + From 98e7dcfe2c3707c9c5a190d529599b85096d51ae Mon Sep 17 00:00:00 2001 From: nabinhait Date: Tue, 29 Jul 2014 11:26:52 +0530 Subject: [PATCH 13/22] landed cost voucher minor fix --- .../stock/doctype/landed_cost_voucher/landed_cost_voucher.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6101074282..042011a991 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js @@ -24,7 +24,7 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ return { filters:[ ['Account', 'group_or_ledger', '=', 'Ledger'], - ['Account', 'account_type', 'in', ['Tax', 'Chargeable']], + ['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Expense Account']], ['Account', 'company', '=', me.frm.doc.company] ] } From 8ca3189f075bdb176ecf02961b164c29bb1b60a6 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Wed, 30 Jul 2014 14:24:24 +0530 Subject: [PATCH 14/22] pr and pi gl entries --- .../doctype/purchase_invoice/purchase_invoice.py | 12 ++++++------ .../doctype/purchase_receipt/purchase_receipt.py | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index e465e2c973..68a21d9117 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -275,7 +275,8 @@ class PurchaseInvoice(BuyingController): cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed") - + expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") + gl_entries = [] # parent's gl entry @@ -331,13 +332,13 @@ class PurchaseInvoice(BuyingController): if auto_accounting_for_stock and item.item_code in stock_items and item.item_tax_amount: # Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt - stock_rbnb_booked_in_pr = None + negative_expense_booked_in_pi = None if item.purchase_receipt: - stock_rbnb_booked_in_pr = frappe.db.sql("""select name from `tabGL Entry` + negative_expense_booked_in_pi = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Purchase Receipt' and voucher_no=%s and account=%s""", - (item.purchase_receipt, stock_received_but_not_billed)) + (item.purchase_receipt, expenses_included_in_valuation)) - if stock_rbnb_booked_in_pr: + if not negative_expense_booked_in_pi: gl_entries.append( self.get_gl_dict({ "account": stock_received_but_not_billed, @@ -353,7 +354,6 @@ class PurchaseInvoice(BuyingController): if negative_expense_to_be_booked and valuation_tax: # credit valuation tax amount in "Expenses Included In Valuation" # this will balance out valuation amount included in cost of goods sold - expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") total_valuation_amount = sum(valuation_tax.values()) amount_including_divisional_loss = negative_expense_to_be_booked diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 206d407890..12bb96de64 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -37,7 +37,7 @@ class PurchaseReceipt(BuyingController): def onload(self): billed_qty = frappe.db.sql("""select sum(ifnull(qty, 0)) from `tabPurchase Invoice Item` - where purchase_receipt=%s""", self.name) + where purchase_receipt=%s and docstatus=1""", self.name) if billed_qty: total_qty = sum((item.qty for item in self.get("purchase_receipt_details"))) self.get("__onload").billing_complete = (billed_qty[0][0] == total_qty) @@ -358,12 +358,12 @@ class PurchaseReceipt(BuyingController): # and charges added via Landed Cost Voucher, # post valuation related charges on "Stock Received But Not Billed" - stock_rbnb_booked_in_pi = frappe.db.sql("""select name from `tabPurchase Invoice Item` pi + negative_expense_booked_in_pi = frappe.db.sql("""select name from `tabPurchase Invoice Item` pi where docstatus = 1 and purchase_receipt=%s and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' - and voucher_no=pi.parent and account=%s)""", (self.name, stock_rbnb)) + and voucher_no=pi.parent and account=%s)""", (self.name, expenses_included_in_valuation)) - if stock_rbnb_booked_in_pi: + if negative_expense_booked_in_pi: expenses_included_in_valuation = stock_rbnb against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0]) From 9cbcf96aeb68db3c7d29a63b4d71acef2ccd5095 Mon Sep 17 00:00:00 2001 From: nabinhait Date: Wed, 30 Jul 2014 18:35:28 +0530 Subject: [PATCH 15/22] no copy property for landed cost voucher amount field --- .../doctype/purchase_receipt_item/purchase_receipt_item.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 3b45ca2756..549c572b57 100755 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -506,6 +506,7 @@ "fieldname": "landed_cost_voucher_amount", "fieldtype": "Currency", "label": "Landed Cost Voucher Amount", + "no_copy": 1, "permlevel": 0, "read_only": 1 }, @@ -554,7 +555,7 @@ ], "idx": 1, "istable": 1, - "modified": "2014-07-24 05:56:12.997533", + "modified": "2014-07-30 18:13:18.337564", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From f3e1181ad96750008eab8c00cf57dbff8b36feb6 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 8 Aug 2014 12:45:46 +0530 Subject: [PATCH 16/22] Landed cost voucher fixes --- .../landed_cost_voucher.py | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) 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 dd3fd9070c..7c2ae4fa04 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -14,12 +14,12 @@ class LandedCostVoucher(Document): def get_items_from_purchase_receipts(self): self.set("landed_cost_items", []) for pr in self.get("landed_cost_purchase_receipts"): - pr_items = frappe.db.sql("""select pr_item.item_code, pr_item.description, + pr_items = frappe.db.sql("""select pr_item.item_code, pr_item.description, pr_item.qty, pr_item.rate, pr_item.amount, pr_item.name - from `tabPurchase Receipt Item` pr_item where parent = %s - and exists(select name from tabItem where name = pr_item.item_code and is_stock_item = 'Yes')""", + from `tabPurchase Receipt Item` pr_item where parent = %s + and exists(select name from tabItem where name = pr_item.item_code and is_stock_item = 'Yes')""", pr.purchase_receipt, as_dict=True) - + for d in pr_items: item = self.append("landed_cost_items") item.item_code = d.item_code @@ -29,11 +29,11 @@ class LandedCostVoucher(Document): item.amount = d.amount item.purchase_receipt = pr.purchase_receipt item.pr_item_row_id = d.name - + if self.get("landed_cost_taxes_and_charges"): self.set_applicable_charges_for_item() - - + + def validate(self): self.check_mandatory() self.validate_purchase_receipts() @@ -42,14 +42,14 @@ class LandedCostVoucher(Document): self.get_items_from_purchase_receipts() else: self.set_applicable_charges_for_item() - + def check_mandatory(self): if not self.get("landed_cost_purchase_receipts"): frappe.throw(_("Please enter Purchase Receipts")) - + if not self.get("landed_cost_taxes_and_charges"): frappe.throw(_("Please enter Taxes and Charges")) - + def validate_purchase_receipts(self): purchase_receipts = [] for d in self.get("landed_cost_purchase_receipts"): @@ -57,41 +57,50 @@ class LandedCostVoucher(Document): frappe.throw(_("Purchase Receipt must be submitted")) else: purchase_receipts.append(d.purchase_receipt) - + for item in self.get("landed_cost_items"): if not item.purchase_receipt: frappe.throw(_("Item must be added using 'Get Items from Purchase Receipts' button")) elif item.purchase_receipt not in purchase_receipts: frappe.throw(_("Item Row {0}: Purchase Receipt {1} does not exist in above 'Purchase Receipts' table") .format(item.idx, item.purchase_receipt)) - + def set_total_taxes_and_charges(self): 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")]) - + for item in self.get("landed_cost_items"): item.applicable_charges = flt(item.amount) * flt(self.total_taxes_and_charges) / flt(total_item_cost) - + def on_submit(self): self.update_landed_cost() - + def on_cancel(self): self.update_landed_cost() - + def update_landed_cost(self): purchase_receipts = list(set([d.purchase_receipt for d in self.get("landed_cost_items")])) - self.delete_sle_and_gle(purchase_receipts) for purchase_receipt in purchase_receipts: pr = frappe.get_doc("Purchase Receipt", purchase_receipt) + + # set landed cost voucher amount in pr item pr.set_landed_cost_voucher_amount() + + # set valuation amount in pr item 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 pr.save() + + # update stock & gl entries for cancelled state of PR + pr.docstatus = 2 + pr.update_stock() + pr.make_gl_entries_on_cancel() + + # update stock & gl entries for submit state of PR + pr.docstatus = 1 pr.update_stock() pr.make_gl_entries() - - def delete_sle_and_gle(self, purchase_receipts): - for doctype in ["Stock Ledger Entry", "GL Entry"]: - frappe.db.sql("""delete from `tab%s` where voucher_type='Purchase Receipt' - and voucher_no in (%s)""" % (doctype, ', '.join(['%s']*len(purchase_receipts))), purchase_receipts) \ No newline at end of file From 2b776e8c3ae7a4645cf32cc27c71865527fab926 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 8 Aug 2014 13:16:33 +0530 Subject: [PATCH 17/22] Landed cost voucher minor fixes --- .../landed_cost_item/landed_cost_item.json | 18 ++++-- .../landed_cost_taxes_and_charges.json | 3 +- .../landed_cost_voucher.json | 3 +- .../landed_cost_voucher.py | 2 +- .../purchase_receipt/purchase_receipt.py | 64 +++++++++---------- .../purchase_receipt_item.json | 2 +- 6 files changed, 51 insertions(+), 41 deletions(-) diff --git a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json index 3b77d0f18d..7b0ef917b4 100644 --- a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json +++ b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json @@ -32,8 +32,10 @@ "fieldtype": "Link", "hidden": 0, "label": "Purchase Receipt", + "no_copy": 1, "options": "Purchase Receipt", "permlevel": 0, + "print_hide": 1, "read_only": 1 }, { @@ -54,6 +56,7 @@ "fieldtype": "Currency", "in_list_view": 0, "label": "Rate", + "options": "Company:company:default_currency", "permlevel": 0, "read_only": 1 }, @@ -64,7 +67,7 @@ "label": "Amount", "oldfieldname": "amount", "oldfieldtype": "Currency", - "options": "currency", + "options": "Company:company:default_currency", "permlevel": 0, "read_only": 1, "reqd": 1 @@ -74,19 +77,24 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Applicable Charges", + "options": "Company:company:default_currency", "permlevel": 0, "read_only": 1 }, { - "fieldname": "pr_item_row_id", + "fieldname": "purchase_receipt_item", "fieldtype": "Data", - "label": "PR Item Row Id", - "permlevel": 0 + "hidden": 1, + "label": "Purchase Receipt Item", + "no_copy": 1, + "permlevel": 0, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, - "modified": "2014-07-15 18:01:26.152422", + "modified": "2014-08-08 13:11:29.438664", "modified_by": "Administrator", "module": "Stock", "name": "Landed Cost Item", 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 d077dbc31d..f183b331a2 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 @@ -34,12 +34,13 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", + "options": "Company:company:default_currency", "permlevel": 0, "reqd": 1 } ], "istable": 1, - "modified": "2014-07-11 13:00:14.770284", + "modified": "2014-08-08 13:12:02.594698", "modified_by": "Administrator", "module": "Stock", "name": "Landed Cost Taxes and Charges", 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 11425c27b7..f064198d8f 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json @@ -48,6 +48,7 @@ "fieldname": "total_taxes_and_charges", "fieldtype": "Currency", "label": "Total Taxes and Charges", + "options": "Company:company:default_currency", "permlevel": 0, "read_only": 1, "reqd": 1 @@ -71,7 +72,7 @@ } ], "is_submittable": 1, - "modified": "2014-07-11 15:34:51.306164", + "modified": "2014-08-08 13:11:55.764550", "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 7c2ae4fa04..e6bb68b13b 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -28,7 +28,7 @@ class LandedCostVoucher(Document): item.rate = d.rate item.amount = d.amount item.purchase_receipt = pr.purchase_receipt - item.pr_item_row_id = d.name + item.purchase_receipt_item = d.name if self.get("landed_cost_taxes_and_charges"): self.set_applicable_charges_for_item() diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 12bb96de64..a7fa6bb88e 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -69,14 +69,14 @@ class PurchaseReceipt(BuyingController): self.create_raw_materials_supplied("pr_raw_material_details") self.set_landed_cost_voucher_amount() self.update_valuation_rate("purchase_receipt_details") - + def set_landed_cost_voucher_amount(self): for d in self.get("purchase_receipt_details"): - lc_voucher_amount = frappe.db.sql("""select sum(ifnull(applicable_charges, 0)) - from `tabLanded Cost Item` - where docstatus = 1 and pr_item_row_id = %s""", d.name) + lc_voucher_amount = frappe.db.sql("""select sum(ifnull(applicable_charges, 0)) + from `tabLanded Cost Item` + where docstatus = 1 and purchase_receipt_item = %s""", d.name) d.landed_cost_voucher_amount = lc_voucher_amount[0][0] if lc_voucher_amount else 0.0 - + def validate_rejected_warehouse(self): for d in self.get("purchase_receipt_details"): if flt(d.rejected_qty) and not d.rejected_warehouse: @@ -285,26 +285,26 @@ class PurchaseReceipt(BuyingController): def get_gl_entries(self, warehouse_account=None): from erpnext.accounts.general_ledger import process_gl_map - + stock_rbnb = self.get_company_default("stock_received_but_not_billed") expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") - default_cost_center = self.get_company_default("cost_center") - + gl_entries = [] warehouse_with_no_account = [] negative_expense_to_be_booked = 0.0 stock_items = self.get_stock_items() for d in self.get("purchase_receipt_details"): - if d.item_code in stock_items and flt(d.valuation_rate): - if warehouse_account.get(d.warehouse) and flt(d.qty) and flt(d.valuation_rate): - + if d.item_code in stock_items and flt(d.valuation_rate) and flt(d.qty): + if warehouse_account.get(d.warehouse): + # warehouse account gl_entries.append(self.get_gl_dict({ "account": warehouse_account[d.warehouse], "against": stock_rbnb, "cost_center": d.cost_center, - "remarks": self.get("remarks") or "Accounting Entry for Stock", - "debit": flt(d.valuation_rate) * flt(d.qty) * flt(d.conversion_factor) + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "debit": flt(flt(d.valuation_rate) * flt(d.qty) * flt(d.conversion_factor), + self.precision("valuation_rate", d)) })) # stock received but not billed @@ -312,36 +312,36 @@ class PurchaseReceipt(BuyingController): "account": stock_rbnb, "against": warehouse_account[d.warehouse], "cost_center": d.cost_center, - "remarks": self.get("remarks") or "Accounting Entry for Stock", + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "credit": flt(d.base_amount, self.precision("base_amount", d)) })) - + negative_expense_to_be_booked += flt(d.item_tax_amount) - + # Amount added through landed-cost-voucher if flt(d.landed_cost_voucher_amount): gl_entries.append(self.get_gl_dict({ "account": expenses_included_in_valuation, "against": warehouse_account[d.warehouse], "cost_center": d.cost_center, - "remarks": self.get("remarks") or "Accounting Entry for Stock", + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "credit": flt(d.landed_cost_voucher_amount) })) - + # sub-contracting warehouse if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse): gl_entries.append(self.get_gl_dict({ "account": warehouse_account[self.supplier_warehouse], "against": warehouse_account[d.warehouse], "cost_center": d.cost_center, - "remarks": self.get("remarks") or "Accounting Entry for Stock", + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "credit": flt(d.rm_supp_cost) })) - + elif d.warehouse not in warehouse_with_no_account or \ d.rejected_warehouse not in warehouse_with_no_account: warehouse_with_no_account.append(d.warehouse) - + # Cost center-wise amount breakup for other charges included for valuation valuation_tax = {} for tax in self.get("other_charges"): @@ -351,21 +351,21 @@ class PurchaseReceipt(BuyingController): valuation_tax.setdefault(tax.cost_center, 0) valuation_tax[tax.cost_center] += \ (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount) - + if negative_expense_to_be_booked and valuation_tax: # Backward compatibility: - # If expenses_included_in_valuation account has been credited in against PI - # and charges added via Landed Cost Voucher, - # post valuation related charges on "Stock Received But Not Billed" + # If expenses_included_in_valuation account has been credited in against PI + # and charges added via Landed Cost Voucher, + # post valuation related charges on "Stock Received But Not Billed" negative_expense_booked_in_pi = frappe.db.sql("""select name from `tabPurchase Invoice Item` pi - where docstatus = 1 and purchase_receipt=%s - and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' + where docstatus = 1 and purchase_receipt=%s + and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=pi.parent and account=%s)""", (self.name, expenses_included_in_valuation)) - + if negative_expense_booked_in_pi: expenses_included_in_valuation = stock_rbnb - + against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0]) total_valuation_amount = sum(valuation_tax.values()) amount_including_divisional_loss = negative_expense_to_be_booked @@ -376,17 +376,17 @@ class PurchaseReceipt(BuyingController): else: applicable_amount = negative_expense_to_be_booked * (amount / total_valuation_amount) amount_including_divisional_loss -= applicable_amount - + gl_entries.append( self.get_gl_dict({ "account": expenses_included_in_valuation, "cost_center": cost_center, "credit": applicable_amount, - "remarks": self.remarks or "Accounting Entry for Stock", + "remarks": self.remarks or _("Accounting Entry for Stock"), "against": against_account }) ) - + i += 1 if warehouse_with_no_account: diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 549c572b57..e5ae5166f9 100755 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -555,7 +555,7 @@ ], "idx": 1, "istable": 1, - "modified": "2014-07-30 18:13:18.337564", + "modified": "2014-08-08 13:15:06.362562", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From 4e6c52dd24f9fd1f2e6f25c115c371d85258d089 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 8 Aug 2014 17:02:37 +0530 Subject: [PATCH 18/22] Test case fixes --- .../doctype/purchase_invoice/test_purchase_invoice.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 5eb5f0103b..bc97b91d30 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -57,7 +57,8 @@ class TestPurchaseInvoice(unittest.TestCase): expected_values = sorted([ ["_Test Supplier - _TC", 0, 720], - ["Stock Received But Not Billed - _TC", 500.0, 0], + ["Stock Received But Not Billed - _TC", 750.0, 0], + ["Expenses Included In Valuation - _TC", 0.0, 250.0], ["_Test Account Shipping Charges - _TC", 100.0, 0], ["_Test Account VAT - _TC", 120.0, 0], ]) @@ -68,11 +69,11 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEquals(expected_values[i][2], gle.credit) set_perpetual_inventory(0) - + def test_gl_entries_with_auto_accounting_for_stock_against_pr(self): set_perpetual_inventory(1) self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1) - + pr = frappe.copy_doc(pr_test_records[0]) pr.submit() @@ -89,10 +90,9 @@ class TestPurchaseInvoice(unittest.TestCase): expected_values = sorted([ ["_Test Supplier - _TC", 0, 720], - ["Stock Received But Not Billed - _TC", 750.0, 0], + ["Stock Received But Not Billed - _TC", 500.0, 0], ["_Test Account Shipping Charges - _TC", 100.0, 0], ["_Test Account VAT - _TC", 120.0, 0], - ["Expenses Included In Valuation - _TC", 0, 250.0], ]) for i, gle in enumerate(gl_entries): From a97e700371f6583cb78f78edeb4fc53d0382acdc Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 11 Aug 2014 14:17:17 +0530 Subject: [PATCH 19/22] Add rejected serial nos in Purchase Receipt if auto-created based on series --- .../purchase_receipt_item.json | 4 ++- erpnext/stock/doctype/serial_no/serial_no.py | 27 +++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index e5ae5166f9..0222e58da5 100755 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -426,6 +426,7 @@ "fieldname": "rejected_serial_no", "fieldtype": "Text", "label": "Rejected Serial No", + "no_copy": 1, "permlevel": 0, "print_hide": 1, "read_only": 0 @@ -508,6 +509,7 @@ "label": "Landed Cost Voucher Amount", "no_copy": 1, "permlevel": 0, + "print_hide": 1, "read_only": 1 }, { @@ -555,7 +557,7 @@ ], "idx": 1, "istable": 1, - "modified": "2014-08-08 13:15:06.362562", + "modified": "2014-08-11 12:48:13.509109", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index fe4af21d78..b07eab74fb 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -295,19 +295,30 @@ def make_serial_no(serial_no, sle): return sr.name def update_serial_nos_after_submit(controller, parentfield): - stock_ledger_entries = frappe.db.sql("""select voucher_detail_no, serial_no + stock_ledger_entries = frappe.db.sql("""select voucher_detail_no, serial_no, actual_qty, warehouse from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""", (controller.doctype, controller.name), as_dict=True) if not stock_ledger_entries: return for d in controller.get(parentfield): - serial_no = None + update_rejected_serial_nos = True if (controller.doctype=="Purchase Receipt" and d.rejected_qty) else False + accepted_serial_nos_updated = False + warehouse = d.t_warehouse if controller.doctype == "Stock Entry" else d.warehouse + for sle in stock_ledger_entries: if sle.voucher_detail_no==d.name: - serial_no = sle.serial_no - break - - if d.serial_no != serial_no: - d.serial_no = serial_no - frappe.db.set_value(d.doctype, d.name, "serial_no", serial_no) + if not accepted_serial_nos_updated and d.qty and abs(sle.actual_qty)==d.qty \ + and sle.warehouse == warehouse and sle.serial_no != d.serial_no: + d.serial_no = sle.serial_no + frappe.db.set_value(d.doctype, d.name, "serial_no", sle.serial_no) + accepted_serial_nos_updated = True + if not update_rejected_serial_nos: + break + elif update_rejected_serial_nos and abs(sle.actual_qty)==d.rejected_qty \ + and sle.warehouse == d.rejected_warehouse and sle.serial_no != d.rejected_serial_no: + d.rejected_serial_no = sle.serial_no + frappe.db.set_value(d.doctype, d.name, "rejected_serial_no", sle.serial_no) + update_rejected_serial_nos = False + if accepted_serial_nos_updated: + break From 200e8d971ff1e32bebbd06ad85905a371754a5a2 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 11 Aug 2014 16:12:11 +0530 Subject: [PATCH 20/22] Landed Cost voucher test case for serialized items --- .../test_landed_cost_voucher.py | 54 ++++++++++++++----- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py index ca16b4493b..ba8bee281d 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py @@ -12,19 +12,31 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \ class TestLandedCostVoucher(unittest.TestCase): def test_landed_cost_voucher(self): set_perpetual_inventory(1) - pr = self.submit_pr() + pr = frappe.copy_doc(pr_test_records[0]) + pr.submit() + + bin_details = frappe.db.get_value("Bin", {"warehouse": "_Test Warehouse - _TC", + "item_code": "_Test Item"}, ["actual_qty", "stock_value"], as_dict=1) + self.submit_landed_cost_voucher(pr) - + pr_lc_value = frappe.db.get_value("Purchase Receipt Item", {"parent": pr.name}, "landed_cost_voucher_amount") self.assertEquals(pr_lc_value, 25.0) - + + bin_details_after_lcv = frappe.db.get_value("Bin", {"warehouse": "_Test Warehouse - _TC", + "item_code": "_Test Item"}, ["actual_qty", "stock_value"], as_dict=1) + + self.assertEqual(bin_details.actual_qty, bin_details_after_lcv.actual_qty) + + self.assertEqual(bin_details_after_lcv.stock_value - bin_details.stock_value, 25.0) + gl_entries = get_gl_entries("Purchase Receipt", pr.name) self.assertTrue(gl_entries) stock_in_hand_account = pr.get("purchase_receipt_details")[0].warehouse fixed_asset_account = pr.get("purchase_receipt_details")[1].warehouse - + expected_values = { stock_in_hand_account: [400.0, 0.0], @@ -38,7 +50,27 @@ class TestLandedCostVoucher(unittest.TestCase): self.assertEquals(expected_values[gle.account][1], gle.credit) set_perpetual_inventory(0) - + + def test_landed_cost_voucher_for_serialized_item(self): + set_perpetual_inventory(1) + frappe.db.sql("delete from `tabSerial No` where name in ('SN001', 'SN002', 'SN003', 'SN004', 'SN005')") + + pr = frappe.copy_doc(pr_test_records[0]) + pr.purchase_receipt_details[0].item_code = "_Test Serialized Item" + pr.purchase_receipt_details[0].serial_no = "SN001\nSN002\nSN003\nSN004\nSN005" + pr.submit() + + self.submit_landed_cost_voucher(pr) + + serial_no = frappe.db.get_value("Serial No", "SN001", + ["status", "warehouse", "purchase_rate"], as_dict=1) + + self.assertEquals(serial_no.status, "Available") + self.assertEquals(serial_no.purchase_rate, 80.0) + self.assertEquals(serial_no.warehouse, "_Test Warehouse - _TC") + + set_perpetual_inventory(0) + def submit_landed_cost_voucher(self, pr): lcv = frappe.new_doc("Landed Cost Voucher") lcv.company = "_Test Company" @@ -53,15 +85,9 @@ class TestLandedCostVoucher(unittest.TestCase): "account": "_Test Account Insurance Charges - _TC", "amount": 50.0 }]) - + lcv.insert() lcv.submit() - - def submit_pr(self): - pr = frappe.copy_doc(pr_test_records[0]) - pr.submit() - return pr - - -test_records = frappe.get_test_records('Landed Cost Voucher') \ No newline at end of file + +test_records = frappe.get_test_records('Landed Cost Voucher') From 5c0db8d05e4465240112bba483d9a83d54531a01 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 11 Aug 2014 16:28:29 +0530 Subject: [PATCH 21/22] Landed Cost voucher test case for serialized items --- .../doctype/landed_cost_voucher/test_landed_cost_voucher.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py index ba8bee281d..d84d63c913 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py @@ -60,13 +60,15 @@ class TestLandedCostVoucher(unittest.TestCase): pr.purchase_receipt_details[0].serial_no = "SN001\nSN002\nSN003\nSN004\nSN005" pr.submit() + serial_no_rate = frappe.db.get_value("Serial No", "SN001", "purchase_rate") + self.submit_landed_cost_voucher(pr) serial_no = frappe.db.get_value("Serial No", "SN001", ["status", "warehouse", "purchase_rate"], as_dict=1) self.assertEquals(serial_no.status, "Available") - self.assertEquals(serial_no.purchase_rate, 80.0) + self.assertEquals(serial_no.purchase_rate - serial_no_rate, 5.0) self.assertEquals(serial_no.warehouse, "_Test Warehouse - _TC") set_perpetual_inventory(0) From f957cbd8ef708428a3a8646efd2fb5ed371ebbac Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 11 Aug 2014 18:03:43 +0530 Subject: [PATCH 22/22] [test] Purchase Receipt - update rejected serial no --- .../purchase_receipt/test_purchase_receipt.py | 22 +++++++++++++++++++ .../stock/doctype/warehouse/test_records.json | 6 +++++ 2 files changed, 28 insertions(+) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 9b76a14381..51c85ce48b 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -118,6 +118,28 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertFalse(frappe.db.get_value("Serial No", pr.get("purchase_receipt_details")[0].serial_no, "warehouse")) + def test_rejected_serial_no(self): + pr = frappe.copy_doc(test_records[0]) + pr.get("purchase_receipt_details")[0].item_code = "_Test Serialized Item With Series" + pr.get("purchase_receipt_details")[0].qty = 3 + pr.get("purchase_receipt_details")[0].rejected_qty = 2 + pr.get("purchase_receipt_details")[0].received_qty = 5 + pr.get("purchase_receipt_details")[0].rejected_warehouse = "_Test Rejected Warehouse - _TC" + pr.insert() + pr.submit() + + accepted_serial_nos = pr.get("purchase_receipt_details")[0].serial_no.split("\n") + self.assertEquals(len(accepted_serial_nos), 3) + for serial_no in accepted_serial_nos: + self.assertEquals(frappe.db.get_value("Serial No", serial_no, "warehouse"), + pr.get("purchase_receipt_details")[0].warehouse) + + rejected_serial_nos = pr.get("purchase_receipt_details")[0].rejected_serial_no.split("\n") + self.assertEquals(len(rejected_serial_nos), 2) + for serial_no in rejected_serial_nos: + self.assertEquals(frappe.db.get_value("Serial No", serial_no, "warehouse"), + pr.get("purchase_receipt_details")[0].rejected_warehouse) + def get_gl_entries(voucher_type, voucher_no): return frappe.db.sql("""select account, debit, credit from `tabGL Entry` where voucher_type=%s and voucher_no=%s diff --git a/erpnext/stock/doctype/warehouse/test_records.json b/erpnext/stock/doctype/warehouse/test_records.json index 72071f88ca..d5df1759d8 100644 --- a/erpnext/stock/doctype/warehouse/test_records.json +++ b/erpnext/stock/doctype/warehouse/test_records.json @@ -11,6 +11,12 @@ "doctype": "Warehouse", "warehouse_name": "_Test Warehouse 1" }, + { + "company": "_Test Company", + "create_account_under": "Stock Assets - _TC", + "doctype": "Warehouse", + "warehouse_name": "_Test Rejected Warehouse" + }, { "company": "_Test Company 1", "create_account_under": "Stock Assets - _TC1",