Merge branch 'staging-fixes' into staging

This commit is contained in:
Ameya Shenoy 2018-10-04 11:42:56 +00:00
commit c6eac7c60b
No known key found for this signature in database
GPG Key ID: AC016A555657D0A3
103 changed files with 11239 additions and 9550 deletions

View File

@ -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__ = '10.1.54' __version__ = '10.1.55'
def get_default_company(user=None): def get_default_company(user=None):
'''Get default company for user''' '''Get default company for user'''

View File

@ -35,8 +35,8 @@ def validate_service_stop_date(doc):
def convert_deferred_expense_to_expense(start_date=None, end_date=None): def convert_deferred_expense_to_expense(start_date=None, end_date=None):
# check for the purchase invoice for which GL entries has to be done # check for the purchase invoice for which GL entries has to be done
invoices = frappe.db.sql_list(''' invoices = frappe.db.sql_list('''
select parent from `tabPurchase Invoice Item` where service_start_date<=%s and service_end_date>=%s select distinct parent from `tabPurchase Invoice Item` where service_start_date<=%s and service_end_date>=%s
and enable_deferred_expense = 1 and docstatus = 1 and enable_deferred_expense = 1 and docstatus = 1 and ifnull(amount, 0) > 0
''', (end_date or today(), start_date or add_months(today(), -1))) ''', (end_date or today(), start_date or add_months(today(), -1)))
# For each invoice, book deferred expense # For each invoice, book deferred expense
@ -47,8 +47,8 @@ def convert_deferred_expense_to_expense(start_date=None, end_date=None):
def convert_deferred_revenue_to_income(start_date=None, end_date=None): def convert_deferred_revenue_to_income(start_date=None, end_date=None):
# check for the sales invoice for which GL entries has to be done # check for the sales invoice for which GL entries has to be done
invoices = frappe.db.sql_list(''' invoices = frappe.db.sql_list('''
select parent from `tabSales Invoice Item` where service_start_date<=%s and service_end_date>=%s select distinct parent from `tabSales Invoice Item` where service_start_date<=%s and service_end_date>=%s
and enable_deferred_revenue = 1 and docstatus = 1 and enable_deferred_revenue = 1 and docstatus = 1 and ifnull(amount, 0) > 0
''', (end_date or today(), start_date or add_months(today(), -1))) ''', (end_date or today(), start_date or add_months(today(), -1)))
# For each invoice, book deferred revenue # For each invoice, book deferred revenue

View File

@ -58,7 +58,6 @@ class CostCenter(NestedSet):
# Validate properties before merging # Validate properties before merging
super(CostCenter, self).before_rename(olddn, new_cost_center, merge, "is_group") super(CostCenter, self).before_rename(olddn, new_cost_center, merge, "is_group")
if not merge: if not merge:
from erpnext.accounts.doctype.account.account import get_name_with_number
new_cost_center = get_name_with_number(new_cost_center, self.cost_center_number) new_cost_center = get_name_with_number(new_cost_center, self.cost_center_number)
return new_cost_center return new_cost_center
@ -89,3 +88,8 @@ class CostCenter(NestedSet):
def on_doctype_update(): def on_doctype_update():
frappe.db.add_index("Cost Center", ["lft", "rgt"]) frappe.db.add_index("Cost Center", ["lft", "rgt"])
def get_name_with_number(new_account, account_number):
if account_number and not new_account[0].isdigit():
new_account = account_number + " - " + new_account
return new_account

View File

