Compare commits
28 Commits
version-15
...
v15.55.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dd99f896e | ||
|
|
7579e00425 | ||
|
|
c22869fed9 | ||
|
|
57e2619cf1 | ||
|
|
66d0ad1bc6 | ||
|
|
3395e7c2cd | ||
|
|
0721816763 | ||
|
|
e45d0779ef | ||
|
|
c6ce76170b | ||
|
|
de3e6922b5 | ||
|
|
ae6d3f27a2 | ||
|
|
7795030b7b | ||
|
|
9bac43acff | ||
|
|
1e987153c9 | ||
|
|
d36a7c2389 | ||
|
|
b548cc411d | ||
|
|
ad3f985dc4 | ||
|
|
f3ba5a81ab | ||
|
|
1b6aeba267 | ||
|
|
e393ce9a47 | ||
|
|
8951efb457 | ||
|
|
5db2a19778 | ||
|
|
35ac96f1ec | ||
|
|
187ebaaecd | ||
|
|
1ec971f805 | ||
|
|
4d7071299e | ||
|
|
08f47b626c | ||
|
|
0283f7526c |
@@ -4,7 +4,7 @@ import inspect
|
||||
import frappe
|
||||
from frappe.utils.user import is_website_user
|
||||
|
||||
__version__ = "15.54.3"
|
||||
__version__ = "15.55.4"
|
||||
|
||||
|
||||
def get_default_company(user=None):
|
||||
|
||||
@@ -105,8 +105,7 @@
|
||||
"label": "Cost Center",
|
||||
"oldfieldname": "cost_center",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Cost Center",
|
||||
"search_index": 1
|
||||
"options": "Cost Center"
|
||||
},
|
||||
{
|
||||
"fieldname": "debit",
|
||||
@@ -359,7 +358,7 @@
|
||||
"idx": 1,
|
||||
"in_create": 1,
|
||||
"links": [],
|
||||
"modified": "2025-03-21 15:29:11.221890",
|
||||
"modified": "2025-02-21 14:36:49.431166",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "GL Entry",
|
||||
|
||||
@@ -443,21 +443,22 @@ class AccountsController(TransactionBase):
|
||||
)
|
||||
|
||||
def validate_party_address_and_contact(self):
|
||||
party, party_type = None, None
|
||||
if self.get("customer"):
|
||||
party, party_type = self.customer, "Customer"
|
||||
party_type, party = self.get_party()
|
||||
|
||||
if not (party_type and party):
|
||||
return
|
||||
|
||||
if party_type == "Customer":
|
||||
billing_address, shipping_address = (
|
||||
self.get("customer_address"),
|
||||
self.get("shipping_address_name"),
|
||||
)
|
||||
self.validate_party_address(party, party_type, billing_address, shipping_address)
|
||||
elif self.get("supplier"):
|
||||
party, party_type = self.supplier, "Supplier"
|
||||
elif party_type == "Supplier":
|
||||
billing_address = self.get("supplier_address")
|
||||
self.validate_party_address(party, party_type, billing_address)
|
||||
|
||||
if party and party_type:
|
||||
self.validate_party_contact(party, party_type)
|
||||
self.validate_party_contact(party, party_type)
|
||||
|
||||
def validate_party_address(self, party, party_type, billing_address, shipping_address=None):
|
||||
if billing_address or shipping_address:
|
||||
|
||||
@@ -545,7 +545,11 @@ class SubcontractingController(StockController):
|
||||
def __get_batch_nos_for_bundle(self, qty, key):
|
||||
available_batches = defaultdict(float)
|
||||
|
||||
precision = frappe.get_precision("Subcontracting Receipt Supplied Item", "consumed_qty")
|
||||
for batch_no, batch_qty in self.available_materials[key]["batch_no"].items():
|
||||
if flt(batch_qty, precision) <= 0:
|
||||
continue
|
||||
|
||||
qty_to_consumed = 0
|
||||
if qty > 0:
|
||||
if batch_qty >= qty:
|
||||
|
||||
@@ -2602,6 +2602,109 @@ class TestWorkOrder(FrappeTestCase):
|
||||
status = frappe.db.get_value("Serial No", row, "status")
|
||||
self.assertEqual(status, "Consumed")
|
||||
|
||||
def test_work_order_valuation_auto_pick(self):
|
||||
fg_item = "Test FG Item For Non Transfer Item Batch"
|
||||
rm_item = "Test RM Item For Non Transfer Item Batch"
|
||||
|
||||
make_item(fg_item, {"is_stock_item": 1})
|
||||
make_item(
|
||||
rm_item,
|
||||
{
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "TST-BATCH-NTI-.###",
|
||||
},
|
||||
)
|
||||
|
||||
source_warehouse = "_Test Warehouse - _TC"
|
||||
wip_warehouse = "Stores - _TC"
|
||||
finished_goods_warehouse = create_warehouse("_Test Finished Goods Warehouse", company="_Test Company")
|
||||
|
||||
batches = make_stock_in_entries_and_get_batches(rm_item, source_warehouse, wip_warehouse)
|
||||
|
||||
if not frappe.db.get_value("BOM", {"item": fg_item}):
|
||||
make_bom(item=fg_item, raw_materials=[rm_item])
|
||||
|
||||
wo = make_wo_order_test_record(
|
||||
item=fg_item,
|
||||
qty=5,
|
||||
source_warehouse=source_warehouse,
|
||||
wip_warehouse=wip_warehouse,
|
||||
fg_warehouse=finished_goods_warehouse,
|
||||
)
|
||||
|
||||
stock_entry = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 5))
|
||||
stock_entry.items[0].batch_no = batches[1]
|
||||
stock_entry.items[0].use_serial_batch_fields = 1
|
||||
stock_entry.submit()
|
||||
stock_entry.reload()
|
||||
|
||||
self.assertEqual(stock_entry.items[0].valuation_rate, 200)
|
||||
|
||||
original_value = frappe.db.get_single_value(
|
||||
"Stock Settings", "auto_create_serial_and_batch_bundle_for_outward"
|
||||
)
|
||||
original_based_on = frappe.db.get_single_value("Stock Settings", "pick_serial_and_batch_based_on")
|
||||
|
||||
frappe.db.set_single_value("Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 1)
|
||||
frappe.db.set_single_value("Stock Settings", "pick_serial_and_batch_based_on", "Expiry")
|
||||
|
||||
stock_entry = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 5))
|
||||
stock_entry.items[0].use_serial_batch_fields = 1
|
||||
stock_entry.submit()
|
||||
stock_entry.reload()
|
||||
|
||||
batch_no = get_batch_from_bundle(stock_entry.items[0].serial_and_batch_bundle)
|
||||
self.assertEqual(batch_no, batches[1])
|
||||
self.assertEqual(stock_entry.items[0].valuation_rate, 200)
|
||||
self.assertEqual(stock_entry.items[1].valuation_rate, 200)
|
||||
|
||||
frappe.db.set_single_value(
|
||||
"Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", original_value
|
||||
)
|
||||
frappe.db.set_single_value("Stock Settings", "pick_serial_and_batch_based_on", original_based_on)
|
||||
|
||||
|
||||
def make_stock_in_entries_and_get_batches(rm_item, source_warehouse, wip_warehouse):
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import (
|
||||
make_stock_entry as make_stock_entry_test_record,
|
||||
)
|
||||
|
||||
batches = []
|
||||
for qty, rate in ((5, 100), (5, 200)):
|
||||
stock_entry = make_stock_entry_test_record(
|
||||
item_code=rm_item,
|
||||
target=source_warehouse,
|
||||
qty=qty,
|
||||
basic_rate=rate,
|
||||
)
|
||||
stock_entry.submit()
|
||||
stock_entry.reload()
|
||||
|
||||
batch_no = get_batch_from_bundle(stock_entry.items[0].serial_and_batch_bundle)
|
||||
batch_doc = frappe.get_doc("Batch", batch_no)
|
||||
|
||||
# keep early expiry date for the batch having rate 200
|
||||
days = 10 if rate == 100 else 1
|
||||
batch_doc.db_set("expiry_date", add_to_date(now(), days=days))
|
||||
|
||||
batches.append(batch_no)
|
||||
|
||||
stock_entry = make_stock_entry_test_record(
|
||||
item_code=rm_item,
|
||||
target=wip_warehouse,
|
||||
qty=qty,
|
||||
basic_rate=rate,
|
||||
)
|
||||
stock_entry.submit()
|
||||
stock_entry.reload()
|
||||
batch_no = get_batch_from_bundle(stock_entry.items[0].serial_and_batch_bundle)
|
||||
batch_doc = frappe.get_doc("Batch", batch_no)
|
||||
batch_doc.db_set("expiry_date", add_to_date(now(), days=10))
|
||||
|
||||
return batches
|
||||
|
||||
|
||||
def make_operation(**kwargs):
|
||||
kwargs = frappe._dict(kwargs)
|
||||
|
||||
@@ -335,7 +335,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
let d = locals[cdt][cdn];
|
||||
return {
|
||||
filters: {
|
||||
docstatus: ("<", 2),
|
||||
docstatus: ["<", 2],
|
||||
inspection_type: inspection_type,
|
||||
reference_name: doc.name,
|
||||
item_code: d.item_code
|
||||
|
||||
@@ -252,7 +252,6 @@ class DeprecatedBatchNoValuation:
|
||||
from erpnext.stock.utils import get_combine_datetime
|
||||
|
||||
sle = frappe.qb.DocType("Stock Ledger Entry")
|
||||
batch = frappe.qb.DocType("Batch")
|
||||
|
||||
posting_datetime = get_combine_datetime(self.sle.posting_date, self.sle.posting_time)
|
||||
if not self.sle.creation:
|
||||
@@ -267,8 +266,6 @@ class DeprecatedBatchNoValuation:
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(sle)
|
||||
.inner_join(batch)
|
||||
.on(sle.batch_no == batch.name)
|
||||
.select(
|
||||
sle.stock_value,
|
||||
sle.qty_after_transaction,
|
||||
@@ -276,7 +273,6 @@ class DeprecatedBatchNoValuation:
|
||||
.where(
|
||||
(sle.item_code == self.sle.item_code)
|
||||
& (sle.warehouse == self.sle.warehouse)
|
||||
& (sle.batch_no.isnotnull())
|
||||
& (sle.is_cancelled == 0)
|
||||
)
|
||||
.where(timestamp_condition)
|
||||
|
||||
@@ -1026,10 +1026,6 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle
|
||||
};
|
||||
});
|
||||
|
||||
if (me.frm.doc.company && erpnext.is_perpetual_inventory_enabled(me.frm.doc.company)) {
|
||||
this.frm.add_fetch("company", "stock_adjustment_account", "expense_account");
|
||||
}
|
||||
|
||||
this.frm.fields_dict.items.grid.get_field("expense_account").get_query = function () {
|
||||
if (erpnext.is_perpetual_inventory_enabled(me.frm.doc.company)) {
|
||||
return {
|
||||
@@ -1143,8 +1139,6 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle
|
||||
this.frm.trigger("toggle_display_account_head");
|
||||
|
||||
erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
|
||||
if (this.frm.doc.company && erpnext.is_perpetual_inventory_enabled(this.frm.doc.company))
|
||||
this.set_default_account("stock_adjustment_account", "expense_account");
|
||||
this.set_default_account("cost_center", "cost_center");
|
||||
|
||||
this.frm.refresh_fields("items");
|
||||
|
||||
@@ -1672,7 +1672,7 @@ class StockEntry(StockController):
|
||||
if self.purpose == "Material Issue":
|
||||
ret["expense_account"] = item.get("expense_account") or item_group_defaults.get("expense_account")
|
||||
|
||||
if self.purpose == "Manufacture":
|
||||
if self.purpose == "Manufacture" or not ret.get("expense_account"):
|
||||
ret["expense_account"] = frappe.get_cached_value(
|
||||
"Company", self.company, "stock_adjustment_account"
|
||||
)
|
||||
|
||||
@@ -250,6 +250,7 @@
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.uom != doc.stock_uom",
|
||||
"fetch_from": "item_code.stock_uom",
|
||||
"fieldname": "stock_uom",
|
||||
"fieldtype": "Link",
|
||||
"label": "Stock UOM",
|
||||
@@ -588,7 +589,8 @@
|
||||
"label": "Serial and Batch Bundle",
|
||||
"no_copy": 1,
|
||||
"options": "Serial and Batch Bundle",
|
||||
"print_hide": 1
|
||||
"print_hide": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@@ -606,11 +608,12 @@
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-02-25 15:58:40.982582",
|
||||
"modified": "2025-03-26 21:01:58.544797",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Entry Detail",
|
||||
@@ -620,4 +623,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"states": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1212,9 +1212,21 @@ class update_entries_after:
|
||||
frappe.db.set_value("Stock Entry Detail", sle.voucher_detail_no, "basic_rate", outgoing_rate)
|
||||
|
||||
# Update outgoing item's rate, recalculate FG Item's rate and total incoming/outgoing amount
|
||||
if not sle.dependant_sle_voucher_detail_no:
|
||||
if not sle.dependant_sle_voucher_detail_no or self.is_manufacture_entry_with_sabb(sle):
|
||||
self.recalculate_amounts_in_stock_entry(sle.voucher_no, sle.voucher_detail_no)
|
||||
|
||||
def is_manufacture_entry_with_sabb(self, sle):
|
||||
if (
|
||||
self.args.get("sle_id")
|
||||
and sle.serial_and_batch_bundle
|
||||
and sle.auto_created_serial_and_batch_bundle
|
||||
):
|
||||
purpose = frappe.get_cached_value("Stock Entry", sle.voucher_no, "purpose")
|
||||
if purpose in ["Manufacture", "Repack"]:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def recalculate_amounts_in_stock_entry(self, voucher_no, voucher_detail_no):
|
||||
stock_entry = frappe.get_doc("Stock Entry", voucher_no, for_update=True)
|
||||
stock_entry.calculate_rate_and_amount(reset_outgoing_rate=False, raise_error_if_no_rate=False)
|
||||
|
||||
Reference in New Issue
Block a user