mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-07 15:12:51 +00:00
Merge branch 'develop' into refactor-util-use
This commit is contained in:
@@ -165,13 +165,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.toggle_get_items();
|
||||||
// Show buttons only when pos view is active
|
|
||||||
if (cint(doc.docstatus == 0) && this.frm.page.current_view_name !== "pos" && !doc.is_return) {
|
|
||||||
this.frm.cscript.sales_order_btn();
|
|
||||||
this.frm.cscript.delivery_note_btn();
|
|
||||||
this.frm.cscript.quotation_btn();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set_default_print_format();
|
this.set_default_print_format();
|
||||||
if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) {
|
if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) {
|
||||||
@@ -260,6 +254,93 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggle_get_items() {
|
||||||
|
const buttons = ["Sales Order", "Quotation", "Timesheet", "Delivery Note"];
|
||||||
|
|
||||||
|
buttons.forEach((label) => {
|
||||||
|
this.frm.remove_custom_button(label, "Get Items From");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cint(this.frm.doc.docstatus) !== 0 || this.frm.page.current_view_name === "pos") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.frm.doc.is_return) {
|
||||||
|
this.frm.cscript.sales_order_btn();
|
||||||
|
this.frm.cscript.quotation_btn();
|
||||||
|
this.frm.cscript.timesheet_btn();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.frm.cscript.delivery_note_btn();
|
||||||
|
}
|
||||||
|
|
||||||
|
timesheet_btn() {
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
me.frm.add_custom_button(
|
||||||
|
__("Timesheet"),
|
||||||
|
function () {
|
||||||
|
let d = new frappe.ui.Dialog({
|
||||||
|
title: __("Fetch Timesheet"),
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
label: __("From"),
|
||||||
|
fieldname: "from_time",
|
||||||
|
fieldtype: "Date",
|
||||||
|
reqd: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __("Item Code"),
|
||||||
|
fieldname: "item_code",
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Item",
|
||||||
|
get_query: () => {
|
||||||
|
return {
|
||||||
|
query: "erpnext.controllers.queries.item_query",
|
||||||
|
filters: {
|
||||||
|
is_sales_item: 1,
|
||||||
|
customer: me.frm.doc.customer,
|
||||||
|
has_variants: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Column Break",
|
||||||
|
fieldname: "col_break_1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __("To"),
|
||||||
|
fieldname: "to_time",
|
||||||
|
fieldtype: "Date",
|
||||||
|
reqd: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __("Project"),
|
||||||
|
fieldname: "project",
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Project",
|
||||||
|
default: me.frm.doc.project,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primary_action: function () {
|
||||||
|
const data = d.get_values();
|
||||||
|
me.frm.events.add_timesheet_data(me.frm, {
|
||||||
|
from_time: data.from_time,
|
||||||
|
to_time: data.to_time,
|
||||||
|
project: data.project,
|
||||||
|
item_code: data.item_code,
|
||||||
|
});
|
||||||
|
d.hide();
|
||||||
|
},
|
||||||
|
primary_action_label: __("Get Timesheets"),
|
||||||
|
});
|
||||||
|
d.show();
|
||||||
|
},
|
||||||
|
__("Get Items From")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
sales_order_btn() {
|
sales_order_btn() {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
|
||||||
@@ -331,6 +412,12 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
|
|||||||
this.$delivery_note_btn = this.frm.add_custom_button(
|
this.$delivery_note_btn = this.frm.add_custom_button(
|
||||||
__("Delivery Note"),
|
__("Delivery Note"),
|
||||||
function () {
|
function () {
|
||||||
|
if (!me.frm.doc.customer) {
|
||||||
|
frappe.throw({
|
||||||
|
title: __("Mandatory"),
|
||||||
|
message: __("Please Select a Customer"),
|
||||||
|
});
|
||||||
|
}
|
||||||
erpnext.utils.map_current_doc({
|
erpnext.utils.map_current_doc({
|
||||||
method: "erpnext.stock.doctype.delivery_note.delivery_note.make_sales_invoice",
|
method: "erpnext.stock.doctype.delivery_note.delivery_note.make_sales_invoice",
|
||||||
source_doctype: "Delivery Note",
|
source_doctype: "Delivery Note",
|
||||||
@@ -343,7 +430,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
|
|||||||
var filters = {
|
var filters = {
|
||||||
docstatus: 1,
|
docstatus: 1,
|
||||||
company: me.frm.doc.company,
|
company: me.frm.doc.company,
|
||||||
is_return: 0,
|
is_return: me.frm.doc.is_return,
|
||||||
};
|
};
|
||||||
if (me.frm.doc.customer) filters["customer"] = me.frm.doc.customer;
|
if (me.frm.doc.customer) filters["customer"] = me.frm.doc.customer;
|
||||||
return {
|
return {
|
||||||
@@ -610,6 +697,10 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
|
|||||||
apply_tds(frm) {
|
apply_tds(frm) {
|
||||||
this.frm.clear_table("tax_withholding_entries");
|
this.frm.clear_table("tax_withholding_entries");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_return() {
|
||||||
|
this.toggle_get_items();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// for backward compatibility: combine new and previous states
|
// for backward compatibility: combine new and previous states
|
||||||
@@ -1061,71 +1152,6 @@ frappe.ui.form.on("Sales Invoice", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
refresh: function (frm) {
|
refresh: function (frm) {
|
||||||
if (frm.doc.docstatus === 0 && !frm.doc.is_return) {
|
|
||||||
frm.add_custom_button(
|
|
||||||
__("Timesheet"),
|
|
||||||
function () {
|
|
||||||
let d = new frappe.ui.Dialog({
|
|
||||||
title: __("Fetch Timesheet"),
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
label: __("From"),
|
|
||||||
fieldname: "from_time",
|
|
||||||
fieldtype: "Date",
|
|
||||||
reqd: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: __("Item Code"),
|
|
||||||
fieldname: "item_code",
|
|
||||||
fieldtype: "Link",
|
|
||||||
options: "Item",
|
|
||||||
get_query: () => {
|
|
||||||
return {
|
|
||||||
query: "erpnext.controllers.queries.item_query",
|
|
||||||
filters: {
|
|
||||||
is_sales_item: 1,
|
|
||||||
customer: frm.doc.customer,
|
|
||||||
has_variants: 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldtype: "Column Break",
|
|
||||||
fieldname: "col_break_1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: __("To"),
|
|
||||||
fieldname: "to_time",
|
|
||||||
fieldtype: "Date",
|
|
||||||
reqd: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: __("Project"),
|
|
||||||
fieldname: "project",
|
|
||||||
fieldtype: "Link",
|
|
||||||
options: "Project",
|
|
||||||
default: frm.doc.project,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
primary_action: function () {
|
|
||||||
const data = d.get_values();
|
|
||||||
frm.events.add_timesheet_data(frm, {
|
|
||||||
from_time: data.from_time,
|
|
||||||
to_time: data.to_time,
|
|
||||||
project: data.project,
|
|
||||||
item_code: data.item_code,
|
|
||||||
});
|
|
||||||
d.hide();
|
|
||||||
},
|
|
||||||
primary_action_label: __("Get Timesheets"),
|
|
||||||
});
|
|
||||||
d.show();
|
|
||||||
},
|
|
||||||
__("Get Items From")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frm.doc.is_debit_note) {
|
if (frm.doc.is_debit_note) {
|
||||||
frm.set_df_property("return_against", "label", __("Adjustment Against"));
|
frm.set_df_property("return_against", "label", __("Adjustment Against"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -379,39 +379,42 @@ def get_project_name(
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def get_delivery_notes_to_be_billed(
|
def get_delivery_notes_to_be_billed(
|
||||||
doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict, as_dict: bool
|
doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict, as_dict: bool = False
|
||||||
):
|
):
|
||||||
doctype = "Delivery Note"
|
DeliveryNote = frappe.qb.DocType("Delivery Note")
|
||||||
|
|
||||||
fields = get_fields(doctype, ["name", "customer", "posting_date"])
|
fields = get_fields(doctype, ["name", "customer", "posting_date"])
|
||||||
|
|
||||||
return frappe.db.sql(
|
original_dn = (
|
||||||
"""
|
frappe.qb.from_(DeliveryNote)
|
||||||
select {fields}
|
.select(DeliveryNote.name)
|
||||||
from `tabDelivery Note`
|
.where((DeliveryNote.docstatus == 1) & (DeliveryNote.is_return == 0) & (DeliveryNote.per_billed > 0))
|
||||||
where `tabDelivery Note`.`{key}` like {txt} and
|
)
|
||||||
`tabDelivery Note`.docstatus = 1
|
|
||||||
and status not in ('Stopped', 'Closed') {fcond}
|
query = (
|
||||||
and (
|
frappe.qb.from_(DeliveryNote)
|
||||||
(`tabDelivery Note`.is_return = 0 and `tabDelivery Note`.per_billed < 100)
|
.select(*[DeliveryNote[f] for f in fields])
|
||||||
or (`tabDelivery Note`.grand_total = 0 and `tabDelivery Note`.per_billed < 100)
|
.where(
|
||||||
or (
|
(DeliveryNote.docstatus == 1)
|
||||||
`tabDelivery Note`.is_return = 1
|
& (DeliveryNote.status.notin(["Stopped", "Closed"]))
|
||||||
and return_against in (select name from `tabDelivery Note` where per_billed < 100)
|
& (DeliveryNote[searchfield].like(f"%{txt}%"))
|
||||||
|
& (
|
||||||
|
((DeliveryNote.is_return == 0) & (DeliveryNote.per_billed < 100))
|
||||||
|
| ((DeliveryNote.grand_total == 0) & (DeliveryNote.per_billed < 100))
|
||||||
|
| (
|
||||||
|
(DeliveryNote.is_return == 1)
|
||||||
|
& (DeliveryNote.per_billed < 100)
|
||||||
|
& (DeliveryNote.return_against.isin(original_dn))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
{mcond} order by `tabDelivery Note`.`{key}` asc limit {page_len} offset {start}
|
|
||||||
""".format(
|
|
||||||
fields=", ".join([f"`tabDelivery Note`.{f}" for f in fields]),
|
|
||||||
key=searchfield,
|
|
||||||
fcond=get_filters_cond(doctype, filters, []),
|
|
||||||
mcond=get_match_cond(doctype),
|
|
||||||
start=start,
|
|
||||||
page_len=page_len,
|
|
||||||
txt="%(txt)s",
|
|
||||||
),
|
|
||||||
{"txt": ("%%%s%%" % txt)},
|
|
||||||
as_dict=as_dict,
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
if filters and isinstance(filters, dict):
|
||||||
|
for key, value in filters.items():
|
||||||
|
query = query.where(DeliveryNote[key] == value)
|
||||||
|
|
||||||
|
query = query.orderby(DeliveryNote[searchfield], order=Order.asc).limit(page_len).offset(start)
|
||||||
|
return query.run(as_dict=as_dict)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -4,6 +4,12 @@
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.query_builder import DocType
|
||||||
|
from frappe.query_builder.custom import GROUP_CONCAT
|
||||||
|
from frappe.query_builder.functions import Date
|
||||||
|
|
||||||
|
Opportunity = DocType("Opportunity")
|
||||||
|
OpportunityLostReasonDetail = DocType("Opportunity Lost Reason Detail")
|
||||||
|
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
@@ -66,58 +72,48 @@ def get_columns():
|
|||||||
|
|
||||||
|
|
||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
return frappe.db.sql(
|
query = (
|
||||||
f"""
|
frappe.qb.from_(Opportunity)
|
||||||
SELECT
|
.left_join(OpportunityLostReasonDetail)
|
||||||
`tabOpportunity`.name,
|
.on(
|
||||||
`tabOpportunity`.opportunity_from,
|
(OpportunityLostReasonDetail.parenttype == "Opportunity")
|
||||||
`tabOpportunity`.party_name,
|
& (OpportunityLostReasonDetail.parent == Opportunity.name)
|
||||||
`tabOpportunity`.customer_name,
|
)
|
||||||
`tabOpportunity`.opportunity_type,
|
.select(
|
||||||
GROUP_CONCAT(`tabOpportunity Lost Reason Detail`.lost_reason separator ', ') lost_reason,
|
Opportunity.name,
|
||||||
`tabOpportunity`.sales_stage,
|
Opportunity.opportunity_from,
|
||||||
`tabOpportunity`.territory
|
Opportunity.party_name,
|
||||||
FROM
|
Opportunity.customer_name,
|
||||||
`tabOpportunity`
|
Opportunity.opportunity_type,
|
||||||
{get_join(filters)}
|
GROUP_CONCAT(OpportunityLostReasonDetail.lost_reason, alias="lost_reason").separator(", "),
|
||||||
WHERE
|
Opportunity.sales_stage,
|
||||||
`tabOpportunity`.status = 'Lost' and `tabOpportunity`.company = %(company)s
|
Opportunity.territory,
|
||||||
AND DATE(`tabOpportunity`.modified) BETWEEN %(from_date)s AND %(to_date)s
|
)
|
||||||
{get_conditions(filters)}
|
.where(
|
||||||
GROUP BY
|
(Opportunity.status == "Lost")
|
||||||
`tabOpportunity`.name
|
& (Opportunity.company == filters.get("company"))
|
||||||
ORDER BY
|
& (Date(Opportunity.modified).between(filters.get("from_date"), filters.get("to_date")))
|
||||||
`tabOpportunity`.creation asc """,
|
)
|
||||||
filters,
|
.groupby(Opportunity.name)
|
||||||
as_dict=1,
|
.orderby(Opportunity.creation)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
query = get_conditions(filters, query)
|
||||||
|
|
||||||
def get_conditions(filters):
|
return query.run(as_dict=1)
|
||||||
conditions = []
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_conditions(filters, query):
|
||||||
if filters.get("territory"):
|
if filters.get("territory"):
|
||||||
conditions.append(" and `tabOpportunity`.territory=%(territory)s")
|
query = query.where(Opportunity.territory == filters.get("territory"))
|
||||||
|
|
||||||
if filters.get("opportunity_from"):
|
if filters.get("opportunity_from"):
|
||||||
conditions.append(" and `tabOpportunity`.opportunity_from=%(opportunity_from)s")
|
query = query.where(Opportunity.opportunity_from == filters.get("opportunity_from"))
|
||||||
|
|
||||||
if filters.get("party_name"):
|
if filters.get("party_name"):
|
||||||
conditions.append(" and `tabOpportunity`.party_name=%(party_name)s")
|
query = query.where(Opportunity.party_name == filters.get("party_name"))
|
||||||
|
|
||||||
return " ".join(conditions) if conditions else ""
|
|
||||||
|
|
||||||
|
|
||||||
def get_join(filters):
|
|
||||||
join = """LEFT JOIN `tabOpportunity Lost Reason Detail`
|
|
||||||
ON `tabOpportunity Lost Reason Detail`.parenttype = 'Opportunity' and
|
|
||||||
`tabOpportunity Lost Reason Detail`.parent = `tabOpportunity`.name"""
|
|
||||||
|
|
||||||
if filters.get("lost_reason"):
|
if filters.get("lost_reason"):
|
||||||
join = """JOIN `tabOpportunity Lost Reason Detail`
|
query = query.where(OpportunityLostReasonDetail.lost_reason == filters.get("lost_reason"))
|
||||||
ON `tabOpportunity Lost Reason Detail`.parenttype = 'Opportunity' and
|
|
||||||
`tabOpportunity Lost Reason Detail`.parent = `tabOpportunity`.name and
|
|
||||||
`tabOpportunity Lost Reason Detail`.lost_reason = '{}'
|
|
||||||
""".format(filters.get("lost_reason"))
|
|
||||||
|
|
||||||
return join
|
return query
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"is_subcontracted",
|
"is_subcontracted",
|
||||||
"is_final_finished_good",
|
"is_final_finished_good",
|
||||||
"set_cost_based_on_bom_qty",
|
"set_cost_based_on_bom_qty",
|
||||||
|
"quality_inspection_required",
|
||||||
"warehouse_section",
|
"warehouse_section",
|
||||||
"skip_material_transfer",
|
"skip_material_transfer",
|
||||||
"backflush_from_wip_warehouse",
|
"backflush_from_wip_warehouse",
|
||||||
@@ -290,13 +291,20 @@
|
|||||||
"fieldname": "backflush_from_wip_warehouse",
|
"fieldname": "backflush_from_wip_warehouse",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Backflush Materials From WIP Warehouse"
|
"label": "Backflush Materials From WIP Warehouse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:parent.inspection_required",
|
||||||
|
"fieldname": "quality_inspection_required",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Quality Inspection Required"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2026-03-31 17:09:48.771834",
|
"modified": "2026-04-01 17:09:48.771834",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "BOM Operation",
|
"name": "BOM Operation",
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class BOMOperation(Document):
|
|||||||
parent: DF.Data
|
parent: DF.Data
|
||||||
parentfield: DF.Data
|
parentfield: DF.Data
|
||||||
parenttype: DF.Data
|
parenttype: DF.Data
|
||||||
|
quality_inspection_required: DF.Check
|
||||||
sequence_id: DF.Int
|
sequence_id: DF.Int
|
||||||
set_cost_based_on_bom_qty: DF.Check
|
set_cost_based_on_bom_qty: DF.Check
|
||||||
skip_material_transfer: DF.Check
|
skip_material_transfer: DF.Check
|
||||||
|
|||||||
@@ -789,27 +789,27 @@ class JobCard(Document):
|
|||||||
["action_if_quality_inspection_is_not_submitted", "action_if_quality_inspection_is_rejected"],
|
["action_if_quality_inspection_is_not_submitted", "action_if_quality_inspection_is_rejected"],
|
||||||
)
|
)
|
||||||
|
|
||||||
item = self.finished_good or self.production_item
|
bom_inspection_required = frappe.get_value("BOM", self.bom_no, "inspection_required")
|
||||||
bom_inspection_required = frappe.db.get_value(
|
operation_inspection_required = frappe.get_value(
|
||||||
"BOM", self.semi_fg_bom or self.bom_no, "inspection_required"
|
"Work Order Operation", self.operation_id, "quality_inspection_required"
|
||||||
)
|
)
|
||||||
if bom_inspection_required:
|
if bom_inspection_required and operation_inspection_required:
|
||||||
if not self.quality_inspection:
|
if not self.quality_inspection:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_(
|
||||||
"Quality Inspection is required for the item {0} before completing the job card {1}"
|
"Quality Inspection is required for the item {0} before completing the job card {1}"
|
||||||
).format(get_link_to_form("Item", item), bold(self.name))
|
).format(get_link_to_form("Item", self.finished_good), bold(self.name))
|
||||||
)
|
|
||||||
qa_status, docstatus = frappe.db.get_value(
|
|
||||||
"Quality Inspection", self.quality_inspection, ["status", "docstatus"]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
qa_status, docstatus = frappe.get_value(
|
||||||
|
"Quality Inspection", self.quality_inspection, ["status", "docstatus"]
|
||||||
|
)
|
||||||
if docstatus != 1:
|
if docstatus != 1:
|
||||||
if action_submit == "Stop":
|
if action_submit == "Stop":
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Quality Inspection {0} is not submitted for the item: {1}").format(
|
_("Quality Inspection {0} is not submitted for the item: {1}").format(
|
||||||
get_link_to_form("Quality Inspection", self.quality_inspection),
|
get_link_to_form("Quality Inspection", self.quality_inspection),
|
||||||
get_link_to_form("Item", item),
|
get_link_to_form("Item", self.finished_good),
|
||||||
),
|
),
|
||||||
title=_("Inspection Submission"),
|
title=_("Inspection Submission"),
|
||||||
exc=QualityInspectionNotSubmittedError,
|
exc=QualityInspectionNotSubmittedError,
|
||||||
@@ -818,7 +818,7 @@ class JobCard(Document):
|
|||||||
frappe.msgprint(
|
frappe.msgprint(
|
||||||
_("Quality Inspection {0} is not submitted for the item: {1}").format(
|
_("Quality Inspection {0} is not submitted for the item: {1}").format(
|
||||||
get_link_to_form("Quality Inspection", self.quality_inspection),
|
get_link_to_form("Quality Inspection", self.quality_inspection),
|
||||||
get_link_to_form("Item", item),
|
get_link_to_form("Item", self.finished_good),
|
||||||
),
|
),
|
||||||
alert=True,
|
alert=True,
|
||||||
indicator="orange",
|
indicator="orange",
|
||||||
@@ -828,7 +828,7 @@ class JobCard(Document):
|
|||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Quality Inspection {0} is rejected for the item: {1}").format(
|
_("Quality Inspection {0} is rejected for the item: {1}").format(
|
||||||
get_link_to_form("Quality Inspection", self.quality_inspection),
|
get_link_to_form("Quality Inspection", self.quality_inspection),
|
||||||
get_link_to_form("Item", item),
|
get_link_to_form("Item", self.finished_good),
|
||||||
),
|
),
|
||||||
title=_("Inspection Rejected"),
|
title=_("Inspection Rejected"),
|
||||||
exc=QualityInspectionRejectedError,
|
exc=QualityInspectionRejectedError,
|
||||||
@@ -837,7 +837,7 @@ class JobCard(Document):
|
|||||||
frappe.msgprint(
|
frappe.msgprint(
|
||||||
_("Quality Inspection {0} is rejected for the item: {1}").format(
|
_("Quality Inspection {0} is rejected for the item: {1}").format(
|
||||||
get_link_to_form("Quality Inspection", self.quality_inspection),
|
get_link_to_form("Quality Inspection", self.quality_inspection),
|
||||||
get_link_to_form("Item", item),
|
get_link_to_form("Item", self.finished_good),
|
||||||
),
|
),
|
||||||
alert=True,
|
alert=True,
|
||||||
indicator="orange",
|
indicator="orange",
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ class TestJobCard(ERPNextTestSuite):
|
|||||||
with_operations=1,
|
with_operations=1,
|
||||||
track_semi_finished_goods=1,
|
track_semi_finished_goods=1,
|
||||||
company="_Test Company",
|
company="_Test Company",
|
||||||
|
inspection_required=1,
|
||||||
)
|
)
|
||||||
final_bom.append("items", {"item_code": raw.name, "qty": 1})
|
final_bom.append("items", {"item_code": raw.name, "qty": 1})
|
||||||
final_bom.append(
|
final_bom.append(
|
||||||
@@ -97,6 +98,7 @@ class TestJobCard(ERPNextTestSuite):
|
|||||||
"bom_no": cut_bom,
|
"bom_no": cut_bom,
|
||||||
"skip_material_transfer": 1,
|
"skip_material_transfer": 1,
|
||||||
"time_in_mins": 60,
|
"time_in_mins": 60,
|
||||||
|
"quality_inspection_required": 1,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
final_bom.append(
|
final_bom.append(
|
||||||
@@ -134,6 +136,15 @@ class TestJobCard(ERPNextTestSuite):
|
|||||||
work_order.submit()
|
work_order.submit()
|
||||||
job_card = frappe.get_all("Job Card", filters={"work_order": work_order.name, "operation": "Cutting"})
|
job_card = frappe.get_all("Job Card", filters={"work_order": work_order.name, "operation": "Cutting"})
|
||||||
job_card_doc = frappe.get_doc("Job Card", job_card[0].name)
|
job_card_doc = frappe.get_doc("Job Card", job_card[0].name)
|
||||||
|
job_card_doc.append(
|
||||||
|
"time_logs",
|
||||||
|
{
|
||||||
|
"from_time": "2024-01-01 08:00:00",
|
||||||
|
"to_time": "2024-01-01 09:00:00",
|
||||||
|
"time_in_mins": 60,
|
||||||
|
"completed_qty": 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
self.assertRaises(frappe.ValidationError, job_card_doc.submit)
|
self.assertRaises(frappe.ValidationError, job_card_doc.submit)
|
||||||
|
|
||||||
def test_job_card_operations(self):
|
def test_job_card_operations(self):
|
||||||
|
|||||||
@@ -518,9 +518,23 @@ class TestWorkOrder(ERPNextTestSuite):
|
|||||||
do_not_save=True,
|
do_not_save=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
operation_name = "_Test Custom Operation"
|
||||||
|
workstation_name = "_Test Custom Workstation"
|
||||||
|
|
||||||
|
if not frappe.db.exists("Workstation", workstation_name):
|
||||||
|
doc = frappe.new_doc("Workstation")
|
||||||
|
doc.workstation_name = workstation_name
|
||||||
|
doc.save()
|
||||||
|
|
||||||
|
if not frappe.db.exists("Operation", operation_name):
|
||||||
|
doc = frappe.new_doc("Operation")
|
||||||
|
doc.name = operation_name
|
||||||
|
doc.workstation = workstation_name
|
||||||
|
doc.save()
|
||||||
|
|
||||||
operation = {
|
operation = {
|
||||||
"operation": "_Test Operation 1",
|
"operation": operation_name,
|
||||||
"workstation": "_Test Workstation 1",
|
"workstation": workstation_name,
|
||||||
"description": "Test Data",
|
"description": "Test Data",
|
||||||
"operating_cost": 100,
|
"operating_cost": 100,
|
||||||
"time_in_mins": 40,
|
"time_in_mins": 40,
|
||||||
|
|||||||
@@ -1277,6 +1277,7 @@ class WorkOrder(Document):
|
|||||||
"skip_material_transfer",
|
"skip_material_transfer",
|
||||||
"backflush_from_wip_warehouse",
|
"backflush_from_wip_warehouse",
|
||||||
"set_cost_based_on_bom_qty",
|
"set_cost_based_on_bom_qty",
|
||||||
|
"quality_inspection_required",
|
||||||
],
|
],
|
||||||
order_by="idx",
|
order_by="idx",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"workstation_type",
|
"workstation_type",
|
||||||
"workstation",
|
"workstation",
|
||||||
"sequence_id",
|
"sequence_id",
|
||||||
|
"quality_inspection_required",
|
||||||
"section_break_insy",
|
"section_break_insy",
|
||||||
"bom_no",
|
"bom_no",
|
||||||
"finished_good",
|
"finished_good",
|
||||||
@@ -294,13 +295,19 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Backflush Materials From WIP Warehouse",
|
"label": "Backflush Materials From WIP Warehouse",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "quality_inspection_required",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Quality Inspection Required"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"grid_page_length": 50,
|
"grid_page_length": 50,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-05-15 15:10:06.885440",
|
"modified": "2026-03-30 17:20:08.874381",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Work Order Operation",
|
"name": "Work Order Operation",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class WorkOrderOperation(Document):
|
|||||||
planned_operating_cost: DF.Currency
|
planned_operating_cost: DF.Currency
|
||||||
planned_start_time: DF.Datetime | None
|
planned_start_time: DF.Datetime | None
|
||||||
process_loss_qty: DF.Float
|
process_loss_qty: DF.Float
|
||||||
|
quality_inspection_required: DF.Check
|
||||||
sequence_id: DF.Int
|
sequence_id: DF.Int
|
||||||
skip_material_transfer: DF.Check
|
skip_material_transfer: DF.Check
|
||||||
source_warehouse: DF.Link | None
|
source_warehouse: DF.Link | None
|
||||||
|
|||||||
@@ -473,3 +473,4 @@ erpnext.patches.v16_0.enable_serial_batch_setting
|
|||||||
erpnext.patches.v16_0.co_by_product_patch
|
erpnext.patches.v16_0.co_by_product_patch
|
||||||
erpnext.patches.v16_0.update_requested_qty_packed_item
|
erpnext.patches.v16_0.update_requested_qty_packed_item
|
||||||
erpnext.patches.v16_0.remove_payables_receivables_workspace
|
erpnext.patches.v16_0.remove_payables_receivables_workspace
|
||||||
|
erpnext.patches.v16_0.depends_on_inv_dimensions
|
||||||
89
erpnext/patches/v16_0/depends_on_inv_dimensions.py
Normal file
89
erpnext/patches/v16_0/depends_on_inv_dimensions.py
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def get_inventory_dimensions():
|
||||||
|
return frappe.get_all(
|
||||||
|
"Inventory Dimension",
|
||||||
|
fields=[
|
||||||
|
"target_fieldname as fieldname",
|
||||||
|
"source_fieldname",
|
||||||
|
"reference_document as doctype",
|
||||||
|
"reqd",
|
||||||
|
"mandatory_depends_on",
|
||||||
|
],
|
||||||
|
order_by="creation",
|
||||||
|
distinct=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_display_depends_on(doctype, fieldname):
|
||||||
|
if doctype not in [
|
||||||
|
"Stock Entry Detail",
|
||||||
|
"Sales Invoice Item",
|
||||||
|
"Delivery Note Item",
|
||||||
|
"Purchase Invoice Item",
|
||||||
|
"Purchase Receipt Item",
|
||||||
|
]:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
fieldname_start_with = "to"
|
||||||
|
display_depends_on = ""
|
||||||
|
|
||||||
|
if doctype in ["Purchase Invoice Item", "Purchase Receipt Item"]:
|
||||||
|
display_depends_on = "eval:parent.is_internal_supplier == 1"
|
||||||
|
fieldname_start_with = "from"
|
||||||
|
elif doctype != "Stock Entry Detail":
|
||||||
|
display_depends_on = "eval:parent.is_internal_customer == 1"
|
||||||
|
elif doctype == "Stock Entry Detail":
|
||||||
|
display_depends_on = "eval:doc.t_warehouse"
|
||||||
|
|
||||||
|
return f"{fieldname_start_with}_{fieldname}", display_depends_on
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
for dimension in get_inventory_dimensions():
|
||||||
|
if frappe.db.exists(
|
||||||
|
"Custom Field", {"fieldname": dimension.source_fieldname, "dt": "Stock Entry Detail"}
|
||||||
|
):
|
||||||
|
frappe.set_value(
|
||||||
|
"Custom Field",
|
||||||
|
{"fieldname": dimension.source_fieldname, "dt": "Stock Entry Detail"},
|
||||||
|
"depends_on",
|
||||||
|
"eval:doc.s_warehouse",
|
||||||
|
)
|
||||||
|
if frappe.db.exists(
|
||||||
|
"Custom Field", {"fieldname": dimension.source_fieldname, "dt": "Stock Entry Detail", "reqd": 1}
|
||||||
|
):
|
||||||
|
frappe.set_value(
|
||||||
|
"Custom Field",
|
||||||
|
{"fieldname": dimension.source_fieldname, "dt": "Stock Entry Detail", "reqd": 1},
|
||||||
|
{"mandatory_depends_on": "eval:doc.s_warehouse", "reqd": 0},
|
||||||
|
)
|
||||||
|
if frappe.db.exists(
|
||||||
|
"Custom Field",
|
||||||
|
{
|
||||||
|
"fieldname": f"to_{dimension.fieldname}",
|
||||||
|
"dt": "Stock Entry Detail",
|
||||||
|
"depends_on": "eval:parent.purpose != 'Material Issue'",
|
||||||
|
},
|
||||||
|
):
|
||||||
|
frappe.set_value(
|
||||||
|
"Custom Field",
|
||||||
|
{
|
||||||
|
"fieldname": f"to_{dimension.fieldname}",
|
||||||
|
"dt": "Stock Entry Detail",
|
||||||
|
"depends_on": "eval:parent.purpose != 'Material Issue'",
|
||||||
|
},
|
||||||
|
"depends_on",
|
||||||
|
"eval:doc.t_warehouse",
|
||||||
|
)
|
||||||
|
fieldname, display_depends_on = get_display_depends_on(dimension.doctype, dimension.fieldname)
|
||||||
|
if display_depends_on and frappe.db.exists(
|
||||||
|
"Custom Field", {"fieldname": fieldname, "dt": dimension.doctype}
|
||||||
|
):
|
||||||
|
frappe.set_value(
|
||||||
|
"Custom Field",
|
||||||
|
{"fieldname": fieldname, "dt": dimension.doctype},
|
||||||
|
"mandatory_depends_on",
|
||||||
|
display_depends_on if dimension.reqd else dimension.mandatory_depends_on,
|
||||||
|
)
|
||||||
@@ -8,9 +8,8 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"dimension_details_tab",
|
"dimension_details_tab",
|
||||||
"dimension_name",
|
"dimension_name",
|
||||||
"reference_document",
|
|
||||||
"column_break_4",
|
"column_break_4",
|
||||||
"disabled",
|
"reference_document",
|
||||||
"field_mapping_section",
|
"field_mapping_section",
|
||||||
"source_fieldname",
|
"source_fieldname",
|
||||||
"column_break_9",
|
"column_break_9",
|
||||||
@@ -93,12 +92,6 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Apply to All Inventory Documents"
|
"label": "Apply to All Inventory Documents"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"default": "0",
|
|
||||||
"fieldname": "disabled",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"label": "Disabled"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "target_fieldname",
|
"fieldname": "target_fieldname",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
@@ -159,6 +152,7 @@
|
|||||||
"label": "Conditional Rule Examples"
|
"label": "Conditional Rule Examples"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:!doc.apply_to_all_doctypes",
|
||||||
"description": "To apply condition on parent field use parent.field_name and to apply condition on child table use doc.field_name. Here field_name could be based on the actual column name of the respective field.",
|
"description": "To apply condition on parent field use parent.field_name and to apply condition on child table use doc.field_name. Here field_name could be based on the actual column name of the respective field.",
|
||||||
"fieldname": "mandatory_depends_on",
|
"fieldname": "mandatory_depends_on",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
@@ -188,7 +182,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-07-07 15:51:29.329064",
|
"modified": "2026-04-08 10:10:16.884388",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Inventory Dimension",
|
"name": "Inventory Dimension",
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ class InventoryDimension(Document):
|
|||||||
apply_to_all_doctypes: DF.Check
|
apply_to_all_doctypes: DF.Check
|
||||||
condition: DF.Code | None
|
condition: DF.Code | None
|
||||||
dimension_name: DF.Data
|
dimension_name: DF.Data
|
||||||
disabled: DF.Check
|
|
||||||
document_type: DF.Link | None
|
document_type: DF.Link | None
|
||||||
fetch_from_parent: DF.Literal[None]
|
fetch_from_parent: DF.Literal[None]
|
||||||
istable: DF.Check
|
istable: DF.Check
|
||||||
@@ -78,7 +77,6 @@ class InventoryDimension(Document):
|
|||||||
|
|
||||||
old_doc = self._doc_before_save
|
old_doc = self._doc_before_save
|
||||||
allow_to_edit_fields = [
|
allow_to_edit_fields = [
|
||||||
"disabled",
|
|
||||||
"fetch_from_parent",
|
"fetch_from_parent",
|
||||||
"type_of_transaction",
|
"type_of_transaction",
|
||||||
"condition",
|
"condition",
|
||||||
@@ -122,6 +120,7 @@ class InventoryDimension(Document):
|
|||||||
def reset_value(self):
|
def reset_value(self):
|
||||||
if self.apply_to_all_doctypes:
|
if self.apply_to_all_doctypes:
|
||||||
self.type_of_transaction = ""
|
self.type_of_transaction = ""
|
||||||
|
self.mandatory_depends_on = ""
|
||||||
|
|
||||||
self.istable = 0
|
self.istable = 0
|
||||||
for field in ["document_type", "condition"]:
|
for field in ["document_type", "condition"]:
|
||||||
@@ -186,8 +185,12 @@ class InventoryDimension(Document):
|
|||||||
label=_(label),
|
label=_(label),
|
||||||
depends_on="eval:doc.s_warehouse" if doctype == "Stock Entry Detail" else "",
|
depends_on="eval:doc.s_warehouse" if doctype == "Stock Entry Detail" else "",
|
||||||
search_index=1,
|
search_index=1,
|
||||||
reqd=self.reqd,
|
reqd=1
|
||||||
mandatory_depends_on=self.mandatory_depends_on,
|
if self.reqd and not self.mandatory_depends_on and doctype != "Stock Entry Detail"
|
||||||
|
else 0,
|
||||||
|
mandatory_depends_on="eval:doc.s_warehouse"
|
||||||
|
if self.reqd and doctype == "Stock Entry Detail"
|
||||||
|
else self.mandatory_depends_on,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -298,12 +301,13 @@ class InventoryDimension(Document):
|
|||||||
options=self.reference_document,
|
options=self.reference_document,
|
||||||
label=label,
|
label=label,
|
||||||
depends_on=display_depends_on,
|
depends_on=display_depends_on,
|
||||||
|
mandatory_depends_on=display_depends_on if self.reqd else self.mandatory_depends_on,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def field_exists(doctype, fieldname) -> str or None:
|
def field_exists(doctype, fieldname) -> str | None:
|
||||||
return frappe.db.get_value("DocField", {"parent": doctype, "fieldname": fieldname}, "name")
|
return frappe.db.get_value("DocField", {"parent": doctype, "fieldname": fieldname}, "name")
|
||||||
|
|
||||||
|
|
||||||
@@ -379,7 +383,6 @@ def get_document_wise_inventory_dimensions(doctype) -> dict:
|
|||||||
"type_of_transaction",
|
"type_of_transaction",
|
||||||
"fetch_from_parent",
|
"fetch_from_parent",
|
||||||
],
|
],
|
||||||
filters={"disabled": 0},
|
|
||||||
or_filters={"document_type": doctype, "apply_to_all_doctypes": 1},
|
or_filters={"document_type": doctype, "apply_to_all_doctypes": 1},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -396,7 +399,6 @@ def get_inventory_dimensions():
|
|||||||
"validate_negative_stock",
|
"validate_negative_stock",
|
||||||
"name as dimension_name",
|
"name as dimension_name",
|
||||||
],
|
],
|
||||||
filters={"disabled": 0},
|
|
||||||
order_by="creation",
|
order_by="creation",
|
||||||
distinct=True,
|
distinct=True,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -210,9 +210,9 @@ class TestInventoryDimension(ERPNextTestSuite):
|
|||||||
doc = create_inventory_dimension(
|
doc = create_inventory_dimension(
|
||||||
reference_document="Pallet",
|
reference_document="Pallet",
|
||||||
type_of_transaction="Outward",
|
type_of_transaction="Outward",
|
||||||
dimension_name="Pallet",
|
dimension_name="Pallet 75",
|
||||||
apply_to_all_doctypes=0,
|
apply_to_all_doctypes=0,
|
||||||
document_type="Stock Entry Detail",
|
document_type="Delivery Note Item",
|
||||||
)
|
)
|
||||||
|
|
||||||
doc.reqd = 1
|
doc.reqd = 1
|
||||||
@@ -220,7 +220,7 @@ class TestInventoryDimension(ERPNextTestSuite):
|
|||||||
|
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
frappe.db.get_value(
|
frappe.db.get_value(
|
||||||
"Custom Field", {"fieldname": "pallet", "dt": "Stock Entry Detail", "reqd": 1}, "name"
|
"Custom Field", {"fieldname": "pallet_75", "dt": "Delivery Note Item", "reqd": 1}, "name"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ frappe.ui.form.on("Quality Inspection", {
|
|||||||
if (doc.reference_type && doc.reference_name) {
|
if (doc.reference_type && doc.reference_name) {
|
||||||
let filters = {
|
let filters = {
|
||||||
from: doctype,
|
from: doctype,
|
||||||
|
parent_doctype: doc.reference_type,
|
||||||
inspection_type: doc.inspection_type,
|
inspection_type: doc.inspection_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -370,10 +370,11 @@ def item_query(doctype: Any, txt: str | None, searchfield: Any, start: int, page
|
|||||||
from frappe.desk.reportview import get_match_cond
|
from frappe.desk.reportview import get_match_cond
|
||||||
|
|
||||||
from_doctype = cstr(filters.get("from"))
|
from_doctype = cstr(filters.get("from"))
|
||||||
|
parent_doctype = cstr(filters.get("parent_doctype"))
|
||||||
if not from_doctype or not frappe.db.exists("DocType", from_doctype):
|
if not from_doctype or not frappe.db.exists("DocType", from_doctype):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
mcond = get_match_cond(from_doctype)
|
mcond = get_match_cond(parent_doctype or from_doctype)
|
||||||
cond, qi_condition = "", "and (quality_inspection is null or quality_inspection = '')"
|
cond, qi_condition = "", "and (quality_inspection is null or quality_inspection = '')"
|
||||||
|
|
||||||
if filters.get("parent"):
|
if filters.get("parent"):
|
||||||
|
|||||||
@@ -3932,9 +3932,12 @@ def get_operating_cost_per_unit(work_order=None, bom_no=None):
|
|||||||
|
|
||||||
for d in work_order.get("operations"):
|
for d in work_order.get("operations"):
|
||||||
if flt(d.completed_qty):
|
if flt(d.completed_qty):
|
||||||
operating_cost_per_unit += flt(
|
if not (remaining_qty := flt(d.completed_qty - work_order.produced_qty)):
|
||||||
d.actual_operating_cost - get_consumed_operating_cost(work_order.name, bom_no)
|
continue
|
||||||
) / flt(d.completed_qty - work_order.produced_qty)
|
operating_cost_per_unit += (
|
||||||
|
flt(d.actual_operating_cost - get_consumed_operating_cost(work_order.name, bom_no))
|
||||||
|
/ remaining_qty
|
||||||
|
)
|
||||||
elif work_order.qty:
|
elif work_order.qty:
|
||||||
operating_cost_per_unit += flt(d.planned_operating_cost) / flt(work_order.qty)
|
operating_cost_per_unit += flt(d.planned_operating_cost) / flt(work_order.qty)
|
||||||
|
|
||||||
|
|||||||
@@ -267,17 +267,11 @@ def update_args_in_repost_item_valuation(
|
|||||||
items_to_be_repost,
|
items_to_be_repost,
|
||||||
repost_affected_transaction,
|
repost_affected_transaction,
|
||||||
item_wh_wise_last_posted_sle=None,
|
item_wh_wise_last_posted_sle=None,
|
||||||
only_affected_transaction=False,
|
|
||||||
):
|
):
|
||||||
file_name = ""
|
file_name = ""
|
||||||
has_file = False
|
|
||||||
|
|
||||||
if not item_wh_wise_last_posted_sle:
|
if not item_wh_wise_last_posted_sle:
|
||||||
item_wh_wise_last_posted_sle = {}
|
item_wh_wise_last_posted_sle = {}
|
||||||
|
|
||||||
if doc.reposting_data_file:
|
|
||||||
has_file = True
|
|
||||||
|
|
||||||
if doc.reposting_data_file:
|
if doc.reposting_data_file:
|
||||||
file_name = get_reposting_file_name(doc.doctype, doc.name)
|
file_name = get_reposting_file_name(doc.doctype, doc.name)
|
||||||
# frappe.delete_doc("File", file_name, ignore_permissions=True, delete_permanently=True)
|
# frappe.delete_doc("File", file_name, ignore_permissions=True, delete_permanently=True)
|
||||||
@@ -292,7 +286,6 @@ def update_args_in_repost_item_valuation(
|
|||||||
file_name,
|
file_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not only_affected_transaction or not has_file:
|
|
||||||
doc.db_set(
|
doc.db_set(
|
||||||
{
|
{
|
||||||
"current_index": index,
|
"current_index": index,
|
||||||
@@ -584,13 +577,9 @@ class update_entries_after:
|
|||||||
self.update_bin()
|
self.update_bin()
|
||||||
else:
|
else:
|
||||||
self.item_wh_wise_last_posted_sle = self.get_item_wh_wise_last_posted_sle()
|
self.item_wh_wise_last_posted_sle = self.get_item_wh_wise_last_posted_sle()
|
||||||
_item_wh_sle = self.sort_sles(self.item_wh_wise_last_posted_sle.values())
|
item_wh_sles = self.sort_sles(self.item_wh_wise_last_posted_sle.values())
|
||||||
|
|
||||||
while _item_wh_sle:
|
|
||||||
self.initialize_reposting()
|
self.initialize_reposting()
|
||||||
sle_dict = _item_wh_sle.pop(0)
|
self.repost_stock_ledgers(item_wh_sles)
|
||||||
self.repost_stock_ledgers(sle_dict)
|
|
||||||
|
|
||||||
self.update_bin()
|
self.update_bin()
|
||||||
self.reset_vouchers_and_idx()
|
self.reset_vouchers_and_idx()
|
||||||
self.update_data_in_repost()
|
self.update_data_in_repost()
|
||||||
@@ -625,8 +614,19 @@ class update_entries_after:
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def repost_stock_ledgers(self, sle_dict=None):
|
def _get_future_entries_to_repost(self, item_wh_sles):
|
||||||
self._sles = self.get_future_entries_to_repost(sle_dict)
|
sles = []
|
||||||
|
|
||||||
|
for sle in item_wh_sles:
|
||||||
|
if (sle.item_code, sle.warehouse) not in self.distinct_dependant_item_wh:
|
||||||
|
self.distinct_dependant_item_wh.add((sle.item_code, sle.warehouse))
|
||||||
|
|
||||||
|
sles.extend(self.get_future_entries_to_repost(sle))
|
||||||
|
|
||||||
|
return self.sort_sles(sles)
|
||||||
|
|
||||||
|
def repost_stock_ledgers(self, item_wh_sles=None):
|
||||||
|
self._sles = self._get_future_entries_to_repost(item_wh_sles)
|
||||||
|
|
||||||
if not isinstance(self._sles, deque):
|
if not isinstance(self._sles, deque):
|
||||||
self._sles = deque(self._sles)
|
self._sles = deque(self._sles)
|
||||||
@@ -634,10 +634,13 @@ class update_entries_after:
|
|||||||
i = 0
|
i = 0
|
||||||
while self._sles:
|
while self._sles:
|
||||||
sle = self._sles.popleft()
|
sle = self._sles.popleft()
|
||||||
i += 1
|
if (sle.item_code, sle.warehouse) not in self.distinct_dependant_item_wh:
|
||||||
|
self.distinct_dependant_item_wh.add((sle.item_code, sle.warehouse))
|
||||||
|
|
||||||
if sle.name in self.distinct_sles:
|
if sle.name in self.distinct_sles:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
i += 1
|
||||||
item_wh_key = (sle.item_code, sle.warehouse)
|
item_wh_key = (sle.item_code, sle.warehouse)
|
||||||
if item_wh_key not in self.prev_sle_dict:
|
if item_wh_key not in self.prev_sle_dict:
|
||||||
self.prev_sle_dict[item_wh_key] = get_previous_sle_of_current_voucher(sle)
|
self.prev_sle_dict[item_wh_key] = get_previous_sle_of_current_voucher(sle)
|
||||||
@@ -651,7 +654,7 @@ class update_entries_after:
|
|||||||
self.include_dependant_sle_in_reposting(sle)
|
self.include_dependant_sle_in_reposting(sle)
|
||||||
self.update_item_wh_wise_last_posted_sle(sle)
|
self.update_item_wh_wise_last_posted_sle(sle)
|
||||||
|
|
||||||
if i % 1000 == 0:
|
if i % 2000 == 0:
|
||||||
self.update_data_in_repost(len(self._sles), i)
|
self.update_data_in_repost(len(self._sles), i)
|
||||||
|
|
||||||
def sort_sles(self, sles):
|
def sort_sles(self, sles):
|
||||||
@@ -733,7 +736,6 @@ class update_entries_after:
|
|||||||
self.items_to_be_repost,
|
self.items_to_be_repost,
|
||||||
self.repost_affected_transaction,
|
self.repost_affected_transaction,
|
||||||
self.item_wh_wise_last_posted_sle,
|
self.item_wh_wise_last_posted_sle,
|
||||||
only_affected_transaction=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if not frappe.in_test:
|
if not frappe.in_test:
|
||||||
@@ -990,6 +992,9 @@ class update_entries_after:
|
|||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not cint(erpnext.is_perpetual_inventory_enabled(sle.company)):
|
||||||
|
return
|
||||||
|
|
||||||
if self.args.item_code != sle.item_code or self.args.warehouse != sle.warehouse:
|
if self.args.item_code != sle.item_code or self.args.warehouse != sle.warehouse:
|
||||||
self.repost_affected_transaction.add((sle.voucher_type, sle.voucher_no))
|
self.repost_affected_transaction.add((sle.voucher_type, sle.voucher_no))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user