Merge pull request #30675 from deepeshgarg007/item_wise_provisional_accounting

feat: Item-wise provisional accounting for service items
This commit is contained in:
Deepesh Garg 2022-04-14 12:30:50 +05:30 committed by GitHub
commit 7536da0b41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 72 additions and 33 deletions

View File

@ -811,7 +811,9 @@ class PurchaseInvoice(BuyingController):
if provisional_accounting_for_non_stock_items:
if item.purchase_receipt:
provisional_account = self.get_company_default("default_provisional_account")
provisional_account = frappe.db.get_value(
"Purchase Receipt Item", item.pr_detail, "provisional_expense_account"
) or self.get_company_default("default_provisional_account")
purchase_receipt_doc = purchase_receipt_doc_map.get(item.purchase_receipt)
if not purchase_receipt_doc:
@ -834,7 +836,7 @@ class PurchaseInvoice(BuyingController):
if expense_booked_in_pr:
# Intentionally passing purchase invoice item to handle partial billing
purchase_receipt_doc.add_provisional_gl_entry(
item, gl_entries, self.posting_date, reverse=1
item, gl_entries, self.posting_date, provisional_account, reverse=1
)
if not self.is_internal_transfer():

View File

@ -1482,7 +1482,8 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertEqual(payment_entry.taxes[0].allocated_amount, 0)
def test_provisional_accounting_entry(self):
item = create_item("_Test Non Stock Item", is_stock_item=0)
create_item("_Test Non Stock Item", is_stock_item=0)
provisional_account = create_account(
account_name="Provision Account",
parent_account="Current Liabilities - _TC",
@ -1505,6 +1506,8 @@ class TestPurchaseInvoice(unittest.TestCase):
pi.save()
pi.submit()
self.assertEquals(pr.items[0].provisional_expense_account, "Provision Account - _TC")
# Check GLE for Purchase Invoice
expected_gle = [
["Cost of Goods Sold - _TC", 250, 0, add_days(pr.posting_date, -1)],

View File

@ -233,7 +233,8 @@ erpnext.company.setup_queries = function(frm) {
["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation"}],
["capital_work_in_progress_account", {"account_type": "Capital Work in Progress"}],
["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}],
["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}]
["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}],
["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}]
], function(i, v) {
erpnext.company.set_custom_query(frm, v);
});

View File

@ -377,6 +377,17 @@ $.extend(erpnext.item, {
}
}
frm.set_query('default_provisional_account', 'item_defaults', (doc, cdt, cdn) => {
let row = locals[cdt][cdn];
return {
filters: {
"company": row.company,
"root_type": ["in", ["Liability", "Asset"]],
"is_group": 0
}
};
});
},
make_dashboard: function(frm) {

View File

@ -15,6 +15,7 @@
"default_supplier",
"column_break_8",
"expense_account",
"default_provisional_account",
"selling_defaults",
"selling_cost_center",
"column_break_12",
@ -101,11 +102,17 @@
"fieldtype": "Link",
"label": "Default Discount Account",
"options": "Account"
},
{
"fieldname": "default_provisional_account",
"fieldtype": "Link",
"label": "Default Provisional Account",
"options": "Account"
}
],
"istable": 1,
"links": [],
"modified": "2021-07-13 01:26:03.860065",
"modified": "2022-04-10 20:18:54.148195",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Default",
@ -114,5 +121,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@ -106,8 +106,6 @@
"terms",
"bill_no",
"bill_date",
"accounting_details_section",
"provisional_expense_account",
"more_info",
"project",
"status",
@ -1145,26 +1143,13 @@
"label": "Represents Company",
"options": "Company",
"read_only": 1
},
{
"collapsible": 1,
"fieldname": "accounting_details_section",
"fieldtype": "Section Break",
"label": "Accounting Details"
},
{
"fieldname": "provisional_expense_account",
"fieldtype": "Link",
"hidden": 1,
"label": "Provisional Expense Account",
"options": "Account"
}
],
"icon": "fa fa-truck",
"idx": 261,
"is_submittable": 1,
"links": [],
"modified": "2022-03-10 11:40:52.690984",
"modified": "2022-04-10 22:50:37.761362",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt",

