mirror of
https://github.com/frappe/erpnext.git
synced 2026-03-24 13:42:10 +01:00
feat: service expense account in the subcontracting receipt
(cherry picked from commit 6e597b9c42)
# Conflicts:
# erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
# erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
# erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
This commit is contained in:
committed by
Mergify
parent
0ff32ac554
commit
ba9b63af49
@@ -1308,6 +1308,7 @@ def make_subcontracted_items():
|
|||||||
"Subcontracted Item SA7": {},
|
"Subcontracted Item SA7": {},
|
||||||
"Subcontracted Item SA8": {},
|
"Subcontracted Item SA8": {},
|
||||||
"Subcontracted Item SA9": {"stock_uom": "Litre"},
|
"Subcontracted Item SA9": {"stock_uom": "Litre"},
|
||||||
|
"Subcontracted Item SA10": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
for item, properties in sub_contracted_items.items():
|
for item, properties in sub_contracted_items.items():
|
||||||
@@ -1329,6 +1330,7 @@ def make_raw_materials():
|
|||||||
"Subcontracted SRM Item 5": {"has_serial_no": 1, "serial_no_series": "SRIID.####"},
|
"Subcontracted SRM Item 5": {"has_serial_no": 1, "serial_no_series": "SRIID.####"},
|
||||||
"Subcontracted SRM Item 8": {},
|
"Subcontracted SRM Item 8": {},
|
||||||
"Subcontracted SRM Item 9": {"stock_uom": "Litre"},
|
"Subcontracted SRM Item 9": {"stock_uom": "Litre"},
|
||||||
|
"Subcontracted SRM Item 10": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
for item, properties in raw_materials.items():
|
for item, properties in raw_materials.items():
|
||||||
@@ -1357,6 +1359,7 @@ def make_service_items():
|
|||||||
"Subcontracted Service Item 7": {},
|
"Subcontracted Service Item 7": {},
|
||||||
"Subcontracted Service Item 8": {},
|
"Subcontracted Service Item 8": {},
|
||||||
"Subcontracted Service Item 9": {},
|
"Subcontracted Service Item 9": {},
|
||||||
|
"Subcontracted Service Item 10": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
for item, properties in service_items.items():
|
for item, properties in service_items.items():
|
||||||
@@ -1381,6 +1384,7 @@ def make_bom_for_subcontracted_items():
|
|||||||
"Subcontracted Item SA6": ["Subcontracted SRM Item 3"],
|
"Subcontracted Item SA6": ["Subcontracted SRM Item 3"],
|
||||||
"Subcontracted Item SA7": ["Subcontracted SRM Item 1"],
|
"Subcontracted Item SA7": ["Subcontracted SRM Item 1"],
|
||||||
"Subcontracted Item SA8": ["Subcontracted SRM Item 8"],
|
"Subcontracted Item SA8": ["Subcontracted SRM Item 8"],
|
||||||
|
"Subcontracted Item SA10": ["Subcontracted SRM Item 10"],
|
||||||
}
|
}
|
||||||
|
|
||||||
for item_code, raw_materials in boms.items():
|
for item_code, raw_materials in boms.items():
|
||||||
|
|||||||
@@ -116,7 +116,13 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
self.validate_items_qty()
|
self.validate_items_qty()
|
||||||
self.set_items_bom()
|
self.set_items_bom()
|
||||||
self.set_items_cost_center()
|
self.set_items_cost_center()
|
||||||
self.set_items_expense_account()
|
|
||||||
|
if self.company:
|
||||||
|
default_expense_account = self.get_company_default(
|
||||||
|
"default_expense_account", ignore_validation=True
|
||||||
|
)
|
||||||
|
self.set_service_expense_account(default_expense_account)
|
||||||
|
self.set_expense_account_for_subcontracted_items(default_expense_account)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.reset_supplied_items()
|
self.reset_supplied_items()
|
||||||
@@ -196,6 +202,59 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
if item.subcontracting_order:
|
if item.subcontracting_order:
|
||||||
check_on_hold_or_closed_status("Subcontracting Order", item.subcontracting_order)
|
check_on_hold_or_closed_status("Subcontracting Order", item.subcontracting_order)
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
def update_job_card(self):
|
||||||
|
for row in self.get("items"):
|
||||||
|
if row.job_card:
|
||||||
|
doc = frappe.get_doc("Job Card", row.job_card)
|
||||||
|
doc.set_manufactured_qty()
|
||||||
|
|
||||||
|
def set_service_expense_account(self, default_expense_account):
|
||||||
|
for row in self.get("items"):
|
||||||
|
if not row.service_expense_account and row.purchase_order_item:
|
||||||
|
service_item = frappe.db.get_value(
|
||||||
|
"Purchase Order Item", row.purchase_order_item, "item_code"
|
||||||
|
)
|
||||||
|
|
||||||
|
if service_item:
|
||||||
|
if default := (
|
||||||
|
get_item_defaults(service_item, self.company)
|
||||||
|
or get_item_group_defaults(service_item, self.company)
|
||||||
|
or get_brand_defaults(service_item, self.company)
|
||||||
|
):
|
||||||
|
if service_expense_account := default.get("expense_account"):
|
||||||
|
row.service_expense_account = service_expense_account
|
||||||
|
|
||||||
|
if not row.service_expense_account:
|
||||||
|
row.service_expense_account = default_expense_account
|
||||||
|
|
||||||
|
def set_expense_account_for_subcontracted_items(self, default_expense_account):
|
||||||
|
for row in self.get("items"):
|
||||||
|
if not row.expense_account:
|
||||||
|
if default := (
|
||||||
|
get_item_defaults(row.item_code, self.company)
|
||||||
|
or get_item_group_defaults(row.item_code, self.company)
|
||||||
|
or get_brand_defaults(row.item_code, self.company)
|
||||||
|
):
|
||||||
|
if expense_account := default.get("expense_account"):
|
||||||
|
row.expense_account = expense_account
|
||||||
|
|
||||||
|
if not row.expense_account:
|
||||||
|
row.expense_account = default_expense_account
|
||||||
|
|
||||||
|
def get_manufactured_qty(self, job_card):
|
||||||
|
table = frappe.qb.DocType("Subcontracting Receipt Item")
|
||||||
|
query = (
|
||||||
|
frappe.qb.from_(table)
|
||||||
|
.select(Sum(table.qty))
|
||||||
|
.where((table.job_card == job_card) & (table.docstatus == 1))
|
||||||
|
)
|
||||||
|
|
||||||
|
qty = query.run()[0][0] or 0.0
|
||||||
|
return flt(qty)
|
||||||
|
|
||||||
|
>>>>>>> 6e597b9c42 (feat: service expense account in the subcontracting receipt)
|
||||||
def validate_items_qty(self):
|
def validate_items_qty(self):
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
if not (item.qty or item.rejected_qty):
|
if not (item.qty or item.rejected_qty):
|
||||||
@@ -242,14 +301,6 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
self.company,
|
self.company,
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_items_expense_account(self):
|
|
||||||
if self.company:
|
|
||||||
expense_account = self.get_company_default("default_expense_account", ignore_validation=True)
|
|
||||||
|
|
||||||
for item in self.items:
|
|
||||||
if not item.expense_account:
|
|
||||||
item.expense_account = expense_account
|
|
||||||
|
|
||||||
def set_supplied_items_expense_account(self):
|
def set_supplied_items_expense_account(self):
|
||||||
for item in self.supplied_items:
|
for item in self.supplied_items:
|
||||||
if not item.expense_account:
|
if not item.expense_account:
|
||||||
@@ -599,13 +650,17 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
project=item.project,
|
project=item.project,
|
||||||
item=item,
|
item=item,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
service_cost = flt(
|
||||||
|
item.service_cost_per_qty, item.precision("service_cost_per_qty")
|
||||||
|
) * flt(item.qty, item.precision("qty"))
|
||||||
# Expense Account (Credit)
|
# Expense Account (Credit)
|
||||||
self.add_gl_entry(
|
self.add_gl_entry(
|
||||||
gl_entries=gl_entries,
|
gl_entries=gl_entries,
|
||||||
account=item.expense_account,
|
account=item.expense_account,
|
||||||
cost_center=item.cost_center,
|
cost_center=item.cost_center,
|
||||||
debit=0.0,
|
debit=0.0,
|
||||||
credit=stock_value_diff,
|
credit=flt(stock_value_diff) - service_cost,
|
||||||
remarks=remarks,
|
remarks=remarks,
|
||||||
against_account=accepted_warehouse_account,
|
against_account=accepted_warehouse_account,
|
||||||
account_currency=get_account_currency(item.expense_account),
|
account_currency=get_account_currency(item.expense_account),
|
||||||
@@ -613,6 +668,21 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
item=item,
|
item=item,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
service_account = item.service_expense_account or item.expense_account
|
||||||
|
# Expense Account (Credit)
|
||||||
|
self.add_gl_entry(
|
||||||
|
gl_entries=gl_entries,
|
||||||
|
account=service_account,
|
||||||
|
cost_center=item.cost_center,
|
||||||
|
debit=0.0,
|
||||||
|
credit=service_cost,
|
||||||
|
remarks=remarks,
|
||||||
|
against_account=accepted_warehouse_account,
|
||||||
|
account_currency=get_account_currency(service_account),
|
||||||
|
project=item.project,
|
||||||
|
item=item,
|
||||||
|
)
|
||||||
|
|
||||||
if flt(item.rm_supp_cost) and supplier_warehouse_account:
|
if flt(item.rm_supp_cost) and supplier_warehouse_account:
|
||||||
for rm_item in supplied_items_details.get(item.name):
|
for rm_item in supplied_items_details.get(item.name):
|
||||||
# Supplier Warehouse Account (Credit)
|
# Supplier Warehouse Account (Credit)
|
||||||
|
|||||||
@@ -422,7 +422,84 @@ class TestSubcontractingReceipt(FrappeTestCase):
|
|||||||
self.assertEqual(expected_values[gle.account][0], gle.debit)
|
self.assertEqual(expected_values[gle.account][0], gle.debit)
|
||||||
self.assertEqual(expected_values[gle.account][1], gle.credit)
|
self.assertEqual(expected_values[gle.account][1], gle.credit)
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
@change_settings("Stock Settings", {"use_serial_batch_fields": 0})
|
@change_settings("Stock Settings", {"use_serial_batch_fields": 0})
|
||||||
|
=======
|
||||||
|
def test_subcontracting_receipt_for_service_expense_account(self):
|
||||||
|
service_expense_account = (
|
||||||
|
frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Account",
|
||||||
|
"account_name": "_Test Service Expense",
|
||||||
|
"account_type": "Expense Account",
|
||||||
|
"company": "_Test Company with perpetual inventory",
|
||||||
|
"is_group": 0,
|
||||||
|
"parent_account": "Indirect Expenses - TCP1",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.insert(ignore_if_duplicate=True)
|
||||||
|
.name
|
||||||
|
)
|
||||||
|
|
||||||
|
service_item_doc = frappe.get_doc("Item", "Subcontracted Service Item 10")
|
||||||
|
service_item_doc.append(
|
||||||
|
"item_defaults",
|
||||||
|
{
|
||||||
|
"company": "_Test Company with perpetual inventory",
|
||||||
|
"expense_account": service_expense_account,
|
||||||
|
"default_warehouse": "Stores - TCP1",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
service_item_doc.save()
|
||||||
|
|
||||||
|
service_items = [
|
||||||
|
{
|
||||||
|
"warehouse": "Stores - TCP1",
|
||||||
|
"item_code": "Subcontracted Service Item 10",
|
||||||
|
"qty": 10,
|
||||||
|
"rate": 100,
|
||||||
|
"fg_item": "Subcontracted Item SA10",
|
||||||
|
"fg_item_qty": 10,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
sco = get_subcontracting_order(
|
||||||
|
company="_Test Company with perpetual inventory",
|
||||||
|
warehouse="Stores - TCP1",
|
||||||
|
supplier_warehouse="Work In Progress - TCP1",
|
||||||
|
service_items=service_items,
|
||||||
|
)
|
||||||
|
rm_items = get_rm_items(sco.supplied_items)
|
||||||
|
itemwise_details = make_stock_in_entry(rm_items=rm_items)
|
||||||
|
make_stock_transfer_entry(
|
||||||
|
sco_no=sco.name,
|
||||||
|
rm_items=rm_items,
|
||||||
|
itemwise_details=copy.deepcopy(itemwise_details),
|
||||||
|
)
|
||||||
|
|
||||||
|
scr = make_subcontracting_receipt(sco.name)
|
||||||
|
scr.submit()
|
||||||
|
|
||||||
|
for item in scr.items:
|
||||||
|
self.assertEqual(item.service_expense_account, service_expense_account)
|
||||||
|
|
||||||
|
gl_entries = get_gl_entries("Subcontracting Receipt", scr.name)
|
||||||
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
|
fg_warehouse_ac = get_inventory_account(scr.company, scr.items[0].warehouse)
|
||||||
|
expense_account = scr.items[0].expense_account
|
||||||
|
expected_values = {
|
||||||
|
fg_warehouse_ac: [2000, 1000],
|
||||||
|
expense_account: [1000, 1000],
|
||||||
|
service_expense_account: [0, 1000],
|
||||||
|
}
|
||||||
|
|
||||||
|
for gle in gl_entries:
|
||||||
|
self.assertEqual(expected_values[gle.account][0], gle.debit)
|
||||||
|
self.assertEqual(expected_values[gle.account][1], gle.credit)
|
||||||
|
|
||||||
|
@IntegrationTestCase.change_settings("Stock Settings", {"use_serial_batch_fields": 0})
|
||||||
|
>>>>>>> 6e597b9c42 (feat: service expense account in the subcontracting receipt)
|
||||||
def test_subcontracting_receipt_with_zero_service_cost(self):
|
def test_subcontracting_receipt_with_zero_service_cost(self):
|
||||||
warehouse = "Stores - TCP1"
|
warehouse = "Stores - TCP1"
|
||||||
service_items = [
|
service_items = [
|
||||||
|
|||||||
@@ -64,6 +64,8 @@
|
|||||||
"manufacturer_part_no",
|
"manufacturer_part_no",
|
||||||
"accounting_details_section",
|
"accounting_details_section",
|
||||||
"expense_account",
|
"expense_account",
|
||||||
|
"column_break_exht",
|
||||||
|
"service_expense_account",
|
||||||
"accounting_dimensions_section",
|
"accounting_dimensions_section",
|
||||||
"cost_center",
|
"cost_center",
|
||||||
"dimension_col_break",
|
"dimension_col_break",
|
||||||
@@ -580,12 +582,44 @@
|
|||||||
"fieldname": "add_serial_batch_for_rejected_qty",
|
"fieldname": "add_serial_batch_for_rejected_qty",
|
||||||
"fieldtype": "Button",
|
"fieldtype": "Button",
|
||||||
"label": "Add Serial / Batch No (Rejected Qty)"
|
"label": "Add Serial / Batch No (Rejected Qty)"
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "job_card",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Job Card",
|
||||||
|
"options": "Job Card",
|
||||||
|
"read_only": 1,
|
||||||
|
"search_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "landed_cost_voucher_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Landed Cost Voucher Amount",
|
||||||
|
"no_copy": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_exht",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "service_expense_account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Service Expense Account",
|
||||||
|
"options": "Account"
|
||||||
|
>>>>>>> 6e597b9c42 (feat: service expense account in the subcontracting receipt)
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
|
<<<<<<< HEAD
|
||||||
"modified": "2024-12-06 15:23:58.680169",
|
"modified": "2024-12-06 15:23:58.680169",
|
||||||
|
=======
|
||||||
|
"modified": "2025-09-26 12:00:38.877638",
|
||||||
|
>>>>>>> 6e597b9c42 (feat: service expense account in the subcontracting receipt)
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Subcontracting",
|
"module": "Subcontracting",
|
||||||
"name": "Subcontracting Receipt Item",
|
"name": "Subcontracting Receipt Item",
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ class SubcontractingReceiptItem(Document):
|
|||||||
serial_and_batch_bundle: DF.Link | None
|
serial_and_batch_bundle: DF.Link | None
|
||||||
serial_no: DF.SmallText | None
|
serial_no: DF.SmallText | None
|
||||||
service_cost_per_qty: DF.Currency
|
service_cost_per_qty: DF.Currency
|
||||||
|
service_expense_account: DF.Link | None
|
||||||
stock_uom: DF.Link
|
stock_uom: DF.Link
|
||||||
subcontracting_order: DF.Link | None
|
subcontracting_order: DF.Link | None
|
||||||
subcontracting_order_item: DF.Data | None
|
subcontracting_order_item: DF.Data | None
|
||||||
|
|||||||
Reference in New Issue
Block a user