Merge branch 'develop' into navbar_links
This commit is contained in:
commit
ecc282930a
@ -9,6 +9,8 @@ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_orde
|
|||||||
from erpnext.stock.get_item_details import get_item_details
|
from erpnext.stock.get_item_details import get_item_details
|
||||||
from frappe.test_runner import make_test_objects
|
from frappe.test_runner import make_test_objects
|
||||||
|
|
||||||
|
test_dependencies = ['Item']
|
||||||
|
|
||||||
def test_create_test_data():
|
def test_create_test_data():
|
||||||
frappe.set_user("Administrator")
|
frappe.set_user("Administrator")
|
||||||
# create test item
|
# create test item
|
||||||
@ -95,7 +97,6 @@ def test_create_test_data():
|
|||||||
})
|
})
|
||||||
coupon_code.insert()
|
coupon_code.insert()
|
||||||
|
|
||||||
|
|
||||||
class TestCouponCode(unittest.TestCase):
|
class TestCouponCode(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
test_create_test_data()
|
test_create_test_data()
|
||||||
|
@ -8,6 +8,8 @@ import unittest
|
|||||||
from erpnext.stock.get_item_details import get_pos_profile
|
from erpnext.stock.get_item_details import get_pos_profile
|
||||||
from erpnext.accounts.doctype.pos_profile.pos_profile import get_child_nodes
|
from erpnext.accounts.doctype.pos_profile.pos_profile import get_child_nodes
|
||||||
|
|
||||||
|
test_dependencies = ['Item']
|
||||||
|
|
||||||
class TestPOSProfile(unittest.TestCase):
|
class TestPOSProfile(unittest.TestCase):
|
||||||
def test_pos_profile(self):
|
def test_pos_profile(self):
|
||||||
make_pos_profile()
|
make_pos_profile()
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# MIT License. See license.txt
|
|
||||||
|
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
@ -237,7 +236,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
|
|||||||
|
|
||||||
update_args_for_pricing_rule(args)
|
update_args_for_pricing_rule(args)
|
||||||
|
|
||||||
pricing_rules = (get_applied_pricing_rules(args)
|
pricing_rules = (get_applied_pricing_rules(args.get('pricing_rules'))
|
||||||
if for_validate and args.get("pricing_rules") else get_pricing_rules(args, doc))
|
if for_validate and args.get("pricing_rules") else get_pricing_rules(args, doc))
|
||||||
|
|
||||||
if pricing_rules:
|
if pricing_rules:
|
||||||
@ -365,8 +364,9 @@ def set_discount_amount(rate, item_details):
|
|||||||
item_details.rate = rate
|
item_details.rate = rate
|
||||||
|
|
||||||
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
|
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
|
||||||
from erpnext.accounts.doctype.pricing_rule.utils import get_pricing_rule_items
|
from erpnext.accounts.doctype.pricing_rule.utils import (get_applied_pricing_rules,
|
||||||
for d in json.loads(pricing_rules):
|
get_pricing_rule_items)
|
||||||
|
for d in get_applied_pricing_rules(pricing_rules):
|
||||||
if not d or not frappe.db.exists("Pricing Rule", d): continue
|
if not d or not frappe.db.exists("Pricing Rule", d): continue
|
||||||
pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
|
pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
|
||||||
|
|
||||||
|
@ -447,9 +447,14 @@ def apply_pricing_rule_on_transaction(doc):
|
|||||||
apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
|
apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
|
||||||
doc.set_missing_values()
|
doc.set_missing_values()
|
||||||
|
|
||||||
def get_applied_pricing_rules(item_row):
|
def get_applied_pricing_rules(pricing_rules):
|
||||||
return (json.loads(item_row.get("pricing_rules"))
|
if pricing_rules:
|
||||||
if item_row.get("pricing_rules") else [])
|
if pricing_rules.startswith('['):
|
||||||
|
return json.loads(pricing_rules)
|
||||||
|
else:
|
||||||
|
return pricing_rules.split(',')
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
||||||
free_item = pricing_rule.free_item
|
free_item = pricing_rule.free_item
|
||||||
|
@ -447,7 +447,7 @@
|
|||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"fieldname": "po_no",
|
"fieldname": "po_no",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Data",
|
||||||
"hide_days": 1,
|
"hide_days": 1,
|
||||||
"hide_seconds": 1,
|
"hide_seconds": 1,
|
||||||
"label": "Customer's Purchase Order",
|
"label": "Customer's Purchase Order",
|
||||||
@ -1946,7 +1946,7 @@
|
|||||||
"idx": 181,
|
"idx": 181,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-08-03 23:31:12.675040",
|
"modified": "2020-08-27 01:56:28.532140",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
@ -13,7 +13,8 @@ def get_data():
|
|||||||
'Auto Repeat': 'reference_document',
|
'Auto Repeat': 'reference_document',
|
||||||
},
|
},
|
||||||
'internal_links': {
|
'internal_links': {
|
||||||
'Sales Order': ['items', 'sales_order']
|
'Sales Order': ['items', 'sales_order'],
|
||||||
|
'Delivery Note': ['items', 'delivery_note']
|
||||||
},
|
},
|
||||||
'transactions': [
|
'transactions': [
|
||||||
{
|
{
|
||||||
|
@ -3,6 +3,22 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Shipping Rule', {
|
frappe.ui.form.on('Shipping Rule', {
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
|
frm.set_query("cost_center", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: frm.doc.company
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
frm.set_query("account", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: frm.doc.company
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
frm.trigger('toggle_reqd');
|
frm.trigger('toggle_reqd');
|
||||||
},
|
},
|
||||||
calculate_based_on: function(frm) {
|
calculate_based_on: function(frm) {
|
||||||
|
@ -378,7 +378,7 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g
|
|||||||
if filters and filters.get('presentation_currency') != d.default_currency:
|
if filters and filters.get('presentation_currency') != d.default_currency:
|
||||||
currency_info['company'] = d.name
|
currency_info['company'] = d.name
|
||||||
currency_info['company_currency'] = d.default_currency
|
currency_info['company_currency'] = d.default_currency
|
||||||
convert_to_presentation_currency(gl_entries, currency_info)
|
convert_to_presentation_currency(gl_entries, currency_info, filters.get('company'))
|
||||||
|
|
||||||
for entry in gl_entries:
|
for entry in gl_entries:
|
||||||
key = entry.account_number or entry.account_name
|
key = entry.account_number or entry.account_name
|
||||||
|
@ -423,7 +423,7 @@ def set_gl_entries_by_account(
|
|||||||
distributed_cost_center_query=distributed_cost_center_query), gl_filters, as_dict=True) #nosec
|
distributed_cost_center_query=distributed_cost_center_query), gl_filters, as_dict=True) #nosec
|
||||||
|
|
||||||
if filters and filters.get('presentation_currency'):
|
if filters and filters.get('presentation_currency'):
|
||||||
convert_to_presentation_currency(gl_entries, get_currency(filters))
|
convert_to_presentation_currency(gl_entries, get_currency(filters), filters.get('company'))
|
||||||
|
|
||||||
for entry in gl_entries:
|
for entry in gl_entries:
|
||||||
gl_entries_by_account.setdefault(entry.account, []).append(entry)
|
gl_entries_by_account.setdefault(entry.account, []).append(entry)
|
||||||
|
@ -146,6 +146,12 @@ frappe.query_reports["General Ledger"] = {
|
|||||||
return frappe.db.get_link_options('Project', txt);
|
return frappe.db.get_link_options('Project', txt);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "include_dimensions",
|
||||||
|
"label": __("Consider Accounting Dimensions"),
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "show_opening_entries",
|
"fieldname": "show_opening_entries",
|
||||||
"label": __("Show Opening Entries"),
|
"label": __("Show Opening Entries"),
|
||||||
|
@ -106,15 +106,20 @@ def set_account_currency(filters):
|
|||||||
return filters
|
return filters
|
||||||
|
|
||||||
def get_result(filters, account_details):
|
def get_result(filters, account_details):
|
||||||
gl_entries = get_gl_entries(filters)
|
accounting_dimensions = []
|
||||||
|
if filters.get("include_dimensions"):
|
||||||
|
accounting_dimensions = get_accounting_dimensions()
|
||||||
|
|
||||||
data = get_data_with_opening_closing(filters, account_details, gl_entries)
|
gl_entries = get_gl_entries(filters, accounting_dimensions)
|
||||||
|
|
||||||
|
data = get_data_with_opening_closing(filters, account_details,
|
||||||
|
accounting_dimensions, gl_entries)
|
||||||
|
|
||||||
result = get_result_as_list(data, filters)
|
result = get_result_as_list(data, filters)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_gl_entries(filters):
|
def get_gl_entries(filters, accounting_dimensions):
|
||||||
currency_map = get_currency(filters)
|
currency_map = get_currency(filters)
|
||||||
select_fields = """, debit, credit, debit_in_account_currency,
|
select_fields = """, debit, credit, debit_in_account_currency,
|
||||||
credit_in_account_currency """
|
credit_in_account_currency """
|
||||||
@ -128,6 +133,10 @@ def get_gl_entries(filters):
|
|||||||
filters['company_fb'] = frappe.db.get_value("Company",
|
filters['company_fb'] = frappe.db.get_value("Company",
|
||||||
filters.get("company"), 'default_finance_book')
|
filters.get("company"), 'default_finance_book')
|
||||||
|
|
||||||
|
dimension_fields = ""
|
||||||
|
if accounting_dimensions:
|
||||||
|
dimension_fields = ', '.join(accounting_dimensions) + ','
|
||||||
|
|
||||||
distributed_cost_center_query = ""
|
distributed_cost_center_query = ""
|
||||||
if filters and filters.get('cost_center'):
|
if filters and filters.get('cost_center'):
|
||||||
select_fields_with_percentage = """, debit*(DCC_allocation.percentage_allocation/100) as debit, credit*(DCC_allocation.percentage_allocation/100) as credit, debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency,
|
select_fields_with_percentage = """, debit*(DCC_allocation.percentage_allocation/100) as debit, credit*(DCC_allocation.percentage_allocation/100) as credit, debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency,
|
||||||
@ -141,7 +150,7 @@ def get_gl_entries(filters):
|
|||||||
party_type,
|
party_type,
|
||||||
party,
|
party,
|
||||||
voucher_type,
|
voucher_type,
|
||||||
voucher_no,
|
voucher_no, {dimension_fields}
|
||||||
cost_center, project,
|
cost_center, project,
|
||||||
against_voucher_type,
|
against_voucher_type,
|
||||||
against_voucher,
|
against_voucher,
|
||||||
@ -160,13 +169,14 @@ def get_gl_entries(filters):
|
|||||||
{conditions}
|
{conditions}
|
||||||
AND posting_date <= %(to_date)s
|
AND posting_date <= %(to_date)s
|
||||||
AND cost_center = DCC_allocation.parent
|
AND cost_center = DCC_allocation.parent
|
||||||
""".format(select_fields_with_percentage=select_fields_with_percentage, conditions=get_conditions(filters).replace("and cost_center in %(cost_center)s ", ''))
|
""".format(dimension_fields=dimension_fields,select_fields_with_percentage=select_fields_with_percentage, conditions=get_conditions(filters).replace("and cost_center in %(cost_center)s ", ''))
|
||||||
|
|
||||||
gl_entries = frappe.db.sql(
|
gl_entries = frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
select
|
select
|
||||||
name as gl_entry, posting_date, account, party_type, party,
|
name as gl_entry, posting_date, account, party_type, party,
|
||||||
voucher_type, voucher_no, cost_center, project,
|
voucher_type, voucher_no, {dimension_fields}
|
||||||
|
cost_center, project,
|
||||||
against_voucher_type, against_voucher, account_currency,
|
against_voucher_type, against_voucher, account_currency,
|
||||||
remarks, against, is_opening, creation {select_fields}
|
remarks, against, is_opening, creation {select_fields}
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
@ -174,13 +184,13 @@ def get_gl_entries(filters):
|
|||||||
{distributed_cost_center_query}
|
{distributed_cost_center_query}
|
||||||
{order_by_statement}
|
{order_by_statement}
|
||||||
""".format(
|
""".format(
|
||||||
select_fields=select_fields, conditions=get_conditions(filters), distributed_cost_center_query=distributed_cost_center_query,
|
dimension_fields=dimension_fields, select_fields=select_fields, conditions=get_conditions(filters), distributed_cost_center_query=distributed_cost_center_query,
|
||||||
order_by_statement=order_by_statement
|
order_by_statement=order_by_statement
|
||||||
),
|
),
|
||||||
filters, as_dict=1)
|
filters, as_dict=1)
|
||||||
|
|
||||||
if filters.get('presentation_currency'):
|
if filters.get('presentation_currency'):
|
||||||
return convert_to_presentation_currency(gl_entries, currency_map)
|
return convert_to_presentation_currency(gl_entries, currency_map, filters.get('company'))
|
||||||
else:
|
else:
|
||||||
return gl_entries
|
return gl_entries
|
||||||
|
|
||||||
@ -247,12 +257,12 @@ def get_conditions(filters):
|
|||||||
return "and {}".format(" and ".join(conditions)) if conditions else ""
|
return "and {}".format(" and ".join(conditions)) if conditions else ""
|
||||||
|
|
||||||
|
|
||||||
def get_data_with_opening_closing(filters, account_details, gl_entries):
|
def get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries):
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
gle_map = initialize_gle_map(gl_entries, filters)
|
gle_map = initialize_gle_map(gl_entries, filters)
|
||||||
|
|
||||||
totals, entries = get_accountwise_gle(filters, gl_entries, gle_map)
|
totals, entries = get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map)
|
||||||
|
|
||||||
# Opening for filtered account
|
# Opening for filtered account
|
||||||
data.append(totals.opening)
|
data.append(totals.opening)
|
||||||
@ -318,7 +328,7 @@ def initialize_gle_map(gl_entries, filters):
|
|||||||
return gle_map
|
return gle_map
|
||||||
|
|
||||||
|
|
||||||
def get_accountwise_gle(filters, gl_entries, gle_map):
|
def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map):
|
||||||
totals = get_totals_dict()
|
totals = get_totals_dict()
|
||||||
entries = []
|
entries = []
|
||||||
consolidated_gle = OrderedDict()
|
consolidated_gle = OrderedDict()
|
||||||
@ -350,8 +360,11 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
|
|||||||
if filters.get("group_by") != _('Group by Voucher (Consolidated)'):
|
if filters.get("group_by") != _('Group by Voucher (Consolidated)'):
|
||||||
gle_map[gle.get(group_by)].entries.append(gle)
|
gle_map[gle.get(group_by)].entries.append(gle)
|
||||||
elif filters.get("group_by") == _('Group by Voucher (Consolidated)'):
|
elif filters.get("group_by") == _('Group by Voucher (Consolidated)'):
|
||||||
key = (gle.get("voucher_type"), gle.get("voucher_no"),
|
keylist = [gle.get("voucher_type"), gle.get("voucher_no"), gle.get("account")]
|
||||||
gle.get("account"), gle.get("cost_center"))
|
for dim in accounting_dimensions:
|
||||||
|
keylist.append(gle.get(dim))
|
||||||
|
keylist.append(gle.get("cost_center"))
|
||||||
|
key = tuple(keylist)
|
||||||
if key not in consolidated_gle:
|
if key not in consolidated_gle:
|
||||||
consolidated_gle.setdefault(key, gle)
|
consolidated_gle.setdefault(key, gle)
|
||||||
else:
|
else:
|
||||||
@ -478,7 +491,19 @@ def get_columns(filters):
|
|||||||
"options": "Project",
|
"options": "Project",
|
||||||
"fieldname": "project",
|
"fieldname": "project",
|
||||||
"width": 100
|
"width": 100
|
||||||
},
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
if filters.get("include_dimensions"):
|
||||||
|
for dim in get_accounting_dimensions(as_list = False):
|
||||||
|
columns.append({
|
||||||
|
"label": _(dim.label),
|
||||||
|
"options": dim.label,
|
||||||
|
"fieldname": dim.fieldname,
|
||||||
|
"width": 100
|
||||||
|
})
|
||||||
|
|
||||||
|
columns.extend([
|
||||||
{
|
{
|
||||||
"label": _("Cost Center"),
|
"label": _("Cost Center"),
|
||||||
"options": "Cost Center",
|
"options": "Cost Center",
|
||||||
|
@ -6,10 +6,6 @@ from erpnext.accounts.doctype.fiscal_year.fiscal_year import get_from_and_to_dat
|
|||||||
from frappe.utils import cint, get_datetime_str, formatdate, flt
|
from frappe.utils import cint, get_datetime_str, formatdate, flt
|
||||||
|
|
||||||
__exchange_rates = {}
|
__exchange_rates = {}
|
||||||
P_OR_L_ACCOUNTS = list(
|
|
||||||
sum(frappe.get_list('Account', fields=['name'], or_filters=[{'root_type': 'Income'}, {'root_type': 'Expense'}], as_list=True), ())
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_currency(filters):
|
def get_currency(filters):
|
||||||
"""
|
"""
|
||||||
@ -73,18 +69,7 @@ def get_rate_as_at(date, from_currency, to_currency):
|
|||||||
|
|
||||||
return rate
|
return rate
|
||||||
|
|
||||||
|
def convert_to_presentation_currency(gl_entries, currency_info, company):
|
||||||
def is_p_or_l_account(account_name):
|
|
||||||
"""
|
|
||||||
Check if the given `account name` is an `Account` with `root_type` of either 'Income'
|
|
||||||
or 'Expense'.
|
|
||||||
:param account_name:
|
|
||||||
:return: Boolean
|
|
||||||
"""
|
|
||||||
return account_name in P_OR_L_ACCOUNTS
|
|
||||||
|
|
||||||
|
|
||||||
def convert_to_presentation_currency(gl_entries, currency_info):
|
|
||||||
"""
|
"""
|
||||||
Take a list of GL Entries and change the 'debit' and 'credit' values to currencies
|
Take a list of GL Entries and change the 'debit' and 'credit' values to currencies
|
||||||
in `currency_info`.
|
in `currency_info`.
|
||||||
@ -96,6 +81,9 @@ def convert_to_presentation_currency(gl_entries, currency_info):
|
|||||||
presentation_currency = currency_info['presentation_currency']
|
presentation_currency = currency_info['presentation_currency']
|
||||||
company_currency = currency_info['company_currency']
|
company_currency = currency_info['company_currency']
|
||||||
|
|
||||||
|
pl_accounts = [d.name for d in frappe.get_list('Account',
|
||||||
|
filters={'report_type': 'Profit and Loss', 'company': company})]
|
||||||
|
|
||||||
for entry in gl_entries:
|
for entry in gl_entries:
|
||||||
account = entry['account']
|
account = entry['account']
|
||||||
debit = flt(entry['debit'])
|
debit = flt(entry['debit'])
|
||||||
@ -107,7 +95,7 @@ def convert_to_presentation_currency(gl_entries, currency_info):
|
|||||||
if account_currency != presentation_currency:
|
if account_currency != presentation_currency:
|
||||||
value = debit or credit
|
value = debit or credit
|
||||||
|
|
||||||
date = currency_info['report_date'] if not is_p_or_l_account(account) else entry['posting_date']
|
date = entry['posting_date'] if account in pl_accounts else currency_info['report_date']
|
||||||
converted_value = convert(value, presentation_currency, company_currency, date)
|
converted_value = convert(value, presentation_currency, company_currency, date)
|
||||||
|
|
||||||
if entry.get('debit'):
|
if entry.get('debit'):
|
||||||
|
@ -325,7 +325,7 @@ class AccountsController(TransactionBase):
|
|||||||
apply_pricing_rule_for_free_items(self, pricing_rule_args.get('free_item_data'))
|
apply_pricing_rule_for_free_items(self, pricing_rule_args.get('free_item_data'))
|
||||||
|
|
||||||
elif pricing_rule_args.get("validate_applied_rule"):
|
elif pricing_rule_args.get("validate_applied_rule"):
|
||||||
for pricing_rule in get_applied_pricing_rules(item):
|
for pricing_rule in get_applied_pricing_rules(item.get('pricing_rules')):
|
||||||
pricing_rule_doc = frappe.get_cached_doc("Pricing Rule", pricing_rule)
|
pricing_rule_doc = frappe.get_cached_doc("Pricing Rule", pricing_rule)
|
||||||
for field in ['discount_percentage', 'discount_amount', 'rate']:
|
for field in ['discount_percentage', 'discount_amount', 'rate']:
|
||||||
if item.get(field) < pricing_rule_doc.get(field):
|
if item.get(field) < pricing_rule_doc.get(field):
|
||||||
|
@ -9,6 +9,7 @@ from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
|
|||||||
from erpnext.controllers.accounts_controller import validate_conversion_rate, \
|
from erpnext.controllers.accounts_controller import validate_conversion_rate, \
|
||||||
validate_taxes_and_charges, validate_inclusive_tax
|
validate_taxes_and_charges, validate_inclusive_tax
|
||||||
from erpnext.stock.get_item_details import _get_item_tax_template
|
from erpnext.stock.get_item_details import _get_item_tax_template
|
||||||
|
from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
|
||||||
|
|
||||||
class calculate_taxes_and_totals(object):
|
class calculate_taxes_and_totals(object):
|
||||||
def __init__(self, doc):
|
def __init__(self, doc):
|
||||||
@ -607,7 +608,7 @@ class calculate_taxes_and_totals(object):
|
|||||||
base_rate_with_margin = 0.0
|
base_rate_with_margin = 0.0
|
||||||
if item.price_list_rate:
|
if item.price_list_rate:
|
||||||
if item.pricing_rules and not self.doc.ignore_pricing_rule:
|
if item.pricing_rules and not self.doc.ignore_pricing_rule:
|
||||||
for d in json.loads(item.pricing_rules):
|
for d in get_applied_pricing_rules(item.pricing_rules):
|
||||||
pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
|
pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
|
||||||
|
|
||||||
if (pricing_rule.margin_type == 'Amount' and pricing_rule.currency == self.doc.currency)\
|
if (pricing_rule.margin_type == 'Amount' and pricing_rule.currency == self.doc.currency)\
|
||||||
|
@ -30,14 +30,14 @@ frappe.ui.form.on('Social Media Post', {
|
|||||||
let color = frm.doc.twitter_post_id ? "green" : "red";
|
let color = frm.doc.twitter_post_id ? "green" : "red";
|
||||||
let status = frm.doc.twitter_post_id ? "Posted" : "Not Posted";
|
let status = frm.doc.twitter_post_id ? "Posted" : "Not Posted";
|
||||||
html += `<div class="col-xs-6">
|
html += `<div class="col-xs-6">
|
||||||
<span class="indicator whitespace-nowrap ${color}"><span class="hidden-xs">Twitter : ${status} </span></span>
|
<span class="indicator whitespace-nowrap ${color}"><span>Twitter : ${status} </span></span>
|
||||||
</div>` ;
|
</div>` ;
|
||||||
}
|
}
|
||||||
if (frm.doc.linkedin){
|
if (frm.doc.linkedin){
|
||||||
let color = frm.doc.linkedin_post_id ? "green" : "red";
|
let color = frm.doc.linkedin_post_id ? "green" : "red";
|
||||||
let status = frm.doc.linkedin_post_id ? "Posted" : "Not Posted";
|
let status = frm.doc.linkedin_post_id ? "Posted" : "Not Posted";
|
||||||
html += `<div class="col-xs-6">
|
html += `<div class="col-xs-6">
|
||||||
<span class="indicator whitespace-nowrap ${color}"><span class="hidden-xs">LinkedIn : ${status} </span></span>
|
<span class="indicator whitespace-nowrap ${color}"><span>LinkedIn : ${status} </span></span>
|
||||||
</div>` ;
|
</div>` ;
|
||||||
}
|
}
|
||||||
html = `<div class="row">${html}</div>`;
|
html = `<div class="row">${html}</div>`;
|
||||||
|
52
erpnext/crm/report/lead_details/lead_details.js
Normal file
52
erpnext/crm/report/lead_details/lead_details.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
frappe.query_reports["Lead Details"] = {
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"fieldname":"company",
|
||||||
|
"label": __("Company"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Company",
|
||||||
|
"default": frappe.defaults.get_user_default("Company"),
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"from_date",
|
||||||
|
"label": __("From Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -12),
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"to_date",
|
||||||
|
"label": __("To Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"default": frappe.datetime.get_today(),
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"status",
|
||||||
|
"label": __("Status"),
|
||||||
|
"fieldtype": "Select",
|
||||||
|
options: [
|
||||||
|
{ "value": "Lead", "label": __("Lead") },
|
||||||
|
{ "value": "Open", "label": __("Open") },
|
||||||
|
{ "value": "Replied", "label": __("Replied") },
|
||||||
|
{ "value": "Opportunity", "label": __("Opportunity") },
|
||||||
|
{ "value": "Quotation", "label": __("Quotation") },
|
||||||
|
{ "value": "Lost Quotation", "label": __("Lost Quotation") },
|
||||||
|
{ "value": "Interested", "label": __("Interested") },
|
||||||
|
{ "value": "Converted", "label": __("Converted") },
|
||||||
|
{ "value": "Do Not Contact", "label": __("Do Not Contact") },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"territory",
|
||||||
|
"label": __("Territory"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Territory",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
@ -7,16 +7,15 @@
|
|||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
"idx": 3,
|
"idx": 3,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"modified": "2020-01-22 16:51:56.591110",
|
"modified": "2020-07-26 23:59:49.897577",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Lead Details",
|
"name": "Lead Details",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"prepared_report": 0,
|
"prepared_report": 0,
|
||||||
"query": "SELECT\n `tabLead`.name as \"Lead Id:Link/Lead:120\",\n `tabLead`.lead_name as \"Lead Name::120\",\n\t`tabLead`.company_name as \"Company Name::120\",\n\t`tabLead`.status as \"Status::120\",\n\tconcat_ws(', ', \n\t\ttrim(',' from `tabAddress`.address_line1), \n\t\ttrim(',' from tabAddress.address_line2)\n\t) as 'Address::180',\n\t`tabAddress`.state as \"State::100\",\n\t`tabAddress`.pincode as \"Pincode::70\",\n\t`tabAddress`.country as \"Country::100\",\n\t`tabLead`.phone as \"Phone::100\",\n\t`tabLead`.mobile_no as \"Mobile No::100\",\n\t`tabLead`.email_id as \"Email Id::120\",\n\t`tabLead`.lead_owner as \"Lead Owner::120\",\n\t`tabLead`.source as \"Source::120\",\n\t`tabLead`.territory as \"Territory::120\",\n\t`tabLead`.notes as \"Notes::360\",\n `tabLead`.owner as \"Owner:Link/User:120\"\nFROM\n\t`tabLead`\n\tleft join `tabDynamic Link` on (\n\t\t`tabDynamic Link`.link_name=`tabLead`.name \n\t\tand `tabDynamic Link`.parenttype = 'Address'\n\t)\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.name=`tabDynamic Link`.parent\n\t)\nWHERE\n\t`tabLead`.docstatus<2\nORDER BY\n\t`tabLead`.name asc",
|
|
||||||
"ref_doctype": "Lead",
|
"ref_doctype": "Lead",
|
||||||
"report_name": "Lead Details",
|
"report_name": "Lead Details",
|
||||||
"report_type": "Query Report",
|
"report_type": "Script Report",
|
||||||
"roles": [
|
"roles": [
|
||||||
{
|
{
|
||||||
"role": "Sales User"
|
"role": "Sales User"
|
||||||
|
158
erpnext/crm/report/lead_details/lead_details.py
Normal file
158
erpnext/crm/report/lead_details/lead_details.py
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute(filters=None):
|
||||||
|
columns, data = get_columns(), get_data(filters)
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
def get_columns():
|
||||||
|
columns = [
|
||||||
|
{
|
||||||
|
"label": _("Lead"),
|
||||||
|
"fieldname": "name",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Lead",
|
||||||
|
"width": 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Lead Name"),
|
||||||
|
"fieldname": "lead_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"status",
|
||||||
|
"label": _("Status"),
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"lead_owner",
|
||||||
|
"label": _("Lead Owner"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "User",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Territory"),
|
||||||
|
"fieldname": "territory",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Territory",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Source"),
|
||||||
|
"fieldname": "source",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Email"),
|
||||||
|
"fieldname": "email_id",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Mobile"),
|
||||||
|
"fieldname": "mobile_no",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Phone"),
|
||||||
|
"fieldname": "phone",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Owner"),
|
||||||
|
"fieldname": "owner",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "user",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Company"),
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Company",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"address",
|
||||||
|
"label": _("Address"),
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 130
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"state",
|
||||||
|
"label": _("State"),
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"pincode",
|
||||||
|
"label": _("Postal Code"),
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 90
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"country",
|
||||||
|
"label": _("Country"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Country",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
|
return columns
|
||||||
|
|
||||||
|
def get_data(filters):
|
||||||
|
return frappe.db.sql("""
|
||||||
|
SELECT
|
||||||
|
`tabLead`.name,
|
||||||
|
`tabLead`.lead_name,
|
||||||
|
`tabLead`.status,
|
||||||
|
`tabLead`.lead_owner,
|
||||||
|
`tabLead`.territory,
|
||||||
|
`tabLead`.source,
|
||||||
|
`tabLead`.email_id,
|
||||||
|
`tabLead`.mobile_no,
|
||||||
|
`tabLead`.phone,
|
||||||
|
`tabLead`.owner,
|
||||||
|
`tabLead`.company,
|
||||||
|
concat_ws(', ',
|
||||||
|
trim(',' from `tabAddress`.address_line1),
|
||||||
|
trim(',' from tabAddress.address_line2)
|
||||||
|
) AS address,
|
||||||
|
`tabAddress`.state,
|
||||||
|
`tabAddress`.pincode,
|
||||||
|
`tabAddress`.country
|
||||||
|
FROM
|
||||||
|
`tabLead` left join `tabDynamic Link` on (
|
||||||
|
`tabLead`.name = `tabDynamic Link`.link_name and
|
||||||
|
`tabDynamic Link`.parenttype = 'Address')
|
||||||
|
left join `tabAddress` on (
|
||||||
|
`tabAddress`.name=`tabDynamic Link`.parent)
|
||||||
|
WHERE
|
||||||
|
company = %(company)s
|
||||||
|
AND `tabLead`.creation BETWEEN %(from_date)s AND %(to_date)s
|
||||||
|
{conditions}
|
||||||
|
ORDER BY
|
||||||
|
`tabLead`.creation asc """.format(conditions=get_conditions(filters)), filters, as_dict=1)
|
||||||
|
|
||||||
|
def get_conditions(filters) :
|
||||||
|
conditions = []
|
||||||
|
|
||||||
|
if filters.get("territory"):
|
||||||
|
conditions.append(" and `tabLead`.territory=%(territory)s")
|
||||||
|
|
||||||
|
if filters.get("status"):
|
||||||
|
conditions.append(" and `tabLead`.status=%(status)s")
|
||||||
|
|
||||||
|
return " ".join(conditions) if conditions else ""
|
||||||
|
|
67
erpnext/crm/report/lost_opportunity/lost_opportunity.js
Normal file
67
erpnext/crm/report/lost_opportunity/lost_opportunity.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
frappe.query_reports["Lost Opportunity"] = {
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"fieldname":"company",
|
||||||
|
"label": __("Company"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Company",
|
||||||
|
"default": frappe.defaults.get_user_default("Company"),
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"from_date",
|
||||||
|
"label": __("From Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -12),
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"to_date",
|
||||||
|
"label": __("To Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"default": frappe.datetime.get_today(),
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"lost_reason",
|
||||||
|
"label": __("Lost Reason"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Opportunity Lost Reason"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"territory",
|
||||||
|
"label": __("Territory"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Territory"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"opportunity_from",
|
||||||
|
"label": __("Opportunity From"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "DocType",
|
||||||
|
"get_query": function() {
|
||||||
|
return {
|
||||||
|
"filters": {
|
||||||
|
"name": ["in", ["Customer", "Lead"]],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"party_name",
|
||||||
|
"label": __("Party"),
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"options": "opportunity_from"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"contact_by",
|
||||||
|
"label": __("Next Contact By"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "User"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
@ -1,13 +1,14 @@
|
|||||||
{
|
{
|
||||||
"add_total_row": 0,
|
"add_total_row": 0,
|
||||||
"creation": "2018-12-31 16:30:57.188837",
|
"creation": "2018-12-31 16:30:57.188837",
|
||||||
|
"disable_prepared_report": 0,
|
||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"json": "{\"order_by\": \"`tabOpportunity`.`modified` desc\", \"filters\": [[\"Opportunity\", \"status\", \"=\", \"Lost\"]], \"fields\": [[\"name\", \"Opportunity\"], [\"opportunity_from\", \"Opportunity\"], [\"party_name\", \"Opportunity\"], [\"customer_name\", \"Opportunity\"], [\"opportunity_type\", \"Opportunity\"], [\"status\", \"Opportunity\"], [\"contact_by\", \"Opportunity\"], [\"docstatus\", \"Opportunity\"], [\"lost_reason\", \"Lost Reason Detail\"]], \"add_totals_row\": 0, \"add_total_row\": 0, \"page_length\": 20}",
|
"json": "{\"order_by\": \"`tabOpportunity`.`modified` desc\", \"filters\": [[\"Opportunity\", \"status\", \"=\", \"Lost\"]], \"fields\": [[\"name\", \"Opportunity\"], [\"opportunity_from\", \"Opportunity\"], [\"party_name\", \"Opportunity\"], [\"customer_name\", \"Opportunity\"], [\"opportunity_type\", \"Opportunity\"], [\"status\", \"Opportunity\"], [\"contact_by\", \"Opportunity\"], [\"docstatus\", \"Opportunity\"], [\"lost_reason\", \"Lost Reason Detail\"]], \"add_totals_row\": 0, \"add_total_row\": 0, \"page_length\": 20}",
|
||||||
"modified": "2019-06-26 16:33:08.083618",
|
"modified": "2020-07-29 15:49:02.848845",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Lost Opportunity",
|
"name": "Lost Opportunity",
|
||||||
@ -15,7 +16,7 @@
|
|||||||
"prepared_report": 0,
|
"prepared_report": 0,
|
||||||
"ref_doctype": "Opportunity",
|
"ref_doctype": "Opportunity",
|
||||||
"report_name": "Lost Opportunity",
|
"report_name": "Lost Opportunity",
|
||||||
"report_type": "Report Builder",
|
"report_type": "Script Report",
|
||||||
"roles": [
|
"roles": [
|
||||||
{
|
{
|
||||||
"role": "Sales User"
|
"role": "Sales User"
|
||||||
|
131
erpnext/crm/report/lost_opportunity/lost_opportunity.py
Normal file
131
erpnext/crm/report/lost_opportunity/lost_opportunity.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute(filters=None):
|
||||||
|
columns, data = get_columns(), get_data(filters)
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
def get_columns():
|
||||||
|
columns = [
|
||||||
|
{
|
||||||
|
"label": _("Opportunity"),
|
||||||
|
"fieldname": "name",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Opportunity",
|
||||||
|
"width": 170,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Opportunity From"),
|
||||||
|
"fieldname": "opportunity_from",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "DocType",
|
||||||
|
"width": 130
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Party"),
|
||||||
|
"fieldname":"party_name",
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"options": "opportunity_from",
|
||||||
|
"width": 160
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Customer/Lead Name"),
|
||||||
|
"fieldname":"customer_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Opportunity Type"),
|
||||||
|
"fieldname": "opportunity_type",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 130
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Lost Reasons"),
|
||||||
|
"fieldname": "lost_reason",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Sales Stage"),
|
||||||
|
"fieldname": "sales_stage",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Sales Stage",
|
||||||
|
"width": 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Territory"),
|
||||||
|
"fieldname": "territory",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Territory",
|
||||||
|
"width": 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Next Contact By"),
|
||||||
|
"fieldname": "contact_by",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "User",
|
||||||
|
"width": 150
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return columns
|
||||||
|
|
||||||
|
def get_data(filters):
|
||||||
|
return frappe.db.sql("""
|
||||||
|
SELECT
|
||||||
|
`tabOpportunity`.name,
|
||||||
|
`tabOpportunity`.opportunity_from,
|
||||||
|
`tabOpportunity`.party_name,
|
||||||
|
`tabOpportunity`.customer_name,
|
||||||
|
`tabOpportunity`.opportunity_type,
|
||||||
|
`tabOpportunity`.contact_by,
|
||||||
|
GROUP_CONCAT(`tabOpportunity Lost Reason Detail`.lost_reason separator ', ') lost_reason,
|
||||||
|
`tabOpportunity`.sales_stage,
|
||||||
|
`tabOpportunity`.territory
|
||||||
|
FROM
|
||||||
|
`tabOpportunity`
|
||||||
|
{join}
|
||||||
|
WHERE
|
||||||
|
`tabOpportunity`.status = 'Lost' and `tabOpportunity`.company = %(company)s
|
||||||
|
AND `tabOpportunity`.modified BETWEEN %(from_date)s AND %(to_date)s
|
||||||
|
{conditions}
|
||||||
|
GROUP BY
|
||||||
|
`tabOpportunity`.name
|
||||||
|
ORDER BY
|
||||||
|
`tabOpportunity`.creation asc """.format(conditions=get_conditions(filters), join=get_join(filters)), filters, as_dict=1)
|
||||||
|
|
||||||
|
|
||||||
|
def get_conditions(filters):
|
||||||
|
conditions = []
|
||||||
|
|
||||||
|
if filters.get("territory"):
|
||||||
|
conditions.append(" and `tabOpportunity`.territory=%(territory)s")
|
||||||
|
|
||||||
|
if filters.get("opportunity_from"):
|
||||||
|
conditions.append(" and `tabOpportunity`.opportunity_from=%(opportunity_from)s")
|
||||||
|
|
||||||
|
if filters.get("party_name"):
|
||||||
|
conditions.append(" and `tabOpportunity`.party_name=%(party_name)s")
|
||||||
|
|
||||||
|
if filters.get("contact_by"):
|
||||||
|
conditions.append(" and `tabOpportunity`.contact_by=%(contact_by)s")
|
||||||
|
|
||||||
|
return " ".join(conditions) if conditions else ""
|
||||||
|
|
||||||
|
def get_join(filters):
|
||||||
|
join = """LEFT JOIN `tabOpportunity Lost Reason Detail`
|
||||||
|
ON `tabOpportunity Lost Reason Detail`.parenttype = 'Opportunity' and
|
||||||
|
`tabOpportunity Lost Reason Detail`.parent = `tabOpportunity`.name"""
|
||||||
|
|
||||||
|
if filters.get("lost_reason"):
|
||||||
|
join = """JOIN `tabOpportunity Lost Reason Detail`
|
||||||
|
ON `tabOpportunity Lost Reason Detail`.parenttype = 'Opportunity' and
|
||||||
|
`tabOpportunity Lost Reason Detail`.parent = `tabOpportunity`.name and
|
||||||
|
`tabOpportunity Lost Reason Detail`.lost_reason = '{0}'
|
||||||
|
""".format(filters.get("lost_reason"))
|
||||||
|
|
||||||
|
return join
|
@ -7,6 +7,8 @@ import unittest
|
|||||||
import frappe
|
import frappe
|
||||||
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_clinical_procedure_template
|
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_clinical_procedure_template
|
||||||
|
|
||||||
|
test_dependencies = ['Item']
|
||||||
|
|
||||||
class TestClinicalProcedure(unittest.TestCase):
|
class TestClinicalProcedure(unittest.TestCase):
|
||||||
def test_procedure_template_item(self):
|
def test_procedure_template_item(self):
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, medical_department, practitioner = create_healthcare_docs()
|
||||||
|
@ -226,7 +226,9 @@ let check_and_set_availability = function(frm) {
|
|||||||
primary_action_label: __('Book'),
|
primary_action_label: __('Book'),
|
||||||
primary_action: function() {
|
primary_action: function() {
|
||||||
frm.set_value('appointment_time', selected_slot);
|
frm.set_value('appointment_time', selected_slot);
|
||||||
|
if (!frm.doc.duration) {
|
||||||
frm.set_value('duration', duration);
|
frm.set_value('duration', duration);
|
||||||
|
}
|
||||||
frm.set_value('practitioner', d.get_value('practitioner'));
|
frm.set_value('practitioner', d.get_value('practitioner'));
|
||||||
frm.set_value('department', d.get_value('department'));
|
frm.set_value('department', d.get_value('department'));
|
||||||
frm.set_value('appointment_date', d.get_value('appointment_date'));
|
frm.set_value('appointment_date', d.get_value('appointment_date'));
|
||||||
|
@ -132,6 +132,9 @@ def get_conditions(filters):
|
|||||||
if filters.get('employee'):
|
if filters.get('employee'):
|
||||||
conditions['name'] = filters.get('employee')
|
conditions['name'] = filters.get('employee')
|
||||||
|
|
||||||
|
if filters.get('company'):
|
||||||
|
conditions['company'] = filters.get('company')
|
||||||
|
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
def get_department_leave_approver_map(department=None):
|
def get_department_leave_approver_map(department=None):
|
||||||
|
@ -494,7 +494,7 @@ class BOM(WebsiteGenerator):
|
|||||||
'image' : d.image,
|
'image' : d.image,
|
||||||
'stock_uom' : d.stock_uom,
|
'stock_uom' : d.stock_uom,
|
||||||
'stock_qty' : flt(d.stock_qty),
|
'stock_qty' : flt(d.stock_qty),
|
||||||
'rate' : flt(d.base_rate) / flt(d.conversion_factor),
|
'rate' : flt(d.base_rate) / (flt(d.conversion_factor) or 1.0),
|
||||||
'include_item_in_manufacturing': d.include_item_in_manufacturing
|
'include_item_in_manufacturing': d.include_item_in_manufacturing
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
@ -90,6 +90,7 @@ def update_latest_price_in_all_boms():
|
|||||||
update_cost()
|
update_cost()
|
||||||
|
|
||||||
def replace_bom(args):
|
def replace_bom(args):
|
||||||
|
frappe.db.auto_commit_on_many_writes = 1
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|
||||||
doc = frappe.get_doc("BOM Update Tool")
|
doc = frappe.get_doc("BOM Update Tool")
|
||||||
@ -97,6 +98,8 @@ def replace_bom(args):
|
|||||||
doc.new_bom = args.new_bom
|
doc.new_bom = args.new_bom
|
||||||
doc.replace_bom()
|
doc.replace_bom()
|
||||||
|
|
||||||
|
frappe.db.auto_commit_on_many_writes = 0
|
||||||
|
|
||||||
def update_cost():
|
def update_cost():
|
||||||
frappe.db.auto_commit_on_many_writes = 1
|
frappe.db.auto_commit_on_many_writes = 1
|
||||||
bom_list = get_boms_in_bottom_up_order()
|
bom_list = get_boms_in_bottom_up_order()
|
||||||
|
@ -7,6 +7,7 @@ def execute():
|
|||||||
frappe.reload_doc('accounts', 'doctype', 'allowed_to_transact_with', force=True)
|
frappe.reload_doc('accounts', 'doctype', 'allowed_to_transact_with', force=True)
|
||||||
frappe.reload_doc('accounts', 'doctype', 'pricing_rule_detail', force=True)
|
frappe.reload_doc('accounts', 'doctype', 'pricing_rule_detail', force=True)
|
||||||
frappe.reload_doc('crm', 'doctype', 'lost_reason_detail', force=True)
|
frappe.reload_doc('crm', 'doctype', 'lost_reason_detail', force=True)
|
||||||
|
frappe.reload_doc('setup', 'doctype', 'quotation_lost_reason_detail', force=True)
|
||||||
|
|
||||||
company = frappe.get_all('Company', filters = {'country': 'United States'})
|
company = frappe.get_all('Company', filters = {'country': 'United States'})
|
||||||
if not company:
|
if not company:
|
||||||
|
@ -3,6 +3,7 @@ import frappe
|
|||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
if frappe.db.exists("DocType", "Lost Reason Detail"):
|
if frappe.db.exists("DocType", "Lost Reason Detail"):
|
||||||
|
frappe.reload_doc("crm", "doctype", "opportunity_lost_reason")
|
||||||
frappe.reload_doc("crm", "doctype", "opportunity_lost_reason_detail")
|
frappe.reload_doc("crm", "doctype", "opportunity_lost_reason_detail")
|
||||||
frappe.reload_doc("setup", "doctype", "quotation_lost_reason_detail")
|
frappe.reload_doc("setup", "doctype", "quotation_lost_reason_detail")
|
||||||
|
|
||||||
|
@ -7,4 +7,7 @@ import frappe
|
|||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
frappe.reload_doc('hr', 'doctype', 'shift_assignment')
|
frappe.reload_doc('hr', 'doctype', 'shift_assignment')
|
||||||
frappe.db.sql("update `tabShift Assignment` set end_date=date, start_date=date where date IS NOT NULL and start_date IS NULL and end_date IS NULL;")
|
if frappe.db.has_column('Shift Assignment', 'date'):
|
||||||
|
frappe.db.sql("""update `tabShift Assignment`
|
||||||
|
set end_date=date, start_date=date
|
||||||
|
where date IS NOT NULL and start_date IS NULL and end_date IS NULL;""")
|
||||||
|
@ -21,34 +21,6 @@ frappe.ui.form.on('Homepage', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
frappe.ui.form.on('Homepage Featured Product', {
|
frappe.ui.form.on('Homepage Featured Product', {
|
||||||
item_code: function(frm, cdt, cdn) {
|
|
||||||
var featured_product = frappe.model.get_doc(cdt, cdn);
|
|
||||||
if (featured_product.item_code) {
|
|
||||||
frappe.call({
|
|
||||||
method: 'frappe.client.get_value',
|
|
||||||
args: {
|
|
||||||
'doctype': 'Item',
|
|
||||||
'filters': {'name': featured_product.item_code},
|
|
||||||
'fieldname': [
|
|
||||||
'item_name',
|
|
||||||
'web_long_description',
|
|
||||||
'description',
|
|
||||||
'image',
|
|
||||||
'thumbnail'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
callback: function(r) {
|
|
||||||
if (!r.exc) {
|
|
||||||
$.extend(featured_product, r.message);
|
|
||||||
if (r.message.web_long_description) {
|
|
||||||
featured_product.description = r.message.web_long_description;
|
|
||||||
}
|
|
||||||
frm.refresh_field('products');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
view: function(frm, cdt, cdn){
|
view: function(frm, cdt, cdn){
|
||||||
var child= locals[cdt][cdn]
|
var child= locals[cdt][cdn]
|
||||||
|
@ -1,301 +1,116 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2016-04-22 05:57:06.261401",
|
"creation": "2016-04-22 05:57:06.261401",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Document",
|
"document_type": "Document",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"item_code",
|
||||||
|
"col_break1",
|
||||||
|
"item_name",
|
||||||
|
"view",
|
||||||
|
"section_break_5",
|
||||||
|
"description",
|
||||||
|
"column_break_7",
|
||||||
|
"image",
|
||||||
|
"thumbnail",
|
||||||
|
"route"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 1,
|
"bold": 1,
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "item_code",
|
"fieldname": "item_code",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Item Code",
|
"label": "Item Code",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "item_code",
|
"oldfieldname": "item_code",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Item",
|
"options": "Item",
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "150px",
|
"print_width": "150px",
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1,
|
"search_index": 1,
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "150px"
|
"width": "150px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "col_break1",
|
"fieldname": "col_break1",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fetch_from": "item_code.item_name",
|
||||||
"bold": 0,
|
"fetch_if_empty": 1,
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "item_name",
|
"fieldname": "item_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Item Name",
|
"label": "Item Name",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "item_name",
|
"oldfieldname": "item_name",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "150",
|
"print_width": "150",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "150"
|
"width": "150"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "view",
|
"fieldname": "view",
|
||||||
"fieldtype": "Button",
|
"fieldtype": "Button",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "View",
|
"label": "View"
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"fieldname": "section_break_5",
|
"fieldname": "section_break_5",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Description"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Description",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fetch_from": "item_code.web_long_description",
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Text Editor",
|
"fieldtype": "Text Editor",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Description",
|
"label": "Description",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "description",
|
"oldfieldname": "description",
|
||||||
"oldfieldtype": "Small Text",
|
"oldfieldtype": "Small Text",
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "300px",
|
"print_width": "300px",
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "300px"
|
"width": "300px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "column_break_7",
|
"fieldname": "column_break_7",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fetch_from": "item_code.website_image",
|
||||||
"bold": 0,
|
"fetch_if_empty": 1,
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach Image",
|
"fieldtype": "Attach Image",
|
||||||
"hidden": 0,
|
"label": "Image"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Image",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "thumbnail",
|
"fieldname": "thumbnail",
|
||||||
"fieldtype": "Attach Image",
|
"fieldtype": "Attach Image",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 0,
|
"label": "Thumbnail"
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Thumbnail",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "route",
|
"fieldname": "route",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "route",
|
"label": "route",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
"index_web_pages_for_search": 1,
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2016-08-09 06:09:34.731971",
|
"modified": "2020-08-25 15:27:49.573537",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Portal",
|
"module": "Portal",
|
||||||
"name": "Homepage Featured Product",
|
"name": "Homepage Featured Product",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC"
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
@ -3,7 +3,7 @@
|
|||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"label": "Retail Operations",
|
"label": "Retail Operations",
|
||||||
"links": "[\n {\n \"description\": \"Setup default values for POS Invoices\",\n \"label\": \"Point-of-Sale Profile\",\n \"name\": \"POS Profile\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"POS Profile\"\n ],\n \"description\": \"Point of Sale\",\n \"label\": \"POS\",\n \"name\": \"pos\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"description\": \"Cashier Closing\",\n \"label\": \"Cashier Closing\",\n \"name\": \"Cashier Closing\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup mode of POS (Online / Offline)\",\n \"label\": \"POS Settings\",\n \"name\": \"POS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To make Customer based incentive schemes.\",\n \"label\": \"Loyalty Program\",\n \"name\": \"Loyalty Program\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To view logs of Loyalty Points assigned to a Customer.\",\n \"label\": \"Loyalty Point Entry\",\n \"name\": \"Loyalty Point Entry\",\n \"type\": \"doctype\"\n }\n]"
|
"links": "[\n {\n \"description\": \"Setup default values for POS Invoices\",\n \"label\": \"Point of Sale Profile\",\n \"name\": \"POS Profile\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"POS Profile\"\n ],\n \"description\": \"Point of Sale\",\n \"label\": \"Point of Sale\",\n \"name\": \"point-of-sale\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"description\": \"Setup mode of POS (Online / Offline)\",\n \"label\": \"POS Settings\",\n \"name\": \"POS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Cashier Closing\",\n \"label\": \"Cashier Closing\",\n \"name\": \"Cashier Closing\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To make Customer based incentive schemes.\",\n \"label\": \"Loyalty Program\",\n \"name\": \"Loyalty Program\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To view logs of Loyalty Points assigned to a Customer.\",\n \"label\": \"Loyalty Point Entry\",\n \"name\": \"Loyalty Point Entry\",\n \"type\": \"doctype\"\n }\n]"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"category": "Domains",
|
"category": "Domains",
|
||||||
@ -14,10 +14,11 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Desk Page",
|
"doctype": "Desk Page",
|
||||||
"extends_another_page": 0,
|
"extends_another_page": 0,
|
||||||
|
"hide_custom": 0,
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "Retail",
|
"label": "Retail",
|
||||||
"modified": "2020-04-26 22:42:39.346750",
|
"modified": "2020-08-20 18:00:07.515691",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Retail",
|
"name": "Retail",
|
||||||
@ -25,5 +26,27 @@
|
|||||||
"pin_to_bottom": 0,
|
"pin_to_bottom": 0,
|
||||||
"pin_to_top": 0,
|
"pin_to_top": 0,
|
||||||
"restrict_to_domain": "Retail",
|
"restrict_to_domain": "Retail",
|
||||||
"shortcuts": []
|
"shortcuts": [
|
||||||
|
{
|
||||||
|
"color": "#9deca2",
|
||||||
|
"doc_view": "",
|
||||||
|
"format": "{} Active",
|
||||||
|
"label": "Point of Sale Profile",
|
||||||
|
"link_to": "POS Profile",
|
||||||
|
"stats_filter": "{\n \"disabled\": 0\n}",
|
||||||
|
"type": "DocType"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doc_view": "",
|
||||||
|
"label": "Point of Sale",
|
||||||
|
"link_to": "point-of-sale",
|
||||||
|
"type": "Page"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doc_view": "",
|
||||||
|
"label": "POS Settings",
|
||||||
|
"link_to": "POS Settings",
|
||||||
|
"type": "DocType"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
@ -33,7 +33,7 @@ def get_data():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Support'),
|
'label': _('Support'),
|
||||||
'items': ['Issue']
|
'items': ['Issue', 'Maintenance Visit', 'Installation Note', 'Warranty Claim']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Projects'),
|
'label': _('Projects'),
|
||||||
|
@ -10,6 +10,7 @@ def get_data():
|
|||||||
'Payment Entry': 'reference_name',
|
'Payment Entry': 'reference_name',
|
||||||
'Payment Request': 'reference_name',
|
'Payment Request': 'reference_name',
|
||||||
'Auto Repeat': 'reference_document',
|
'Auto Repeat': 'reference_document',
|
||||||
|
'Maintenance Visit': 'prevdoc_docname'
|
||||||
},
|
},
|
||||||
'internal_links': {
|
'internal_links': {
|
||||||
'Quotation': ['items', 'prevdoc_docname']
|
'Quotation': ['items', 'prevdoc_docname']
|
||||||
@ -17,7 +18,7 @@ def get_data():
|
|||||||
'transactions': [
|
'transactions': [
|
||||||
{
|
{
|
||||||
'label': _('Fulfillment'),
|
'label': _('Fulfillment'),
|
||||||
'items': ['Sales Invoice', 'Pick List', 'Delivery Note']
|
'items': ['Sales Invoice', 'Pick List', 'Delivery Note', 'Maintenance Visit']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Purchasing'),
|
'label': _('Purchasing'),
|
||||||
|
@ -26,7 +26,8 @@ def delete_company_transactions(company_name):
|
|||||||
tabDocField where fieldtype='Link' and options='Company'"""):
|
tabDocField where fieldtype='Link' and options='Company'"""):
|
||||||
if doctype not in ("Account", "Cost Center", "Warehouse", "Budget",
|
if doctype not in ("Account", "Cost Center", "Warehouse", "Budget",
|
||||||
"Party Account", "Employee", "Sales Taxes and Charges Template",
|
"Party Account", "Employee", "Sales Taxes and Charges Template",
|
||||||
"Purchase Taxes and Charges Template", "POS Profile", 'BOM'):
|
"Purchase Taxes and Charges Template", "POS Profile", "BOM",
|
||||||
|
"Company", "Bank Account"):
|
||||||
delete_for_doctype(doctype, company_name)
|
delete_for_doctype(doctype, company_name)
|
||||||
|
|
||||||
# reset company values
|
# reset company values
|
||||||
|
@ -111,6 +111,7 @@ class Item(WebsiteGenerator):
|
|||||||
self.synced_with_hub = 0
|
self.synced_with_hub = 0
|
||||||
|
|
||||||
self.validate_has_variants()
|
self.validate_has_variants()
|
||||||
|
self.validate_attributes_in_variants()
|
||||||
self.validate_stock_exists_for_template_item()
|
self.validate_stock_exists_for_template_item()
|
||||||
self.validate_attributes()
|
self.validate_attributes()
|
||||||
self.validate_variant_attributes()
|
self.validate_variant_attributes()
|
||||||
@ -806,6 +807,77 @@ class Item(WebsiteGenerator):
|
|||||||
if frappe.db.exists("Item", {"variant_of": self.name}):
|
if frappe.db.exists("Item", {"variant_of": self.name}):
|
||||||
frappe.throw(_("Item has variants."))
|
frappe.throw(_("Item has variants."))
|
||||||
|
|
||||||
|
def validate_attributes_in_variants(self):
|
||||||
|
if not self.has_variants or self.get("__islocal"):
|
||||||
|
return
|
||||||
|
|
||||||
|
old_doc = self.get_doc_before_save()
|
||||||
|
old_doc_attributes = set([attr.attribute for attr in old_doc.attributes])
|
||||||
|
own_attributes = [attr.attribute for attr in self.attributes]
|
||||||
|
|
||||||
|
# Check if old attributes were removed from the list
|
||||||
|
# Is old_attrs is a subset of new ones
|
||||||
|
# that means we need not check any changes
|
||||||
|
if old_doc_attributes.issubset(set(own_attributes)):
|
||||||
|
return
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
# get all item variants
|
||||||
|
items = [item["name"] for item in frappe.get_all("Item", {"variant_of": self.name})]
|
||||||
|
|
||||||
|
# get all deleted attributes
|
||||||
|
deleted_attribute = list(old_doc_attributes.difference(set(own_attributes)))
|
||||||
|
|
||||||
|
# fetch all attributes of these items
|
||||||
|
item_attributes = frappe.get_all(
|
||||||
|
"Item Variant Attribute",
|
||||||
|
filters={
|
||||||
|
"parent": ["in", items],
|
||||||
|
"attribute": ["in", deleted_attribute]
|
||||||
|
},
|
||||||
|
fields=["attribute", "parent"]
|
||||||
|
)
|
||||||
|
not_included = defaultdict(list)
|
||||||
|
|
||||||
|
for attr in item_attributes:
|
||||||
|
if attr["attribute"] not in own_attributes:
|
||||||
|
not_included[attr["parent"]].append(attr["attribute"])
|
||||||
|
|
||||||
|
if not len(not_included):
|
||||||
|
return
|
||||||
|
|
||||||
|
def body(docnames):
|
||||||
|
docnames.sort()
|
||||||
|
return "<br>".join(docnames)
|
||||||
|
|
||||||
|
def table_row(title, body):
|
||||||
|
return """<tr>
|
||||||
|
<td>{0}</td>
|
||||||
|
<td>{1}</td>
|
||||||
|
</tr>""".format(title, body)
|
||||||
|
|
||||||
|
rows = ''
|
||||||
|
for docname, attr_list in not_included.items():
|
||||||
|
link = "<a href='#Form/Item/{0}'>{0}</a>".format(frappe.bold(_(docname)))
|
||||||
|
rows += table_row(link, body(attr_list))
|
||||||
|
|
||||||
|
error_description = _('The following deleted attributes exist in Variants but not in the Template. You can either delete the Variants or keep the attribute(s) in template.')
|
||||||
|
|
||||||
|
message = """
|
||||||
|
<div>{0}</div><br>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<td>{1}</td>
|
||||||
|
<td>{2}</td>
|
||||||
|
</thead>
|
||||||
|
{3}
|
||||||
|
</table>
|
||||||
|
""".format(error_description, _('Variant Items'), _('Attributes'), rows)
|
||||||
|
|
||||||
|
frappe.throw(message, title=_("Variant Attribute Error"), is_minimizable=True, wide=True)
|
||||||
|
|
||||||
|
|
||||||
def validate_stock_exists_for_template_item(self):
|
def validate_stock_exists_for_template_item(self):
|
||||||
if self.stock_ledger_created() and self._doc_before_save:
|
if self.stock_ledger_created() and self._doc_before_save:
|
||||||
if (cint(self._doc_before_save.has_variants) != cint(self.has_variants)
|
if (cint(self._doc_before_save.has_variants) != cint(self.has_variants)
|
||||||
|
@ -227,6 +227,14 @@ class PurchaseReceipt(BuyingController):
|
|||||||
if not stock_value_diff:
|
if not stock_value_diff:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# If PR is sub-contracted and fg item rate is zero
|
||||||
|
# in that case if account for shource and target warehouse are same,
|
||||||
|
# then GL entries should not be posted
|
||||||
|
if flt(stock_value_diff) == flt(d.rm_supp_cost) \
|
||||||
|
and warehouse_account.get(self.supplier_warehouse) \
|
||||||
|
and warehouse_account[d.warehouse]["account"] == warehouse_account[self.supplier_warehouse]["account"]:
|
||||||
|
continue
|
||||||
|
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": warehouse_account[d.warehouse]["account"],
|
"account": warehouse_account[d.warehouse]["account"],
|
||||||
"against": stock_rbnb,
|
"against": stock_rbnb,
|
||||||
@ -242,7 +250,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
credit_amount = flt(d.base_net_amount, d.precision("base_net_amount")) \
|
credit_amount = flt(d.base_net_amount, d.precision("base_net_amount")) \
|
||||||
if credit_currency == self.company_currency else flt(d.net_amount, d.precision("net_amount"))
|
if credit_currency == self.company_currency else flt(d.net_amount, d.precision("net_amount"))
|
||||||
|
if credit_amount:
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": warehouse_account[d.from_warehouse]['account'] \
|
"account": warehouse_account[d.from_warehouse]['account'] \
|
||||||
if d.from_warehouse else stock_rbnb,
|
if d.from_warehouse else stock_rbnb,
|
||||||
|
@ -18,6 +18,28 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
|
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
|
||||||
|
|
||||||
|
def test_reverse_purchase_receipt_sle(self):
|
||||||
|
|
||||||
|
frappe.db.set_value('UOM', '_Test UOM', 'must_be_whole_number', 0)
|
||||||
|
|
||||||
|
pr = make_purchase_receipt(qty=0.5)
|
||||||
|
|
||||||
|
sl_entry = frappe.db.get_all("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
|
||||||
|
"voucher_no": pr.name}, ['actual_qty'])
|
||||||
|
|
||||||
|
self.assertEqual(len(sl_entry), 1)
|
||||||
|
self.assertEqual(sl_entry[0].actual_qty, 0.5)
|
||||||
|
|
||||||
|
pr.cancel()
|
||||||
|
|
||||||
|
sl_entry_cancelled = frappe.db.get_all("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
|
||||||
|
"voucher_no": pr.name}, ['actual_qty'], order_by='creation')
|
||||||
|
|
||||||
|
self.assertEqual(len(sl_entry_cancelled), 2)
|
||||||
|
self.assertEqual(sl_entry_cancelled[1].actual_qty, -0.5)
|
||||||
|
|
||||||
|
frappe.db.set_value('UOM', '_Test UOM', 'must_be_whole_number', 1)
|
||||||
|
|
||||||
def test_make_purchase_invoice(self):
|
def test_make_purchase_invoice(self):
|
||||||
pr = make_purchase_receipt(do_not_save=True)
|
pr = make_purchase_receipt(do_not_save=True)
|
||||||
self.assertRaises(frappe.ValidationError, make_purchase_invoice, pr.name)
|
self.assertRaises(frappe.ValidationError, make_purchase_invoice, pr.name)
|
||||||
@ -121,6 +143,22 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
rm_supp_cost = sum([d.amount for d in pr.get("supplied_items")])
|
rm_supp_cost = sum([d.amount for d in pr.get("supplied_items")])
|
||||||
self.assertEqual(pr.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2))
|
self.assertEqual(pr.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2))
|
||||||
|
|
||||||
|
def test_subcontracting_gle_fg_item_rate_zero(self):
|
||||||
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||||
|
set_perpetual_inventory()
|
||||||
|
frappe.db.set_value("Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM")
|
||||||
|
make_stock_entry(item_code="_Test Item", target="Work In Progress - TCP1", qty=100, basic_rate=100, company="_Test Company with perpetual inventory")
|
||||||
|
make_stock_entry(item_code="_Test Item Home Desktop 100", target="Work In Progress - TCP1",
|
||||||
|
qty=100, basic_rate=100, company="_Test Company with perpetual inventory")
|
||||||
|
pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=0, is_subcontracted="Yes",
|
||||||
|
company="_Test Company with perpetual inventory", warehouse='Stores - TCP1', supplier_warehouse='Work In Progress - TCP1')
|
||||||
|
|
||||||
|
gl_entries = get_gl_entries("Purchase Receipt", pr.name)
|
||||||
|
|
||||||
|
self.assertFalse(gl_entries)
|
||||||
|
|
||||||
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
def test_serial_no_supplier(self):
|
def test_serial_no_supplier(self):
|
||||||
pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1)
|
pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1)
|
||||||
self.assertEqual(frappe.db.get_value("Serial No", pr.get("items")[0].serial_no, "supplier"),
|
self.assertEqual(frappe.db.get_value("Serial No", pr.get("items")[0].serial_no, "supplier"),
|
||||||
@ -688,7 +726,7 @@ def make_purchase_receipt(**args):
|
|||||||
"received_qty": received_qty,
|
"received_qty": received_qty,
|
||||||
"rejected_qty": rejected_qty,
|
"rejected_qty": rejected_qty,
|
||||||
"rejected_warehouse": args.rejected_warehouse or "_Test Rejected Warehouse - _TC" if rejected_qty != 0 else "",
|
"rejected_warehouse": args.rejected_warehouse or "_Test Rejected Warehouse - _TC" if rejected_qty != 0 else "",
|
||||||
"rate": args.rate or 50,
|
"rate": args.rate if args.rate != None else 50,
|
||||||
"conversion_factor": args.conversion_factor or 1.0,
|
"conversion_factor": args.conversion_factor or 1.0,
|
||||||
"serial_no": args.serial_no,
|
"serial_no": args.serial_no,
|
||||||
"stock_uom": args.stock_uom or "_Test UOM",
|
"stock_uom": args.stock_uom or "_Test UOM",
|
||||||
|
@ -31,7 +31,7 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc
|
|||||||
sle['posting_time'] = now_datetime().strftime('%H:%M:%S.%f')
|
sle['posting_time'] = now_datetime().strftime('%H:%M:%S.%f')
|
||||||
|
|
||||||
if cancel:
|
if cancel:
|
||||||
sle['actual_qty'] = -flt(sle.get('actual_qty'), 0)
|
sle['actual_qty'] = -flt(sle.get('actual_qty'))
|
||||||
|
|
||||||
if sle['actual_qty'] < 0 and not sle.get('outgoing_rate'):
|
if sle['actual_qty'] < 0 and not sle.get('outgoing_rate'):
|
||||||
sle['outgoing_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code,
|
sle['outgoing_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code,
|
||||||
|
@ -209,11 +209,11 @@ function set_time_to_resolve_and_response(frm) {
|
|||||||
|
|
||||||
frm.dashboard.set_headline_alert(
|
frm.dashboard.set_headline_alert(
|
||||||
'<div class="row">' +
|
'<div class="row">' +
|
||||||
'<div class="col-xs-6">' +
|
'<div class="col-xs-12 col-sm-6">' +
|
||||||
'<span class="indicator whitespace-nowrap '+ time_to_respond.indicator +'"><span class="hidden-xs">Time to Respond: '+ time_to_respond.diff_display +'</span></span> ' +
|
'<span class="indicator whitespace-nowrap '+ time_to_respond.indicator +'"><span>Time to Respond: '+ time_to_respond.diff_display +'</span></span> ' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'<div class="col-xs-6">' +
|
'<div class="col-xs-12 col-sm-6">' +
|
||||||
'<span class="indicator whitespace-nowrap '+ time_to_resolve.indicator +'"><span class="hidden-xs">Time to Resolve: '+ time_to_resolve.diff_display +'</span></span> ' +
|
'<span class="indicator whitespace-nowrap '+ time_to_resolve.indicator +'"><span>Time to Resolve: '+ time_to_resolve.diff_display +'</span></span> ' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'</div>'
|
'</div>'
|
||||||
);
|
);
|
||||||
|
@ -20,6 +20,13 @@ frappe.ready(() => {
|
|||||||
options: 'Email',
|
options: 'Email',
|
||||||
reqd: 1
|
reqd: 1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldtype: 'Data',
|
||||||
|
label: __('Phone Number'),
|
||||||
|
fieldname: 'phone',
|
||||||
|
options: 'Phone',
|
||||||
|
reqd: 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fieldtype: 'Data',
|
fieldtype: 'Data',
|
||||||
label: __('Subject'),
|
label: __('Subject'),
|
||||||
|
Loading…
Reference in New Issue
Block a user