View File

@ -145,10 +145,13 @@ class PurchaseReceipt(BuyingController):
)
)
if provisional_accounting_for_non_stock_items:
if not provisional_accounting_for_non_stock_items:
return
default_provisional_account = self.get_company_default("default_provisional_account")
if not self.provisional_expense_account:
self.provisional_expense_account = default_provisional_account
for item in self.get("items"):
if not item.get("provisional_expense_account"):
item.provisional_expense_account = default_provisional_account
def validate_with_previous_doc(self):
super(PurchaseReceipt, self).validate_with_previous_doc(
@ -509,7 +512,9 @@ class PurchaseReceipt(BuyingController):
and flt(d.qty)
and provisional_accounting_for_non_stock_items
):
self.add_provisional_gl_entry(d, gl_entries, self.posting_date)
self.add_provisional_gl_entry(
d, gl_entries, self.posting_date, d.get("provisional_expense_account")
)
if warehouse_with_no_account:
frappe.msgprint(
@ -518,9 +523,10 @@ class PurchaseReceipt(BuyingController):
+ "\n".join(warehouse_with_no_account)
)
def add_provisional_gl_entry(self, item, gl_entries, posting_date, reverse=0):
provisional_expense_account = self.get("provisional_expense_account")
credit_currency = get_account_currency(provisional_expense_account)
def add_provisional_gl_entry(
self, item, gl_entries, posting_date, provisional_account, reverse=0
):
credit_currency = get_account_currency(provisional_account)
debit_currency = get_account_currency(item.expense_account)
expense_account = item.expense_account
remarks = self.get("remarks") or _("Accounting Entry for Service")
@ -534,7 +540,7 @@ class PurchaseReceipt(BuyingController):
self.add_gl_entry(
gl_entries=gl_entries,
account=provisional_expense_account,
account=provisional_account,
cost_center=item.cost_center,
debit=0.0,
credit=multiplication_factor * item.amount,
@ -554,7 +560,7 @@ class PurchaseReceipt(BuyingController):
debit=multiplication_factor * item.amount,
credit=0.0,
remarks=remarks,
against_account=provisional_expense_account,
against_account=provisional_account,
account_currency=debit_currency,
project=item.project,
voucher_detail_no=item.name,

View File

@ -96,7 +96,6 @@
"include_exploded_items",
"batch_no",
"rejected_serial_no",
"expense_account",
"item_tax_rate",
"item_weight_details",
"weight_per_unit",
@ -107,6 +106,10 @@
"manufacturer",
"column_break_16",
"manufacturer_part_no",
"accounting_details_section",
"expense_account",
"column_break_102",
"provisional_expense_account",
"accounting_dimensions_section",
"project",
"dimension_col_break",
@ -971,12 +974,27 @@
"label": "Product Bundle",
"options": "Product Bundle",
"read_only": 1
},
{
"fieldname": "provisional_expense_account",
"fieldtype": "Link",
"label": "Provisional Expense Account",
"options": "Account"
},
{
"fieldname": "accounting_details_section",
"fieldtype": "Section Break",
"label": "Accounting Details"
},
{
"fieldname": "column_break_102",
"fieldtype": "Column Break"
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2022-02-01 11:32:27.980524",
"modified": "2022-04-11 13:07:32.061402",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Item",

View File

@ -345,6 +345,7 @@ def get_basic_details(args, item, overwrite_warehouse=True):
"expense_account": expense_account
or get_default_expense_account(args, item_defaults, item_group_defaults, brand_defaults),
"discount_account": get_default_discount_account(args, item_defaults),
"provisional_expense_account": get_provisional_account(args, item_defaults),
"cost_center": get_default_cost_center(
args, item_defaults, item_group_defaults, brand_defaults
),
@ -699,6 +700,10 @@ def get_default_expense_account(args, item, item_group, brand):
)
def get_provisional_account(args, item):
return item.get("default_provisional_account") or args.default_provisional_account
def get_default_discount_account(args, item):
return item.get("default_discount_account") or args.discount_account