fix: travis for POS merge invoice and putaway rule
This commit is contained in:
parent
f8bf4aa7c8
commit
74ab20f97a
@ -387,7 +387,7 @@ def split_invoices(invoices):
|
||||
]
|
||||
for pos_invoice in pos_return_docs:
|
||||
for item in pos_invoice.items:
|
||||
if not item.serial_no:
|
||||
if not item.serial_no and not item.serial_and_batch_bundle:
|
||||
continue
|
||||
|
||||
return_against_is_added = any(
|
||||
|
@ -13,6 +13,9 @@ from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_inv
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import (
|
||||
consolidate_pos_invoices,
|
||||
)
|
||||
from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle import (
|
||||
get_serial_nos_from_bundle,
|
||||
)
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
|
||||
|
||||
@ -410,13 +413,13 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
|
||||
|
||||
try:
|
||||
se = make_serialized_item()
|
||||
serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
|
||||
serial_no = get_serial_nos_from_bundle(se.get("items")[0].serial_and_batch_bundle)[0]
|
||||
|
||||
init_user_and_profile()
|
||||
|
||||
pos_inv = create_pos_invoice(
|
||||
item_code="_Test Serialized Item With Series",
|
||||
serial_no=serial_no,
|
||||
serial_no=[serial_no],
|
||||
qty=1,
|
||||
rate=100,
|
||||
do_not_submit=1,
|
||||
@ -430,7 +433,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
|
||||
|
||||
pos_inv2 = create_pos_invoice(
|
||||
item_code="_Test Serialized Item With Series",
|
||||
serial_no=serial_no,
|
||||
serial_no=[serial_no],
|
||||
qty=1,
|
||||
rate=100,
|
||||
do_not_submit=1,
|
||||
|
@ -2981,7 +2981,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
# Sales Invoice with Payment Schedule
|
||||
si_with_payment_schedule = create_sales_invoice(do_not_submit=True)
|
||||
si_with_payment_schedule.extend(
|
||||
si_with_payment_schedule.set(
|
||||
"payment_schedule",
|
||||
[
|
||||
{
|
||||
|
@ -663,19 +663,31 @@ def get_filters(
|
||||
return filters
|
||||
|
||||
|
||||
def get_returned_serial_nos(child_doc, parent_doc, serial_no_field=None):
|
||||
def get_returned_serial_nos(
|
||||
child_doc, parent_doc, serial_no_field=None, ignore_voucher_detail_no=None
|
||||
):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import (
|
||||
get_serial_nos as get_serial_nos_from_serial_no,
|
||||
)
|
||||
from erpnext.stock.serial_batch_bundle import get_serial_nos
|
||||
|
||||
if not serial_no_field:
|
||||
serial_no_field = "serial_and_batch_bundle"
|
||||
|
||||
old_field = "serial_no"
|
||||
if serial_no_field == "rejected_serial_and_batch_bundle":
|
||||
old_field = "rejected_serial_no"
|
||||
|
||||
return_ref_field = frappe.scrub(child_doc.doctype)
|
||||
if child_doc.doctype == "Delivery Note Item":
|
||||
return_ref_field = "dn_detail"
|
||||
|
||||
serial_nos = []
|
||||
|
||||
fields = [f"`{'tab' + child_doc.doctype}`.`{serial_no_field}`"]
|
||||
fields = [
|
||||
f"`{'tab' + child_doc.doctype}`.`{serial_no_field}`",
|
||||
f"`{'tab' + child_doc.doctype}`.`{old_field}`",
|
||||
]
|
||||
|
||||
filters = [
|
||||
[parent_doc.doctype, "return_against", "=", parent_doc.name],
|
||||
@ -684,9 +696,15 @@ def get_returned_serial_nos(child_doc, parent_doc, serial_no_field=None):
|
||||
[parent_doc.doctype, "docstatus", "=", 1],
|
||||
]
|
||||
|
||||
# Required for POS Invoice
|
||||
if ignore_voucher_detail_no:
|
||||
filters.append([child_doc.doctype, "name", "!=", ignore_voucher_detail_no])
|
||||
|
||||
ids = []
|
||||
for row in frappe.get_all(parent_doc.doctype, fields=fields, filters=filters):
|
||||
ids.append(row.get("serial_and_batch_bundle"))
|
||||
if row.get(old_field):
|
||||
serial_nos.extend(get_serial_nos_from_serial_no(row.get(old_field)))
|
||||
|
||||
serial_nos.extend(get_serial_nos(ids))
|
||||
|
||||
|
@ -931,7 +931,7 @@ class TestDeliveryNote(FrappeTestCase):
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "TESTBATCH.#####",
|
||||
"batch_number_series": "TESTBATCHIUU.#####",
|
||||
},
|
||||
)
|
||||
make_product_bundle(parent=batched_bundle.name, items=[batched_item.name])
|
||||
@ -942,7 +942,7 @@ class TestDeliveryNote(FrappeTestCase):
|
||||
dn = create_delivery_note(item_code=batched_bundle.name, qty=1)
|
||||
dn.load_from_db()
|
||||
|
||||
batch_no = get_batch_from_bundle(dn.items[0].serial_and_batch_bundle)
|
||||
batch_no = get_batch_from_bundle(dn.packed_items[0].serial_and_batch_bundle)
|
||||
self.assertTrue(batch_no)
|
||||
|
||||
def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
|
||||
|
@ -11,7 +11,6 @@ from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint, cstr, floor, flt, nowdate
|
||||
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
from erpnext.stock.utils import get_stock_balance
|
||||
|
||||
|
||||
@ -99,7 +98,6 @@ def apply_putaway_rule(doctype, items, company, sync=None, purpose=None):
|
||||
item = frappe._dict(item)
|
||||
|
||||
source_warehouse = item.get("s_warehouse")
|
||||
serial_nos = get_serial_nos(item.get("serial_no"))
|
||||
item.conversion_factor = flt(item.conversion_factor) or 1.0
|
||||
pending_qty, item_code = flt(item.qty), item.item_code
|
||||
pending_stock_qty = flt(item.transfer_qty) if doctype == "Stock Entry" else flt(item.stock_qty)
|
||||
@ -145,9 +143,7 @@ def apply_putaway_rule(doctype, items, company, sync=None, purpose=None):
|
||||
if not qty_to_allocate:
|
||||
break
|
||||
|
||||
updated_table = add_row(
|
||||
item, qty_to_allocate, rule.warehouse, updated_table, rule.name, serial_nos=serial_nos
|
||||
)
|
||||
updated_table = add_row(item, qty_to_allocate, rule.warehouse, updated_table, rule.name)
|
||||
|
||||
pending_stock_qty -= stock_qty_to_allocate
|
||||
pending_qty -= qty_to_allocate
|
||||
@ -245,7 +241,7 @@ def get_ordered_putaway_rules(item_code, company, source_warehouse=None):
|
||||
return False, vacant_rules
|
||||
|
||||
|
||||
def add_row(item, to_allocate, warehouse, updated_table, rule=None, serial_nos=None):
|
||||
def add_row(item, to_allocate, warehouse, updated_table, rule=None):
|
||||
new_updated_table_row = copy.deepcopy(item)
|
||||
new_updated_table_row.idx = 1 if not updated_table else cint(updated_table[-1].idx) + 1
|
||||
new_updated_table_row.name = None
|
||||
@ -264,8 +260,8 @@ def add_row(item, to_allocate, warehouse, updated_table, rule=None, serial_nos=N
|
||||
|
||||
if rule:
|
||||
new_updated_table_row.putaway_rule = rule
|
||||
if serial_nos:
|
||||
new_updated_table_row.serial_no = get_serial_nos_to_allocate(serial_nos, to_allocate)
|
||||
|
||||
new_updated_table_row.serial_and_batch_bundle = ""
|
||||
|
||||
updated_table.append(new_updated_table_row)
|
||||
return updated_table
|
||||
@ -297,12 +293,3 @@ def show_unassigned_items_message(items_not_accomodated):
|
||||
)
|
||||
|
||||
frappe.msgprint(msg, title=_("Insufficient Capacity"), is_minimizable=True, wide=True)
|
||||
|
||||
|
||||
def get_serial_nos_to_allocate(serial_nos, to_allocate):
|
||||
if serial_nos:
|
||||
allocated_serial_nos = serial_nos[0 : cint(to_allocate)]
|
||||
serial_nos[:] = serial_nos[cint(to_allocate) :] # pop out allocated serial nos and modify list
|
||||
return "\n".join(allocated_serial_nos) if allocated_serial_nos else ""
|
||||
else:
|
||||
return ""
|
||||
|
@ -7,6 +7,11 @@ from frappe.tests.utils import FrappeTestCase
|
||||
from erpnext.stock.doctype.batch.test_batch import make_new_batch
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle import (
|
||||
get_batch_from_bundle,
|
||||
get_serial_nos_from_bundle,
|
||||
make_serial_batch_bundle,
|
||||
)
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||
from erpnext.stock.get_item_details import get_conversion_factor
|
||||
@ -382,42 +387,49 @@ class TestPutawayRule(FrappeTestCase):
|
||||
make_new_batch(batch_id="BOTTL-BATCH-1", item_code="Water Bottle")
|
||||
|
||||
pr = make_purchase_receipt(item_code="Water Bottle", qty=5, do_not_submit=1)
|
||||
pr.items[0].batch_no = "BOTTL-BATCH-1"
|
||||
pr.save()
|
||||
pr.submit()
|
||||
pr.load_from_db()
|
||||
|
||||
serial_nos = frappe.get_list(
|
||||
"Serial No", filters={"purchase_document_no": pr.name, "status": "Active"}
|
||||
)
|
||||
serial_nos = [d.name for d in serial_nos]
|
||||
batch_no = get_batch_from_bundle(pr.items[0].serial_and_batch_bundle)
|
||||
serial_nos = get_serial_nos_from_bundle(pr.items[0].serial_and_batch_bundle)
|
||||
|
||||
stock_entry = make_stock_entry(
|
||||
item_code="Water Bottle",
|
||||
source="_Test Warehouse - _TC",
|
||||
qty=5,
|
||||
serial_no=serial_nos,
|
||||
target="Finished Goods - _TC",
|
||||
purpose="Material Transfer",
|
||||
apply_putaway_rule=1,
|
||||
do_not_save=1,
|
||||
)
|
||||
stock_entry.items[0].batch_no = "BOTTL-BATCH-1"
|
||||
stock_entry.items[0].serial_no = "\n".join(serial_nos)
|
||||
stock_entry.save()
|
||||
stock_entry.load_from_db()
|
||||
|
||||
self.assertEqual(stock_entry.items[0].t_warehouse, self.warehouse_1)
|
||||
self.assertEqual(stock_entry.items[0].qty, 3)
|
||||
self.assertEqual(stock_entry.items[0].putaway_rule, rule_1.name)
|
||||
self.assertEqual(stock_entry.items[0].serial_no, "\n".join(serial_nos[:3]))
|
||||
self.assertEqual(stock_entry.items[0].batch_no, "BOTTL-BATCH-1")
|
||||
self.assertEqual(
|
||||
get_serial_nos_from_bundle(stock_entry.items[0].serial_and_batch_bundle), serial_nos[0:3]
|
||||
)
|
||||
self.assertEqual(get_batch_from_bundle(stock_entry.items[0].serial_and_batch_bundle), batch_no)
|
||||
|
||||
self.assertEqual(stock_entry.items[1].t_warehouse, self.warehouse_2)
|
||||
self.assertEqual(stock_entry.items[1].qty, 2)
|
||||
self.assertEqual(stock_entry.items[1].putaway_rule, rule_2.name)
|
||||
self.assertEqual(stock_entry.items[1].serial_no, "\n".join(serial_nos[3:]))
|
||||
self.assertEqual(stock_entry.items[1].batch_no, "BOTTL-BATCH-1")
|
||||
self.assertEqual(
|
||||
get_serial_nos_from_bundle(stock_entry.items[1].serial_and_batch_bundle), serial_nos[3:5]
|
||||
)
|
||||
self.assertEqual(get_batch_from_bundle(stock_entry.items[1].serial_and_batch_bundle), batch_no)
|
||||
|
||||
self.assertUnchangedItemsOnResave(stock_entry)
|
||||
|
||||
for row in stock_entry.items:
|
||||
if row.serial_and_batch_bundle:
|
||||
frappe.delete_doc("Serial and Batch Bundle", row.serial_and_batch_bundle)
|
||||
|
||||
stock_entry.load_from_db()
|
||||
stock_entry.delete()
|
||||
pr.cancel()
|
||||
rule_1.delete()
|
||||
|
@ -6,7 +6,7 @@ from collections import defaultdict
|
||||
from typing import Dict, List
|
||||
|
||||
import frappe
|
||||
from frappe import _, bold
|
||||
from frappe import _, _dict, bold
|
||||
from frappe.model.document import Document
|
||||
from frappe.query_builder.functions import CombineDatetime, Sum
|
||||
from frappe.utils import add_days, cint, flt, get_link_to_form, nowtime, today
|
||||
@ -82,16 +82,20 @@ class SerialandBatchBundle(Document):
|
||||
return
|
||||
|
||||
serial_nos = [d.serial_no for d in self.entries if d.serial_no]
|
||||
available_serial_nos = get_available_serial_nos(
|
||||
frappe._dict(
|
||||
{
|
||||
"item_code": self.item_code,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
}
|
||||
)
|
||||
kwargs = frappe._dict(
|
||||
{
|
||||
"item_code": self.item_code,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"serial_nos": serial_nos,
|
||||
}
|
||||
)
|
||||
|
||||
if self.returned_against and self.docstatus == 1:
|
||||
kwargs["ignore_voucher_detail_no"] = self.voucher_detail_no
|
||||
|
||||
available_serial_nos = get_available_serial_nos(kwargs)
|
||||
|
||||
for data in available_serial_nos:
|
||||
if data.serial_no in serial_nos:
|
||||
self.throw_error_message(
|
||||
@ -776,6 +780,10 @@ def get_available_serial_nos(kwargs):
|
||||
|
||||
ignore_serial_nos = get_reserved_serial_nos_for_pos(kwargs)
|
||||
|
||||
# To ignore serial nos in the same record for the draft state
|
||||
if kwargs.get("ignore_serial_nos"):
|
||||
ignore_serial_nos.extend(kwargs.get("ignore_serial_nos"))
|
||||
|
||||
if kwargs.get("posting_date"):
|
||||
if kwargs.get("posting_time") is None:
|
||||
kwargs.posting_time = nowtime()
|
||||
@ -801,7 +809,7 @@ def get_serial_nos_based_on_posting_date(kwargs, ignore_serial_nos):
|
||||
|
||||
for d in data:
|
||||
if d.serial_and_batch_bundle:
|
||||
sns = get_serial_nos_from_bundle(d.serial_and_batch_bundle)
|
||||
sns = get_serial_nos_from_bundle(d.serial_and_batch_bundle, kwargs.get("serial_nos", []))
|
||||
if d.actual_qty > 0:
|
||||
serial_nos.update(sns)
|
||||
else:
|
||||
@ -823,12 +831,19 @@ def get_serial_nos_based_on_posting_date(kwargs, ignore_serial_nos):
|
||||
|
||||
|
||||
def get_reserved_serial_nos_for_pos(kwargs):
|
||||
from erpnext.controllers.sales_and_purchase_return import get_returned_serial_nos
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
||||
ignore_serial_nos = []
|
||||
pos_invoices = frappe.get_all(
|
||||
"POS Invoice",
|
||||
fields=["`tabPOS Invoice Item`.serial_no", "`tabPOS Invoice Item`.serial_and_batch_bundle"],
|
||||
fields=[
|
||||
"`tabPOS Invoice Item`.serial_no",
|
||||
"`tabPOS Invoice`.is_return",
|
||||
"`tabPOS Invoice Item`.name as child_docname",
|
||||
"`tabPOS Invoice`.name as parent_docname",
|
||||
"`tabPOS Invoice Item`.serial_and_batch_bundle",
|
||||
],
|
||||
filters=[
|
||||
["POS Invoice", "consolidated_invoice", "is", "not set"],
|
||||
["POS Invoice", "docstatus", "=", 1],
|
||||
@ -850,11 +865,35 @@ def get_reserved_serial_nos_for_pos(kwargs):
|
||||
ignore_serial_nos.append(d.serial_no)
|
||||
|
||||
# Will be deprecated in v16
|
||||
returned_serial_nos = []
|
||||
for pos_invoice in pos_invoices:
|
||||
if pos_invoice.serial_no:
|
||||
ignore_serial_nos.extend(get_serial_nos(pos_invoice.serial_no))
|
||||
|
||||
return ignore_serial_nos
|
||||
if pos_invoice.is_return:
|
||||
continue
|
||||
|
||||
child_doc = _dict(
|
||||
{
|
||||
"doctype": "POS Invoice Item",
|
||||
"name": pos_invoice.child_docname,
|
||||
}
|
||||
)
|
||||
|
||||
parent_doc = _dict(
|
||||
{
|
||||
"doctype": "POS Invoice",
|
||||
"name": pos_invoice.parent_docname,
|
||||
}
|
||||
)
|
||||
|
||||
returned_serial_nos.extend(
|
||||
get_returned_serial_nos(
|
||||
child_doc, parent_doc, ignore_voucher_detail_no=kwargs.get("ignore_voucher_detail_no")
|
||||
)
|
||||
)
|
||||
|
||||
return list(set(ignore_serial_nos) - set(returned_serial_nos))
|
||||
|
||||
|
||||
def get_auto_batch_nos(kwargs):
|
||||
|
@ -11,6 +11,11 @@ from frappe.tests.utils import FrappeTestCase
|
||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle import (
|
||||
get_batch_from_bundle,
|
||||
get_serial_nos_from_bundle,
|
||||
make_serial_batch_bundle,
|
||||
)
|
||||
from erpnext.stock.doctype.serial_no.serial_no import *
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
@ -209,23 +214,6 @@ class TestSerialNo(FrappeTestCase):
|
||||
self.assertEqual(sn_doc.warehouse, "_Test Warehouse - _TC")
|
||||
self.assertEqual(sn_doc.purchase_document_no, se.name)
|
||||
|
||||
def test_auto_creation_of_serial_no(self):
|
||||
"""
|
||||
Test if auto created Serial No excludes existing serial numbers
|
||||
"""
|
||||
item_code = make_item(
|
||||
"_Test Auto Serial Item ", {"has_serial_no": 1, "serial_no_series": "XYZ.###"}
|
||||
).item_code
|
||||
|
||||
# Reserve XYZ005
|
||||
pr_1 = make_purchase_receipt(item_code=item_code, qty=1, serial_no="XYZ005")
|
||||
# XYZ005 is already used and will throw an error if used again
|
||||
pr_2 = make_purchase_receipt(item_code=item_code, qty=10)
|
||||
|
||||
self.assertEqual(get_serial_nos(pr_1.get("items")[0].serial_no)[0], "XYZ005")
|
||||
for serial_no in get_serial_nos(pr_2.get("items")[0].serial_no):
|
||||
self.assertNotEqual(serial_no, "XYZ005")
|
||||
|
||||
def test_serial_no_sanitation(self):
|
||||
"Test if Serial No input is sanitised before entering the DB."
|
||||
item_code = "_Test Serialized Item"
|
||||
@ -288,12 +276,12 @@ class TestSerialNo(FrappeTestCase):
|
||||
in1.reload()
|
||||
in2.reload()
|
||||
|
||||
batch1 = in1.items[0].batch_no
|
||||
batch2 = in2.items[0].batch_no
|
||||
batch1 = get_batch_from_bundle(in1.items[0].serial_and_batch_bundle)
|
||||
batch2 = get_batch_from_bundle(in2.items[0].serial_and_batch_bundle)
|
||||
|
||||
batch_wise_serials = {
|
||||
batch1: get_serial_nos(in1.items[0].serial_no),
|
||||
batch2: get_serial_nos(in2.items[0].serial_no),
|
||||
batch1: get_serial_nos_from_bundle(in1.items[0].serial_and_batch_bundle),
|
||||
batch2: get_serial_nos_from_bundle(in2.items[0].serial_and_batch_bundle),
|
||||
}
|
||||
|
||||
# Test FIFO
|
||||
|
@ -142,7 +142,6 @@ class StockEntry(StockController):
|
||||
self.validate_job_card_item()
|
||||
self.set_purpose_for_stock_entry()
|
||||
self.clean_serial_nos()
|
||||
self.validate_duplicate_serial_no()
|
||||
|
||||
if not self.from_bom:
|
||||
self.fg_completed_qty = 0.0
|
||||
@ -878,52 +877,63 @@ class StockEntry(StockController):
|
||||
if self.stock_entry_type and not self.purpose:
|
||||
self.purpose = frappe.get_cached_value("Stock Entry Type", self.stock_entry_type, "purpose")
|
||||
|
||||
def validate_duplicate_serial_no(self):
|
||||
warehouse_wise_serial_nos = {}
|
||||
|
||||
# In case of repack the source and target serial nos could be same
|
||||
for warehouse in ["s_warehouse", "t_warehouse"]:
|
||||
serial_nos = []
|
||||
for row in self.items:
|
||||
if not (row.serial_no and row.get(warehouse)):
|
||||
continue
|
||||
|
||||
for sn in get_serial_nos(row.serial_no):
|
||||
if sn in serial_nos:
|
||||
frappe.throw(
|
||||
_("The serial no {0} has added multiple times in the stock entry {1}").format(
|
||||
frappe.bold(sn), self.name
|
||||
)
|
||||
)
|
||||
|
||||
serial_nos.append(sn)
|
||||
|
||||
def make_serial_and_batch_bundle_for_outward(self):
|
||||
if self.docstatus == 1:
|
||||
return
|
||||
|
||||
serial_or_batch_items = get_serial_or_batch_items(self.items)
|
||||
if not serial_or_batch_items:
|
||||
return
|
||||
|
||||
already_picked_serial_nos = []
|
||||
|
||||
for row in self.items:
|
||||
if not row.s_warehouse:
|
||||
continue
|
||||
|
||||
if row.serial_and_batch_bundle or row.item_code not in serial_or_batch_items:
|
||||
if row.item_code not in serial_or_batch_items:
|
||||
continue
|
||||
|
||||
bundle_doc = SerialBatchCreation(
|
||||
{
|
||||
"item_code": row.item_code,
|
||||
"warehouse": row.s_warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_detail_no": row.name,
|
||||
"qty": row.qty * -1,
|
||||
"type_of_transaction": "Outward",
|
||||
"company": self.company,
|
||||
"do_not_submit": True,
|
||||
}
|
||||
).make_serial_and_batch_bundle()
|
||||
bundle_doc = None
|
||||
if row.serial_and_batch_bundle and abs(row.qty) != abs(
|
||||
frappe.get_cached_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "total_qty")
|
||||
):
|
||||
bundle_doc = SerialBatchCreation(
|
||||
{
|
||||
"item_code": row.item_code,
|
||||
"warehouse": row.s_warehouse,
|
||||
"serial_and_batch_bundle": row.serial_and_batch_bundle,
|
||||
"type_of_transaction": "Outward",
|
||||
"ignore_serial_nos": already_picked_serial_nos,
|
||||
"qty": row.qty * -1,
|
||||
}
|
||||
).update_serial_and_batch_entries()
|
||||
elif not row.serial_and_batch_bundle:
|
||||
bundle_doc = SerialBatchCreation(
|
||||
{
|
||||
"item_code": row.item_code,
|
||||
"warehouse": row.s_warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_detail_no": row.name,
|
||||
"qty": row.qty * -1,
|
||||
"ignore_serial_nos": already_picked_serial_nos,
|
||||
"type_of_transaction": "Outward",
|
||||
"company": self.company,
|
||||
"do_not_submit": True,
|
||||
}
|
||||
).make_serial_and_batch_bundle()
|
||||
|
||||
if not bundle_doc:
|
||||
continue
|
||||
|
||||
if self.docstatus == 0:
|
||||
for entry in bundle_doc.entries:
|
||||
if not entry.serial_no:
|
||||
continue
|
||||
|
||||
already_picked_serial_nos.append(entry.serial_no)
|
||||
|
||||
row.serial_and_batch_bundle = bundle_doc.name
|
||||
|
||||
|
@ -255,11 +255,14 @@ class SerialBatchBundle:
|
||||
frappe.db.set_value("Batch", batch_no, "batch_qty", batches_qty.get(batch_no, 0))
|
||||
|
||||
|
||||
def get_serial_nos(serial_and_batch_bundle):
|
||||
def get_serial_nos(serial_and_batch_bundle, serial_nos=None):
|
||||
filters = {"parent": serial_and_batch_bundle}
|
||||
if isinstance(serial_and_batch_bundle, list):
|
||||
filters = {"parent": ("in", serial_and_batch_bundle)}
|
||||
|
||||
if serial_nos:
|
||||
filters["serial_no"] = ("in", serial_nos)
|
||||
|
||||
entries = frappe.get_all("Serial and Batch Entry", fields=["serial_no"], filters=filters)
|
||||
|
||||
return [d.serial_no for d in entries]
|
||||
@ -694,6 +697,18 @@ class SerialBatchCreation:
|
||||
|
||||
return doc
|
||||
|
||||
def update_serial_and_batch_entries(self):
|
||||
doc = frappe.get_doc("Serial and Batch Bundle", self.serial_and_batch_bundle)
|
||||
doc.type_of_transaction = self.type_of_transaction
|
||||
doc.set("entries", [])
|
||||
self.set_auto_serial_batch_entries_for_outward()
|
||||
self.set_serial_batch_entries(doc)
|
||||
if not doc.get("entries"):
|
||||
return frappe._dict({})
|
||||
|
||||
doc.save()
|
||||
return doc
|
||||
|
||||
def set_auto_serial_batch_entries_for_outward(self):
|
||||
from erpnext.stock.doctype.batch.batch import get_available_batches
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos_for_outward
|
||||
@ -707,6 +722,9 @@ class SerialBatchCreation:
|
||||
}
|
||||
)
|
||||
|
||||
if self.get("ignore_serial_nos"):
|
||||
kwargs["ignore_serial_nos"] = self.ignore_serial_nos
|
||||
|
||||
if self.has_serial_no and not self.get("serial_nos"):
|
||||
self.serial_nos = get_serial_nos_for_outward(kwargs)
|
||||
elif not self.has_serial_no and self.has_batch_no and not self.get("batches"):
|
||||
|
Loading…
x
Reference in New Issue
Block a user