@ -23,7 +23,6 @@ frappe.ui.form.on('Payment Entry', {
} }
} }
}); });
frm.set_query("party_type", function() { frm.set_query("party_type", function() {
return{ return{
"filters": { "filters": {
@ -31,7 +30,17 @@ frappe.ui.form.on('Payment Entry', {
} }
} }
}); });
frm.set_query("contact_person", function() {
if (frm.doc.party) {
return {
query: 'frappe.contacts.doctype.contact.contact.contact_query',
filters: {
link_doctype: frm.doc.party_type,
link_name: frm.doc.party
}
};
}
});
frm.set_query("paid_to", function() { frm.set_query("paid_to", function() {
var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ? var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ?
["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
@ -114,6 +123,11 @@ frappe.ui.form.on('Payment Entry', {
frm.events.set_dynamic_labels(frm); frm.events.set_dynamic_labels(frm);
}, },
contact_person: function(frm) {
frm.set_value("contact_email", "");
erpnext.utils.get_contact_details(frm);
},
hide_unhide_fields: function(frm) { hide_unhide_fields: function(frm) {
var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: ""; var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: "";
@ -230,13 +244,16 @@ frappe.ui.form.on('Payment Entry', {
}, },
party: function(frm) { party: function(frm) {
if (frm.doc.contact_email || frm.doc.contact_person) {
frm.set_value("contact_email", "");
frm.set_value("contact_person", "");
}
if(frm.doc.payment_type && frm.doc.party_type && frm.doc.party) { if(frm.doc.payment_type && frm.doc.party_type && frm.doc.party) {
if(!frm.doc.posting_date) { if(!frm.doc.posting_date) {
frappe.msgprint(__("Please select Posting Date before selecting Party")) frappe.msgprint(__("Please select Posting Date before selecting Party"))
frm.set_value("party", ""); frm.set_value("party", "");
return ; return ;
} }
frm.set_party_account_based_on_party = true; frm.set_party_account_based_on_party = true;
return frappe.call({ return frappe.call({

View File

@ -376,6 +376,40 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "party",
"fieldname": "contact_person",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Contact",
"length": 0,
"no_copy": 0,
"options": "Contact",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -441,6 +475,40 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "contact_person",
"fieldname": "contact_email",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Email",
"length": 0,
"no_copy": 0,
"options": "Email",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -1972,7 +2040,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-09-11 15:44:28.647566", "modified": "2018-09-25 14:38:48.312629",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Payment Entry", "name": "Payment Entry",

View File

@ -790,7 +790,6 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
@frappe.whitelist() @frappe.whitelist()
def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None): def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None):
doc = frappe.get_doc(dt, dn) doc = frappe.get_doc(dt, dn)
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0: if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0:
frappe.throw(_("Can only make payment against unbilled {0}").format(dt)) frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
@ -877,7 +876,8 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
pe.mode_of_payment = doc.get("mode_of_payment") pe.mode_of_payment = doc.get("mode_of_payment")
pe.party_type = party_type pe.party_type = party_type
pe.party = doc.get(scrub(party_type)) pe.party = doc.get(scrub(party_type))
pe.contact_person = doc.get("contact_person")
pe.contact_email = doc.get("contact_email")
pe.ensure_supplier_is_not_blocked() pe.ensure_supplier_is_not_blocked()
pe.paid_from = party_account if payment_type=="Receive" else bank.account pe.paid_from = party_account if payment_type=="Receive" else bank.account

View File

@ -116,6 +116,18 @@ frappe.ui.form.on('Pricing Rule', {
}; };
}, },
onload: function(frm) {
if(frm.doc.__islocal && !frm.doc.applicable_for && (frm.doc.customer || frm.doc.supplier)) {
if(frm.doc.customer) {
frm.doc.applicable_for = "Customer";
frm.doc.selling = 1
} else {
frm.doc.applicable_for = "Supplier";
frm.doc.buying = 1
}
}
},
refresh: function(frm) { refresh: function(frm) {
var help_content = var help_content =
`<table class="table table-bordered" style="background-color: #f9f9f9;"> `<table class="table table-bordered" style="background-color: #f9f9f9;">

View File

@ -384,3 +384,13 @@ def set_transaction_type(args):
args.transaction_type = "selling" args.transaction_type = "selling"
else: else:
args.transaction_type = "buying" args.transaction_type = "buying"
@frappe.whitelist()
def make_pricing_rule(doctype, docname):
doc = frappe.new_doc("Pricing Rule")
doc.applicable_for = doctype
doc.set(frappe.scrub(doctype), docname)
doc.selling = 1 if doctype == "Customer" else 0
doc.buying = 1 if doctype == "Supplier" else 0
return doc

View File

@ -77,8 +77,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
} }
if (doc.outstanding_amount > 0 && !cint(doc.is_return)) { if (doc.outstanding_amount > 0 && !cint(doc.is_return)) {
cur_frm.add_custom_button(__('Payment Request'), cur_frm.add_custom_button(__('Payment Request'), function() {
this.make_payment_request, __("Make")); me.make_payment_request()
}, __("Make"));
} }
if(doc.docstatus===0) { if(doc.docstatus===0) {

View File

@ -403,16 +403,20 @@ class PurchaseInvoice(BuyingController):
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
warehouse_account = get_warehouse_account_map() warehouse_account = get_warehouse_account_map()
voucher_wise_stock_value = {}
if self.update_stock:
for d in frappe.get_all('Stock Ledger Entry',
fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}):
voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference)
for item in self.get("items"): for item in self.get("items"):
if flt(item.base_net_amount): if flt(item.base_net_amount):
account_currency = get_account_currency(item.expense_account) account_currency = get_account_currency(item.expense_account)
if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items: if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items:
val_rate_db_precision = 6 if cint(item.precision("valuation_rate")) <= 6 else 9
# warehouse account # warehouse account
warehouse_debit_amount = flt(flt(item.valuation_rate, val_rate_db_precision) warehouse_debit_amount = self.make_stock_adjustment_entry(gl_entries,
* flt(item.qty) * flt(item.conversion_factor), item.precision("base_net_amount")) item, voucher_wise_stock_value, account_currency)
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
@ -552,6 +556,36 @@ class PurchaseInvoice(BuyingController):
return gl_entries return gl_entries
def make_stock_adjustment_entry(self, gl_entries, item, voucher_wise_stock_value, account_currency):
net_amt_precision = item.precision("base_net_amount")
val_rate_db_precision = 6 if cint(item.precision("valuation_rate")) <= 6 else 9
warehouse_debit_amount = flt(flt(item.valuation_rate, val_rate_db_precision)
* flt(item.qty) * flt(item.conversion_factor), net_amt_precision)
# Stock ledger value is not matching with the warehouse amount
if (self.update_stock and voucher_wise_stock_value.get(item.name) and
warehouse_debit_amount != flt(voucher_wise_stock_value.get(item.name), net_amt_precision)):
cost_of_goods_sold_account = self.get_company_default("default_expense_account")
stock_amount = flt(voucher_wise_stock_value.get(item.name), net_amt_precision)
stock_adjustment_amt = warehouse_debit_amount - stock_amount
gl_entries.append(
self.get_gl_dict({
"account": cost_of_goods_sold_account,
"against": item.expense_account,
"debit": stock_adjustment_amt,
"remarks": self.get("remarks") or _("Stock Adjustment"),
"cost_center": item.cost_center,
"project": item.project
}, account_currency)
)
warehouse_debit_amount = stock_amount
return warehouse_debit_amount
def make_tax_gl_entries(self, gl_entries): def make_tax_gl_entries(self, gl_entries):
# tax table gl entries # tax table gl entries
valuation_tax = {} valuation_tax = {}

View File

@ -1742,7 +1742,7 @@
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 1,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
@ -2544,7 +2544,7 @@
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-09-04 10:11:28.246395", "modified": "2018-10-04 09:05:43.166721",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice Item", "name": "Purchase Invoice Item",

View File

@ -35,6 +35,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}, },
refresh: function(doc, dt, dn) { refresh: function(doc, dt, dn) {
const me = this;
this._super(); this._super();
if(cur_frm.msgbox && cur_frm.msgbox.$wrapper.is(":visible")) { if(cur_frm.msgbox && cur_frm.msgbox.$wrapper.is(":visible")) {
// hide new msgbox // hide new msgbox
@ -82,9 +83,10 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
} }
} }
if(doc.outstanding_amount>0 && !cint(doc.is_return)) { if (doc.outstanding_amount>0 && !cint(doc.is_return)) {
cur_frm.add_custom_button(__('Payment Request'), cur_frm.add_custom_button(__('Payment Request'), function() {
this.make_payment_request, __("Make")); me.make_payment_request();
}, __("Make"));
} }
if(!doc.auto_repeat) { if(!doc.auto_repeat) {
@ -102,7 +104,6 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
} }
this.set_default_print_format(); this.set_default_print_format();
var me = this;
if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) { if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) {
frappe.model.with_doc("Customer", me.frm.doc.customer, function() { frappe.model.with_doc("Customer", me.frm.doc.customer, function() {
var customer = frappe.model.get_doc("Customer", me.frm.doc.customer); var customer = frappe.model.get_doc("Customer", me.frm.doc.customer);

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, erpnext import frappe, erpnext
import frappe.defaults import frappe.defaults
from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate
from frappe import _, msgprint, throw from frappe import _, msgprint, throw
from erpnext.accounts.party import get_party_account, get_due_date from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.controllers.stock_controller import update_gl_entries_after from erpnext.controllers.stock_controller import update_gl_entries_after
@ -54,9 +54,18 @@ class SalesInvoice(SellingController):
def set_indicator(self): def set_indicator(self):
"""Set indicator for portal""" """Set indicator for portal"""
if self.outstanding_amount > 0: if cint(self.is_return) == 1:
self.indicator_title = _("Return")
self.indicator_color = "darkgrey"
elif self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()):
self.indicator_color = "orange" self.indicator_color = "orange"
self.indicator_title = _("Unpaid") self.indicator_title = _("Unpaid")
elif self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()):
self.indicator_color = "red"
self.indicator_title = _("Overdue")
elif self.outstanding_amount < 0:
self.indicator_title = _("Credit Note Issued")
self.indicator_color = "darkgrey"
else: else:
self.indicator_color = "green" self.indicator_color = "green"
self.indicator_title = _("Paid") self.indicator_title = _("Paid")

View File

@ -3,6 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, erpnext import frappe, erpnext
from frappe.utils import flt
from frappe import _ from frappe import _
def execute(filters=None): def execute(filters=None):
@ -10,47 +11,73 @@ def execute(filters=None):
return columns, data return columns, data
def get_data(filters): def get_data(filters):
data = frappe.db.sql(""" data = []
select depreciation_accounts = frappe.db.sql_list(""" select name from tabAccount
a.name as asset, a.asset_category, a.status, where ifnull(account_type, '') = 'Depreciation' """)
ds.depreciation_method, a.purchase_date, a.gross_purchase_amount,
ds.schedule_date as depreciation_date, ds.depreciation_amount, filters_data = [["company", "=", filters.get('company')],
ds.accumulated_depreciation_amount, ["posting_date", ">=", filters.get('from_date')],
(a.gross_purchase_amount - ds.accumulated_depreciation_amount) as amount_after_depreciation, ["posting_date", "<=", filters.get('to_date')],
ds.journal_entry as depreciation_entry ["against_voucher_type", "=", "Asset"],
from ["account", "in", depreciation_accounts]]
`tabAsset` a, `tabDepreciation Schedule` ds
where if filters.get("asset"):
a.name = ds.parent filters_data.append(["against_voucher", "=", filters.get("asset")])
and a.docstatus=1
and ifnull(ds.journal_entry, '') != '' if filters.get("asset_category"):
and ds.schedule_date between %(from_date)s and %(to_date)s
and a.company = %(company)s assets = frappe.db.sql_list("""select name from tabAsset
{conditions} where asset_category = %s and docstatus=1""", filters.get("asset_category"))
order by
a.name asc, ds.schedule_date asc filters_data.append(["against_voucher", "in", assets])
""".format(conditions=get_filter_conditions(filters)), filters, as_dict=1)
company_finance_book = erpnext.get_default_finance_book(filters.get("company"))
if (not filters.get('finance_book') or (filters.get('finance_book') == company_finance_book)):
filters_data.append(["finance_book", "in", ['', filters.get('finance_book')]])
elif filters.get("finance_book"):
filters_data.append(["finance_book", "=", filters.get('finance_book')])
gl_entries = frappe.get_all('GL Entry',
filters= filters_data,
fields = ["against_voucher", "debit_in_account_currency as debit", "voucher_no", "posting_date"],
order_by= "against_voucher, posting_date")
if not gl_entries:
return data
assets = [d.against_voucher for d in gl_entries]
assets_details = get_assets_details(assets)
for d in gl_entries:
asset_data = assets_details.get(d.against_voucher)
if not asset_data.get("accumulated_depreciation_amount"):
asset_data.accumulated_depreciation_amount = d.debit
else:
asset_data.accumulated_depreciation_amount += d.debit
row = frappe._dict(asset_data)
row.update({
"depreciation_amount": d.debit,
"depreciation_date": d.posting_date,
"amount_after_depreciation": (flt(row.gross_purchase_amount) -
flt(row.accumulated_depreciation_amount)),
"depreciation_entry": d.voucher_no
})
data.append(row)
return data return data
def get_filter_conditions(filters): def get_assets_details(assets):
conditions = "" assets_details = {}
if filters.get("asset"): fields = ["name as asset", "gross_purchase_amount",
conditions += " and a.name = %(asset)s" "asset_category", "status", "depreciation_method", "purchase_date"]
if filters.get("asset_category"): for d in frappe.get_all("Asset", fields = fields, filters = {'name': ('in', assets)}):
conditions += " and a.asset_category = %(asset_category)s" assets_details.setdefault(d.asset, d)
company_finance_book = erpnext.get_default_finance_book(filters.get("company")) return assets_details
if (not filters.get('finance_book') or (filters.get('finance_book') == company_finance_book)):
filters['finance_book'] = company_finance_book
conditions += " and ifnull(ds.finance_book, '') in (%(finance_book)s, '') "
elif filters.get("finance_book"):
conditions += " and ifnull(ds.finance_book, '') = %(finance_book)s"
return conditions
def get_columns(): def get_columns():
return [ return [

View File

@ -4,8 +4,16 @@
frappe.query_reports["Budget Variance Report"] = { frappe.query_reports["Budget Variance Report"] = {
"filters": [ "filters": [
{ {
fieldname: "fiscal_year", fieldname: "from_fiscal_year",
label: __("Fiscal Year"), label: __("From Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year,
reqd: 1
},
{
fieldname: "to_fiscal_year",
label: __("To Fiscal Year"),
fieldtype: "Link", fieldtype: "Link",
options: "Fiscal Year", options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year, default: frappe.sys_defaults.fiscal_year,
@ -21,7 +29,7 @@ frappe.query_reports["Budget Variance Report"] = {
{ "value": "Half-Yearly", "label": __("Half-Yearly") }, { "value": "Half-Yearly", "label": __("Half-Yearly") },
{ "value": "Yearly", "label": __("Yearly") } { "value": "Yearly", "label": __("Yearly") }
], ],
default: "Monthly", default: "Yearly",
reqd: 1 reqd: 1
}, },
{ {
@ -46,5 +54,11 @@ frappe.query_reports["Budget Variance Report"] = {
fieldtype: "Link", fieldtype: "Link",
options: "Cost Center" options: "Cost Center"
}, },
{
fieldname:"show_cumulative",
label: __("Show Cumulative Amount"),
fieldtype: "Check",
default: 0,
},
] ]
} }

View File

@ -9,7 +9,7 @@ from frappe.utils import formatdate
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
from six import iteritems from six import iteritems
from pprint import pprint
def execute(filters=None): def execute(filters=None):
if not filters: filters = {} if not filters: filters = {}
validate_filters(filters) validate_filters(filters)
@ -19,7 +19,7 @@ def execute(filters=None):
else: else:
cost_centers = get_cost_centers(filters) cost_centers = get_cost_centers(filters)
period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"]) period_month_ranges = get_period_month_ranges(filters["period"], filters["from_fiscal_year"])
cam_map = get_cost_center_account_month_map(filters) cam_map = get_cost_center_account_month_map(filters)
data = [] data = []
@ -29,18 +29,28 @@ def execute(filters=None):
for account, monthwise_data in iteritems(cost_center_items): for account, monthwise_data in iteritems(cost_center_items):
row = [cost_center, account] row = [cost_center, account]
totals = [0, 0, 0] totals = [0, 0, 0]
for relevant_months in period_month_ranges: for year in get_fiscal_years(filters):
period_data = [0, 0, 0] last_total = 0
for month in relevant_months: for relevant_months in period_month_ranges:
month_data = monthwise_data.get(month, {}) period_data = [0, 0, 0]
for i, fieldname in enumerate(["target", "actual", "variance"]): for month in relevant_months:
value = flt(month_data.get(fieldname)) if monthwise_data.get(year[0]):
period_data[i] += value month_data = monthwise_data.get(year[0]).get(month, {})
totals[i] += value for i, fieldname in enumerate(["target", "actual", "variance"]):
period_data[2] = period_data[0] - period_data[1] value = flt(month_data.get(fieldname))
row += period_data period_data[i] += value
totals[i] += value
period_data[0] += last_total
if(filters.get("show_cumulative")):
last_total = period_data[0] - period_data[1]
period_data[2] = period_data[0] - period_data[1]
row += period_data
totals[2] = totals[0] - totals[1] totals[2] = totals[0] - totals[1]
row += totals if filters["period"] != "Yearly" :
row += totals
data.append(row) data.append(row)
return columns, data return columns, data
@ -50,21 +60,32 @@ def validate_filters(filters):
frappe.throw(_("Filter based on Cost Center is only applicable if Budget Against is selected as Cost Center")) frappe.throw(_("Filter based on Cost Center is only applicable if Budget Against is selected as Cost Center"))
def get_columns(filters): def get_columns(filters):
columns = [_(filters.get("budget_against")) + ":Link/%s:120"%(filters.get("budget_against")), _("Account") + ":Link/Account:120"] columns = [_(filters.get("budget_against")) + ":Link/%s:80"%(filters.get("budget_against")), _("Account") + ":Link/Account:80"]
group_months = False if filters["period"] == "Monthly" else True group_months = False if filters["period"] == "Monthly" else True
for from_date, to_date in get_period_date_ranges(filters["period"], filters["fiscal_year"]): fiscal_year = get_fiscal_years(filters)
for label in [_("Target") + " (%s)", _("Actual") + " (%s)", _("Variance") + " (%s)"]:
if group_months: for year in fiscal_year:
label = label % (formatdate(from_date, format_string="MMM") + " - " + formatdate(to_date, format_string="MMM")) for from_date, to_date in get_period_date_ranges(filters["period"], year[0]):
if filters["period"] == "Yearly":
labels = [_("Budget") + " " + str(year[0]), _("Actual ") + " " + str(year[0]), _("Varaiance ") + " " + str(year[0])]
for label in labels:
columns.append(label+":Float:80")
else: else:
label = label % formatdate(from_date, format_string="MMM") for label in [_("Budget") + " (%s)" + " " + str(year[0]), _("Actual") + " (%s)" + " " + str(year[0]), _("Variance") + " (%s)" + " " + str(year[0])]:
if group_months:
label = label % (formatdate(from_date, format_string="MMM") + "-" + formatdate(to_date, format_string="MMM"))
else:
label = label % formatdate(from_date, format_string="MMM")
columns.append(label+":Float:120") columns.append(label+":Float:80")
return columns + [_("Total Target") + ":Float:120", _("Total Actual") + ":Float:120", if filters["period"] != "Yearly" :
_("Total Variance") + ":Float:120"] return columns + [_("Total Budget") + ":Float:80", _("Total Actual") + ":Float:80",
_("Total Variance") + ":Float:80"]
else:
return columns
def get_cost_centers(filters): def get_cost_centers(filters):
cond = "and 1=1" cond = "and 1=1"
@ -81,19 +102,21 @@ def get_cost_center_target_details(filters):
cond += " and b.cost_center='%s'" % frappe.db.escape(filters.get("cost_center")) cond += " and b.cost_center='%s'" % frappe.db.escape(filters.get("cost_center"))
return frappe.db.sql(""" return frappe.db.sql("""
select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount,b.fiscal_year
from `tabBudget` b, `tabBudget Account` ba from `tabBudget` b, `tabBudget Account` ba
where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year=%s where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year between %s and %s
and b.budget_against = %s and b.company=%s {cond} and b.budget_against = %s and b.company=%s {cond} order by b.fiscal_year
""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond), """.format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond),
(filters.fiscal_year, filters.budget_against, filters.company), as_dict=True) (filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company), as_dict=True)
#Get target distribution details of accounts of cost center #Get target distribution details of accounts of cost center
def get_target_distribution_details(filters): def get_target_distribution_details(filters):
target_details = {} target_details = {}
for d in frappe.db.sql("""select md.name, mdp.month, mdp.percentage_allocation for d in frappe.db.sql("""select md.name, mdp.month, mdp.percentage_allocation
from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
where mdp.parent=md.name and md.fiscal_year=%s""", (filters["fiscal_year"]), as_dict=1): where mdp.parent=md.name and md.fiscal_year between %s and %s order by md.fiscal_year""",(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1):
target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation)) target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation))
return target_details return target_details
@ -107,7 +130,7 @@ def get_actual_details(name, filters):
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"])
cond = "lft>='{lft}' and rgt<='{rgt}'".format(lft = cc_lft, rgt=cc_rgt) cond = "lft>='{lft}' and rgt<='{rgt}'".format(lft = cc_lft, rgt=cc_rgt)
ac_details = frappe.db.sql("""select gl.account, gl.debit, gl.credit, ac_details = frappe.db.sql("""select gl.account, gl.debit, gl.credit,gl.fiscal_year,
MONTHNAME(gl.posting_date) as month_name, b.{budget_against} as budget_against MONTHNAME(gl.posting_date) as month_name, b.{budget_against} as budget_against
from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b
where where
@ -115,11 +138,11 @@ def get_actual_details(name, filters):
and b.docstatus = 1 and b.docstatus = 1
and ba.account=gl.account and ba.account=gl.account
and b.{budget_against} = gl.{budget_against} and b.{budget_against} = gl.{budget_against}
and gl.fiscal_year=%s and gl.fiscal_year between %s and %s
and b.{budget_against}=%s and b.{budget_against}=%s
and exists(select name from `tab{tab}` where name=gl.{budget_against} and {cond}) group by gl.name and exists(select name from `tab{tab}` where name=gl.{budget_against} and {cond}) group by gl.name order by gl.fiscal_year
""".format(tab = filters.budget_against, budget_against = budget_against, cond = cond), """.format(tab = filters.budget_against, budget_against = budget_against, cond = cond,from_year=filters.from_fiscal_year,to_year=filters.to_fiscal_year),
(filters.fiscal_year, name), as_dict=1) (filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1)
cc_actual_details = {} cc_actual_details = {}
for d in ac_details: for d in ac_details:
@ -139,13 +162,12 @@ def get_cost_center_account_month_map(filters):
for month_id in range(1, 13): for month_id in range(1, 13):
month = datetime.date(2013, month_id, 1).strftime('%B') month = datetime.date(2013, month_id, 1).strftime('%B')
cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {}).setdefault(ccd.fiscal_year,{})\
cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {})\
.setdefault(month, frappe._dict({ .setdefault(month, frappe._dict({
"target": 0.0, "actual": 0.0 "target": 0.0, "actual": 0.0
})) }))
tav_dict = cam_map[ccd.budget_against][ccd.account][month] tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month]
month_percentage = tdd.get(ccd.monthly_distribution, {}).get(month, 0) \ month_percentage = tdd.get(ccd.monthly_distribution, {}).get(month, 0) \
if ccd.monthly_distribution else 100.0/12 if ccd.monthly_distribution else 100.0/12
@ -156,3 +178,11 @@ def get_cost_center_account_month_map(filters):
tav_dict.actual += flt(ad.debit) - flt(ad.credit) tav_dict.actual += flt(ad.debit) - flt(ad.credit)
return cam_map return cam_map
def get_fiscal_years(filters):
fiscal_year = frappe.db.sql("""select name from `tabFiscal Year` where
name between %(from_fiscal_year)s and %(to_fiscal_year)s""",
{'from_fiscal_year': filters["from_fiscal_year"], 'to_fiscal_year': filters["to_fiscal_year"]})
return fiscal_year

View File

@ -10,7 +10,7 @@ from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement
def get_mapper_for(mappers, position): def get_mapper_for(mappers, position):
mapper_list = filter(lambda x: x['position'] == position, mappers) mapper_list = list(filter(lambda x: x['position'] == position, mappers))
return mapper_list[0] if mapper_list else [] return mapper_list[0] if mapper_list else []
@ -345,13 +345,13 @@ def execute(filters=None):
# compute net profit / loss # compute net profit / loss
income = get_data( income = get_data(
filters.company, "Income", "Credit", period_list, filters.company, "Income", "Credit", period_list, filters=filters,
accumulated_values=filters.accumulated_values, ignore_closing_entries=True, accumulated_values=filters.accumulated_values, ignore_closing_entries=True,
ignore_accumulated_values_for_fy=True ignore_accumulated_values_for_fy=True
) )
expense = get_data( expense = get_data(
filters.company, "Expense", "Debit", period_list, filters.company, "Expense", "Debit", period_list, filters=filters,
accumulated_values=filters.accumulated_values, ignore_closing_entries=True, accumulated_values=filters.accumulated_values, ignore_closing_entries=True,
ignore_accumulated_values_for_fy=True ignore_accumulated_values_for_fy=True
) )

View File

@ -238,6 +238,9 @@ def accumulate_values_into_parents(accounts, accounts_by_name, companies):
for d in reversed(accounts): for d in reversed(accounts):
if d.parent_account: if d.parent_account:
account = d.parent_account.split('-')[0].strip() account = d.parent_account.split('-')[0].strip()
if not accounts_by_name.get(account):
continue
for company in companies: for company in companies:
accounts_by_name[account][company] = \ accounts_by_name[account][company] = \
accounts_by_name[account].get(company, 0.0) + d.get(company, 0.0) accounts_by_name[account].get(company, 0.0) + d.get(company, 0.0)
@ -268,8 +271,7 @@ def get_companies(filters):
return all_companies, companies return all_companies, companies
def get_subsidiary_companies(company): def get_subsidiary_companies(company):
lft, rgt = frappe.get_cached_value('Company', lft, rgt = frappe.db.get_value('Company', company, ["lft", "rgt"])
company, ["lft", "rgt"])
return frappe.db.sql_list("""select name from `tabCompany` return frappe.db.sql_list("""select name from `tabCompany`
where lft >= {0} and rgt <= {1} order by lft, rgt""".format(lft, rgt)) where lft >= {0} and rgt <= {1} order by lft, rgt""".format(lft, rgt))

View File

@ -28,6 +28,24 @@ frappe.query_reports["Item-wise Sales Register"] = {
"label": __("Mode of Payment"), "label": __("Mode of Payment"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Mode of Payment" "options": "Mode of Payment"
},
{
"fieldname":"warehouse",
"label": __("Warehouse"),
"fieldtype": "Link",
"options": "Warehouse"
},
{
"fieldname":"brand",
"label": __("Brand"),
"fieldtype": "Link",
"options": "Brand"
},
{
"fieldname":"item_group",
"label": __("Item Group"),
"fieldtype": "Link",
"options": "Item Group"
} }
] ]
} }

View File

@ -14,10 +14,10 @@ def execute(filters=None):
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
if not filters: filters = {} if not filters: filters = {}
filters.update({"from_date": filters.get("date_range")[0], "to_date": filters.get("date_range")[1]}) filters.update({"from_date": filters.get("date_range") and filters.get("date_range")[0], "to_date": filters.get("date_range") and filters.get("date_range")[1]})
columns = get_columns(additional_table_columns) columns = get_columns(additional_table_columns)
company_currency = erpnext.get_company_currency(filters.company) company_currency = erpnext.get_company_currency(filters.get('company'))
item_list = get_items(filters, additional_query_columns) item_list = get_items(filters, additional_query_columns)
if item_list: if item_list:
@ -109,6 +109,23 @@ def get_conditions(filters):
where parent=`tabSales Invoice`.name where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)""" and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)"""
if filters.get("warehouse"):
conditions += """ and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s)"""
if filters.get("brand"):
conditions += """ and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s)"""
if filters.get("item_group"):
conditions += """ and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)"""
return conditions return conditions
def get_items(filters, additional_query_columns): def get_items(filters, additional_query_columns):

View File

@ -52,6 +52,18 @@ frappe.query_reports["Sales Register"] = {
"label": __("Warehouse"), "label": __("Warehouse"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Warehouse" "options": "Warehouse"
},
{
"fieldname":"brand",
"label": __("Brand"),
"fieldtype": "Link",
"options": "Brand"
},
{
"fieldname":"item_group",
"label": __("Item Group"),
"fieldtype": "Link",
"options": "Item Group"
} }
] ]
} }

View File

@ -153,6 +153,16 @@ def get_conditions(filters):
where parent=`tabSales Invoice`.name where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s)""" and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s)"""
if filters.get("brand"):
conditions += """ and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s)"""
if filters.get("item_group"):
conditions += """ and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)"""
return conditions return conditions
def get_invoices(filters, additional_query_columns): def get_invoices(filters, additional_query_columns):

View File

@ -94,6 +94,12 @@ frappe.ui.form.on('Asset', {
}, __("Make")); }, __("Make"));
} }
if (!frm.doc.calculate_depreciation) {
frm.add_custom_button(__("Depreciation Entry"), function() {
frm.trigger("make_journal_entry");
}, __("Make"));
}
frm.page.set_inner_btn_group_as_primary(__("Make")); frm.page.set_inner_btn_group_as_primary(__("Make"));
frm.trigger("setup_chart"); frm.trigger("setup_chart");
} }
@ -103,6 +109,21 @@ frappe.ui.form.on('Asset', {
} }
}, },
make_journal_entry: function(frm) {
frappe.call({
method: "erpnext.assets.doctype.asset.asset.make_journal_entry",
args: {
asset_name: frm.doc.name
},
callback: function(r) {
if (r.message) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
}
})
},
setup_chart: function(frm) { setup_chart: function(frm) {
var x_intervals = [frm.doc.purchase_date]; var x_intervals = [frm.doc.purchase_date];
var asset_values = [frm.doc.gross_purchase_amount]; var asset_values = [frm.doc.gross_purchase_amount];

View File

@ -511,3 +511,34 @@ def get_asset_account(account_name, asset=None, asset_category=None, company=Non
.format(account_name.replace('_', ' ').title(), asset_category, company)) .format(account_name.replace('_', ' ').title(), asset_category, company))
return account return account
@frappe.whitelist()
def make_journal_entry(asset_name):
asset = frappe.get_doc("Asset", asset_name)
fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \
get_depreciation_accounts(asset)
depreciation_cost_center, depreciation_series = frappe.db.get_value("Company", asset.company,
["depreciation_cost_center", "series_for_depreciation_entry"])
depreciation_cost_center = asset.cost_center or depreciation_cost_center
je = frappe.new_doc("Journal Entry")
je.voucher_type = "Depreciation Entry"
je.naming_series = depreciation_series
je.company = asset.company
je.remark = "Depreciation Entry against asset {0}".format(asset_name)
je.append("accounts", {
"account": depreciation_expense_account,
"reference_type": "Asset",
"reference_name": asset.name,
"cost_center": depreciation_cost_center
})
je.append("accounts", {
"account": accumulated_depreciation_account,
"reference_type": "Asset",
"reference_name": asset.name
})
return je

View File

@ -9,8 +9,9 @@ from frappe.utils import flt, today, getdate, cint
def post_depreciation_entries(date=None): def post_depreciation_entries(date=None):
# Return if automatic booking of asset depreciation is disabled # Return if automatic booking of asset depreciation is disabled
if not frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically"): if not cint(frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically")):
return return
if not date: if not date:
date = today() date = today()
for asset in get_depreciable_assets(date): for asset in get_depreciable_assets(date):

View File

@ -49,6 +49,10 @@ frappe.ui.form.on("Supplier", {
erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name); erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name);
}, __("Make")); }, __("Make"));
frm.add_custom_button(__('Pricing Rule'), function () {
erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name);
}, __("Make"));
// indicators // indicators
erpnext.utils.set_party_dashboard_indicators(frm); erpnext.utils.set_party_dashboard_indicators(frm);
} }

View File

@ -1,5 +1,6 @@
{ {
"allow_copy": 0, "allow_copy": 0,
"allow_events_in_timeline": 1,
"allow_guest_to_view": 0, "allow_guest_to_view": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
@ -1430,7 +1431,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-09-07 08:48:57.719713", "modified": "2018-09-24 15:36:46.141734",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Supplier", "name": "Supplier",

View File

@ -13,6 +13,10 @@ def get_data():
{ {
'label': _('Orders'), 'label': _('Orders'),
'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'] 'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
},
{
'label': _('Pricing'),
'items': ['Pricing Rule']
} }
] ]
} }

View File

@ -235,6 +235,16 @@ def get_data():
"type": "doctype", "type": "doctype",
"name": "GST HSN Code", "name": "GST HSN Code",
}, },
{
"type": "report",
"name": "GSTR-1",
"is_query_report": True
},
{
"type": "report",
"name": "GSTR-2",
"is_query_report": True
},
{ {
"type": "report", "type": "report",
"name": "GST Sales Register", "name": "GST Sales Register",

View File

@ -661,7 +661,7 @@ class BuyingController(StockController):
if self.doctype == 'Purchase Invoice' and not self.get('update_stock'): if self.doctype == 'Purchase Invoice' and not self.get('update_stock'):
return return
frappe.db.sql("delete from `tabAsset Movement` where reference_name=%s and docstatus = 0", self.name) frappe.db.sql("delete from `tabAsset Movement` where reference_name=%s", self.name)
frappe.db.sql("delete from `tabSerial No` where purchase_document_no=%s", self.name) frappe.db.sql("delete from `tabSerial No` where purchase_document_no=%s", self.name)
def validate_schedule_date(self): def validate_schedule_date(self):

View File

@ -137,7 +137,7 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
.format(args.item_code), StockOverReturnError) .format(args.item_code), StockOverReturnError)
elif abs(current_stock_qty) > max_returnable_qty: elif abs(current_stock_qty) > max_returnable_qty:
frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}") frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}")
.format(args.idx, reference_qty, args.item_code), StockOverReturnError) .format(args.idx, max_returnable_qty, args.item_code), StockOverReturnError)
def get_ref_item_dict(valid_items, ref_item_row): def get_ref_item_dict(valid_items, ref_item_row):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos

View File

@ -1,5 +1,6 @@
{ {
"allow_copy": 0, "allow_copy": 0,
"allow_events_in_timeline": 1,
"allow_guest_to_view": 0, "allow_guest_to_view": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 0, "allow_rename": 0,
@ -650,6 +651,70 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"columns": 0,
"fieldname": "notes_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Notes",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "notes",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Notes",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -1088,7 +1153,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fieldname": "market_segment", "fieldname": "market_segment",
"fieldtype": "Select", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
@ -1101,7 +1166,7 @@
"no_copy": 0, "no_copy": 0,
"oldfieldname": "market_segment", "oldfieldname": "market_segment",
"oldfieldtype": "Select", "oldfieldtype": "Select",
"options": "\nLower Income\nMiddle Income\nUpper Income", "options": "Market Segment",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
@ -1323,7 +1388,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-08-29 06:27:00.582023", "modified": "2018-10-02 09:12:23.415379",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Lead", "name": "Lead",

View File

@ -0,0 +1,8 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Market Segment', {
refresh: function(frm) {
}
});

View File

@ -0,0 +1,96 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:market_segment",
"beta": 0,
"creation": "2018-10-01 09:59:14.479509",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "market_segment",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Market Segment",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-01 09:59:14.479509",
"modified_by": "Administrator",
"module": "CRM",
"name": "Market Segment",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class MarketSegment(Document):
pass

View File

@ -0,0 +1,23 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Market Segment", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Market Segment
() => frappe.tests.make('Market Segment', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
class TestMarketSegment(unittest.TestCase):
pass

View File

@ -107,6 +107,8 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
if(!this.frm.doc.company && frappe.defaults.get_user_default("Company")) if(!this.frm.doc.company && frappe.defaults.get_user_default("Company"))
set_multiple(this.frm.doc.doctype, this.frm.doc.name, set_multiple(this.frm.doc.doctype, this.frm.doc.name,
{ company:frappe.defaults.get_user_default("Company") }); { company:frappe.defaults.get_user_default("Company") });
if(!this.frm.doc.currency)
set_multiple(this.frm.doc.doctype, this.frm.doc.name, { currency:frappe.defaults.get_user_default("Currency") });
this.setup_queries(); this.setup_queries();
}, },

View File

@ -1,5 +1,6 @@
{ {
"allow_copy": 0, "allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0, "allow_guest_to_view": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 0, "allow_rename": 0,
@ -414,38 +415,6 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "with_items",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "With Items",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -614,6 +583,233 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_14",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sales",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "currency",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Currency",
"length": 0,
"no_copy": 0,
"options": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "opportunity_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Opportunity Amount",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "with_items",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "With Items",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_17",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Prospecting",
"fieldname": "sales_stage",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sales Stage",
"length": 0,
"no_copy": 0,
"options": "Sales Stage",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "100",
"fieldname": "probability",
"fieldtype": "Percent",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Probability (%)",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -1264,7 +1460,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-08-29 06:31:05.638516", "modified": "2018-10-01 09:28:43.990999",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Opportunity", "name": "Opportunity",

View File

@ -0,0 +1,8 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Sales Stage', {
refresh: function(frm) {
}
});

View File

@ -0,0 +1,96 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:stage_name",
"beta": 0,
"creation": "2018-10-01 09:28:16.399518",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "stage_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Stage Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-01 09:29:43.230378",
"modified_by": "Administrator",
"module": "CRM",
"name": "Sales Stage",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class SalesStage(Document):
pass

View File

@ -0,0 +1,23 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Sales Stage", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Sales Stage
() => frappe.tests.make('Sales Stage', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
class TestSalesStage(unittest.TestCase):
pass

View File

@ -1,18 +1,18 @@
{ {
"add_total_row": 0, "add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2013-10-22 11:58:16", "creation": "2013-10-22 11:58:16",
"disabled": 0, "disabled": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Report", "doctype": "Report",
"idx": 3, "idx": 3,
"is_standard": "Yes", "is_standard": "Yes",
"modified": "2017-02-24 20:20:28.725080", "modified": "2018-09-26 18:59:46.520731",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "CRM",
"name": "Lead Details", "name": "Lead Details",
"owner": "Administrator", "owner": "Administrator",
"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 `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)\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", "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)\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": "Query Report",

View File

@ -12,7 +12,7 @@ app_license = "GNU General Public License (v3)"
source_link = "https://github.com/frappe/erpnext" source_link = "https://github.com/frappe/erpnext"
develop_version = '11.x.x-develop' develop_version = '11.x.x-develop'
staging_version = '11.0.3-beta.2' staging_version = '11.0.3-beta.3'
error_report_email = "support@erpnext.com" error_report_email = "support@erpnext.com"
@ -25,6 +25,7 @@ web_include_css = "assets/css/erpnext-web.css"
doctype_js = { doctype_js = {
"Communication": "public/js/communication.js", "Communication": "public/js/communication.js",
"Event": "public/js/event.js"
} }
welcome_email = "erpnext.setup.utils.welcome_email" welcome_email = "erpnext.setup.utils.welcome_email"

View File

@ -1,5 +1,6 @@
{ {
"allow_copy": 0, "allow_copy": 0,
"allow_events_in_timeline": 1,
"allow_guest_to_view": 0, "allow_guest_to_view": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
@ -3077,7 +3078,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-08-29 08:35:01.321251", "modified": "2018-09-24 15:36:58.234710",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Employee", "name": "Employee",

View File

@ -651,6 +651,39 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "salary_slip",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Salary Slip",
"length": 0,
"no_copy": 0,
"options": "Salary Slip",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -916,7 +949,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 3, "max_attachments": 3,
"modified": "2018-08-21 14:44:42.766422", "modified": "2018-09-21 15:53:11.935416",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Leave Application", "name": "Leave Application",

View File

@ -770,7 +770,7 @@
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "", "collapsible_depends_on": "",
"columns": 0, "columns": 0,
"depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1 && doc.is_additional_component != 1", "depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1",
"fieldname": "condition_and_formula", "fieldname": "condition_and_formula",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -835,7 +835,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"default": "1", "default": "",
"fieldname": "amount_based_on_formula", "fieldname": "amount_based_on_formula",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -1003,7 +1003,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-07-02 16:55:44.467519", "modified": "2018-09-20 16:44:58.876044",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Salary Component", "name": "Salary Component",
@ -1056,5 +1056,6 @@
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"track_changes": 0, "track_changes": 0,
"track_seen": 0 "track_seen": 0,
"track_views": 0
} }

View File

@ -385,7 +385,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"default": "1", "default": "0",
"depends_on": "eval:doc.parenttype=='Salary Structure'", "depends_on": "eval:doc.parenttype=='Salary Structure'",
"fetch_from": "", "fetch_from": "",
"fieldname": "amount_based_on_formula", "fieldname": "amount_based_on_formula",
@ -692,7 +692,7 @@
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-07-04 16:28:32.314907", "modified": "2018-09-20 16:59:33.622652",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Salary Detail", "name": "Salary Detail",
@ -706,5 +706,6 @@
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"track_changes": 0, "track_changes": 0,
"track_seen": 0 "track_seen": 0,
"track_views": 0
} }

View File

@ -122,16 +122,14 @@ frappe.ui.form.on('Salary Slip Timesheet', {
// Get leave details // Get leave details
//--------------------------------------------------------------------- //---------------------------------------------------------------------
var get_emp_and_leave_details = function(doc, dt, dn) { var get_emp_and_leave_details = function(doc, dt, dn) {
if(!doc.start_date){ return frappe.call({
return frappe.call({ method: 'get_emp_and_leave_details',
method: 'get_emp_and_leave_details', doc: locals[dt][dn],
doc: locals[dt][dn], callback: function(r, rt) {
callback: function(r, rt) { cur_frm.refresh();
cur_frm.refresh(); calculate_all(doc, dt, dn);
calculate_all(doc, dt, dn); }
} });
});
}
} }
cur_frm.cscript.employee = function(doc,dt,dn){ cur_frm.cscript.employee = function(doc,dt,dn){

View File

@ -384,8 +384,8 @@ class SalarySlip(TransactionBase):
and t2.is_lwp = 1 and t2.is_lwp = 1
and t1.docstatus = 1 and t1.docstatus = 1
and t1.employee = %(employee)s and t1.employee = %(employee)s
and CASE WHEN t2.include_holiday != 1 THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date and CASE WHEN t2.include_holiday != 1 THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date and ifnull(t1.salary_slip, '') = ''
WHEN t2.include_holiday THEN %(dt)s between from_date and to_date WHEN t2.include_holiday THEN %(dt)s between from_date and to_date and ifnull(t1.salary_slip, '') = ''
END END
""".format(holidays), {"employee": self.employee, "dt": dt}) """.format(holidays), {"employee": self.employee, "dt": dt})
if leave: if leave:

View File

@ -323,11 +323,12 @@ def make_salary_component(salary_components, test_tax):
def get_salary_component_account(sal_comp): def get_salary_component_account(sal_comp):
company = erpnext.get_default_company() company = erpnext.get_default_company()
sal_comp = frappe.get_doc("Salary Component", sal_comp) sal_comp = frappe.get_doc("Salary Component", sal_comp)
sal_comp.append("accounts", { if not sal_comp.get("accounts"):
"company": company, sal_comp.append("accounts", {
"default_account": create_account(company) "company": company,
}) "default_account": create_account(company)
sal_comp.save() })
sal_comp.save()
def create_account(company): def create_account(company):
salary_account = frappe.db.get_value("Account", "Salary - " + frappe.get_cached_value('Company', company, 'abbr')) salary_account = frappe.db.get_value("Account", "Salary - " + frappe.get_cached_value('Company', company, 'abbr'))
@ -347,7 +348,8 @@ def make_earning_salary_component(setup=False, test_tax=False):
"abbr":'BS', "abbr":'BS',
"condition": 'base > 10000', "condition": 'base > 10000',
"formula": 'base*.5', "formula": 'base*.5',
"type": "Earning" "type": "Earning",
"amount_based_on_formula": 1
}, },
{ {
"salary_component": 'HRA', "salary_component": 'HRA',
@ -360,7 +362,8 @@ def make_earning_salary_component(setup=False, test_tax=False):
"abbr":'SA', "abbr":'SA',
"condition": 'H < 10000', "condition": 'H < 10000',
"formula": 'BS*.5', "formula": 'BS*.5',
"type": "Earning" "type": "Earning",
"amount_based_on_formula": 1
}, },
{ {
"salary_component": "Leave Encashment", "salary_component": "Leave Encashment",
@ -401,7 +404,8 @@ def make_earning_salary_component(setup=False, test_tax=False):
"abbr":'BS', "abbr":'BS',
"condition": 'base < 10000', "condition": 'base < 10000',
"formula": 'base*.2', "formula": 'base*.2',
"type": "Earning" "type": "Earning",
"amount_based_on_formula": 1
}) })
return data return data
@ -412,13 +416,15 @@ def make_deduction_salary_component(setup=False, test_tax=False):
"abbr":'PT', "abbr":'PT',
"condition": 'base > 10000', "condition": 'base > 10000',
"formula": 'base*.1', "formula": 'base*.1',
"type": "Deduction" "type": "Deduction",
"amount_based_on_formula": 1
}, },
{ {
"salary_component": 'TDS', "salary_component": 'TDS',
"abbr":'T', "abbr":'T',
"formula": 'base*.1', "formula": 'base*.1',
"type": "Deduction" "type": "Deduction",
"amount_based_on_formula": 1
} }
] ]
if not test_tax: if not test_tax:
@ -427,7 +433,8 @@ def make_deduction_salary_component(setup=False, test_tax=False):
"abbr":'T', "abbr":'T',
"condition": 'employment_type=="Intern"', "condition": 'employment_type=="Intern"',
"formula": 'base*.1', "formula": 'base*.1',
"type": "Deduction" "type": "Deduction",
"amount_based_on_formula": 1
}) })
if setup or test_tax: if setup or test_tax:
make_salary_component(data, test_tax) make_salary_component(data, test_tax)

View File

@ -18,14 +18,21 @@ class SalaryStructure(Document):
self.validate_max_benefits_with_flexi() self.validate_max_benefits_with_flexi()
def set_missing_values(self): def set_missing_values(self):
fields = ["depends_on_lwp", "variable_based_on_taxable_salary", "is_tax_applicable", "is_flexible_benefit"] overwritten_fields = ["depends_on_lwp", "variable_based_on_taxable_salary", "is_tax_applicable", "is_flexible_benefit"]
overwritten_fields_if_missing = ["amount_based_on_formula", "formula", "amount"]
for table in ["earnings", "deductions"]: for table in ["earnings", "deductions"]:
for d in self.get(table): for d in self.get(table):
component_default_value = frappe.db.get_value("Salary Component", str(d.salary_component), fields, as_dict=1) component_default_value = frappe.db.get_value("Salary Component", str(d.salary_component),
overwritten_fields + overwritten_fields_if_missing, as_dict=1)
if component_default_value: if component_default_value:
for fieldname, value in iteritems(component_default_value): for fieldname in overwritten_fields:
value = component_default_value.get(fieldname)
if d.get(fieldname) != value: if d.get(fieldname) != value:
d[fieldname] = value d.set(fieldname, value)
if not (d.get("amount") or d.get("formula")):
for fieldname in overwritten_fields_if_missing:
d.set(fieldname, component_default_value.get(fieldname))
def validate_amount(self): def validate_amount(self):
if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet: if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet:

View File

@ -5,7 +5,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cstr, add_days, date_diff from frappe.utils import cstr, add_days, date_diff, getdate
from frappe import _ from frappe import _
from frappe.utils.csvutils import UnicodeWriter from frappe.utils.csvutils import UnicodeWriter
from frappe.model.document import Document from frappe.model.document import Document
@ -48,8 +48,9 @@ def add_data(w, args):
for employee in employees: for employee in employees:
existing_attendance = {} existing_attendance = {}
if existing_attendance_records \ if existing_attendance_records \
and tuple([date, employee.name]) in existing_attendance_records: and tuple([getdate(date), employee.name]) in existing_attendance_records:
existing_attendance = existing_attendance_records[tuple([date, employee.name])] existing_attendance = existing_attendance_records[tuple([getdate(date), employee.name])]
row = [ row = [
existing_attendance and existing_attendance.name or "", existing_attendance and existing_attendance.name or "",
employee.name, employee.employee_name, date, employee.name, employee.employee_name, date,
@ -114,6 +115,7 @@ def upload():
if not row: continue if not row: continue
row_idx = i + 5 row_idx = i + 5
d = frappe._dict(zip(columns, row)) d = frappe._dict(zip(columns, row))
d["doctype"] = "Attendance" d["doctype"] = "Attendance"
if d.name: if d.name:
d["docstatus"] = frappe.db.get_value("Attendance", d.name, "docstatus") d["docstatus"] = frappe.db.get_value("Attendance", d.name, "docstatus")
@ -121,6 +123,8 @@ def upload():
try: try:
check_record(d) check_record(d)
ret.append(import_doc(d, "Attendance", 1, row_idx, submit=True)) ret.append(import_doc(d, "Attendance", 1, row_idx, submit=True))
except AttributeError:
pass
except Exception as e: except Exception as e:
error = True error = True
ret.append('Error for row (#%d) %s : %s' % (row_idx, ret.append('Error for row (#%d) %s : %s' % (row_idx,

View File

@ -506,7 +506,7 @@
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-07-12 16:16:54.237829", "modified": "2018-10-04 16:16:54.237829",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Work Order Item", "name": "Work Order Item",

View File

@ -566,3 +566,6 @@ erpnext.patches.v11_0.redesign_healthcare_billing_work_flow
erpnext.patches.v10_0.delete_hub_documents # 12-08-2018 erpnext.patches.v10_0.delete_hub_documents # 12-08-2018
erpnext.patches.v11_0.rename_healthcare_fields erpnext.patches.v11_0.rename_healthcare_fields
erpnext.patches.v11_0.remove_land_unit_icon erpnext.patches.v11_0.remove_land_unit_icon
erpnext.patches.v11_0.add_default_dispatch_notification_template
erpnext.patches.v11_0.add_market_segments
erpnext.patches.v11_0.add_sales_stages

View File

@ -0,0 +1,25 @@
import os
import frappe
from frappe import _
def execute():
frappe.reload_doc("email", "doctype", "email_template")
frappe.reload_doc("stock", "doctype", "delivery_settings")
if not frappe.db.exists("Email Template", _("Dispatch Notification")):
base_path = frappe.get_app_path("erpnext", "stock", "doctype")
response = frappe.read_file(os.path.join(base_path, "delivery_trip/dispatch_notification_template.html"))
frappe.get_doc({
"doctype": "Email Template",
"name": _("Dispatch Notification"),
"response": response,
"subject": _("Your order is out for delivery!"),
"owner": frappe.session.user,
}).insert(ignore_permissions=True)
delivery_settings = frappe.get_doc("Delivery Settings")
delivery_settings.dispatch_template = _("Dispatch Notification")
delivery_settings.save()

View File

@ -0,0 +1,11 @@
import frappe
from frappe import _
from erpnext.setup.setup_wizard.operations.install_fixtures import add_market_segments
def execute():
frappe.reload_doc('crm', 'doctype', 'market_segment')
frappe.local.lang = frappe.db.get_default("lang") or 'en'
add_market_segments()

View File

@ -0,0 +1,10 @@
import frappe
from frappe import _
from erpnext.setup.setup_wizard.operations.install_fixtures import add_sale_stages
def execute():
frappe.reload_doc('crm', 'doctype', 'sales_stage')
frappe.local.lang = frappe.db.get_default("lang") or 'en'
add_sale_stages()

View File

@ -1,80 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8"?>
<svg <svg width="88px" height="88px" viewBox="0 0 88 88" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
xmlns:dc="http://purl.org/dc/elements/1.1/" <!-- Generator: Sketch 44.1 (41455) - http://www.bohemiancoding.com/sketch -->
xmlns:cc="http://creativecommons.org/ns#" <title>erpnext-logo</title>
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" <desc>Created with Sketch.</desc>
xmlns:svg="http://www.w3.org/2000/svg" <defs></defs>
xmlns="http://www.w3.org/2000/svg" <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
xml:space="preserve" <g id="erpnext-logo" transform="translate(-2.000000, -2.000000)" fill-rule="nonzero">
enable-background="new 0 0 512 512" <g id="g1422-7-2" transform="translate(0.025630, 0.428785)" fill="#5E64FF">
viewBox="0 0 512 512" <g id="g1418-4-6" transform="translate(0.268998, 0.867736)">
height="512px" <g id="g1416-4-9" transform="translate(0.749997, 0.000000)">
width="512px" <path d="M14.1845844,0.703479866 L75.0387175,0.703479866 C82.3677094,0.703479866 88.2679029,6.60367875 88.2679029,13.9326374 L88.2679029,74.7868158 C88.2679029,82.1157744 82.3677094,88.0159833 75.0387175,88.0159833 L14.1845844,88.0159833 C6.85569246,88.0159833 0.955398949,82.1157744 0.955398949,74.7868158 L0.955398949,13.9326374 C0.955398949,6.60367875 6.85569246,0.703479866 14.1845844,0.703479866 L14.1845844,0.703479866 Z" id="path1414-3-4"></path>
y="0px" </g>
x="0px" </g>
id="Layer_1" </g>
version="1.1"><metadata <g id="g1444-6-7" transform="translate(27.708247, 23.320960)" fill="#FFFFFF">
id="metadata45"><rdf:RDF><cc:Work <path d="M4.06942472,0.507006595 C3.79457554,0.507006595 3.52673783,0.534925429 3.26792241,0.587619847 C3.00908052,0.640314265 2.75926093,0.717948309 2.52171801,0.818098395 C2.40292009,0.868173438 2.28745592,0.924056085 2.17495509,0.985013441 C1.94997987,1.10692286 1.73828674,1.24983755 1.54244215,1.41134187 C0.661062132,2.13811791 0.100674618,3.23899362 0.100674618,4.4757567 L0.100674618,4.71760174 L0.100674618,39.9531653 L0.100674618,40.1945182 C0.100674618,42.3932057 1.87073716,44.1632683 4.06942472,44.1632683 L31.8263867,44.1632683 C34.0250742,44.1632683 35.7951368,42.3932057 35.7951368,40.1945182 L35.7951368,39.9531653 C35.7951368,37.7544777 34.0250742,35.9844152 31.8263867,35.9844152 L8.28000399,35.9844152 L8.28000399,26.0992376 L25.7874571,26.0992376 C27.9861447,26.0992376 29.7562072,24.3291751 29.7562072,22.1304875 L29.7562072,21.8891611 C29.7562072,19.6904735 27.9861447,17.920411 25.7874571,17.920411 L8.28000399,17.920411 L8.28000399,8.68635184 L31.8263867,8.68635184 C34.0250742,8.68635184 35.7951368,6.9162893 35.7951368,4.71760174 L35.7951368,4.4757567 C35.7951368,2.27706914 34.0250742,0.507006595 31.8263867,0.507006595 L4.06942472,0.507006595 Z" id="rect1436-8-4"></path>
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type </g>
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title><cc:license </g>
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" /></cc:Work><cc:License </g>
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/"><cc:permits </svg>
rdf:resource="http://creativecommons.org/ns#Reproduction" /><cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" /><cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" /><cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" /><cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /><cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" /></cc:License></rdf:RDF></metadata><defs
id="defs43" /><g
id="g8"><path
id="path6"
d="M150.483,371.684V141.15c0-15.167,9.534-25.133,23.833-25.133h162.5c13.866,0,20.8,6.933,20.8,18.633v2.6 c0,12.133-6.934,18.633-20.8,18.633h-141.7v78.434h109.634c14.3,0,20.8,6.066,20.8,17.767v1.3c0,12.133-6.934,18.633-20.8,18.633 H195.117v84.934h144.3c13.867,0,20.367,6.066,20.367,17.767v2.167c0,12.566-6.5,19.5-20.367,19.5h-165.1 C160.017,396.384,150.483,386.851,150.483,371.684z"
fill="#FFFFFF" /></g><g
id="g10" /><g
id="g12" /><g
id="g14" /><g
id="g16" /><g
id="g18" /><g
id="g20" /><g
id="g22" /><g
id="g24" /><g
id="g26" /><g
id="g28" /><g
id="g30" /><g
id="g32" /><g
id="g34" /><g
id="g36" /><g
id="g38" /><g
style="stroke-width:0.64453173"
transform="matrix(5.8639898,0,0,5.8639898,-9653.0918,-3338.6848)"
id="g1446-4-5"><g
style="stroke-width:0.64453214"
transform="matrix(0.9999989,0,0,0.99999981,279.19167,438.7826)"
id="g1422-7-2"><g
style="stroke-width:0.64453214"
id="g1418-4-6"
transform="translate(-25.731002,-0.1322636)"><g
style="stroke-width:0.64453214"
transform="translate(-95.250002)"
id="g1416-4-9"><path
style="opacity:1;vector-effect:none;fill:#7574ff;fill-opacity:1;stroke:none;stroke-width:2.55798697;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
d="m 1501.1846,130.70348 h 60.8542 c 7.329,0 13.2292,5.9002 13.2292,13.22916 v 60.85419 c 0,7.32896 -5.9002,13.22917 -13.2292,13.22917 h -60.8542 c -7.3289,0 -13.2292,-5.90021 -13.2292,-13.22917 v -60.85419 c 0,-7.32896 5.9003,-13.22916 13.2292,-13.22916 z"
id="path1414-3-4" /></g></g><g
id="g1420-0-4"
transform="matrix(1.0131472,0,0,1.0131472,-144.01349,-2.2905938)"
style="stroke-width:0.63616848" /></g><path
id="circle1424-7-6"
transform="matrix(0.26458333,0,0,0.26458333,1646.1645,569.35379)"
d="M 164.92578,228.00977 A 412.60453,412.60453 0 0 0 0,262.94922 V 280 c 0,27.70001 22.30022,50 50,50 h 230 c 27.69978,0 50,-22.29999 50,-50 V 262.70508 A 412.60453,412.60453 0 0 0 164.92578,228.00977 Z"
clip-path="none"
style="opacity:1;vector-effect:none;fill:#6564f9;fill-opacity:1;stroke:none;stroke-width:9.66797638;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" /><g
style="fill:#4a15c6;fill-opacity:1;stroke-width:0.64453173"
transform="translate(2.8144523,0.84784259)"
id="g1434-8-0" /><g
style="stroke-width:0.64453173"
id="g1444-6-7"
transform="translate(3.8727857,-0.32524952)"><path
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:9.66797638;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
d="m 3738.9473,951.78516 c -1.0388,0 -2.0511,0.10552 -3.0293,0.30468 -0.9783,0.19916 -1.9225,0.49258 -2.8203,0.8711 -0.449,0.18926 -0.8854,0.40047 -1.3106,0.63086 -0.8503,0.46076 -1.6504,1.00091 -2.3906,1.61132 -3.3312,2.74687 -5.4492,6.90766 -5.4492,11.58204 v 0.91406 133.17378 0.9122 c 0,8.31 6.69,15 15,15 h 104.9082 c 8.31,0 15,-6.69 15,-15 v -0.9122 c 0,-8.31 -6.69,-15 -15,-15 h -88.9942 v -37.3613 h 66.1699 c 8.31,0 15,-6.69 15,-15 v -0.9121 c 0,-8.31 -6.69,-15 -15,-15 h -66.1699 v -34.90038 h 88.9942 c 8.31,0 15,-6.69 15,-15 v -0.91406 c 0,-8.31 -6.69,-15 -15,-15 z"
transform="matrix(0.26458334,0,0,0.26458334,682.80626,339.68051)"
id="rect1436-8-4" /></g></g></svg>

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -211,8 +211,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}, },
make_payment_request: function() { make_payment_request: function() {
const me = this; var me = this;
const payment_request_type = (in_list(['Sales Order', 'Sales Invoice'], me.frm.doc.doctype)) const payment_request_type = (in_list(['Sales Order', 'Sales Invoice'], this.frm.doc.doctype))
? "Inward" : "Outward"; ? "Inward" : "Outward";
frappe.call({ frappe.call({

View File

@ -0,0 +1,35 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt
frappe.provide("frappe.desk");
frappe.ui.form.on("Event", {
refresh: function(frm) {
frm.set_query('reference_doctype', "event_participants", function() {
return {
"filters": {
"name": ["in", ["Contact", "Lead", "Customer", "Supplier", "Employee", "Sales Partner"]]
}
};
});
frm.add_custom_button(__('Add Leads'), function() {
new frappe.desk.eventParticipants(frm, "Lead");
}, __("Add Participants"));
frm.add_custom_button(__('Add Customers'), function() {
new frappe.desk.eventParticipants(frm, "Customer");
}, __("Add Participants"));
frm.add_custom_button(__('Add Suppliers'), function() {
new frappe.desk.eventParticipants(frm, "Supplier");
}, __("Add Participants"));
frm.add_custom_button(__('Add Employees'), function() {
new frappe.desk.eventParticipants(frm, "Employee");
}, __("Add Participants"));
frm.add_custom_button(__('Add Sales Partners'), function() {
new frappe.desk.eventParticipants(frm, "Sales Partners");
}, __("Add Participants"));
}
});

View File

@ -18,18 +18,19 @@ export default {
}, },
methods: { methods: {
make_input() { make_input() {
this.message_input = new frappe.ui.CommentArea({ this.message_input = frappe.ui.form.make_control({
parent: this.$refs['comment-input'], parent: this.$refs['comment-input'],
on_submit: (message) => { on_submit: (message) => {
this.message_input.reset(); this.message_input.reset();
this.$emit('change', message); this.$emit('change', message);
}, },
only_input: true,
no_wrapper: true no_wrapper: true
}); });
}, },
submit_input() { submit_input() {
if (!this.message_input) return; if (!this.message_input) return;
const value = this.message_input.val(); const value = this.message_input.get_value();
if (!value) return; if (!value) return;
this.message_input.submit(); this.message_input.submit();
} }

View File

@ -1,6 +1,44 @@
<template> <template>
<div> <div>
<div ref="review-area" class="timeline-head"></div> <div class="timeline-head">
<div class="comment-input-wrapper">
<div class="comment-input-header">
<span class="text-muted">{{ __('Add your review') }}</span>
<div class="btn btn-default btn-xs pull-right"
@click="on_submit_review"
:disabled="!(user_review.rating && user_review.subject)"
>
{{ __('Submit Review') }}
</div>
</div>
<div class="comment-input-container">
<div class="rating-area text-muted">
<span>{{ __('Your rating:') }}</span>
<div
v-for="i in [1, 2, 3, 4, 5]"
:key="i"
:class="['fa fa-fw', user_review.rating < i ? 'fa-star-o' : 'fa-star']"
:data-index="i"
@click="set_rating(i)"
>
</div>
</div>
<div class="comment-input-body margin-top" v-show="user_review.rating">
<input
type="text"
placeholder="Subject"
class="form-control margin-bottom"
style="border-color: #ebeff2"
v-model="user_review.subject"
>
<div ref="review-content"></div>
<div>
<span class="text-muted text-small">{{ __('Ctrl+Enter to submit') }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="timeline-items"> <div class="timeline-items">
<review-timeline-item v-for="review in reviews" <review-timeline-item v-for="review in reviews"
:key="review.user" :key="review.user"
@ -22,6 +60,11 @@ export default {
props: ['hub_item_name'], props: ['hub_item_name'],
data() { data() {
return { return {
user_review: {
rating: 0,
subject: '',
content: ''
},
reviews: [] reviews: []
} }
}, },
@ -35,6 +78,10 @@ export default {
this.make_input(); this.make_input();
}, },
methods: { methods: {
set_rating(i) {
this.user_review.rating = i;
},
when(datetime) { when(datetime) {
return comment_when(datetime); return comment_when(datetime);
}, },
@ -48,21 +95,37 @@ export default {
}, },
make_input() { make_input() {
this.review_area = new frappe.ui.ReviewArea({ this.review_content = frappe.ui.form.make_control({
parent: this.$refs['review-area'], parent: this.$refs['review-content'],
mentions: [], on_submit: this.on_submit_review.bind(this),
on_submit: this.on_submit_review.bind(this) no_wrapper: true,
only_input: true,
render_input: true,
df: {
fieldtype: 'Comment',
fieldname: 'comment'
}
}); });
}, },
on_submit_review(values) { on_submit_review() {
this.review_area.reset(); const review = Object.assign({}, this.user_review, {
content: this.review_content.get_value()
});
hub.call('add_item_review', { hub.call('add_item_review', {
hub_item_name: this.hub_item_name, hub_item_name: this.hub_item_name,
review: JSON.stringify(values) review: JSON.stringify(review)
}) })
.then(this.push_review.bind(this)); .then(this.push_review.bind(this));
this.reset_user_review();
},
reset_user_review() {
this.user_review.rating = 0;
this.user_review.subject = '';
this.review_content.set_value('');
}, },
push_review(review){ push_review(review){

View File

@ -173,6 +173,20 @@ $.extend(erpnext.utils, {
}) })
}, },
make_pricing_rule: function(doctype, docname) {
frappe.call({
method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.make_pricing_rule",
args: {
doctype: doctype,
docname: docname
},
callback: function(r) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
})
},
/** /**
* Checks if the first row of a given child table is empty * Checks if the first row of a given child table is empty
* @param child_table - Child table Doctype * @param child_table - Child table Doctype

View File

@ -106,6 +106,10 @@ frappe.ui.form.on("Customer", {
frappe.set_route('query-report', 'Accounts Receivable', {customer:frm.doc.name}); frappe.set_route('query-report', 'Accounts Receivable', {customer:frm.doc.name});
}); });
frm.add_custom_button(__('Pricing Rule'), function () {
erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name);
}, __("Make"));
// indicator // indicator
erpnext.utils.set_party_dashboard_indicators(frm); erpnext.utils.set_party_dashboard_indicators(frm);

View File

@ -1,5 +1,6 @@
{ {
"allow_copy": 0, "allow_copy": 0,
"allow_events_in_timeline": 1,
"allow_guest_to_view": 0, "allow_guest_to_view": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
@ -1433,6 +1434,103 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_45",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "market_segment",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Market Segment",
"length": 0,
"no_copy": 0,
"options": "Market Segment",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "industry",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Industry",
"length": 0,
"no_copy": 0,
"options": "Industry Type",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -1775,7 +1873,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-08-29 06:26:02.197750", "modified": "2018-10-01 10:07:34.510264",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Customer", "name": "Customer",

View File

@ -21,6 +21,10 @@ def get_data():
{ {
'label': _('Projects'), 'label': _('Projects'),
'items': ['Project'] 'items': ['Project']
},
{
'label': _('Pricing'),
'items': ['Pricing Rule']
} }
] ]
} }

View File

@ -13,23 +13,34 @@ frappe.pages['sales-funnel'].on_page_load = function(wrapper) {
frappe.breadcrumbs.add("Selling"); frappe.breadcrumbs.add("Selling");
} }
erpnext.SalesFunnel = Class.extend({ erpnext.SalesFunnel = class SalesFunnel {
init: function(wrapper) { constructor(wrapper) {
var me = this; var me = this;
// 0 setTimeout hack - this gives time for canvas to get width and height // 0 setTimeout hack - this gives time for canvas to get width and height
setTimeout(function() { setTimeout(function() {
me.setup(wrapper); me.setup(wrapper);
me.get_data(); me.get_data();
}, 0); }, 0);
}, }
setup: function(wrapper) { setup(wrapper) {
var me = this; var me = this;
this.company_field = wrapper.page.add_field({"fieldtype": "Link", "fieldname": "company", "options": "Company",
"label": __("Company"), "reqd": 1, "default": frappe.defaults.get_user_default('company'),
change: function() {
me.company = this.value || frappe.defaults.get_user_default('company');
me.get_data();
}
}),
this.elements = { this.elements = {
layout: $(wrapper).find(".layout-main"), layout: $(wrapper).find(".layout-main"),
from_date: wrapper.page.add_date(__("From Date")), from_date: wrapper.page.add_date(__("From Date")),
to_date: wrapper.page.add_date(__("To Date")), to_date: wrapper.page.add_date(__("To Date")),
chart: wrapper.page.add_select(__("Chart"), [{value: 'sales_funnel', label:__("Sales Funnel")},
{value: 'sales_pipeline', label:__("Sales Pipeline")},
{value: 'opp_by_lead_source', label:__("Opportunities by lead source")}]),
refresh_btn: wrapper.page.set_primary_action(__("Refresh"), refresh_btn: wrapper.page.set_primary_action(__("Refresh"),
function() { me.get_data(); }, "fa fa-refresh"), function() { me.get_data(); }, "fa fa-refresh"),
}; };
@ -41,16 +52,27 @@ erpnext.SalesFunnel = Class.extend({
this.elements.funnel_wrapper = $('<div class="funnel-wrapper text-center"></div>') this.elements.funnel_wrapper = $('<div class="funnel-wrapper text-center"></div>')
.appendTo(this.elements.layout); .appendTo(this.elements.layout);
this.company = frappe.defaults.get_user_default('company');
this.options = { this.options = {
from_date: frappe.datetime.add_months(frappe.datetime.get_today(), -1), from_date: frappe.datetime.add_months(frappe.datetime.get_today(), -1),
to_date: frappe.datetime.get_today() to_date: frappe.datetime.get_today(),
chart: 'sales_funnel'
}; };
// set defaults and bind on change // set defaults and bind on change
$.each(this.options, function(k, v) { $.each(this.options, function(k, v) {
me.elements[k].val(frappe.datetime.str_to_user(v)); if (['from_date', 'to_date'].includes(k)) {
me.elements[k].val(frappe.datetime.str_to_user(v));
} else {
me.elements[k].val(v);
}
me.elements[k].on("change", function() { me.elements[k].on("change", function() {
me.options[k] = frappe.datetime.user_to_str($(this).val()); if (['from_date', 'to_date'].includes(k)) {
me.options[k] = frappe.datetime.user_to_str($(this).val()) != 'Invalid date' ? frappe.datetime.user_to_str($(this).val()) : frappe.datetime.get_today();
} else {
me.options.chart = $(this).val();
}
me.get_data(); me.get_data();
}); });
}); });
@ -64,29 +86,90 @@ erpnext.SalesFunnel = Class.extend({
$(window).resize(function() { $(window).resize(function() {
me.render(); me.render();
}); });
}, }
get_data: function(btn) { get_data(btn) {
var me = this; var me = this;
frappe.call({ if (me.options.chart == 'sales_funnel'){
method: "erpnext.selling.page.sales_funnel.sales_funnel.get_funnel_data", frappe.call({
args: { method: "erpnext.selling.page.sales_funnel.sales_funnel.get_funnel_data",
from_date: this.options.from_date, args: {
to_date: this.options.to_date from_date: this.options.from_date,
}, to_date: this.options.to_date,
btn: btn, company: this.company
callback: function(r) { },
if(!r.exc) { btn: btn,
me.options.data = r.message; callback: function(r) {
me.render(); if(!r.exc) {
me.options.data = r.message;
if (me.options.data=='empty') {
const $parent = me.elements.funnel_wrapper;
$parent.html(__('No data for this period'));
} else {
me.render_funnel();
}
}
} }
} });
}); } else if (me.options.chart == 'opp_by_lead_source'){
}, frappe.call({
method: "erpnext.selling.page.sales_funnel.sales_funnel.get_opp_by_lead_source",
args: {
from_date: this.options.from_date,
to_date: this.options.to_date,
company: this.company
},
btn: btn,
callback: function(r) {
if(!r.exc) {
me.options.data = r.message;
if (me.options.data=='empty') {
const $parent = me.elements.funnel_wrapper;
$parent.html(__('No data for this period'));
} else {
me.render_opp_by_lead_source();
}
}
}
});
} else if (me.options.chart == 'sales_pipeline'){
frappe.call({
method: "erpnext.selling.page.sales_funnel.sales_funnel.get_pipeline_data",
args: {
from_date: this.options.from_date,
to_date: this.options.to_date,
company: this.company
},
btn: btn,
callback: function(r) {
if(!r.exc) {
me.options.data = r.message;
if (me.options.data=='empty') {
const $parent = me.elements.funnel_wrapper;
$parent.html(__('No data for this period'));
} else {
me.render_pipeline();
}
}
}
});
}
}
render: function() { render() {
let me = this;
if (me.options.chart == 'sales_funnel'){
me.render_funnel();
} else if (me.options.chart == 'opp_by_lead_source'){
me.render_opp_by_lead_source();
} else if (me.options.chart == 'sales_pipeline'){
me.render_pipeline();
}
}
render_funnel() {
var me = this; var me = this;
this.prepare(); this.prepare_funnel();
var context = this.elements.context, var context = this.elements.context,
x_start = 0.0, x_start = 0.0,
@ -119,9 +202,9 @@ erpnext.SalesFunnel = Class.extend({
me.draw_legend(x_mid, y_mid, me.options.width, me.options.height, d.value + " - " + d.title); me.draw_legend(x_mid, y_mid, me.options.width, me.options.height, d.value + " - " + d.title);
}); });
}, }
prepare: function() { prepare_funnel() {
var me = this; var me = this;
this.elements.no_data.toggle(false); this.elements.no_data.toggle(false);
@ -147,9 +230,9 @@ erpnext.SalesFunnel = Class.extend({
.attr("height", this.options.height); .attr("height", this.options.height);
this.elements.context = this.elements.canvas.get(0).getContext("2d"); this.elements.context = this.elements.canvas.get(0).getContext("2d");
}, }
draw_triangle: function(x_start, x_mid, x_end, y, height) { draw_triangle(x_start, x_mid, x_end, y, height) {
var context = this.elements.context; var context = this.elements.context;
context.beginPath(); context.beginPath();
context.moveTo(x_start, y); context.moveTo(x_start, y);
@ -158,9 +241,9 @@ erpnext.SalesFunnel = Class.extend({
context.lineTo(x_start, y); context.lineTo(x_start, y);
context.closePath(); context.closePath();
context.fill(); context.fill();
}, }
draw_legend: function(x_mid, y_mid, width, height, title) { draw_legend(x_mid, y_mid, width, height, title) {
var context = this.elements.context; var context = this.elements.context;
if(y_mid == 0) { if(y_mid == 0) {
@ -186,4 +269,44 @@ erpnext.SalesFunnel = Class.extend({
context.font = "1.1em sans-serif"; context.font = "1.1em sans-serif";
context.fillText(__(title), width + 20, y_mid); context.fillText(__(title), width + 20, y_mid);
} }
});
render_opp_by_lead_source() {
let me = this;
let currency = frappe.defaults.get_default("currency");
let chart_data = me.options.data ? me.options.data : null;
const parent = me.elements.funnel_wrapper[0];
this.chart = new Chart(parent, {
title: __("Sales Opportunities by Source"),
height: 400,
data: chart_data,
type: 'bar',
barOptions: {
stacked: 1
},
tooltipOptions: {
formatTooltipY: d => format_currency(d, currency),
}
});
}
render_pipeline() {
let me = this;
let currency = frappe.defaults.get_default("currency");
let chart_data = me.options.data ? me.options.data : null;
const parent = me.elements.funnel_wrapper[0];
this.chart = new Chart(parent, {
title: __("Sales Pipeline by Stage"),
height: 400,
data: chart_data,
type: 'bar',
tooltipOptions: {
formatTooltipY: d => format_currency(d, currency),
},
colors: ['light-green', 'green']
});
}
};

View File

@ -1,16 +1,18 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from erpnext.accounts.report.utils import convert
import pandas as pd
@frappe.whitelist() @frappe.whitelist()
def get_funnel_data(from_date, to_date): def get_funnel_data(from_date, to_date, company):
active_leads = frappe.db.sql("""select count(*) from `tabLead` active_leads = frappe.db.sql("""select count(*) from `tabLead`
where (date(`modified`) between %s and %s) where (date(`modified`) between %s and %s)
and status != "Do Not Contact" """, (from_date, to_date))[0][0] and status != "Do Not Contact" and company=%s""", (from_date, to_date, company))[0][0]
active_leads += frappe.db.sql("""select count(distinct contact.name) from `tabContact` contact active_leads += frappe.db.sql("""select count(distinct contact.name) from `tabContact` contact
left join `tabDynamic Link` dl on (dl.parent=contact.name) where dl.link_doctype='Customer' left join `tabDynamic Link` dl on (dl.parent=contact.name) where dl.link_doctype='Customer'
@ -18,14 +20,14 @@ def get_funnel_data(from_date, to_date):
opportunities = frappe.db.sql("""select count(*) from `tabOpportunity` opportunities = frappe.db.sql("""select count(*) from `tabOpportunity`
where (date(`creation`) between %s and %s) where (date(`creation`) between %s and %s)
and status != "Lost" """, (from_date, to_date))[0][0] and status != "Lost" and company=%s""", (from_date, to_date, company))[0][0]
quotations = frappe.db.sql("""select count(*) from `tabQuotation` quotations = frappe.db.sql("""select count(*) from `tabQuotation`
where docstatus = 1 and (date(`creation`) between %s and %s) where docstatus = 1 and (date(`creation`) between %s and %s)
and status != "Lost" """, (from_date, to_date))[0][0] and status != "Lost" and company=%s""", (from_date, to_date, company))[0][0]
sales_orders = frappe.db.sql("""select count(*) from `tabSales Order` sales_orders = frappe.db.sql("""select count(*) from `tabSales Order`
where docstatus = 1 and (date(`creation`) between %s and %s)""", (from_date, to_date))[0][0] where docstatus = 1 and (date(`creation`) between %s and %s) and company=%s""", (from_date, to_date, company))[0][0]
return [ return [
{ "title": _("Active Leads / Customers"), "value": active_leads, "color": "#B03B46" }, { "title": _("Active Leads / Customers"), "value": active_leads, "color": "#B03B46" },
@ -33,3 +35,54 @@ def get_funnel_data(from_date, to_date):
{ "title": _("Quotations"), "value": quotations, "color": "#006685" }, { "title": _("Quotations"), "value": quotations, "color": "#006685" },
{ "title": _("Sales Orders"), "value": sales_orders, "color": "#00AD65" } { "title": _("Sales Orders"), "value": sales_orders, "color": "#00AD65" }
] ]
@frappe.whitelist()
def get_opp_by_lead_source(from_date, to_date, company):
opportunities = frappe.get_all("Opportunity", filters=[['status', 'in', ['Open', 'Quotation', 'Replied']], ['company', '=', company], ['transaction_date', 'Between', [from_date, to_date]]], fields=['currency', 'sales_stage', 'opportunity_amount', 'probability', 'source'])
if opportunities:
default_currency = frappe.get_cached_value('Global Defaults', 'None', 'default_currency')
cp_opportunities = [dict(x, **{'compound_amount': (convert(x['opportunity_amount'], x['currency'], default_currency, to_date) * x['probability']/100)}) for x in opportunities]
df = pd.DataFrame(cp_opportunities).groupby(['source', 'sales_stage'], as_index=False).agg({'compound_amount': 'sum'})
result = {}
result['labels'] = list(set(df.source.values))
result['datasets'] = []
for s in set(df.sales_stage.values):
result['datasets'].append({'name': s, 'values': [0]*len(result['labels']), 'chartType': 'bar'})
for row in df.itertuples():
source_index = result['labels'].index(row.source)
for dataset in result['datasets']:
if dataset['name'] == row.sales_stage:
dataset['values'][source_index] = row.compound_amount
return result
else:
return 'empty'
@frappe.whitelist()
def get_pipeline_data(from_date, to_date, company):
opportunities = frappe.get_all("Opportunity", filters=[['status', 'in', ['Open', 'Quotation', 'Replied']], ['company', '=', company], ['transaction_date', 'Between', [from_date, to_date]]], fields=['currency', 'sales_stage', 'opportunity_amount', 'probability'])
if opportunities:
default_currency = frappe.get_cached_value('Global Defaults', 'None', 'default_currency')
cp_opportunities = [dict(x, **{'compound_amount': (convert(x['opportunity_amount'], x['currency'], default_currency, to_date) * x['probability']/100)}) for x in opportunities]
df = pd.DataFrame(cp_opportunities).groupby(['sales_stage'], as_index=True).agg({'compound_amount': 'sum'}).to_dict()
result = {}
result['labels'] = df['compound_amount'].keys()
result['datasets'] = []
result['datasets'].append({'name': _("Total Amount"), 'values': df['compound_amount'].values(), 'chartType': 'bar'})
return result
else:
return 'empty'

View File

@ -29,9 +29,9 @@ def execute(filters=None):
if customer_naming_type == "Naming Series": if customer_naming_type == "Naming Series":
row = [d.name, d.customer_name, credit_limit, outstanding_amt, bal, row = [d.name, d.customer_name, credit_limit, outstanding_amt, bal,
d.bypass_credit_limit_check_at_sales_order] d.bypass_credit_limit_check_at_sales_order, d.disabled]
else: else:
row = [d.name, credit_limit, outstanding_amt, bal, d.bypass_credit_limit_check_at_sales_order] row = [d.name, credit_limit, outstanding_amt, bal, d.bypass_credit_limit_check_at_sales_order, d.disabled]
if credit_limit: if credit_limit:
data.append(row) data.append(row)
@ -40,11 +40,13 @@ def execute(filters=None):
def get_columns(customer_naming_type): def get_columns(customer_naming_type):
columns = [ columns = [
_("Name") + ":Link/Customer:120",
_("Customer") + ":Link/Customer:120", _("Customer") + ":Link/Customer:120",
_("Credit Limit") + ":Currency:120", _("Credit Limit") + ":Currency:120",
_("Outstanding Amt") + ":Currency:100", _("Outstanding Amt") + ":Currency:100",
_("Credit Balance") + ":Currency:120", _("Credit Balance") + ":Currency:120",
_("Bypass credit check at Sales Order ") + ":Check:240" _("Bypass credit check at Sales Order ") + ":Check:240",
_("Is Disabled ") + ":Check:240"
] ]
if customer_naming_type == "Naming Series": if customer_naming_type == "Naming Series":
@ -59,5 +61,5 @@ def get_details(filters):
conditions += " where name = %(customer)s" conditions += " where name = %(customer)s"
return frappe.db.sql("""select name, customer_name, return frappe.db.sql("""select name, customer_name,
bypass_credit_limit_check_at_sales_order from `tabCustomer` %s bypass_credit_limit_check_at_sales_order,disabled from `tabCustomer` %s
""" % conditions, filters, as_dict=1) """ % conditions, filters, as_dict=1)

View File

@ -17,7 +17,7 @@ def execute(filters=None):
for d in entries: for d in entries:
if d.stock_qty > 0 or filters.get('show_return_entries', 0): if d.stock_qty > 0 or filters.get('show_return_entries', 0):
data.append([ data.append([
d.name, d.customer, d.territory, d.posting_date, d.item_code, d.name, d.customer, d.territory, item_details.get(d.item_code, {}).get("website_warehouse"), d.posting_date, d.item_code,
item_details.get(d.item_code, {}).get("item_group"), item_details.get(d.item_code, {}).get("brand"), item_details.get(d.item_code, {}).get("item_group"), item_details.get(d.item_code, {}).get("brand"),
d.stock_qty, d.base_net_amount, d.sales_person, d.allocated_percentage, d.contribution_amt d.stock_qty, d.base_net_amount, d.sales_person, d.allocated_percentage, d.contribution_amt
]) ])
@ -33,7 +33,8 @@ def get_columns(filters):
msgprint(_("Please select the document type first"), raise_exception=1) msgprint(_("Please select the document type first"), raise_exception=1)
return [filters["doc_type"] + ":Link/" + filters["doc_type"] + ":140", return [filters["doc_type"] + ":Link/" + filters["doc_type"] + ":140",
_("Customer") + ":Link/Customer:140", _("Territory") + ":Link/Territory:100", _("Posting Date") + ":Date:100", _("Customer") + ":Link/Customer:140", _("Territory") + ":Link/Territory:100", _("Warehouse") + ":Link/Warehouse:100",
_("Posting Date") + ":Date:100",
_("Item Code") + ":Link/Item:120", _("Item Group") + ":Link/Item Group:120", _("Item Code") + ":Link/Item:120", _("Item Group") + ":Link/Item Group:120",
_("Brand") + ":Link/Brand:120", _("Qty") + ":Float:100", _("Amount") + ":Currency:120", _("Brand") + ":Link/Brand:120", _("Qty") + ":Float:100", _("Amount") + ":Currency:120",
_("Sales Person") + ":Link/Sales Person:140", _("Contribution %") + "::110", _("Sales Person") + ":Link/Sales Person:140", _("Contribution %") + "::110",
@ -115,7 +116,7 @@ def get_items(filters):
def get_item_details(): def get_item_details():
item_details = {} item_details = {}
for d in frappe.db.sql("""select name, item_group, brand from `tabItem`""", as_dict=1): for d in frappe.db.sql("""select name, item_group, brand, website_warehouse from `tabItem`""", as_dict=1):
item_details.setdefault(d.name, d) item_details.setdefault(d.name, d)
return item_details return item_details

View File

@ -264,7 +264,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
{ {
row.incentives = flt( row.incentives = flt(
row.allocated_amount * row.commission_rate / 100.0, row.allocated_amount * row.commission_rate / 100.0,
precision("incentives", sales_person)); precision("incentives", row));
} }
}, },

View File

@ -166,13 +166,12 @@ class NamingSeries(Document):
def parse_naming_series(self): def parse_naming_series(self):
parts = self.prefix.split('.') parts = self.prefix.split('.')
# If series contain date format like INV.YYYY.MM.#####
if len(parts) > 2:
del parts[-1] # Removed ### from the series
prefix = parse_naming_series(parts)
else:
prefix = parts[0]
# Remove ### from the end of series
if parts[-1] == "#" * len(parts[-1]):
del parts[-1]
prefix = parse_naming_series(parts)
return prefix return prefix
def set_by_naming_series(doctype, fieldname, naming_series, hide_name_field=True): def set_by_naming_series(doctype, fieldname, naming_series, hide_name_field=True):

View File

@ -55,6 +55,10 @@ def set_default_settings(args):
buying_settings.allow_multiple_items = 1 buying_settings.allow_multiple_items = 1
buying_settings.save() buying_settings.save()
delivery_settings = frappe.get_doc("Delivery Settings")
delivery_settings.dispatch_template = _("Dispatch Notification")
delivery_settings.save()
hr_settings = frappe.get_doc("HR Settings") hr_settings = frappe.get_doc("HR Settings")
hr_settings.emp_created_by = "Naming Series" hr_settings.emp_created_by = "Naming Series"
hr_settings.leave_approval_notification_template = _("Leave Approval Notification") hr_settings.leave_approval_notification_template = _("Leave Approval Notification")

View File

@ -231,7 +231,7 @@ def install(country=None):
# Share Management # Share Management
{"doctype": "Share Type", "title": _("Equity")}, {"doctype": "Share Type", "title": _("Equity")},
{"doctype": "Share Type", "title": _("Preference")}, {"doctype": "Share Type", "title": _("Preference")}
] ]
from erpnext.setup.setup_wizard.data.industry_type import get_industry_types from erpnext.setup.setup_wizard.data.industry_type import get_industry_types
@ -250,6 +250,12 @@ def install(country=None):
records += [{'doctype': 'Email Template', 'name': _("Leave Status Notification"), 'response': response,\ records += [{'doctype': 'Email Template', 'name': _("Leave Status Notification"), 'response': response,\
'subject': _("Leave Status Notification"), 'owner': frappe.session.user}] 'subject': _("Leave Status Notification"), 'owner': frappe.session.user}]
base_path = frappe.get_app_path("erpnext", "stock", "doctype")
response = frappe.read_file(os.path.join(base_path, "delivery_trip/dispatch_notification_template.html"))
records += [{'doctype': 'Email Template', 'name': _("Dispatch Notification"), 'response': response,\
'subject': _("Your order is out for delivery!"), 'owner': frappe.session.user}]
# Records for the Supplier Scorecard # Records for the Supplier Scorecard
from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import make_default_records from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import make_default_records
make_default_records() make_default_records()
@ -292,6 +298,30 @@ def add_uom_data():
"value": d.get("value") "value": d.get("value")
}).insert(ignore_permissions=True) }).insert(ignore_permissions=True)
def add_market_segments():
records = [
# Market Segments
{"doctype": "Market Segment", "market_segment": _("Lower Income")},
{"doctype": "Market Segment", "market_segment": _("Middle Income")},
{"doctype": "Market Segment", "market_segment": _("Upper Income")}
]
make_fixture_records(records)
def add_sale_stages():
# Sale Stages
records = [
{"doctype": "Sales Stage", "stage_name": _("Prospecting")},
{"doctype": "Sales Stage", "stage_name": _("Qualification")},
{"doctype": "Sales Stage", "stage_name": _("Needs Analysis")},
{"doctype": "Sales Stage", "stage_name": _("Value Proposition")},
{"doctype": "Sales Stage", "stage_name": _("Identifying Decision Makers")},
{"doctype": "Sales Stage", "stage_name": _("Perception Analysis")},
{"doctype": "Sales Stage", "stage_name": _("Proposal/Price Quote")},
{"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")}
]
make_fixture_records(records)
def make_fixture_records(records): def make_fixture_records(records):
from frappe.modules import scrub from frappe.modules import scrub
for r in records: for r in records:

View File

@ -104,6 +104,8 @@ def setup_complete(args=None):
def stage_fixtures(args): def stage_fixtures(args):
install_fixtures.install(args.get("country")) install_fixtures.install(args.get("country"))
install_fixtures.add_market_segments()
install_fixtures.add_sale_stages()
def setup_company(args): def setup_company(args):
defaults_setup.create_price_lists(args) defaults_setup.create_price_lists(args)

View File

@ -460,6 +460,7 @@ def make_delivery_trip(source_name, target_doc=None):
target_doc.customer_address = source_parent.shipping_address target_doc.customer_address = source_parent.shipping_address
target_doc.contact = source_parent.contact_person target_doc.contact = source_parent.contact_person
target_doc.customer_contact = source_parent.contact_display target_doc.customer_contact = source_parent.contact_display
target_doc.grand_total = source_parent.grand_total
# Append unique Delivery Notes in Delivery Trip # Append unique Delivery Notes in Delivery Trip
delivery_notes.append(target_doc.delivery_note) delivery_notes.append(target_doc.delivery_note)

View File

@ -0,0 +1,8 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Delivery Settings', {
refresh: function(frm) {
}
});

View File

@ -0,0 +1,194 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-09-04 23:01:34.458550",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sb_dispatch",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Dispatch Settings",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "dispatch_template",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Dispatch Notification Template",
"length": 0,
"no_copy": 0,
"options": "Email Template",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "send_with_attachment",
"description": "Leave blank to use the standard Delivery Note format",
"fieldname": "dispatch_attachment",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Dispatch Notification Attachment",
"length": 0,
"no_copy": 0,
"options": "Print Format",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "send_with_attachment",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Send with Attachment",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2018-09-05 00:16:23.569855",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Settings",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class DeliverySettings(Document):
pass

View File

@ -0,0 +1,23 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Delivery Settings", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Delivery Settings
() => frappe.tests.make('Delivery Settings', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
class TestDeliverySettings(unittest.TestCase):
pass

View File

@ -18,7 +18,7 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 2,
"fieldname": "customer", "fieldname": "customer",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -78,6 +78,38 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "lock",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Lock",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -148,7 +180,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fieldname": "section_break_7", "fieldname": "order_information_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
@ -157,6 +189,135 @@
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Order Information",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "delivery_note",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Delivery Note",
"length": 0,
"no_copy": 1,
"options": "Delivery Note",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "cb_order",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "grand_total",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Grand Total",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_7",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Contact Information",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
@ -186,7 +347,7 @@
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 1, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Contact Name", "label": "Contact Name",
"length": 0, "length": 0,
@ -205,6 +366,38 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "email_sent_to",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Email sent to",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -284,6 +477,7 @@
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Dispatch Information",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
@ -362,196 +556,6 @@
"set_only_once": 0, "set_only_once": 0,
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_12",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "delivery_note",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Delivery Note",
"length": 0,
"no_copy": 1,
"options": "Delivery Note",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_14",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "notified_by_email",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Notified by Email",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_18",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "email_sent_to",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Email sent to",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
} }
], ],
"has_web_view": 0, "has_web_view": 0,
@ -564,7 +568,7 @@
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-08-21 22:25:53.276548", "modified": "2018-09-05 00:51:55.275009",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Delivery Stop", "name": "Delivery Stop",

View File

@ -2,8 +2,8 @@
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on('Delivery Trip', { frappe.ui.form.on('Delivery Trip', {
setup: function(frm) { setup: function (frm) {
frm.set_query("driver", function() { frm.set_query("driver", function () {
return { return {
filters: { filters: {
"status": "Active" "status": "Active"
@ -11,7 +11,7 @@ frappe.ui.form.on('Delivery Trip', {
}; };
}); });
frm.set_query("address", "delivery_stops", function(doc, cdt, cdn) { frm.set_query("address", "delivery_stops", function (doc, cdt, cdn) {
var row = locals[cdt][cdn]; var row = locals[cdt][cdn];
if (row.customer) { if (row.customer) {
return { return {
@ -24,7 +24,7 @@ frappe.ui.form.on('Delivery Trip', {
} }
}) })
frm.set_query("contact", "delivery_stops", function(doc, cdt, cdn) { frm.set_query("contact", "delivery_stops", function (doc, cdt, cdn) {
var row = locals[cdt][cdn]; var row = locals[cdt][cdn];
if (row.customer) { if (row.customer) {
return { return {
@ -45,7 +45,7 @@ frappe.ui.form.on('Delivery Trip', {
}); });
} }
if (frm.doc.docstatus===0) { if (frm.doc.docstatus === 0) {
frm.add_custom_button(__('Delivery Note'), () => { frm.add_custom_button(__('Delivery Note'), () => {
erpnext.utils.map_current_doc({ erpnext.utils.map_current_doc({
method: "erpnext.stock.doctype.delivery_note.delivery_note.make_delivery_trip", method: "erpnext.stock.doctype.delivery_note.delivery_note.make_delivery_trip",
@ -93,12 +93,8 @@ frappe.ui.form.on('Delivery Trip', {
}, },
notify_customers: function (frm) { notify_customers: function (frm) {
var owner_email = frm.doc.owner == "Administrator"
? frappe.user_info("Administrator").email
: frm.doc.owner;
$.each(frm.doc.delivery_stops || [], function (i, delivery_stop) { $.each(frm.doc.delivery_stops || [], function (i, delivery_stop) {
if (!delivery_stop.delivery_notes) { if (!delivery_stop.delivery_note) {
frappe.msgprint({ frappe.msgprint({
"message": __("No Delivery Note selected for Customer {}", [delivery_stop.customer]), "message": __("No Delivery Note selected for Customer {}", [delivery_stop.customer]),
"title": __("Warning"), "title": __("Warning"),
@ -107,34 +103,37 @@ frappe.ui.form.on('Delivery Trip', {
}); });
} }
}); });
frappe.confirm(__("Do you want to notify all the customers by email?"), function () {
frappe.call({ frappe.db.get_value("Delivery Settings", { name: "Delivery Settings" }, "dispatch_template", (r) => {
method: "erpnext.stock.doctype.delivery_trip.delivery_trip.notify_customers", if (!r.dispatch_template) {
args: { frappe.throw(__("Missing email template for dispatch. Please set one in Delivery Settings."));
"docname": frm.doc.name, } else {
"date": frm.doc.date, frappe.confirm(__("Do you want to notify all the customers by email?"), function () {
"driver": frm.doc.driver, frappe.call({
"vehicle": frm.doc.vehicle, method: "erpnext.stock.doctype.delivery_trip.delivery_trip.notify_customers",
"sender_email": owner_email, args: {
"sender_name": frappe.user.full_name(owner_email), "delivery_trip": frm.doc.name
"delivery_notification": frm.doc.delivery_notification },
} callback: function (r) {
}); if (!r.exc) {
frm.doc.email_notification_sent = true; frm.doc.email_notification_sent = true;
frm.refresh_field('email_notification_sent'); frm.refresh_field('email_notification_sent');
}
}
});
});
}
}); });
} }
}); });
frappe.ui.form.on('Delivery Stop', { frappe.ui.form.on('Delivery Stop', {
customer: function (frm, cdt, cdn) { customer: function (frm, cdt, cdn) {
var row = locals[cdt][cdn]; var row = locals[cdt][cdn];
if(row.customer) { if (row.customer) {
frappe.call({ frappe.call({
method: "erpnext.stock.doctype.delivery_trip.delivery_trip.get_contact_and_address", method: "erpnext.stock.doctype.delivery_trip.delivery_trip.get_contact_and_address",
args: {"name": row.customer}, args: { "name": row.customer },
callback: function (r) { callback: function (r) {
if (r.message) { if (r.message) {
if (r.message["shipping_address"]) { if (r.message["shipping_address"]) {
@ -158,12 +157,13 @@ frappe.ui.form.on('Delivery Stop', {
}); });
} }
}, },
address: function (frm, cdt, cdn) { address: function (frm, cdt, cdn) {
var row = locals[cdt][cdn]; var row = locals[cdt][cdn];
if(row.address) { if (row.address) {
frappe.call({ frappe.call({
method: "frappe.contacts.doctype.address.address.get_address_display", method: "frappe.contacts.doctype.address.address.get_address_display",
args: {"address_dict": row.address}, args: { "address_dict": row.address },
callback: function (r) { callback: function (r) {
if (r.message) { if (r.message) {
frappe.model.set_value(cdt, cdn, "customer_address", r.message); frappe.model.set_value(cdt, cdn, "customer_address", r.message);
@ -177,10 +177,10 @@ frappe.ui.form.on('Delivery Stop', {
contact: function (frm, cdt, cdn) { contact: function (frm, cdt, cdn) {
var row = locals[cdt][cdn]; var row = locals[cdt][cdn];
if(row.contact) { if (row.contact) {
frappe.call({ frappe.call({
method: "erpnext.stock.doctype.delivery_trip.delivery_trip.get_contact_display", method: "erpnext.stock.doctype.delivery_trip.delivery_trip.get_contact_display",
args: {"contact": row.contact}, args: { "contact": row.contact },
callback: function (r) { callback: function (r) {
if (r.message) { if (r.message) {
frappe.model.set_value(cdt, cdn, "customer_contact", r.message); frappe.model.set_value(cdt, cdn, "customer_contact", r.message);

View File

@ -114,7 +114,7 @@
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
"allow_on_submit": 0, "allow_on_submit": 1,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
@ -437,7 +437,7 @@
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
"allow_on_submit": 1, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
@ -507,7 +507,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fieldname": "section_break_13", "fieldname": "section_break_15",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
@ -531,40 +531,6 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "delivery_notification",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Delivery Notification",
"length": 0,
"no_copy": 0,
"options": "Email Template",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -608,7 +574,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-08-30 02:31:49.400138", "modified": "2018-09-05 01:20:34.165834",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Delivery Trip", "name": "Delivery Trip",

View File

@ -10,8 +10,7 @@ import frappe
from frappe import _ from frappe import _
from frappe.contacts.doctype.address.address import get_address_display from frappe.contacts.doctype.address.address import get_address_display
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import cstr, get_datetime, getdate, get_link_to_form from frappe.utils import get_datetime, get_link_to_form, cstr
from frappe.utils.user import get_user_fullname
class DeliveryTrip(Document): class DeliveryTrip(Document):
@ -169,59 +168,54 @@ def get_arrival_times(name):
@frappe.whitelist() @frappe.whitelist()
def notify_customers(docname, date, driver, vehicle, sender_email, delivery_notification): def notify_customers(delivery_trip):
sender_name = get_user_fullname(sender_email) delivery_trip = frappe.get_doc("Delivery Trip", delivery_trip)
attachments = []
parent_doc = frappe.get_doc('Delivery Trip', docname) context = delivery_trip.as_dict()
args = parent_doc.as_dict() context.update({
"departure_time": cstr(context.get("departure_time")),
"estimated_arrival": cstr(context.get("estimated_arrival"))
})
for delivery_stop in parent_doc.delivery_stops: if delivery_trip.driver:
contact_info = frappe.db.get_value("Contact", delivery_stop.contact, context.update(frappe.db.get_value("Driver", delivery_trip.driver, "cell_number", as_dict=1))
["first_name", "last_name", "email_id", "gender"], as_dict=1)
args.update(delivery_stop.as_dict()) email_recipients = []
args.update(contact_info)
if delivery_stop.delivery_note: for stop in delivery_trip.delivery_stops:
default_print_format = frappe.get_meta('Delivery Note').default_print_format contact_info = frappe.db.get_value("Contact", stop.contact,
attachments = frappe.attach_print('Delivery Note', ["first_name", "last_name", "email_id", "gender"], as_dict=1)
delivery_stop.delivery_note,
file_name="Delivery Note",
print_format=default_print_format or "Standard")
if not delivery_stop.notified_by_email and contact_info.email_id: if contact_info and contact_info.email_id:
driver_info = frappe.db.get_value("Driver", driver, ["full_name", "cell_number"], as_dict=1) context.update(stop.as_dict())
sender_designation = frappe.db.get_value("Employee", sender_email, ["designation"]) context.update(contact_info)
estimated_arrival = cstr(delivery_stop.estimated_arrival)[:-3] dispatch_template_name = frappe.db.get_single_value("Delivery Settings", "dispatch_template")
email_template = frappe.get_doc("Email Template", delivery_notification) dispatch_template = frappe.get_doc("Email Template", dispatch_template_name)
message = frappe.render_template(email_template.response, args)
frappe.sendmail( frappe.sendmail(recipients=contact_info.email_id,
recipients=contact_info.email_id, subject=dispatch_template.subject,
sender=sender_email, message=frappe.render_template(dispatch_template.response, context),
message=message, attachments=get_attachments(stop))
attachments=attachments,
subject=_(email_template.subject).format(getdate(date).strftime('%d.%m.%y'),
estimated_arrival))
frappe.db.set_value("Delivery Stop", delivery_stop.name, "notified_by_email", 1) stop.db_set("email_sent_to", contact_info.email_id)
frappe.db.set_value("Delivery Stop", delivery_stop.name, email_recipients.append(contact_info.email_id)
"email_sent_to", contact_info.email_id)
frappe.msgprint(_("Email sent to {0}").format(contact_info.email_id))
def round_timedelta(td, period): if email_recipients:
"""Round timedelta""" frappe.msgprint(_("Email sent to {0}").format(", ".join(email_recipients)))
period_seconds = period.total_seconds() delivery_trip.db_set("email_notification_sent", True)
half_period_seconds = period_seconds / 2
remainder = td.total_seconds() % period_seconds
if remainder >= half_period_seconds:
return datetime.timedelta(seconds=td.total_seconds() + (period_seconds - remainder))
else: else:
return datetime.timedelta(seconds=td.total_seconds() - remainder) frappe.msgprint(_("No contacts with email IDs found."))
def format_address(address):
"""Customer Address format """ def get_attachments(delivery_stop):
address = frappe.get_doc('Address', address) if not (frappe.db.get_single_value("Delivery Settings", "send_with_attachment") and delivery_stop.delivery_note):
return '{}, {}, {}, {}'.format(address.address_line1, address.city, address.pincode, address.country) return []
dispatch_attachment = frappe.db.get_single_value("Delivery Settings", "dispatch_attachment")
attachments = frappe.attach_print("Delivery Note",
delivery_stop.delivery_note,
file_name="Delivery Note",
print_format=dispatch_attachment)
return [attachments]

View File

@ -0,0 +1,50 @@
<h1>Dispatch Notification</h1>
<h3>Details:</h3>
<table class="table table-bordered small" style="max-width: 500px;">
<tbody>
<tr>
<td>Customer's Name</td>
<td>{{ customer }}</td>
</tr>
<tr>
<td>Contact's Name</td>
<td>{{ first_name }} {{ last_name }}</td>
</tr>
<tr>
<td>Address Name</td>
<td>{{ address }}</td>
</tr>
<tr>
<td>Address Details</td>
<td>{{ customer_address }}</td>
</tr>
<tr>
<td>Order Number</td>
<td>{{ delivery_note }}</td>
</tr>
<tr>
<td>Order Total</td>
<td>{{ grand_total }}</td>
</tr>
<tr>
<td>Departure Time</td>
<td>{{ departure_time }}</td>
</tr>
<tr>
<td>Estimated Arrival</td>
<td>{{ estimated_arrival }}</td>
</tr>
<tr>
<td>Driver's Name</td>
<td>{{ driver_name }}</td>
</tr>
<tr>
<td>Driver's Number</td>
<td>{{ cell_number }}</td>
</tr>
<tr>
<td>Vehicle Number</td>
<td>{{ vehicle }}</td>
</tr>
</tbody>
</table>

View File

@ -3,60 +3,71 @@
# See license.txt # See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe
import erpnext
import unittest import unittest
from frappe.utils import nowdate, add_days
import erpnext
import frappe
from erpnext.stock.doctype.delivery_trip.delivery_trip import get_contact_and_address, notify_customers
from erpnext.tests.utils import create_test_contact_and_address from erpnext.tests.utils import create_test_contact_and_address
from erpnext.stock.doctype.delivery_trip.delivery_trip import notify_customers, get_contact_and_address from frappe.utils import add_days, now_datetime, nowdate
class TestDeliveryTrip(unittest.TestCase): class TestDeliveryTrip(unittest.TestCase):
def setUp(self): def setUp(self):
create_driver() create_driver()
create_vehicle() create_vehicle()
create_delivery_notfication() create_delivery_notification()
create_test_contact_and_address() create_test_contact_and_address()
def test_delivery_trip(self): def test_delivery_trip(self):
contact = get_contact_and_address("_Test Customer") contact = get_contact_and_address("_Test Customer")
if not frappe.db.exists("Delivery Trip", "TOUR-00000"): if not frappe.db.exists("Delivery Trip", "TOUR-00000"):
delivery_trip = frappe.new_doc("Delivery Trip") delivery_trip = frappe.get_doc({
delivery_trip.company = erpnext.get_default_company() "doctype": "Delivery Trip",
delivery_trip.date = add_days(nowdate(), 5) "company": erpnext.get_default_company(),
delivery_trip.driver = "DRIVER-00001" "date": add_days(nowdate(), 5),
delivery_trip.vehicle = "JB 007" "departure_time": add_days(now_datetime(), 5),
delivery_trip.append("delivery_stops", { "driver": frappe.db.get_value('Driver', {"full_name": "Newton Scmander"}),
"customer": "_Test Customer", "vehicle": "JB 007",
"address": contact.shipping_address.parent, "delivery_stops": [{
"contact": contact.contact_person.parent "customer": "_Test Customer",
"address": contact.shipping_address.parent,
"contact": contact.contact_person.parent
}]
}) })
delivery_trip.delivery_notification = 'Delivery Notification'
delivery_trip.insert() delivery_trip.insert()
sender_email = frappe.db.get_value("User", frappe.session.user, "email")
notify_customers(docname=delivery_trip.name, date=delivery_trip.date, driver=delivery_trip.driver,
vehicle=delivery_trip.vehicle,
sender_email=sender_email, delivery_notification=delivery_trip.delivery_notification)
self.assertEqual(delivery_trip.get("delivery_stops")[0].notified_by_email, 0) notify_customers(delivery_trip=delivery_trip.name)
delivery_trip.load_from_db()
self.assertEqual(delivery_trip.email_notification_sent, 1)
def create_driver(): def create_driver():
if not frappe.db.exists("Driver", "Newton Scmander"): if not frappe.db.exists("Driver", "Newton Scmander"):
driver = frappe.new_doc("Driver") driver = frappe.get_doc({
driver.full_name = "Newton Scmander" "doctype": "Driver",
driver.cell_number = "98343424242" "full_name": "Newton Scmander",
driver.license_number = "B809" "cell_number": "98343424242",
"license_number": "B809"
})
driver.insert() driver.insert()
def create_delivery_notfication():
def create_delivery_notification():
if not frappe.db.exists("Email Template", "Delivery Notification"): if not frappe.db.exists("Email Template", "Delivery Notification"):
frappe.get_doc({ dispatch_template = frappe.get_doc({
'doctype': 'Email Template', 'doctype': 'Email Template',
'name': 'Delivery Notification', 'name': 'Delivery Notification',
'response': 'Test Delivery Trip', 'response': 'Test Delivery Trip',
'subject': 'Test Subject', 'subject': 'Test Subject',
'owner': frappe.session.user 'owner': frappe.session.user
}).insert() })
dispatch_template.insert()
delivery_settings = frappe.get_single("Delivery Settings")
delivery_settings.dispatch_template = 'Delivery Notification'
def create_vehicle(): def create_vehicle():
if not frappe.db.exists("Vehicle", "JB 007"): if not frappe.db.exists("Vehicle", "JB 007"):

View File

@ -7,7 +7,8 @@ def get_data():
'Purchase Invoice': 'purchase_receipt', 'Purchase Invoice': 'purchase_receipt',
'Asset': 'purchase_receipt', 'Asset': 'purchase_receipt',
'Landed Cost Voucher': 'receipt_document', 'Landed Cost Voucher': 'receipt_document',
'Auto Repeat': 'reference_document' 'Auto Repeat': 'reference_document',
'Purchase Receipt': 'return_against'
}, },
'internal_links': { 'internal_links': {
'Purchase Order': ['items', 'purchase_order'], 'Purchase Order': ['items', 'purchase_order'],
@ -25,7 +26,7 @@ def get_data():
}, },
{ {
'label': _('Returns'), 'label': _('Returns'),
'items': ['Stock Entry'] 'items': ['Purchase Receipt']
}, },
{ {
'label': _('Subscription'), 'label': _('Subscription'),

View File

@ -379,7 +379,7 @@ class StockEntry(StockController):
def calculate_rate_and_amount(self, force=False, def calculate_rate_and_amount(self, force=False,
update_finished_item_rate=True, raise_error_if_no_rate=True): update_finished_item_rate=True, raise_error_if_no_rate=True):
self.set_basic_rate(force, update_finished_item_rate) self.set_basic_rate(force, update_finished_item_rate, raise_error_if_no_rate)
self.distribute_additional_costs() self.distribute_additional_costs()
self.update_valuation_rate() self.update_valuation_rate()
self.set_total_incoming_outgoing_value() self.set_total_incoming_outgoing_value()

View File

@ -1,17 +1,17 @@
{ {
"add_total_row": 0, "add_total_row": 1,
"apply_user_permissions": 1,
"creation": "2013-06-04 11:03:47", "creation": "2013-06-04 11:03:47",
"disabled": 0, "disabled": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Report", "doctype": "Report",
"idx": 3, "idx": 3,
"is_standard": "Yes", "is_standard": "Yes",
"modified": "2017-02-24 20:19:13.150769", "modified": "2018-09-20 18:20:49.229153",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Batch-Wise Balance History", "name": "Batch-Wise Balance History",
"owner": "Administrator", "owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Stock Ledger Entry", "ref_doctype": "Stock Ledger Entry",
"report_name": "Batch-Wise Balance History", "report_name": "Batch-Wise Balance History",
"report_type": "Script Report", "report_type": "Script Report",

View File

@ -3,6 +3,15 @@
frappe.query_reports["Item Prices"] = { frappe.query_reports["Item Prices"] = {
"filters": [ "filters": [
{
"fieldname": "items",
"label": __("Items Filter"),
"fieldtype": "Select",
"options": "Enabled Items only\nDisabled Items only\nAll Items",
"default": "Enabled Items only",
"on_change": function(query_report) {
query_report.trigger_refresh();
}
}
] ]
} }

View File

@ -10,7 +10,8 @@ def execute(filters=None):
if not filters: filters = {} if not filters: filters = {}
columns = get_columns(filters) columns = get_columns(filters)
item_map = get_item_details() conditions = get_condition(filters)
item_map = get_item_details(conditions)
pl = get_price_list() pl = get_price_list()
last_purchase_rate = get_last_purchase_rate() last_purchase_rate = get_last_purchase_rate()
bom_rate = get_item_bom_rate() bom_rate = get_item_bom_rate()
@ -41,14 +42,14 @@ def get_columns(filters):
return columns return columns
def get_item_details(): def get_item_details(conditions):
"""returns all items details""" """returns all items details"""
item_map = {} item_map = {}
for i in frappe.db.sql("""select name, item_group, item_name, description, for i in frappe.db.sql("""select name, item_group, item_name, description,
brand, stock_uom from tabItem brand, stock_uom from tabItem %s
order by item_code, item_group""", as_dict=1): order by item_code, item_group""" % (conditions), as_dict=1):
item_map.setdefault(i.name, i) item_map.setdefault(i.name, i)
return item_map return item_map
@ -133,3 +134,15 @@ def get_valuation_rate():
item_val_rate_map.setdefault(d.item_code, d.val_rate) item_val_rate_map.setdefault(d.item_code, d.val_rate)
return item_val_rate_map return item_val_rate_map
def get_condition(filters):
"""Get Filter Items"""
if filters.get("items") == "Enabled Items only":
conditions = " where disabled=0 "
elif filters.get("items") == "Disabled Items only":
conditions = " where disabled=1 "
else:
conditions = ""
return conditions

View File

@ -3,8 +3,8 @@
<a class="product-link" href="{{ route|abs_url }}"> <a class="product-link" href="{{ route|abs_url }}">
<div class="col-sm-4 col-xs-4 product-image-wrapper"> <div class="col-sm-4 col-xs-4 product-image-wrapper">
<div class="product-image-img"> <div class="product-image-img">
{{ product_image_square(thumbnail or website_image) }} {{ product_image_square(thumbnail or website_image or image) }}
<div class="product-text" itemprop="name">{{ item_name }}</div> <div class="product-text" itemprop="name">{{ item_name or name }}</div>
{% if price_sales_uom %} {% if price_sales_uom %}
<div>{{ price_sales_uom }}</div> <div>{{ price_sales_uom }}</div>
<div style='font-size: small; margin-bottom: 10px;'>({{ price_stock_uom }} / {{ stock_uom }})</div> <div style='font-size: small; margin-bottom: 10px;'>({{ price_stock_uom }} / {{ stock_uom }})</div>

Some files were not shown because too many files have changed in this diff Show More