fix: travis issue

This commit is contained in:
Rohit Waghchaure 2023-03-29 11:40:36 +05:30
parent 7290dd87be
commit c2d7461d3c
10 changed files with 201 additions and 177 deletions

View File

@ -128,6 +128,7 @@ class POSInvoice(SalesInvoice):
doc = frappe.get_doc("Serial and Batch Bundle", item.serial_and_batch_bundle)
if doc.docstatus == 0:
doc.flags.ignore_voucher_validation = True
doc.submit()
def check_phone_payments(self):

View File

@ -76,7 +76,6 @@ class DeprecatedBatchNoValuation:
@deprecated
def get_sle_for_batches(self):
batch_nos = list(self.batch_nos.keys())
sle = frappe.qb.DocType("Stock Ledger Entry")
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)
) & (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)
.select(
sle.batch_no,
@ -97,11 +100,15 @@ class DeprecatedBatchNoValuation:
)
.where(
(sle.item_code == self.sle.item_code)
& (sle.name != self.sle.name)
& (sle.warehouse == self.sle.warehouse)
& (sle.batch_no.isin(batch_nos))
& (sle.is_cancelled == 0)
)
.where(timestamp_condition)
.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)

View File

@ -8,8 +8,8 @@ import frappe
from frappe import _
from frappe.model.document import Document
from frappe.model.naming import make_autoname, revert_series_if_last
from frappe.query_builder.functions import CombineDatetime, CurDate, Sum
from frappe.utils import cint, flt, get_link_to_form, nowtime
from frappe.query_builder.functions import CurDate, Sum
from frappe.utils import cint, flt, get_link_to_form
from frappe.utils.data import add_days
from frappe.utils.jinja import render_template
@ -179,44 +179,28 @@ def get_batch_qty(
:param warehouse: Optional - give qty for this warehouse
: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
if batch_no and warehouse:
query = (
frappe.qb.from_(sle)
.select(Sum(sle.actual_qty))
.where((sle.is_cancelled == 0) & (sle.warehouse == warehouse) & (sle.batch_no == batch_no))
)
batchwise_qty = defaultdict(float)
kwargs = frappe._dict({
"item_code": item_code,
"warehouse": warehouse,
"posting_date": posting_date,
"posting_time": posting_time,
"batch_no": batch_no
})
if posting_date:
if posting_time is None:
posting_time = nowtime()
batches = get_auto_batch_nos(kwargs)
query = query.where(
CombineDatetime(sle.posting_date, sle.posting_time)
<= CombineDatetime(posting_date, posting_time)
)
if not (batch_no and warehouse):
return batches
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:
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
return batchwise_qty[batch_no]
@frappe.whitelist()
@ -366,3 +350,14 @@ def get_available_batches(kwargs):
batchwise_qty[batch.get("batch_no")] += batch.get("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

View File

@ -10,15 +10,15 @@ from frappe.utils import cint, flt
from frappe.utils.data import add_to_date, getdate
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.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.stock_reconciliation.test_stock_reconciliation import (
create_stock_reconciliation,
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
BatchNegativeStockError,
)
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.stock_ledger import get_valuation_rate
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
class TestBatch(FrappeTestCase):
@ -49,8 +49,10 @@ class TestBatch(FrappeTestCase):
).insert()
receipt.submit()
self.assertTrue(receipt.items[0].batch_no)
self.assertEqual(get_batch_qty(receipt.items[0].batch_no, receipt.items[0].warehouse), batch_qty)
receipt.load_from_db()
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
@ -80,9 +82,12 @@ class TestBatch(FrappeTestCase):
stock_entry.insert()
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(
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):
@ -103,25 +108,35 @@ class TestBatch(FrappeTestCase):
).insert()
delivery_note.submit()
receipt.load_from_db()
delivery_note.load_from_db()
# shipped from FEFO batch
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"""
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)
],
)
receipt.load_from_db()
batch_no = get_batch_from_bundle(receipt.items[0].serial_and_batch_bundle)
sn_doc = SerialBatchCreation(
{
"item_code": "ITEM-BATCH-1",
"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):
"""Test automatic batch selection for outgoing stock entry"""
@ -149,9 +164,9 @@ class TestBatch(FrappeTestCase):
stock_entry.insert()
stock_entry.submit()
# assert same batch is selected
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):
@ -201,6 +216,19 @@ class TestBatch(FrappeTestCase):
)
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(
dict(
doctype="Stock Entry",
@ -210,10 +238,10 @@ class TestBatch(FrappeTestCase):
dict(
item_code=item_name,
qty=90,
serial_and_batch_bundle=sn_doc.name,
t_warehouse=warehouse,
cost_center="Main - _TC",
rate=10,
batch_no=batch_name,
allow_zero_valuation_rate=1,
)
],
@ -320,7 +348,8 @@ class TestBatch(FrappeTestCase):
batches = {}
for rate in rates:
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())
@ -341,7 +370,9 @@ class TestBatch(FrappeTestCase):
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)
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
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):
item_code = "_TestBatchWiseVal"
self.make_batch_item(item_code)
@ -430,6 +422,12 @@ class TestBatch(FrappeTestCase):
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):
pi = make_purchase_invoice(
company="_Test Company",

View File

@ -9,7 +9,7 @@ import frappe
from frappe import _, 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, today
from frappe.utils import add_days, cint, flt, get_link_to_form, nowtime, today
from erpnext.stock.serial_batch_bundle import BatchNoValuation, SerialNoValuation
@ -18,6 +18,10 @@ class SerialNoExistsInFutureTransactionError(frappe.ValidationError):
pass
class BatchNegativeStockError(frappe.ValidationError):
pass
class SerialandBatchBundle(Document):
def validate(self):
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):
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
if self.has_serial_no:
@ -122,7 +126,7 @@ class SerialandBatchBundle(Document):
of quantity {bold(available_qty)} in the
warehouse {self.warehouse}"""
frappe.throw(_(msg))
frappe.throw(_(msg), BatchNegativeStockError)
def get_sle_for_outward_transaction(self, row):
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):
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.")
def check_future_entries_exists(self):
@ -750,6 +760,16 @@ def get_available_batches(kwargs):
.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"]:
if not kwargs.get(field):
continue

