fix: Item valuation for internal stocktransfers

This commit is contained in:
Deepesh Garg
2020-12-23 18:43:37 +05:30
parent 0413e34f0d
commit 573d9094bb
7 changed files with 116 additions and 73 deletions

View File

@@ -539,8 +539,8 @@ class PurchaseInvoice(BuyingController):
voucher_wise_stock_value = {} voucher_wise_stock_value = {}
if self.update_stock: if self.update_stock:
for d in frappe.get_all('Stock Ledger Entry', for d in frappe.get_all('Stock Ledger Entry',
fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}): fields = ["voucher_detail_no", "stock_value_difference", "warehouse"], filters={'voucher_no': self.name}):
voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference) voucher_wise_stock_value.setdefault((d.voucher_detail_no, d.warehouse), d.stock_value_difference)
valuation_tax_accounts = [d.account_head for d in self.get("taxes") valuation_tax_accounts = [d.account_head for d in self.get("taxes")
if d.category in ('Valuation', 'Total and Valuation') if d.category in ('Valuation', 'Total and Valuation')
@@ -823,10 +823,10 @@ class PurchaseInvoice(BuyingController):
# Stock ledger value is not matching with the warehouse amount # Stock ledger value is not matching with the warehouse amount
if (self.update_stock and voucher_wise_stock_value.get(item.name) and if (self.update_stock and voucher_wise_stock_value.get(item.name) and
warehouse_debit_amount != flt(voucher_wise_stock_value.get(item.name), net_amt_precision)): warehouse_debit_amount != flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)):
cost_of_goods_sold_account = self.get_company_default("default_expense_account") cost_of_goods_sold_account = self.get_company_default("default_expense_account")
stock_amount = flt(voucher_wise_stock_value.get(item.name), net_amt_precision) stock_amount = flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)
stock_adjustment_amt = warehouse_debit_amount - stock_amount stock_adjustment_amt = warehouse_debit_amount - stock_amount
gl_entries.append( gl_entries.append(

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "hash", "autoname": "hash",
"creation": "2013-05-22 12:43:10", "creation": "2013-05-22 12:43:10",
"doctype": "DocType", "doctype": "DocType",
@@ -46,6 +47,7 @@
"column_break_25", "column_break_25",
"base_net_rate", "base_net_rate",
"base_net_amount", "base_net_amount",
"outgoing_rate",
"valuation_rate", "valuation_rate",
"item_tax_amount", "item_tax_amount",
"landed_cost_voucher_amount", "landed_cost_voucher_amount",
@@ -553,8 +555,8 @@
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 1,
"label": "Brand", "label": "Brand",
"print_hide": 1, "options": "Brand",
"options": "Brand" "print_hide": 1
}, },
{ {
"fetch_from": "item_code.item_group", "fetch_from": "item_code.item_group",
@@ -562,9 +564,9 @@
"fieldname": "item_group", "fieldname": "item_group",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Item Group", "label": "Item Group",
"options": "Item Group",
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1
"options": "Item Group"
}, },
{ {
"description": "Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges", "description": "Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges",
@@ -779,11 +781,18 @@
"no_copy": 1, "no_copy": 1,
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
},
{
"fieldname": "outgoing_rate",
"fieldtype": "Currency",
"label": "Outgoing Rate",
"read_only": 1
} }
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"modified": "2020-08-20 11:48:01.398356", "links": [],
"modified": "2020-12-23 17:30:57.458876",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice Item", "name": "Purchase Invoice Item",

View File

