Merge pull request #26415 from GangaManoj/backport-asset-repair-refactor
refactor: Asset Repair
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 4e93fc6..0ba8507 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -717,9 +717,8 @@
"ignore_conversion_rate": True
})
item_doc = frappe.get_cached_doc("Item", args.get("item_code"))
- out = frappe._dict()
- get_price_list_rate(bom_args, item_doc, out)
- rate = out.price_list_rate
+ price_list_data = get_price_list_rate(bom_args, item_doc)
+ rate = price_list_data.price_list_rate
return rate
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 69c7f5c..66e2394 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -608,6 +608,11 @@
target.set_missing_values()
target.set_stock_entry_type()
+ wo_allows_alternate_item = frappe.db.get_value("Work Order", target.work_order, "allow_alternative_item")
+ for item in target.items:
+ item.allow_alternative_item = int(wo_allows_alternate_item and
+ frappe.get_cached_value("Item", item.item_code, "allow_alternative_item"))
+
doclist = get_mapped_doc("Job Card", source_name, {
"Job Card": {
"doctype": "Stock Entry",
@@ -698,4 +703,4 @@
}
}, target_doc, set_missing_values)
- return doclist
\ No newline at end of file
+ return doclist
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 6a024f2..8c27d6c 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -109,6 +109,15 @@
so_mr_list = [d.get(field) for d in self.get(table) if d.get(field)]
return so_mr_list
+ def get_bom_item(self):
+ """Check if Item or if its Template has a BOM."""
+ bom_item = None
+ has_bom = frappe.db.exists({'doctype': 'BOM', 'item': self.item_code, 'docstatus': 1})
+ if not has_bom:
+ template_item = frappe.db.get_value('Item', self.item_code, ['variant_of'])
+ bom_item = "bom.item = {0}".format(frappe.db.escape(template_item)) if template_item else bom_item
+ return bom_item
+
def get_so_items(self):
# Check for empty table or empty rows
if not self.get("sales_orders") or not self.get_so_mr_list("sales_order", "sales_orders"):
@@ -117,16 +126,26 @@
so_list = self.get_so_mr_list("sales_order", "sales_orders")
item_condition = ""
- if self.item_code:
+ bom_item = "bom.item = so_item.item_code"
+ if self.item_code and frappe.db.exists('Item', self.item_code):
+ bom_item = self.get_bom_item() or bom_item
item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code))
- items = frappe.db.sql("""select distinct parent, item_code, warehouse,
- (qty - work_order_qty) * conversion_factor as pending_qty, description, name
- from `tabSales Order Item` so_item
- where parent in (%s) and docstatus = 1 and qty > work_order_qty
- and exists (select name from `tabBOM` bom where bom.item=so_item.item_code
- and bom.is_active = 1) %s""" % \
- (", ".join(["%s"] * len(so_list)), item_condition), tuple(so_list), as_dict=1)
+ items = frappe.db.sql("""
+ select
+ distinct parent, item_code, warehouse,
+ (qty - work_order_qty) * conversion_factor as pending_qty,
+ description, name
+ from
+ `tabSales Order Item` so_item
+ where
+ parent in (%s) and docstatus = 1 and qty > work_order_qty
+ and exists (select name from `tabBOM` bom where %s
+ and bom.is_active = 1) %s""" %
+ (", ".join(["%s"] * len(so_list)),
+ bom_item,
+ item_condition),
+ tuple(so_list), as_dict=1)
if self.item_code:
item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code))
@@ -683,6 +702,7 @@
def get_sales_orders(self):
so_filter = item_filter = ""
+ bom_item = "bom.item = so_item.item_code"
if self.from_date:
so_filter += " and so.transaction_date >= %(from_date)s"
if self.to_date:
@@ -694,7 +714,8 @@
if self.sales_order_status:
so_filter += "and so.status = %(sales_order_status)s"
- if self.item_code:
+ if self.item_code and frappe.db.exists('Item', self.item_code):
+ bom_item = self.get_bom_item() or bom_item
item_filter += " and so_item.item_code = %(item)s"
open_so = frappe.db.sql("""
@@ -704,13 +725,13 @@
and so.docstatus = 1 and so.status not in ("Stopped", "Closed")
and so.company = %(company)s
and so_item.qty > so_item.work_order_qty {0} {1}
- and (exists (select name from `tabBOM` bom where bom.item=so_item.item_code
+ and (exists (select name from `tabBOM` bom where {2}
and bom.is_active = 1)
or exists (select name from `tabPacked Item` pi
where pi.parent = so.name and pi.parent_item = so_item.item_code
and exists (select name from `tabBOM` bom where bom.item=pi.item_code
and bom.is_active = 1)))
- """.format(so_filter, item_filter), {
+ """.format(so_filter, item_filter, bom_item), {
"from_date": self.from_date,
"to_date": self.to_date,
"customer": self.customer,
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 93e6d7a..af8de8e 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -11,6 +11,7 @@
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests, get_warehouse_list
+from erpnext.controllers.item_variant import create_variant
class TestProductionPlan(unittest.TestCase):
def setUp(self):
@@ -271,6 +272,60 @@
self.assertEqual(warehouses, expected_warehouses)
+ def test_get_sales_order_with_variant(self):
+ if not frappe.db.exists('Item', {"item_code": 'PIV'}):
+ item = create_item('PIV', valuation_rate = 100)
+ variant_settings = {
+ "attributes": [
+ {
+ "attribute": "Colour"
+ },
+ ],
+ "has_variants": 1
+ }
+ item.update(variant_settings)
+ item.save()
+ parent_bom = make_bom(item = 'PIV', raw_materials = ['PIV'])
+ if not frappe.db.exists('BOM', {"item": 'PIV'}):
+ parent_bom = make_bom(item = 'PIV', raw_materials = ['PIV'])
+ else:
+ parent_bom = frappe.get_doc('BOM', {"item": 'PIV'})
+
+ if not frappe.db.exists('Item', {"item_code": 'PIV-RED'}):
+ variant = create_variant("PIV", {"Colour": "Red"})
+ variant.save()
+ variant_bom = make_bom(item = variant.item_code, raw_materials = [variant.item_code])
+ else:
+ variant = frappe.get_doc('Item', 'PIV-RED')
+ if not frappe.db.exists('BOM', {"item": 'PIV-RED'}):
+ variant_bom = make_bom(item = variant.item_code, raw_materials = [variant.item_code])
+
+ """Testing when item variant has a BOM"""
+ so = make_sales_order(item_code="PIV-RED", qty=5)
+ pln = frappe.new_doc('Production Plan')
+ pln.company = so.company
+ pln.get_items_from = 'Sales Order'
+ pln.item_code = 'PIV-RED'
+ pln.get_open_sales_orders()
+ self.assertEqual(pln.sales_orders[0].sales_order, so.name)
+ pln.get_so_items()
+ self.assertEqual(pln.po_items[0].item_code, 'PIV-RED')
+ self.assertEqual(pln.po_items[0].bom_no, variant_bom.name)
+ so.cancel()
+ frappe.delete_doc('Sales Order', so.name)
+ variant_bom.cancel()
+ frappe.delete_doc('BOM', variant_bom.name)
+
+ """Testing when item variant doesn't have a BOM"""
+ so = make_sales_order(item_code="PIV-RED", qty=5)
+ pln.get_open_sales_orders()
+ self.assertEqual(pln.sales_orders[0].sales_order, so.name)
+ pln.po_items = []
+ pln.get_so_items()
+ self.assertEqual(pln.po_items[0].item_code, 'PIV-RED')
+ self.assertEqual(pln.po_items[0].bom_no, parent_bom.name)
+
+ frappe.db.rollback()
def create_production_plan(**args):
args = frappe._dict(args)
diff --git a/erpnext/regional/address_template/templates/france.html b/erpnext/regional/address_template/templates/france.html
new file mode 100644
index 0000000..752331e
--- /dev/null
+++ b/erpnext/regional/address_template/templates/france.html
@@ -0,0 +1,5 @@
+{% if address_line1 %}{{ address_line1 }}{% endif -%}
+{% if address_line2 %}<br>{{ address_line2 }}{% endif -%}
+{% if pincode %}<br>{{ pincode }}{% endif -%}
+{% if city %} {{ city }}{% endif -%}
+{% if country %}<br>{{ country }}{% endif -%}
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index cbd272d..a85a022 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -269,11 +269,14 @@
batch2 = create_batch('_Test Batch Price Item', 300, 1)
batch3 = create_batch('_Test Batch Price Item', 400, 0)
+ company = "_Test Company with perpetual inventory"
+ currency = frappe.get_cached_value("Company", company, "default_currency")
+
args = frappe._dict({
"item_code": "_Test Batch Price Item",
- "company": "_Test Company with perpetual inventory",
+ "company": company,
"price_list": "_Test Price List",
- "currency": "_Test Currency",
+ "currency": currency,
"doctype": "Sales Invoice",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
@@ -333,4 +336,4 @@
except frappe.DuplicateEntryError:
batch = frappe.get_doc("Batch", args.batch_id)
- return batch
\ No newline at end of file
+ return batch
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index c7467a5..9ec44d2 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -83,14 +83,17 @@
make_test_objects("Item Price")
+ company = "_Test Company"
+ currency = frappe.get_cached_value("Company", company, "default_currency")
+
details = get_item_details({
"item_code": "_Test Item",
- "company": "_Test Company",
+ "company": company,
"price_list": "_Test Price List",
- "currency": "_Test Currency",
+ "currency": currency,
"doctype": "Sales Order",
"conversion_rate": 1,
- "price_list_currency": "_Test Currency",
+ "price_list_currency": currency,
"plc_conversion_rate": 1,
"order_type": "Sales",
"customer": "_Test Customer",
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 2eb8bfd..ca6e61f 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -23,9 +23,7 @@
def test_reverse_purchase_receipt_sle(self):
- frappe.db.set_value('UOM', '_Test UOM', 'must_be_whole_number', 0)
-
- pr = make_purchase_receipt(qty=0.5)
+ pr = make_purchase_receipt(qty=0.5, item_code="_Test Item Home Desktop 200")
sl_entry = frappe.db.get_all("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
"voucher_no": pr.name}, ['actual_qty'])
@@ -41,8 +39,6 @@
self.assertEqual(len(sl_entry_cancelled), 2)
self.assertEqual(sl_entry_cancelled[1].actual_qty, -0.5)
- frappe.db.set_value('UOM', '_Test UOM', 'must_be_whole_number', 1)
-
def test_make_purchase_invoice(self):
if not frappe.db.exists('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice'):
frappe.get_doc({
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 2ed7a04..be8508a 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -74,8 +74,7 @@
update_party_blanket_order(args, out)
-
- get_price_list_rate(args, item, out)
+ out.update(get_price_list_rate(args, item))
if args.customer and cint(args.is_pos):
out.update(get_pos_profile_item_details(args.company, args, update_data=True))
@@ -638,7 +637,10 @@
or item_group.get("default_supplier")
or brand.get("default_supplier"))
-def get_price_list_rate(args, item_doc, out):
+def get_price_list_rate(args, item_doc, out=None):
+ if out is None:
+ out = frappe._dict()
+
meta = frappe.get_meta(args.parenttype or args.doctype)
if meta.get_field("currency") or args.get('currency'):
@@ -651,17 +653,17 @@
if meta.get_field("currency"):
validate_conversion_rate(args, meta)
- price_list_rate = get_price_list_rate_for(args, item_doc.name) or 0
+ price_list_rate = get_price_list_rate_for(args, item_doc.name)
# variant
- if not price_list_rate and item_doc.variant_of:
+ if price_list_rate is None and item_doc.variant_of:
price_list_rate = get_price_list_rate_for(args, item_doc.variant_of)
# insert in database
- if not price_list_rate:
+ if price_list_rate is None:
if args.price_list and args.rate:
insert_item_price(args)
- return {}
+ return out
out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) \
/ flt(args.conversion_rate)
@@ -671,6 +673,8 @@
out.update(get_last_purchase_details(item_doc.name,
args.name, args.conversion_rate))
+ return out
+
def insert_item_price(args):
"""Insert Item Price if Price List and Price List Rate are specified and currency is the same"""
if frappe.db.get_value("Price List", args.price_list, "currency", cache=True) == args.currency \
@@ -1073,9 +1077,8 @@
}
def apply_price_list_on_item(args):
- item_details = frappe._dict()
item_doc = frappe.get_doc("Item", args.item_code)
- get_price_list_rate(args, item_doc, item_details)
+ item_details = get_price_list_rate(args, item_doc)
item_details.update(get_pricing_rule_for_item(args, item_details.price_list_rate))