View File

@ -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) {
frappe.call({
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', {
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) {
@ -692,9 +668,7 @@ frappe.ui.form.on('Stock Entry Detail', {
},
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.
let item = frappe.get_doc(cdt, cdn);

View File

@ -747,7 +747,7 @@ class StockEntry(StockController):
currency=erpnext.get_company_currency(self.company),
company=self.company,
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
@ -904,6 +904,9 @@ class StockEntry(StockController):
return
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:
continue
@ -915,7 +918,7 @@ class StockEntry(StockController):
"posting_time": self.posting_time,
"voucher_type": self.doctype,
"voucher_detail_no": row.name,
"total_qty": row.qty,
"qty": row.qty * -1,
"type_of_transaction": "Outward",
"company": self.company,
"do_not_submit": True,
@ -1437,10 +1440,8 @@ class StockEntry(StockController):
"qty": args.get("qty"),
"transfer_qty": args.get("qty"),
"conversion_factor": 1,
"batch_no": "",
"actual_qty": 0,
"basic_rate": 0,
"serial_no": "",
"has_serial_no": item.has_serial_no,
"has_batch_no": item.has_batch_no,
"sample_quantity": item.sample_quantity,

View File

@ -52,6 +52,7 @@ def make_stock_entry(**args):
:do_not_save: Optional flag
:do_not_submit: Optional flag
"""
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
def process_serial_numbers(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
serial_number = args.serial_no
bundle_id = None
if not args.serial_no and args.qty and args.batch_no:
serial_number_list = frappe.get_list(
doctype="Stock Ledger Entry",
fields=["serial_no"],
filters={"batch_no": args.batch_no, "warehouse": args.from_warehouse},
batches = frappe._dict({args.batch_no: args.qty})
bundle_id = (
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
s.append(
"items",
{
@ -148,6 +160,7 @@ def make_stock_entry(**args):
"s_warehouse": args.source,
"t_warehouse": args.target,
"qty": args.qty,
"serial_and_batch_bundle": bundle_id,
"basic_rate": args.rate or args.basic_rate,
"conversion_factor": 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()
if not args.do_not_submit:
s.submit()
s.load_from_db()
return s

View File

@ -67,7 +67,7 @@ class SerialBatchBundle:
"voucher_type": self.sle.voucher_type,
"voucher_no": self.sle.voucher_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,
"total_amount": flt(self.sle.actual_qty) * flt(self.sle.incoming_rate),
"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 self.item_details.has_batch_no == 1
and self.item_details.create_new_batch
and self.item_details.batch_number_series
):
self.make_serial_batch_no_bundle()
elif not self.sle.is_cancelled:
@ -393,7 +392,7 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
self.calculate_valuation_rate()
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(
"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")
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 = ""
if self.sle.posting_date and self.sle.posting_time:
@ -433,7 +434,6 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
)
.where(
(child.batch_no.isin(batch_nos))
& (child.parent != self.sle.serial_and_batch_bundle)
& (parent.warehouse == self.sle.warehouse)
& (parent.item_code == self.sle.item_code)
& (parent.docstatus == 1)
@ -443,8 +443,11 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
.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:
query.where(timestamp_condition)
query = query.where(timestamp_condition)
return query.run(as_dict=True)
@ -455,6 +458,9 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
return get_batch_nos(self.sle.serial_and_batch_bundle)
def set_stock_value_difference(self):
if not self.sle.serial_and_batch_bundle:
return
self.stock_value_change = 0
for batch_no, ledger in self.batch_nos.items():
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.qty_after_transaction += self.sle.actual_qty
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.qty_after_transaction += self.sle.actual_qty
def get_incoming_rate(self):
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(
"Serial and Batch Entry",
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}
@ -591,6 +597,12 @@ class SerialBatchCreation:
setattr(self, "posting_date", today())
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):
if not self.serial_and_batch_bundle:
return
@ -613,14 +625,14 @@ class SerialBatchCreation:
if self.type_of_transaction == "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_serial_batch_entries(doc)
doc.set_incoming_rate()
doc.save()
if not hasattr(self, "do_not_submit") or not self.do_not_submit:
doc.flags.ignore_voucher_validation = True
doc.submit()
return doc
@ -633,7 +645,7 @@ class SerialBatchCreation:
{
"item_code": self.item_code,
"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"),
}
)
@ -651,7 +663,7 @@ class SerialBatchCreation:
if self.has_serial_no:
self.serial_nos = self.get_auto_created_serial_nos()
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):
if self.get("serial_nos"):
@ -698,9 +710,9 @@ class SerialBatchCreation:
return make_batch(
frappe._dict(
{
"item": self.item_code,
"reference_doctype": self.voucher_type,
"reference_name": self.voucher_no,
"item": self.get("item_code"),
"reference_doctype": self.get("voucher_type"),
"reference_name": self.get("voucher_no"),
}
)
)
@ -709,7 +721,7 @@ class SerialBatchCreation:
sr_nos = []
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")
sr_nos.append(serial_no)
serial_nos_details.append(

View File

@ -732,6 +732,7 @@ class update_entries_after(object):
self.wh_data.stock_value = flt(self.wh_data.stock_value, self.currency_precision)
if not self.wh_data.qty_after_transaction:
self.wh_data.stock_value = 0.0
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
@ -1421,7 +1422,7 @@ def get_valuation_rate(
currency=None,
company=None,
raise_error_if_no_rate=True,
batch_no=None,
serial_and_batch_bundle=None,
):
if not company:
@ -1430,21 +1431,20 @@ def get_valuation_rate(
last_valuation_rate = None
# 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"):
last_valuation_rate = frappe.db.sql(
"""
select sum(stock_value_difference) / sum(actual_qty)
from `tabStock Ledger Entry`
where
item_code = %s
AND warehouse = %s
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),
if warehouse and serial_and_batch_bundle:
batch_obj = BatchNoValuation(
sle=frappe._dict(
{
"item_code": item_code,
"warehouse": warehouse,
"actual_qty": -1,
"serial_and_batch_bundle": serial_and_batch_bundle,
}
)
)
return batch_obj.get_incoming_rate()
# 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:
last_valuation_rate = frappe.db.sql(