mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-27 17:04:47 +00:00
Merge branch 'version-14-hotfix' into manual_asset_schedule
This commit is contained in:
@@ -21,8 +21,24 @@ class POSClosingEntry(StatusUpdater):
|
|||||||
if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open":
|
if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open":
|
||||||
frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry"))
|
frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry"))
|
||||||
|
|
||||||
|
self.validate_duplicate_pos_invoices()
|
||||||
self.validate_pos_invoices()
|
self.validate_pos_invoices()
|
||||||
|
|
||||||
|
def validate_duplicate_pos_invoices(self):
|
||||||
|
pos_occurences = {}
|
||||||
|
for idx, inv in enumerate(self.pos_transactions, 1):
|
||||||
|
pos_occurences.setdefault(inv.pos_invoice, []).append(idx)
|
||||||
|
|
||||||
|
error_list = []
|
||||||
|
for key, value in pos_occurences.items():
|
||||||
|
if len(value) > 1:
|
||||||
|
error_list.append(
|
||||||
|
_("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value)))
|
||||||
|
)
|
||||||
|
|
||||||
|
if error_list:
|
||||||
|
frappe.throw(error_list, title=_("Duplicate POS Invoices found"), as_list=True)
|
||||||
|
|
||||||
def validate_pos_invoices(self):
|
def validate_pos_invoices(self):
|
||||||
invalid_rows = []
|
invalid_rows = []
|
||||||
for d in self.pos_transactions:
|
for d in self.pos_transactions:
|
||||||
|
|||||||
@@ -18,6 +18,22 @@ class POSInvoiceMergeLog(Document):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_customer()
|
self.validate_customer()
|
||||||
self.validate_pos_invoice_status()
|
self.validate_pos_invoice_status()
|
||||||
|
self.validate_duplicate_pos_invoices()
|
||||||
|
|
||||||
|
def validate_duplicate_pos_invoices(self):
|
||||||
|
pos_occurences = {}
|
||||||
|
for idx, inv in enumerate(self.pos_invoices, 1):
|
||||||
|
pos_occurences.setdefault(inv.pos_invoice, []).append(idx)
|
||||||
|
|
||||||
|
error_list = []
|
||||||
|
for key, value in pos_occurences.items():
|
||||||
|
if len(value) > 1:
|
||||||
|
error_list.append(
|
||||||
|
_("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value)))
|
||||||
|
)
|
||||||
|
|
||||||
|
if error_list:
|
||||||
|
frappe.throw(error_list, title=_("Duplicate POS Invoices found"), as_list=True)
|
||||||
|
|
||||||
def validate_customer(self):
|
def validate_customer(self):
|
||||||
if self.merge_invoices_based_on == "Customer Group":
|
if self.merge_invoices_based_on == "Customer Group":
|
||||||
@@ -426,6 +442,8 @@ def create_merge_logs(invoice_by_customer, closing_entry=None):
|
|||||||
|
|
||||||
if closing_entry:
|
if closing_entry:
|
||||||
closing_entry.set_status(update=True, status="Failed")
|
closing_entry.set_status(update=True, status="Failed")
|
||||||
|
if type(error_message) == list:
|
||||||
|
error_message = frappe.json.dumps(error_message)
|
||||||
closing_entry.db_set("error_message", error_message)
|
closing_entry.db_set("error_message", error_message)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|||||||
@@ -395,6 +395,7 @@ def get_column_names():
|
|||||||
|
|
||||||
class GrossProfitGenerator(object):
|
class GrossProfitGenerator(object):
|
||||||
def __init__(self, filters=None):
|
def __init__(self, filters=None):
|
||||||
|
self.sle = {}
|
||||||
self.data = []
|
self.data = []
|
||||||
self.average_buying_rate = {}
|
self.average_buying_rate = {}
|
||||||
self.filters = frappe._dict(filters)
|
self.filters = frappe._dict(filters)
|
||||||
@@ -404,7 +405,6 @@ class GrossProfitGenerator(object):
|
|||||||
if filters.group_by == "Invoice":
|
if filters.group_by == "Invoice":
|
||||||
self.group_items_by_invoice()
|
self.group_items_by_invoice()
|
||||||
|
|
||||||
self.load_stock_ledger_entries()
|
|
||||||
self.load_product_bundle()
|
self.load_product_bundle()
|
||||||
self.load_non_stock_items()
|
self.load_non_stock_items()
|
||||||
self.get_returned_invoice_items()
|
self.get_returned_invoice_items()
|
||||||
@@ -633,7 +633,7 @@ class GrossProfitGenerator(object):
|
|||||||
return flt(row.qty) * item_rate
|
return flt(row.qty) * item_rate
|
||||||
|
|
||||||
else:
|
else:
|
||||||
my_sle = self.sle.get((item_code, row.warehouse))
|
my_sle = self.get_stock_ledger_entries(item_code, row.warehouse)
|
||||||
if (row.update_stock or row.dn_detail) and my_sle:
|
if (row.update_stock or row.dn_detail) and my_sle:
|
||||||
parenttype, parent = row.parenttype, row.parent
|
parenttype, parent = row.parenttype, row.parent
|
||||||
if row.dn_detail:
|
if row.dn_detail:
|
||||||
@@ -651,7 +651,7 @@ class GrossProfitGenerator(object):
|
|||||||
dn["item_row"],
|
dn["item_row"],
|
||||||
dn["warehouse"],
|
dn["warehouse"],
|
||||||
)
|
)
|
||||||
my_sle = self.sle.get((item_code, warehouse))
|
my_sle = self.get_stock_ledger_entries(item_code, row.warehouse)
|
||||||
return self.calculate_buying_amount_from_sle(
|
return self.calculate_buying_amount_from_sle(
|
||||||
row, my_sle, parenttype, parent, item_row, item_code
|
row, my_sle, parenttype, parent, item_row, item_code
|
||||||
)
|
)
|
||||||
@@ -667,15 +667,12 @@ class GrossProfitGenerator(object):
|
|||||||
def get_buying_amount_from_so_dn(self, sales_order, so_detail, item_code):
|
def get_buying_amount_from_so_dn(self, sales_order, so_detail, item_code):
|
||||||
from frappe.query_builder.functions import Sum
|
from frappe.query_builder.functions import Sum
|
||||||
|
|
||||||
delivery_note = frappe.qb.DocType("Delivery Note")
|
|
||||||
delivery_note_item = frappe.qb.DocType("Delivery Note Item")
|
delivery_note_item = frappe.qb.DocType("Delivery Note Item")
|
||||||
|
|
||||||
query = (
|
query = (
|
||||||
frappe.qb.from_(delivery_note)
|
frappe.qb.from_(delivery_note_item)
|
||||||
.inner_join(delivery_note_item)
|
|
||||||
.on(delivery_note.name == delivery_note_item.parent)
|
|
||||||
.select(Sum(delivery_note_item.incoming_rate * delivery_note_item.stock_qty))
|
.select(Sum(delivery_note_item.incoming_rate * delivery_note_item.stock_qty))
|
||||||
.where(delivery_note.docstatus == 1)
|
.where(delivery_note_item.docstatus == 1)
|
||||||
.where(delivery_note_item.item_code == item_code)
|
.where(delivery_note_item.item_code == item_code)
|
||||||
.where(delivery_note_item.against_sales_order == sales_order)
|
.where(delivery_note_item.against_sales_order == sales_order)
|
||||||
.where(delivery_note_item.so_detail == so_detail)
|
.where(delivery_note_item.so_detail == so_detail)
|
||||||
@@ -940,24 +937,36 @@ class GrossProfitGenerator(object):
|
|||||||
"Item", item_code, ["item_name", "description", "item_group", "brand"]
|
"Item", item_code, ["item_name", "description", "item_group", "brand"]
|
||||||
)
|
)
|
||||||
|
|
||||||
def load_stock_ledger_entries(self):
|
def get_stock_ledger_entries(self, item_code, warehouse):
|
||||||
res = frappe.db.sql(
|
if item_code and warehouse:
|
||||||
"""select item_code, voucher_type, voucher_no,
|
if (item_code, warehouse) not in self.sle:
|
||||||
voucher_detail_no, stock_value, warehouse, actual_qty as qty
|
sle = qb.DocType("Stock Ledger Entry")
|
||||||
from `tabStock Ledger Entry`
|
res = (
|
||||||
where company=%(company)s and is_cancelled = 0
|
qb.from_(sle)
|
||||||
order by
|
.select(
|
||||||
item_code desc, warehouse desc, posting_date desc,
|
sle.item_code,
|
||||||
posting_time desc, creation desc""",
|
sle.voucher_type,
|
||||||
self.filters,
|
sle.voucher_no,
|
||||||
as_dict=True,
|
sle.voucher_detail_no,
|
||||||
)
|
sle.stock_value,
|
||||||
self.sle = {}
|
sle.warehouse,
|
||||||
for r in res:
|
sle.actual_qty.as_("qty"),
|
||||||
if (r.item_code, r.warehouse) not in self.sle:
|
)
|
||||||
self.sle[(r.item_code, r.warehouse)] = []
|
.where(
|
||||||
|
(sle.company == self.filters.company)
|
||||||
|
& (sle.item_code == item_code)
|
||||||
|
& (sle.warehouse == warehouse)
|
||||||
|
& (sle.is_cancelled == 0)
|
||||||
|
)
|
||||||
|
.orderby(sle.item_code)
|
||||||
|
.orderby(sle.warehouse, sle.posting_date, sle.posting_time, sle.creation, order=Order.desc)
|
||||||
|
.run(as_dict=True)
|
||||||
|
)
|
||||||
|
|
||||||
self.sle[(r.item_code, r.warehouse)].append(r)
|
self.sle[(item_code, warehouse)] = res
|
||||||
|
|
||||||
|
return self.sle[(item_code, warehouse)]
|
||||||
|
return []
|
||||||
|
|
||||||
def load_product_bundle(self):
|
def load_product_bundle(self):
|
||||||
self.product_bundles = {}
|
self.product_bundles = {}
|
||||||
|
|||||||
@@ -252,7 +252,6 @@ def get_already_returned_items(doc):
|
|||||||
child.parent = par.name and par.docstatus = 1
|
child.parent = par.name and par.docstatus = 1
|
||||||
and par.is_return = 1 and par.return_against = %s
|
and par.is_return = 1 and par.return_against = %s
|
||||||
group by item_code
|
group by item_code
|
||||||
for update
|
|
||||||
""".format(
|
""".format(
|
||||||
column, doc.doctype, doc.doctype
|
column, doc.doctype, doc.doctype
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ frappe.query_reports["BOM Stock Report"] = {
|
|||||||
],
|
],
|
||||||
"formatter": function(value, row, column, data, default_formatter) {
|
"formatter": function(value, row, column, data, default_formatter) {
|
||||||
value = default_formatter(value, row, column, data);
|
value = default_formatter(value, row, column, data);
|
||||||
|
|
||||||
if (column.id == "item") {
|
if (column.id == "item") {
|
||||||
if (data["enough_parts_to_build"] > 0) {
|
if (data["in_stock_qty"] >= data["required_qty"]) {
|
||||||
value = `<a style='color:green' href="/app/item/${data['item']}" data-doctype="Item">${data['item']}</a>`;
|
value = `<a style='color:green' href="/app/item/${data['item']}" data-doctype="Item">${data['item']}</a>`;
|
||||||
} else {
|
} else {
|
||||||
value = `<a style='color:red' href="/app/item/${data['item']}" data-doctype="Item">${data['item']}</a>`;
|
value = `<a style='color:red' href="/app/item/${data['item']}" data-doctype="Item">${data['item']}</a>`;
|
||||||
|
|||||||
@@ -418,8 +418,6 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran
|
|||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if(r.message) {
|
if(r.message) {
|
||||||
frappe.model.set_value(doc.doctype, doc.name, 'batch_no', r.message);
|
frappe.model.set_value(doc.doctype, doc.name, 'batch_no', r.message);
|
||||||
} else {
|
|
||||||
frappe.model.set_value(doc.doctype, doc.name, 'batch_no', r.message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,18 @@
|
|||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
frappe.ui.form.on("Item Price", {
|
frappe.ui.form.on("Item Price", {
|
||||||
onload: function (frm) {
|
setup(frm) {
|
||||||
|
frm.set_query("item_code", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
"disabled": 0,
|
||||||
|
"has_variants": 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onload(frm) {
|
||||||
// Fetch price list details
|
// Fetch price list details
|
||||||
frm.add_fetch("price_list", "buying", "buying");
|
frm.add_fetch("price_list", "buying", "buying");
|
||||||
frm.add_fetch("price_list", "selling", "selling");
|
frm.add_fetch("price_list", "selling", "selling");
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _, bold
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.query_builder import Criterion
|
from frappe.query_builder import Criterion
|
||||||
from frappe.query_builder.functions import Cast_
|
from frappe.query_builder.functions import Cast_
|
||||||
@@ -21,6 +21,7 @@ class ItemPrice(Document):
|
|||||||
self.update_price_list_details()
|
self.update_price_list_details()
|
||||||
self.update_item_details()
|
self.update_item_details()
|
||||||
self.check_duplicates()
|
self.check_duplicates()
|
||||||
|
self.validate_item_template()
|
||||||
|
|
||||||
def validate_item(self):
|
def validate_item(self):
|
||||||
if not frappe.db.exists("Item", self.item_code):
|
if not frappe.db.exists("Item", self.item_code):
|
||||||
@@ -49,6 +50,12 @@ class ItemPrice(Document):
|
|||||||
"Item", self.item_code, ["item_name", "description"]
|
"Item", self.item_code, ["item_name", "description"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def validate_item_template(self):
|
||||||
|
if frappe.get_cached_value("Item", self.item_code, "has_variants"):
|
||||||
|
msg = f"Item Price cannot be created for the template item {bold(self.item_code)}"
|
||||||
|
|
||||||
|
frappe.throw(_(msg))
|
||||||
|
|
||||||
def check_duplicates(self):
|
def check_duplicates(self):
|
||||||
|
|
||||||
item_price = frappe.qb.DocType("Item Price")
|
item_price = frappe.qb.DocType("Item Price")
|
||||||
|
|||||||
@@ -16,6 +16,28 @@ class TestItemPrice(FrappeTestCase):
|
|||||||
frappe.db.sql("delete from `tabItem Price`")
|
frappe.db.sql("delete from `tabItem Price`")
|
||||||
make_test_records_for_doctype("Item Price", force=True)
|
make_test_records_for_doctype("Item Price", force=True)
|
||||||
|
|
||||||
|
def test_template_item_price(self):
|
||||||
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
|
||||||
|
item = make_item(
|
||||||
|
"Test Template Item 1",
|
||||||
|
{
|
||||||
|
"has_variants": 1,
|
||||||
|
"variant_based_on": "Manufacturer",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
doc = frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Item Price",
|
||||||
|
"price_list": "_Test Price List",
|
||||||
|
"item_code": item.name,
|
||||||
|
"price_list_rate": 100,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRaises(frappe.ValidationError, doc.save)
|
||||||
|
|
||||||
def test_duplicate_item(self):
|
def test_duplicate_item(self):
|
||||||
doc = frappe.copy_doc(test_records[0])
|
doc = frappe.copy_doc(test_records[0])
|
||||||
self.assertRaises(ItemPriceDuplicateItem, doc.save)
|
self.assertRaises(ItemPriceDuplicateItem, doc.save)
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ class LandedCostVoucher(Document):
|
|||||||
self.get_items_from_purchase_receipts()
|
self.get_items_from_purchase_receipts()
|
||||||
|
|
||||||
self.set_applicable_charges_on_item()
|
self.set_applicable_charges_on_item()
|
||||||
self.validate_applicable_charges_for_item()
|
|
||||||
|
|
||||||
def check_mandatory(self):
|
def check_mandatory(self):
|
||||||
if not self.get("purchase_receipts"):
|
if not self.get("purchase_receipts"):
|
||||||
@@ -115,6 +114,13 @@ class LandedCostVoucher(Document):
|
|||||||
total_item_cost += item.get(based_on_field)
|
total_item_cost += item.get(based_on_field)
|
||||||
|
|
||||||
for item in self.get("items"):
|
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'"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
item.applicable_charges = flt(
|
item.applicable_charges = flt(
|
||||||
flt(item.get(based_on_field)) * (flt(self.total_taxes_and_charges) / flt(total_item_cost)),
|
flt(item.get(based_on_field)) * (flt(self.total_taxes_and_charges) / flt(total_item_cost)),
|
||||||
item.precision("applicable_charges"),
|
item.precision("applicable_charges"),
|
||||||
@@ -162,6 +168,7 @@ class LandedCostVoucher(Document):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
|
self.validate_applicable_charges_for_item()
|
||||||
self.update_landed_cost()
|
self.update_landed_cost()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
|
|||||||
@@ -175,6 +175,59 @@ class TestLandedCostVoucher(FrappeTestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(last_sle_after_landed_cost.stock_value - last_sle.stock_value, 50.0)
|
self.assertEqual(last_sle_after_landed_cost.stock_value - last_sle.stock_value, 50.0)
|
||||||
|
|
||||||
|
def test_landed_cost_voucher_for_zero_purchase_rate(self):
|
||||||
|
"Test impact of LCV on future stock balances."
|
||||||
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
|
||||||
|
item = make_item("LCV Stock Item", {"is_stock_item": 1})
|
||||||
|
warehouse = "Stores - _TC"
|
||||||
|
|
||||||
|
pr = make_purchase_receipt(
|
||||||
|
item_code=item.name,
|
||||||
|
warehouse=warehouse,
|
||||||
|
qty=10,
|
||||||
|
rate=0,
|
||||||
|
posting_date=add_days(frappe.utils.nowdate(), -2),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
frappe.db.get_value(
|
||||||
|
"Stock Ledger Entry",
|
||||||
|
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name, "is_cancelled": 0},
|
||||||
|
"stock_value_difference",
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
|
||||||
|
lcv = make_landed_cost_voucher(
|
||||||
|
company=pr.company,
|
||||||
|
receipt_document_type="Purchase Receipt",
|
||||||
|
receipt_document=pr.name,
|
||||||
|
charges=100,
|
||||||
|
distribute_charges_based_on="Distribute Manually",
|
||||||
|
do_not_save=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
lcv.get_items_from_purchase_receipts()
|
||||||
|
lcv.items[0].applicable_charges = 100
|
||||||
|
lcv.save()
|
||||||
|
lcv.submit()
|
||||||
|
|
||||||
|
self.assertTrue(
|
||||||
|
frappe.db.exists(
|
||||||
|
"Stock Ledger Entry",
|
||||||
|
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name, "is_cancelled": 0},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
frappe.db.get_value(
|
||||||
|
"Stock Ledger Entry",
|
||||||
|
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name, "is_cancelled": 0},
|
||||||
|
"stock_value_difference",
|
||||||
|
),
|
||||||
|
100,
|
||||||
|
)
|
||||||
|
|
||||||
def test_landed_cost_voucher_against_purchase_invoice(self):
|
def test_landed_cost_voucher_against_purchase_invoice(self):
|
||||||
|
|
||||||
pi = make_purchase_invoice(
|
pi = make_purchase_invoice(
|
||||||
@@ -516,7 +569,7 @@ def make_landed_cost_voucher(**args):
|
|||||||
|
|
||||||
lcv = frappe.new_doc("Landed Cost Voucher")
|
lcv = frappe.new_doc("Landed Cost Voucher")
|
||||||
lcv.company = args.company or "_Test Company"
|
lcv.company = args.company or "_Test Company"
|
||||||
lcv.distribute_charges_based_on = "Amount"
|
lcv.distribute_charges_based_on = args.distribute_charges_based_on or "Amount"
|
||||||
|
|
||||||
lcv.set(
|
lcv.set(
|
||||||
"purchase_receipts",
|
"purchase_receipts",
|
||||||
|
|||||||
@@ -1134,13 +1134,25 @@ def get_item_account_wise_additional_cost(purchase_document):
|
|||||||
account.expense_account, {"amount": 0.0, "base_amount": 0.0}
|
account.expense_account, {"amount": 0.0, "base_amount": 0.0}
|
||||||
)
|
)
|
||||||
|
|
||||||
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account][
|
if total_item_cost > 0:
|
||||||
"amount"
|
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][
|
||||||
] += (account.amount * item.get(based_on_field) / total_item_cost)
|
account.expense_account
|
||||||
|
]["amount"] += (
|
||||||
|
account.amount * item.get(based_on_field) / total_item_cost
|
||||||
|
)
|
||||||
|
|
||||||
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account][
|
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][
|
||||||
"base_amount"
|
account.expense_account
|
||||||
] += (account.base_amount * item.get(based_on_field) / total_item_cost)
|
]["base_amount"] += (
|
||||||
|
account.base_amount * item.get(based_on_field) / total_item_cost
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][
|
||||||
|
account.expense_account
|
||||||
|
]["amount"] += item.applicable_charges
|
||||||
|
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][
|
||||||
|
account.expense_account
|
||||||
|
]["base_amount"] += item.applicable_charges
|
||||||
|
|
||||||
return item_account_wise_cost
|
return item_account_wise_cost
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user