[enhance] automatic batch selection in Delivery Note and Stock Entry
This commit is contained in:
parent
e385b5b97b
commit
551406ab11
@ -177,17 +177,18 @@ class StockController(AccountsController):
|
|||||||
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
|
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
|
||||||
return stock_ledger
|
return stock_ledger
|
||||||
|
|
||||||
def make_batches(self):
|
def make_batches(self, warehouse_field):
|
||||||
'''Create batches if required. Called before submit'''
|
'''Create batches if required. Called before submit'''
|
||||||
for d in self.items:
|
for d in self.items:
|
||||||
has_batch_no, create_new_batch = frappe.db.get_value('Item', d.item_code, ['has_batch_no', 'create_new_batch'])
|
if d.get(warehouse_field) and not d.batch_no:
|
||||||
if has_batch_no and not d.batch_no and create_new_batch:
|
has_batch_no, create_new_batch = frappe.db.get_value('Item', d.item_code, ['has_batch_no', 'create_new_batch'])
|
||||||
d.batch_no = frappe.get_doc(dict(
|
if has_batch_no and create_new_batch:
|
||||||
doctype='Batch',
|
d.batch_no = frappe.get_doc(dict(
|
||||||
item=d.item_code,
|
doctype='Batch',
|
||||||
supplier=getattr(self, 'supplier', None),
|
item=d.item_code,
|
||||||
reference_doctype=self.doctype,
|
supplier=getattr(self, 'supplier', None),
|
||||||
reference_name=self.name)).insert().name
|
reference_doctype=self.doctype,
|
||||||
|
reference_name=self.name)).insert().name
|
||||||
|
|
||||||
def make_adjustment_entry(self, expected_gle, voucher_obj):
|
def make_adjustment_entry(self, expected_gle, voucher_obj):
|
||||||
from erpnext.accounts.utils import get_stock_and_account_difference
|
from erpnext.accounts.utils import get_stock_and_account_difference
|
||||||
|
@ -6,6 +6,8 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class UnableToSelectBatchError(frappe.ValidationError): pass
|
||||||
|
|
||||||
class Batch(Document):
|
class Batch(Document):
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
'''Generate random ID for batch if not specified'''
|
'''Generate random ID for batch if not specified'''
|
||||||
@ -34,8 +36,15 @@ class Batch(Document):
|
|||||||
frappe.throw(_("The selected item cannot have Batch"))
|
frappe.throw(_("The selected item cannot have Batch"))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_batch_qty(batch_no, warehouse=None):
|
def get_batch_qty(batch_no=None, warehouse=None, item_code=None):
|
||||||
'''Returns batch actual qty if warehouse is passed, or returns dict of qty by warehouse if warehouse is None'''
|
'''Returns batch actual qty if warehouse is passed,
|
||||||
|
or returns dict of qty by warehouse if warehouse is None
|
||||||
|
|
||||||
|
The user must pass either batch_no or batch_no + warehouse or item_code + warehouse
|
||||||
|
|
||||||
|
:param batch_no: Optional - give qty for this batch no
|
||||||
|
:param warehouse: Optional - give qty for this warehouse
|
||||||
|
:param item_code: Optional - give qty for this item'''
|
||||||
frappe.has_permission('Batch', throw=True)
|
frappe.has_permission('Batch', throw=True)
|
||||||
out = 0
|
out = 0
|
||||||
if batch_no and warehouse:
|
if batch_no and warehouse:
|
||||||
@ -48,6 +57,11 @@ def get_batch_qty(batch_no, warehouse=None):
|
|||||||
from `tabStock Ledger Entry`
|
from `tabStock Ledger Entry`
|
||||||
where batch_no=%s
|
where batch_no=%s
|
||||||
group by warehouse''', batch_no, as_dict=1)
|
group by warehouse''', batch_no, as_dict=1)
|
||||||
|
if not batch_no and item_code and warehouse:
|
||||||
|
out = frappe.db.sql('''select batch_no, sum(actual_qty) as qty
|
||||||
|
from `tabStock Ledger Entry`
|
||||||
|
where item_code = %s and warehouse=%s
|
||||||
|
group by batch_no''', (item_code, warehouse), as_dict=1)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@ -76,3 +90,30 @@ def split_batch(batch_no, item_code, warehouse, qty, new_batch_id = None):
|
|||||||
stock_entry.submit()
|
stock_entry.submit()
|
||||||
|
|
||||||
return batch.name
|
return batch.name
|
||||||
|
|
||||||
|
def set_batch_nos(doc, warehouse_field, throw = False):
|
||||||
|
'''Automatically select `batch_no` for outgoing items in item table'''
|
||||||
|
for d in doc.items:
|
||||||
|
has_batch_no = frappe.db.get_value('Item', d.item_code, 'has_batch_no')
|
||||||
|
warehouse = d.get(warehouse_field, None)
|
||||||
|
if has_batch_no and not d.batch_no and warehouse:
|
||||||
|
d.batch_no = get_batch_no(d.item_code, warehouse, d.qty, throw)
|
||||||
|
|
||||||
|
def get_batch_no(item_code, warehouse, qty, throw=False):
|
||||||
|
'''get the smallest batch with for the given item_code, warehouse and qty'''
|
||||||
|
batches = sorted(
|
||||||
|
get_batch_qty(item_code = item_code, warehouse = warehouse),
|
||||||
|
lambda a, b: 1 if a.qty > b.qty else -1)
|
||||||
|
|
||||||
|
batch_no = None
|
||||||
|
for b in batches:
|
||||||
|
if b.qty >= qty:
|
||||||
|
batch_no = b.batch_no
|
||||||
|
# found!
|
||||||
|
break
|
||||||
|
|
||||||
|
if not batch_no:
|
||||||
|
frappe.msgprint(_('Please select a Batch for Item {0}. Unable to find a single batch that fulfills this requirement').format(frappe.bold(item_code)))
|
||||||
|
if throw: raise UnableToSelectBatchError
|
||||||
|
|
||||||
|
return batch_no
|
@ -6,7 +6,7 @@ import frappe
|
|||||||
from frappe.exceptions import ValidationError
|
from frappe.exceptions import ValidationError
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from erpnext.stock.doctype.batch.batch import get_batch_qty
|
from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError
|
||||||
|
|
||||||
class TestBatch(unittest.TestCase):
|
class TestBatch(unittest.TestCase):
|
||||||
def test_item_has_batch_enabled(self):
|
def test_item_has_batch_enabled(self):
|
||||||
@ -21,7 +21,7 @@ class TestBatch(unittest.TestCase):
|
|||||||
if not frappe.db.exists('ITEM-BATCH-1'):
|
if not frappe.db.exists('ITEM-BATCH-1'):
|
||||||
make_item('ITEM-BATCH-1', dict(has_batch_no = 1, create_new_batch = 1))
|
make_item('ITEM-BATCH-1', dict(has_batch_no = 1, create_new_batch = 1))
|
||||||
|
|
||||||
def test_purchase_receipt(self):
|
def test_purchase_receipt(self, batch_qty = 100):
|
||||||
'''Test automated batch creation from Purchase Receipt'''
|
'''Test automated batch creation from Purchase Receipt'''
|
||||||
self.make_batch_item()
|
self.make_batch_item()
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ class TestBatch(unittest.TestCase):
|
|||||||
items = [
|
items = [
|
||||||
dict(
|
dict(
|
||||||
item_code = 'ITEM-BATCH-1',
|
item_code = 'ITEM-BATCH-1',
|
||||||
qty = 100,
|
qty = batch_qty,
|
||||||
rate = 10
|
rate = 10
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
@ -39,11 +39,12 @@ class TestBatch(unittest.TestCase):
|
|||||||
receipt.submit()
|
receipt.submit()
|
||||||
|
|
||||||
self.assertTrue(receipt.items[0].batch_no)
|
self.assertTrue(receipt.items[0].batch_no)
|
||||||
self.assertEquals(get_batch_qty(receipt.items[0].batch_no, receipt.items[0].warehouse), 100)
|
self.assertEquals(get_batch_qty(receipt.items[0].batch_no,
|
||||||
|
receipt.items[0].warehouse), batch_qty)
|
||||||
|
|
||||||
return receipt
|
return receipt
|
||||||
|
|
||||||
def test_stock_entry(self):
|
def test_stock_entry_incoming(self):
|
||||||
'''Test batch creation via Stock Entry (Production Order)'''
|
'''Test batch creation via Stock Entry (Production Order)'''
|
||||||
|
|
||||||
self.make_batch_item()
|
self.make_batch_item()
|
||||||
@ -67,6 +68,78 @@ class TestBatch(unittest.TestCase):
|
|||||||
self.assertTrue(stock_entry.items[0].batch_no)
|
self.assertTrue(stock_entry.items[0].batch_no)
|
||||||
self.assertEquals(get_batch_qty(stock_entry.items[0].batch_no, stock_entry.items[0].t_warehouse), 90)
|
self.assertEquals(get_batch_qty(stock_entry.items[0].batch_no, stock_entry.items[0].t_warehouse), 90)
|
||||||
|
|
||||||
|
def test_delivery_note(self):
|
||||||
|
'''Test automatic batch selection for outgoing items'''
|
||||||
|
batch_qty = 15
|
||||||
|
receipt = self.test_purchase_receipt(batch_qty)
|
||||||
|
|
||||||
|
delivery_note = frappe.get_doc(dict(
|
||||||
|
doctype = 'Delivery Note',
|
||||||
|
customer = '_Test Customer',
|
||||||
|
company = receipt.company,
|
||||||
|
items = [
|
||||||
|
dict(
|
||||||
|
item_code = 'ITEM-BATCH-1',
|
||||||
|
qty = batch_qty,
|
||||||
|
rate = 10,
|
||||||
|
warehouse = receipt.items[0].warehouse
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)).insert()
|
||||||
|
delivery_note.submit()
|
||||||
|
|
||||||
|
# shipped with same batch
|
||||||
|
self.assertEquals(delivery_note.items[0].batch_no, receipt.items[0].batch_no)
|
||||||
|
|
||||||
|
# balance is 0
|
||||||
|
self.assertEquals(get_batch_qty(receipt.items[0].batch_no,
|
||||||
|
receipt.items[0].warehouse), 0)
|
||||||
|
|
||||||
|
def test_delivery_note_fail(self):
|
||||||
|
'''Test automatic batch selection for outgoing items'''
|
||||||
|
receipt = self.test_purchase_receipt(100)
|
||||||
|
delivery_note = frappe.get_doc(dict(
|
||||||
|
doctype = 'Delivery Note',
|
||||||
|
customer = '_Test Customer',
|
||||||
|
company = receipt.company,
|
||||||
|
items = [
|
||||||
|
dict(
|
||||||
|
item_code = 'ITEM-BATCH-1',
|
||||||
|
qty = 5000,
|
||||||
|
rate = 10,
|
||||||
|
warehouse = receipt.items[0].warehouse
|
||||||
|
)
|
||||||
|
]
|
||||||
|
))
|
||||||
|
self.assertRaises(UnableToSelectBatchError, delivery_note.insert)
|
||||||
|
|
||||||
|
def test_stock_entry_outgoing(self):
|
||||||
|
'''Test automatic batch selection for outgoing stock entry'''
|
||||||
|
|
||||||
|
batch_qty = 16
|
||||||
|
receipt = self.test_purchase_receipt(batch_qty)
|
||||||
|
|
||||||
|
stock_entry = frappe.get_doc(dict(
|
||||||
|
doctype = 'Stock Entry',
|
||||||
|
purpose = 'Material Issue',
|
||||||
|
company = receipt.company,
|
||||||
|
items = [
|
||||||
|
dict(
|
||||||
|
item_code = 'ITEM-BATCH-1',
|
||||||
|
qty = batch_qty,
|
||||||
|
s_warehouse = receipt.items[0].warehouse,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)).insert()
|
||||||
|
stock_entry.submit()
|
||||||
|
|
||||||
|
# assert same batch is selected
|
||||||
|
self.assertEqual(stock_entry.items[0].batch_no, receipt.items[0].batch_no)
|
||||||
|
|
||||||
|
# balance is 0
|
||||||
|
self.assertEquals(get_batch_qty(receipt.items[0].batch_no,
|
||||||
|
receipt.items[0].warehouse), 0)
|
||||||
|
|
||||||
def test_batch_split(self):
|
def test_batch_split(self):
|
||||||
'''Test batch splitting'''
|
'''Test batch splitting'''
|
||||||
receipt = self.test_purchase_receipt()
|
receipt = self.test_purchase_receipt()
|
||||||
|
@ -11,7 +11,7 @@ import frappe.defaults
|
|||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from erpnext.controllers.selling_controller import SellingController
|
from erpnext.controllers.selling_controller import SellingController
|
||||||
from frappe.desk.notifications import clear_doctype_notifications
|
from frappe.desk.notifications import clear_doctype_notifications
|
||||||
|
from erpnext.stock.doctype.batch.batch import set_batch_nos
|
||||||
|
|
||||||
form_grid_templates = {
|
form_grid_templates = {
|
||||||
"items": "templates/form_grid/item_grid.html"
|
"items": "templates/form_grid/item_grid.html"
|
||||||
@ -106,6 +106,9 @@ class DeliveryNote(SellingController):
|
|||||||
self.validate_uom_is_integer("uom", "qty")
|
self.validate_uom_is_integer("uom", "qty")
|
||||||
self.validate_with_previous_doc()
|
self.validate_with_previous_doc()
|
||||||
|
|
||||||
|
if self._action != 'submit':
|
||||||
|
set_batch_nos(self, 'warehouse', True)
|
||||||
|
|
||||||
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
||||||
make_packing_list(self)
|
make_packing_list(self)
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
super(PurchaseReceipt, self).validate()
|
super(PurchaseReceipt, self).validate()
|
||||||
|
|
||||||
if self._action=="submit":
|
if self._action=="submit":
|
||||||
self.make_batches()
|
self.make_batches('warehouse')
|
||||||
else:
|
else:
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ from frappe.utils import cstr, cint, flt, comma_or, getdate, nowdate, formatdate
|
|||||||
from erpnext.stock.utils import get_incoming_rate
|
from erpnext.stock.utils import get_incoming_rate
|
||||||
from erpnext.stock.stock_ledger import get_previous_sle, NegativeStockError
|
from erpnext.stock.stock_ledger import get_previous_sle, NegativeStockError
|
||||||
from erpnext.stock.get_item_details import get_bin_details, get_default_cost_center, get_conversion_factor
|
from erpnext.stock.get_item_details import get_bin_details, get_default_cost_center, get_conversion_factor
|
||||||
|
from erpnext.stock.doctype.batch.batch import get_batch_no, set_batch_nos
|
||||||
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
|
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
|
||||||
import json
|
import json
|
||||||
|
|
||||||
@ -49,7 +50,9 @@ class StockEntry(StockController):
|
|||||||
self.validate_batch()
|
self.validate_batch()
|
||||||
|
|
||||||
if self._action == 'submit':
|
if self._action == 'submit':
|
||||||
self.make_batches()
|
self.make_batches('t_warehouse')
|
||||||
|
else:
|
||||||
|
set_batch_nos(self, 's_warehouse', True)
|
||||||
|
|
||||||
self.set_actual_qty()
|
self.set_actual_qty()
|
||||||
self.calculate_rate_and_amount(update_finished_item_rate=False)
|
self.calculate_rate_and_amount(update_finished_item_rate=False)
|
||||||
@ -89,8 +92,10 @@ class StockEntry(StockController):
|
|||||||
if item.item_code not in stock_items:
|
if item.item_code not in stock_items:
|
||||||
frappe.throw(_("{0} is not a stock Item").format(item.item_code))
|
frappe.throw(_("{0} is not a stock Item").format(item.item_code))
|
||||||
|
|
||||||
item_details = self.get_item_details(frappe._dict({"item_code": item.item_code,
|
item_details = self.get_item_details(frappe._dict(
|
||||||
"company": self.company, "project": self.project, "uom": item.uom}), for_update=True)
|
{"item_code": item.item_code, "company": self.company,
|
||||||
|
"project": self.project, "uom": item.uom, 's_warehouse': item.s_warehouse}),
|
||||||
|
for_update=True)
|
||||||
|
|
||||||
for f in ("uom", "stock_uom", "description", "item_name", "expense_account",
|
for f in ("uom", "stock_uom", "description", "item_name", "expense_account",
|
||||||
"cost_center", "conversion_factor"):
|
"cost_center", "conversion_factor"):
|
||||||
@ -465,7 +470,9 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
def get_item_details(self, args=None, for_update=False):
|
def get_item_details(self, args=None, for_update=False):
|
||||||
item = frappe.db.sql("""select stock_uom, description, image, item_name,
|
item = frappe.db.sql("""select stock_uom, description, image, item_name,
|
||||||
expense_account, buying_cost_center, item_group from `tabItem`
|
expense_account, buying_cost_center, item_group, has_serial_no,
|
||||||
|
has_batch_no
|
||||||
|
from `tabItem`
|
||||||
where name = %s
|
where name = %s
|
||||||
and disabled=0
|
and disabled=0
|
||||||
and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)""",
|
and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)""",
|
||||||
@ -475,7 +482,7 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
item = item[0]
|
item = item[0]
|
||||||
|
|
||||||
ret = {
|
ret = frappe._dict({
|
||||||
'uom' : item.stock_uom,
|
'uom' : item.stock_uom,
|
||||||
'stock_uom' : item.stock_uom,
|
'stock_uom' : item.stock_uom,
|
||||||
'description' : item.description,
|
'description' : item.description,
|
||||||
@ -489,8 +496,10 @@ class StockEntry(StockController):
|
|||||||
'batch_no' : '',
|
'batch_no' : '',
|
||||||
'actual_qty' : 0,
|
'actual_qty' : 0,
|
||||||
'basic_rate' : 0,
|
'basic_rate' : 0,
|
||||||
'serial_no' : ''
|
'serial_no' : '',
|
||||||
}
|
'has_serial_no' : item.has_serial_no,
|
||||||
|
'has_batch_no' : item.has_batch_no
|
||||||
|
})
|
||||||
for d in [["Account", "expense_account", "default_expense_account"],
|
for d in [["Account", "expense_account", "default_expense_account"],
|
||||||
["Cost Center", "cost_center", "cost_center"]]:
|
["Cost Center", "cost_center", "cost_center"]]:
|
||||||
company = frappe.db.get_value(d[0], ret.get(d[1]), "company")
|
company = frappe.db.get_value(d[0], ret.get(d[1]), "company")
|
||||||
@ -510,6 +519,11 @@ class StockEntry(StockController):
|
|||||||
stock_and_rate = args.get('warehouse') and get_warehouse_details(args) or {}
|
stock_and_rate = args.get('warehouse') and get_warehouse_details(args) or {}
|
||||||
ret.update(stock_and_rate)
|
ret.update(stock_and_rate)
|
||||||
|
|
||||||
|
# automatically select batch for outgoing item
|
||||||
|
if (args.get('s_warehouse', None) and args.get('qty') and
|
||||||
|
ret.get('has_batch_no') and not args.get('batch_no')):
|
||||||
|
args.batch_no = get_batch_no(args['item_code'], args['s_warehouse'], args['qty'])
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_items(self):
|
def get_items(self):
|
||||||
|
@ -813,7 +813,7 @@
|
|||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"fieldname": "serial_no",
|
"fieldname": "serial_no",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Small Text",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
@ -1040,11 +1040,11 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"fieldname": "allow_zero_valuation_rate",
|
"fieldname": "is_sample_item",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
@ -1053,7 +1053,7 @@
|
|||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 0,
|
||||||
"label": "Allow Zero Valuation Rate",
|
"label": "Is Sample Item",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@ -1225,7 +1225,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2017-04-19 11:54:31.645381",
|
"modified": "2017-04-21 02:56:48.306626",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Stock Entry Detail",
|
"name": "Stock Entry Detail",
|
||||||
|
@ -9,6 +9,7 @@ import json
|
|||||||
from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item, set_transaction_type
|
from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item, set_transaction_type
|
||||||
from erpnext.setup.utils import get_exchange_rate
|
from erpnext.setup.utils import get_exchange_rate
|
||||||
from frappe.model.meta import get_field_precision
|
from frappe.model.meta import get_field_precision
|
||||||
|
from erpnext.stock.doctype.batch.batch import get_batch_no
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_item_details(args):
|
def get_item_details(args):
|
||||||
@ -74,7 +75,12 @@ def get_item_details(args):
|
|||||||
out.update(get_pricing_rule_for_item(args))
|
out.update(get_pricing_rule_for_item(args))
|
||||||
|
|
||||||
if args.get("doctype") in ("Sales Invoice", "Delivery Note") and out.stock_qty > 0:
|
if args.get("doctype") in ("Sales Invoice", "Delivery Note") and out.stock_qty > 0:
|
||||||
out.serial_no = get_serial_no(out)
|
if out.has_serial_no:
|
||||||
|
out.serial_no = get_serial_no(out)
|
||||||
|
|
||||||
|
if out.has_batch_no:
|
||||||
|
out.batch_no = get_batch_no(out.item_code, out.warehouse, out.qty)
|
||||||
|
|
||||||
|
|
||||||
if args.transaction_date and item.lead_time_days:
|
if args.transaction_date and item.lead_time_days:
|
||||||
out.schedule_date = out.lead_time_date = add_days(args.transaction_date,
|
out.schedule_date = out.lead_time_date = add_days(args.transaction_date,
|
||||||
@ -154,6 +160,8 @@ def get_basic_details(args, item):
|
|||||||
"income_account": get_default_income_account(args, item),
|
"income_account": get_default_income_account(args, item),
|
||||||
"expense_account": get_default_expense_account(args, item),
|
"expense_account": get_default_expense_account(args, item),
|
||||||
"cost_center": get_default_cost_center(args, item),
|
"cost_center": get_default_cost_center(args, item),
|
||||||
|
'has_serial_no': item.has_serial_no,
|
||||||
|
'has_batch_no': item.has_batch_no,
|
||||||
"batch_no": None,
|
"batch_no": None,
|
||||||
"item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in
|
"item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in
|
||||||
item.get("taxes")))),
|
item.get("taxes")))),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user