mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-22 22:49:19 +00:00
Merge branch 'hotfix'
This commit is contained in:
@@ -5,7 +5,7 @@ import frappe
|
|||||||
from erpnext.hooks import regional_overrides
|
from erpnext.hooks import regional_overrides
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate
|
||||||
|
|
||||||
__version__ = '11.1.30'
|
__version__ = '11.1.31'
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
'''Get default company for user'''
|
'''Get default company for user'''
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.validate_fixed_asset()
|
self.validate_fixed_asset()
|
||||||
self.create_remarks()
|
self.create_remarks()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
self.validate_purchase_receipt_if_update_stock()
|
||||||
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference)
|
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference)
|
||||||
|
|
||||||
def validate_release_date(self):
|
def validate_release_date(self):
|
||||||
@@ -284,7 +285,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
def update_status_updater_args(self):
|
def update_status_updater_args(self):
|
||||||
if cint(self.update_stock):
|
if cint(self.update_stock):
|
||||||
self.status_updater.extend([{
|
self.status_updater.append({
|
||||||
'source_dt': 'Purchase Invoice Item',
|
'source_dt': 'Purchase Invoice Item',
|
||||||
'target_dt': 'Purchase Order Item',
|
'target_dt': 'Purchase Order Item',
|
||||||
'join_field': 'po_detail',
|
'join_field': 'po_detail',
|
||||||
@@ -292,28 +293,29 @@ class PurchaseInvoice(BuyingController):
|
|||||||
'target_parent_dt': 'Purchase Order',
|
'target_parent_dt': 'Purchase Order',
|
||||||
'target_parent_field': 'per_received',
|
'target_parent_field': 'per_received',
|
||||||
'target_ref_field': 'qty',
|
'target_ref_field': 'qty',
|
||||||
'source_field': 'qty',
|
'source_field': 'received_qty',
|
||||||
|
'second_source_dt': 'Purchase Receipt Item',
|
||||||
|
'second_source_field': 'received_qty',
|
||||||
|
'second_join_field': 'purchase_order_item',
|
||||||
'percent_join_field':'purchase_order',
|
'percent_join_field':'purchase_order',
|
||||||
# 'percent_join_field': 'prevdoc_docname',
|
|
||||||
'overflow_type': 'receipt',
|
'overflow_type': 'receipt',
|
||||||
'extra_cond': """ and exists(select name from `tabPurchase Invoice`
|
'extra_cond': """ and exists(select name from `tabPurchase Invoice`
|
||||||
where name=`tabPurchase Invoice Item`.parent and update_stock = 1)"""
|
where name=`tabPurchase Invoice Item`.parent and update_stock = 1)"""
|
||||||
},
|
})
|
||||||
{
|
if cint(self.is_return):
|
||||||
'source_dt': 'Purchase Invoice Item',
|
self.status_updater.append({
|
||||||
'target_dt': 'Purchase Order Item',
|
'source_dt': 'Purchase Invoice Item',
|
||||||
'join_field': 'po_detail',
|
'target_dt': 'Purchase Order Item',
|
||||||
'target_field': 'returned_qty',
|
'join_field': 'po_detail',
|
||||||
'target_parent_dt': 'Purchase Order',
|
'target_field': 'returned_qty',
|
||||||
# 'target_parent_field': 'per_received',
|
'source_field': '-1 * qty',
|
||||||
# 'target_ref_field': 'qty',
|
'second_source_dt': 'Purchase Receipt Item',
|
||||||
'source_field': '-1 * qty',
|
'second_source_field': '-1 * qty',
|
||||||
# 'percent_join_field': 'prevdoc_docname',
|
'second_join_field': 'purchase_order_item',
|
||||||
# 'overflow_type': 'receipt',
|
'overflow_type': 'receipt',
|
||||||
'extra_cond': """ and exists (select name from `tabPurchase Invoice`
|
'extra_cond': """ and exists (select name from `tabPurchase Invoice`
|
||||||
where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||||
}
|
})
|
||||||
])
|
|
||||||
|
|
||||||
def validate_purchase_receipt_if_update_stock(self):
|
def validate_purchase_receipt_if_update_stock(self):
|
||||||
if self.update_stock:
|
if self.update_stock:
|
||||||
@@ -327,13 +329,13 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
self.check_prev_docstatus()
|
self.check_prev_docstatus()
|
||||||
self.update_status_updater_args()
|
self.update_status_updater_args()
|
||||||
|
self.update_prevdoc_status()
|
||||||
|
|
||||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||||
self.company, self.base_grand_total)
|
self.company, self.base_grand_total)
|
||||||
|
|
||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
self.update_against_document_in_jv()
|
self.update_against_document_in_jv()
|
||||||
self.update_prevdoc_status()
|
|
||||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||||
self.update_billing_status_in_pr()
|
self.update_billing_status_in_pr()
|
||||||
|
|
||||||
@@ -763,13 +765,13 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.check_for_closed_status()
|
self.check_for_closed_status()
|
||||||
|
|
||||||
self.update_status_updater_args()
|
self.update_status_updater_args()
|
||||||
|
self.update_prevdoc_status()
|
||||||
|
|
||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
|
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
|
||||||
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
|
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
|
||||||
unlink_ref_doc_from_payment_entries(self)
|
unlink_ref_doc_from_payment_entries(self)
|
||||||
|
|
||||||
self.update_prevdoc_status()
|
|
||||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||||
self.update_billing_status_in_pr()
|
self.update_billing_status_in_pr()
|
||||||
|
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
def update_status_updater_args(self):
|
def update_status_updater_args(self):
|
||||||
if cint(self.update_stock):
|
if cint(self.update_stock):
|
||||||
self.status_updater.extend([{
|
self.status_updater.append({
|
||||||
'source_dt':'Sales Invoice Item',
|
'source_dt':'Sales Invoice Item',
|
||||||
'target_dt':'Sales Order Item',
|
'target_dt':'Sales Order Item',
|
||||||
'target_parent_dt':'Sales Order',
|
'target_parent_dt':'Sales Order',
|
||||||
@@ -274,21 +274,20 @@ class SalesInvoice(SellingController):
|
|||||||
'overflow_type': 'delivery',
|
'overflow_type': 'delivery',
|
||||||
'extra_cond': """ and exists(select name from `tabSales Invoice`
|
'extra_cond': """ and exists(select name from `tabSales Invoice`
|
||||||
where name=`tabSales Invoice Item`.parent and update_stock = 1)"""
|
where name=`tabSales Invoice Item`.parent and update_stock = 1)"""
|
||||||
},
|
})
|
||||||
{
|
if cint(self.is_return):
|
||||||
'source_dt': 'Sales Invoice Item',
|
self.status_updater.append({
|
||||||
'target_dt': 'Sales Order Item',
|
'source_dt': 'Sales Invoice Item',
|
||||||
'join_field': 'so_detail',
|
'target_dt': 'Sales Order Item',
|
||||||
'target_field': 'returned_qty',
|
'join_field': 'so_detail',
|
||||||
'target_parent_dt': 'Sales Order',
|
'target_field': 'returned_qty',
|
||||||
# 'target_parent_field': 'per_delivered',
|
'target_parent_dt': 'Sales Order',
|
||||||
# 'target_ref_field': 'qty',
|
'source_field': '-1 * qty',
|
||||||
'source_field': '-1 * qty',
|
'second_source_dt': 'Delivery Note Item',
|
||||||
# 'percent_join_field': 'sales_order',
|
'second_source_field': '-1 * qty',
|
||||||
# 'overflow_type': 'delivery',
|
'second_join_field': 'so_detail',
|
||||||
'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||||
}
|
})
|
||||||
])
|
|
||||||
|
|
||||||
def check_credit_limit(self):
|
def check_credit_limit(self):
|
||||||
from erpnext.selling.doctype.customer.customer import check_credit_limit
|
from erpnext.selling.doctype.customer.customer import check_credit_limit
|
||||||
@@ -504,11 +503,14 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
def so_dn_required(self):
|
def so_dn_required(self):
|
||||||
"""check in manage account if sales order / delivery note required or not."""
|
"""check in manage account if sales order / delivery note required or not."""
|
||||||
|
if self.is_return:
|
||||||
|
return
|
||||||
dic = {'Sales Order':['so_required', 'is_pos'],'Delivery Note':['dn_required', 'update_stock']}
|
dic = {'Sales Order':['so_required', 'is_pos'],'Delivery Note':['dn_required', 'update_stock']}
|
||||||
for i in dic:
|
for i in dic:
|
||||||
if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes':
|
if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes':
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
if (d.item_code and frappe.get_cached_value('Item', d.item_code, 'is_stock_item') == 1
|
is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item')
|
||||||
|
if (d.item_code and is_stock_item == 1\
|
||||||
and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])):
|
and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])):
|
||||||
msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
|
msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
|
||||||
|
|
||||||
@@ -1167,6 +1169,8 @@ class SalesInvoice(SellingController):
|
|||||||
self.set_missing_values(for_validate = True)
|
self.set_missing_values(for_validate = True)
|
||||||
|
|
||||||
def validate_inter_company_party(doctype, party, company, inter_company_invoice_reference):
|
def validate_inter_company_party(doctype, party, company, inter_company_invoice_reference):
|
||||||
|
if not party:
|
||||||
|
return
|
||||||
if doctype == "Sales Invoice":
|
if doctype == "Sales Invoice":
|
||||||
partytype, ref_partytype, internal = "Customer", "Supplier", "is_internal_customer"
|
partytype, ref_partytype, internal = "Customer", "Supplier", "is_internal_customer"
|
||||||
ref_doc = "Purchase Invoice"
|
ref_doc = "Purchase Invoice"
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ def check_matching_amount(bank_account, company, transaction):
|
|||||||
|
|
||||||
payment_entries = frappe.get_all("Payment Entry", fields=["'Payment Entry' as doctype", "name", "paid_amount", "payment_type", "reference_no", "reference_date",
|
payment_entries = frappe.get_all("Payment Entry", fields=["'Payment Entry' as doctype", "name", "paid_amount", "payment_type", "reference_no", "reference_date",
|
||||||
"party", "party_type", "posting_date", "{0}".format(currency_field)], filters=[["paid_amount", "like", "{0}%".format(amount)],
|
"party", "party_type", "posting_date", "{0}".format(currency_field)], filters=[["paid_amount", "like", "{0}%".format(amount)],
|
||||||
["docstatus", "=", "1"], ["payment_type", "=", payment_type], ["ifnull(clearance_date, '')", "=", ""], ["{0}".format(account_from_to), "=", "{0}".format(bank_account)]])
|
["docstatus", "=", "1"], ["payment_type", "=", [payment_type, "Internal Transfer"]], ["ifnull(clearance_date, '')", "=", ""], ["{0}".format(account_from_to), "=", "{0}".format(bank_account)]])
|
||||||
|
|
||||||
if transaction.credit > 0:
|
if transaction.credit > 0:
|
||||||
journal_entries = frappe.db.sql("""
|
journal_entries = frappe.db.sql("""
|
||||||
|
|||||||
@@ -321,7 +321,10 @@ def sort_accounts(accounts, is_root=False, key="name"):
|
|||||||
"""Sort root types as Asset, Liability, Equity, Income, Expense"""
|
"""Sort root types as Asset, Liability, Equity, Income, Expense"""
|
||||||
|
|
||||||
def compare_accounts(a, b):
|
def compare_accounts(a, b):
|
||||||
if is_root:
|
if re.split('\W+', a[key])[0].isdigit():
|
||||||
|
# if chart of accounts is numbered, then sort by number
|
||||||
|
return cmp(a[key], b[key])
|
||||||
|
elif is_root:
|
||||||
if a.report_type != b.report_type and a.report_type == "Balance Sheet":
|
if a.report_type != b.report_type and a.report_type == "Balance Sheet":
|
||||||
return -1
|
return -1
|
||||||
if a.root_type != b.root_type and a.root_type == "Asset":
|
if a.root_type != b.root_type and a.root_type == "Asset":
|
||||||
@@ -330,10 +333,6 @@ def sort_accounts(accounts, is_root=False, key="name"):
|
|||||||
return -1
|
return -1
|
||||||
if a.root_type == "Income" and b.root_type == "Expense":
|
if a.root_type == "Income" and b.root_type == "Expense":
|
||||||
return -1
|
return -1
|
||||||
else:
|
|
||||||
if re.split('\W+', a[key])[0].isdigit():
|
|
||||||
# if chart of accounts is numbered, then sort by number
|
|
||||||
return cmp(a[key], b[key])
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
accounts.sort(key = functools.cmp_to_key(compare_accounts))
|
accounts.sort(key = functools.cmp_to_key(compare_accounts))
|
||||||
|
|||||||
@@ -211,6 +211,11 @@ frappe.query_reports["General Ledger"] = {
|
|||||||
"label": __("Currency"),
|
"label": __("Currency"),
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"options": erpnext.get_presentation_currency_list()
|
"options": erpnext.get_presentation_currency_list()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "show_opening_entries",
|
||||||
|
"label": __("Show Opening Entries"),
|
||||||
|
"fieldtype": "Check"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,7 +283,8 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
|
|||||||
|
|
||||||
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
|
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
|
||||||
for gle in gl_entries:
|
for gle in gl_entries:
|
||||||
if gle.posting_date < from_date or cstr(gle.is_opening) == "Yes":
|
if (gle.posting_date < from_date or
|
||||||
|
(cstr(gle.is_opening) == "Yes" and not filters.get("show_opening_entries"))):
|
||||||
update_value_in_dict(gle_map[gle.get(group_by)].totals, 'opening', gle)
|
update_value_in_dict(gle_map[gle.get(group_by)].totals, 'opening', gle)
|
||||||
update_value_in_dict(totals, 'opening', gle)
|
update_value_in_dict(totals, 'opening', gle)
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ def get_columns():
|
|||||||
"width": 150
|
"width": 150
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "item_name",
|
"fieldname": "item",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Item",
|
"options": "Item",
|
||||||
"label": "Item",
|
"label": "Item",
|
||||||
@@ -82,12 +82,12 @@ def get_data(filters):
|
|||||||
row = {
|
row = {
|
||||||
"territory": territory.name,
|
"territory": territory.name,
|
||||||
"item_group": item.item_group,
|
"item_group": item.item_group,
|
||||||
"item": item.name,
|
"item": item.item_code,
|
||||||
"item_name": item.item_name
|
"item_name": item.item_name
|
||||||
}
|
}
|
||||||
|
|
||||||
if sales_invoice_data.get((territory.name,item.name)):
|
if sales_invoice_data.get((territory.name,item.item_code)):
|
||||||
item_obj = sales_invoice_data[(territory.name,item.name)]
|
item_obj = sales_invoice_data[(territory.name,item.item_code)]
|
||||||
if item_obj.days_since_last_order > cint(filters['days']):
|
if item_obj.days_since_last_order > cint(filters['days']):
|
||||||
row.update({
|
row.update({
|
||||||
"territory": item_obj.territory,
|
"territory": item_obj.territory,
|
||||||
@@ -111,15 +111,15 @@ def get_sales_details(filters):
|
|||||||
date_field = "s.transaction_date" if filters["based_on"] == "Sales Order" else "s.posting_date"
|
date_field = "s.transaction_date" if filters["based_on"] == "Sales Order" else "s.posting_date"
|
||||||
|
|
||||||
sales_data = frappe.db.sql("""
|
sales_data = frappe.db.sql("""
|
||||||
select s.territory, s.customer, si.item_group, si.item_name, si.qty, {date_field} as last_order_date,
|
select s.territory, s.customer, si.item_group, si.item_code, si.qty, {date_field} as last_order_date,
|
||||||
DATEDIFF(CURDATE(), {date_field}) as days_since_last_order
|
DATEDIFF(CURDATE(), {date_field}) as days_since_last_order
|
||||||
from `tab{doctype}` s, `tab{doctype} Item` si
|
from `tab{doctype}` s, `tab{doctype} Item` si
|
||||||
where s.name = si.parent and s.docstatus = 1
|
where s.name = si.parent and s.docstatus = 1
|
||||||
group by si.name order by days_since_last_order """ #nosec
|
order by days_since_last_order """ #nosec
|
||||||
.format(date_field = date_field, doctype = filters['based_on']), as_dict=1)
|
.format(date_field = date_field, doctype = filters['based_on']), as_dict=1)
|
||||||
|
|
||||||
for d in sales_data:
|
for d in sales_data:
|
||||||
item_details_map.setdefault((d.territory,d.item_name), d)
|
item_details_map.setdefault((d.territory,d.item_code), d)
|
||||||
|
|
||||||
return item_details_map
|
return item_details_map
|
||||||
|
|
||||||
@@ -149,6 +149,6 @@ def get_items(filters):
|
|||||||
"name": filters["item"]
|
"name": filters["item"]
|
||||||
})
|
})
|
||||||
|
|
||||||
items = frappe.get_all("Item", fields=["name", "item_group", "item_name"], filters=filters_dict, order_by="name")
|
items = frappe.get_all("Item", fields=["name", "item_group", "item_name", "item_code"], filters=filters_dict, order_by="name")
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
|||||||
@@ -333,6 +333,9 @@ def reconcile_against_document(args):
|
|||||||
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
||||||
doc.make_gl_entries(cancel = 0, adv_adj =1)
|
doc.make_gl_entries(cancel = 0, adv_adj =1)
|
||||||
|
|
||||||
|
if d.voucher_type == 'Payment Entry':
|
||||||
|
doc.update_expense_claim()
|
||||||
|
|
||||||
def check_if_advance_entry_modified(args):
|
def check_if_advance_entry_modified(args):
|
||||||
"""
|
"""
|
||||||
check if there is already a voucher reference
|
check if there is already a voucher reference
|
||||||
|
|||||||
@@ -108,6 +108,69 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
self.assertEqual(po.get("items")[0].amount, 1400)
|
self.assertEqual(po.get("items")[0].amount, 1400)
|
||||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
||||||
|
|
||||||
|
def test_update_qty(self):
|
||||||
|
po = create_purchase_order()
|
||||||
|
|
||||||
|
make_pr_against_po(po.name, 6)
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
self.assertEqual(po.get("items")[0].received_qty, 6)
|
||||||
|
|
||||||
|
# Check received_qty after make_purchase_invoice without update_stock checked
|
||||||
|
pi1 = make_purchase_invoice(po.name)
|
||||||
|
pi1.get("items")[0].qty = 6
|
||||||
|
pi1.insert()
|
||||||
|
pi1.submit()
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
self.assertEqual(po.get("items")[0].received_qty, 6)
|
||||||
|
|
||||||
|
# Check received_qty after make_purchase_invoice with update_stock checked
|
||||||
|
pi2 = make_purchase_invoice(po.name)
|
||||||
|
pi2.set("update_stock", 1)
|
||||||
|
pi2.get("items")[0].qty = 3
|
||||||
|
pi2.insert()
|
||||||
|
pi2.submit()
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
self.assertEqual(po.get("items")[0].received_qty, 9)
|
||||||
|
|
||||||
|
def test_return_against_purchase_order(self):
|
||||||
|
po = create_purchase_order()
|
||||||
|
|
||||||
|
pr = make_pr_against_po(po.name, 6)
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
self.assertEqual(po.get("items")[0].received_qty, 6)
|
||||||
|
|
||||||
|
pi2 = make_purchase_invoice(po.name)
|
||||||
|
pi2.set("update_stock", 1)
|
||||||
|
pi2.get("items")[0].qty = 3
|
||||||
|
pi2.insert()
|
||||||
|
pi2.submit()
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
self.assertEqual(po.get("items")[0].received_qty, 9)
|
||||||
|
|
||||||
|
# Make return purchase receipt, purchase invoice and check quantity
|
||||||
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
|
||||||
|
import make_purchase_receipt as make_purchase_receipt_return
|
||||||
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice \
|
||||||
|
import make_purchase_invoice as make_purchase_invoice_return
|
||||||
|
|
||||||
|
pr1 = make_purchase_receipt_return(is_return=1, return_against=pr.name, qty=-3, do_not_submit=True)
|
||||||
|
pr1.items[0].purchase_order = po.name
|
||||||
|
pr1.items[0].purchase_order_item = po.items[0].name
|
||||||
|
pr1.submit()
|
||||||
|
|
||||||
|
pi1= make_purchase_invoice_return(is_return=1, return_against=pi2.name, qty=-1, update_stock=1, do_not_submit=True)
|
||||||
|
pi1.items[0].purchase_order = po.name
|
||||||
|
pi1.items[0].po_detail = po.items[0].name
|
||||||
|
pi1.submit()
|
||||||
|
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
self.assertEqual(po.get("items")[0].received_qty, 5)
|
||||||
|
|
||||||
def test_make_purchase_invoice(self):
|
def test_make_purchase_invoice(self):
|
||||||
po = create_purchase_order(do_not_submit=True)
|
po = create_purchase_order(do_not_submit=True)
|
||||||
@@ -469,6 +532,13 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
self.assertEquals(se_items, supplied_items)
|
self.assertEquals(se_items, supplied_items)
|
||||||
update_backflush_based_on("BOM")
|
update_backflush_based_on("BOM")
|
||||||
|
|
||||||
|
def make_pr_against_po(po, received_qty=0):
|
||||||
|
pr = make_purchase_receipt(po)
|
||||||
|
pr.get("items")[0].qty = received_qty or 5
|
||||||
|
pr.insert()
|
||||||
|
pr.submit()
|
||||||
|
return pr
|
||||||
|
|
||||||
def make_subcontracted_item(item_code):
|
def make_subcontracted_item(item_code):
|
||||||
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
||||||
|
|
||||||
|
|||||||
@@ -287,19 +287,15 @@ def copy_attributes_to_variant(item, variant):
|
|||||||
variant.set(field.fieldname, item.get(field.fieldname))
|
variant.set(field.fieldname, item.get(field.fieldname))
|
||||||
|
|
||||||
variant.variant_of = item.name
|
variant.variant_of = item.name
|
||||||
if 'description' in allow_fields:
|
|
||||||
variant.has_variants = 0
|
|
||||||
if not variant.description:
|
|
||||||
variant.description = ""
|
|
||||||
|
|
||||||
if item.variant_based_on=='Item Attribute':
|
if not variant.description:
|
||||||
if variant.attributes:
|
variant.description = ""
|
||||||
attributes_description = ""
|
|
||||||
for d in variant.attributes:
|
|
||||||
attributes_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
|
|
||||||
|
|
||||||
if attributes_description not in variant.description:
|
if 'description' not in allow_fields:
|
||||||
variant.description += attributes_description
|
if item.variant_based_on == 'Item Attribute' and not variant.description:
|
||||||
|
variant.description = "<div><b>" + item.name + "</b></div>"
|
||||||
|
for d in variant.attributes:
|
||||||
|
variant.description += "<div><b>" + d.attribute + "</b>: " + cstr(d.attribute_value) + "</div>"
|
||||||
|
|
||||||
def make_variant_item_code(template_item_code, template_item_name, variant):
|
def make_variant_item_code(template_item_code, template_item_name, variant):
|
||||||
"""Uses template's item code and abbreviations to make variant's item code"""
|
"""Uses template's item code and abbreviations to make variant's item code"""
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ class StockController(AccountsController):
|
|||||||
"cost_center": item_row.cost_center,
|
"cost_center": item_row.cost_center,
|
||||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||||
"debit": flt(sle.stock_value_difference, 2),
|
"debit": flt(sle.stock_value_difference, 2),
|
||||||
|
"is_opening": item_row.get("is_opening"),
|
||||||
}, warehouse_account[sle.warehouse]["account_currency"]))
|
}, warehouse_account[sle.warehouse]["account_currency"]))
|
||||||
|
|
||||||
# to target warehouse / expense account
|
# to target warehouse / expense account
|
||||||
@@ -89,7 +90,8 @@ class StockController(AccountsController):
|
|||||||
"cost_center": item_row.cost_center,
|
"cost_center": item_row.cost_center,
|
||||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||||
"credit": flt(sle.stock_value_difference, 2),
|
"credit": flt(sle.stock_value_difference, 2),
|
||||||
"project": item_row.get("project") or self.get("project")
|
"project": item_row.get("project") or self.get("project"),
|
||||||
|
"is_opening": item_row.get("is_opening")
|
||||||
}))
|
}))
|
||||||
elif sle.warehouse not in warehouse_with_no_account:
|
elif sle.warehouse not in warehouse_with_no_account:
|
||||||
warehouse_with_no_account.append(sle.warehouse)
|
warehouse_with_no_account.append(sle.warehouse)
|
||||||
@@ -123,8 +125,17 @@ class StockController(AccountsController):
|
|||||||
|
|
||||||
def get_voucher_details(self, default_expense_account, default_cost_center, sle_map):
|
def get_voucher_details(self, default_expense_account, default_cost_center, sle_map):
|
||||||
if self.doctype == "Stock Reconciliation":
|
if self.doctype == "Stock Reconciliation":
|
||||||
return [frappe._dict({ "name": voucher_detail_no, "expense_account": default_expense_account,
|
reconciliation_purpose = frappe.db.get_value(self.doctype, self.name, "purpose")
|
||||||
"cost_center": default_cost_center }) for voucher_detail_no, sle in sle_map.items()]
|
is_opening = "Yes" if reconciliation_purpose == "Opening Stock" else "No"
|
||||||
|
details = []
|
||||||
|
for voucher_detail_no in sle_map:
|
||||||
|
details.append(frappe._dict({
|
||||||
|
"name": voucher_detail_no,
|
||||||
|
"expense_account": default_expense_account,
|
||||||
|
"cost_center": default_cost_center,
|
||||||
|
"is_opening": is_opening
|
||||||
|
}))
|
||||||
|
return details
|
||||||
else:
|
else:
|
||||||
details = self.get("items")
|
details = self.get("items")
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ class Lead(SellingController):
|
|||||||
def set_lead_name(self):
|
def set_lead_name(self):
|
||||||
if not self.lead_name:
|
if not self.lead_name:
|
||||||
# Check for leads being created through data import
|
# Check for leads being created through data import
|
||||||
if not self.company_name:
|
if not self.company_name and not self.flags.ignore_mandatory:
|
||||||
frappe.throw(_("A Lead requires either a person's name or an organization's name"))
|
frappe.throw(_("A Lead requires either a person's name or an organization's name"))
|
||||||
|
|
||||||
self.lead_name = self.company_name
|
self.lead_name = self.company_name
|
||||||
|
|||||||
@@ -20,10 +20,22 @@ frappe.ui.form.on("Opportunity", {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onload_post_render: function(frm) {
|
||||||
|
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||||
|
},
|
||||||
|
|
||||||
party_name: function(frm) {
|
party_name: function(frm) {
|
||||||
|
frm.toggle_display("contact_info", frm.doc.party_name);
|
||||||
|
|
||||||
if (frm.doc.opportunity_from == "Customer") {
|
if (frm.doc.opportunity_from == "Customer") {
|
||||||
frm.trigger('set_contact_link');
|
frm.trigger('set_contact_link');
|
||||||
erpnext.utils.get_party_details(frm);
|
erpnext.utils.get_party_details(frm);
|
||||||
|
} else if (frm.doc.opportunity_from == "Lead") {
|
||||||
|
erpnext.utils.map_current_doc({
|
||||||
|
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
||||||
|
source_name: frm.doc.party_name,
|
||||||
|
frm: frm
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -82,9 +94,9 @@ frappe.ui.form.on("Opportunity", {
|
|||||||
|
|
||||||
set_contact_link: function(frm) {
|
set_contact_link: function(frm) {
|
||||||
if(frm.doc.opportunity_from == "Customer" && frm.doc.party_name) {
|
if(frm.doc.opportunity_from == "Customer" && frm.doc.party_name) {
|
||||||
frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'}
|
frappe.dynamic_link = {doc: frm.doc, fieldname: 'party_name', doctype: 'Customer'}
|
||||||
} else if(frm.doc.opportunity_from == "Lead" && frm.doc.party_name) {
|
} else if(frm.doc.opportunity_from == "Lead" && frm.doc.party_name) {
|
||||||
frappe.dynamic_link = {doc: frm.doc, fieldname: 'lead', doctype: 'Lead'}
|
frappe.dynamic_link = {doc: frm.doc, fieldname: 'party_name', doctype: 'Lead'}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -138,12 +150,14 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
$.each([["lead", "lead"],
|
me.frm.set_query('contact_person', erpnext.queries['contact_query'])
|
||||||
["customer", "customer"],
|
|
||||||
["contact_person", "contact_query"]],
|
if (me.frm.doc.opportunity_from == "Lead") {
|
||||||
function(i, opts) {
|
me.frm.set_query('party_name', erpnext.queries['lead']);
|
||||||
me.frm.set_query(opts[0], erpnext.queries[opts[1]]);
|
}
|
||||||
});
|
else if (me.frm.doc.opportunity_from == "Cuatomer") {
|
||||||
|
me.frm.set_query('party_name', erpnext.queries['customer']);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
create_quotation: function() {
|
create_quotation: function() {
|
||||||
@@ -156,11 +170,6 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
|
|||||||
|
|
||||||
$.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
|
$.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
|
||||||
|
|
||||||
cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
|
|
||||||
if(doc.opportunity_from == 'Lead' && doc.party_name)
|
|
||||||
cur_frm.cscript.lead(doc, cdt, cdn);
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.item_code = function(doc, cdt, cdn) {
|
cur_frm.cscript.item_code = function(doc, cdt, cdn) {
|
||||||
var d = locals[cdt][cdn];
|
var d = locals[cdt][cdn];
|
||||||
if (d.item_code) {
|
if (d.item_code) {
|
||||||
@@ -179,15 +188,6 @@ cur_frm.cscript.item_code = function(doc, cdt, cdn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.cscript.lead = function(doc, cdt, cdn) {
|
|
||||||
cur_frm.toggle_display("contact_info", doc.party_name);
|
|
||||||
erpnext.utils.map_current_doc({
|
|
||||||
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
|
||||||
source_name: cur_frm.doc.party_name,
|
|
||||||
frm: cur_frm
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript['Declare Opportunity Lost'] = function() {
|
cur_frm.cscript['Declare Opportunity Lost'] = function() {
|
||||||
var dialog = new frappe.ui.Dialog({
|
var dialog = new frappe.ui.Dialog({
|
||||||
title: __("Set as Lost"),
|
title: __("Set as Lost"),
|
||||||
|
|||||||
@@ -99,7 +99,7 @@
|
|||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 1,
|
||||||
"label": "Opportunity From",
|
"label": "Opportunity From",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@@ -878,7 +878,7 @@
|
|||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"collapsible_depends_on": "next_contact_by",
|
"collapsible_depends_on": "next_contact_by",
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"depends_on": "eval:doc.lead || doc.customer",
|
"depends_on": "eval:doc.party_name",
|
||||||
"fetch_if_empty": 0,
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "contact_info",
|
"fieldname": "contact_info",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@@ -912,7 +912,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"depends_on": "eval:doc.customer || doc.lead",
|
"depends_on": "eval:doc.party_name",
|
||||||
"fetch_if_empty": 0,
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "customer_address",
|
"fieldname": "customer_address",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@@ -1083,7 +1083,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"depends_on": "eval:doc.lead || doc.customer",
|
"depends_on": "eval:doc.party_name",
|
||||||
"fetch_if_empty": 0,
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "contact_person",
|
"fieldname": "contact_person",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@@ -1150,7 +1150,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"depends_on": "eval:doc.lead || doc.customer",
|
"depends_on": "eval:doc.party_name",
|
||||||
"fetch_if_empty": 0,
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "contact_email",
|
"fieldname": "contact_email",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
@@ -1183,7 +1183,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"depends_on": "eval:doc.lead || doc.customer",
|
"depends_on": "eval:doc.party_name",
|
||||||
"fetch_if_empty": 0,
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "contact_mobile",
|
"fieldname": "contact_mobile",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
@@ -1468,7 +1468,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2019-05-11 19:22:33.533487",
|
"modified": "2019-05-17 19:03:32.740910",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Opportunity",
|
"name": "Opportunity",
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ def get_healthcare_services_to_invoice(patient):
|
|||||||
'service': service_item, 'rate': practitioner_charge,
|
'service': service_item, 'rate': practitioner_charge,
|
||||||
'income_account': income_account})
|
'income_account': income_account})
|
||||||
|
|
||||||
lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False})
|
lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False, 'docstatus': 1})
|
||||||
if lab_tests:
|
if lab_tests:
|
||||||
for lab_test in lab_tests:
|
for lab_test in lab_tests:
|
||||||
lab_test_obj = frappe.get_doc("Lab Test", lab_test['name'])
|
lab_test_obj = frappe.get_doc("Lab Test", lab_test['name'])
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ from frappe import _
|
|||||||
from frappe.utils import getdate, date_diff
|
from frappe.utils import getdate, date_diff
|
||||||
|
|
||||||
class AdditionalSalary(Document):
|
class AdditionalSalary(Document):
|
||||||
|
def before_insert(self):
|
||||||
|
if frappe.db.exists("Additional Salary", {"employee": self.employee, "salary_component": self.salary_component,
|
||||||
|
"amount": self.amount, "payroll_date": self.payroll_date, "company": self.company}):
|
||||||
|
|
||||||
|
frappe.throw(_("Additional Salary Component Exists."))
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_dates()
|
self.validate_dates()
|
||||||
if self.amount < 0:
|
if self.amount < 0:
|
||||||
|
|||||||
13
erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py
Normal file
13
erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
'fieldname': 'reference_name',
|
||||||
|
'transactions': [
|
||||||
|
{
|
||||||
|
'label': _('Payment'),
|
||||||
|
'items': ['Payment Entry']
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -172,18 +172,20 @@ frappe.ui.form.on('Loan', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
mode_of_payment: function (frm) {
|
mode_of_payment: function (frm) {
|
||||||
frappe.call({
|
if (frm.doc.mode_of_payment && frm.doc.company) {
|
||||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
|
frappe.call({
|
||||||
args: {
|
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
|
||||||
"mode_of_payment": frm.doc.mode_of_payment,
|
args: {
|
||||||
"company": frm.doc.company
|
"mode_of_payment": frm.doc.mode_of_payment,
|
||||||
},
|
"company": frm.doc.company
|
||||||
callback: function (r, rt) {
|
},
|
||||||
if (r.message) {
|
callback: function (r, rt) {
|
||||||
frm.set_value("payment_account", r.message.account);
|
if (r.message) {
|
||||||
|
frm.set_value("payment_account", r.message.account);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
loan_application: function (frm) {
|
loan_application: function (frm) {
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ class TrainingFeedback(Document):
|
|||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
training_event = frappe.get_doc("Training Event", self.training_event)
|
training_event = frappe.get_doc("Training Event", self.training_event)
|
||||||
status = None
|
event_status = None
|
||||||
for e in training_event.employees:
|
for e in training_event.employees:
|
||||||
if e.employee == self.employee:
|
if e.employee == self.employee:
|
||||||
status = 'Feedback Submitted'
|
event_status = 'Feedback Submitted'
|
||||||
break
|
break
|
||||||
|
|
||||||
if status:
|
if event_status:
|
||||||
frappe.db.set_value("Training Event", self.training_event, "status", status)
|
frappe.db.set_value("Training Event", self.training_event, "event_status", event_status)
|
||||||
|
|||||||
@@ -31,5 +31,9 @@ frappe.ui.form.on('Blanket Order', {
|
|||||||
});
|
});
|
||||||
}).addClass("btn-primary");
|
}).addClass("btn-primary");
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onload_post_render: function(frm) {
|
||||||
|
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -205,7 +205,12 @@ var get_bom_material_detail= function(doc, cdt, cdn, scrap_items) {
|
|||||||
'item_code': d.item_code,
|
'item_code': d.item_code,
|
||||||
'bom_no': d.bom_no != null ? d.bom_no: '',
|
'bom_no': d.bom_no != null ? d.bom_no: '',
|
||||||
"scrap_items": scrap_items,
|
"scrap_items": scrap_items,
|
||||||
'qty': d.qty
|
'qty': d.qty,
|
||||||
|
"stock_qty": d.stock_qty,
|
||||||
|
"include_item_in_manufacturing": d.include_item_in_manufacturing,
|
||||||
|
"uom": d.uom,
|
||||||
|
"stock_uom": d.stock_uom,
|
||||||
|
"conversion_factor": d.conversion_factor
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
d = locals[cdt][cdn];
|
d = locals[cdt][cdn];
|
||||||
|
|||||||
@@ -170,13 +170,14 @@ class BOM(WebsiteGenerator):
|
|||||||
rate = self.get_valuation_rate(arg)
|
rate = self.get_valuation_rate(arg)
|
||||||
elif arg:
|
elif arg:
|
||||||
if arg.get('bom_no') and self.set_rate_of_sub_assembly_item_based_on_bom:
|
if arg.get('bom_no') and self.set_rate_of_sub_assembly_item_based_on_bom:
|
||||||
rate = self.get_bom_unitcost(arg['bom_no'])
|
rate = self.get_bom_unitcost(arg['bom_no']) * (arg.get("conversion_factor") or 1)
|
||||||
else:
|
else:
|
||||||
if self.rm_cost_as_per == 'Valuation Rate':
|
if self.rm_cost_as_per == 'Valuation Rate':
|
||||||
rate = self.get_valuation_rate(arg)
|
rate = self.get_valuation_rate(arg) * (arg.get("conversion_factor") or 1)
|
||||||
elif self.rm_cost_as_per == 'Last Purchase Rate':
|
elif self.rm_cost_as_per == 'Last Purchase Rate':
|
||||||
rate = arg.get('last_purchase_rate') \
|
rate = (arg.get('last_purchase_rate') \
|
||||||
or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")
|
or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")) \
|
||||||
|
* (arg.get("conversion_factor") or 1)
|
||||||
elif self.rm_cost_as_per == "Price List":
|
elif self.rm_cost_as_per == "Price List":
|
||||||
if not self.buying_price_list:
|
if not self.buying_price_list:
|
||||||
frappe.throw(_("Please select Price List"))
|
frappe.throw(_("Please select Price List"))
|
||||||
@@ -189,7 +190,7 @@ class BOM(WebsiteGenerator):
|
|||||||
"transaction_type": "buying",
|
"transaction_type": "buying",
|
||||||
"company": self.company,
|
"company": self.company,
|
||||||
"currency": self.currency,
|
"currency": self.currency,
|
||||||
"conversion_rate": self.conversion_rate or 1,
|
"conversion_rate": 1, # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function
|
||||||
"conversion_factor": arg.get("conversion_factor") or 1,
|
"conversion_factor": arg.get("conversion_factor") or 1,
|
||||||
"plc_conversion_rate": 1,
|
"plc_conversion_rate": 1,
|
||||||
"ignore_party": True
|
"ignore_party": True
|
||||||
@@ -207,7 +208,7 @@ class BOM(WebsiteGenerator):
|
|||||||
frappe.msgprint(_("{0} not found for item {1}")
|
frappe.msgprint(_("{0} not found for item {1}")
|
||||||
.format(self.rm_cost_as_per, arg["item_code"]), alert=True)
|
.format(self.rm_cost_as_per, arg["item_code"]), alert=True)
|
||||||
|
|
||||||
return flt(rate)
|
return flt(rate) / (self.conversion_rate or 1)
|
||||||
|
|
||||||
def update_cost(self, update_parent=True, from_child_bom=False, save=True):
|
def update_cost(self, update_parent=True, from_child_bom=False, save=True):
|
||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from frappe.utils import cstr
|
|||||||
from frappe.test_runner import make_test_records
|
from frappe.test_runner import make_test_records
|
||||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
|
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
|
||||||
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
|
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
|
||||||
|
from six import string_types
|
||||||
|
|
||||||
test_records = frappe.get_test_records('BOM')
|
test_records = frappe.get_test_records('BOM')
|
||||||
|
|
||||||
@@ -63,16 +64,8 @@ class TestBOM(unittest.TestCase):
|
|||||||
and item_code='_Test Item 2' and docstatus=1 and parenttype='BOM'""")
|
and item_code='_Test Item 2' and docstatus=1 and parenttype='BOM'""")
|
||||||
rm_rate = rm_rate[0][0] if rm_rate else 0
|
rm_rate = rm_rate[0][0] if rm_rate else 0
|
||||||
|
|
||||||
# update valuation rate of item '_Test Item 2'
|
# Reset item valuation rate
|
||||||
warehouse_list = frappe.db.sql_list("""select warehouse from `tabBin`
|
reset_item_valuation_rate(item_code='_Test Item 2', qty=200, rate=rm_rate + 10)
|
||||||
where item_code='_Test Item 2' and actual_qty > 0""")
|
|
||||||
|
|
||||||
if not warehouse_list:
|
|
||||||
warehouse_list.append("_Test Warehouse - _TC")
|
|
||||||
|
|
||||||
for warehouse in warehouse_list:
|
|
||||||
create_stock_reconciliation(item_code="_Test Item 2", warehouse=warehouse,
|
|
||||||
qty=200, rate=rm_rate + 10)
|
|
||||||
|
|
||||||
# update cost of all BOMs based on latest valuation rate
|
# update cost of all BOMs based on latest valuation rate
|
||||||
update_cost()
|
update_cost()
|
||||||
@@ -96,7 +89,7 @@ class TestBOM(unittest.TestCase):
|
|||||||
self.assertEqual(bom.base_raw_material_cost, 480000)
|
self.assertEqual(bom.base_raw_material_cost, 480000)
|
||||||
self.assertEqual(bom.base_total_cost, 486000)
|
self.assertEqual(bom.base_total_cost, 486000)
|
||||||
|
|
||||||
def test_bom_cost_multi_uom_multi_currency(self):
|
def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self):
|
||||||
frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependant", 1)
|
frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependant", 1)
|
||||||
for item_code, rate in (("_Test Item", 3600), ("_Test Item Home Desktop Manufactured", 3000)):
|
for item_code, rate in (("_Test Item", 3600), ("_Test Item Home Desktop Manufactured", 3000)):
|
||||||
frappe.db.sql("delete from `tabItem Price` where price_list='_Test Price List' and item_code=%s",
|
frappe.db.sql("delete from `tabItem Price` where price_list='_Test Price List' and item_code=%s",
|
||||||
@@ -131,5 +124,35 @@ class TestBOM(unittest.TestCase):
|
|||||||
self.assertEqual(bom.base_raw_material_cost, 27000)
|
self.assertEqual(bom.base_raw_material_cost, 27000)
|
||||||
self.assertEqual(bom.base_total_cost, 33000)
|
self.assertEqual(bom.base_total_cost, 33000)
|
||||||
|
|
||||||
|
def test_bom_cost_multi_uom_based_on_valuation_rate(self):
|
||||||
|
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 = 6
|
||||||
|
bom.insert()
|
||||||
|
|
||||||
|
reset_item_valuation_rate(item_code='_Test Item', qty=200, rate=200)
|
||||||
|
|
||||||
|
bom.update_cost()
|
||||||
|
|
||||||
|
self.assertEqual(bom.items[0].rate, 20)
|
||||||
|
|
||||||
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})
|
||||||
|
|
||||||
|
def reset_item_valuation_rate(item_code, warehouse_list=None, qty=None, rate=None):
|
||||||
|
if warehouse_list and isinstance(warehouse_list, string_types):
|
||||||
|
warehouse_list = [warehouse_list]
|
||||||
|
|
||||||
|
if not warehouse_list:
|
||||||
|
warehouse_list = frappe.db.sql_list("""
|
||||||
|
select warehouse from `tabBin`
|
||||||
|
where item_code=%s and actual_qty > 0
|
||||||
|
""", item_code)
|
||||||
|
|
||||||
|
if not warehouse_list:
|
||||||
|
warehouse_list.append("_Test Warehouse - _TC")
|
||||||
|
|
||||||
|
for warehouse in warehouse_list:
|
||||||
|
create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=qty, rate=rate)
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class Project(Document):
|
|||||||
'name': ("not in", self.deleted_task_list)
|
'name': ("not in", self.deleted_task_list)
|
||||||
})
|
})
|
||||||
|
|
||||||
return frappe.get_all("Task", "*", filters, order_by="exp_start_date asc")
|
return frappe.get_all("Task", "*", filters, order_by="exp_start_date asc, status asc")
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_project_name()
|
self.validate_project_name()
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
}
|
}
|
||||||
if(frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "item_code")) {
|
if(frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "item_code")) {
|
||||||
this.setup_item_selector();
|
this.setup_item_selector();
|
||||||
|
this.frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1120,6 +1121,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
return {
|
return {
|
||||||
"items": this._get_item_list(item),
|
"items": this._get_item_list(item),
|
||||||
"customer": me.frm.doc.customer || me.frm.doc.party_name,
|
"customer": me.frm.doc.customer || me.frm.doc.party_name,
|
||||||
|
"quotation_to": me.doc.frm.quotation_to,
|
||||||
"customer_group": me.frm.doc.customer_group,
|
"customer_group": me.frm.doc.customer_group,
|
||||||
"territory": me.frm.doc.territory,
|
"territory": me.frm.doc.territory,
|
||||||
"supplier": me.frm.doc.supplier,
|
"supplier": me.frm.doc.supplier,
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class Gstr1Report(object):
|
|||||||
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
|
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
|
||||||
invoice_details = self.invoices.get(inv)
|
invoice_details = self.invoices.get(inv)
|
||||||
for rate, items in items_based_on_rate.items():
|
for rate, items in items_based_on_rate.items():
|
||||||
row = self.get_row_data_for_invoice(inv, invoice_details, rate, items)
|
row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items)
|
||||||
|
|
||||||
if self.filters.get("type_of_business") == "CDNR":
|
if self.filters.get("type_of_business") == "CDNR":
|
||||||
row.append("Y" if invoice_details.posting_date <= date(2017, 7, 1) else "N")
|
row.append("Y" if invoice_details.posting_date <= date(2017, 7, 1) else "N")
|
||||||
@@ -118,7 +118,7 @@ class Gstr1Report(object):
|
|||||||
for item_code, net_amount in self.invoice_items.get(invoice).items() if item_code in items])
|
for item_code, net_amount in self.invoice_items.get(invoice).items() if item_code in items])
|
||||||
row += [tax_rate or 0, taxable_value]
|
row += [tax_rate or 0, taxable_value]
|
||||||
|
|
||||||
return row
|
return row, taxable_value
|
||||||
|
|
||||||
def get_invoice_data(self):
|
def get_invoice_data(self):
|
||||||
self.invoices = frappe._dict()
|
self.invoices = frappe._dict()
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
|
|||||||
erpnext.utils.get_party_details(this.frm, null, null, function() {
|
erpnext.utils.get_party_details(this.frm, null, null, function() {
|
||||||
me.apply_price_list();
|
me.apply_price_list();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(me.frm.doc.quotation_to=="Lead" && me.frm.doc.party_name) {
|
||||||
|
me.frm.trigger("get_lead_details");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
refresh: function(doc, dt, dn) {
|
refresh: function(doc, dt, dn) {
|
||||||
this._super(doc, dt, dn);
|
this._super(doc, dt, dn);
|
||||||
@@ -87,10 +91,10 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
|
|||||||
this.frm.add_custom_button(__('Opportunity'),
|
this.frm.add_custom_button(__('Opportunity'),
|
||||||
function() {
|
function() {
|
||||||
var setters = {};
|
var setters = {};
|
||||||
if(me.frm.doc.customer) {
|
if(me.frm.doc.quotation_to == "Customer" && me.frm.doc.party_name) {
|
||||||
setters.customer = me.frm.doc.customer || undefined;
|
setters.customer = me.frm.doc.party_name || undefined;
|
||||||
} else if (me.frm.doc.lead) {
|
} else if (me.frm.doc.quotation_to == "Lead" && me.frm.doc.party_name) {
|
||||||
setters.lead = me.frm.doc.lead || undefined;
|
setters.lead = me.frm.doc.party_name || undefined;
|
||||||
}
|
}
|
||||||
erpnext.utils.map_current_doc({
|
erpnext.utils.map_current_doc({
|
||||||
method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation",
|
method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation",
|
||||||
@@ -162,16 +166,16 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
lead: function() {
|
get_lead_details: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
if(!this.frm.doc.lead) {
|
if(!this.frm.doc.quotation_to === "Lead") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.crm.doctype.lead.lead.get_lead_details",
|
method: "erpnext.crm.doctype.lead.lead.get_lead_details",
|
||||||
args: {
|
args: {
|
||||||
'lead': this.frm.doc.lead,
|
'lead': this.frm.doc.party_name,
|
||||||
'posting_date': this.frm.doc.transaction_date,
|
'posting_date': this.frm.doc.transaction_date,
|
||||||
'company': this.frm.doc.company,
|
'company': this.frm.doc.company,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -77,10 +77,6 @@ frappe.ui.form.on("Sales Order", {
|
|||||||
if(!d.delivery_date) d.delivery_date = frm.doc.delivery_date;
|
if(!d.delivery_date) d.delivery_date = frm.doc.delivery_date;
|
||||||
});
|
});
|
||||||
refresh_field("items");
|
refresh_field("items");
|
||||||
},
|
|
||||||
|
|
||||||
onload_post_render: function(frm) {
|
|
||||||
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,44 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
so.load_from_db()
|
so.load_from_db()
|
||||||
self.assertEqual(so.get("items")[0].delivered_qty, 9)
|
self.assertEqual(so.get("items")[0].delivered_qty, 9)
|
||||||
|
|
||||||
|
def test_return_against_sales_order(self):
|
||||||
|
so = make_sales_order()
|
||||||
|
|
||||||
|
dn = create_dn_against_so(so.name, 6)
|
||||||
|
|
||||||
|
so.load_from_db()
|
||||||
|
self.assertEqual(so.get("items")[0].delivered_qty, 6)
|
||||||
|
|
||||||
|
# Check delivered_qty after make_sales_invoice with update_stock checked
|
||||||
|
si2 = make_sales_invoice(so.name)
|
||||||
|
si2.set("update_stock", 1)
|
||||||
|
si2.get("items")[0].qty = 3
|
||||||
|
si2.insert()
|
||||||
|
si2.submit()
|
||||||
|
|
||||||
|
so.load_from_db()
|
||||||
|
|
||||||
|
self.assertEqual(so.get("items")[0].delivered_qty, 9)
|
||||||
|
|
||||||
|
# Make return deliver note, sales invoice and check quantity
|
||||||
|
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||||
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
|
|
||||||
|
dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-3, do_not_submit=True)
|
||||||
|
dn1.items[0].against_sales_order = so.name
|
||||||
|
dn1.items[0].so_detail = so.items[0].name
|
||||||
|
dn1.submit()
|
||||||
|
|
||||||
|
si1 = create_sales_invoice(is_return=1, return_against=si2.name, qty=-1, update_stock=1, do_not_submit=True)
|
||||||
|
si1.items[0].sales_order = so.name
|
||||||
|
si1.items[0].so_detail = so.items[0].name
|
||||||
|
si1.submit()
|
||||||
|
|
||||||
|
|
||||||
|
so.load_from_db()
|
||||||
|
self.assertEqual(so.get("items")[0].delivered_qty, 5)
|
||||||
|
|
||||||
|
|
||||||
def test_reserved_qty_for_partial_delivery(self):
|
def test_reserved_qty_for_partial_delivery(self):
|
||||||
make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
|
make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
|
||||||
existing_reserved_qty = get_reserved_qty()
|
existing_reserved_qty = get_reserved_qty()
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ def execute(filters=None):
|
|||||||
columns = get_columns()
|
columns = get_columns()
|
||||||
iwq_map = get_item_warehouse_quantity_map()
|
iwq_map = get_item_warehouse_quantity_map()
|
||||||
item_map = get_item_details()
|
item_map = get_item_details()
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
for sbom, warehouse in iwq_map.items():
|
for sbom, warehouse in iwq_map.items():
|
||||||
total = 0
|
total = 0
|
||||||
@@ -20,7 +19,7 @@ def execute(filters=None):
|
|||||||
for wh, item_qty in warehouse.items():
|
for wh, item_qty in warehouse.items():
|
||||||
total += 1
|
total += 1
|
||||||
row = [sbom, item_map.get(sbom).item_name, item_map.get(sbom).description,
|
row = [sbom, item_map.get(sbom).item_name, item_map.get(sbom).description,
|
||||||
item_map.get(sbom).stock_uom, wh]
|
item_map.get(sbom).stock_uom, wh]
|
||||||
available_qty = item_qty
|
available_qty = item_qty
|
||||||
total_qty += flt(available_qty)
|
total_qty += flt(available_qty)
|
||||||
row += [available_qty]
|
row += [available_qty]
|
||||||
@@ -30,54 +29,38 @@ def execute(filters=None):
|
|||||||
if (total == len(warehouse)):
|
if (total == len(warehouse)):
|
||||||
row = ["", "", "Total", "", "", total_qty]
|
row = ["", "", "Total", "", "", total_qty]
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
def get_columns():
|
def get_columns():
|
||||||
columns = ["Item Code:Link/Item:100", "Item Name::100", "Description::120", \
|
columns = ["Item Code:Link/Item:100", "Item Name::100", "Description::120", \
|
||||||
"UOM:Link/UOM:80", "Warehouse:Link/Warehouse:100", "Quantity::100"]
|
"UOM:Link/UOM:80", "Warehouse:Link/Warehouse:100", "Quantity::100"]
|
||||||
|
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
def get_product_bundle_items():
|
|
||||||
sbom_item_map = {}
|
|
||||||
for sbom in frappe.db.sql("""select pb.new_item_code as parent, pbi.item_code, pbi.qty
|
|
||||||
from `tabProduct Bundle Item` as pbi, `tabProduct Bundle` as pb
|
|
||||||
where pb.docstatus < 2 and pb.name = pbi.parent""", as_dict=1):
|
|
||||||
sbom_item_map.setdefault(sbom.parent, {}).setdefault(sbom.item_code, sbom.qty)
|
|
||||||
|
|
||||||
return sbom_item_map
|
|
||||||
|
|
||||||
def get_item_details():
|
def get_item_details():
|
||||||
item_map = {}
|
item_map = {}
|
||||||
for item in frappe.db.sql("""select name, item_name, description, stock_uom
|
for item in frappe.db.sql("""SELECT name, item_name, description, stock_uom
|
||||||
from `tabItem`""", as_dict=1):
|
from `tabItem`""", as_dict=1):
|
||||||
item_map.setdefault(item.name, item)
|
item_map.setdefault(item.name, item)
|
||||||
|
|
||||||
return item_map
|
return item_map
|
||||||
|
|
||||||
def get_item_warehouse_quantity():
|
|
||||||
iwq_map = {}
|
|
||||||
bin = frappe.db.sql("""select item_code, warehouse, actual_qty from `tabBin`
|
|
||||||
where actual_qty > 0""")
|
|
||||||
for item, wh, qty in bin:
|
|
||||||
iwq_map.setdefault(item, {}).setdefault(wh, qty)
|
|
||||||
|
|
||||||
return iwq_map
|
|
||||||
|
|
||||||
def get_item_warehouse_quantity_map():
|
def get_item_warehouse_quantity_map():
|
||||||
query = """SELECT parent, warehouse, MIN(qty) AS qty
|
query = """SELECT parent, warehouse, MIN(qty) AS qty
|
||||||
FROM (SELECT b.parent, bi.item_code, bi.warehouse,
|
FROM (SELECT b.parent, bi.item_code, bi.warehouse,
|
||||||
sum(bi.projected_qty) / b.qty AS qty
|
sum(bi.projected_qty) / b.qty AS qty
|
||||||
FROM tabBin AS bi, (SELECT b.parent, b.item_code, b.qty, w.name
|
FROM tabBin AS bi, (SELECT pb.new_item_code as parent, b.item_code, b.qty, w.name
|
||||||
FROM `tabProduct Bundle Item` b, `tabWarehouse` w) AS b
|
FROM `tabProduct Bundle Item` b, `tabWarehouse` w,
|
||||||
|
`tabProduct Bundle` pb
|
||||||
|
where b.parent = pb.name) AS b
|
||||||
WHERE bi.item_code = b.item_code
|
WHERE bi.item_code = b.item_code
|
||||||
AND bi.warehouse = b.name
|
AND bi.warehouse = b.name
|
||||||
GROUP BY b.parent, b.item_code, bi.warehouse
|
GROUP BY b.parent, b.item_code, bi.warehouse
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT b.parent, b.item_code, b.name, 0 AS qty
|
SELECT b.parent, b.item_code, b.name, 0 AS qty
|
||||||
FROM (SELECT b.parent, b.item_code, b.qty, w.name
|
FROM (SELECT pb.new_item_code as parent, b.item_code, b.qty, w.name
|
||||||
FROM `tabProduct Bundle Item` b, `tabWarehouse` w) AS b
|
FROM `tabProduct Bundle Item` b, `tabWarehouse` w,
|
||||||
|
`tabProduct Bundle` pb
|
||||||
|
where b.parent = pb.name) AS b
|
||||||
WHERE NOT EXISTS(SELECT *
|
WHERE NOT EXISTS(SELECT *
|
||||||
FROM `tabBin` AS bi
|
FROM `tabBin` AS bi
|
||||||
WHERE bi.item_code = b.item_code
|
WHERE bi.item_code = b.item_code
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class ItemGroup(NestedSet, WebsiteGenerator):
|
|||||||
limit=context.page_length + 1, search=frappe.form_dict.get("search")),
|
limit=context.page_length + 1, search=frappe.form_dict.get("search")),
|
||||||
"parents": get_parent_item_groups(self.parent_item_group),
|
"parents": get_parent_item_groups(self.parent_item_group),
|
||||||
"title": self.name,
|
"title": self.name,
|
||||||
"products_as_list": cint(frappe.db.get_single_value('Website Settings', 'products_as_list'))
|
"products_as_list": cint(frappe.db.get_single_value('Products Settings', 'products_as_list'))
|
||||||
})
|
})
|
||||||
|
|
||||||
if self.slideshow:
|
if self.slideshow:
|
||||||
@@ -114,8 +114,9 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non
|
|||||||
data = frappe.db.sql(query, {"product_group": product_group,"search": search, "today": nowdate()}, as_dict=1)
|
data = frappe.db.sql(query, {"product_group": product_group,"search": search, "today": nowdate()}, as_dict=1)
|
||||||
data = adjust_qty_for_expired_items(data)
|
data = adjust_qty_for_expired_items(data)
|
||||||
|
|
||||||
for item in data:
|
if cint(frappe.db.get_single_value("Shopping Cart Settings", "enabled")):
|
||||||
set_product_info_for_website(item)
|
for item in data:
|
||||||
|
set_product_info_for_website(item)
|
||||||
|
|
||||||
return [get_item_for_list_in_html(r) for r in data]
|
return [get_item_for_list_in_html(r) for r in data]
|
||||||
|
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ class TestBatch(unittest.TestCase):
|
|||||||
item = item_name,
|
item = item_name,
|
||||||
batch_id = batch_name
|
batch_id = batch_name
|
||||||
)).insert(ignore_permissions=True)
|
)).insert(ignore_permissions=True)
|
||||||
batch.submit()
|
batch.save()
|
||||||
|
|
||||||
stock_entry = frappe.get_doc(dict(
|
stock_entry = frappe.get_doc(dict(
|
||||||
doctype = 'Stock Entry',
|
doctype = 'Stock Entry',
|
||||||
|
|||||||
@@ -52,16 +52,20 @@ class DeliveryNote(SellingController):
|
|||||||
'percent_join_field': 'against_sales_invoice',
|
'percent_join_field': 'against_sales_invoice',
|
||||||
'overflow_type': 'delivery',
|
'overflow_type': 'delivery',
|
||||||
'no_tolerance': 1
|
'no_tolerance': 1
|
||||||
},
|
|
||||||
{
|
|
||||||
'source_dt': 'Delivery Note Item',
|
|
||||||
'target_dt': 'Sales Order Item',
|
|
||||||
'join_field': 'so_detail',
|
|
||||||
'target_field': 'returned_qty',
|
|
||||||
'target_parent_dt': 'Sales Order',
|
|
||||||
'source_field': '-1 * qty',
|
|
||||||
'extra_cond': """ and exists (select name from `tabDelivery Note` where name=`tabDelivery Note Item`.parent and is_return=1)"""
|
|
||||||
}]
|
}]
|
||||||
|
if cint(self.is_return):
|
||||||
|
self.status_updater.append({
|
||||||
|
'source_dt': 'Delivery Note Item',
|
||||||
|
'target_dt': 'Sales Order Item',
|
||||||
|
'join_field': 'so_detail',
|
||||||
|
'target_field': 'returned_qty',
|
||||||
|
'target_parent_dt': 'Sales Order',
|
||||||
|
'source_field': '-1 * qty',
|
||||||
|
'second_source_dt': 'Sales Invoice Item',
|
||||||
|
'second_source_field': '-1 * qty',
|
||||||
|
'second_join_field': 'so_detail',
|
||||||
|
'extra_cond': """ and exists (select name from `tabDelivery Note` where name=`tabDelivery Note Item`.parent and is_return=1)"""
|
||||||
|
})
|
||||||
|
|
||||||
def before_print(self):
|
def before_print(self):
|
||||||
def toggle_print_hide(meta, fieldname):
|
def toggle_print_hide(meta, fieldname):
|
||||||
|
|||||||
@@ -365,10 +365,18 @@ $.extend(erpnext.item, {
|
|||||||
show_modal_for_manufacturers: function(frm) {
|
show_modal_for_manufacturers: function(frm) {
|
||||||
var dialog = new frappe.ui.Dialog({
|
var dialog = new frappe.ui.Dialog({
|
||||||
fields: [
|
fields: [
|
||||||
{fieldtype:'Link', options:'Manufacturer',
|
{
|
||||||
reqd:1, label:'Manufacturer'},
|
fieldtype: 'Link',
|
||||||
{fieldtype:'Data', label:'Manufacturer Part Number',
|
fieldname: 'manufacturer',
|
||||||
fieldname: 'manufacturer_part_no'},
|
options: 'Manufacturer',
|
||||||
|
label: 'Manufacturer',
|
||||||
|
reqd: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: 'Data',
|
||||||
|
label: 'Manufacturer Part Number',
|
||||||
|
fieldname: 'manufacturer_part_no'
|
||||||
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -379,7 +387,7 @@ $.extend(erpnext.item, {
|
|||||||
// call the server to make the variant
|
// call the server to make the variant
|
||||||
data.template = frm.doc.name;
|
data.template = frm.doc.name;
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:"erpnext.controllers.item_variant.get_variant",
|
method: "erpnext.controllers.item_variant.get_variant",
|
||||||
args: data,
|
args: data,
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
var doclist = frappe.model.sync(r.message);
|
var doclist = frappe.model.sync(r.message);
|
||||||
@@ -454,7 +462,7 @@ $.extend(erpnext.item, {
|
|||||||
|
|
||||||
me.multiple_variant_dialog.hide();
|
me.multiple_variant_dialog.hide();
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:"erpnext.controllers.item_variant.enqueue_multiple_variant_creation",
|
method: "erpnext.controllers.item_variant.enqueue_multiple_variant_creation",
|
||||||
args: {
|
args: {
|
||||||
"item": frm.doc.name,
|
"item": frm.doc.name,
|
||||||
"args": selected_attributes
|
"args": selected_attributes
|
||||||
@@ -504,9 +512,9 @@ $.extend(erpnext.item, {
|
|||||||
let p = new Promise(resolve => {
|
let p = new Promise(resolve => {
|
||||||
if(!d.numeric_values) {
|
if(!d.numeric_values) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:"frappe.client.get_list",
|
method: "frappe.client.get_list",
|
||||||
args:{
|
args: {
|
||||||
doctype:"Item Attribute Value",
|
doctype: "Item Attribute Value",
|
||||||
filters: [
|
filters: [
|
||||||
["parent","=", d.attribute]
|
["parent","=", d.attribute]
|
||||||
],
|
],
|
||||||
@@ -524,9 +532,9 @@ $.extend(erpnext.item, {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:"frappe.client.get",
|
method: "frappe.client.get",
|
||||||
args:{
|
args: {
|
||||||
doctype:"Item Attribute",
|
doctype: "Item Attribute",
|
||||||
name: d.attribute
|
name: d.attribute
|
||||||
}
|
}
|
||||||
}).then((r) => {
|
}).then((r) => {
|
||||||
@@ -589,7 +597,7 @@ $.extend(erpnext.item, {
|
|||||||
var args = d.get_values();
|
var args = d.get_values();
|
||||||
if(!args) return;
|
if(!args) return;
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:"erpnext.controllers.item_variant.get_variant",
|
method: "erpnext.controllers.item_variant.get_variant",
|
||||||
args: {
|
args: {
|
||||||
"template": frm.doc.name,
|
"template": frm.doc.name,
|
||||||
"args": d.get_values()
|
"args": d.get_values()
|
||||||
@@ -611,7 +619,7 @@ $.extend(erpnext.item, {
|
|||||||
} else {
|
} else {
|
||||||
d.hide();
|
d.hide();
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:"erpnext.controllers.item_variant.create_variant",
|
method: "erpnext.controllers.item_variant.create_variant",
|
||||||
args: {
|
args: {
|
||||||
"item": frm.doc.name,
|
"item": frm.doc.name,
|
||||||
"args": d.get_values()
|
"args": d.get_values()
|
||||||
@@ -649,8 +657,8 @@ $.extend(erpnext.item, {
|
|||||||
.on('input', function(e) {
|
.on('input', function(e) {
|
||||||
var term = e.target.value;
|
var term = e.target.value;
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:"erpnext.stock.doctype.item.item.get_item_attribute",
|
method: "erpnext.stock.doctype.item.item.get_item_attribute",
|
||||||
args:{
|
args: {
|
||||||
parent: i,
|
parent: i,
|
||||||
attribute_value: term
|
attribute_value: term
|
||||||
},
|
},
|
||||||
@@ -712,7 +720,7 @@ frappe.ui.form.on("UOM Conversion Detail", {
|
|||||||
var row = locals[cdt][cdn];
|
var row = locals[cdt][cdn];
|
||||||
if (row.uom) {
|
if (row.uom) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:"erpnext.stock.doctype.item.item.get_uom_conv_factor",
|
method: "erpnext.stock.doctype.item.item.get_uom_conv_factor",
|
||||||
args: {
|
args: {
|
||||||
"uom": row.uom,
|
"uom": row.uom,
|
||||||
"stock_uom": frm.doc.stock_uom
|
"stock_uom": frm.doc.stock_uom
|
||||||
|
|||||||
@@ -691,7 +691,18 @@ class Item(WebsiteGenerator):
|
|||||||
'income_account': item.income_account
|
'income_account': item.income_account
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
self.append("item_defaults", {"company": frappe.defaults.get_defaults().company})
|
warehouse = ''
|
||||||
|
defaults = frappe.defaults.get_defaults() or {}
|
||||||
|
|
||||||
|
# To check default warehouse is belong to the default company
|
||||||
|
if defaults.get("default_warehouse") and frappe.db.exists("Warehouse",
|
||||||
|
{'name': defaults.default_warehouse, 'company': defaults.company}):
|
||||||
|
warehouse = defaults.default_warehouse
|
||||||
|
|
||||||
|
self.append("item_defaults", {
|
||||||
|
"company": defaults.get("company"),
|
||||||
|
"default_warehouse": warehouse
|
||||||
|
})
|
||||||
|
|
||||||
def update_variants(self):
|
def update_variants(self):
|
||||||
if self.flags.dont_update_variants or \
|
if self.flags.dont_update_variants or \
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ frappe.ui.form.on('Material Request', {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onload_post_render: function(frm) {
|
||||||
|
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||||
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frm.events.make_custom_buttons(frm);
|
frm.events.make_custom_buttons(frm);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,10 +26,6 @@ frappe.ui.form.on("Purchase Receipt", {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onload_post_render: function(frm) {
|
|
||||||
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
|
||||||
},
|
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
if(frm.doc.company) {
|
if(frm.doc.company) {
|
||||||
frm.trigger("toggle_display_account_head");
|
frm.trigger("toggle_display_account_head");
|
||||||
|
|||||||
@@ -24,29 +24,32 @@ class PurchaseReceipt(BuyingController):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(PurchaseReceipt, self).__init__(*args, **kwargs)
|
super(PurchaseReceipt, self).__init__(*args, **kwargs)
|
||||||
self.status_updater = [{
|
self.status_updater = [{
|
||||||
'source_dt': 'Purchase Receipt Item',
|
|
||||||
'target_dt': 'Purchase Order Item',
|
'target_dt': 'Purchase Order Item',
|
||||||
'join_field': 'purchase_order_item',
|
'join_field': 'purchase_order_item',
|
||||||
'target_field': 'received_qty',
|
'target_field': 'received_qty',
|
||||||
'target_parent_dt': 'Purchase Order',
|
'target_parent_dt': 'Purchase Order',
|
||||||
'target_parent_field': 'per_received',
|
'target_parent_field': 'per_received',
|
||||||
'target_ref_field': 'qty',
|
'target_ref_field': 'qty',
|
||||||
|
'source_dt': 'Purchase Receipt Item',
|
||||||
'source_field': 'received_qty',
|
'source_field': 'received_qty',
|
||||||
|
'second_source_dt': 'Purchase Invoice Item',
|
||||||
|
'second_source_field': 'received_qty',
|
||||||
|
'second_join_field': 'po_detail',
|
||||||
'percent_join_field': 'purchase_order',
|
'percent_join_field': 'purchase_order',
|
||||||
'overflow_type': 'receipt'
|
'overflow_type': 'receipt'
|
||||||
},
|
|
||||||
{
|
|
||||||
'source_dt': 'Purchase Receipt Item',
|
|
||||||
'target_dt': 'Purchase Order Item',
|
|
||||||
'join_field': 'purchase_order_item',
|
|
||||||
'target_field': 'returned_qty',
|
|
||||||
'target_parent_dt': 'Purchase Order',
|
|
||||||
# 'target_parent_field': 'per_received',
|
|
||||||
# 'target_ref_field': 'qty',
|
|
||||||
'source_field': '-1 * qty',
|
|
||||||
# 'overflow_type': 'receipt',
|
|
||||||
'extra_cond': """ and exists (select name from `tabPurchase Receipt` where name=`tabPurchase Receipt Item`.parent and is_return=1)"""
|
|
||||||
}]
|
}]
|
||||||
|
if cint(self.is_return):
|
||||||
|
self.status_updater.append({
|
||||||
|
'source_dt': 'Purchase Receipt Item',
|
||||||
|
'target_dt': 'Purchase Order Item',
|
||||||
|
'join_field': 'purchase_order_item',
|
||||||
|
'target_field': 'returned_qty',
|
||||||
|
'source_field': '-1 * qty',
|
||||||
|
'second_source_dt': 'Purchase Invoice Item',
|
||||||
|
'second_source_field': '-1 * qty',
|
||||||
|
'second_join_field': 'po_detail',
|
||||||
|
'extra_cond': """ and exists (select name from `tabPurchase Receipt` where name=`tabPurchase Receipt Item`.parent and is_return=1)"""
|
||||||
|
})
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_posting_time()
|
self.validate_posting_time()
|
||||||
|
|||||||
@@ -579,9 +579,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// if(!this.item_selector && false) {
|
this.frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||||
// this.item_selector = new erpnext.ItemSelector({frm: this.frm});
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function() {
|
refresh: function() {
|
||||||
|
|||||||
@@ -306,8 +306,21 @@ def get_basic_details(args, item):
|
|||||||
for fieldname in ("item_name", "item_group", "barcodes", "brand", "stock_uom"):
|
for fieldname in ("item_name", "item_group", "barcodes", "brand", "stock_uom"):
|
||||||
out[fieldname] = item.get(fieldname)
|
out[fieldname] = item.get(fieldname)
|
||||||
|
|
||||||
|
child_doctype = args.doctype + ' Item'
|
||||||
|
meta = frappe.get_meta(child_doctype)
|
||||||
|
if meta.get_field("barcode"):
|
||||||
|
update_barcode_value(out)
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
def update_barcode_value(out):
|
||||||
|
from erpnext.accounts.doctype.sales_invoice.pos import get_barcode_data
|
||||||
|
barcode_data = get_barcode_data([out])
|
||||||
|
|
||||||
|
# If item has one barcode then update the value of the barcode field
|
||||||
|
if barcode_data and len(barcode_data.get(out.item_code)) == 1:
|
||||||
|
out['barcode'] = barcode_data.get(out.item_code)[0]
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def calculate_service_end_date(args, item=None):
|
def calculate_service_end_date(args, item=None):
|
||||||
args = process_args(args)
|
args = process_args(args)
|
||||||
@@ -377,7 +390,7 @@ def get_price_list_rate(args, item_doc, out):
|
|||||||
pl_details = get_price_list_currency_and_exchange_rate(args)
|
pl_details = get_price_list_currency_and_exchange_rate(args)
|
||||||
args.update(pl_details)
|
args.update(pl_details)
|
||||||
validate_price_list(args)
|
validate_price_list(args)
|
||||||
if meta.get_field("currency") and args.price_list:
|
if meta.get_field("currency"):
|
||||||
validate_conversion_rate(args, meta)
|
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) or 0
|
||||||
@@ -554,21 +567,22 @@ def validate_conversion_rate(args, meta):
|
|||||||
get_field_precision(meta.get_field("conversion_rate"),
|
get_field_precision(meta.get_field("conversion_rate"),
|
||||||
frappe._dict({"fields": args})))
|
frappe._dict({"fields": args})))
|
||||||
|
|
||||||
if (not args.plc_conversion_rate
|
if args.price_list:
|
||||||
and args.price_list_currency==frappe.db.get_value("Price List", args.price_list, "currency", cache=True)):
|
if (not args.plc_conversion_rate
|
||||||
args.plc_conversion_rate = 1.0
|
and args.price_list_currency==frappe.db.get_value("Price List", args.price_list, "currency", cache=True)):
|
||||||
|
args.plc_conversion_rate = 1.0
|
||||||
|
|
||||||
# validate price list currency conversion rate
|
# validate price list currency conversion rate
|
||||||
if not args.get("price_list_currency"):
|
if not args.get("price_list_currency"):
|
||||||
throw(_("Price List Currency not selected"))
|
throw(_("Price List Currency not selected"))
|
||||||
else:
|
else:
|
||||||
validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate,
|
validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate,
|
||||||
meta.get_label("plc_conversion_rate"), args.company)
|
meta.get_label("plc_conversion_rate"), args.company)
|
||||||
|
|
||||||
if meta.get_field("plc_conversion_rate"):
|
if meta.get_field("plc_conversion_rate"):
|
||||||
args.plc_conversion_rate = flt(args.plc_conversion_rate,
|
args.plc_conversion_rate = flt(args.plc_conversion_rate,
|
||||||
get_field_precision(meta.get_field("plc_conversion_rate"),
|
get_field_precision(meta.get_field("plc_conversion_rate"),
|
||||||
frappe._dict({"fields": args})))
|
frappe._dict({"fields": args})))
|
||||||
|
|
||||||
def get_party_item_code(args, item_doc, out):
|
def get_party_item_code(args, item_doc, out):
|
||||||
if args.transaction_type=="selling" and args.customer:
|
if args.transaction_type=="selling" and args.customer:
|
||||||
|
|||||||
Reference in New Issue
Block a user