mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-24 13:57:05 +00:00
Co-authored-by: pandiyan <pandiyanpalani37@gmail.com>
This commit is contained in:
@@ -73,24 +73,42 @@ erpnext.stock.LandedCostVoucher = class LandedCostVoucher extends erpnext.stock.
|
||||
}
|
||||
|
||||
set_applicable_charges_for_item() {
|
||||
var me = this;
|
||||
|
||||
if (this.frm.doc.taxes.length) {
|
||||
var total_item_cost = 0.0;
|
||||
var based_on = this.frm.doc.distribute_charges_based_on.toLowerCase();
|
||||
|
||||
if (based_on != "distribute manually") {
|
||||
$.each(this.frm.doc.items || [], function (i, d) {
|
||||
total_item_cost += flt(d[based_on]);
|
||||
var items = this.frm.doc.items || [];
|
||||
items.forEach((item) => {
|
||||
total_item_cost += flt(item[based_on]);
|
||||
});
|
||||
|
||||
if (items.length) {
|
||||
total_item_cost = flt(total_item_cost, precision(based_on, items[0]));
|
||||
}
|
||||
|
||||
if (!total_item_cost) {
|
||||
items.forEach((item) => {
|
||||
item.applicable_charges = 0;
|
||||
});
|
||||
refresh_field("items");
|
||||
if (items.length) {
|
||||
frappe.show_alert({
|
||||
message: __(
|
||||
"Total {0} of all items is zero, charges cannot be distributed on it.",
|
||||
[this.frm.doc.distribute_charges_based_on]
|
||||
),
|
||||
indicator: "red",
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var total_charges = 0.0;
|
||||
$.each(this.frm.doc.items || [], function (i, item) {
|
||||
item.applicable_charges =
|
||||
(flt(item[based_on]) * flt(me.frm.doc.total_taxes_and_charges)) /
|
||||
flt(total_item_cost);
|
||||
items.forEach((item) => {
|
||||
item.applicable_charges = flt(
|
||||
item.applicable_charges,
|
||||
(flt(item[based_on]) * flt(this.frm.doc.total_taxes_and_charges)) /
|
||||
flt(total_item_cost),
|
||||
precision("applicable_charges", item)
|
||||
);
|
||||
total_charges += item.applicable_charges;
|
||||
@@ -98,7 +116,7 @@ erpnext.stock.LandedCostVoucher = class LandedCostVoucher extends erpnext.stock.
|
||||
|
||||
if (total_charges != this.frm.doc.total_taxes_and_charges) {
|
||||
var diff = this.frm.doc.total_taxes_and_charges - flt(total_charges);
|
||||
this.frm.doc.items.slice(-1)[0].applicable_charges += diff;
|
||||
items.slice(-1)[0].applicable_charges += diff;
|
||||
}
|
||||
refresh_field("items");
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ class LandedCostVoucher(Document):
|
||||
item.item_code = d.item_code
|
||||
item.description = d.description
|
||||
item.qty = d.qty
|
||||
item.rate = d.get("base_rate") or d.get("rate")
|
||||
item.rate = d.base_rate
|
||||
item.cost_center = d.cost_center or erpnext.get_default_cost_center(self.company)
|
||||
item.amount = d.base_amount
|
||||
item.receipt_document_type = pr.receipt_document_type
|
||||
@@ -288,22 +288,24 @@ class LandedCostVoucher(Document):
|
||||
|
||||
def set_applicable_charges_on_item(self):
|
||||
if self.get("taxes") and self.distribute_charges_based_on != "Distribute Manually":
|
||||
total_item_cost = 0.0
|
||||
items = self.get("items")
|
||||
total_charges = 0.0
|
||||
item_count = 0
|
||||
based_on_field = frappe.scrub(self.distribute_charges_based_on)
|
||||
|
||||
for item in self.get("items"):
|
||||
total_item_cost += item.get(based_on_field)
|
||||
total_item_cost = sum(flt(item.get(based_on_field)) for item in items)
|
||||
if items:
|
||||
total_item_cost = flt(total_item_cost, items[0].precision(based_on_field))
|
||||
|
||||
for item in self.get("items"):
|
||||
if not total_item_cost and not item.get(based_on_field):
|
||||
frappe.throw(
|
||||
_(
|
||||
"It's not possible to distribute charges equally when total amount is zero, please set 'Distribute Charges Based On' as 'Quantity'"
|
||||
)
|
||||
if not total_item_cost:
|
||||
frappe.throw(
|
||||
_("Total {0} of all items is zero. Set 'Distribute Charges Based On' to {1}.").format(
|
||||
self.distribute_charges_based_on,
|
||||
_("Qty") if based_on_field == "amount" else _("Amount"),
|
||||
)
|
||||
)
|
||||
|
||||
for item in self.get("items"):
|
||||
item.applicable_charges = flt(
|
||||
flt(item.get(based_on_field))
|
||||
* (flt(self.total_taxes_and_charges) / flt(total_item_cost)),
|
||||
@@ -548,8 +550,8 @@ def get_pr_items(purchase_receipt):
|
||||
query = query.where(pr_item.is_finished_item == 1)
|
||||
else:
|
||||
query = query.select(
|
||||
pr_item.base_rate,
|
||||
pr_item.base_amount,
|
||||
pr_item.base_net_rate.as_("base_rate"),
|
||||
pr_item.base_net_amount.as_("base_amount"),
|
||||
pr_item.is_fixed_asset,
|
||||
)
|
||||
|
||||
|
||||
@@ -27,6 +27,115 @@ class TestLandedCostVoucher(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
self.load_test_records("Currency Exchange")
|
||||
|
||||
def test_landed_cost_uses_discounted_purchase_values(self):
|
||||
for make_purchase in (make_purchase_receipt, make_purchase_invoice):
|
||||
for apply_discount_on in ("Net Total", "Grand Total"):
|
||||
with self.subTest(purchase=make_purchase.__name__, apply_discount_on=apply_discount_on):
|
||||
lcv = frappe.new_doc("Landed Cost Voucher")
|
||||
lcv.company = "_Test Company"
|
||||
lcv.distribute_charges_based_on = "Amount"
|
||||
for discount in (40, 0, 100):
|
||||
purchase = make_purchase(qty=2, rate=100, update_stock=1, do_not_save=True)
|
||||
purchase.apply_discount_on = apply_discount_on
|
||||
purchase.additional_discount_percentage = discount
|
||||
purchase.items[0].allow_zero_valuation_rate = 1
|
||||
purchase.insert()
|
||||
purchase.submit()
|
||||
lcv.append(
|
||||
"purchase_receipts",
|
||||
{
|
||||
"receipt_document_type": purchase.doctype,
|
||||
"receipt_document": purchase.name,
|
||||
},
|
||||
)
|
||||
|
||||
lcv.get_items_from_purchase_receipts()
|
||||
self.assertEqual([item.amount for item in lcv.items], [120, 200, 0])
|
||||
self.assertEqual([item.rate for item in lcv.items], [60, 100, 0])
|
||||
lcv.append("taxes", {"amount": 80})
|
||||
lcv.total_taxes_and_charges = 80
|
||||
lcv.set_applicable_charges_on_item()
|
||||
self.assertEqual([item.applicable_charges for item in lcv.items], [30, 50, 0])
|
||||
|
||||
def test_landed_cost_rejects_offsetting_purchase_and_return_amounts(self):
|
||||
for make_purchase in (make_purchase_receipt, make_purchase_invoice):
|
||||
with self.subTest(purchase=make_purchase.__name__):
|
||||
purchase = make_purchase(qty=2, rate=100, update_stock=1, do_not_save=True)
|
||||
purchase.apply_discount_on = "Grand Total"
|
||||
purchase.additional_discount_percentage = 50
|
||||
purchase.insert()
|
||||
purchase.submit()
|
||||
original = make_purchase(qty=1, rate=100, update_stock=1)
|
||||
purchase_return = make_purchase(
|
||||
qty=-1, rate=100, update_stock=1, is_return=1, return_against=original.name
|
||||
)
|
||||
lcv = make_landed_cost_voucher(
|
||||
receipt_document_type=purchase.doctype,
|
||||
receipt_document=purchase.name,
|
||||
charges=80,
|
||||
do_not_save=True,
|
||||
)
|
||||
lcv.append(
|
||||
"purchase_receipts",
|
||||
{
|
||||
"receipt_document_type": purchase_return.doctype,
|
||||
"receipt_document": purchase_return.name,
|
||||
},
|
||||
)
|
||||
lcv.get_items_from_purchase_receipts()
|
||||
self.assertEqual([item.amount for item in lcv.items], [100, -100])
|
||||
with self.assertRaisesRegex(frappe.ValidationError, "of all items is zero"):
|
||||
lcv.insert()
|
||||
lcv.distribute_charges_based_on = "Qty"
|
||||
lcv.insert()
|
||||
self.assertEqual([item.applicable_charges for item in lcv.items], [160, -80])
|
||||
|
||||
def test_landed_cost_rejects_fully_discounted_purchase(self):
|
||||
for make_purchase in (make_purchase_receipt, make_purchase_invoice):
|
||||
with self.subTest(purchase=make_purchase.__name__):
|
||||
purchase = make_purchase(qty=2, rate=100, update_stock=1, do_not_save=True)
|
||||
purchase.apply_discount_on = "Net Total"
|
||||
purchase.additional_discount_percentage = 100
|
||||
purchase.items[0].allow_zero_valuation_rate = 1
|
||||
purchase.insert()
|
||||
purchase.submit()
|
||||
lcv = make_landed_cost_voucher(
|
||||
receipt_document_type=purchase.doctype,
|
||||
receipt_document=purchase.name,
|
||||
charges=80,
|
||||
do_not_save=True,
|
||||
)
|
||||
with self.assertRaisesRegex(frappe.ValidationError, "of all items is zero"):
|
||||
lcv.insert()
|
||||
lcv.distribute_charges_based_on = "Qty"
|
||||
lcv.insert()
|
||||
self.assertEqual([item.applicable_charges for item in lcv.items], [80])
|
||||
|
||||
def test_landed_cost_rejects_amounts_that_cancel_to_float_residue(self):
|
||||
lcv = frappe.new_doc("Landed Cost Voucher")
|
||||
lcv.company = "_Test Company"
|
||||
lcv.distribute_charges_based_on = "Amount"
|
||||
for amount in (100.10, 200.20, -300.30):
|
||||
lcv.append("items", {"item_code": "_Test Item", "qty": 1, "amount": amount})
|
||||
lcv.append("taxes", {"amount": 80})
|
||||
lcv.total_taxes_and_charges = 80
|
||||
|
||||
self.assertNotEqual(sum(item.amount for item in lcv.items), 0)
|
||||
with self.assertRaisesRegex(frappe.ValidationError, "of all items is zero"):
|
||||
lcv.set_applicable_charges_on_item()
|
||||
|
||||
def test_get_vendor_invoices_runs(self):
|
||||
# get_vendor_invoice_query filters unclaimed vendor invoices; the threshold moved from a HAVING
|
||||
# (which referenced a SELECT alias with no GROUP BY -- invalid on Postgres) to a WHERE.
|
||||
from erpnext.stock.doctype.landed_cost_voucher.landed_cost_voucher import get_vendor_invoices
|
||||
|
||||
pi = make_purchase_invoice(item_code="_Test Non Stock Item", qty=1, rate=100)
|
||||
|
||||
rows = get_vendor_invoices(
|
||||
"Purchase Invoice", "", "name", 0, 20, {"company": "_Test Company", "name": pi.name}
|
||||
)
|
||||
self.assertTrue(any(r[0] == pi.name for r in rows))
|
||||
|
||||
def test_landed_cost_voucher(self):
|
||||
frappe.db.set_single_value("Buying Settings", "allow_multiple_items", 1)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user