fix: serial and batch selector
This commit is contained in:
parent
39da92929b
commit
f4cfc589c6
@ -1527,7 +1527,6 @@ class TestWorkOrder(FrappeTestCase):
|
|||||||
ste_doc.load_from_db()
|
ste_doc.load_from_db()
|
||||||
|
|
||||||
# Create a stock entry to manufacture the item
|
# Create a stock entry to manufacture the item
|
||||||
print("remove 2 qty from each item")
|
|
||||||
ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 5))
|
ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 5))
|
||||||
for row in ste_doc.items:
|
for row in ste_doc.items:
|
||||||
if row.s_warehouse and not row.t_warehouse:
|
if row.s_warehouse and not row.t_warehouse:
|
||||||
|
@ -12,12 +12,12 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
make() {
|
make() {
|
||||||
let label = this.item?.has_serial_no ? __('Serial No') : __('Batch No');
|
let label = this.item?.has_serial_no ? __('Serial Nos') : __('Batch Nos');
|
||||||
let primary_label = this.bundle
|
let primary_label = this.bundle
|
||||||
? __('Update') : __('Add');
|
? __('Update') : __('Add');
|
||||||
|
|
||||||
if (this.item?.has_serial_no && this.item?.batch_no) {
|
if (this.item?.has_serial_no && this.item?.batch_no) {
|
||||||
label = __('Serial No / Batch No');
|
label = __('Serial Nos / Batch Nos');
|
||||||
}
|
}
|
||||||
|
|
||||||
primary_label += ' ' + label;
|
primary_label += ' ' + label;
|
||||||
@ -26,7 +26,9 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
|
|||||||
title: this.item?.title || primary_label,
|
title: this.item?.title || primary_label,
|
||||||
fields: this.get_dialog_fields(),
|
fields: this.get_dialog_fields(),
|
||||||
primary_action_label: primary_label,
|
primary_action_label: primary_label,
|
||||||
primary_action: () => this.update_ledgers()
|
primary_action: () => this.update_ledgers(),
|
||||||
|
secondary_action_label: __('Edit Full Form'),
|
||||||
|
secondary_action: () => this.edit_full_form(),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.dialog.set_value("qty", this.item.qty);
|
this.dialog.set_value("qty", this.item.qty);
|
||||||
@ -48,7 +50,7 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
|
|||||||
|
|
||||||
if (this.item.has_serial_no) {
|
if (this.item.has_serial_no) {
|
||||||
fields.push({
|
fields.push({
|
||||||
fieldtype: 'Link',
|
fieldtype: 'Data',
|
||||||
fieldname: 'scan_serial_no',
|
fieldname: 'scan_serial_no',
|
||||||
label: __('Scan Serial No'),
|
label: __('Scan Serial No'),
|
||||||
options: 'Serial No',
|
options: 'Serial No',
|
||||||
@ -279,6 +281,37 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
edit_full_form() {
|
||||||
|
let bundle_id = this.item.serial_and_batch_bundle
|
||||||
|
if (!bundle_id) {
|
||||||
|
_new = frappe.model.get_new_doc(
|
||||||
|
"Serial and Batch Bundle", null, null, true
|
||||||
|
);
|
||||||
|
|
||||||
|
_new.item_code = this.item.item_code;
|
||||||
|
_new.warehouse = this.get_warehouse();
|
||||||
|
_new.has_serial_no = this.item.has_serial_no;
|
||||||
|
_new.has_batch_no = this.item.has_batch_no;
|
||||||
|
_new.type_of_transaction = this.get_type_of_transaction();
|
||||||
|
_new.company = this.frm.doc.company;
|
||||||
|
_new.voucher_type = this.frm.doc.doctype;
|
||||||
|
bundle_id = _new.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
frappe.set_route("Form", "Serial and Batch Bundle", bundle_id);
|
||||||
|
this.dialog.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
get_warehouse() {
|
||||||
|
return (this.item?.outward ?
|
||||||
|
(this.item.warehouse || this.item.s_warehouse)
|
||||||
|
: (this.item.warehouse || this.item.t_warehouse));
|
||||||
|
}
|
||||||
|
|
||||||
|
get_type_of_transaction() {
|
||||||
|
return (this.item?.outward ? 'Outward' : 'Inward');
|
||||||
|
}
|
||||||
|
|
||||||
render_data() {
|
render_data() {
|
||||||
if (!this.frm.is_new() && this.bundle) {
|
if (!this.frm.is_new() && this.bundle) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
|
@ -4,6 +4,7 @@ import frappe
|
|||||||
from frappe.query_builder.functions import CombineDatetime, Sum
|
from frappe.query_builder.functions import CombineDatetime, Sum
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
from frappe.utils.deprecations import deprecated
|
from frappe.utils.deprecations import deprecated
|
||||||
|
from pypika import Order
|
||||||
|
|
||||||
|
|
||||||
class DeprecatedSerialNoValuation:
|
class DeprecatedSerialNoValuation:
|
||||||
@ -39,25 +40,25 @@ class DeprecatedSerialNoValuation:
|
|||||||
# Get rate for serial nos which has been transferred to other company
|
# Get rate for serial nos which has been transferred to other company
|
||||||
invalid_serial_nos = [d.name for d in all_serial_nos if d.company != self.sle.company]
|
invalid_serial_nos = [d.name for d in all_serial_nos if d.company != self.sle.company]
|
||||||
for serial_no in invalid_serial_nos:
|
for serial_no in invalid_serial_nos:
|
||||||
incoming_rate = frappe.db.sql(
|
table = frappe.qb.DocType("Stock Ledger Entry")
|
||||||
"""
|
incoming_rate = (
|
||||||
select incoming_rate
|
frappe.qb.from_(table)
|
||||||
from `tabStock Ledger Entry`
|
.select(table.incoming_rate)
|
||||||
where
|
.where(
|
||||||
company = %s
|
(
|
||||||
and serial_and_batch_bundle IS NULL
|
(table.serial_no == serial_no)
|
||||||
and actual_qty > 0
|
| (table.serial_no.like(serial_no + "\n%"))
|
||||||
and is_cancelled = 0
|
| (table.serial_no.like("%\n" + serial_no))
|
||||||
and (serial_no = %s
|
| (table.serial_no.like("%\n" + serial_no + "\n%"))
|
||||||
or serial_no like %s
|
|
||||||
or serial_no like %s
|
|
||||||
or serial_no like %s
|
|
||||||
)
|
)
|
||||||
order by posting_date desc
|
& (table.company == self.sle.company)
|
||||||
limit 1
|
& (table.serial_and_batch_bundle.isnull())
|
||||||
""",
|
& (table.actual_qty > 0)
|
||||||
(self.sle.company, serial_no, serial_no + "\n%", "%\n" + serial_no, "%\n" + serial_no + "\n%"),
|
& (table.is_cancelled == 0)
|
||||||
)
|
)
|
||||||
|
.orderby(table.posting_date, order=Order.desc)
|
||||||
|
.limit(1)
|
||||||
|
).run()
|
||||||
|
|
||||||
self.serial_no_incoming_rate[serial_no] += flt(incoming_rate[0][0]) if incoming_rate else 0
|
self.serial_no_incoming_rate[serial_no] += flt(incoming_rate[0][0]) if incoming_rate else 0
|
||||||
incoming_values += self.serial_no_incoming_rate[serial_no]
|
incoming_values += self.serial_no_incoming_rate[serial_no]
|
||||||
|
@ -8,6 +8,17 @@ frappe.ui.form.on('Serial and Batch Bundle', {
|
|||||||
|
|
||||||
refresh(frm) {
|
refresh(frm) {
|
||||||
frm.trigger('toggle_fields');
|
frm.trigger('toggle_fields');
|
||||||
|
frm.trigger('prepare_serial_batch_prompt');
|
||||||
|
},
|
||||||
|
|
||||||
|
item_code(frm) {
|
||||||
|
frm.clear_custom_buttons();
|
||||||
|
frm.trigger('prepare_serial_batch_prompt');
|
||||||
|
},
|
||||||
|
|
||||||
|
type_of_transaction(frm) {
|
||||||
|
frm.clear_custom_buttons();
|
||||||
|
frm.trigger('prepare_serial_batch_prompt');
|
||||||
},
|
},
|
||||||
|
|
||||||
warehouse(frm) {
|
warehouse(frm) {
|
||||||
@ -30,6 +41,91 @@ frappe.ui.form.on('Serial and Batch Bundle', {
|
|||||||
frm.trigger('toggle_fields');
|
frm.trigger('toggle_fields');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
prepare_serial_batch_prompt(frm) {
|
||||||
|
if (frm.doc.docstatus === 0 && frm.doc.item_code
|
||||||
|
&& frm.doc.type_of_transaction === "Inward") {
|
||||||
|
let label = frm.doc?.has_serial_no === 1
|
||||||
|
? __('Serial Nos') : __('Batch Nos');
|
||||||
|
|
||||||
|
if (frm.doc?.has_serial_no === 1 && frm.doc?.has_batch_no === 1) {
|
||||||
|
label = __('Serial and Batch Nos');
|
||||||
|
}
|
||||||
|
|
||||||
|
let fields = frm.events.get_prompt_fields(frm);
|
||||||
|
|
||||||
|
frm.add_custom_button(__("Make " + label), () => {
|
||||||
|
frappe.prompt(fields, (data) => {
|
||||||
|
frm.events.add_serial_batch(frm, data);
|
||||||
|
}, "Add " + label, "Make " + label);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get_prompt_fields(frm) {
|
||||||
|
let attach_field = {
|
||||||
|
"label": __("Attach CSV File"),
|
||||||
|
"fieldname": "csv_file",
|
||||||
|
"fieldtype": "Attach"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!frm.doc.has_batch_no) {
|
||||||
|
attach_field.depends_on = "eval:doc.using_csv_file === 1"
|
||||||
|
}
|
||||||
|
|
||||||
|
let fields = [
|
||||||
|
{
|
||||||
|
"label": __("Using CSV File"),
|
||||||
|
"fieldname": "using_csv_file",
|
||||||
|
"default": 1,
|
||||||
|
"fieldtype": "Check",
|
||||||
|
},
|
||||||
|
attach_field,
|
||||||
|
{
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
if (frm.doc.has_serial_no) {
|
||||||
|
fields.push({
|
||||||
|
"label": "Serial Nos",
|
||||||
|
"fieldname": "serial_nos",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"depends_on": "eval:doc.using_csv_file === 0"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frm.doc.has_batch_no) {
|
||||||
|
fields = attach_field
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
},
|
||||||
|
|
||||||
|
add_serial_batch(frm, prompt_data) {
|
||||||
|
frm.events.validate_prompt_data(frm, prompt_data);
|
||||||
|
|
||||||
|
frm.call({
|
||||||
|
method: "add_serial_batch",
|
||||||
|
doc: frm.doc,
|
||||||
|
args: {
|
||||||
|
"data": prompt_data,
|
||||||
|
},
|
||||||
|
callback(r) {
|
||||||
|
refresh_field("entries");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
validate_prompt_data(frm, prompt_data) {
|
||||||
|
if (prompt_data.using_csv_file && !prompt_data.csv_file) {
|
||||||
|
frappe.throw(__("Please attach CSV file"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frm.doc.has_serial_no && !prompt_data.using_csv_file && !prompt_data.serial_nos) {
|
||||||
|
frappe.throw(__("Please enter serial nos"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
toggle_fields(frm) {
|
toggle_fields(frm) {
|
||||||
frm.fields_dict.entries.grid.update_docfield_property(
|
frm.fields_dict.entries.grid.update_docfield_property(
|
||||||
'serial_no', 'read_only', !frm.doc.has_serial_no
|
'serial_no', 'read_only', !frm.doc.has_serial_no
|
||||||
|
@ -9,13 +9,13 @@
|
|||||||
"item_details_tab",
|
"item_details_tab",
|
||||||
"naming_series",
|
"naming_series",
|
||||||
"company",
|
"company",
|
||||||
"warehouse",
|
|
||||||
"type_of_transaction",
|
|
||||||
"column_break_4",
|
|
||||||
"item_code",
|
|
||||||
"item_name",
|
"item_name",
|
||||||
"has_serial_no",
|
"has_serial_no",
|
||||||
"has_batch_no",
|
"has_batch_no",
|
||||||
|
"column_break_4",
|
||||||
|
"item_code",
|
||||||
|
"warehouse",
|
||||||
|
"type_of_transaction",
|
||||||
"serial_no_and_batch_no_tab",
|
"serial_no_and_batch_no_tab",
|
||||||
"entries",
|
"entries",
|
||||||
"quantity_and_rate_section",
|
"quantity_and_rate_section",
|
||||||
@ -84,7 +84,8 @@
|
|||||||
"fetch_from": "item_code.item_name",
|
"fetch_from": "item_code.item_name",
|
||||||
"fieldname": "item_name",
|
"fieldname": "item_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Item Name"
|
"label": "Item Name",
|
||||||
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@ -243,7 +244,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-04-06 02:35:38.404537",
|
"modified": "2023-04-10 20:02:42.964309",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Serial and Batch Bundle",
|
"name": "Serial and Batch Bundle",
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
import csv
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
@ -9,7 +10,17 @@ import frappe
|
|||||||
from frappe import _, _dict, bold
|
from frappe import _, _dict, bold
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.query_builder.functions import CombineDatetime, Sum
|
from frappe.query_builder.functions import CombineDatetime, Sum
|
||||||
from frappe.utils import add_days, cint, flt, get_link_to_form, nowtime, today
|
from frappe.utils import (
|
||||||
|
add_days,
|
||||||
|
cint,
|
||||||
|
cstr,
|
||||||
|
flt,
|
||||||
|
get_link_to_form,
|
||||||
|
now,
|
||||||
|
nowtime,
|
||||||
|
parse_json,
|
||||||
|
today,
|
||||||
|
)
|
||||||
|
|
||||||
from erpnext.stock.serial_batch_bundle import BatchNoValuation, SerialNoValuation
|
from erpnext.stock.serial_batch_bundle import BatchNoValuation, SerialNoValuation
|
||||||
from erpnext.stock.serial_batch_bundle import get_serial_nos as get_serial_nos_from_bundle
|
from erpnext.stock.serial_batch_bundle import get_serial_nos as get_serial_nos_from_bundle
|
||||||
@ -626,6 +637,173 @@ class SerialandBatchBundle(Document):
|
|||||||
self.delink_reference_from_batch()
|
self.delink_reference_from_batch()
|
||||||
self.clear_table()
|
self.clear_table()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def add_serial_batch(self, data):
|
||||||
|
serial_nos, batch_nos = [], []
|
||||||
|
if isinstance(data, str):
|
||||||
|
data = parse_json(data)
|
||||||
|
|
||||||
|
if data.get("csv_file"):
|
||||||
|
serial_nos, batch_nos = get_serial_batch_from_csv(self.item_code, data.get("csv_file"))
|
||||||
|
else:
|
||||||
|
serial_nos, batch_nos = get_serial_batch_from_data(self.item_code, data)
|
||||||
|
|
||||||
|
if not serial_nos and not batch_nos:
|
||||||
|
return
|
||||||
|
|
||||||
|
if serial_nos:
|
||||||
|
self.set("entries", serial_nos)
|
||||||
|
elif batch_nos:
|
||||||
|
self.set("entries", batch_nos)
|
||||||
|
|
||||||
|
|
||||||
|
def get_serial_batch_from_csv(item_code, file_path):
|
||||||
|
file_path = frappe.get_site_path() + file_path
|
||||||
|
serial_nos = []
|
||||||
|
batch_nos = []
|
||||||
|
|
||||||
|
with open(file_path, "r") as f:
|
||||||
|
reader = csv.reader(f)
|
||||||
|
serial_nos, batch_nos = parse_csv_file_to_get_serial_batch(reader)
|
||||||
|
|
||||||
|
if serial_nos:
|
||||||
|
make_serial_nos(item_code, serial_nos)
|
||||||
|
|
||||||
|
print(batch_nos)
|
||||||
|
if batch_nos:
|
||||||
|
make_batch_nos(item_code, batch_nos)
|
||||||
|
|
||||||
|
return serial_nos, batch_nos
|
||||||
|
|
||||||
|
|
||||||
|
def parse_csv_file_to_get_serial_batch(reader):
|
||||||
|
has_serial_no, has_batch_no = False, False
|
||||||
|
serial_nos = []
|
||||||
|
batch_nos = []
|
||||||
|
|
||||||
|
for index, row in enumerate(reader):
|
||||||
|
if index == 0:
|
||||||
|
has_serial_no = row[0] == "Serial No"
|
||||||
|
has_batch_no = row[0] == "Batch No"
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not row[0]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if has_serial_no or (has_serial_no and has_batch_no):
|
||||||
|
_dict = {"serial_no": row[0], "qty": 1}
|
||||||
|
|
||||||
|
if has_batch_no:
|
||||||
|
_dict.update(
|
||||||
|
{
|
||||||
|
"batch_no": row[1],
|
||||||
|
"qty": row[2],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
serial_nos.append(_dict)
|
||||||
|
elif has_batch_no:
|
||||||
|
batch_nos.append(
|
||||||
|
{
|
||||||
|
"batch_no": row[0],
|
||||||
|
"qty": row[1],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return serial_nos, batch_nos
|
||||||
|
|
||||||
|
|
||||||
|
def get_serial_batch_from_data(item_code, kwargs):
|
||||||
|
serial_nos = []
|
||||||
|
batch_nos = []
|
||||||
|
if kwargs.get("serial_nos"):
|
||||||
|
data = parse_serial_nos(kwargs.get("serial_nos"))
|
||||||
|
for serial_no in data:
|
||||||
|
if not serial_no:
|
||||||
|
continue
|
||||||
|
serial_nos.append({"serial_no": serial_no, "qty": 1})
|
||||||
|
|
||||||
|
make_serial_nos(item_code, serial_nos)
|
||||||
|
|
||||||
|
return serial_nos, batch_nos
|
||||||
|
|
||||||
|
|
||||||
|
def make_serial_nos(item_code, serial_nos):
|
||||||
|
item = frappe.get_cached_value("Item", item_code, ["description", "item_code"], as_dict=1)
|
||||||
|
|
||||||
|
serial_nos = [d.get("serial_no") for d in serial_nos if d.get("serial_no")]
|
||||||
|
|
||||||
|
serial_nos_details = []
|
||||||
|
user = frappe.session.user
|
||||||
|
for serial_no in serial_nos:
|
||||||
|
serial_nos_details.append(
|
||||||
|
(
|
||||||
|
serial_no,
|
||||||
|
serial_no,
|
||||||
|
now(),
|
||||||
|
now(),
|
||||||
|
user,
|
||||||
|
user,
|
||||||
|
item.item_code,
|
||||||
|
item.item_name,
|
||||||
|
item.description,
|
||||||
|
"Inactive",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
"name",
|
||||||
|
"serial_no",
|
||||||
|
"creation",
|
||||||
|
"modified",
|
||||||
|
"owner",
|
||||||
|
"modified_by",
|
||||||
|
"item_code",
|
||||||
|
"item_name",
|
||||||
|
"description",
|
||||||
|
"status",
|
||||||
|
]
|
||||||
|
|
||||||
|
frappe.db.bulk_insert("Serial No", fields=fields, values=set(serial_nos_details))
|
||||||
|
|
||||||
|
frappe.msgprint(_("Serial Nos are created successfully"))
|
||||||
|
|
||||||
|
|
||||||
|
def make_batch_nos(item_code, batch_nos):
|
||||||
|
item = frappe.get_cached_value("Item", item_code, ["description", "item_code"], as_dict=1)
|
||||||
|
|
||||||
|
batch_nos = [d.get("batch_no") for d in batch_nos if d.get("batch_no")]
|
||||||
|
|
||||||
|
batch_nos_details = []
|
||||||
|
user = frappe.session.user
|
||||||
|
for batch_no in batch_nos:
|
||||||
|
batch_nos_details.append(
|
||||||
|
(batch_no, batch_no, now(), now(), user, user, item.item_code, item.item_name, item.description)
|
||||||
|
)
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
"name",
|
||||||
|
"batch_id",
|
||||||
|
"creation",
|
||||||
|
"modified",
|
||||||
|
"owner",
|
||||||
|
"modified_by",
|
||||||
|
"item",
|
||||||
|
"item_name",
|
||||||
|
"description",
|
||||||
|
]
|
||||||
|
|
||||||
|
frappe.db.bulk_insert("Batch", fields=fields, values=set(batch_nos_details))
|
||||||
|
|
||||||
|
frappe.msgprint(_("Batch Nos are created successfully"))
|
||||||
|
|
||||||
|
|
||||||
|
def parse_serial_nos(data):
|
||||||
|
if isinstance(data, list):
|
||||||
|
return data
|
||||||
|
|
||||||
|
return [s.strip() for s in cstr(data).strip().upper().replace(",", "\n").split("\n") if s.strip()]
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
@ -690,13 +868,13 @@ def get_filters_for_bundle(item_code, docstatus=None, voucher_no=None, name=None
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def add_serial_batch_ledgers(entries, child_row, doc) -> object:
|
def add_serial_batch_ledgers(entries, child_row, doc) -> object:
|
||||||
if isinstance(child_row, str):
|
if isinstance(child_row, str):
|
||||||
child_row = frappe._dict(frappe.parse_json(child_row))
|
child_row = frappe._dict(parse_json(child_row))
|
||||||
|
|
||||||
if isinstance(entries, str):
|
if isinstance(entries, str):
|
||||||
entries = frappe.parse_json(entries)
|
entries = parse_json(entries)
|
||||||
|
|
||||||
if doc and isinstance(doc, str):
|
if doc and isinstance(doc, str):
|
||||||
parent_doc = frappe.parse_json(doc)
|
parent_doc = parse_json(doc)
|
||||||
|
|
||||||
if frappe.db.exists("Serial and Batch Bundle", child_row.serial_and_batch_bundle):
|
if frappe.db.exists("Serial and Batch Bundle", child_row.serial_and_batch_bundle):
|
||||||
doc = update_serial_batch_no_ledgers(entries, child_row, parent_doc)
|
doc = update_serial_batch_no_ledgers(entries, child_row, parent_doc)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user