mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-21 22:19:18 +00:00
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:
@@ -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()
|
@frappe.whitelist()
|
||||||
def get_bom_diff(bom1, bom2):
|
def get_bom_diff(bom1, bom2):
|
||||||
|
|||||||
@@ -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(
|
self.append(
|
||||||
"items",
|
"items",
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -442,6 +442,90 @@ class TestJobCard(IntegrationTestCase):
|
|||||||
cost_after_cancel = self.work_order.total_operating_cost
|
cost_after_cancel = self.work_order.total_operating_cost
|
||||||
self.assertEqual(cost_after_cancel, original_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 test_job_card_statuses(self):
|
||||||
def assertStatus(status):
|
def assertStatus(status):
|
||||||
jc.set_status()
|
jc.set_status()
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
"description",
|
"description",
|
||||||
"col_break3",
|
"col_break3",
|
||||||
"amount",
|
"amount",
|
||||||
"base_amount"
|
"base_amount",
|
||||||
|
"has_corrective_cost"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -62,12 +63,19 @@
|
|||||||
"label": "Amount (Company Currency)",
|
"label": "Amount (Company Currency)",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "has_corrective_cost",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Has Corrective Cost",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-03-27 13:09:59.493991",
|
"modified": "2025-01-20 12:22:03.455762",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Landed Cost Taxes and Charges",
|
"name": "Landed Cost Taxes and Charges",
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class LandedCostTaxesandCharges(Document):
|
|||||||
description: DF.SmallText
|
description: DF.SmallText
|
||||||
exchange_rate: DF.Float
|
exchange_rate: DF.Float
|
||||||
expense_account: DF.Link | None
|
expense_account: DF.Link | None
|
||||||
|
has_corrective_cost: DF.Check
|
||||||
parent: DF.Data
|
parent: DF.Data
|
||||||
parentfield: DF.Data
|
parentfield: DF.Data
|
||||||
parenttype: DF.Data
|
parenttype: DF.Data
|
||||||
|
|||||||
@@ -2892,17 +2892,6 @@ def get_operating_cost_per_unit(work_order=None, bom_no=None):
|
|||||||
if bom.quantity:
|
if bom.quantity:
|
||||||
operating_cost_per_unit = flt(bom.operating_cost) / flt(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
|
return operating_cost_per_unit
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user