fix: travis issue
This commit is contained in:
parent
7290dd87be
commit
c2d7461d3c
@ -128,6 +128,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
doc = frappe.get_doc("Serial and Batch Bundle", item.serial_and_batch_bundle)
|
doc = frappe.get_doc("Serial and Batch Bundle", item.serial_and_batch_bundle)
|
||||||
|
|
||||||
if doc.docstatus == 0:
|
if doc.docstatus == 0:
|
||||||
|
doc.flags.ignore_voucher_validation = True
|
||||||
doc.submit()
|
doc.submit()
|
||||||
|
|
||||||
def check_phone_payments(self):
|
def check_phone_payments(self):
|
||||||
|
|||||||
@ -76,7 +76,6 @@ class DeprecatedBatchNoValuation:
|
|||||||
|
|
||||||
@deprecated
|
@deprecated
|
||||||
def get_sle_for_batches(self):
|
def get_sle_for_batches(self):
|
||||||
batch_nos = list(self.batch_nos.keys())
|
|
||||||
sle = frappe.qb.DocType("Stock Ledger Entry")
|
sle = frappe.qb.DocType("Stock Ledger Entry")
|
||||||
|
|
||||||
timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(
|
timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(
|
||||||
@ -88,7 +87,11 @@ class DeprecatedBatchNoValuation:
|
|||||||
== CombineDatetime(self.sle.posting_date, self.sle.posting_time)
|
== CombineDatetime(self.sle.posting_date, self.sle.posting_time)
|
||||||
) & (sle.creation < self.sle.creation)
|
) & (sle.creation < self.sle.creation)
|
||||||
|
|
||||||
return (
|
batch_nos = self.batch_nos
|
||||||
|
if isinstance(self.batch_nos, dict):
|
||||||
|
batch_nos = list(self.batch_nos.keys())
|
||||||
|
|
||||||
|
query = (
|
||||||
frappe.qb.from_(sle)
|
frappe.qb.from_(sle)
|
||||||
.select(
|
.select(
|
||||||
sle.batch_no,
|
sle.batch_no,
|
||||||
@ -97,11 +100,15 @@ class DeprecatedBatchNoValuation:
|
|||||||
)
|
)
|
||||||
.where(
|
.where(
|
||||||
(sle.item_code == self.sle.item_code)
|
(sle.item_code == self.sle.item_code)
|
||||||
& (sle.name != self.sle.name)
|
|
||||||
& (sle.warehouse == self.sle.warehouse)
|
& (sle.warehouse == self.sle.warehouse)
|
||||||
& (sle.batch_no.isin(batch_nos))
|
& (sle.batch_no.isin(batch_nos))
|
||||||
& (sle.is_cancelled == 0)
|
& (sle.is_cancelled == 0)
|
||||||
)
|
)
|
||||||
.where(timestamp_condition)
|
.where(timestamp_condition)
|
||||||
.groupby(sle.batch_no)
|
.groupby(sle.batch_no)
|
||||||
).run(as_dict=True)
|
)
|
||||||
|
|
||||||
|
if self.sle.name:
|
||||||
|
query = query.where(sle.name != self.sle.name)
|
||||||
|
|
||||||
|
return query.run(as_dict=True)
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.model.naming import make_autoname, revert_series_if_last
|
from frappe.model.naming import make_autoname, revert_series_if_last
|
||||||
from frappe.query_builder.functions import CombineDatetime, CurDate, Sum
|
from frappe.query_builder.functions import CurDate, Sum
|
||||||
from frappe.utils import cint, flt, get_link_to_form, nowtime
|
from frappe.utils import cint, flt, get_link_to_form
|
||||||
from frappe.utils.data import add_days
|
from frappe.utils.data import add_days
|
||||||
from frappe.utils.jinja import render_template
|
from frappe.utils.jinja import render_template
|
||||||
|
|
||||||
@ -179,44 +179,28 @@ def get_batch_qty(
|
|||||||
:param warehouse: Optional - give qty for this warehouse
|
:param warehouse: Optional - give qty for this warehouse
|
||||||
:param item_code: Optional - give qty for this item"""
|
:param item_code: Optional - give qty for this item"""
|
||||||
|
|
||||||
sle = frappe.qb.DocType("Stock Ledger Entry")
|
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
|
||||||
|
get_auto_batch_nos,
|
||||||
|
)
|
||||||
|
|
||||||
out = 0
|
batchwise_qty = defaultdict(float)
|
||||||
if batch_no and warehouse:
|
kwargs = frappe._dict({
|
||||||
query = (
|
"item_code": item_code,
|
||||||
frappe.qb.from_(sle)
|
"warehouse": warehouse,
|
||||||
.select(Sum(sle.actual_qty))
|
"posting_date": posting_date,
|
||||||
.where((sle.is_cancelled == 0) & (sle.warehouse == warehouse) & (sle.batch_no == batch_no))
|
"posting_time": posting_time,
|
||||||
)
|
"batch_no": batch_no
|
||||||
|
})
|
||||||
|
|
||||||
if posting_date:
|
batches = get_auto_batch_nos(kwargs)
|
||||||
if posting_time is None:
|
|
||||||
posting_time = nowtime()
|
|
||||||
|
|
||||||
query = query.where(
|
if not (batch_no and warehouse):
|
||||||
CombineDatetime(sle.posting_date, sle.posting_time)
|
return batches
|
||||||
<= CombineDatetime(posting_date, posting_time)
|
|
||||||
)
|
|
||||||
|
|
||||||
out = query.run(as_list=True)[0][0] or 0
|
for batch in batches:
|
||||||
|
batchwise_qty[batch.get("batch_no")] += batch.get("qty")
|
||||||
|
|
||||||
if batch_no and not warehouse:
|
return batchwise_qty[batch_no]
|
||||||
out = (
|
|
||||||
frappe.qb.from_(sle)
|
|
||||||
.select(sle.warehouse, Sum(sle.actual_qty).as_("qty"))
|
|
||||||
.where((sle.is_cancelled == 0) & (sle.batch_no == batch_no))
|
|
||||||
.groupby(sle.warehouse)
|
|
||||||
).run(as_dict=True)
|
|
||||||
|
|
||||||
if not batch_no and item_code and warehouse:
|
|
||||||
out = (
|
|
||||||
frappe.qb.from_(sle)
|
|
||||||
.select(sle.batch_no, Sum(sle.actual_qty).as_("qty"))
|
|
||||||
.where((sle.is_cancelled == 0) & (sle.item_code == item_code) & (sle.warehouse == warehouse))
|
|
||||||
.groupby(sle.batch_no)
|
|
||||||
).run(as_dict=True)
|
|
||||||
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@ -366,3 +350,14 @@ def get_available_batches(kwargs):
|
|||||||
batchwise_qty[batch.get("batch_no")] += batch.get("qty")
|
batchwise_qty[batch.get("batch_no")] += batch.get("qty")
|
||||||
|
|
||||||
return batchwise_qty
|
return batchwise_qty
|
||||||
|
|
||||||
|
|
||||||
|
def get_batch_no(bundle_id):
|
||||||
|
from erpnext.stock.serial_batch_bundle import get_batch_nos
|
||||||
|
|
||||||
|
batches = defaultdict(float)
|
||||||
|
|
||||||
|
for batch_id, d in get_batch_nos(bundle_id).items():
|
||||||
|
batches[batch_id] += abs(d.get("qty"))
|
||||||
|
|
||||||
|
return batches
|
||||||
|
|||||||
@ -10,15 +10,15 @@ from frappe.utils import cint, flt
|
|||||||
from frappe.utils.data import add_to_date, getdate
|
from frappe.utils.data import add_to_date, getdate
|
||||||
|
|
||||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||||
from erpnext.stock.doctype.batch.batch import UnableToSelectBatchError, get_batch_no, get_batch_qty
|
from erpnext.stock.doctype.batch.batch import get_batch_no, get_batch_qty
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
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.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
|
||||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
BatchNegativeStockError,
|
||||||
create_stock_reconciliation,
|
|
||||||
)
|
)
|
||||||
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
from erpnext.stock.get_item_details import get_item_details
|
from erpnext.stock.get_item_details import get_item_details
|
||||||
from erpnext.stock.stock_ledger import get_valuation_rate
|
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
|
||||||
|
|
||||||
|
|
||||||
class TestBatch(FrappeTestCase):
|
class TestBatch(FrappeTestCase):
|
||||||
@ -49,8 +49,10 @@ class TestBatch(FrappeTestCase):
|
|||||||
).insert()
|
).insert()
|
||||||
receipt.submit()
|
receipt.submit()
|
||||||
|
|
||||||
self.assertTrue(receipt.items[0].batch_no)
|
receipt.load_from_db()
|
||||||
self.assertEqual(get_batch_qty(receipt.items[0].batch_no, receipt.items[0].warehouse), batch_qty)
|
self.assertTrue(receipt.items[0].serial_and_batch_bundle)
|
||||||
|
batch_no = get_batch_from_bundle(receipt.items[0].serial_and_batch_bundle)
|
||||||
|
self.assertEqual(get_batch_qty(batch_no, receipt.items[0].warehouse), batch_qty)
|
||||||
|
|
||||||
return receipt
|
return receipt
|
||||||
|
|
||||||
@ -80,9 +82,12 @@ class TestBatch(FrappeTestCase):
|
|||||||
stock_entry.insert()
|
stock_entry.insert()
|
||||||
stock_entry.submit()
|
stock_entry.submit()
|
||||||
|
|
||||||
self.assertTrue(stock_entry.items[0].batch_no)
|
stock_entry.load_from_db()
|
||||||
|
|
||||||
|
bundle = stock_entry.items[0].serial_and_batch_bundle
|
||||||
|
self.assertTrue(bundle)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
get_batch_qty(stock_entry.items[0].batch_no, stock_entry.items[0].t_warehouse), 90
|
get_batch_qty(get_batch_from_bundle(bundle), stock_entry.items[0].t_warehouse), 90
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_delivery_note(self):
|
def test_delivery_note(self):
|
||||||
@ -103,25 +108,35 @@ class TestBatch(FrappeTestCase):
|
|||||||
).insert()
|
).insert()
|
||||||
delivery_note.submit()
|
delivery_note.submit()
|
||||||
|
|
||||||
|
receipt.load_from_db()
|
||||||
|
delivery_note.load_from_db()
|
||||||
|
|
||||||
# shipped from FEFO batch
|
# shipped from FEFO batch
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
delivery_note.items[0].batch_no, get_batch_no(item_code, receipt.items[0].warehouse, batch_qty)
|
get_batch_no(delivery_note.items[0].serial_and_batch_bundle),
|
||||||
|
get_batch_no(receipt.items[0].serial_and_batch_bundle),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_delivery_note_fail(self):
|
def test_batch_negative_stock_error(self):
|
||||||
"""Test automatic batch selection for outgoing items"""
|
"""Test automatic batch selection for outgoing items"""
|
||||||
receipt = self.test_purchase_receipt(100)
|
receipt = self.test_purchase_receipt(100)
|
||||||
delivery_note = frappe.get_doc(
|
|
||||||
dict(
|
receipt.load_from_db()
|
||||||
doctype="Delivery Note",
|
batch_no = get_batch_from_bundle(receipt.items[0].serial_and_batch_bundle)
|
||||||
customer="_Test Customer",
|
sn_doc = SerialBatchCreation(
|
||||||
company=receipt.company,
|
{
|
||||||
items=[
|
"item_code": "ITEM-BATCH-1",
|
||||||
dict(item_code="ITEM-BATCH-1", qty=5000, rate=10, warehouse=receipt.items[0].warehouse)
|
"warehouse": receipt.items[0].warehouse,
|
||||||
],
|
"voucher_type": "Delivery Note",
|
||||||
)
|
"qty": 5000,
|
||||||
|
"avg_rate": 10,
|
||||||
|
"batches": frappe._dict({batch_no: 90}),
|
||||||
|
"type_of_transaction": "Outward",
|
||||||
|
"company": receipt.company,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
self.assertRaises(UnableToSelectBatchError, delivery_note.insert)
|
|
||||||
|
self.assertRaises(BatchNegativeStockError, sn_doc.make_serial_and_batch_bundle)
|
||||||
|
|
||||||
def test_stock_entry_outgoing(self):
|
def test_stock_entry_outgoing(self):
|
||||||
"""Test automatic batch selection for outgoing stock entry"""
|
"""Test automatic batch selection for outgoing stock entry"""
|
||||||
@ -149,9 +164,9 @@ class TestBatch(FrappeTestCase):
|
|||||||
stock_entry.insert()
|
stock_entry.insert()
|
||||||
stock_entry.submit()
|
stock_entry.submit()
|
||||||
|
|
||||||
# assert same batch is selected
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
stock_entry.items[0].batch_no, get_batch_no(item_code, receipt.items[0].warehouse, batch_qty)
|
get_batch_no(stock_entry.items[0].serial_and_batch_bundle),
|
||||||
|
get_batch_no(receipt.items[0].serial_and_batch_bundle),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_batch_split(self):
|
def test_batch_split(self):
|
||||||
@ -201,6 +216,19 @@ class TestBatch(FrappeTestCase):
|
|||||||
)
|
)
|
||||||
batch.save()
|
batch.save()
|
||||||
|
|
||||||
|
sn_doc = SerialBatchCreation(
|
||||||
|
{
|
||||||
|
"item_code": item_name,
|
||||||
|
"warehouse": warehouse,
|
||||||
|
"voucher_type": "Stock Entry",
|
||||||
|
"qty": 90,
|
||||||
|
"avg_rate": 10,
|
||||||
|
"batches": frappe._dict({batch_name: 90}),
|
||||||
|
"type_of_transaction": "Inward",
|
||||||
|
"company": "_Test Company",
|
||||||
|
}
|
||||||
|
).make_serial_and_batch_bundle()
|
||||||
|
|
||||||
stock_entry = frappe.get_doc(
|
stock_entry = frappe.get_doc(
|
||||||
dict(
|
dict(
|
||||||
doctype="Stock Entry",
|
doctype="Stock Entry",
|
||||||
@ -210,10 +238,10 @@ class TestBatch(FrappeTestCase):
|
|||||||
dict(
|
dict(
|
||||||
item_code=item_name,
|
item_code=item_name,
|
||||||
qty=90,
|
qty=90,
|
||||||
|
serial_and_batch_bundle=sn_doc.name,
|
||||||
t_warehouse=warehouse,
|
t_warehouse=warehouse,
|
||||||
cost_center="Main - _TC",
|
cost_center="Main - _TC",
|
||||||
rate=10,
|
rate=10,
|
||||||
batch_no=batch_name,
|
|
||||||
allow_zero_valuation_rate=1,
|
allow_zero_valuation_rate=1,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -320,7 +348,8 @@ class TestBatch(FrappeTestCase):
|
|||||||
batches = {}
|
batches = {}
|
||||||
for rate in rates:
|
for rate in rates:
|
||||||
se = make_stock_entry(item_code=item_code, qty=10, rate=rate, target=warehouse)
|
se = make_stock_entry(item_code=item_code, qty=10, rate=rate, target=warehouse)
|
||||||
batches[se.items[0].batch_no] = rate
|
batch_no = get_batch_from_bundle(se.items[0].serial_and_batch_bundle)
|
||||||
|
batches[batch_no] = rate
|
||||||
|
|
||||||
LOW, HIGH = list(batches.keys())
|
LOW, HIGH = list(batches.keys())
|
||||||
|
|
||||||
@ -341,7 +370,9 @@ class TestBatch(FrappeTestCase):
|
|||||||
|
|
||||||
sle = frappe.get_last_doc("Stock Ledger Entry", {"is_cancelled": 0, "voucher_no": se.name})
|
sle = frappe.get_last_doc("Stock Ledger Entry", {"is_cancelled": 0, "voucher_no": se.name})
|
||||||
|
|
||||||
stock_value_difference = sle.actual_qty * batches[sle.batch_no]
|
stock_value_difference = (
|
||||||
|
sle.actual_qty * batches[get_batch_from_bundle(sle.serial_and_batch_bundle)]
|
||||||
|
)
|
||||||
self.assertAlmostEqual(sle.stock_value_difference, stock_value_difference)
|
self.assertAlmostEqual(sle.stock_value_difference, stock_value_difference)
|
||||||
|
|
||||||
stock_value += stock_value_difference
|
stock_value += stock_value_difference
|
||||||
@ -353,45 +384,6 @@ class TestBatch(FrappeTestCase):
|
|||||||
|
|
||||||
self.assertEqual(json.loads(sle.stock_queue), []) # queues don't apply on batched items
|
self.assertEqual(json.loads(sle.stock_queue), []) # queues don't apply on batched items
|
||||||
|
|
||||||
def test_moving_batch_valuation_rates(self):
|
|
||||||
item_code = "_TestBatchWiseVal"
|
|
||||||
warehouse = "_Test Warehouse - _TC"
|
|
||||||
self.make_batch_item(item_code)
|
|
||||||
|
|
||||||
def assertValuation(expected):
|
|
||||||
actual = get_valuation_rate(
|
|
||||||
item_code, warehouse, "voucher_type", "voucher_no", batch_no=batch_no
|
|
||||||
)
|
|
||||||
self.assertAlmostEqual(actual, expected)
|
|
||||||
|
|
||||||
se = make_stock_entry(item_code=item_code, qty=100, rate=10, target=warehouse)
|
|
||||||
batch_no = se.items[0].batch_no
|
|
||||||
assertValuation(10)
|
|
||||||
|
|
||||||
# consumption should never affect current valuation rate
|
|
||||||
make_stock_entry(item_code=item_code, qty=20, source=warehouse)
|
|
||||||
assertValuation(10)
|
|
||||||
|
|
||||||
make_stock_entry(item_code=item_code, qty=30, source=warehouse)
|
|
||||||
assertValuation(10)
|
|
||||||
|
|
||||||
# 50 * 10 = 500 current value, add more item with higher valuation
|
|
||||||
make_stock_entry(item_code=item_code, qty=50, rate=20, target=warehouse, batch_no=batch_no)
|
|
||||||
assertValuation(15)
|
|
||||||
|
|
||||||
# consuming again shouldn't do anything
|
|
||||||
make_stock_entry(item_code=item_code, qty=20, source=warehouse)
|
|
||||||
assertValuation(15)
|
|
||||||
|
|
||||||
# reset rate with stock reconiliation
|
|
||||||
create_stock_reconciliation(
|
|
||||||
item_code=item_code, warehouse=warehouse, qty=10, rate=25, batch_no=batch_no
|
|
||||||
)
|
|
||||||
assertValuation(25)
|
|
||||||
|
|
||||||
make_stock_entry(item_code=item_code, qty=20, rate=20, target=warehouse, batch_no=batch_no)
|
|
||||||
assertValuation((20 * 20 + 10 * 25) / (10 + 20))
|
|
||||||
|
|
||||||
def test_update_batch_properties(self):
|
def test_update_batch_properties(self):
|
||||||
item_code = "_TestBatchWiseVal"
|
item_code = "_TestBatchWiseVal"
|
||||||
self.make_batch_item(item_code)
|
self.make_batch_item(item_code)
|
||||||
@ -430,6 +422,12 @@ class TestBatch(FrappeTestCase):
|
|||||||
self.assertEqual("BATCHEXISTING002", pr_2.items[0].batch_no)
|
self.assertEqual("BATCHEXISTING002", pr_2.items[0].batch_no)
|
||||||
|
|
||||||
|
|
||||||
|
def get_batch_from_bundle(bundle):
|
||||||
|
batches = get_batch_no(bundle)
|
||||||
|
|
||||||
|
return list(batches.keys())[0]
|
||||||
|
|
||||||
|
|
||||||
def create_batch(item_code, rate, create_item_price_for_batch):
|
def create_batch(item_code, rate, create_item_price_for_batch):
|
||||||
pi = make_purchase_invoice(
|
pi = make_purchase_invoice(
|
||||||
company="_Test Company",
|
company="_Test Company",
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import frappe
|
|||||||
from frappe import _, bold
|
from frappe import _, 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, today
|
from frappe.utils import add_days, cint, flt, get_link_to_form, nowtime, today
|
||||||
|
|
||||||
from erpnext.stock.serial_batch_bundle import BatchNoValuation, SerialNoValuation
|
from erpnext.stock.serial_batch_bundle import BatchNoValuation, SerialNoValuation
|
||||||
|
|
||||||
@ -18,6 +18,10 @@ class SerialNoExistsInFutureTransactionError(frappe.ValidationError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BatchNegativeStockError(frappe.ValidationError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SerialandBatchBundle(Document):
|
class SerialandBatchBundle(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_serial_and_batch_no()
|
self.validate_serial_and_batch_no()
|
||||||
@ -81,7 +85,7 @@ class SerialandBatchBundle(Document):
|
|||||||
|
|
||||||
def set_incoming_rate_for_outward_transaction(self, row=None, save=False):
|
def set_incoming_rate_for_outward_transaction(self, row=None, save=False):
|
||||||
sle = self.get_sle_for_outward_transaction(row)
|
sle = self.get_sle_for_outward_transaction(row)
|
||||||
if not sle.actual_qty:
|
if not sle.actual_qty and sle.qty:
|
||||||
sle.actual_qty = sle.qty
|
sle.actual_qty = sle.qty
|
||||||
|
|
||||||
if self.has_serial_no:
|
if self.has_serial_no:
|
||||||
@ -122,7 +126,7 @@ class SerialandBatchBundle(Document):
|
|||||||
of quantity {bold(available_qty)} in the
|
of quantity {bold(available_qty)} in the
|
||||||
warehouse {self.warehouse}"""
|
warehouse {self.warehouse}"""
|
||||||
|
|
||||||
frappe.throw(_(msg))
|
frappe.throw(_(msg), BatchNegativeStockError)
|
||||||
|
|
||||||
def get_sle_for_outward_transaction(self, row):
|
def get_sle_for_outward_transaction(self, row):
|
||||||
return frappe._dict(
|
return frappe._dict(
|
||||||
@ -228,7 +232,13 @@ class SerialandBatchBundle(Document):
|
|||||||
if self.voucher_no and not frappe.db.exists(self.voucher_type, self.voucher_no):
|
if self.voucher_no and not frappe.db.exists(self.voucher_type, self.voucher_no):
|
||||||
self.throw_error_message(f"The {self.voucher_type} # {self.voucher_no} does not exist")
|
self.throw_error_message(f"The {self.voucher_type} # {self.voucher_no} does not exist")
|
||||||
|
|
||||||
if frappe.get_cached_value(self.voucher_type, self.voucher_no, "docstatus") != 1:
|
if self.flags.ignore_voucher_validation:
|
||||||
|
return
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.docstatus == 1
|
||||||
|
and frappe.get_cached_value(self.voucher_type, self.voucher_no, "docstatus") != 1
|
||||||
|
):
|
||||||
self.throw_error_message(f"The {self.voucher_type} # {self.voucher_no} should be submit first.")
|
self.throw_error_message(f"The {self.voucher_type} # {self.voucher_no} should be submit first.")
|
||||||
|
|
||||||
def check_future_entries_exists(self):
|
def check_future_entries_exists(self):
|
||||||
@ -750,6 +760,16 @@ def get_available_batches(kwargs):
|
|||||||
.groupby(batch_ledger.batch_no)
|
.groupby(batch_ledger.batch_no)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if kwargs.get("posting_date"):
|
||||||
|
if kwargs.get("posting_time") is None:
|
||||||
|
kwargs.posting_time = nowtime()
|
||||||
|
|
||||||
|
timestamp_condition = CombineDatetime(
|
||||||
|
stock_ledger_entry.posting_date, stock_ledger_entry.posting_time
|
||||||
|
) <= CombineDatetime(kwargs.posting_date, kwargs.posting_time)
|
||||||
|
|
||||||
|
query = query.where(timestamp_condition)
|
||||||
|
|
||||||
for field in ["warehouse", "item_code"]:
|
for field in ["warehouse", "item_code"]:
|
||||||
if not kwargs.get(field):
|
if not kwargs.get(field):
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -405,28 +405,6 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
set_serial_no: function(frm, cdt, cdn, callback) {
|
|
||||||
var d = frappe.model.get_doc(cdt, cdn);
|
|
||||||
if(!d.item_code && !d.s_warehouse && !d.qty) return;
|
|
||||||
var args = {
|
|
||||||
'item_code' : d.item_code,
|
|
||||||
'warehouse' : cstr(d.s_warehouse),
|
|
||||||
'stock_qty' : d.transfer_qty
|
|
||||||
};
|
|
||||||
frappe.call({
|
|
||||||
method: "erpnext.stock.get_item_details.get_serial_no",
|
|
||||||
args: {"args": args},
|
|
||||||
callback: function(r) {
|
|
||||||
if (!r.exe && r.message){
|
|
||||||
frappe.model.set_value(cdt, cdn, "serial_no", r.message);
|
|
||||||
}
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
make_retention_stock_entry: function(frm) {
|
make_retention_stock_entry: function(frm) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.stock.doctype.stock_entry.stock_entry.move_sample_to_retention_warehouse",
|
method: "erpnext.stock.doctype.stock_entry.stock_entry.move_sample_to_retention_warehouse",
|
||||||
@ -682,9 +660,7 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
|
|
||||||
frappe.ui.form.on('Stock Entry Detail', {
|
frappe.ui.form.on('Stock Entry Detail', {
|
||||||
qty(frm, cdt, cdn) {
|
qty(frm, cdt, cdn) {
|
||||||
frm.events.set_serial_no(frm, cdt, cdn, () => {
|
frm.events.set_basic_rate(frm, cdt, cdn);
|
||||||
frm.events.set_basic_rate(frm, cdt, cdn);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
conversion_factor(frm, cdt, cdn) {
|
conversion_factor(frm, cdt, cdn) {
|
||||||
@ -692,9 +668,7 @@ frappe.ui.form.on('Stock Entry Detail', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
s_warehouse(frm, cdt, cdn) {
|
s_warehouse(frm, cdt, cdn) {
|
||||||
frm.events.set_serial_no(frm, cdt, cdn, () => {
|
frm.events.get_warehouse_details(frm, cdt, cdn);
|
||||||
frm.events.get_warehouse_details(frm, cdt, cdn);
|
|
||||||
});
|
|
||||||
|
|
||||||
// set allow_zero_valuation_rate to 0 if s_warehouse is selected.
|
// set allow_zero_valuation_rate to 0 if s_warehouse is selected.
|
||||||
let item = frappe.get_doc(cdt, cdn);
|
let item = frappe.get_doc(cdt, cdn);
|
||||||
|
|||||||
@ -747,7 +747,7 @@ class StockEntry(StockController):
|
|||||||
currency=erpnext.get_company_currency(self.company),
|
currency=erpnext.get_company_currency(self.company),
|
||||||
company=self.company,
|
company=self.company,
|
||||||
raise_error_if_no_rate=raise_error_if_no_rate,
|
raise_error_if_no_rate=raise_error_if_no_rate,
|
||||||
batch_no=d.batch_no,
|
serial_and_batch_bundle=d.serial_and_batch_bundle,
|
||||||
)
|
)
|
||||||
|
|
||||||
# do not round off basic rate to avoid precision loss
|
# do not round off basic rate to avoid precision loss
|
||||||
@ -904,6 +904,9 @@ class StockEntry(StockController):
|
|||||||
return
|
return
|
||||||
|
|
||||||
for row in self.items:
|
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.serial_and_batch_bundle or row.item_code not in serial_or_batch_items:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -915,7 +918,7 @@ class StockEntry(StockController):
|
|||||||
"posting_time": self.posting_time,
|
"posting_time": self.posting_time,
|
||||||
"voucher_type": self.doctype,
|
"voucher_type": self.doctype,
|
||||||
"voucher_detail_no": row.name,
|
"voucher_detail_no": row.name,
|
||||||
"total_qty": row.qty,
|
"qty": row.qty * -1,
|
||||||
"type_of_transaction": "Outward",
|
"type_of_transaction": "Outward",
|
||||||
"company": self.company,
|
"company": self.company,
|
||||||
"do_not_submit": True,
|
"do_not_submit": True,
|
||||||
@ -1437,10 +1440,8 @@ class StockEntry(StockController):
|
|||||||
"qty": args.get("qty"),
|
"qty": args.get("qty"),
|
||||||
"transfer_qty": args.get("qty"),
|
"transfer_qty": args.get("qty"),
|
||||||
"conversion_factor": 1,
|
"conversion_factor": 1,
|
||||||
"batch_no": "",
|
|
||||||
"actual_qty": 0,
|
"actual_qty": 0,
|
||||||
"basic_rate": 0,
|
"basic_rate": 0,
|
||||||
"serial_no": "",
|
|
||||||
"has_serial_no": item.has_serial_no,
|
"has_serial_no": item.has_serial_no,
|
||||||
"has_batch_no": item.has_batch_no,
|
"has_batch_no": item.has_batch_no,
|
||||||
"sample_quantity": item.sample_quantity,
|
"sample_quantity": item.sample_quantity,
|
||||||
|
|||||||
@ -52,6 +52,7 @@ def make_stock_entry(**args):
|
|||||||
:do_not_save: Optional flag
|
:do_not_save: Optional flag
|
||||||
:do_not_submit: Optional flag
|
:do_not_submit: Optional flag
|
||||||
"""
|
"""
|
||||||
|
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
|
||||||
|
|
||||||
def process_serial_numbers(serial_nos_list):
|
def process_serial_numbers(serial_nos_list):
|
||||||
serial_nos_list = [
|
serial_nos_list = [
|
||||||
@ -131,16 +132,27 @@ def make_stock_entry(**args):
|
|||||||
# We can find out the serial number using the batch source document
|
# We can find out the serial number using the batch source document
|
||||||
serial_number = args.serial_no
|
serial_number = args.serial_no
|
||||||
|
|
||||||
|
bundle_id = None
|
||||||
if not args.serial_no and args.qty and args.batch_no:
|
if not args.serial_no and args.qty and args.batch_no:
|
||||||
serial_number_list = frappe.get_list(
|
batches = frappe._dict({args.batch_no: args.qty})
|
||||||
doctype="Stock Ledger Entry",
|
|
||||||
fields=["serial_no"],
|
bundle_id = (
|
||||||
filters={"batch_no": args.batch_no, "warehouse": args.from_warehouse},
|
SerialBatchCreation(
|
||||||
|
{
|
||||||
|
"item_code": args.item,
|
||||||
|
"warehouse": args.source or args.target,
|
||||||
|
"voucher_type": "Stock Entry",
|
||||||
|
"total_qty": args.qty * (-1 if args.source else 1),
|
||||||
|
"batches": batches,
|
||||||
|
"type_of_transaction": "Outward" if args.source else "Inward",
|
||||||
|
"company": s.company,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.make_serial_and_batch_bundle()
|
||||||
|
.name
|
||||||
)
|
)
|
||||||
serial_number = process_serial_numbers(serial_number_list)
|
|
||||||
|
|
||||||
args.serial_no = serial_number
|
args.serial_no = serial_number
|
||||||
|
|
||||||
s.append(
|
s.append(
|
||||||
"items",
|
"items",
|
||||||
{
|
{
|
||||||
@ -148,6 +160,7 @@ def make_stock_entry(**args):
|
|||||||
"s_warehouse": args.source,
|
"s_warehouse": args.source,
|
||||||
"t_warehouse": args.target,
|
"t_warehouse": args.target,
|
||||||
"qty": args.qty,
|
"qty": args.qty,
|
||||||
|
"serial_and_batch_bundle": bundle_id,
|
||||||
"basic_rate": args.rate or args.basic_rate,
|
"basic_rate": args.rate or args.basic_rate,
|
||||||
"conversion_factor": args.conversion_factor or 1.0,
|
"conversion_factor": args.conversion_factor or 1.0,
|
||||||
"transfer_qty": flt(args.qty) * (flt(args.conversion_factor) or 1.0),
|
"transfer_qty": flt(args.qty) * (flt(args.conversion_factor) or 1.0),
|
||||||
@ -164,4 +177,7 @@ def make_stock_entry(**args):
|
|||||||
s.insert()
|
s.insert()
|
||||||
if not args.do_not_submit:
|
if not args.do_not_submit:
|
||||||
s.submit()
|
s.submit()
|
||||||
|
|
||||||
|
s.load_from_db()
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
|||||||
@ -67,7 +67,7 @@ class SerialBatchBundle:
|
|||||||
"voucher_type": self.sle.voucher_type,
|
"voucher_type": self.sle.voucher_type,
|
||||||
"voucher_no": self.sle.voucher_no,
|
"voucher_no": self.sle.voucher_no,
|
||||||
"voucher_detail_no": self.sle.voucher_detail_no,
|
"voucher_detail_no": self.sle.voucher_detail_no,
|
||||||
"total_qty": self.sle.actual_qty,
|
"qty": self.sle.actual_qty,
|
||||||
"avg_rate": self.sle.incoming_rate,
|
"avg_rate": self.sle.incoming_rate,
|
||||||
"total_amount": flt(self.sle.actual_qty) * flt(self.sle.incoming_rate),
|
"total_amount": flt(self.sle.actual_qty) * flt(self.sle.incoming_rate),
|
||||||
"type_of_transaction": "Inward" if self.sle.actual_qty > 0 else "Outward",
|
"type_of_transaction": "Inward" if self.sle.actual_qty > 0 else "Outward",
|
||||||
@ -136,7 +136,6 @@ class SerialBatchBundle:
|
|||||||
and not self.sle.serial_and_batch_bundle
|
and not self.sle.serial_and_batch_bundle
|
||||||
and self.item_details.has_batch_no == 1
|
and self.item_details.has_batch_no == 1
|
||||||
and self.item_details.create_new_batch
|
and self.item_details.create_new_batch
|
||||||
and self.item_details.batch_number_series
|
|
||||||
):
|
):
|
||||||
self.make_serial_batch_no_bundle()
|
self.make_serial_batch_no_bundle()
|
||||||
elif not self.sle.is_cancelled:
|
elif not self.sle.is_cancelled:
|
||||||
@ -393,7 +392,7 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
|
|||||||
self.calculate_valuation_rate()
|
self.calculate_valuation_rate()
|
||||||
|
|
||||||
def calculate_avg_rate(self):
|
def calculate_avg_rate(self):
|
||||||
if self.sle.actual_qty > 0:
|
if flt(self.sle.actual_qty) > 0:
|
||||||
self.stock_value_change = frappe.get_cached_value(
|
self.stock_value_change = frappe.get_cached_value(
|
||||||
"Serial and Batch Bundle", self.sle.serial_and_batch_bundle, "total_amount"
|
"Serial and Batch Bundle", self.sle.serial_and_batch_bundle, "total_amount"
|
||||||
)
|
)
|
||||||
@ -414,7 +413,9 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
|
|||||||
parent = frappe.qb.DocType("Serial and Batch Bundle")
|
parent = frappe.qb.DocType("Serial and Batch Bundle")
|
||||||
child = frappe.qb.DocType("Serial and Batch Entry")
|
child = frappe.qb.DocType("Serial and Batch Entry")
|
||||||
|
|
||||||
batch_nos = list(self.batch_nos.keys())
|
batch_nos = self.batch_nos
|
||||||
|
if isinstance(self.batch_nos, dict):
|
||||||
|
batch_nos = list(self.batch_nos.keys())
|
||||||
|
|
||||||
timestamp_condition = ""
|
timestamp_condition = ""
|
||||||
if self.sle.posting_date and self.sle.posting_time:
|
if self.sle.posting_date and self.sle.posting_time:
|
||||||
@ -433,7 +434,6 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
|
|||||||
)
|
)
|
||||||
.where(
|
.where(
|
||||||
(child.batch_no.isin(batch_nos))
|
(child.batch_no.isin(batch_nos))
|
||||||
& (child.parent != self.sle.serial_and_batch_bundle)
|
|
||||||
& (parent.warehouse == self.sle.warehouse)
|
& (parent.warehouse == self.sle.warehouse)
|
||||||
& (parent.item_code == self.sle.item_code)
|
& (parent.item_code == self.sle.item_code)
|
||||||
& (parent.docstatus == 1)
|
& (parent.docstatus == 1)
|
||||||
@ -443,8 +443,11 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
|
|||||||
.groupby(child.batch_no)
|
.groupby(child.batch_no)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.sle.serial_and_batch_bundle:
|
||||||
|
query = query.where(child.parent != self.sle.serial_and_batch_bundle)
|
||||||
|
|
||||||
if timestamp_condition:
|
if timestamp_condition:
|
||||||
query.where(timestamp_condition)
|
query = query.where(timestamp_condition)
|
||||||
|
|
||||||
return query.run(as_dict=True)
|
return query.run(as_dict=True)
|
||||||
|
|
||||||
@ -455,6 +458,9 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
|
|||||||
return get_batch_nos(self.sle.serial_and_batch_bundle)
|
return get_batch_nos(self.sle.serial_and_batch_bundle)
|
||||||
|
|
||||||
def set_stock_value_difference(self):
|
def set_stock_value_difference(self):
|
||||||
|
if not self.sle.serial_and_batch_bundle:
|
||||||
|
return
|
||||||
|
|
||||||
self.stock_value_change = 0
|
self.stock_value_change = 0
|
||||||
for batch_no, ledger in self.batch_nos.items():
|
for batch_no, ledger in self.batch_nos.items():
|
||||||
stock_value_change = self.batch_avg_rate[batch_no] * ledger.qty
|
stock_value_change = self.batch_avg_rate[batch_no] * ledger.qty
|
||||||
@ -471,11 +477,10 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
|
|||||||
self.wh_data.stock_value + self.stock_value_change
|
self.wh_data.stock_value + self.stock_value_change
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.wh_data.qty_after_transaction += self.sle.actual_qty
|
||||||
if self.wh_data.qty_after_transaction:
|
if self.wh_data.qty_after_transaction:
|
||||||
self.wh_data.valuation_rate = self.wh_data.stock_value / self.wh_data.qty_after_transaction
|
self.wh_data.valuation_rate = self.wh_data.stock_value / self.wh_data.qty_after_transaction
|
||||||
|
|
||||||
self.wh_data.qty_after_transaction += self.sle.actual_qty
|
|
||||||
|
|
||||||
def get_incoming_rate(self):
|
def get_incoming_rate(self):
|
||||||
return abs(flt(self.stock_value_change) / flt(self.sle.actual_qty))
|
return abs(flt(self.stock_value_change) / flt(self.sle.actual_qty))
|
||||||
|
|
||||||
@ -484,7 +489,8 @@ def get_batch_nos(serial_and_batch_bundle):
|
|||||||
entries = frappe.get_all(
|
entries = frappe.get_all(
|
||||||
"Serial and Batch Entry",
|
"Serial and Batch Entry",
|
||||||
fields=["batch_no", "qty", "name"],
|
fields=["batch_no", "qty", "name"],
|
||||||
filters={"parent": serial_and_batch_bundle, "is_outward": 1},
|
filters={"parent": serial_and_batch_bundle},
|
||||||
|
order_by="idx",
|
||||||
)
|
)
|
||||||
|
|
||||||
return {d.batch_no: d for d in entries}
|
return {d.batch_no: d for d in entries}
|
||||||
@ -591,6 +597,12 @@ class SerialBatchCreation:
|
|||||||
setattr(self, "posting_date", today())
|
setattr(self, "posting_date", today())
|
||||||
self.__dict__["posting_date"] = self.posting_date
|
self.__dict__["posting_date"] = self.posting_date
|
||||||
|
|
||||||
|
if not self.get("actual_qty"):
|
||||||
|
qty = self.get("qty") or self.get("total_qty")
|
||||||
|
|
||||||
|
setattr(self, "actual_qty", qty)
|
||||||
|
self.__dict__["actual_qty"] = self.actual_qty
|
||||||
|
|
||||||
def duplicate_package(self):
|
def duplicate_package(self):
|
||||||
if not self.serial_and_batch_bundle:
|
if not self.serial_and_batch_bundle:
|
||||||
return
|
return
|
||||||
@ -613,14 +625,14 @@ class SerialBatchCreation:
|
|||||||
|
|
||||||
if self.type_of_transaction == "Outward":
|
if self.type_of_transaction == "Outward":
|
||||||
self.set_auto_serial_batch_entries_for_outward()
|
self.set_auto_serial_batch_entries_for_outward()
|
||||||
elif self.type_of_transaction == "Inward":
|
elif self.type_of_transaction == "Inward" and not self.get("batches"):
|
||||||
self.set_auto_serial_batch_entries_for_inward()
|
self.set_auto_serial_batch_entries_for_inward()
|
||||||
|
|
||||||
self.set_serial_batch_entries(doc)
|
self.set_serial_batch_entries(doc)
|
||||||
doc.set_incoming_rate()
|
|
||||||
doc.save()
|
doc.save()
|
||||||
|
|
||||||
if not hasattr(self, "do_not_submit") or not self.do_not_submit:
|
if not hasattr(self, "do_not_submit") or not self.do_not_submit:
|
||||||
|
doc.flags.ignore_voucher_validation = True
|
||||||
doc.submit()
|
doc.submit()
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
@ -633,7 +645,7 @@ class SerialBatchCreation:
|
|||||||
{
|
{
|
||||||
"item_code": self.item_code,
|
"item_code": self.item_code,
|
||||||
"warehouse": self.warehouse,
|
"warehouse": self.warehouse,
|
||||||
"qty": abs(self.total_qty),
|
"qty": abs(self.actual_qty),
|
||||||
"based_on": frappe.db.get_single_value("Stock Settings", "pick_serial_and_batch_based_on"),
|
"based_on": frappe.db.get_single_value("Stock Settings", "pick_serial_and_batch_based_on"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -651,7 +663,7 @@ class SerialBatchCreation:
|
|||||||
if self.has_serial_no:
|
if self.has_serial_no:
|
||||||
self.serial_nos = self.get_auto_created_serial_nos()
|
self.serial_nos = self.get_auto_created_serial_nos()
|
||||||
else:
|
else:
|
||||||
self.batches = frappe._dict({self.batch_no: abs(self.total_qty)})
|
self.batches = frappe._dict({self.batch_no: abs(self.actual_qty)})
|
||||||
|
|
||||||
def set_serial_batch_entries(self, doc):
|
def set_serial_batch_entries(self, doc):
|
||||||
if self.get("serial_nos"):
|
if self.get("serial_nos"):
|
||||||
@ -698,9 +710,9 @@ class SerialBatchCreation:
|
|||||||
return make_batch(
|
return make_batch(
|
||||||
frappe._dict(
|
frappe._dict(
|
||||||
{
|
{
|
||||||
"item": self.item_code,
|
"item": self.get("item_code"),
|
||||||
"reference_doctype": self.voucher_type,
|
"reference_doctype": self.get("voucher_type"),
|
||||||
"reference_name": self.voucher_no,
|
"reference_name": self.get("voucher_no"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -709,7 +721,7 @@ class SerialBatchCreation:
|
|||||||
sr_nos = []
|
sr_nos = []
|
||||||
serial_nos_details = []
|
serial_nos_details = []
|
||||||
|
|
||||||
for i in range(abs(cint(self.total_qty))):
|
for i in range(abs(cint(self.actual_qty))):
|
||||||
serial_no = make_autoname(self.serial_no_series, "Serial No")
|
serial_no = make_autoname(self.serial_no_series, "Serial No")
|
||||||
sr_nos.append(serial_no)
|
sr_nos.append(serial_no)
|
||||||
serial_nos_details.append(
|
serial_nos_details.append(
|
||||||
|
|||||||
@ -732,6 +732,7 @@ class update_entries_after(object):
|
|||||||
self.wh_data.stock_value = flt(self.wh_data.stock_value, self.currency_precision)
|
self.wh_data.stock_value = flt(self.wh_data.stock_value, self.currency_precision)
|
||||||
if not self.wh_data.qty_after_transaction:
|
if not self.wh_data.qty_after_transaction:
|
||||||
self.wh_data.stock_value = 0.0
|
self.wh_data.stock_value = 0.0
|
||||||
|
|
||||||
stock_value_difference = self.wh_data.stock_value - self.wh_data.prev_stock_value
|
stock_value_difference = self.wh_data.stock_value - self.wh_data.prev_stock_value
|
||||||
self.wh_data.prev_stock_value = self.wh_data.stock_value
|
self.wh_data.prev_stock_value = self.wh_data.stock_value
|
||||||
|
|
||||||
@ -1421,7 +1422,7 @@ def get_valuation_rate(
|
|||||||
currency=None,
|
currency=None,
|
||||||
company=None,
|
company=None,
|
||||||
raise_error_if_no_rate=True,
|
raise_error_if_no_rate=True,
|
||||||
batch_no=None,
|
serial_and_batch_bundle=None,
|
||||||
):
|
):
|
||||||
|
|
||||||
if not company:
|
if not company:
|
||||||
@ -1430,21 +1431,20 @@ def get_valuation_rate(
|
|||||||
last_valuation_rate = None
|
last_valuation_rate = None
|
||||||
|
|
||||||
# Get moving average rate of a specific batch number
|
# Get moving average rate of a specific batch number
|
||||||
if warehouse and batch_no and frappe.db.get_value("Batch", batch_no, "use_batchwise_valuation"):
|
if warehouse and serial_and_batch_bundle:
|
||||||
last_valuation_rate = frappe.db.sql(
|
batch_obj = BatchNoValuation(
|
||||||
"""
|
sle=frappe._dict(
|
||||||
select sum(stock_value_difference) / sum(actual_qty)
|
{
|
||||||
from `tabStock Ledger Entry`
|
"item_code": item_code,
|
||||||
where
|
"warehouse": warehouse,
|
||||||
item_code = %s
|
"actual_qty": -1,
|
||||||
AND warehouse = %s
|
"serial_and_batch_bundle": serial_and_batch_bundle,
|
||||||
AND batch_no = %s
|
}
|
||||||
AND is_cancelled = 0
|
)
|
||||||
AND NOT (voucher_no = %s AND voucher_type = %s)
|
|
||||||
""",
|
|
||||||
(item_code, warehouse, batch_no, voucher_no, voucher_type),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return batch_obj.get_incoming_rate()
|
||||||
|
|
||||||
# Get valuation rate from last sle for the same item and warehouse
|
# Get valuation rate from last sle for the same item and warehouse
|
||||||
if not last_valuation_rate or last_valuation_rate[0][0] is None:
|
if not last_valuation_rate or last_valuation_rate[0][0] is None:
|
||||||
last_valuation_rate = frappe.db.sql(
|
last_valuation_rate = frappe.db.sql(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user