Merge pull request #45282 from mihir-kandoi/44976

feat: Add corrective job card operating cost as additional costs in s…
This commit is contained in:
rohitwaghchaure
2025-01-20 22:48:46 +05:30
committed by GitHub
6 changed files with 158 additions and 14 deletions

View File

@@ -1433,6 +1433,64 @@ def add_operations_cost(stock_entry, work_order=None, expense_account=None):
},
)
def get_max_op_qty():
from frappe.query_builder.functions import Sum
table = frappe.qb.DocType("Job Card")
query = (
frappe.qb.from_(table)
.select(Sum(table.total_completed_qty).as_("qty"))
.where(
(table.docstatus == 1)
& (table.work_order == work_order.name)
& (table.is_corrective_job_card == 0)
)
.groupby(table.operation)
)
return min([d.qty for d in query.run(as_dict=True)], default=0)
def get_utilised_cc():
from frappe.query_builder.functions import Sum
table = frappe.qb.DocType("Stock Entry")
subquery = (
frappe.qb.from_(table)
.select(table.name)
.where(
(table.docstatus == 1)
& (table.work_order == work_order.name)
& (table.purpose == "Manufacture")
)
)
table = frappe.qb.DocType("Landed Cost Taxes and Charges")
query = (
frappe.qb.from_(table)
.select(Sum(table.amount).as_("amount"))
.where(table.parent.isin(subquery) & (table.has_corrective_cost == 1))
)
return query.run(as_dict=True)[0].amount or 0
if (
work_order
and work_order.corrective_operation_cost
and cint(
frappe.db.get_single_value(
"Manufacturing Settings", "add_corrective_operation_cost_in_finished_good_valuation"
)
)
):
max_qty = get_max_op_qty() - work_order.produced_qty
remaining_cc = work_order.corrective_operation_cost - get_utilised_cc()
stock_entry.append(
"additional_costs",
{
"expense_account": expense_account,
"description": "Corrective Operation Cost",
"has_corrective_cost": 1,
"amount": remaining_cc / max_qty * flt(stock_entry.fg_completed_qty),
},
)
@frappe.whitelist()
def get_bom_diff(bom1, bom2):

View File

@@ -670,7 +670,11 @@ class JobCard(Document):
)
)
if self.get("operation") == d.operation or self.operation_row_id == d.operation_row_id:
if (
self.get("operation") == d.operation
or self.operation_row_id == d.operation_row_id
or self.is_corrective_job_card
):
self.append(
"items",
{

View File

@@ -442,6 +442,90 @@ class TestJobCard(IntegrationTestCase):
cost_after_cancel = self.work_order.total_operating_cost
self.assertEqual(cost_after_cancel, original_cost)
@IntegrationTestCase.change_settings(
"Manufacturing Settings", {"add_corrective_operation_cost_in_finished_good_valuation": 1}
)
def test_if_corrective_jc_ops_cost_is_added_to_manufacture_stock_entry(self):
wo = make_wo_order_test_record(
item="_Test FG Item 2",
qty=10,
transfer_material_against=self.transfer_material_against,
source_warehouse=self.source_warehouse,
)
self.generate_required_stock(wo)
job_card = frappe.get_last_doc("Job Card", {"work_order": wo.name})
job_card.update({"for_quantity": 4})
job_card.append(
"time_logs",
{"from_time": now(), "to_time": add_to_date(now(), hours=1), "completed_qty": 4},
)
job_card.submit()
corrective_action = frappe.get_doc(
doctype="Operation", is_corrective_operation=1, name=frappe.generate_hash()
).insert()
corrective_job_card = make_corrective_job_card(
job_card.name, operation=corrective_action.name, for_operation=job_card.operation
)
corrective_job_card.hour_rate = 100
corrective_job_card.insert()
corrective_job_card.append(
"time_logs",
{
"from_time": add_to_date(now(), hours=2),
"to_time": add_to_date(now(), hours=2, minutes=30),
"completed_qty": 4,
},
)
corrective_job_card.submit()
wo.reload()
from erpnext.manufacturing.doctype.work_order.work_order import (
make_stock_entry as make_stock_entry_for_wo,
)
stock_entry = make_stock_entry_for_wo(wo.name, "Manufacture", qty=3)
self.assertEqual(stock_entry.additional_costs[1].amount, 37.5)
frappe.get_doc(stock_entry).submit()
from erpnext.manufacturing.doctype.work_order.work_order import make_job_card
make_job_card(
wo.name,
[{"name": wo.operations[0].name, "operation": "_Test Operation 1", "qty": 3, "pending_qty": 3}],
)
job_card = frappe.get_last_doc("Job Card", {"work_order": wo.name})
job_card.update({"for_quantity": 3})
job_card.append(
"time_logs",
{
"from_time": add_to_date(now(), hours=3),
"to_time": add_to_date(now(), hours=4),
"completed_qty": 3,
},
)
job_card.submit()
corrective_job_card = make_corrective_job_card(
job_card.name, operation=corrective_action.name, for_operation=job_card.operation
)
corrective_job_card.hour_rate = 80
corrective_job_card.insert()
corrective_job_card.append(
"time_logs",
{
"from_time": add_to_date(now(), hours=4),
"to_time": add_to_date(now(), hours=4, minutes=30),
"completed_qty": 3,
},
)
corrective_job_card.submit()
wo.reload()
stock_entry = make_stock_entry_for_wo(wo.name, "Manufacture", qty=4)
self.assertEqual(stock_entry.additional_costs[1].amount, 52.5)
def test_job_card_statuses(self):
def assertStatus(status):
jc.set_status()

View File

@@ -11,7 +11,8 @@
"description",
"col_break3",
"amount",
"base_amount"
"base_amount",
"has_corrective_cost"
],
"fields": [
{
@@ -62,12 +63,19 @@
"label": "Amount (Company Currency)",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"default": "0",
"fieldname": "has_corrective_cost",
"fieldtype": "Check",
"label": "Has Corrective Cost",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-03-27 13:09:59.493991",
"modified": "2025-01-20 12:22:03.455762",
"modified_by": "Administrator",
"module": "Stock",
"name": "Landed Cost Taxes and Charges",

View File

@@ -20,6 +20,7 @@ class LandedCostTaxesandCharges(Document):
description: DF.SmallText
exchange_rate: DF.Float
expense_account: DF.Link | None
has_corrective_cost: DF.Check
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data

View File

@@ -2892,17 +2892,6 @@ def get_operating_cost_per_unit(work_order=None, bom_no=None):
if bom.quantity:
operating_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity)
if (
work_order
and work_order.produced_qty
and cint(
frappe.db.get_single_value(
"Manufacturing Settings", "add_corrective_operation_cost_in_finished_good_valuation"
)
)
):
operating_cost_per_unit += flt(work_order.corrective_operation_cost) / flt(work_order.produced_qty)
return operating_cost_per_unit