mirror of
https://github.com/frappe/erpnext.git
synced 2026-02-18 00:55:02 +00:00
Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-28269
This commit is contained in:
@@ -113,9 +113,15 @@ class POSInvoiceMergeLog(Document):
|
||||
|
||||
def merge_pos_invoice_into(self, invoice, data):
|
||||
items, payments, taxes = [], [], []
|
||||
|
||||
loyalty_amount_sum, loyalty_points_sum = 0, 0
|
||||
|
||||
rounding_adjustment, base_rounding_adjustment = 0, 0
|
||||
rounded_total, base_rounded_total = 0, 0
|
||||
|
||||
loyalty_amount_sum, loyalty_points_sum, idx = 0, 0, 1
|
||||
|
||||
|
||||
for doc in data:
|
||||
map_doc(doc, invoice, table_map={ "doctype": invoice.doctype })
|
||||
|
||||
@@ -149,6 +155,8 @@ class POSInvoiceMergeLog(Document):
|
||||
found = True
|
||||
if not found:
|
||||
tax.charge_type = 'Actual'
|
||||
tax.idx = idx
|
||||
idx += 1
|
||||
tax.included_in_print_rate = 0
|
||||
tax.tax_amount = tax.tax_amount_after_discount_amount
|
||||
tax.base_tax_amount = tax.base_tax_amount_after_discount_amount
|
||||
@@ -166,8 +174,8 @@ class POSInvoiceMergeLog(Document):
|
||||
payments.append(payment)
|
||||
rounding_adjustment += doc.rounding_adjustment
|
||||
rounded_total += doc.rounded_total
|
||||
base_rounding_adjustment += doc.rounding_adjustment
|
||||
base_rounded_total += doc.rounded_total
|
||||
base_rounding_adjustment += doc.base_rounding_adjustment
|
||||
base_rounded_total += doc.base_rounded_total
|
||||
|
||||
|
||||
if loyalty_points_sum:
|
||||
@@ -179,9 +187,9 @@ class POSInvoiceMergeLog(Document):
|
||||
invoice.set('payments', payments)
|
||||
invoice.set('taxes', taxes)
|
||||
invoice.set('rounding_adjustment',rounding_adjustment)
|
||||
invoice.set('rounding_adjustment',base_rounding_adjustment)
|
||||
invoice.set('base_rounded_total',base_rounded_total)
|
||||
invoice.set('base_rounding_adjustment',base_rounding_adjustment)
|
||||
invoice.set('rounded_total',rounded_total)
|
||||
invoice.set('base_rounded_total',base_rounded_total)
|
||||
invoice.additional_discount_percentage = 0
|
||||
invoice.discount_amount = 0.0
|
||||
invoice.taxes_and_charges = None
|
||||
|
||||
@@ -546,6 +546,75 @@ class TestPricingRule(unittest.TestCase):
|
||||
frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete()
|
||||
item.delete()
|
||||
|
||||
def test_pricing_rule_for_different_currency(self):
|
||||
make_item("Test Sanitizer Item")
|
||||
|
||||
pricing_rule_record = {
|
||||
"doctype": "Pricing Rule",
|
||||
"title": "_Test Sanitizer Rule",
|
||||
"apply_on": "Item Code",
|
||||
"items": [{
|
||||
"item_code": "Test Sanitizer Item",
|
||||
}],
|
||||
"selling": 1,
|
||||
"currency": "INR",
|
||||
"rate_or_discount": "Rate",
|
||||
"rate": 0,
|
||||
"priority": 2,
|
||||
"margin_type": "Percentage",
|
||||
"margin_rate_or_amount": 0.0,
|
||||
"company": "_Test Company"
|
||||
}
|
||||
|
||||
rule = frappe.get_doc(pricing_rule_record)
|
||||
rule.rate_or_discount = 'Rate'
|
||||
rule.rate = 100.0
|
||||
rule.insert()
|
||||
|
||||
rule1 = frappe.get_doc(pricing_rule_record)
|
||||
rule1.currency = 'USD'
|
||||
rule1.rate_or_discount = 'Rate'
|
||||
rule1.rate = 2.0
|
||||
rule1.priority = 1
|
||||
rule1.insert()
|
||||
|
||||
args = frappe._dict({
|
||||
"item_code": "Test Sanitizer Item",
|
||||
"company": "_Test Company",
|
||||
"price_list": "_Test Price List",
|
||||
"currency": "USD",
|
||||
"doctype": "Sales Invoice",
|
||||
"conversion_rate": 1,
|
||||
"price_list_currency": "_Test Currency",
|
||||
"plc_conversion_rate": 1,
|
||||
"order_type": "Sales",
|
||||
"customer": "_Test Customer",
|
||||
"name": None,
|
||||
"transaction_date": frappe.utils.nowdate()
|
||||
})
|
||||
|
||||
details = get_item_details(args)
|
||||
self.assertEqual(details.price_list_rate, 2.0)
|
||||
|
||||
|
||||
args = frappe._dict({
|
||||
"item_code": "Test Sanitizer Item",
|
||||
"company": "_Test Company",
|
||||
"price_list": "_Test Price List",
|
||||
"currency": "INR",
|
||||
"doctype": "Sales Invoice",
|
||||
"conversion_rate": 1,
|
||||
"price_list_currency": "_Test Currency",
|
||||
"plc_conversion_rate": 1,
|
||||
"order_type": "Sales",
|
||||
"customer": "_Test Customer",
|
||||
"name": None,
|
||||
"transaction_date": frappe.utils.nowdate()
|
||||
})
|
||||
|
||||
details = get_item_details(args)
|
||||
self.assertEqual(details.price_list_rate, 100.0)
|
||||
|
||||
def test_pricing_rule_for_transaction(self):
|
||||
make_item("Water Flask 1")
|
||||
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
|
||||
|
||||
@@ -265,6 +265,11 @@ def filter_pricing_rules(args, pricing_rules, doc=None):
|
||||
else:
|
||||
p.variant_of = None
|
||||
|
||||
if len(pricing_rules) > 1:
|
||||
filtered_rules = list(filter(lambda x: x.currency==args.get('currency'), pricing_rules))
|
||||
if filtered_rules:
|
||||
pricing_rules = filtered_rules
|
||||
|
||||
# find pricing rule with highest priority
|
||||
if pricing_rules:
|
||||
max_priority = max(cint(p.priority) for p in pricing_rules)
|
||||
|
||||
@@ -617,7 +617,8 @@ def make_material_request(source_name, target_doc=None):
|
||||
"doctype": "Material Request Item",
|
||||
"field_map": {
|
||||
"required_qty": "qty",
|
||||
"uom": "stock_uom"
|
||||
"uom": "stock_uom",
|
||||
"name": "job_card_item"
|
||||
},
|
||||
"postprocess": update_item,
|
||||
}
|
||||
@@ -627,17 +628,22 @@ def make_material_request(source_name, target_doc=None):
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_stock_entry(source_name, target_doc=None):
|
||||
def update_item(obj, target, source_parent):
|
||||
def update_item(source, target, source_parent):
|
||||
target.t_warehouse = source_parent.wip_warehouse
|
||||
|
||||
if not target.conversion_factor:
|
||||
target.conversion_factor = 1
|
||||
|
||||
pending_rm_qty = flt(source.required_qty) - flt(source.transferred_qty)
|
||||
if pending_rm_qty > 0:
|
||||
target.qty = pending_rm_qty
|
||||
|
||||
def set_missing_values(source, target):
|
||||
target.purpose = "Material Transfer for Manufacture"
|
||||
target.from_bom = 1
|
||||
|
||||
# avoid negative 'For Quantity'
|
||||
pending_fg_qty = source.get('for_quantity', 0) - source.get('transferred_qty', 0)
|
||||
pending_fg_qty = flt(source.get('for_quantity', 0)) - flt(source.get('transferred_qty', 0))
|
||||
target.fg_completed_qty = pending_fg_qty if pending_fg_qty > 0 else 0
|
||||
|
||||
target.set_transfer_qty()
|
||||
|
||||
@@ -16,11 +16,22 @@ from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
|
||||
|
||||
class TestJobCard(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
make_bom_for_jc_tests()
|
||||
|
||||
transfer_material_against, source_warehouse = None, None
|
||||
tests_that_transfer_against_jc = ("test_job_card_multiple_materials_transfer",
|
||||
"test_job_card_excess_material_transfer")
|
||||
|
||||
tests_that_skip_setup = (
|
||||
"test_job_card_material_transfer_correctness",
|
||||
)
|
||||
tests_that_transfer_against_jc = (
|
||||
"test_job_card_multiple_materials_transfer",
|
||||
"test_job_card_excess_material_transfer",
|
||||
"test_job_card_partial_material_transfer"
|
||||
)
|
||||
|
||||
if self._testMethodName in tests_that_skip_setup:
|
||||
return
|
||||
|
||||
if self._testMethodName in tests_that_transfer_against_jc:
|
||||
transfer_material_against = "Job Card"
|
||||
@@ -191,4 +202,132 @@ class TestJobCard(unittest.TestCase):
|
||||
job_card.submit()
|
||||
|
||||
# JC is Completed with excess transfer
|
||||
self.assertEqual(job_card.status, "Completed")
|
||||
self.assertEqual(job_card.status, "Completed")
|
||||
|
||||
def test_job_card_partial_material_transfer(self):
|
||||
"Test partial material transfer against Job Card"
|
||||
|
||||
make_stock_entry(item_code="_Test Item", target="Stores - _TC",
|
||||
qty=25, basic_rate=100)
|
||||
make_stock_entry(item_code="_Test Item Home Desktop Manufactured",
|
||||
target="Stores - _TC", qty=15, basic_rate=100)
|
||||
|
||||
job_card_name = frappe.db.get_value("Job Card", {'work_order': self.work_order.name})
|
||||
job_card = frappe.get_doc("Job Card", job_card_name)
|
||||
|
||||
# partially transfer
|
||||
transfer_entry = make_stock_entry_from_jc(job_card_name)
|
||||
transfer_entry.fg_completed_qty = 1
|
||||
transfer_entry.get_items()
|
||||
transfer_entry.insert()
|
||||
transfer_entry.submit()
|
||||
|
||||
job_card.reload()
|
||||
self.assertEqual(job_card.transferred_qty, 1)
|
||||
self.assertEqual(transfer_entry.items[0].qty, 5)
|
||||
self.assertEqual(transfer_entry.items[1].qty, 3)
|
||||
|
||||
# transfer remaining
|
||||
transfer_entry_2 = make_stock_entry_from_jc(job_card_name)
|
||||
|
||||
self.assertEqual(transfer_entry_2.fg_completed_qty, 1)
|
||||
self.assertEqual(transfer_entry_2.items[0].qty, 5)
|
||||
self.assertEqual(transfer_entry_2.items[1].qty, 3)
|
||||
|
||||
transfer_entry_2.insert()
|
||||
transfer_entry_2.submit()
|
||||
|
||||
job_card.reload()
|
||||
self.assertEqual(job_card.transferred_qty, 2)
|
||||
|
||||
def test_job_card_material_transfer_correctness(self):
|
||||
"""
|
||||
1. Test if only current Job Card Items are pulled in a Stock Entry against a Job Card
|
||||
2. Test impact of changing 'For Qty' in such a Stock Entry
|
||||
"""
|
||||
create_bom_with_multiple_operations()
|
||||
work_order = make_wo_with_transfer_against_jc()
|
||||
|
||||
job_card_name = frappe.db.get_value(
|
||||
"Job Card",
|
||||
{"work_order": work_order.name,"operation": "Test Operation A"}
|
||||
)
|
||||
job_card = frappe.get_doc("Job Card", job_card_name)
|
||||
|
||||
self.assertEqual(len(job_card.items), 1)
|
||||
self.assertEqual(job_card.items[0].item_code, "_Test Item")
|
||||
|
||||
# check if right items are mapped in transfer entry
|
||||
transfer_entry = make_stock_entry_from_jc(job_card_name)
|
||||
transfer_entry.insert()
|
||||
|
||||
self.assertEqual(len(transfer_entry.items), 1)
|
||||
self.assertEqual(transfer_entry.items[0].item_code, "_Test Item")
|
||||
self.assertEqual(transfer_entry.items[0].qty, 4)
|
||||
|
||||
# change 'For Qty' and check impact on items table
|
||||
# no.of items should be the same with qty change
|
||||
transfer_entry.fg_completed_qty = 2
|
||||
transfer_entry.get_items()
|
||||
|
||||
self.assertEqual(len(transfer_entry.items), 1)
|
||||
self.assertEqual(transfer_entry.items[0].item_code, "_Test Item")
|
||||
self.assertEqual(transfer_entry.items[0].qty, 2)
|
||||
|
||||
# rollback via tearDown method
|
||||
|
||||
def create_bom_with_multiple_operations():
|
||||
"Create a BOM with multiple operations and Material Transfer against Job Card"
|
||||
from erpnext.manufacturing.doctype.operation.test_operation import make_operation
|
||||
|
||||
test_record = frappe.get_test_records("BOM")[2]
|
||||
bom_doc = frappe.get_doc(test_record)
|
||||
|
||||
row = {
|
||||
"operation": "Test Operation A",
|
||||
"workstation": "_Test Workstation A",
|
||||
"hour_rate_rent": 300,
|
||||
"time_in_mins": 60
|
||||
}
|
||||
make_workstation(row)
|
||||
make_operation(row)
|
||||
|
||||
bom_doc.append("operations", {
|
||||
"operation": "Test Operation A",
|
||||
"description": "Test Operation A",
|
||||
"workstation": "_Test Workstation A",
|
||||
"hour_rate": 300,
|
||||
"time_in_mins": 60,
|
||||
"operating_cost": 100
|
||||
})
|
||||
|
||||
bom_doc.transfer_material_against = "Job Card"
|
||||
bom_doc.save()
|
||||
bom_doc.submit()
|
||||
|
||||
return bom_doc
|
||||
|
||||
def make_wo_with_transfer_against_jc():
|
||||
"Create a WO with multiple operations and Material Transfer against Job Card"
|
||||
|
||||
work_order = make_wo_order_test_record(
|
||||
item="_Test FG Item 2",
|
||||
qty=4,
|
||||
transfer_material_against="Job Card",
|
||||
source_warehouse="Stores - _TC",
|
||||
do_not_submit=True
|
||||
)
|
||||
work_order.required_items[0].operation = "Test Operation A"
|
||||
work_order.required_items[1].operation = "_Test Operation 1"
|
||||
work_order.submit()
|
||||
|
||||
return work_order
|
||||
|
||||
def make_bom_for_jc_tests():
|
||||
test_records = frappe.get_test_records('BOM')
|
||||
bom = frappe.copy_doc(test_records[2])
|
||||
bom.set_rate_of_sub_assembly_item_based_on_bom = 0
|
||||
bom.rm_cost_as_per = "Valuation Rate"
|
||||
bom.items[0].uom = "_Test UOM 1"
|
||||
bom.items[0].conversion_factor = 5
|
||||
bom.insert()
|
||||
@@ -18,15 +18,13 @@ def make_operation(*args, **kwargs):
|
||||
|
||||
args = frappe._dict(args)
|
||||
|
||||
try:
|
||||
if not frappe.db.exists("Operation", args.operation):
|
||||
doc = frappe.get_doc({
|
||||
"doctype": "Operation",
|
||||
"name": args.operation,
|
||||
"workstation": args.workstation
|
||||
})
|
||||
|
||||
doc.insert()
|
||||
|
||||
return doc
|
||||
except frappe.DuplicateEntryError:
|
||||
return frappe.get_doc("Operation", args.operation)
|
||||
|
||||
return frappe.get_doc("Operation", args.operation)
|
||||
|
||||
@@ -90,7 +90,7 @@ def make_workstation(*args, **kwargs):
|
||||
args = frappe._dict(args)
|
||||
|
||||
workstation_name = args.workstation_name or args.workstation
|
||||
try:
|
||||
if not frappe.db.exists("Workstation", workstation_name):
|
||||
doc = frappe.get_doc({
|
||||
"doctype": "Workstation",
|
||||
"workstation_name": workstation_name
|
||||
@@ -100,5 +100,5 @@ def make_workstation(*args, **kwargs):
|
||||
doc.insert()
|
||||
|
||||
return doc
|
||||
except frappe.DuplicateEntryError:
|
||||
return frappe.get_doc("Workstation", workstation_name)
|
||||
|
||||
return frappe.get_doc("Workstation", workstation_name)
|
||||
|
||||
@@ -29,8 +29,15 @@ def get_production_plan_item_details(filters, data, order_details):
|
||||
|
||||
production_plan_doc = frappe.get_cached_doc("Production Plan", filters.get("production_plan"))
|
||||
for row in production_plan_doc.po_items:
|
||||
work_order = frappe.get_cached_value("Work Order", {"production_plan_item": row.name,
|
||||
"bom_no": row.bom_no, "production_item": row.item_code}, "name")
|
||||
work_order = frappe.get_value(
|
||||
"Work Order",
|
||||
{
|
||||
"production_plan_item": row.name,
|
||||
"bom_no": row.bom_no,
|
||||
"production_item": row.item_code
|
||||
},
|
||||
"name"
|
||||
)
|
||||
|
||||
if row.item_code not in itemwise_indent:
|
||||
itemwise_indent.setdefault(row.item_code, {})
|
||||
@@ -41,10 +48,10 @@ def get_production_plan_item_details(filters, data, order_details):
|
||||
"item_name": frappe.get_cached_value("Item", row.item_code, "item_name"),
|
||||
"qty": row.planned_qty,
|
||||
"document_type": "Work Order",
|
||||
"document_name": work_order,
|
||||
"document_name": work_order or "",
|
||||
"bom_level": frappe.get_cached_value("BOM", row.bom_no, "bom_level"),
|
||||
"produced_qty": order_details.get((work_order, row.item_code)).get("produced_qty"),
|
||||
"pending_qty": flt(row.planned_qty) - flt(order_details.get((work_order, row.item_code)).get("produced_qty"))
|
||||
"produced_qty": order_details.get((work_order, row.item_code), {}).get("produced_qty", 0),
|
||||
"pending_qty": flt(row.planned_qty) - flt(order_details.get((work_order, row.item_code), {}).get("produced_qty", 0))
|
||||
})
|
||||
|
||||
get_production_plan_sub_assembly_item_details(filters, row, production_plan_doc, data, order_details)
|
||||
@@ -55,11 +62,23 @@ def get_production_plan_sub_assembly_item_details(filters, row, production_plan_
|
||||
subcontracted_item = (item.type_of_manufacturing == 'Subcontract')
|
||||
|
||||
if subcontracted_item:
|
||||
docname = frappe.get_cached_value("Purchase Order Item",
|
||||
{"production_plan_sub_assembly_item": item.name, "docstatus": ("<", 2)}, "parent")
|
||||
docname = frappe.get_value(
|
||||
"Purchase Order Item",
|
||||
{
|
||||
"production_plan_sub_assembly_item": item.name,
|
||||
"docstatus": ("<", 2)
|
||||
},
|
||||
"parent"
|
||||
)
|
||||
else:
|
||||
docname = frappe.get_cached_value("Work Order",
|
||||
{"production_plan_sub_assembly_item": item.name, "docstatus": ("<", 2)}, "name")
|
||||
docname = frappe.get_value(
|
||||
"Work Order",
|
||||
{
|
||||
"production_plan_sub_assembly_item": item.name,
|
||||
"docstatus": ("<", 2)
|
||||
},
|
||||
"name"
|
||||
)
|
||||
|
||||
data.append({
|
||||
"indent": 1,
|
||||
@@ -67,10 +86,10 @@ def get_production_plan_sub_assembly_item_details(filters, row, production_plan_
|
||||
"item_name": item.item_name,
|
||||
"qty": item.qty,
|
||||
"document_type": "Work Order" if not subcontracted_item else "Purchase Order",
|
||||
"document_name": docname,
|
||||
"document_name": docname or "",
|
||||
"bom_level": item.bom_level,
|
||||
"produced_qty": order_details.get((docname, item.production_item)).get("produced_qty"),
|
||||
"pending_qty": flt(item.qty) - flt(order_details.get((docname, item.production_item)).get("produced_qty"))
|
||||
"produced_qty": order_details.get((docname, item.production_item), {}).get("produced_qty", 0),
|
||||
"pending_qty": flt(item.qty) - flt(order_details.get((docname, item.production_item), {}).get("produced_qty", 0))
|
||||
})
|
||||
|
||||
def get_work_order_details(filters, order_details):
|
||||
|
||||
@@ -440,11 +440,14 @@ def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None):
|
||||
|
||||
|
||||
def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, extra_amount=0):
|
||||
credit_limit = get_credit_limit(customer, company)
|
||||
if not credit_limit:
|
||||
return
|
||||
|
||||
customer_outstanding = get_customer_outstanding(customer, company, ignore_outstanding_sales_order)
|
||||
if extra_amount > 0:
|
||||
customer_outstanding += flt(extra_amount)
|
||||
|
||||
credit_limit = get_credit_limit(customer, company)
|
||||
if credit_limit > 0 and flt(customer_outstanding) > credit_limit:
|
||||
msgprint(_("Credit limit has been crossed for customer {0} ({1}/{2})")
|
||||
.format(customer, customer_outstanding, credit_limit))
|
||||
|
||||
@@ -502,7 +502,8 @@ def make_stock_entry(source_name, target_doc=None):
|
||||
"field_map": {
|
||||
"name": "material_request_item",
|
||||
"parent": "material_request",
|
||||
"uom": "stock_uom"
|
||||
"uom": "stock_uom",
|
||||
"job_card_item": "job_card_item"
|
||||
},
|
||||
"postprocess": update_item,
|
||||
"condition": lambda doc: doc.ordered_qty < doc.stock_qty
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"sales_order_item",
|
||||
"production_plan",
|
||||
"material_request_plan_item",
|
||||
"job_card_item",
|
||||
"col_break4",
|
||||
"expense_account",
|
||||
"section_break_46",
|
||||
@@ -444,16 +445,25 @@
|
||||
{
|
||||
"fieldname": "qty_info_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "job_card_item",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"label": "Job Card Item"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-02 11:44:36.553064",
|
||||
"modified": "2021-11-03 14:40:24.409826",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Material Request Item",
|
||||
"naming_rule": "Random",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
|
||||
@@ -1464,52 +1464,94 @@ class StockEntry(StockController):
|
||||
return item_dict
|
||||
|
||||
def get_pro_order_required_items(self, backflush_based_on=None):
|
||||
item_dict = frappe._dict()
|
||||
pro_order = frappe.get_doc("Work Order", self.work_order)
|
||||
if not frappe.db.get_value("Warehouse", pro_order.wip_warehouse, "is_group"):
|
||||
wip_warehouse = pro_order.wip_warehouse
|
||||
"""
|
||||
Gets Work Order Required Items only if Stock Entry purpose is **Material Transferred for Manufacture**.
|
||||
"""
|
||||
item_dict, job_card_items = frappe._dict(), []
|
||||
work_order = frappe.get_doc("Work Order", self.work_order)
|
||||
|
||||
consider_job_card = work_order.transfer_material_against == "Job Card" and self.get("job_card")
|
||||
if consider_job_card:
|
||||
job_card_items = self.get_job_card_item_codes(self.get("job_card"))
|
||||
|
||||
if not frappe.db.get_value("Warehouse", work_order.wip_warehouse, "is_group"):
|
||||
wip_warehouse = work_order.wip_warehouse
|
||||
else:
|
||||
wip_warehouse = None
|
||||
|
||||
for d in pro_order.get("required_items"):
|
||||
if ( ((flt(d.required_qty) > flt(d.transferred_qty)) or
|
||||
(backflush_based_on == "Material Transferred for Manufacture")) and
|
||||
(d.include_item_in_manufacturing or self.purpose != "Material Transfer for Manufacture")):
|
||||
for d in work_order.get("required_items"):
|
||||
if consider_job_card and (d.item_code not in job_card_items):
|
||||
continue
|
||||
|
||||
transfer_pending = flt(d.required_qty) > flt(d.transferred_qty)
|
||||
can_transfer = transfer_pending or (backflush_based_on == "Material Transferred for Manufacture")
|
||||
|
||||
if not can_transfer:
|
||||
continue
|
||||
|
||||
if d.include_item_in_manufacturing:
|
||||
item_row = d.as_dict()
|
||||
item_row["idx"] = len(item_dict) + 1
|
||||
|
||||
if consider_job_card:
|
||||
job_card_item = frappe.db.get_value(
|
||||
"Job Card Item",
|
||||
{
|
||||
"item_code": d.item_code,
|
||||
"parent": self.get("job_card")
|
||||
}
|
||||
)
|
||||
item_row["job_card_item"] = job_card_item or None
|
||||
|
||||
if d.source_warehouse and not frappe.db.get_value("Warehouse", d.source_warehouse, "is_group"):
|
||||
item_row["from_warehouse"] = d.source_warehouse
|
||||
|
||||
item_row["to_warehouse"] = wip_warehouse
|
||||
if item_row["allow_alternative_item"]:
|
||||
item_row["allow_alternative_item"] = pro_order.allow_alternative_item
|
||||
item_row["allow_alternative_item"] = work_order.allow_alternative_item
|
||||
|
||||
item_dict.setdefault(d.item_code, item_row)
|
||||
|
||||
return item_dict
|
||||
|
||||
def get_job_card_item_codes(self, job_card=None):
|
||||
if not job_card:
|
||||
return []
|
||||
|
||||
job_card_items = frappe.get_all(
|
||||
"Job Card Item",
|
||||
filters={
|
||||
"parent": job_card
|
||||
},
|
||||
fields=["item_code"],
|
||||
distinct=True
|
||||
)
|
||||
return [d.item_code for d in job_card_items]
|
||||
|
||||
def add_to_stock_entry_detail(self, item_dict, bom_no=None):
|
||||
for d in item_dict:
|
||||
stock_uom = item_dict[d].get("stock_uom") or frappe.db.get_value("Item", d, "stock_uom")
|
||||
item_row = item_dict[d]
|
||||
stock_uom = item_row.get("stock_uom") or frappe.db.get_value("Item", d, "stock_uom")
|
||||
|
||||
se_child = self.append('items')
|
||||
se_child.s_warehouse = item_dict[d].get("from_warehouse")
|
||||
se_child.t_warehouse = item_dict[d].get("to_warehouse")
|
||||
se_child.item_code = item_dict[d].get('item_code') or cstr(d)
|
||||
se_child.uom = item_dict[d]["uom"] if item_dict[d].get("uom") else stock_uom
|
||||
se_child.s_warehouse = item_row.get("from_warehouse")
|
||||
se_child.t_warehouse = item_row.get("to_warehouse")
|
||||
se_child.item_code = item_row.get('item_code') or cstr(d)
|
||||
se_child.uom = item_row["uom"] if item_row.get("uom") else stock_uom
|
||||
se_child.stock_uom = stock_uom
|
||||
se_child.qty = flt(item_dict[d]["qty"], se_child.precision("qty"))
|
||||
se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0)
|
||||
se_child.subcontracted_item = item_dict[d].get("main_item_code")
|
||||
se_child.cost_center = (item_dict[d].get("cost_center") or
|
||||
get_default_cost_center(item_dict[d], company = self.company))
|
||||
se_child.is_finished_item = item_dict[d].get("is_finished_item", 0)
|
||||
se_child.is_scrap_item = item_dict[d].get("is_scrap_item", 0)
|
||||
se_child.is_process_loss = item_dict[d].get("is_process_loss", 0)
|
||||
se_child.qty = flt(item_row["qty"], se_child.precision("qty"))
|
||||
se_child.allow_alternative_item = item_row.get("allow_alternative_item", 0)
|
||||
se_child.subcontracted_item = item_row.get("main_item_code")
|
||||
se_child.cost_center = (item_row.get("cost_center") or
|
||||
get_default_cost_center(item_row, company = self.company))
|
||||
se_child.is_finished_item = item_row.get("is_finished_item", 0)
|
||||
se_child.is_scrap_item = item_row.get("is_scrap_item", 0)
|
||||
se_child.is_process_loss = item_row.get("is_process_loss", 0)
|
||||
|
||||
for field in ["idx", "po_detail", "original_item", "expense_account",
|
||||
"description", "item_name", "serial_no", "batch_no", "allow_zero_valuation_rate"]:
|
||||
if item_dict[d].get(field):
|
||||
se_child.set(field, item_dict[d].get(field))
|
||||
if item_row.get(field):
|
||||
se_child.set(field, item_row.get(field))
|
||||
|
||||
if se_child.s_warehouse==None:
|
||||
se_child.s_warehouse = self.from_warehouse
|
||||
@@ -1517,12 +1559,11 @@ class StockEntry(StockController):
|
||||
se_child.t_warehouse = self.to_warehouse
|
||||
|
||||
# in stock uom
|
||||
se_child.conversion_factor = flt(item_dict[d].get("conversion_factor")) or 1
|
||||
se_child.transfer_qty = flt(item_dict[d]["qty"]*se_child.conversion_factor, se_child.precision("qty"))
|
||||
se_child.conversion_factor = flt(item_row.get("conversion_factor")) or 1
|
||||
se_child.transfer_qty = flt(item_row["qty"]*se_child.conversion_factor, se_child.precision("qty"))
|
||||
|
||||
|
||||
# to be assigned for finished item
|
||||
se_child.bom_no = bom_no
|
||||
se_child.bom_no = bom_no # to be assigned for finished item
|
||||
se_child.job_card_item = item_row.get("job_card_item") if self.get("job_card") else None
|
||||
|
||||
def validate_with_material_request(self):
|
||||
for item in self.get("items"):
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{% for d in data %}
|
||||
<div class="dashboard-list-item" style="padding: 7px 15px;">
|
||||
<div class="row">
|
||||
<div class="col-sm-2 small" style="margin-top: 8px;">
|
||||
<div class="col-sm-2" style="margin-top: 8px;">
|
||||
<a data-type="warehouse" data-name="{{ d.warehouse }}">{{ d.warehouse }}</a>
|
||||
</div>
|
||||
<div class="col-sm-2 small" style="margin-top: 8px; ">
|
||||
<div class="col-sm-2" style="margin-top: 8px; ">
|
||||
<a data-type="item" data-name="{{ d.item_code }}">{{ d.item_code }}</a>
|
||||
</div>
|
||||
<div class="col-sm-1 small" style="margin-top: 8px; ">
|
||||
<div class="col-sm-1" style="margin-top: 8px; ">
|
||||
{{ d.stock_capacity }}
|
||||
</div>
|
||||
<div class="col-sm-2 small" style="margin-top: 8px; ">
|
||||
<div class="col-sm-2" style="margin-top: 8px; ">
|
||||
{{ d.actual_qty }}
|
||||
</div>
|
||||
<div class="col-sm-2 small">
|
||||
<div class="col-sm-2">
|
||||
<div class="progress" title="Occupied Qty: {{ d.actual_qty }}" style="margin-bottom: 4px; height: 7px; margin-top: 14px;">
|
||||
<div class="progress-bar" role="progressbar"
|
||||
aria-valuenow="{{ d.percent_occupied }}"
|
||||
@@ -23,16 +23,19 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-1 small" style="margin-top: 8px;">
|
||||
<div class="col-sm-1" style="margin-top: 8px;">
|
||||
{{ d.percent_occupied }}%
|
||||
</div>
|
||||
{% if can_write %}
|
||||
<div class="col-sm-1 text-right" style="margin-top: 2px;">
|
||||
<button class="btn btn-default btn-xs btn-edit"
|
||||
style="margin-top: 4px;margin-bottom: 4px;"
|
||||
data-warehouse="{{ d.warehouse }}"
|
||||
data-item="{{ escape(d.item_code) }}"
|
||||
data-company="{{ escape(d.company) }}">{{ __("Edit Capacity") }}</a>
|
||||
<div class="col-sm-2 text-right" style="margin-top: 2px;">
|
||||
<button
|
||||
class="btn btn-default btn-xs btn-edit"
|
||||
style="margin: 4px 0; float: left;"
|
||||
data-warehouse="{{ d.warehouse }}"
|
||||
data-item="{{ escape(d.item_code) }}"
|
||||
data-company="{{ escape(d.company) }}">
|
||||
{{ __("Edit Capacity") }}
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ frappe.pages['warehouse-capacity-summary'].on_page_load = function(wrapper) {
|
||||
title: 'Warehouse Capacity Summary',
|
||||
single_column: true
|
||||
});
|
||||
page.set_secondary_action('Refresh', () => page.capacity_dashboard.refresh(), 'octicon octicon-sync');
|
||||
page.set_secondary_action('Refresh', () => page.capacity_dashboard.refresh(), 'refresh');
|
||||
page.start = 0;
|
||||
|
||||
page.company_field = page.add_field({
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<div class="dashboard-list-item" style="padding: 12px 15px;">
|
||||
<div class="row">
|
||||
<div class="col-sm-2 small text-muted" style="margin-top: 8px;">
|
||||
<div class="col-sm-2 text-muted" style="margin-top: 8px;">
|
||||
Warehouse
|
||||
</div>
|
||||
<div class="col-sm-2 small text-muted" style="margin-top: 8px;">
|
||||
<div class="col-sm-2 text-muted" style="margin-top: 8px;">
|
||||
Item
|
||||
</div>
|
||||
<div class="col-sm-1 small text-muted" style="margin-top: 8px;">
|
||||
<div class="col-sm-1 text-muted" style="margin-top: 8px;">
|
||||
Stock Capacity
|
||||
</div>
|
||||
<div class="col-sm-2 small text-muted" style="margin-top: 8px;">
|
||||
<div class="col-sm-2 text-muted" style="margin-top: 8px;">
|
||||
Balance Stock Qty
|
||||
</div>
|
||||
<div class="col-sm-2 small text-muted" style="margin-top: 8px;">
|
||||
<div class="col-sm-2 text-muted" style="margin-top: 8px;">
|
||||
% Occupied
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user