mirror of
https://github.com/frappe/erpnext.git
synced 2026-02-15 15:45:01 +00:00
fix: set landed cost based on purchase invoice rate
(cherry picked from commit 17d415b105)
# Conflicts:
# erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
# erpnext/patches.txt
# erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
This commit is contained in:
@@ -2464,6 +2464,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
|
||||
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1)
|
||||
|
||||
<<<<<<< HEAD
|
||||
def test_last_purchase_rate(self):
|
||||
item = create_item("_Test Item For Last Purchase Rate from PI", is_stock_item=1)
|
||||
pi1 = make_purchase_invoice(item_code=item.item_code, qty=10, rate=100)
|
||||
@@ -2481,6 +2482,77 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
pi1.cancel()
|
||||
item.reload()
|
||||
self.assertEqual(item.last_purchase_rate, 0)
|
||||
=======
|
||||
def test_adjust_incoming_rate_from_pi_with_multi_currency_and_partial_billing(self):
|
||||
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0)
|
||||
|
||||
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 1)
|
||||
|
||||
pr = make_purchase_receipt(
|
||||
qty=10, rate=10, currency="USD", do_not_save=1, supplier="_Test Supplier USD"
|
||||
)
|
||||
pr.conversion_rate = 5300
|
||||
pr.save()
|
||||
pr.submit()
|
||||
|
||||
incoming_rate = frappe.db.get_value(
|
||||
"Stock Ledger Entry",
|
||||
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
|
||||
"incoming_rate",
|
||||
)
|
||||
self.assertEqual(incoming_rate, 53000) # Asserting to confirm if the default calculation is correct
|
||||
|
||||
pi = create_purchase_invoice_from_receipt(pr.name)
|
||||
for row in pi.items:
|
||||
row.qty = 1
|
||||
|
||||
pi.save()
|
||||
pi.submit()
|
||||
|
||||
incoming_rate = frappe.db.get_value(
|
||||
"Stock Ledger Entry",
|
||||
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
|
||||
"incoming_rate",
|
||||
)
|
||||
# Test 1 : Incoming rate should not change as only the qty has changed and not the rate (this was not the case before)
|
||||
self.assertEqual(incoming_rate, 53000)
|
||||
|
||||
pi = create_purchase_invoice_from_receipt(pr.name)
|
||||
for row in pi.items:
|
||||
row.qty = 1
|
||||
row.rate = 9
|
||||
|
||||
pi.save()
|
||||
pi.submit()
|
||||
|
||||
incoming_rate = frappe.db.get_value(
|
||||
"Stock Ledger Entry",
|
||||
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
|
||||
"incoming_rate",
|
||||
)
|
||||
# Test 2 : Rate in new PI is lower than PR, so incoming rate should also be lower
|
||||
self.assertEqual(incoming_rate, 50350)
|
||||
|
||||
pi = create_purchase_invoice_from_receipt(pr.name)
|
||||
for row in pi.items:
|
||||
row.qty = 1
|
||||
row.rate = 12
|
||||
|
||||
pi.save()
|
||||
pi.submit()
|
||||
|
||||
incoming_rate = frappe.db.get_value(
|
||||
"Stock Ledger Entry",
|
||||
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
|
||||
"incoming_rate",
|
||||
)
|
||||
# Test 3 : Rate in new PI is higher than PR, so incoming rate should also be higher
|
||||
self.assertEqual(incoming_rate, 54766.667)
|
||||
|
||||
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0)
|
||||
|
||||
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1)
|
||||
>>>>>>> 17d415b105 (fix: set landed cost based on purchase invoice rate)
|
||||
|
||||
def test_opening_invoice_rounding_adjustment_validation(self):
|
||||
pi = make_purchase_invoice(do_not_save=1)
|
||||
|
||||
@@ -333,7 +333,7 @@ class BuyingController(SubcontractingController):
|
||||
net_rate
|
||||
+ item.item_tax_amount
|
||||
+ flt(item.landed_cost_voucher_amount)
|
||||
+ flt(item.get("rate_difference_with_purchase_invoice"))
|
||||
+ flt(item.get("amount_difference_with_purchase_invoice"))
|
||||
) / qty_in_stock_uom
|
||||
else:
|
||||
item.valuation_rate = 0.0
|
||||
|
||||
@@ -261,7 +261,11 @@ erpnext.patches.v14_0.show_loan_management_deprecation_warning
|
||||
erpnext.patches.v14_0.clear_reconciliation_values_from_singles
|
||||
execute:frappe.rename_doc("Report", "TDS Payable Monthly", "Tax Withholding Details", force=True)
|
||||
erpnext.patches.v14_0.update_proprietorship_to_individual
|
||||
<<<<<<< HEAD
|
||||
erpnext.patches.v15_0.rename_subcontracting_fields
|
||||
=======
|
||||
erpnext.stock.doctype.purchase_receipt_item.patches.rename_field_from_rate_difference_to_amount_difference
|
||||
>>>>>>> 17d415b105 (fix: set landed cost based on purchase invoice rate)
|
||||
|
||||
[post_model_sync]
|
||||
erpnext.patches.v15_0.create_asset_depreciation_schedules_from_assets
|
||||
@@ -393,8 +397,12 @@ erpnext.patches.v15_0.migrate_checkbox_to_select_for_reconciliation_effect
|
||||
erpnext.patches.v15_0.sync_auto_reconcile_config
|
||||
execute:frappe.db.set_single_value("Accounts Settings", "exchange_gain_loss_posting_date", "Payment")
|
||||
erpnext.patches.v14_0.disable_add_row_in_gross_profit
|
||||
<<<<<<< HEAD
|
||||
erpnext.patches.v15_0.set_difference_amount_in_asset_value_adjustment
|
||||
erpnext.patches.v14_0.update_posting_datetime
|
||||
erpnext.stock.doctype.stock_ledger_entry.patches.ensure_sle_indexes
|
||||
erpnext.patches.v15_0.rename_sla_fields
|
||||
erpnext.patches.v15_0.update_query_report
|
||||
=======
|
||||
erpnext.stock.doctype.purchase_receipt_item.patches.recalculate_amount_difference_field
|
||||
>>>>>>> 17d415b105 (fix: set landed cost based on purchase invoice rate)
|
||||
|
||||
@@ -424,6 +424,14 @@ class PurchaseReceipt(BuyingController):
|
||||
self.delete_auto_created_batches()
|
||||
self.set_consumed_qty_in_subcontract_order()
|
||||
|
||||
def before_cancel(self):
|
||||
super().before_cancel()
|
||||
self.remove_amount_difference_with_purchase_invoice()
|
||||
|
||||
def remove_amount_difference_with_purchase_invoice(self):
|
||||
for item in self.items:
|
||||
item.amount_difference_with_purchase_invoice = 0
|
||||
|
||||
def get_gl_entries(self, warehouse_account=None, via_landed_cost_voucher=False):
|
||||
from erpnext.accounts.general_ledger import process_gl_map
|
||||
|
||||
@@ -571,15 +579,15 @@ class PurchaseReceipt(BuyingController):
|
||||
item=item,
|
||||
)
|
||||
|
||||
def make_rate_difference_entry(item):
|
||||
if item.rate_difference_with_purchase_invoice and stock_asset_rbnb:
|
||||
def make_amount_difference_entry(item):
|
||||
if item.amount_difference_with_purchase_invoice and stock_asset_rbnb:
|
||||
account_currency = get_account_currency(stock_asset_rbnb)
|
||||
self.add_gl_entry(
|
||||
gl_entries=gl_entries,
|
||||
account=stock_asset_rbnb,
|
||||
cost_center=item.cost_center,
|
||||
debit=0.0,
|
||||
credit=flt(item.rate_difference_with_purchase_invoice),
|
||||
credit=flt(item.amount_difference_with_purchase_invoice),
|
||||
remarks=_("Adjustment based on Purchase Invoice rate"),
|
||||
against_account=stock_asset_account_name,
|
||||
account_currency=account_currency,
|
||||
@@ -612,7 +620,7 @@ class PurchaseReceipt(BuyingController):
|
||||
+ flt(item.landed_cost_voucher_amount)
|
||||
+ flt(item.rm_supp_cost)
|
||||
+ flt(item.item_tax_amount)
|
||||
+ flt(item.rate_difference_with_purchase_invoice)
|
||||
+ flt(item.amount_difference_with_purchase_invoice)
|
||||
)
|
||||
|
||||
divisional_loss = flt(
|
||||
@@ -712,7 +720,7 @@ class PurchaseReceipt(BuyingController):
|
||||
make_item_asset_inward_gl_entry(d, stock_value_diff, stock_asset_account_name)
|
||||
outgoing_amount = make_stock_received_but_not_billed_entry(d)
|
||||
make_landed_cost_gl_entries(d)
|
||||
make_rate_difference_entry(d)
|
||||
make_amount_difference_entry(d)
|
||||
make_sub_contracting_gl_entries(d)
|
||||
make_divisional_loss_gl_entry(d, outgoing_amount)
|
||||
elif (d.warehouse and d.warehouse not in warehouse_with_no_account) or (
|
||||
@@ -1094,11 +1102,19 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate
|
||||
|
||||
if adjust_incoming_rate:
|
||||
adjusted_amt = 0.0
|
||||
if item.billed_amt is not None and item.amount is not None:
|
||||
adjusted_amt = flt(item.billed_amt) - flt(item.amount)
|
||||
item_wise_billed_qty = get_billed_qty_against_purchase_receipt(pr_doc)
|
||||
|
||||
adjusted_amt = adjusted_amt * flt(pr_doc.conversion_rate)
|
||||
item.db_set("rate_difference_with_purchase_invoice", adjusted_amt, update_modified=False)
|
||||
if (
|
||||
item.billed_amt is not None
|
||||
and item.amount is not None
|
||||
and item_wise_billed_qty.get(item.name)
|
||||
):
|
||||
adjusted_amt = (
|
||||
flt(item.billed_amt / item_wise_billed_qty.get(item.name)) - flt(item.rate)
|
||||
) * item.qty
|
||||
|
||||
adjusted_amt = flt(adjusted_amt * flt(pr_doc.conversion_rate), item.precision("amount"))
|
||||
item.db_set("amount_difference_with_purchase_invoice", adjusted_amt, update_modified=False)
|
||||
|
||||
percent_billed = round(100 * (total_billed_amount / (total_amount or 1)), 6)
|
||||
pr_doc.db_set("per_billed", percent_billed)
|
||||
@@ -1111,6 +1127,21 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate
|
||||
adjust_incoming_rate_for_pr(pr_doc)
|
||||
|
||||
|
||||
def get_billed_qty_against_purchase_receipt(pr_doc):
|
||||
pr_names = [d.name for d in pr_doc.items]
|
||||
table = frappe.qb.DocType("Purchase Invoice Item")
|
||||
query = (
|
||||
frappe.qb.from_(table)
|
||||
.select(table.pr_detail, fn.Sum(table.qty).as_("qty"))
|
||||
.where((table.pr_detail.isin(pr_names)) & (table.docstatus == 1))
|
||||
)
|
||||
invoice_data = query.run(as_list=1)
|
||||
|
||||
if not invoice_data:
|
||||
return frappe._dict()
|
||||
return frappe._dict(invoice_data)
|
||||
|
||||
|
||||
def adjust_incoming_rate_for_pr(doc):
|
||||
doc.update_valuation_rate(reset_outgoing_rate=False)
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import frappe
|
||||
from frappe.utils import flt
|
||||
|
||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
|
||||
adjust_incoming_rate_for_pr,
|
||||
get_billed_qty_against_purchase_receipt,
|
||||
)
|
||||
|
||||
|
||||
def execute():
|
||||
table = frappe.qb.DocType("Purchase Receipt Item")
|
||||
query = (
|
||||
frappe.qb.from_(table)
|
||||
.select(table.parent)
|
||||
.distinct()
|
||||
.where((table.amount_difference_with_purchase_invoice > 0) & (table.docstatus == 1))
|
||||
)
|
||||
pr_names = [item.parent for item in query.run(as_dict=True)]
|
||||
|
||||
for pr_name in pr_names:
|
||||
pr_doc = frappe.get_doc("Purchase Receipt", pr_name)
|
||||
for item in pr_doc.items:
|
||||
adjusted_amt = 0.0
|
||||
item_wise_billed_qty = get_billed_qty_against_purchase_receipt(pr_doc)
|
||||
|
||||
if (
|
||||
item.billed_amt is not None
|
||||
and item.amount is not None
|
||||
and item_wise_billed_qty.get(item.name)
|
||||
):
|
||||
adjusted_amt = (
|
||||
flt(item.billed_amt / item_wise_billed_qty.get(item.name)) - flt(item.rate)
|
||||
) * item.qty
|
||||
|
||||
adjusted_amt = flt(adjusted_amt * flt(pr_doc.conversion_rate), item.precision("amount"))
|
||||
item.db_set("amount_difference_with_purchase_invoice", adjusted_amt, update_modified=False)
|
||||
adjust_incoming_rate_for_pr(pr_doc)
|
||||
@@ -0,0 +1,17 @@
|
||||
import frappe
|
||||
from frappe.model.utils.rename_field import rename_field
|
||||
|
||||
|
||||
def execute():
|
||||
frappe.db.set_value(
|
||||
"DocField",
|
||||
{"parent": "Purchase Receipt Item", "fieldname": "rate_difference_with_purchase_invoice"},
|
||||
"label",
|
||||
"Amount Difference with Purchase Invoice",
|
||||
)
|
||||
rename_field(
|
||||
"Purchase Receipt Item",
|
||||
"rate_difference_with_purchase_invoice",
|
||||
"amount_difference_with_purchase_invoice",
|
||||
)
|
||||
frappe.clear_cache(doctype="Purchase Receipt Item")
|
||||
@@ -71,7 +71,7 @@
|
||||
"item_tax_amount",
|
||||
"rm_supp_cost",
|
||||
"landed_cost_voucher_amount",
|
||||
"rate_difference_with_purchase_invoice",
|
||||
"amount_difference_with_purchase_invoice",
|
||||
"billed_amt",
|
||||
"warehouse_and_reference",
|
||||
"warehouse",
|
||||
@@ -998,14 +998,6 @@
|
||||
"label": "Has Item Scanned",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "rate_difference_with_purchase_invoice",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Rate Difference with Purchase Invoice",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
|
||||
"fieldname": "serial_and_batch_bundle",
|
||||
@@ -1135,12 +1127,29 @@
|
||||
"no_copy": 1,
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
},
|
||||
{
|
||||
"fieldname": "distributed_discount_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Distributed Discount Amount",
|
||||
"options": "currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "amount_difference_with_purchase_invoice",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Amount Difference with Purchase Invoice",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
>>>>>>> 17d415b105 (fix: set landed cost based on purchase invoice rate)
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-07-19 12:14:21.521466",
|
||||
"modified": "2025-02-17 13:15:36.692202",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Purchase Receipt Item",
|
||||
|
||||
@@ -16,6 +16,7 @@ class PurchaseReceiptItem(Document):
|
||||
|
||||
allow_zero_valuation_rate: DF.Check
|
||||
amount: DF.Currency
|
||||
amount_difference_with_purchase_invoice: DF.Currency
|
||||
apply_tds: DF.Check
|
||||
asset_category: DF.Link | None
|
||||
asset_location: DF.Link | None
|
||||
@@ -76,7 +77,6 @@ class PurchaseReceiptItem(Document):
|
||||
qty: DF.Float
|
||||
quality_inspection: DF.Link | None
|
||||
rate: DF.Currency
|
||||
rate_difference_with_purchase_invoice: DF.Currency
|
||||
rate_with_margin: DF.Currency
|
||||
received_qty: DF.Float
|
||||
received_stock_qty: DF.Float
|
||||
|
||||
Reference in New Issue
Block a user