Merge branch 'develop' into SCR-SCRAP-ITEMS
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index b396b27..b1ce539 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -436,24 +436,6 @@
# validate rate with ref PR
- def validate_rejected_warehouse(self):
- for item in self.get("items"):
- if flt(item.rejected_qty) and not item.rejected_warehouse:
- if self.rejected_warehouse:
- item.rejected_warehouse = self.rejected_warehouse
-
- if not item.rejected_warehouse:
- frappe.throw(
- _("Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1}").format(
- item.idx, item.item_code
- )
- )
-
- if item.get("rejected_warehouse") and (item.get("rejected_warehouse") == item.get("warehouse")):
- frappe.throw(
- _("Row #{0}: Accepted Warehouse and Rejected Warehouse cannot be same").format(item.idx)
- )
-
# validate accepted and rejected qty
def validate_accepted_rejected_qty(self):
for d in self.get("items"):
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
index 6633f4f..913c80b 100644
--- a/erpnext/controllers/subcontracting_controller.py
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -55,6 +55,23 @@
else:
super(SubcontractingController, self).validate()
+ def validate_rejected_warehouse(self):
+ for item in self.get("items"):
+ if flt(item.rejected_qty) and not item.rejected_warehouse:
+ if self.rejected_warehouse:
+ item.rejected_warehouse = self.rejected_warehouse
+ else:
+ frappe.throw(
+ _("Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1}").format(
+ item.idx, item.item_code
+ )
+ )
+
+ if item.get("rejected_warehouse") and (item.get("rejected_warehouse") == item.get("warehouse")):
+ frappe.throw(
+ _("Row #{0}: Accepted Warehouse and Rejected Warehouse cannot be same").format(item.idx)
+ )
+
def remove_empty_rows(self):
for key in ["service_items", "items", "supplied_items"]:
if self.get(key):
@@ -80,23 +97,27 @@
if not is_stock_item:
frappe.throw(_("Row {0}: Item {1} must be a stock item.").format(item.idx, item.item_name))
- if not is_sub_contracted_item:
- frappe.throw(
- _("Row {0}: Item {1} must be a subcontracted item.").format(item.idx, item.item_name)
- )
+ if not item.is_scrap_item:
+ if not is_sub_contracted_item:
+ frappe.throw(
+ _("Row {0}: Item {1} must be a subcontracted item.").format(item.idx, item.item_name)
+ )
- if item.bom:
- bom = frappe.get_doc("BOM", item.bom)
- if not bom.is_active:
- frappe.throw(
- _("Row {0}: Please select an active BOM for Item {1}.").format(item.idx, item.item_name)
- )
- if bom.item != item.item_code:
- frappe.throw(
- _("Row {0}: Please select an valid BOM for Item {1}.").format(item.idx, item.item_name)
- )
+ if item.bom:
+ is_active, bom_item = frappe.get_value("BOM", item.bom, ["is_active", "item"])
+
+ if not is_active:
+ frappe.throw(
+ _("Row {0}: Please select an active BOM for Item {1}.").format(item.idx, item.item_name)
+ )
+ if bom_item != item.item_code:
+ frappe.throw(
+ _("Row {0}: Please select an valid BOM for Item {1}.").format(item.idx, item.item_name)
+ )
+ else:
+ frappe.throw(_("Row {0}: Please select a BOM for Item {1}.").format(item.idx, item.item_name))
else:
- frappe.throw(_("Row {0}: Please select a BOM for Item {1}.").format(item.idx, item.item_name))
+ item.bom = None
def __get_data_before_save(self):
item_dict = {}
@@ -874,19 +895,24 @@
if self.total_additional_costs:
if self.distribute_additional_costs_based_on == "Amount":
- total_amt = sum(flt(item.amount) for item in self.get("items"))
+ total_amt = sum(
+ flt(item.amount) for item in self.get("items") if not item.get("is_scrap_item")
+ )
for item in self.items:
- item.additional_cost_per_qty = (
- (item.amount * self.total_additional_costs) / total_amt
- ) / item.qty
+ if not item.get("is_scrap_item"):
+ item.additional_cost_per_qty = (
+ (item.amount * self.total_additional_costs) / total_amt
+ ) / item.qty
else:
- total_qty = sum(flt(item.qty) for item in self.get("items"))
+ total_qty = sum(flt(item.qty) for item in self.get("items") if not item.get("is_scrap_item"))
additional_cost_per_qty = self.total_additional_costs / total_qty
for item in self.items:
- item.additional_cost_per_qty = additional_cost_per_qty
+ if not item.get("is_scrap_item"):
+ item.additional_cost_per_qty = additional_cost_per_qty
else:
for item in self.items:
- item.additional_cost_per_qty = 0
+ if not item.get("is_scrap_item"):
+ item.additional_cost_per_qty = 0
@frappe.whitelist()
def get_current_stock(self):
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
index e374077..acf9553 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
@@ -22,7 +22,7 @@
to_date: moment(frm.doc.modified).format('YYYY-MM-DD'),
company: frm.doc.company,
show_cancelled_entries: frm.doc.docstatus === 2
- };
+ }
frappe.set_route('query-report', 'Stock Ledger');
}, __('View'));
@@ -34,7 +34,7 @@
company: frm.doc.company,
group_by: 'Group by Voucher (Consolidated)',
show_cancelled_entries: frm.doc.docstatus === 2
- };
+ }
frappe.set_route('query-report', 'General Ledger');
}, __('View'));
}
@@ -94,7 +94,7 @@
company: frm.doc.company,
is_group: 0
}
- };
+ }
});
frm.set_query('rejected_warehouse', () => {
@@ -103,7 +103,7 @@
company: frm.doc.company,
is_group: 0
}
- };
+ }
});
frm.set_query('supplier_warehouse', () => {
@@ -112,7 +112,7 @@
company: frm.doc.company,
is_group: 0
}
- };
+ }
});
frm.set_query('warehouse', 'items', () => ({
@@ -129,10 +129,12 @@
}
}));
- frm.set_query('expense_account', 'items', () => ({
+ frm.set_query('expense_account', 'items', () => {
+ return {
query: 'erpnext.controllers.queries.get_expense_account',
filters: { 'company': frm.doc.company }
- }));
+ }
+ });
frm.set_query('batch_no', 'items', (doc, cdt, cdn) => {
var row = locals[cdt][cdn];
@@ -140,7 +142,7 @@
filters: {
item: row.item_code
}
- };
+ }
});
frm.set_query('batch_no', 'supplied_items', (doc, cdt, cdn) => {
@@ -149,7 +151,7 @@
filters: {
item: row.rm_item_code
}
- };
+ }
});
frm.set_query('serial_and_batch_bundle', 'supplied_items', (doc, cdt, cdn) => {
@@ -171,7 +173,7 @@
'item_code': row.doc.rm_item_code,
'voucher_type': frm.doc.doctype,
}
- };
+ }
}
let batch_no_field = frm.get_docfield('items', 'batch_no');
@@ -180,7 +182,7 @@
return {
'item': row.doc.item_code
}
- };
+ }
}
},
@@ -190,15 +192,37 @@
transaction_controller.setup_quality_inspection();
}
},
+
+ get_scrap_items: (frm) => {
+ frappe.call({
+ doc: frm.doc,
+ method: 'get_scrap_items',
+ args: {
+ recalculate_rate: true
+ },
+ freeze: true,
+ freeze_message: __('Getting Scrap Items'),
+ callback: (r) => {
+ if (!r.exc) {
+ frm.refresh();
+ }
+ }
+ });
+ },
});
frappe.ui.form.on('Landed Cost Taxes and Charges', {
amount: (frm, cdt, cdn) => {
+ set_missing_values(frm);
frm.events.set_base_amount(frm, cdt, cdn);
},
expense_account: (frm, cdt, cdn) => {
frm.events.set_account_currency(frm, cdt, cdn);
+ },
+
+ additional_costs_remove: (frm) => {
+ set_missing_values(frm);
}
});
@@ -214,6 +238,16 @@
rate(frm) {
set_missing_values(frm);
},
+
+ recalculate_rate(frm) {
+ if (frm.doc.recalculate_rate) {
+ set_missing_values(frm);
+ }
+ },
+
+ items_remove: (frm) => {
+ set_missing_values(frm);
+ },
});
frappe.ui.form.on('Subcontracting Receipt Supplied Item', {
@@ -225,7 +259,7 @@
let set_warehouse_in_children = (child_table, warehouse_field, warehouse) => {
let transaction_controller = new erpnext.TransactionController();
transaction_controller.autofill_warehouse(child_table, warehouse_field, warehouse);
-};
+}
let set_missing_values = (frm) => {
frappe.call({
@@ -235,4 +269,4 @@
if (!r.exc) frm.refresh();
},
});
-};
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
index 4b3cc83..8be1c1b 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
@@ -40,6 +40,7 @@
"col_break_warehouse",
"supplier_warehouse",
"items_section",
+ "get_scrap_items",
"items",
"section_break0",
"total_qty",
@@ -285,7 +286,7 @@
"reqd": 1
},
{
- "depends_on": "supplied_items",
+ "depends_on": "eval: (!doc.__islocal && doc.docstatus == 0 && doc.supplied_items)",
"fieldname": "get_current_stock",
"fieldtype": "Button",
"label": "Get Current Stock",
@@ -626,12 +627,19 @@
"fieldtype": "Check",
"label": "Edit Posting Date and Time",
"print_hide": 1
+ },
+ {
+ "depends_on": "eval: (!doc.__islocal && doc.docstatus == 0)",
+ "fieldname": "get_scrap_items",
+ "fieldtype": "Button",
+ "label": "Get Scrap Items",
+ "options": "get_scrap_items"
}
],
"in_create": 1,
"is_submittable": 1,
"links": [],
- "modified": "2023-07-06 18:43:16.171842",
+ "modified": "2023-08-26 10:52:04.050829",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Receipt",
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
index afe1b60..c601ddb 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
@@ -36,33 +36,6 @@
),
)
- def update_status_updater_args(self):
- if cint(self.is_return):
- self.status_updater.extend(
- [
- {
- "source_dt": "Subcontracting Receipt Item",
- "target_dt": "Subcontracting Order Item",
- "join_field": "subcontracting_order_item",
- "target_field": "returned_qty",
- "source_field": "-1 * qty",
- "extra_cond": """ and exists (select name from `tabSubcontracting Receipt`
- where name=`tabSubcontracting Receipt Item`.parent and is_return=1)""",
- },
- {
- "source_dt": "Subcontracting Receipt Item",
- "target_dt": "Subcontracting Receipt Item",
- "join_field": "subcontracting_receipt_item",
- "target_field": "returned_qty",
- "target_parent_dt": "Subcontracting Receipt",
- "target_parent_field": "per_returned",
- "target_ref_field": "received_qty",
- "source_field": "-1 * received_qty",
- "percent_join_field_parent": "return_against",
- },
- ]
- )
-
def before_validate(self):
super(SubcontractingReceipt, self).before_validate()
self.validate_items_qty()
@@ -71,15 +44,8 @@
self.set_items_expense_account()
def validate(self):
- if (
- frappe.db.get_single_value("Buying Settings", "backflush_raw_materials_of_subcontract_based_on")
- == "BOM"
- ):
- self.supplied_items = []
- super(SubcontractingReceipt, self).validate()
- self.set_missing_values()
+ self.reset_supplied_items()
self.validate_posting_time()
- self.validate_rejected_warehouse()
if not self.get("is_return"):
self.validate_inspection()
@@ -87,15 +53,22 @@
if getdate(self.posting_date) > getdate(nowdate()):
frappe.throw(_("Posting Date cannot be future date"))
+ super(SubcontractingReceipt, self).validate()
+
+ if self.is_new() and self.get("_action") == "save":
+ self.get_scrap_items()
+
+ self.set_missing_values()
+
+ if self.get("_action") == "submit":
+ self.validate_scrap_items()
+ self.validate_accepted_warehouse()
+ self.validate_rejected_warehouse()
+
self.reset_default_field_value("set_warehouse", "items", "warehouse")
self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
self.get_current_stock()
- def on_update(self):
- for table_field in ["items", "supplied_items"]:
- if self.get(table_field):
- self.set_serial_and_batch_bundle(table_field)
-
def on_submit(self):
self.validate_available_qty_for_consumption()
self.update_status_updater_args()
@@ -107,6 +80,11 @@
self.repost_future_sle_and_gle()
self.update_status()
+ def on_update(self):
+ for table_field in ["items", "supplied_items"]:
+ if self.get(table_field):
+ self.set_serial_and_batch_bundle(table_field)
+
def on_cancel(self):
self.ignore_linked_doctypes = (
"GL Entry",
@@ -124,108 +102,6 @@
self.set_subcontracting_order_status()
self.update_status()
- @frappe.whitelist()
- def set_missing_values(self):
- self.calculate_additional_costs()
- self.calculate_supplied_items_qty_and_amount()
- self.calculate_items_qty_and_amount()
-
- def set_available_qty_for_consumption(self):
- supplied_items_details = {}
-
- sco_supplied_item = frappe.qb.DocType("Subcontracting Order Supplied Item")
- for item in self.get("items"):
- supplied_items = (
- frappe.qb.from_(sco_supplied_item)
- .select(
- sco_supplied_item.rm_item_code,
- sco_supplied_item.reference_name,
- (sco_supplied_item.total_supplied_qty - sco_supplied_item.consumed_qty).as_("available_qty"),
- )
- .where(
- (sco_supplied_item.parent == item.subcontracting_order)
- & (sco_supplied_item.main_item_code == item.item_code)
- & (sco_supplied_item.reference_name == item.subcontracting_order_item)
- )
- ).run(as_dict=True)
-
- if supplied_items:
- supplied_items_details[item.name] = {}
-
- for supplied_item in supplied_items:
- supplied_items_details[item.name][supplied_item.rm_item_code] = supplied_item.available_qty
- else:
- for item in self.get("supplied_items"):
- item.available_qty_for_consumption = supplied_items_details.get(item.reference_name, {}).get(
- item.rm_item_code, 0
- )
-
- def calculate_supplied_items_qty_and_amount(self):
- for item in self.get("supplied_items") or []:
- item.amount = item.rate * item.consumed_qty
-
- self.set_available_qty_for_consumption()
-
- def calculate_items_qty_and_amount(self):
- rm_supp_cost = {}
- for item in self.get("supplied_items") or []:
- if item.reference_name in rm_supp_cost:
- rm_supp_cost[item.reference_name] += item.amount
- else:
- rm_supp_cost[item.reference_name] = item.amount
-
- total_qty = total_amount = 0
- for item in self.items:
- if item.qty and item.name in rm_supp_cost:
- item.rm_supp_cost = rm_supp_cost[item.name]
- item.rm_cost_per_qty = item.rm_supp_cost / item.qty
- rm_supp_cost.pop(item.name)
-
- if item.recalculate_rate:
- item.rate = (
- flt(item.rm_cost_per_qty) + flt(item.service_cost_per_qty) + flt(item.additional_cost_per_qty)
- )
-
- item.received_qty = item.qty + flt(item.rejected_qty)
- item.amount = item.qty * item.rate
- total_qty += item.qty
- total_amount += item.amount
- else:
- self.total_qty = total_qty
- self.total = total_amount
-
- def validate_rejected_warehouse(self):
- for item in self.items:
- if flt(item.rejected_qty) and not item.rejected_warehouse:
- if self.rejected_warehouse:
- item.rejected_warehouse = self.rejected_warehouse
-
- if not item.rejected_warehouse:
- frappe.throw(
- _("Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1}").format(
- item.idx, item.item_code
- )
- )
-
- if item.get("rejected_warehouse") and (item.get("rejected_warehouse") == item.get("warehouse")):
- frappe.throw(
- _("Row #{0}: Accepted Warehouse and Rejected Warehouse cannot be same").format(item.idx)
- )
-
- def validate_available_qty_for_consumption(self):
- for item in self.get("supplied_items"):
- precision = item.precision("consumed_qty")
- if (
- item.available_qty_for_consumption
- and flt(item.available_qty_for_consumption, precision) - flt(item.consumed_qty, precision) < 0
- ):
- msg = f"""Row {item.idx}: Consumed Qty {flt(item.consumed_qty, precision)}
- must be less than or equal to Available Qty For Consumption
- {flt(item.available_qty_for_consumption, precision)}
- in Consumed Items Table."""
-
- frappe.throw(_(msg))
-
def validate_items_qty(self):
for item in self.items:
if not (item.qty or item.rejected_qty):
@@ -267,6 +143,225 @@
if not item.expense_account:
item.expense_account = expense_account
+ def reset_supplied_items(self):
+ if (
+ frappe.db.get_single_value("Buying Settings", "backflush_raw_materials_of_subcontract_based_on")
+ == "BOM"
+ ):
+ self.supplied_items = []
+
+ @frappe.whitelist()
+ def get_scrap_items(self, recalculate_rate=False):
+ self.remove_scrap_items()
+
+ for item in list(self.items):
+ if item.bom:
+ bom = frappe.get_doc("BOM", item.bom)
+ for scrap_item in bom.scrap_items:
+ qty = flt(item.qty) * (flt(scrap_item.stock_qty) / flt(bom.quantity))
+ self.append(
+ "items",
+ {
+ "is_scrap_item": 1,
+ "reference_name": item.name,
+ "item_code": scrap_item.item_code,
+ "item_name": scrap_item.item_name,
+ "qty": qty,
+ "stock_uom": scrap_item.stock_uom,
+ "recalculate_rate": 0,
+ "rate": scrap_item.rate,
+ "rm_cost_per_qty": 0,
+ "service_cost_per_qty": 0,
+ "additional_cost_per_qty": 0,
+ "scrap_cost_per_qty": 0,
+ "amount": qty * scrap_item.rate,
+ "warehouse": self.set_warehouse,
+ "rejected_warehouse": self.rejected_warehouse,
+ },
+ )
+
+ if recalculate_rate:
+ self.calculate_additional_costs()
+ self.calculate_items_qty_and_amount()
+
+ def remove_scrap_items(self, recalculate_rate=False):
+ for item in list(self.items):
+ if item.is_scrap_item:
+ self.remove(item)
+ else:
+ item.scrap_cost_per_qty = 0
+
+ if recalculate_rate:
+ self.calculate_items_qty_and_amount()
+
+ @frappe.whitelist()
+ def set_missing_values(self):
+ self.set_available_qty_for_consumption()
+ self.calculate_additional_costs()
+ self.calculate_items_qty_and_amount()
+
+ def set_available_qty_for_consumption(self):
+ supplied_items_details = {}
+
+ sco_supplied_item = frappe.qb.DocType("Subcontracting Order Supplied Item")
+ for item in self.get("items"):
+ supplied_items = (
+ frappe.qb.from_(sco_supplied_item)
+ .select(
+ sco_supplied_item.rm_item_code,
+ sco_supplied_item.reference_name,
+ (sco_supplied_item.total_supplied_qty - sco_supplied_item.consumed_qty).as_("available_qty"),
+ )
+ .where(
+ (sco_supplied_item.parent == item.subcontracting_order)
+ & (sco_supplied_item.main_item_code == item.item_code)
+ & (sco_supplied_item.reference_name == item.subcontracting_order_item)
+ )
+ ).run(as_dict=True)
+
+ if supplied_items:
+ supplied_items_details[item.name] = {}
+
+ for supplied_item in supplied_items:
+ supplied_items_details[item.name][supplied_item.rm_item_code] = supplied_item.available_qty
+ else:
+ for item in self.get("supplied_items"):
+ item.available_qty_for_consumption = supplied_items_details.get(item.reference_name, {}).get(
+ item.rm_item_code, 0
+ )
+
+ def calculate_items_qty_and_amount(self):
+ rm_cost_map = {}
+ for item in self.get("supplied_items") or []:
+ item.amount = flt(item.consumed_qty) * flt(item.rate)
+
+ if item.reference_name in rm_cost_map:
+ rm_cost_map[item.reference_name] += item.amount
+ else:
+ rm_cost_map[item.reference_name] = item.amount
+
+ scrap_cost_map = {}
+ for item in self.get("items") or []:
+ if item.is_scrap_item:
+ item.amount = flt(item.qty) * flt(item.rate)
+
+ if item.reference_name in scrap_cost_map:
+ scrap_cost_map[item.reference_name] += item.amount
+ else:
+ scrap_cost_map[item.reference_name] = item.amount
+
+ total_qty = total_amount = 0
+ for item in self.get("items") or []:
+ if not item.is_scrap_item:
+ if item.qty:
+ if item.name in rm_cost_map:
+ item.rm_supp_cost = rm_cost_map[item.name]
+ item.rm_cost_per_qty = item.rm_supp_cost / item.qty
+ rm_cost_map.pop(item.name)
+
+ if item.name in scrap_cost_map:
+ item.scrap_cost_per_qty = scrap_cost_map[item.name] / item.qty
+ scrap_cost_map.pop(item.name)
+ else:
+ item.scrap_cost_per_qty = 0
+
+ if item.recalculate_rate:
+ item.rate = (
+ flt(item.rm_cost_per_qty)
+ + flt(item.service_cost_per_qty)
+ + flt(item.additional_cost_per_qty)
+ - flt(item.scrap_cost_per_qty)
+ )
+
+ item.received_qty = flt(item.qty) + flt(item.rejected_qty)
+ item.amount = flt(item.qty) * flt(item.rate)
+
+ total_qty += flt(item.qty)
+ total_amount += item.amount
+ else:
+ self.total_qty = total_qty
+ self.total = total_amount
+
+ def validate_scrap_items(self):
+ for item in self.items:
+ if item.is_scrap_item:
+ if not item.qty:
+ frappe.throw(
+ _("Row #{0}: Scrap Item Qty cannot be zero").format(item.idx),
+ )
+
+ if item.rejected_qty:
+ frappe.throw(
+ _("Row #{0}: Rejected Qty cannot be set for Scrap Item {1}.").format(
+ item.idx, frappe.bold(item.item_code)
+ ),
+ )
+
+ if not item.reference_name:
+ frappe.throw(
+ _("Row #{0}: Finished Good reference is mandatory for Scrap Item {1}.").format(
+ item.idx, frappe.bold(item.item_code)
+ ),
+ )
+
+ def validate_accepted_warehouse(self):
+ for item in self.get("items"):
+ if flt(item.qty) and not item.warehouse:
+ if self.set_warehouse:
+ item.warehouse = self.set_warehouse
+ else:
+ frappe.throw(
+ _("Row #{0}: Accepted Warehouse is mandatory for the accepted Item {1}").format(
+ item.idx, item.item_code
+ )
+ )
+
+ if item.get("warehouse") and (item.get("warehouse") == item.get("rejected_warehouse")):
+ frappe.throw(
+ _("Row #{0}: Accepted Warehouse and Rejected Warehouse cannot be same").format(item.idx)
+ )
+
+ def validate_available_qty_for_consumption(self):
+ for item in self.get("supplied_items"):
+ precision = item.precision("consumed_qty")
+ if (
+ item.available_qty_for_consumption
+ and flt(item.available_qty_for_consumption, precision) - flt(item.consumed_qty, precision) < 0
+ ):
+ msg = f"""Row {item.idx}: Consumed Qty {flt(item.consumed_qty, precision)}
+ must be less than or equal to Available Qty For Consumption
+ {flt(item.available_qty_for_consumption, precision)}
+ in Consumed Items Table."""
+
+ frappe.throw(_(msg))
+
+ def update_status_updater_args(self):
+ if cint(self.is_return):
+ self.status_updater.extend(
+ [
+ {
+ "source_dt": "Subcontracting Receipt Item",
+ "target_dt": "Subcontracting Order Item",
+ "join_field": "subcontracting_order_item",
+ "target_field": "returned_qty",
+ "source_field": "-1 * qty",
+ "extra_cond": """ and exists (select name from `tabSubcontracting Receipt`
+ where name=`tabSubcontracting Receipt Item`.parent and is_return=1)""",
+ },
+ {
+ "source_dt": "Subcontracting Receipt Item",
+ "target_dt": "Subcontracting Receipt Item",
+ "join_field": "subcontracting_receipt_item",
+ "target_field": "returned_qty",
+ "target_parent_dt": "Subcontracting Receipt",
+ "target_parent_field": "per_returned",
+ "target_ref_field": "received_qty",
+ "source_field": "-1 * received_qty",
+ "percent_join_field_parent": "return_against",
+ },
+ ]
+ )
+
def update_status(self, status=None, update_modified=False):
if not status:
if self.docstatus == 0:
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
index d728780..c036390 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
@@ -10,6 +10,7 @@
"item_code",
"column_break_2",
"item_name",
+ "is_scrap_item",
"section_break_4",
"description",
"brand",
@@ -24,8 +25,6 @@
"col_break2",
"stock_uom",
"conversion_factor",
- "tracking_section",
- "col_break_tracking_section",
"rate_and_amount",
"rate",
"amount",
@@ -34,18 +33,20 @@
"rm_cost_per_qty",
"service_cost_per_qty",
"additional_cost_per_qty",
+ "scrap_cost_per_qty",
"rm_supp_cost",
"warehouse_and_reference",
"warehouse",
- "rejected_warehouse",
"subcontracting_order",
- "column_break_40",
- "schedule_date",
- "quality_inspection",
"subcontracting_order_item",
"subcontracting_receipt_item",
- "section_break_45",
+ "column_break_40",
+ "rejected_warehouse",
"bom",
+ "quality_inspection",
+ "schedule_date",
+ "reference_name",
+ "section_break_45",
"serial_and_batch_bundle",
"serial_no",
"col_break5",
@@ -85,12 +86,13 @@
"fieldtype": "Column Break"
},
{
+ "fetch_from": "item_code.item_name",
+ "fetch_if_empty": 1,
"fieldname": "item_name",
"fieldtype": "Data",
"in_global_search": 1,
"label": "Item Name",
- "print_hide": 1,
- "reqd": 1
+ "print_hide": 1
},
{
"collapsible": 1,
@@ -99,11 +101,12 @@
"label": "Description"
},
{
+ "fetch_from": "item_code.description",
+ "fetch_if_empty": 1,
"fieldname": "description",
"fieldtype": "Text Editor",
"label": "Description",
"print_width": "300px",
- "reqd": 1,
"width": "300px"
},
{
@@ -157,6 +160,7 @@
"no_copy": 1,
"print_hide": 1,
"print_width": "100px",
+ "read_only_depends_on": "eval: doc.is_scrap_item",
"width": "100px"
},
{
@@ -214,6 +218,8 @@
"fieldtype": "Column Break"
},
{
+ "default": "0",
+ "depends_on": "eval: !doc.is_scrap_item",
"fieldname": "rm_cost_per_qty",
"fieldtype": "Currency",
"label": "Raw Material Cost Per Qty",
@@ -221,6 +227,8 @@
"read_only": 1
},
{
+ "default": "0",
+ "depends_on": "eval: !doc.is_scrap_item",
"fieldname": "service_cost_per_qty",
"fieldtype": "Currency",
"label": "Service Cost Per Qty",
@@ -229,6 +237,7 @@
},
{
"default": "0",
+ "depends_on": "eval: !doc.is_scrap_item",
"fieldname": "additional_cost_per_qty",
"fieldtype": "Currency",
"label": "Additional Cost Per Qty",
@@ -260,6 +269,7 @@
"options": "Warehouse",
"print_hide": 1,
"print_width": "100px",
+ "read_only_depends_on": "eval: doc.is_scrap_item",
"width": "100px"
},
{
@@ -295,7 +305,8 @@
},
{
"fieldname": "section_break_45",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Serial and Batch Details"
},
{
"depends_on": "eval:!doc.is_fixed_asset",
@@ -321,7 +332,8 @@
"fieldtype": "Small Text",
"label": "Rejected Serial No",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "read_only": 1
},
{
"fieldname": "subcontracting_order_item",
@@ -345,7 +357,8 @@
"label": "BOM",
"no_copy": 1,
"options": "BOM",
- "print_hide": 1
+ "print_hide": 1,
+ "read_only_depends_on": "eval: doc.is_scrap_item"
},
{
"fetch_from": "item_code.brand",
@@ -411,14 +424,6 @@
"fieldtype": "Column Break"
},
{
- "fieldname": "tracking_section",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "col_break_tracking_section",
- "fieldtype": "Column Break"
- },
- {
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
"label": "Accounting Dimensions"
@@ -456,6 +461,7 @@
"print_hide": 1
},
{
+ "default": "0",
"depends_on": "returned_qty",
"fieldname": "returned_qty",
"fieldtype": "Float",
@@ -471,9 +477,11 @@
},
{
"default": "1",
+ "depends_on": "eval: !doc.is_scrap_item",
"fieldname": "recalculate_rate",
"fieldtype": "Check",
- "label": "Recalculate Rate"
+ "label": "Recalculate Rate",
+ "read_only_depends_on": "eval: doc.is_scrap_item"
},
{
"fieldname": "serial_and_batch_bundle",
@@ -490,12 +498,40 @@
"no_copy": 1,
"options": "Serial and Batch Bundle",
"print_hide": 1
+ },
+ {
+ "default": "0",
+ "depends_on": "eval: !doc.bom",
+ "fieldname": "is_scrap_item",
+ "fieldtype": "Check",
+ "label": "Is Scrap Item",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only_depends_on": "eval: doc.bom"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval: !doc.is_scrap_item",
+ "fieldname": "scrap_cost_per_qty",
+ "fieldtype": "Float",
+ "label": "Scrap Cost Per Qty",
+ "no_copy": 1,
+ "non_negative": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "reference_name",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Reference Name",
+ "no_copy": 1,
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2023-07-06 18:43:45.599761",
+ "modified": "2023-08-25 20:09:03.069417",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Receipt Item",