mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-26 00:14:50 +00:00
Merge pull request #31880 from frappe/version-13-hotfix
chore: weekly version-13 release
This commit is contained in:
@@ -208,7 +208,7 @@ def set_address_details(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if company_address:
|
if company_address:
|
||||||
party_details.update({"company_address": company_address})
|
party_details.company_address = company_address
|
||||||
else:
|
else:
|
||||||
party_details.update(get_company_address(company))
|
party_details.update(get_company_address(company))
|
||||||
|
|
||||||
@@ -220,12 +220,37 @@ def set_address_details(
|
|||||||
get_regional_address_details(party_details, doctype, company)
|
get_regional_address_details(party_details, doctype, company)
|
||||||
|
|
||||||
elif doctype and doctype in ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]:
|
elif doctype and doctype in ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]:
|
||||||
if party_details.company_address:
|
if shipping_address:
|
||||||
party_details["shipping_address"] = shipping_address or party_details["company_address"]
|
|
||||||
party_details.shipping_address_display = get_address_display(party_details["shipping_address"])
|
|
||||||
party_details.update(
|
party_details.update(
|
||||||
get_fetch_values(doctype, "shipping_address", party_details.shipping_address)
|
{
|
||||||
|
"shipping_address": shipping_address,
|
||||||
|
"shipping_address_display": get_address_display(shipping_address),
|
||||||
|
**get_fetch_values(doctype, "shipping_address", shipping_address),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if party_details.company_address:
|
||||||
|
# billing address
|
||||||
|
party_details.update(
|
||||||
|
{
|
||||||
|
"billing_address": party_details.company_address,
|
||||||
|
"billing_address_display": (
|
||||||
|
party_details.company_address_display or get_address_display(party_details.company_address)
|
||||||
|
),
|
||||||
|
**get_fetch_values(doctype, "billing_address", party_details.company_address),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# shipping address - if not already set
|
||||||
|
if not party_details.shipping_address:
|
||||||
|
party_details.update(
|
||||||
|
{
|
||||||
|
"shipping_address": party_details.billing_address,
|
||||||
|
"shipping_address_display": party_details.billing_address_display,
|
||||||
|
**get_fetch_values(doctype, "shipping_address", party_details.billing_address),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
get_regional_address_details(party_details, doctype, company)
|
get_regional_address_details(party_details, doctype, company)
|
||||||
|
|
||||||
return party_details.get(billing_address_field), party_details.shipping_address_name
|
return party_details.get(billing_address_field), party_details.shipping_address_name
|
||||||
|
|||||||
@@ -15,9 +15,12 @@ frappe.ui.form.on("Request for Quotation",{
|
|||||||
frm.fields_dict["suppliers"].grid.get_field("contact").get_query = function(doc, cdt, cdn) {
|
frm.fields_dict["suppliers"].grid.get_field("contact").get_query = function(doc, cdt, cdn) {
|
||||||
let d = locals[cdt][cdn];
|
let d = locals[cdt][cdn];
|
||||||
return {
|
return {
|
||||||
query: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_supplier_contacts",
|
query: "frappe.contacts.doctype.contact.contact.contact_query",
|
||||||
filters: {'supplier': d.supplier}
|
filters: {
|
||||||
}
|
link_doctype: "Supplier",
|
||||||
|
link_name: d.supplier || ""
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -287,18 +287,6 @@ def get_list_context(context=None):
|
|||||||
return list_context
|
return list_context
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
|
||||||
def get_supplier_contacts(doctype, txt, searchfield, start, page_len, filters):
|
|
||||||
return frappe.db.sql(
|
|
||||||
"""select `tabContact`.name from `tabContact`, `tabDynamic Link`
|
|
||||||
where `tabDynamic Link`.link_doctype = 'Supplier' and (`tabDynamic Link`.link_name=%(name)s
|
|
||||||
and `tabDynamic Link`.link_name like %(txt)s) and `tabContact`.name = `tabDynamic Link`.parent
|
|
||||||
limit %(start)s, %(page_len)s""",
|
|
||||||
{"start": start, "page_len": page_len, "txt": "%%%s%%" % txt, "name": filters.get("supplier")},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_supplier_quotation_from_rfq(source_name, target_doc=None, for_supplier=None):
|
def make_supplier_quotation_from_rfq(source_name, target_doc=None, for_supplier=None):
|
||||||
def postprocess(source, target_doc):
|
def postprocess(source, target_doc):
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ class BuyingController(StockController, Subcontracting):
|
|||||||
company=self.company,
|
company=self.company,
|
||||||
party_address=self.get("supplier_address"),
|
party_address=self.get("supplier_address"),
|
||||||
shipping_address=self.get("shipping_address"),
|
shipping_address=self.get("shipping_address"),
|
||||||
|
company_address=self.get("billing_address"),
|
||||||
fetch_payment_terms_template=not self.get("ignore_default_payment_terms_template"),
|
fetch_payment_terms_template=not self.get("ignore_default_payment_terms_template"),
|
||||||
ignore_permissions=self.flags.ignore_permissions,
|
ignore_permissions=self.flags.ignore_permissions,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BatchExpiredError(frappe.ValidationError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class StockController(AccountsController):
|
class StockController(AccountsController):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
super(StockController, self).validate()
|
super(StockController, self).validate()
|
||||||
@@ -74,6 +78,10 @@ class StockController(AccountsController):
|
|||||||
def validate_serialized_batch(self):
|
def validate_serialized_batch(self):
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
|
|
||||||
|
is_material_issue = False
|
||||||
|
if self.doctype == "Stock Entry" and self.purpose == "Material Issue":
|
||||||
|
is_material_issue = True
|
||||||
|
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
if hasattr(d, "serial_no") and hasattr(d, "batch_no") and d.serial_no and d.batch_no:
|
if hasattr(d, "serial_no") and hasattr(d, "batch_no") and d.serial_no and d.batch_no:
|
||||||
serial_nos = frappe.get_all(
|
serial_nos = frappe.get_all(
|
||||||
@@ -90,6 +98,9 @@ class StockController(AccountsController):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if is_material_issue:
|
||||||
|
continue
|
||||||
|
|
||||||
if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2:
|
if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2:
|
||||||
expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date")
|
expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date")
|
||||||
|
|
||||||
@@ -97,7 +108,8 @@ class StockController(AccountsController):
|
|||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Row #{0}: The batch {1} has already expired.").format(
|
_("Row #{0}: The batch {1} has already expired.").format(
|
||||||
d.idx, get_link_to_form("Batch", d.get("batch_no"))
|
d.idx, get_link_to_form("Batch", d.get("batch_no"))
|
||||||
)
|
),
|
||||||
|
BatchExpiredError,
|
||||||
)
|
)
|
||||||
|
|
||||||
def clean_serial_nos(self):
|
def clean_serial_nos(self):
|
||||||
|
|||||||
@@ -189,8 +189,8 @@ class BOM(WebsiteGenerator):
|
|||||||
self.validate_transfer_against()
|
self.validate_transfer_against()
|
||||||
self.set_routing_operations()
|
self.set_routing_operations()
|
||||||
self.validate_operations()
|
self.validate_operations()
|
||||||
self.update_exploded_items(save=False)
|
|
||||||
self.calculate_cost()
|
self.calculate_cost()
|
||||||
|
self.update_exploded_items(save=False)
|
||||||
self.update_stock_qty()
|
self.update_stock_qty()
|
||||||
self.validate_scrap_items()
|
self.validate_scrap_items()
|
||||||
self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate=False, save=False)
|
self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate=False, save=False)
|
||||||
|
|||||||
@@ -555,6 +555,34 @@ class TestBOM(FrappeTestCase):
|
|||||||
bom.reload()
|
bom.reload()
|
||||||
self.assertEqual(frappe.get_value("Item", fg_item.item_code, "default_bom"), bom.name)
|
self.assertEqual(frappe.get_value("Item", fg_item.item_code, "default_bom"), bom.name)
|
||||||
|
|
||||||
|
def test_exploded_items_rate(self):
|
||||||
|
rm_item = make_item(
|
||||||
|
properties={"is_stock_item": 1, "valuation_rate": 99, "last_purchase_rate": 89}
|
||||||
|
).name
|
||||||
|
fg_item = make_item(properties={"is_stock_item": 1}).name
|
||||||
|
|
||||||
|
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
||||||
|
|
||||||
|
bom = make_bom(item=fg_item, raw_materials=[rm_item], do_not_save=True)
|
||||||
|
|
||||||
|
bom.rm_cost_as_per = "Last Purchase Rate"
|
||||||
|
bom.save()
|
||||||
|
self.assertEqual(bom.items[0].base_rate, 89)
|
||||||
|
self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
|
||||||
|
|
||||||
|
bom.rm_cost_as_per = "Price List"
|
||||||
|
bom.save()
|
||||||
|
self.assertEqual(bom.items[0].base_rate, 0.0)
|
||||||
|
self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
|
||||||
|
|
||||||
|
bom.rm_cost_as_per = "Valuation Rate"
|
||||||
|
bom.save()
|
||||||
|
self.assertEqual(bom.items[0].base_rate, 99)
|
||||||
|
self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
|
||||||
|
|
||||||
|
bom.submit()
|
||||||
|
self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
|
||||||
|
|
||||||
|
|
||||||
def get_default_bom(item_code="_Test FG Item 2"):
|
def get_default_bom(item_code="_Test FG Item 2"):
|
||||||
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
|
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
|
||||||
|
|||||||
@@ -185,6 +185,7 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Rate",
|
"label": "Rate",
|
||||||
"options": "currency",
|
"options": "currency",
|
||||||
|
"read_only": 1,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -298,7 +299,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-01-24 16:57:57.020232",
|
"modified": "2022-07-28 10:20:51.559010",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "BOM Item",
|
"name": "BOM Item",
|
||||||
|
|||||||
@@ -482,7 +482,6 @@ class ProductionPlan(Document):
|
|||||||
"bom_no",
|
"bom_no",
|
||||||
"stock_uom",
|
"stock_uom",
|
||||||
"bom_level",
|
"bom_level",
|
||||||
"production_plan_item",
|
|
||||||
"schedule_date",
|
"schedule_date",
|
||||||
]:
|
]:
|
||||||
if row.get(field):
|
if row.get(field):
|
||||||
@@ -639,6 +638,9 @@ class ProductionPlan(Document):
|
|||||||
def get_sub_assembly_items(self, manufacturing_type=None):
|
def get_sub_assembly_items(self, manufacturing_type=None):
|
||||||
self.sub_assembly_items = []
|
self.sub_assembly_items = []
|
||||||
for row in self.po_items:
|
for row in self.po_items:
|
||||||
|
if not row.item_code:
|
||||||
|
frappe.throw(_("Row #{0}: Please select Item Code in Assembly Items").format(row.idx))
|
||||||
|
|
||||||
bom_data = []
|
bom_data = []
|
||||||
get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty)
|
get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty)
|
||||||
self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
|
self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ from erpnext.manufacturing.doctype.production_plan.production_plan import (
|
|||||||
get_warehouse_list,
|
get_warehouse_list,
|
||||||
)
|
)
|
||||||
from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError
|
from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError
|
||||||
|
from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry as make_se_from_wo
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||||
from erpnext.stock.doctype.item.test_item import create_item
|
from erpnext.stock.doctype.item.test_item import create_item, make_item
|
||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
||||||
create_stock_reconciliation,
|
create_stock_reconciliation,
|
||||||
@@ -536,9 +537,6 @@ class TestProductionPlan(FrappeTestCase):
|
|||||||
Test Prod Plan impact via: SO -> Prod Plan -> WO -> SE -> SE (cancel)
|
Test Prod Plan impact via: SO -> Prod Plan -> WO -> SE -> SE (cancel)
|
||||||
"""
|
"""
|
||||||
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
|
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
|
||||||
from erpnext.manufacturing.doctype.work_order.work_order import (
|
|
||||||
make_stock_entry as make_se_from_wo,
|
|
||||||
)
|
|
||||||
|
|
||||||
make_stock_entry(
|
make_stock_entry(
|
||||||
item_code="Raw Material Item 1", target="Work In Progress - _TC", qty=2, basic_rate=100
|
item_code="Raw Material Item 1", target="Work In Progress - _TC", qty=2, basic_rate=100
|
||||||
@@ -581,9 +579,6 @@ class TestProductionPlan(FrappeTestCase):
|
|||||||
def test_production_plan_pending_qty_independent_items(self):
|
def test_production_plan_pending_qty_independent_items(self):
|
||||||
"Test Prod Plan impact if items are added independently (no from SO or MR)."
|
"Test Prod Plan impact if items are added independently (no from SO or MR)."
|
||||||
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
|
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
|
||||||
from erpnext.manufacturing.doctype.work_order.work_order import (
|
|
||||||
make_stock_entry as make_se_from_wo,
|
|
||||||
)
|
|
||||||
|
|
||||||
make_stock_entry(
|
make_stock_entry(
|
||||||
item_code="Raw Material Item 1", target="Work In Progress - _TC", qty=2, basic_rate=100
|
item_code="Raw Material Item 1", target="Work In Progress - _TC", qty=2, basic_rate=100
|
||||||
@@ -679,6 +674,57 @@ class TestProductionPlan(FrappeTestCase):
|
|||||||
for po_item, subassy_item in zip(pp.po_items, pp.sub_assembly_items):
|
for po_item, subassy_item in zip(pp.po_items, pp.sub_assembly_items):
|
||||||
self.assertEqual(po_item.name, subassy_item.production_plan_item)
|
self.assertEqual(po_item.name, subassy_item.production_plan_item)
|
||||||
|
|
||||||
|
def test_produced_qty_for_multi_level_bom_item(self):
|
||||||
|
# Create Items and BOMs
|
||||||
|
rm_item = make_item(properties={"is_stock_item": 1}).name
|
||||||
|
sub_assembly_item = make_item(properties={"is_stock_item": 1}).name
|
||||||
|
fg_item = make_item(properties={"is_stock_item": 1}).name
|
||||||
|
|
||||||
|
make_stock_entry(
|
||||||
|
item_code=rm_item,
|
||||||
|
qty=60,
|
||||||
|
to_warehouse="Work In Progress - _TC",
|
||||||
|
rate=99,
|
||||||
|
purpose="Material Receipt",
|
||||||
|
)
|
||||||
|
|
||||||
|
make_bom(item=sub_assembly_item, raw_materials=[rm_item], rm_qty=3)
|
||||||
|
make_bom(item=fg_item, raw_materials=[sub_assembly_item], rm_qty=4)
|
||||||
|
|
||||||
|
# Step - 1: Create Production Plan
|
||||||
|
pln = create_production_plan(item_code=fg_item, planned_qty=5, skip_getting_mr_items=1)
|
||||||
|
pln.get_sub_assembly_items()
|
||||||
|
|
||||||
|
# Step - 2: Create Work Orders
|
||||||
|
pln.make_work_order()
|
||||||
|
work_orders = frappe.get_all("Work Order", filters={"production_plan": pln.name}, pluck="name")
|
||||||
|
sa_wo = fg_wo = None
|
||||||
|
for work_order in work_orders:
|
||||||
|
wo_doc = frappe.get_doc("Work Order", work_order)
|
||||||
|
if wo_doc.production_plan_item:
|
||||||
|
wo_doc.update(
|
||||||
|
{"wip_warehouse": "Work In Progress - _TC", "fg_warehouse": "Finished Goods - _TC"}
|
||||||
|
)
|
||||||
|
fg_wo = wo_doc.name
|
||||||
|
else:
|
||||||
|
wo_doc.update(
|
||||||
|
{"wip_warehouse": "Work In Progress - _TC", "fg_warehouse": "Work In Progress - _TC"}
|
||||||
|
)
|
||||||
|
sa_wo = wo_doc.name
|
||||||
|
wo_doc.submit()
|
||||||
|
|
||||||
|
# Step - 3: Complete Work Orders
|
||||||
|
se = frappe.get_doc(make_se_from_wo(sa_wo, "Manufacture"))
|
||||||
|
se.submit()
|
||||||
|
|
||||||
|
se = frappe.get_doc(make_se_from_wo(fg_wo, "Manufacture"))
|
||||||
|
se.submit()
|
||||||
|
|
||||||
|
# Step - 4: Check Production Plan Item Produced Qty
|
||||||
|
pln.load_from_db()
|
||||||
|
self.assertEqual(pln.status, "Completed")
|
||||||
|
self.assertEqual(pln.po_items[0].produced_qty, 5)
|
||||||
|
|
||||||
|
|
||||||
def create_production_plan(**args):
|
def create_production_plan(**args):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
frappe.provide('erpnext.accounts.dimensions');
|
|
||||||
|
|
||||||
erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||||
setup: function() {
|
setup: function() {
|
||||||
@@ -910,24 +909,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
set_party_account(set_pricing);
|
set_party_account(set_pricing);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get default company billing address in Purchase Invoice, Order and Receipt
|
|
||||||
if (this.frm.doc.company && frappe.meta.get_docfield(this.frm.doctype, "billing_address")) {
|
|
||||||
frappe.call({
|
|
||||||
method: "erpnext.setup.doctype.company.company.get_default_company_address",
|
|
||||||
args: {name: this.frm.doc.company, existing_address: this.frm.doc.billing_address || ""},
|
|
||||||
debounce: 2000,
|
|
||||||
callback: function(r) {
|
|
||||||
if (r.message) {
|
|
||||||
me.frm.set_value("billing_address", r.message);
|
|
||||||
} else {
|
|
||||||
if (frappe.meta.get_docfield(me.frm.doctype, 'company_address')) {
|
|
||||||
me.frm.set_value("company_address", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
set_party_account(set_pricing);
|
set_party_account(set_pricing);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,25 +3,14 @@
|
|||||||
|
|
||||||
frappe.provide("erpnext.utils");
|
frappe.provide("erpnext.utils");
|
||||||
|
|
||||||
|
const SALES_DOCTYPES = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice'];
|
||||||
|
const PURCHASE_DOCTYPES = ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'];
|
||||||
|
|
||||||
erpnext.utils.get_party_details = function(frm, method, args, callback) {
|
erpnext.utils.get_party_details = function(frm, method, args, callback) {
|
||||||
if (!method) {
|
if (!method) {
|
||||||
method = "erpnext.accounts.party.get_party_details";
|
method = "erpnext.accounts.party.get_party_details";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args) {
|
|
||||||
if (in_list(['Sales Invoice', 'Sales Order', 'Delivery Note'], frm.doc.doctype)) {
|
|
||||||
if (frm.doc.company_address && (!args.company_address)) {
|
|
||||||
args.company_address = frm.doc.company_address;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_list(['Purchase Invoice', 'Purchase Order', 'Purchase Receipt'], frm.doc.doctype)) {
|
|
||||||
if (frm.doc.shipping_address && (!args.shipping_address)) {
|
|
||||||
args.shipping_address = frm.doc.shipping_address;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!args) {
|
if (!args) {
|
||||||
if ((frm.doctype != "Purchase Order" && frm.doc.customer)
|
if ((frm.doctype != "Purchase Order" && frm.doc.customer)
|
||||||
|| (frm.doc.party_name && in_list(['Quotation', 'Opportunity'], frm.doc.doctype))) {
|
|| (frm.doc.party_name && in_list(['Quotation', 'Opportunity'], frm.doc.doctype))) {
|
||||||
@@ -45,41 +34,44 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_list(['Sales Invoice', 'Sales Order', 'Delivery Note'], frm.doc.doctype)) {
|
if (!args) {
|
||||||
if (!args) {
|
if (in_list(SALES_DOCTYPES, frm.doc.doctype)) {
|
||||||
args = {
|
args = {
|
||||||
party: frm.doc.customer || frm.doc.party_name,
|
party: frm.doc.customer || frm.doc.party_name,
|
||||||
party_type: 'Customer'
|
party_type: 'Customer'
|
||||||
}
|
};
|
||||||
}
|
|
||||||
if (frm.doc.company_address && (!args.company_address)) {
|
|
||||||
args.company_address = frm.doc.company_address;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.shipping_address_name &&(!args.shipping_address_name)) {
|
if (in_list(PURCHASE_DOCTYPES, frm.doc.doctype)) {
|
||||||
args.shipping_address_name = frm.doc.shipping_address_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_list(['Purchase Invoice', 'Purchase Order', 'Purchase Receipt'], frm.doc.doctype)) {
|
|
||||||
if (!args) {
|
|
||||||
args = {
|
args = {
|
||||||
party: frm.doc.supplier,
|
party: frm.doc.supplier,
|
||||||
party_type: 'Supplier'
|
party_type: 'Supplier'
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
if (frm.doc.shipping_address && (!args.shipping_address)) {
|
|
||||||
args.shipping_address = frm.doc.shipping_address;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args) {
|
if (!args || !args.party) return;
|
||||||
args.posting_date = frm.doc.posting_date || frm.doc.transaction_date;
|
|
||||||
args.fetch_payment_terms_template = cint(!frm.doc.ignore_default_payment_terms_template);
|
args.posting_date = frm.doc.posting_date || frm.doc.transaction_date;
|
||||||
|
args.fetch_payment_terms_template = cint(!frm.doc.ignore_default_payment_terms_template);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_list(SALES_DOCTYPES, frm.doc.doctype)) {
|
||||||
|
if (!args.company_address && frm.doc.company_address) {
|
||||||
|
args.company_address = frm.doc.company_address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!args || !args.party) return;
|
|
||||||
|
if (in_list(PURCHASE_DOCTYPES, frm.doc.doctype)) {
|
||||||
|
if (!args.company_address && frm.doc.billing_address) {
|
||||||
|
args.company_address = frm.doc.billing_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!args.shipping_address && frm.doc.shipping_address) {
|
||||||
|
args.shipping_address = frm.doc.shipping_address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (frappe.meta.get_docfield(frm.doc.doctype, "taxes")) {
|
if (frappe.meta.get_docfield(frm.doc.doctype, "taxes")) {
|
||||||
if (!erpnext.utils.validate_mandatory(frm, "Posting / Transaction Date",
|
if (!erpnext.utils.validate_mandatory(frm, "Posting / Transaction Date",
|
||||||
|
|||||||
@@ -366,8 +366,14 @@ def make_new_batch(**args):
|
|||||||
"doctype": "Batch",
|
"doctype": "Batch",
|
||||||
"batch_id": args.batch_id,
|
"batch_id": args.batch_id,
|
||||||
"item": args.item_code,
|
"item": args.item_code,
|
||||||
|
"expiry_date": args.expiry_date,
|
||||||
}
|
}
|
||||||
).insert()
|
)
|
||||||
|
|
||||||
|
if args.expiry_date:
|
||||||
|
batch.expiry_date = args.expiry_date
|
||||||
|
|
||||||
|
batch.insert()
|
||||||
|
|
||||||
except frappe.DuplicateEntryError:
|
except frappe.DuplicateEntryError:
|
||||||
batch = frappe.get_doc("Batch", args.batch_id)
|
batch = frappe.get_doc("Batch", args.batch_id)
|
||||||
|
|||||||
@@ -786,10 +786,8 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "expense_account",
|
"fieldname": "expense_account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 1,
|
|
||||||
"label": "Expense Account",
|
"label": "Expense Account",
|
||||||
"options": "Account",
|
"options": "Account"
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "accounting_dimensions_section",
|
"fieldname": "accounting_dimensions_section",
|
||||||
@@ -994,7 +992,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-04-11 13:07:32.061402",
|
"modified": "2022-07-28 19:27:54.880781",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Purchase Receipt Item",
|
"name": "Purchase Receipt Item",
|
||||||
|
|||||||
@@ -581,18 +581,23 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
add_to_transit: function(frm) {
|
add_to_transit: function(frm) {
|
||||||
if(frm.doc.add_to_transit && frm.doc.purpose=='Material Transfer') {
|
if(frm.doc.purpose=='Material Transfer') {
|
||||||
frm.set_value('to_warehouse', '');
|
var filters = {
|
||||||
|
'is_group': 0,
|
||||||
|
'company': frm.doc.company
|
||||||
|
}
|
||||||
|
|
||||||
|
if(frm.doc.add_to_transit){
|
||||||
|
filters['warehouse_type'] = 'Transit';
|
||||||
|
frm.set_value('to_warehouse', '');
|
||||||
|
frm.trigger('set_transit_warehouse');
|
||||||
|
}
|
||||||
|
|
||||||
frm.fields_dict.to_warehouse.get_query = function() {
|
frm.fields_dict.to_warehouse.get_query = function() {
|
||||||
return {
|
return {
|
||||||
filters:{
|
filters:filters
|
||||||
'warehouse_type' : 'Transit',
|
|
||||||
'is_group': 0,
|
|
||||||
'company': frm.doc.company
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
frm.trigger('set_transit_warehouse');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.permissions import add_user_permission, remove_user_permission
|
from frappe.permissions import add_user_permission, remove_user_permission
|
||||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||||
from frappe.utils import add_days, flt, nowdate, nowtime
|
from frappe.utils import add_days, flt, nowdate, nowtime, today
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
|
|
||||||
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
||||||
@@ -1546,6 +1546,31 @@ class TestStockEntry(FrappeTestCase):
|
|||||||
self.assertEqual(obj.items[index].basic_rate, 200)
|
self.assertEqual(obj.items[index].basic_rate, 200)
|
||||||
self.assertEqual(obj.items[index].basic_amount, 2000)
|
self.assertEqual(obj.items[index].basic_amount, 2000)
|
||||||
|
|
||||||
|
def test_batch_expiry(self):
|
||||||
|
from erpnext.controllers.stock_controller import BatchExpiredError
|
||||||
|
from erpnext.stock.doctype.batch.test_batch import make_new_batch
|
||||||
|
|
||||||
|
item_code = "Test Batch Expiry Test Item - 001"
|
||||||
|
item_doc = create_item(item_code=item_code, is_stock_item=1, valuation_rate=10)
|
||||||
|
|
||||||
|
item_doc.has_batch_no = 1
|
||||||
|
item_doc.save()
|
||||||
|
|
||||||
|
batch = make_new_batch(
|
||||||
|
batch_id=frappe.generate_hash("", 5), item_code=item_doc.name, expiry_date=add_days(today(), -1)
|
||||||
|
)
|
||||||
|
|
||||||
|
se = make_stock_entry(
|
||||||
|
item_code=item_code,
|
||||||
|
purpose="Material Receipt",
|
||||||
|
qty=4,
|
||||||
|
to_warehouse="_Test Warehouse - _TC",
|
||||||
|
batch_no=batch.name,
|
||||||
|
do_not_save=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRaises(BatchExpiredError, se.save)
|
||||||
|
|
||||||
|
|
||||||
def make_serialized_item(**args):
|
def make_serialized_item(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|||||||
Reference in New Issue
Block a user