@@ -1770,59 +1770,59 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(target_doc.company, "_Test Company 1") self.assertEqual(target_doc.company, "_Test Company 1")
self.assertEqual(target_doc.supplier, "_Test Internal Supplier") self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
# def test_internal_transfer_gl_entry(self): def test_internal_transfer_gl_entry(self):
# ## Create internal transfer account ## Create internal transfer account
# account = create_account(account_name="Unrealized Profit", account = create_account(account_name="Unrealized Profit",
# parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory") parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory")
# frappe.db.set_value('Company', '_Test Company with perpetual inventory', frappe.db.set_value('Company', '_Test Company with perpetual inventory',
# 'unrealized_profit_loss_account', account) 'unrealized_profit_loss_account', account)
# customer = create_internal_customer("_Test Internal Customer 2", "_Test Company with perpetual inventory", customer = create_internal_customer("_Test Internal Customer 2", "_Test Company with perpetual inventory",
# "_Test Company with perpetual inventory") "_Test Company with perpetual inventory")
# create_internal_supplier("_Test Internal Supplier 2", "_Test Company with perpetual inventory", create_internal_supplier("_Test Internal Supplier 2", "_Test Company with perpetual inventory",
# "_Test Company with perpetual inventory") "_Test Company with perpetual inventory")
# si = create_sales_invoice( si = create_sales_invoice(
# company = "_Test Company with perpetual inventory", company = "_Test Company with perpetual inventory",
# customer = customer, customer = customer,
# debit_to = "Debtors - TCP1", debit_to = "Debtors - TCP1",
# warehouse = "Stores - TCP1", warehouse = "Stores - TCP1",
# income_account = "Sales - TCP1", income_account = "Sales - TCP1",
# expense_account = "Cost of Goods Sold - TCP1", expense_account = "Cost of Goods Sold - TCP1",
# cost_center = "Main - TCP1", cost_center = "Main - TCP1",
# currency = "INR", currency = "INR",
# do_not_save = 1 do_not_save = 1
# ) )
# si.selling_price_list = "_Test Price List Rest of the World" si.selling_price_list = "_Test Price List Rest of the World"
# si.update_stock = 1 si.update_stock = 1
# si.items[0].target_warehouse = 'Work In Progress - TCP1' si.items[0].target_warehouse = 'Work In Progress - TCP1'
# add_taxes(si) add_taxes(si)
# si.save() si.save()
# si.submit() si.submit()
# target_doc = make_inter_company_transaction("Sales Invoice", si.name) target_doc = make_inter_company_transaction("Sales Invoice", si.name)
# target_doc.company = '_Test Company with perpetual inventory' target_doc.company = '_Test Company with perpetual inventory'
# target_doc.items[0].warehouse = 'Finished Goods - TCP1' target_doc.items[0].warehouse = 'Finished Goods - TCP1'
# add_taxes(target_doc) add_taxes(target_doc)
# target_doc.save() target_doc.save()
# target_doc.submit() target_doc.submit()
# si_gl_entries = [ si_gl_entries = [
# ["_Test Account Excise Duty - TCP1", 0.0, 12.0, nowdate()], ["_Test Account Excise Duty - TCP1", 0.0, 12.0, nowdate()],
# ["Unrealized Profit - TCP1", 12.0, 0.0, nowdate()] ["Unrealized Profit - TCP1", 12.0, 0.0, nowdate()]
# ] ]
# check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1)) check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1))
# pi_gl_entries = [ pi_gl_entries = [
# ["_Test Account Excise Duty - TCP1", 12.0 , 0.0, nowdate()], ["_Test Account Excise Duty - TCP1", 12.0 , 0.0, nowdate()],
# ["Unrealized Profit - TCP1", 0.0, 12.0, nowdate()] ["Unrealized Profit - TCP1", 0.0, 12.0, nowdate()]
# ] ]
# check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1)) check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1))
def test_eway_bill_json(self): def test_eway_bill_json(self):
si = make_sales_invoice_for_ewaybill() si = make_sales_invoice_for_ewaybill()

View File

@@ -44,7 +44,6 @@ class BuyingController(StockController):
self.validate_items() self.validate_items()
self.set_qty_as_per_stock_uom() self.set_qty_as_per_stock_uom()
self.validate_stock_or_nonstock_items() self.validate_stock_or_nonstock_items()
self.update_tax_category_for_internal_transfer()
self.validate_warehouse() self.validate_warehouse()
self.validate_from_warehouse() self.validate_from_warehouse()
self.set_supplier_address() self.set_supplier_address()
@@ -67,6 +66,8 @@ class BuyingController(StockController):
if self.doctype in ("Purchase Receipt", "Purchase Invoice"): if self.doctype in ("Purchase Receipt", "Purchase Invoice"):
self.update_valuation_rate() self.update_valuation_rate()
self.set_out_going_rate()
def set_missing_values(self, for_validate=False): def set_missing_values(self, for_validate=False):
super(BuyingController, self).set_missing_values(for_validate) super(BuyingController, self).set_missing_values(for_validate)
@@ -100,11 +101,6 @@ class BuyingController(StockController):
msg = _('Tax Category has been changed to "Total" because all the Items are non-stock items') msg = _('Tax Category has been changed to "Total" because all the Items are non-stock items')
self.update_tax_category(msg) self.update_tax_category(msg)
def update_tax_category_for_internal_transfer(self):
if self.doctype == 'Purchase Invoice' and self.is_internal_transfer():
msg = _('Tax Category has been changed to "Total" as its an internal purchase.')
self.update_tax_category(msg)
def update_tax_category(self, msg): def update_tax_category(self, msg):
tax_for_valuation = [d for d in self.get("taxes") tax_for_valuation = [d for d in self.get("taxes")
if d.category in ["Valuation", "Valuation and Total"]] if d.category in ["Valuation", "Valuation and Total"]]
@@ -224,6 +220,32 @@ class BuyingController(StockController):
else: else:
item.valuation_rate = 0.0 item.valuation_rate = 0.0
def set_out_going_rate(self):
if self.doctype not in ("Purchase Receipt", "Purchase Invoice"):
return
items = self.get("items")
for d in items:
if not cint(self.get("is_return")) and d.get("target_warehouse"):
# Get outgoing rate based on original item cost based on valuation method
d.outgoing_rate = get_incoming_rate({
"item_code": d.item_code,
"warehouse": d.target_warehouse,
"posting_date": self.posting_date,
"posting_time": self.posting_time,
"qty": -1 * flt(d.qty),
"serial_no": d.serial_no,
"company": self.company,
"voucher_type": self.doctype,
"voucher_no": self.name,
"allow_zero_valuation": d.get("allow_zero_valuation")
}, raise_error_if_no_rate=False)
elif self.get("return_against"):
# Get incoming rate of return entry from reference document
# based on original item cost as per valuation method
d.outgoing_rate = get_rate_for_return(self.doctype, self.name, d.item_code, self.return_against, item_row=d)
def get_supplied_items_cost(self, item_row_id, reset_outgoing_rate=True): def get_supplied_items_cost(self, item_row_id, reset_outgoing_rate=True):
supplied_items_cost = 0.0 supplied_items_cost = 0.0
for d in self.get("supplied_items"): for d in self.get("supplied_items"):
@@ -559,6 +581,7 @@ class BuyingController(StockController):
from_warehouse_sle = self.get_sl_entries(d, { from_warehouse_sle = self.get_sl_entries(d, {
"actual_qty": -1 * pr_qty, "actual_qty": -1 * pr_qty,
"warehouse": d.from_warehouse, "warehouse": d.from_warehouse,
"outgoing_rate": d.outgoing_rate,
"dependant_sle_voucher_detail_no": d.name "dependant_sle_voucher_detail_no": d.name
}) })
@@ -569,10 +592,8 @@ class BuyingController(StockController):
"serial_no": cstr(d.serial_no).strip() "serial_no": cstr(d.serial_no).strip()
}) })
if self.is_return: if self.is_return:
outgoing_rate = get_rate_for_return(self.doctype, self.name, d.item_code, self.return_against, item_row=d)
sle.update({ sle.update({
"outgoing_rate": outgoing_rate, "outgoing_rate": d.outgoing_rate,
"recalculate_rate": 1 "recalculate_rate": 1
}) })
if d.from_warehouse: if d.from_warehouse:

