I changed stock_ledger_entry and serial_no so that when I add manufactured goods to the stock it will check for the last added batch for this item and take the name to generate the new serial numbers.
I want to test the code on the latest version on Monday and will clean it up so I can show it to you
If you want to take a look now here are the (messy) changes:
serial_no.py:
def validate_serial_no(sle, item_det):
if item_det.has_serial_no==0:
if sle.serial_no:
frappe.throw(_("Item {0} is not setup for Serial Nos. Column must be blank").format(sle.item_code),
SerialNoNotRequiredError)
else:
if sle.serial_no:
serial_nos = get_serial_nos(sle.serial_no)
if cint(sle.actual_qty) != flt(sle.actual_qty):
frappe.throw(_("Serial No {0} quantity {1} cannot be a fraction").format(sle.item_code, sle.actual_qty))
if len(serial_nos) and len(serial_nos) != abs(cint(sle.actual_qty)):
frappe.throw(_("{0} Serial Numbers required for Item {1}. You have provided {2}.").format(sle.actual_qty, sle.item_code, len(serial_nos)),
SerialNoQtyError)
if len(serial_nos) != len(set(serial_nos)):
frappe.throw(_("Duplicate Serial No entered for Item {0}").format(sle.item_code), SerialNoDuplicateError)
for serial_no in serial_nos:
if frappe.db.exists("Serial No", serial_no):
sr = frappe.get_doc("Serial No", serial_no)
if sr.item_code!=sle.item_code:
if not allow_serial_nos_with_different_item(serial_no, sle):
frappe.throw(_("Serial No {0} does not belong to Item {1}").format(serial_no,
sle.item_code), SerialNoItemError)
if sle.actual_qty > 0 and has_duplicate_serial_no(sr, sle):
frappe.throw(_("Serial No {0} has already been received").format(serial_no),
SerialNoDuplicateError)
if sle.actual_qty < 0:
if sr.warehouse!=sle.warehouse:
frappe.throw(_("Serial No {0} does not belong to Warehouse {1}").format(serial_no,
sle.warehouse), SerialNoWarehouseError)
if sle.voucher_type in ("Delivery Note", "Sales Invoice") \
and sle.is_cancelled=="No" and not sr.warehouse:
frappe.throw(_("Serial No {0} does not belong to any Warehouse")
.format(serial_no), SerialNoWarehouseError)
elif sle.actual_qty < 0:
# transfer out
frappe.throw(_("Serial No {0} not in stock").format(serial_no), SerialNoNotExistsError)
elif sle.actual_qty < 0 or not item_det.serial_no_series:
if not item_det.serial_no_series:
batch_det = frappe.db.sql("""select name from tabBatch where item=%s""", sle.item_code, as_dict=True)
if batch_det:
item_det.serial_no_series = batch_det[-1].name + ".###";#take the last batch element
else:
if sle.actual_qty < 0 or not item_det.serial_no_series:
frappe.throw(_("Serial Nos Required for Serialized Item {0}").format(sle.item_code),
SerialNoRequiredError)
def update_serial_nos(sle, item_det):
if not item_det.serial_no_series:
batch_det = frappe.db.sql("""select name from tabBatch where item=%s""", sle.item_code, as_dict=True)
if batch_det:
item_det.serial_no_series = batch_det[-1].name + ".XXX";#take the last batch element
frappe.msgprint(_("Item {0} not found").format(sle.item_code))
else:
frappe.msgprint(_("Item {0} not found 2").format(sle.item_code))
else:
frappe.msgprint(_("Item {0} not found 3").format(sle.item_code))
if sle.is_cancelled == "No" and not sle.serial_no and sle.actual_qty > 0 \
and item_det.has_serial_no == 1 and item_det.serial_no_series:
from frappe.model.naming import make_autoname
serial_nos = []
for i in xrange(cint(sle.actual_qty)):
frappe.msgprint(_("Item {0} not found 3").format(item_det.serial_no_series))
serial_nos.append(make_autoname(item_det.serial_no_series, "Serial No"))
frappe.db.set(sle, "serial_no", "\n".join(serial_nos))
validate_serial_no(sle, item_det)
if sle.serial_no:
serial_nos = get_serial_nos(sle.serial_no)
for serial_no in serial_nos:
if frappe.db.exists("Serial No", serial_no):
sr = frappe.get_doc("Serial No", serial_no)
sr.via_stock_ledger = True
sr.item_code = sle.item_code
sr.warehouse = sle.warehouse if sle.actual_qty > 0 else None
sr.save(ignore_permissions=True)
elif sle.actual_qty > 0:
make_serial_no(serial_no, sle)
stock_ledger_item.py:
def validate_item(self):
item_det = frappe.db.sql("""select name, has_batch_no, docstatus,
is_stock_item, has_variants, stock_uom, create_new_batch, serial_no_series, has_serial_no
from tabItem where name=%s""", self.item_code, as_dict=True)
batch_det = frappe.db.sql("""select name from tabBatch where item=%s""", self.item_code, as_dict=True)
if not item_det:
frappe.throw(_("Item {0} not found").format(self.item_code))
item_det = item_det[0]
if item_det.is_stock_item != 1:
frappe.throw(_("Item {0} must be a stock Item").format(self.item_code))
#change batch_no
#frappe.throw(_("Serial {0}").format(item_det.serial_no_series))
#self.batch_no = item_det.serial_no_series.split('.')[0];
# check if batch number is required
if self.voucher_type != 'Stock Reconciliation':
if item_det.has_batch_no ==1:
#custom
if not batch_det:
frappe.throw(_("Item {0} has no batch").format(self.item_code))
else:
self.batch_no = batch_det[-1].name;#take the last batch element
if not self.batch_no:
frappe.throw(_("Batch number is mandatory for Item {0}").format(self.item_code))
elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}):
frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, self.item_code))
elif item_det.has_batch_no ==0 and self.batch_no:
frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code))
if item_det.has_variants:
frappe.throw(_("Stock cannot exist for Item {0} since has variants").format(self.item_code),
ItemTemplateCannotHaveStock)
self.stock_uom = item_det.stock_uom