View File

@@ -331,6 +331,12 @@ class SellingController(StockController):
"voucher_no": self.name, "voucher_no": self.name,
"allow_zero_valuation": d.get("allow_zero_valuation") "allow_zero_valuation": d.get("allow_zero_valuation")
}, raise_error_if_no_rate=False) }, raise_error_if_no_rate=False)
# For internal transfers use incoming rate as the valuation rate
if self.get('is_internal_customer') and d.get('target_warehouse'):
d.rate = d.incoming_rate
frappe.msgprint(_("Row {0}: Item rate updated as the valuation rate since its an internal transfer").format(d.idx))
elif self.get("return_against"): elif self.get("return_against"):
# Get incoming rate of return entry from reference document # Get incoming rate of return entry from reference document
# based on original item cost as per valuation method # based on original item cost as per valuation method

View File

@@ -72,7 +72,7 @@ class StockController(AccountsController):
warehouse_with_no_account = [] warehouse_with_no_account = []
precision = frappe.get_precision("GL Entry", "debit_in_account_currency") precision = frappe.get_precision("GL Entry", "debit_in_account_currency")
for item_row in voucher_details: for item_row in voucher_details:
sle_list = sle_map.get(item_row.name) sle_list = sle_map.get((item_row.name, item_row.warehouse))
if sle_list: if sle_list:
for sle in sle_list: for sle in sle_list:
if warehouse_account.get(sle.warehouse): if warehouse_account.get(sle.warehouse):
@@ -216,7 +216,7 @@ class StockController(AccountsController):
""", (self.doctype, self.name), as_dict=True) """, (self.doctype, self.name), as_dict=True)
for sle in stock_ledger_entries: for sle in stock_ledger_entries:
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle) stock_ledger.setdefault((sle.voucher_detail_no, sle.warehouse), []).append(sle)
return stock_ledger return stock_ledger
def make_batches(self, warehouse_field): def make_batches(self, warehouse_field):

View File

@@ -56,6 +56,7 @@
"column_break_32", "column_break_32",
"base_net_rate", "base_net_rate",
"base_net_amount", "base_net_amount",
"outgoing_rate",
"valuation_rate", "valuation_rate",
"item_tax_amount", "item_tax_amount",
"rm_supp_cost", "rm_supp_cost",
@@ -861,12 +862,18 @@
"fieldtype": "Float", "fieldtype": "Float",
"label": "Received Qty in Stock UOM", "label": "Received Qty in Stock UOM",
"print_hide": 1 "print_hide": 1
},
{
"fieldname": "outgoing_rate",
"fieldtype": "Currency",
"label": "Outgoing Rate",
"read_only": 1
} }
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-12-07 10:00:38.204294", "modified": "2020-12-23 17:33:19.479325",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Purchase Receipt Item", "name": "Purchase Receipt Item",