Merge branch 'staging-fixes' into staging
This commit is contained in:
commit
c6eac7c60b
@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '10.1.54'
|
||||
__version__ = '10.1.55'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
@ -35,8 +35,8 @@ def validate_service_stop_date(doc):
|
||||
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
|
||||
invoices = frappe.db.sql_list('''
|
||||
select parent from `tabPurchase Invoice Item` where service_start_date<=%s and service_end_date>=%s
|
||||
and enable_deferred_expense = 1 and docstatus = 1
|
||||
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 ifnull(amount, 0) > 0
|
||||
''', (end_date or today(), start_date or add_months(today(), -1)))
|
||||
|
||||
# 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):
|
||||
# check for the sales invoice for which GL entries has to be done
|
||||
invoices = frappe.db.sql_list('''
|
||||
select parent from `tabSales Invoice Item` where service_start_date<=%s and service_end_date>=%s
|
||||
and enable_deferred_revenue = 1 and docstatus = 1
|
||||
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 ifnull(amount, 0) > 0
|
||||
''', (end_date or today(), start_date or add_months(today(), -1)))
|
||||
|
||||
# For each invoice, book deferred revenue
|
||||
|
@ -58,7 +58,6 @@ class CostCenter(NestedSet):
|
||||
# Validate properties before merging
|
||||
super(CostCenter, self).before_rename(olddn, new_cost_center, merge, "is_group")
|
||||
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)
|
||||
|
||||
return new_cost_center
|
||||
@ -89,3 +88,8 @@ class CostCenter(NestedSet):
|
||||
|
||||
def on_doctype_update():
|
||||
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
|
||||
|
@ -23,7 +23,6 @@ frappe.ui.form.on('Payment Entry', {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
frm.set_query("party_type", function() {
|
||||
return{
|
||||
"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() {
|
||||
var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_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);
|
||||
},
|
||||
|
||||
contact_person: function(frm) {
|
||||
frm.set_value("contact_email", "");
|
||||
erpnext.utils.get_contact_details(frm);
|
||||
},
|
||||
|
||||
hide_unhide_fields: function(frm) {
|
||||
var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: "";
|
||||
|
||||
@ -146,7 +160,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
frm.toggle_display("set_exchange_gain_loss",
|
||||
(frm.doc.paid_amount && frm.doc.received_amount && frm.doc.difference_amount &&
|
||||
((frm.doc.paid_from_account_currency != company_currency ||
|
||||
frm.doc.paid_to_account_currency != company_currency) &&
|
||||
frm.doc.paid_to_account_currency != company_currency) &&
|
||||
frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency)));
|
||||
|
||||
frm.refresh_fields();
|
||||
@ -208,7 +222,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
});
|
||||
} else {
|
||||
if(frm.doc.party) {
|
||||
frm.events.party(frm);
|
||||
frm.events.party(frm);
|
||||
}
|
||||
|
||||
if(frm.doc.mode_of_payment) {
|
||||
@ -230,13 +244,16 @@ frappe.ui.form.on('Payment Entry', {
|
||||
},
|
||||
|
||||
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.posting_date) {
|
||||
frappe.msgprint(__("Please select Posting Date before selecting Party"))
|
||||
frm.set_value("party", "");
|
||||
return ;
|
||||
}
|
||||
|
||||
frm.set_party_account_based_on_party = true;
|
||||
|
||||
return frappe.call({
|
||||
@ -302,7 +319,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
|
||||
}
|
||||
frm.set_value("received_amount", frm.doc.paid_amount);
|
||||
|
||||
|
||||
} else {
|
||||
frm.events.received_amount(frm);
|
||||
}
|
||||
@ -350,7 +367,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
]);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@ -402,7 +419,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
posting_date: function(frm) {
|
||||
frm.events.paid_from_account_currency(frm);
|
||||
},
|
||||
@ -415,7 +432,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
|
||||
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
|
||||
}
|
||||
|
||||
|
||||
frm.events.set_unallocated_amount(frm);
|
||||
}
|
||||
|
||||
@ -425,17 +442,17 @@ frappe.ui.form.on('Payment Entry', {
|
||||
|
||||
target_exchange_rate: function(frm) {
|
||||
frm.set_paid_amount_based_on_received_amount = true;
|
||||
|
||||
|
||||
if (frm.doc.received_amount) {
|
||||
frm.set_value("base_received_amount",
|
||||
flt(frm.doc.received_amount) * flt(frm.doc.target_exchange_rate));
|
||||
|
||||
if(!frm.doc.source_exchange_rate &&
|
||||
|
||||
if(!frm.doc.source_exchange_rate &&
|
||||
(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency)) {
|
||||
frm.set_value("source_exchange_rate", frm.doc.target_exchange_rate);
|
||||
frm.set_value("base_paid_amount", frm.doc.base_received_amount);
|
||||
}
|
||||
|
||||
|
||||
frm.events.set_unallocated_amount(frm);
|
||||
}
|
||||
frm.set_paid_amount_based_on_received_amount = false;
|
||||
@ -468,14 +485,14 @@ frappe.ui.form.on('Payment Entry', {
|
||||
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount);
|
||||
else
|
||||
frm.events.set_unallocated_amount(frm);
|
||||
|
||||
|
||||
frm.set_paid_amount_based_on_received_amount = false;
|
||||
},
|
||||
|
||||
|
||||
reset_received_amount: function(frm) {
|
||||
if(!frm.set_paid_amount_based_on_received_amount &&
|
||||
(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency)) {
|
||||
|
||||
|
||||
frm.set_value("received_amount", frm.doc.paid_amount);
|
||||
|
||||
if(frm.doc.source_exchange_rate) {
|
||||
@ -483,7 +500,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
}
|
||||
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
|
||||
}
|
||||
|
||||
|
||||
if(frm.doc.payment_type == "Receive")
|
||||
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount);
|
||||
else
|
||||
@ -549,7 +566,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
|
||||
(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier") ||
|
||||
(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Employee") ||
|
||||
(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
|
||||
(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
|
||||
) {
|
||||
if(total_positive_outstanding > total_negative_outstanding)
|
||||
frm.set_value("paid_amount",
|
||||
@ -694,7 +711,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
frm.set_value("unallocated_amount", unallocated_amount);
|
||||
frm.trigger("set_difference_amount");
|
||||
},
|
||||
|
||||
|
||||
set_difference_amount: function(frm) {
|
||||
var difference_amount = 0;
|
||||
var base_unallocated_amount = flt(frm.doc.unallocated_amount) *
|
||||
@ -753,7 +770,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry", [row.idx]));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if(frm.doc.party_type=="Employee" &&
|
||||
!in_list(["Expense Claim", "Journal Entry"], row.reference_doctype)
|
||||
) {
|
||||
|
@ -376,6 +376,40 @@
|
||||
"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": "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_in_quick_entry": 0,
|
||||
@ -441,6 +475,40 @@
|
||||
"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": "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_in_quick_entry": 0,
|
||||
@ -1972,7 +2040,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-09-11 15:44:28.647566",
|
||||
"modified": "2018-09-25 14:38:48.312629",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry",
|
||||
|
@ -299,12 +299,12 @@ class PaymentEntry(AccountsController):
|
||||
if self.payment_type == "Receive" \
|
||||
and self.base_total_allocated_amount < self.base_received_amount + total_deductions \
|
||||
and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate):
|
||||
self.unallocated_amount = (self.base_received_amount + total_deductions -
|
||||
self.unallocated_amount = (self.base_received_amount + total_deductions -
|
||||
self.base_total_allocated_amount) / self.source_exchange_rate
|
||||
elif self.payment_type == "Pay" \
|
||||
and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \
|
||||
and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate):
|
||||
self.unallocated_amount = (self.base_paid_amount - (total_deductions +
|
||||
self.unallocated_amount = (self.base_paid_amount - (total_deductions +
|
||||
self.base_total_allocated_amount)) / self.target_exchange_rate
|
||||
|
||||
def set_difference_amount(self):
|
||||
@ -790,7 +790,6 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
||||
@frappe.whitelist()
|
||||
def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None):
|
||||
doc = frappe.get_doc(dt, dn)
|
||||
|
||||
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))
|
||||
|
||||
@ -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.party_type = 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.paid_from = party_account if payment_type=="Receive" else bank.account
|
||||
|
@ -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) {
|
||||
var help_content =
|
||||
`<table class="table table-bordered" style="background-color: #f9f9f9;">
|
||||
|
@ -384,3 +384,13 @@ def set_transaction_type(args):
|
||||
args.transaction_type = "selling"
|
||||
else:
|
||||
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
|
@ -77,8 +77,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
}
|
||||
|
||||
if (doc.outstanding_amount > 0 && !cint(doc.is_return)) {
|
||||
cur_frm.add_custom_button(__('Payment Request'),
|
||||
this.make_payment_request, __("Make"));
|
||||
cur_frm.add_custom_button(__('Payment Request'), function() {
|
||||
me.make_payment_request()
|
||||
}, __("Make"));
|
||||
}
|
||||
|
||||
if(doc.docstatus===0) {
|
||||
|
@ -403,16 +403,20 @@ class PurchaseInvoice(BuyingController):
|
||||
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
||||
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"):
|
||||
if flt(item.base_net_amount):
|
||||
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:
|
||||
val_rate_db_precision = 6 if cint(item.precision("valuation_rate")) <= 6 else 9
|
||||
|
||||
# warehouse account
|
||||
warehouse_debit_amount = flt(flt(item.valuation_rate, val_rate_db_precision)
|
||||
* flt(item.qty) * flt(item.conversion_factor), item.precision("base_net_amount"))
|
||||
warehouse_debit_amount = self.make_stock_adjustment_entry(gl_entries,
|
||||
item, voucher_wise_stock_value, account_currency)
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
@ -552,6 +556,36 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
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):
|
||||
# tax table gl entries
|
||||
valuation_tax = {}
|
||||
|
@ -1742,7 +1742,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
@ -2544,7 +2544,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-09-04 10:11:28.246395",
|
||||
"modified": "2018-10-04 09:05:43.166721",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice Item",
|
||||
|
@ -35,6 +35,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
},
|
||||
|
||||
refresh: function(doc, dt, dn) {
|
||||
const me = this;
|
||||
this._super();
|
||||
if(cur_frm.msgbox && cur_frm.msgbox.$wrapper.is(":visible")) {
|
||||
// hide new msgbox
|
||||
@ -82,9 +83,10 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
}
|
||||
}
|
||||
|
||||
if(doc.outstanding_amount>0 && !cint(doc.is_return)) {
|
||||
cur_frm.add_custom_button(__('Payment Request'),
|
||||
this.make_payment_request, __("Make"));
|
||||
if (doc.outstanding_amount>0 && !cint(doc.is_return)) {
|
||||
cur_frm.add_custom_button(__('Payment Request'), function() {
|
||||
me.make_payment_request();
|
||||
}, __("Make"));
|
||||
}
|
||||
|
||||
if(!doc.auto_repeat) {
|
||||
@ -102,7 +104,6 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
}
|
||||
|
||||
this.set_default_print_format();
|
||||
var me = this;
|
||||
if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) {
|
||||
frappe.model.with_doc("Customer", me.frm.doc.customer, function() {
|
||||
var customer = frappe.model.get_doc("Customer", me.frm.doc.customer);
|
||||
|
@ -4,7 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe, erpnext
|
||||
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 erpnext.accounts.party import get_party_account, get_due_date
|
||||
from erpnext.controllers.stock_controller import update_gl_entries_after
|
||||
@ -54,9 +54,18 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def set_indicator(self):
|
||||
"""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_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:
|
||||
self.indicator_color = "green"
|
||||
self.indicator_title = _("Paid")
|
||||
|
@ -3,55 +3,82 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, erpnext
|
||||
from frappe.utils import flt
|
||||
from frappe import _
|
||||
|
||||
def execute(filters=None):
|
||||
columns, data = get_columns(), get_data(filters)
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_data(filters):
|
||||
data = frappe.db.sql("""
|
||||
select
|
||||
a.name as asset, a.asset_category, a.status,
|
||||
ds.depreciation_method, a.purchase_date, a.gross_purchase_amount,
|
||||
ds.schedule_date as depreciation_date, ds.depreciation_amount,
|
||||
ds.accumulated_depreciation_amount,
|
||||
(a.gross_purchase_amount - ds.accumulated_depreciation_amount) as amount_after_depreciation,
|
||||
ds.journal_entry as depreciation_entry
|
||||
from
|
||||
`tabAsset` a, `tabDepreciation Schedule` ds
|
||||
where
|
||||
a.name = ds.parent
|
||||
and a.docstatus=1
|
||||
and ifnull(ds.journal_entry, '') != ''
|
||||
and ds.schedule_date between %(from_date)s and %(to_date)s
|
||||
and a.company = %(company)s
|
||||
{conditions}
|
||||
order by
|
||||
a.name asc, ds.schedule_date asc
|
||||
""".format(conditions=get_filter_conditions(filters)), filters, as_dict=1)
|
||||
data = []
|
||||
depreciation_accounts = frappe.db.sql_list(""" select name from tabAccount
|
||||
where ifnull(account_type, '') = 'Depreciation' """)
|
||||
|
||||
filters_data = [["company", "=", filters.get('company')],
|
||||
["posting_date", ">=", filters.get('from_date')],
|
||||
["posting_date", "<=", filters.get('to_date')],
|
||||
["against_voucher_type", "=", "Asset"],
|
||||
["account", "in", depreciation_accounts]]
|
||||
|
||||
if filters.get("asset"):
|
||||
filters_data.append(["against_voucher", "=", filters.get("asset")])
|
||||
|
||||
if filters.get("asset_category"):
|
||||
|
||||
assets = frappe.db.sql_list("""select name from tabAsset
|
||||
where asset_category = %s and docstatus=1""", filters.get("asset_category"))
|
||||
|
||||
filters_data.append(["against_voucher", "in", assets])
|
||||
|
||||
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
|
||||
|
||||
def get_filter_conditions(filters):
|
||||
conditions = ""
|
||||
|
||||
if filters.get("asset"):
|
||||
conditions += " and a.name = %(asset)s"
|
||||
|
||||
if filters.get("asset_category"):
|
||||
conditions += " and a.asset_category = %(asset_category)s"
|
||||
|
||||
company_finance_book = erpnext.get_default_finance_book(filters.get("company"))
|
||||
def get_assets_details(assets):
|
||||
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"
|
||||
fields = ["name as asset", "gross_purchase_amount",
|
||||
"asset_category", "status", "depreciation_method", "purchase_date"]
|
||||
|
||||
for d in frappe.get_all("Asset", fields = fields, filters = {'name': ('in', assets)}):
|
||||
assets_details.setdefault(d.asset, d)
|
||||
|
||||
return assets_details
|
||||
|
||||
return conditions
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
{
|
||||
|
@ -4,8 +4,16 @@
|
||||
frappe.query_reports["Budget Variance Report"] = {
|
||||
"filters": [
|
||||
{
|
||||
fieldname: "fiscal_year",
|
||||
label: __("Fiscal Year"),
|
||||
fieldname: "from_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",
|
||||
options: "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": "Yearly", "label": __("Yearly") }
|
||||
],
|
||||
default: "Monthly",
|
||||
default: "Yearly",
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
@ -46,5 +54,11 @@ frappe.query_reports["Budget Variance Report"] = {
|
||||
fieldtype: "Link",
|
||||
options: "Cost Center"
|
||||
},
|
||||
{
|
||||
fieldname:"show_cumulative",
|
||||
label: __("Show Cumulative Amount"),
|
||||
fieldtype: "Check",
|
||||
default: 0,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ from frappe.utils import formatdate
|
||||
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
|
||||
|
||||
from six import iteritems
|
||||
|
||||
from pprint import pprint
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
validate_filters(filters)
|
||||
@ -19,7 +19,7 @@ def execute(filters=None):
|
||||
else:
|
||||
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)
|
||||
|
||||
data = []
|
||||
@ -29,18 +29,28 @@ def execute(filters=None):
|
||||
for account, monthwise_data in iteritems(cost_center_items):
|
||||
row = [cost_center, account]
|
||||
totals = [0, 0, 0]
|
||||
for relevant_months in period_month_ranges:
|
||||
period_data = [0, 0, 0]
|
||||
for month in relevant_months:
|
||||
month_data = monthwise_data.get(month, {})
|
||||
for i, fieldname in enumerate(["target", "actual", "variance"]):
|
||||
value = flt(month_data.get(fieldname))
|
||||
period_data[i] += value
|
||||
totals[i] += value
|
||||
period_data[2] = period_data[0] - period_data[1]
|
||||
row += period_data
|
||||
for year in get_fiscal_years(filters):
|
||||
last_total = 0
|
||||
for relevant_months in period_month_ranges:
|
||||
period_data = [0, 0, 0]
|
||||
for month in relevant_months:
|
||||
if monthwise_data.get(year[0]):
|
||||
month_data = monthwise_data.get(year[0]).get(month, {})
|
||||
for i, fieldname in enumerate(["target", "actual", "variance"]):
|
||||
value = flt(month_data.get(fieldname))
|
||||
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]
|
||||
row += totals
|
||||
if filters["period"] != "Yearly" :
|
||||
row += totals
|
||||
data.append(row)
|
||||
|
||||
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"))
|
||||
|
||||
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
|
||||
|
||||
for from_date, to_date in get_period_date_ranges(filters["period"], filters["fiscal_year"]):
|
||||
for label in [_("Target") + " (%s)", _("Actual") + " (%s)", _("Variance") + " (%s)"]:
|
||||
if group_months:
|
||||
label = label % (formatdate(from_date, format_string="MMM") + " - " + formatdate(to_date, format_string="MMM"))
|
||||
fiscal_year = get_fiscal_years(filters)
|
||||
|
||||
for year in fiscal_year:
|
||||
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:
|
||||
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",
|
||||
_("Total Variance") + ":Float:120"]
|
||||
if filters["period"] != "Yearly" :
|
||||
return columns + [_("Total Budget") + ":Float:80", _("Total Actual") + ":Float:80",
|
||||
_("Total Variance") + ":Float:80"]
|
||||
else:
|
||||
return columns
|
||||
|
||||
def get_cost_centers(filters):
|
||||
cond = "and 1=1"
|
||||
@ -81,21 +102,23 @@ def get_cost_center_target_details(filters):
|
||||
cond += " and b.cost_center='%s'" % frappe.db.escape(filters.get("cost_center"))
|
||||
|
||||
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
|
||||
where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year=%s
|
||||
and b.budget_against = %s and b.company=%s {cond}
|
||||
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} order by b.fiscal_year
|
||||
""".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
|
||||
def get_target_distribution_details(filters):
|
||||
target_details = {}
|
||||
for d in frappe.db.sql("""select md.name, mdp.month, mdp.percentage_allocation
|
||||
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))
|
||||
|
||||
|
||||
return target_details
|
||||
|
||||
#Get actual details from gl entry
|
||||
@ -107,7 +130,7 @@ def get_actual_details(name, filters):
|
||||
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)
|
||||
|
||||
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
|
||||
from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b
|
||||
where
|
||||
@ -115,11 +138,11 @@ def get_actual_details(name, filters):
|
||||
and b.docstatus = 1
|
||||
and ba.account=gl.account
|
||||
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 exists(select name from `tab{tab}` where name=gl.{budget_against} and {cond}) group by gl.name
|
||||
""".format(tab = filters.budget_against, budget_against = budget_against, cond = cond),
|
||||
(filters.fiscal_year, name), as_dict=1)
|
||||
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,from_year=filters.from_fiscal_year,to_year=filters.to_fiscal_year),
|
||||
(filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1)
|
||||
|
||||
cc_actual_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):
|
||||
month = datetime.date(2013, month_id, 1).strftime('%B')
|
||||
|
||||
cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {})\
|
||||
cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {}).setdefault(ccd.fiscal_year,{})\
|
||||
.setdefault(month, frappe._dict({
|
||||
"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) \
|
||||
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)
|
||||
|
||||
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
|
||||
|
@ -10,7 +10,7 @@ from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement
|
||||
|
||||
|
||||
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 []
|
||||
|
||||
|
||||
@ -345,13 +345,13 @@ def execute(filters=None):
|
||||
|
||||
# compute net profit / loss
|
||||
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,
|
||||
ignore_accumulated_values_for_fy=True
|
||||
)
|
||||
|
||||
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,
|
||||
ignore_accumulated_values_for_fy=True
|
||||
)
|
||||
|
@ -238,6 +238,9 @@ def accumulate_values_into_parents(accounts, accounts_by_name, companies):
|
||||
for d in reversed(accounts):
|
||||
if d.parent_account:
|
||||
account = d.parent_account.split('-')[0].strip()
|
||||
if not accounts_by_name.get(account):
|
||||
continue
|
||||
|
||||
for company in companies:
|
||||
accounts_by_name[account][company] = \
|
||||
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
|
||||
|
||||
def get_subsidiary_companies(company):
|
||||
lft, rgt = frappe.get_cached_value('Company',
|
||||
company, ["lft", "rgt"])
|
||||
lft, rgt = frappe.db.get_value('Company', company, ["lft", "rgt"])
|
||||
|
||||
return frappe.db.sql_list("""select name from `tabCompany`
|
||||
where lft >= {0} and rgt <= {1} order by lft, rgt""".format(lft, rgt))
|
||||
|
@ -28,6 +28,24 @@ frappe.query_reports["Item-wise Sales Register"] = {
|
||||
"label": __("Mode of Payment"),
|
||||
"fieldtype": "Link",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -14,10 +14,10 @@ def execute(filters=None):
|
||||
|
||||
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
|
||||
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)
|
||||
|
||||
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)
|
||||
if item_list:
|
||||
@ -108,6 +108,23 @@ def get_conditions(filters):
|
||||
conditions += """ and exists(select name from `tabSales Invoice Payment`
|
||||
where parent=`tabSales Invoice`.name
|
||||
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
|
||||
|
||||
|
@ -52,6 +52,18 @@ frappe.query_reports["Sales Register"] = {
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -153,6 +153,16 @@ def get_conditions(filters):
|
||||
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
|
||||
|
||||
def get_invoices(filters, additional_query_columns):
|
||||
|
@ -94,6 +94,12 @@ frappe.ui.form.on('Asset', {
|
||||
}, __("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.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) {
|
||||
var x_intervals = [frm.doc.purchase_date];
|
||||
var asset_values = [frm.doc.gross_purchase_amount];
|
||||
|
@ -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))
|
||||
|
||||
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
|
||||
|
@ -9,8 +9,9 @@ from frappe.utils import flt, today, getdate, cint
|
||||
|
||||
def post_depreciation_entries(date=None):
|
||||
# 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
|
||||
|
||||
if not date:
|
||||
date = today()
|
||||
for asset in get_depreciable_assets(date):
|
||||
@ -197,4 +198,4 @@ def get_disposal_account_and_cost_center(company):
|
||||
if not depreciation_cost_center:
|
||||
frappe.throw(_("Please set 'Asset Depreciation Cost Center' in Company {0}").format(company))
|
||||
|
||||
return disposal_account, depreciation_cost_center
|
||||
return disposal_account, depreciation_cost_center
|
||||
|
@ -49,6 +49,10 @@ frappe.ui.form.on("Supplier", {
|
||||
erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name);
|
||||
}, __("Make"));
|
||||
|
||||
frm.add_custom_button(__('Pricing Rule'), function () {
|
||||
erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name);
|
||||
}, __("Make"));
|
||||
|
||||
// indicators
|
||||
erpnext.utils.set_party_dashboard_indicators(frm);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -13,6 +13,10 @@ def get_data():
|
||||
{
|
||||
'label': _('Orders'),
|
||||
'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
|
||||
},
|
||||
{
|
||||
'label': _('Pricing'),
|
||||
'items': ['Pricing Rule']
|
||||
}
|
||||
]
|
||||
}
|
@ -235,6 +235,16 @@ def get_data():
|
||||
"type": "doctype",
|
||||
"name": "GST HSN Code",
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GSTR-1",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GSTR-2",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GST Sales Register",
|
||||
|
@ -661,7 +661,7 @@ class BuyingController(StockController):
|
||||
if self.doctype == 'Purchase Invoice' and not self.get('update_stock'):
|
||||
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)
|
||||
|
||||
def validate_schedule_date(self):
|
||||
|
@ -137,7 +137,7 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
|
||||
.format(args.item_code), StockOverReturnError)
|
||||
elif abs(current_stock_qty) > max_returnable_qty:
|
||||
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):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
File diff suppressed because it is too large
Load Diff
8
erpnext/crm/doctype/market_segment/market_segment.js
Normal file
8
erpnext/crm/doctype/market_segment/market_segment.js
Normal 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) {
|
||||
|
||||
}
|
||||
});
|
96
erpnext/crm/doctype/market_segment/market_segment.json
Normal file
96
erpnext/crm/doctype/market_segment/market_segment.json
Normal 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
|
||||
}
|
10
erpnext/crm/doctype/market_segment/market_segment.py
Normal file
10
erpnext/crm/doctype/market_segment/market_segment.py
Normal 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
|
23
erpnext/crm/doctype/market_segment/test_market_segment.js
Normal file
23
erpnext/crm/doctype/market_segment/test_market_segment.js
Normal 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()
|
||||
]);
|
||||
|
||||
});
|
10
erpnext/crm/doctype/market_segment/test_market_segment.py
Normal file
10
erpnext/crm/doctype/market_segment/test_market_segment.py
Normal 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
|
@ -107,6 +107,8 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
|
||||
if(!this.frm.doc.company && frappe.defaults.get_user_default("Company"))
|
||||
set_multiple(this.frm.doc.doctype, this.frm.doc.name,
|
||||
{ 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();
|
||||
},
|
||||
|
File diff suppressed because it is too large
Load Diff
0
erpnext/crm/doctype/sales_stage/__init__.py
Normal file
0
erpnext/crm/doctype/sales_stage/__init__.py
Normal file
8
erpnext/crm/doctype/sales_stage/sales_stage.js
Normal file
8
erpnext/crm/doctype/sales_stage/sales_stage.js
Normal 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) {
|
||||
|
||||
}
|
||||
});
|
96
erpnext/crm/doctype/sales_stage/sales_stage.json
Normal file
96
erpnext/crm/doctype/sales_stage/sales_stage.json
Normal 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
|
||||
}
|
10
erpnext/crm/doctype/sales_stage/sales_stage.py
Normal file
10
erpnext/crm/doctype/sales_stage/sales_stage.py
Normal 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
|
23
erpnext/crm/doctype/sales_stage/test_sales_stage.js
Normal file
23
erpnext/crm/doctype/sales_stage/test_sales_stage.js
Normal 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()
|
||||
]);
|
||||
|
||||
});
|
10
erpnext/crm/doctype/sales_stage/test_sales_stage.py
Normal file
10
erpnext/crm/doctype/sales_stage/test_sales_stage.py
Normal 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
|
0
erpnext/crm/report/lead_details/__init__.py
Normal file
0
erpnext/crm/report/lead_details/__init__.py
Normal file
@ -1,18 +1,18 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-10-22 11:58:16",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-02-24 20:20:28.725080",
|
||||
"modified": "2018-09-26 18:59:46.520731",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"module": "CRM",
|
||||
"name": "Lead Details",
|
||||
"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",
|
||||
"report_name": "Lead Details",
|
||||
"report_type": "Query Report",
|
@ -12,7 +12,7 @@ app_license = "GNU General Public License (v3)"
|
||||
source_link = "https://github.com/frappe/erpnext"
|
||||
|
||||
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"
|
||||
|
||||
@ -25,6 +25,7 @@ web_include_css = "assets/css/erpnext-web.css"
|
||||
|
||||
doctype_js = {
|
||||
"Communication": "public/js/communication.js",
|
||||
"Event": "public/js/event.js"
|
||||
}
|
||||
|
||||
welcome_email = "erpnext.setup.utils.welcome_email"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -651,6 +651,39 @@
|
||||
"translatable": 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_in_quick_entry": 0,
|
||||
@ -916,7 +949,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 3,
|
||||
"modified": "2018-08-21 14:44:42.766422",
|
||||
"modified": "2018-09-21 15:53:11.935416",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Leave Application",
|
||||
|
@ -770,7 +770,7 @@
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "",
|
||||
"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",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@ -835,7 +835,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"default": "",
|
||||
"fieldname": "amount_based_on_formula",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
@ -1003,7 +1003,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-07-02 16:55:44.467519",
|
||||
"modified": "2018-09-20 16:44:58.876044",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Salary Component",
|
||||
@ -1056,5 +1056,6 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -385,7 +385,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.parenttype=='Salary Structure'",
|
||||
"fetch_from": "",
|
||||
"fieldname": "amount_based_on_formula",
|
||||
@ -692,7 +692,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-07-04 16:28:32.314907",
|
||||
"modified": "2018-09-20 16:59:33.622652",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Salary Detail",
|
||||
@ -706,5 +706,6 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -122,16 +122,14 @@ frappe.ui.form.on('Salary Slip Timesheet', {
|
||||
// Get leave details
|
||||
//---------------------------------------------------------------------
|
||||
var get_emp_and_leave_details = function(doc, dt, dn) {
|
||||
if(!doc.start_date){
|
||||
return frappe.call({
|
||||
method: 'get_emp_and_leave_details',
|
||||
doc: locals[dt][dn],
|
||||
callback: function(r, rt) {
|
||||
cur_frm.refresh();
|
||||
calculate_all(doc, dt, dn);
|
||||
}
|
||||
});
|
||||
}
|
||||
return frappe.call({
|
||||
method: 'get_emp_and_leave_details',
|
||||
doc: locals[dt][dn],
|
||||
callback: function(r, rt) {
|
||||
cur_frm.refresh();
|
||||
calculate_all(doc, dt, dn);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cur_frm.cscript.employee = function(doc,dt,dn){
|
||||
|
@ -384,8 +384,8 @@ class SalarySlip(TransactionBase):
|
||||
and t2.is_lwp = 1
|
||||
and t1.docstatus = 1
|
||||
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
|
||||
WHEN t2.include_holiday THEN %(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 and ifnull(t1.salary_slip, '') = ''
|
||||
END
|
||||
""".format(holidays), {"employee": self.employee, "dt": dt})
|
||||
if leave:
|
||||
|
@ -323,11 +323,12 @@ def make_salary_component(salary_components, test_tax):
|
||||
def get_salary_component_account(sal_comp):
|
||||
company = erpnext.get_default_company()
|
||||
sal_comp = frappe.get_doc("Salary Component", sal_comp)
|
||||
sal_comp.append("accounts", {
|
||||
"company": company,
|
||||
"default_account": create_account(company)
|
||||
})
|
||||
sal_comp.save()
|
||||
if not sal_comp.get("accounts"):
|
||||
sal_comp.append("accounts", {
|
||||
"company": company,
|
||||
"default_account": create_account(company)
|
||||
})
|
||||
sal_comp.save()
|
||||
|
||||
def create_account(company):
|
||||
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',
|
||||
"condition": 'base > 10000',
|
||||
"formula": 'base*.5',
|
||||
"type": "Earning"
|
||||
"type": "Earning",
|
||||
"amount_based_on_formula": 1
|
||||
},
|
||||
{
|
||||
"salary_component": 'HRA',
|
||||
@ -360,7 +362,8 @@ def make_earning_salary_component(setup=False, test_tax=False):
|
||||
"abbr":'SA',
|
||||
"condition": 'H < 10000',
|
||||
"formula": 'BS*.5',
|
||||
"type": "Earning"
|
||||
"type": "Earning",
|
||||
"amount_based_on_formula": 1
|
||||
},
|
||||
{
|
||||
"salary_component": "Leave Encashment",
|
||||
@ -401,7 +404,8 @@ def make_earning_salary_component(setup=False, test_tax=False):
|
||||
"abbr":'BS',
|
||||
"condition": 'base < 10000',
|
||||
"formula": 'base*.2',
|
||||
"type": "Earning"
|
||||
"type": "Earning",
|
||||
"amount_based_on_formula": 1
|
||||
})
|
||||
return data
|
||||
|
||||
@ -412,13 +416,15 @@ def make_deduction_salary_component(setup=False, test_tax=False):
|
||||
"abbr":'PT',
|
||||
"condition": 'base > 10000',
|
||||
"formula": 'base*.1',
|
||||
"type": "Deduction"
|
||||
"type": "Deduction",
|
||||
"amount_based_on_formula": 1
|
||||
},
|
||||
{
|
||||
"salary_component": 'TDS',
|
||||
"abbr":'T',
|
||||
"formula": 'base*.1',
|
||||
"type": "Deduction"
|
||||
"type": "Deduction",
|
||||
"amount_based_on_formula": 1
|
||||
}
|
||||
]
|
||||
if not test_tax:
|
||||
@ -427,7 +433,8 @@ def make_deduction_salary_component(setup=False, test_tax=False):
|
||||
"abbr":'T',
|
||||
"condition": 'employment_type=="Intern"',
|
||||
"formula": 'base*.1',
|
||||
"type": "Deduction"
|
||||
"type": "Deduction",
|
||||
"amount_based_on_formula": 1
|
||||
})
|
||||
if setup or test_tax:
|
||||
make_salary_component(data, test_tax)
|
||||
|
@ -18,14 +18,21 @@ class SalaryStructure(Document):
|
||||
self.validate_max_benefits_with_flexi()
|
||||
|
||||
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 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:
|
||||
for fieldname, value in iteritems(component_default_value):
|
||||
for fieldname in overwritten_fields:
|
||||
value = component_default_value.get(fieldname)
|
||||
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):
|
||||
if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet:
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
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.utils.csvutils import UnicodeWriter
|
||||
from frappe.model.document import Document
|
||||
@ -48,8 +48,9 @@ def add_data(w, args):
|
||||
for employee in employees:
|
||||
existing_attendance = {}
|
||||
if existing_attendance_records \
|
||||
and tuple([date, employee.name]) in existing_attendance_records:
|
||||
existing_attendance = existing_attendance_records[tuple([date, employee.name])]
|
||||
and tuple([getdate(date), employee.name]) in existing_attendance_records:
|
||||
existing_attendance = existing_attendance_records[tuple([getdate(date), employee.name])]
|
||||
|
||||
row = [
|
||||
existing_attendance and existing_attendance.name or "",
|
||||
employee.name, employee.employee_name, date,
|
||||
@ -114,6 +115,7 @@ def upload():
|
||||
if not row: continue
|
||||
row_idx = i + 5
|
||||
d = frappe._dict(zip(columns, row))
|
||||
|
||||
d["doctype"] = "Attendance"
|
||||
if d.name:
|
||||
d["docstatus"] = frappe.db.get_value("Attendance", d.name, "docstatus")
|
||||
@ -121,6 +123,8 @@ def upload():
|
||||
try:
|
||||
check_record(d)
|
||||
ret.append(import_doc(d, "Attendance", 1, row_idx, submit=True))
|
||||
except AttributeError:
|
||||
pass
|
||||
except Exception as e:
|
||||
error = True
|
||||
ret.append('Error for row (#%d) %s : %s' % (row_idx,
|
||||
|
@ -506,7 +506,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-07-12 16:16:54.237829",
|
||||
"modified": "2018-10-04 16:16:54.237829",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Work Order Item",
|
||||
|
@ -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.v11_0.rename_healthcare_fields
|
||||
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
|
||||
|
@ -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()
|
11
erpnext/patches/v11_0/add_market_segments.py
Normal file
11
erpnext/patches/v11_0/add_market_segments.py
Normal 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()
|
10
erpnext/patches/v11_0/add_sales_stages.py
Normal file
10
erpnext/patches/v11_0/add_sales_stages.py
Normal 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()
|
@ -1,80 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xml:space="preserve"
|
||||
enable-background="new 0 0 512 512"
|
||||
viewBox="0 0 512 512"
|
||||
height="512px"
|
||||
width="512px"
|
||||
y="0px"
|
||||
x="0px"
|
||||
id="Layer_1"
|
||||
version="1.1"><metadata
|
||||
id="metadata45"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title><cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" /></cc:Work><cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/"><cc:permits
|
||||
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>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
<!-- Generator: Sketch 44.1 (41455) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>erpnext-logo</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="erpnext-logo" transform="translate(-2.000000, -2.000000)" fill-rule="nonzero">
|
||||
<g id="g1422-7-2" transform="translate(0.025630, 0.428785)" fill="#5E64FF">
|
||||
<g id="g1418-4-6" transform="translate(0.268998, 0.867736)">
|
||||
<g id="g1416-4-9" transform="translate(0.749997, 0.000000)">
|
||||
<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>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="g1444-6-7" transform="translate(27.708247, 23.320960)" fill="#FFFFFF">
|
||||
<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>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 2.6 KiB |
@ -211,8 +211,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
},
|
||||
|
||||
make_payment_request: function() {
|
||||
const me = this;
|
||||
const payment_request_type = (in_list(['Sales Order', 'Sales Invoice'], me.frm.doc.doctype))
|
||||
var me = this;
|
||||
const payment_request_type = (in_list(['Sales Order', 'Sales Invoice'], this.frm.doc.doctype))
|
||||
? "Inward" : "Outward";
|
||||
|
||||
frappe.call({
|
||||
|
35
erpnext/public/js/event.js
Normal file
35
erpnext/public/js/event.js
Normal 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"));
|
||||
}
|
||||
});
|
@ -18,18 +18,19 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
make_input() {
|
||||
this.message_input = new frappe.ui.CommentArea({
|
||||
this.message_input = frappe.ui.form.make_control({
|
||||
parent: this.$refs['comment-input'],
|
||||
on_submit: (message) => {
|
||||
this.message_input.reset();
|
||||
this.$emit('change', message);
|
||||
},
|
||||
only_input: true,
|
||||
no_wrapper: true
|
||||
});
|
||||
},
|
||||
submit_input() {
|
||||
if (!this.message_input) return;
|
||||
const value = this.message_input.val();
|
||||
const value = this.message_input.get_value();
|
||||
if (!value) return;
|
||||
this.message_input.submit();
|
||||
}
|
||||
|
@ -1,6 +1,44 @@
|
||||
<template>
|
||||
<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">
|
||||
<review-timeline-item v-for="review in reviews"
|
||||
:key="review.user"
|
||||
@ -22,6 +60,11 @@ export default {
|
||||
props: ['hub_item_name'],
|
||||
data() {
|
||||
return {
|
||||
user_review: {
|
||||
rating: 0,
|
||||
subject: '',
|
||||
content: ''
|
||||
},
|
||||
reviews: []
|
||||
}
|
||||
},
|
||||
@ -35,6 +78,10 @@ export default {
|
||||
this.make_input();
|
||||
},
|
||||
methods: {
|
||||
set_rating(i) {
|
||||
this.user_review.rating = i;
|
||||
},
|
||||
|
||||
when(datetime) {
|
||||
return comment_when(datetime);
|
||||
},
|
||||
@ -48,21 +95,37 @@ export default {
|
||||
},
|
||||
|
||||
make_input() {
|
||||
this.review_area = new frappe.ui.ReviewArea({
|
||||
parent: this.$refs['review-area'],
|
||||
mentions: [],
|
||||
on_submit: this.on_submit_review.bind(this)
|
||||
this.review_content = frappe.ui.form.make_control({
|
||||
parent: this.$refs['review-content'],
|
||||
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) {
|
||||
this.review_area.reset();
|
||||
on_submit_review() {
|
||||
const review = Object.assign({}, this.user_review, {
|
||||
content: this.review_content.get_value()
|
||||
});
|
||||
|
||||
hub.call('add_item_review', {
|
||||
hub_item_name: this.hub_item_name,
|
||||
review: JSON.stringify(values)
|
||||
review: JSON.stringify(review)
|
||||
})
|
||||
.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){
|
||||
@ -70,4 +133,4 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
@ -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
|
||||
* @param child_table - Child table Doctype
|
||||
|
@ -106,6 +106,10 @@ frappe.ui.form.on("Customer", {
|
||||
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
|
||||
erpnext.utils.set_party_dashboard_indicators(frm);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,10 @@ def get_data():
|
||||
{
|
||||
'label': _('Projects'),
|
||||
'items': ['Project']
|
||||
},
|
||||
{
|
||||
'label': _('Pricing'),
|
||||
'items': ['Pricing Rule']
|
||||
}
|
||||
]
|
||||
}
|
@ -13,23 +13,34 @@ frappe.pages['sales-funnel'].on_page_load = function(wrapper) {
|
||||
frappe.breadcrumbs.add("Selling");
|
||||
}
|
||||
|
||||
erpnext.SalesFunnel = Class.extend({
|
||||
init: function(wrapper) {
|
||||
erpnext.SalesFunnel = class SalesFunnel {
|
||||
constructor(wrapper) {
|
||||
var me = this;
|
||||
// 0 setTimeout hack - this gives time for canvas to get width and height
|
||||
setTimeout(function() {
|
||||
me.setup(wrapper);
|
||||
me.get_data();
|
||||
}, 0);
|
||||
},
|
||||
}
|
||||
|
||||
setup: function(wrapper) {
|
||||
setup(wrapper) {
|
||||
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 = {
|
||||
layout: $(wrapper).find(".layout-main"),
|
||||
from_date: wrapper.page.add_date(__("From 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"),
|
||||
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>')
|
||||
.appendTo(this.elements.layout);
|
||||
|
||||
this.company = frappe.defaults.get_user_default('company');
|
||||
this.options = {
|
||||
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
|
||||
$.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.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();
|
||||
});
|
||||
});
|
||||
@ -64,29 +86,90 @@ erpnext.SalesFunnel = Class.extend({
|
||||
$(window).resize(function() {
|
||||
me.render();
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
get_data: function(btn) {
|
||||
get_data(btn) {
|
||||
var me = this;
|
||||
frappe.call({
|
||||
method: "erpnext.selling.page.sales_funnel.sales_funnel.get_funnel_data",
|
||||
args: {
|
||||
from_date: this.options.from_date,
|
||||
to_date: this.options.to_date
|
||||
},
|
||||
btn: btn,
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
me.options.data = r.message;
|
||||
me.render();
|
||||
if (me.options.chart == 'sales_funnel'){
|
||||
frappe.call({
|
||||
method: "erpnext.selling.page.sales_funnel.sales_funnel.get_funnel_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_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;
|
||||
this.prepare();
|
||||
this.prepare_funnel();
|
||||
|
||||
var context = this.elements.context,
|
||||
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);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
prepare: function() {
|
||||
prepare_funnel() {
|
||||
var me = this;
|
||||
|
||||
this.elements.no_data.toggle(false);
|
||||
@ -147,9 +230,9 @@ erpnext.SalesFunnel = Class.extend({
|
||||
.attr("height", this.options.height);
|
||||
|
||||
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;
|
||||
context.beginPath();
|
||||
context.moveTo(x_start, y);
|
||||
@ -158,9 +241,9 @@ erpnext.SalesFunnel = Class.extend({
|
||||
context.lineTo(x_start, y);
|
||||
context.closePath();
|
||||
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;
|
||||
|
||||
if(y_mid == 0) {
|
||||
@ -186,4 +269,44 @@ erpnext.SalesFunnel = Class.extend({
|
||||
context.font = "1.1em sans-serif";
|
||||
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']
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -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
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe import _
|
||||
from erpnext.accounts.report.utils import convert
|
||||
import pandas as pd
|
||||
|
||||
@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`
|
||||
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
|
||||
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`
|
||||
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`
|
||||
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`
|
||||
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 [
|
||||
{ "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": _("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'
|
@ -29,9 +29,9 @@ def execute(filters=None):
|
||||
|
||||
if customer_naming_type == "Naming Series":
|
||||
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:
|
||||
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:
|
||||
data.append(row)
|
||||
@ -40,11 +40,13 @@ def execute(filters=None):
|
||||
|
||||
def get_columns(customer_naming_type):
|
||||
columns = [
|
||||
_("Name") + ":Link/Customer:120",
|
||||
_("Customer") + ":Link/Customer:120",
|
||||
_("Credit Limit") + ":Currency:120",
|
||||
_("Outstanding Amt") + ":Currency:100",
|
||||
_("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":
|
||||
@ -59,5 +61,5 @@ def get_details(filters):
|
||||
conditions += " where name = %(customer)s"
|
||||
|
||||
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)
|
@ -17,7 +17,7 @@ def execute(filters=None):
|
||||
for d in entries:
|
||||
if d.stock_qty > 0 or filters.get('show_return_entries', 0):
|
||||
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"),
|
||||
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)
|
||||
|
||||
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",
|
||||
_("Brand") + ":Link/Brand:120", _("Qty") + ":Float:100", _("Amount") + ":Currency:120",
|
||||
_("Sales Person") + ":Link/Sales Person:140", _("Contribution %") + "::110",
|
||||
@ -115,7 +116,7 @@ def get_items(filters):
|
||||
|
||||
def get_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)
|
||||
|
||||
return item_details
|
||||
|
@ -264,7 +264,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
||||
{
|
||||
row.incentives = flt(
|
||||
row.allocated_amount * row.commission_rate / 100.0,
|
||||
precision("incentives", sales_person));
|
||||
precision("incentives", row));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -166,13 +166,12 @@ class NamingSeries(Document):
|
||||
|
||||
def parse_naming_series(self):
|
||||
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
|
||||
|
||||
def set_by_naming_series(doctype, fieldname, naming_series, hide_name_field=True):
|
||||
|
@ -55,6 +55,10 @@ def set_default_settings(args):
|
||||
buying_settings.allow_multiple_items = 1
|
||||
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.emp_created_by = "Naming Series"
|
||||
hr_settings.leave_approval_notification_template = _("Leave Approval Notification")
|
||||
|
@ -231,7 +231,7 @@ def install(country=None):
|
||||
|
||||
# Share Management
|
||||
{"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
|
||||
@ -250,6 +250,12 @@ def install(country=None):
|
||||
records += [{'doctype': 'Email Template', 'name': _("Leave Status Notification"), 'response': response,\
|
||||
'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
|
||||
from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import make_default_records
|
||||
make_default_records()
|
||||
@ -292,6 +298,30 @@ def add_uom_data():
|
||||
"value": d.get("value")
|
||||
}).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):
|
||||
from frappe.modules import scrub
|
||||
for r in records:
|
||||
@ -332,4 +362,4 @@ def install_post_company_fixtures(company=None):
|
||||
{'doctype': 'Department', 'department_name': _('Legal'), 'parent_department': _('All Departments'), 'company': company},
|
||||
]
|
||||
|
||||
make_fixture_records(records)
|
||||
make_fixture_records(records)
|
@ -104,6 +104,8 @@ def setup_complete(args=None):
|
||||
|
||||
def stage_fixtures(args):
|
||||
install_fixtures.install(args.get("country"))
|
||||
install_fixtures.add_market_segments()
|
||||
install_fixtures.add_sale_stages()
|
||||
|
||||
def setup_company(args):
|
||||
defaults_setup.create_price_lists(args)
|
||||
|
@ -460,6 +460,7 @@ def make_delivery_trip(source_name, target_doc=None):
|
||||
target_doc.customer_address = source_parent.shipping_address
|
||||
target_doc.contact = source_parent.contact_person
|
||||
target_doc.customer_contact = source_parent.contact_display
|
||||
target_doc.grand_total = source_parent.grand_total
|
||||
|
||||
# Append unique Delivery Notes in Delivery Trip
|
||||
delivery_notes.append(target_doc.delivery_note)
|
||||
|
0
erpnext/stock/doctype/delivery_settings/__init__.py
Normal file
0
erpnext/stock/doctype/delivery_settings/__init__.py
Normal 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) {
|
||||
|
||||
}
|
||||
});
|
194
erpnext/stock/doctype/delivery_settings/delivery_settings.json
Normal file
194
erpnext/stock/doctype/delivery_settings/delivery_settings.json
Normal 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
|
||||
}
|
10
erpnext/stock/doctype/delivery_settings/delivery_settings.py
Normal file
10
erpnext/stock/doctype/delivery_settings/delivery_settings.py
Normal 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
|
@ -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()
|
||||
]);
|
||||
|
||||
});
|
@ -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
|
@ -18,7 +18,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -78,6 +78,38 @@
|
||||
"translatable": 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_in_quick_entry": 0,
|
||||
@ -148,7 +180,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_7",
|
||||
"fieldname": "order_information_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@ -157,6 +189,135 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 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,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -186,7 +347,7 @@
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Contact Name",
|
||||
"length": 0,
|
||||
@ -205,6 +366,38 @@
|
||||
"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
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -284,6 +477,7 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Dispatch Information",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -362,196 +556,6 @@
|
||||
"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_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,
|
||||
@ -564,7 +568,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-21 22:25:53.276548",
|
||||
"modified": "2018-09-05 00:51:55.275009",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Stop",
|
||||
|
@ -2,8 +2,8 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Delivery Trip', {
|
||||
setup: function(frm) {
|
||||
frm.set_query("driver", function() {
|
||||
setup: function (frm) {
|
||||
frm.set_query("driver", function () {
|
||||
return {
|
||||
filters: {
|
||||
"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];
|
||||
if (row.customer) {
|
||||
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];
|
||||
if (row.customer) {
|
||||
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'), () => {
|
||||
erpnext.utils.map_current_doc({
|
||||
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) {
|
||||
var owner_email = frm.doc.owner == "Administrator"
|
||||
? frappe.user_info("Administrator").email
|
||||
: frm.doc.owner;
|
||||
|
||||
$.each(frm.doc.delivery_stops || [], function (i, delivery_stop) {
|
||||
if (!delivery_stop.delivery_notes) {
|
||||
if (!delivery_stop.delivery_note) {
|
||||
frappe.msgprint({
|
||||
"message": __("No Delivery Note selected for Customer {}", [delivery_stop.customer]),
|
||||
"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({
|
||||
method: "erpnext.stock.doctype.delivery_trip.delivery_trip.notify_customers",
|
||||
args: {
|
||||
"docname": frm.doc.name,
|
||||
"date": frm.doc.date,
|
||||
"driver": frm.doc.driver,
|
||||
"vehicle": frm.doc.vehicle,
|
||||
"sender_email": owner_email,
|
||||
"sender_name": frappe.user.full_name(owner_email),
|
||||
"delivery_notification": frm.doc.delivery_notification
|
||||
}
|
||||
});
|
||||
frm.doc.email_notification_sent = true;
|
||||
frm.refresh_field('email_notification_sent');
|
||||
|
||||
frappe.db.get_value("Delivery Settings", { name: "Delivery Settings" }, "dispatch_template", (r) => {
|
||||
if (!r.dispatch_template) {
|
||||
frappe.throw(__("Missing email template for dispatch. Please set one in Delivery Settings."));
|
||||
} else {
|
||||
frappe.confirm(__("Do you want to notify all the customers by email?"), function () {
|
||||
frappe.call({
|
||||
method: "erpnext.stock.doctype.delivery_trip.delivery_trip.notify_customers",
|
||||
args: {
|
||||
"delivery_trip": frm.doc.name
|
||||
},
|
||||
callback: function (r) {
|
||||
if (!r.exc) {
|
||||
frm.doc.email_notification_sent = true;
|
||||
frm.refresh_field('email_notification_sent');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
frappe.ui.form.on('Delivery Stop', {
|
||||
customer: function (frm, cdt, cdn) {
|
||||
var row = locals[cdt][cdn];
|
||||
if(row.customer) {
|
||||
if (row.customer) {
|
||||
frappe.call({
|
||||
method: "erpnext.stock.doctype.delivery_trip.delivery_trip.get_contact_and_address",
|
||||
args: {"name": row.customer},
|
||||
args: { "name": row.customer },
|
||||
callback: function (r) {
|
||||
if (r.message) {
|
||||
if (r.message["shipping_address"]) {
|
||||
@ -158,12 +157,13 @@ frappe.ui.form.on('Delivery Stop', {
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
address: function (frm, cdt, cdn) {
|
||||
var row = locals[cdt][cdn];
|
||||
if(row.address) {
|
||||
if (row.address) {
|
||||
frappe.call({
|
||||
method: "frappe.contacts.doctype.address.address.get_address_display",
|
||||
args: {"address_dict": row.address},
|
||||
args: { "address_dict": row.address },
|
||||
callback: function (r) {
|
||||
if (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) {
|
||||
var row = locals[cdt][cdn];
|
||||
if(row.contact) {
|
||||
if (row.contact) {
|
||||
frappe.call({
|
||||
method: "erpnext.stock.doctype.delivery_trip.delivery_trip.get_contact_display",
|
||||
args: {"contact": row.contact},
|
||||
args: { "contact": row.contact },
|
||||
callback: function (r) {
|
||||
if (r.message) {
|
||||
frappe.model.set_value(cdt, cdn, "customer_contact", r.message);
|
||||
|
@ -114,7 +114,7 @@
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
@ -437,7 +437,7 @@
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 1,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
@ -507,7 +507,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_13",
|
||||
"fieldname": "section_break_15",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@ -531,40 +531,6 @@
|
||||
"translatable": 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_in_quick_entry": 0,
|
||||
@ -608,7 +574,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-30 02:31:49.400138",
|
||||
"modified": "2018-09-05 01:20:34.165834",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Trip",
|
||||
|
@ -10,8 +10,7 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.contacts.doctype.address.address import get_address_display
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cstr, get_datetime, getdate, get_link_to_form
|
||||
from frappe.utils.user import get_user_fullname
|
||||
from frappe.utils import get_datetime, get_link_to_form, cstr
|
||||
|
||||
|
||||
class DeliveryTrip(Document):
|
||||
@ -169,59 +168,54 @@ def get_arrival_times(name):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def notify_customers(docname, date, driver, vehicle, sender_email, delivery_notification):
|
||||
sender_name = get_user_fullname(sender_email)
|
||||
attachments = []
|
||||
def notify_customers(delivery_trip):
|
||||
delivery_trip = frappe.get_doc("Delivery Trip", delivery_trip)
|
||||
|
||||
parent_doc = frappe.get_doc('Delivery Trip', docname)
|
||||
args = parent_doc.as_dict()
|
||||
context = delivery_trip.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:
|
||||
contact_info = frappe.db.get_value("Contact", delivery_stop.contact,
|
||||
["first_name", "last_name", "email_id", "gender"], as_dict=1)
|
||||
if delivery_trip.driver:
|
||||
context.update(frappe.db.get_value("Driver", delivery_trip.driver, "cell_number", as_dict=1))
|
||||
|
||||
args.update(delivery_stop.as_dict())
|
||||
args.update(contact_info)
|
||||
email_recipients = []
|
||||
|
||||
if delivery_stop.delivery_note:
|
||||
default_print_format = frappe.get_meta('Delivery Note').default_print_format
|
||||
attachments = frappe.attach_print('Delivery Note',
|
||||
delivery_stop.delivery_note,
|
||||
file_name="Delivery Note",
|
||||
print_format=default_print_format or "Standard")
|
||||
for stop in delivery_trip.delivery_stops:
|
||||
contact_info = frappe.db.get_value("Contact", stop.contact,
|
||||
["first_name", "last_name", "email_id", "gender"], as_dict=1)
|
||||
|
||||
if not delivery_stop.notified_by_email and contact_info.email_id:
|
||||
driver_info = frappe.db.get_value("Driver", driver, ["full_name", "cell_number"], as_dict=1)
|
||||
sender_designation = frappe.db.get_value("Employee", sender_email, ["designation"])
|
||||
if contact_info and contact_info.email_id:
|
||||
context.update(stop.as_dict())
|
||||
context.update(contact_info)
|
||||
|
||||
estimated_arrival = cstr(delivery_stop.estimated_arrival)[:-3]
|
||||
email_template = frappe.get_doc("Email Template", delivery_notification)
|
||||
message = frappe.render_template(email_template.response, args)
|
||||
dispatch_template_name = frappe.db.get_single_value("Delivery Settings", "dispatch_template")
|
||||
dispatch_template = frappe.get_doc("Email Template", dispatch_template_name)
|
||||
|
||||
frappe.sendmail(
|
||||
recipients=contact_info.email_id,
|
||||
sender=sender_email,
|
||||
message=message,
|
||||
attachments=attachments,
|
||||
subject=_(email_template.subject).format(getdate(date).strftime('%d.%m.%y'),
|
||||
estimated_arrival))
|
||||
frappe.sendmail(recipients=contact_info.email_id,
|
||||
subject=dispatch_template.subject,
|
||||
message=frappe.render_template(dispatch_template.response, context),
|
||||
attachments=get_attachments(stop))
|
||||
|
||||
frappe.db.set_value("Delivery Stop", delivery_stop.name, "notified_by_email", 1)
|
||||
frappe.db.set_value("Delivery Stop", delivery_stop.name,
|
||||
"email_sent_to", contact_info.email_id)
|
||||
frappe.msgprint(_("Email sent to {0}").format(contact_info.email_id))
|
||||
stop.db_set("email_sent_to", contact_info.email_id)
|
||||
email_recipients.append(contact_info.email_id)
|
||||
|
||||
def round_timedelta(td, period):
|
||||
"""Round timedelta"""
|
||||
period_seconds = period.total_seconds()
|
||||
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))
|
||||
if email_recipients:
|
||||
frappe.msgprint(_("Email sent to {0}").format(", ".join(email_recipients)))
|
||||
delivery_trip.db_set("email_notification_sent", True)
|
||||
else:
|
||||
return datetime.timedelta(seconds=td.total_seconds() - remainder)
|
||||
frappe.msgprint(_("No contacts with email IDs found."))
|
||||
|
||||
def format_address(address):
|
||||
"""Customer Address format """
|
||||
address = frappe.get_doc('Address', address)
|
||||
return '{}, {}, {}, {}'.format(address.address_line1, address.city, address.pincode, address.country)
|
||||
|
||||
def get_attachments(delivery_stop):
|
||||
if not (frappe.db.get_single_value("Delivery Settings", "send_with_attachment") and delivery_stop.delivery_note):
|
||||
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]
|
||||
|
@ -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>
|
@ -3,60 +3,71 @@
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import erpnext
|
||||
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.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):
|
||||
def setUp(self):
|
||||
create_driver()
|
||||
create_vehicle()
|
||||
create_delivery_notfication()
|
||||
create_delivery_notification()
|
||||
create_test_contact_and_address()
|
||||
|
||||
def test_delivery_trip(self):
|
||||
contact = get_contact_and_address("_Test Customer")
|
||||
|
||||
if not frappe.db.exists("Delivery Trip", "TOUR-00000"):
|
||||
delivery_trip = frappe.new_doc("Delivery Trip")
|
||||
delivery_trip.company = erpnext.get_default_company()
|
||||
delivery_trip.date = add_days(nowdate(), 5)
|
||||
delivery_trip.driver = "DRIVER-00001"
|
||||
delivery_trip.vehicle = "JB 007"
|
||||
delivery_trip.append("delivery_stops", {
|
||||
"customer": "_Test Customer",
|
||||
"address": contact.shipping_address.parent,
|
||||
"contact": contact.contact_person.parent
|
||||
delivery_trip = frappe.get_doc({
|
||||
"doctype": "Delivery Trip",
|
||||
"company": erpnext.get_default_company(),
|
||||
"date": add_days(nowdate(), 5),
|
||||
"departure_time": add_days(now_datetime(), 5),
|
||||
"driver": frappe.db.get_value('Driver', {"full_name": "Newton Scmander"}),
|
||||
"vehicle": "JB 007",
|
||||
"delivery_stops": [{
|
||||
"customer": "_Test Customer",
|
||||
"address": contact.shipping_address.parent,
|
||||
"contact": contact.contact_person.parent
|
||||
}]
|
||||
})
|
||||
delivery_trip.delivery_notification = 'Delivery Notification'
|
||||
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():
|
||||
if not frappe.db.exists("Driver", "Newton Scmander"):
|
||||
driver = frappe.new_doc("Driver")
|
||||
driver.full_name = "Newton Scmander"
|
||||
driver.cell_number = "98343424242"
|
||||
driver.license_number = "B809"
|
||||
driver = frappe.get_doc({
|
||||
"doctype": "Driver",
|
||||
"full_name": "Newton Scmander",
|
||||
"cell_number": "98343424242",
|
||||
"license_number": "B809"
|
||||
})
|
||||
driver.insert()
|
||||
|
||||
def create_delivery_notfication():
|
||||
|
||||
def create_delivery_notification():
|
||||
if not frappe.db.exists("Email Template", "Delivery Notification"):
|
||||
frappe.get_doc({
|
||||
dispatch_template = frappe.get_doc({
|
||||
'doctype': 'Email Template',
|
||||
'name': 'Delivery Notification',
|
||||
'response': 'Test Delivery Trip',
|
||||
'subject': 'Test Subject',
|
||||
'owner': frappe.session.user
|
||||
}).insert()
|
||||
})
|
||||
dispatch_template.insert()
|
||||
|
||||
delivery_settings = frappe.get_single("Delivery Settings")
|
||||
delivery_settings.dispatch_template = 'Delivery Notification'
|
||||
|
||||
|
||||
def create_vehicle():
|
||||
if not frappe.db.exists("Vehicle", "JB 007"):
|
||||
|
@ -7,7 +7,8 @@ def get_data():
|
||||
'Purchase Invoice': 'purchase_receipt',
|
||||
'Asset': 'purchase_receipt',
|
||||
'Landed Cost Voucher': 'receipt_document',
|
||||
'Auto Repeat': 'reference_document'
|
||||
'Auto Repeat': 'reference_document',
|
||||
'Purchase Receipt': 'return_against'
|
||||
},
|
||||
'internal_links': {
|
||||
'Purchase Order': ['items', 'purchase_order'],
|
||||
@ -25,11 +26,11 @@ def get_data():
|
||||
},
|
||||
{
|
||||
'label': _('Returns'),
|
||||
'items': ['Stock Entry']
|
||||
'items': ['Purchase Receipt']
|
||||
},
|
||||
{
|
||||
'label': _('Subscription'),
|
||||
'items': ['Auto Repeat']
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -379,7 +379,7 @@ class StockEntry(StockController):
|
||||
|
||||
def calculate_rate_and_amount(self, force=False,
|
||||
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.update_valuation_rate()
|
||||
self.set_total_incoming_outgoing_value()
|
||||
|
@ -1,17 +1,17 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"add_total_row": 1,
|
||||
"creation": "2013-06-04 11:03:47",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-02-24 20:19:13.150769",
|
||||
"modified": "2018-09-20 18:20:49.229153",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Batch-Wise Balance History",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Stock Ledger Entry",
|
||||
"report_name": "Batch-Wise Balance History",
|
||||
"report_type": "Script Report",
|
||||
|
@ -3,6 +3,15 @@
|
||||
|
||||
frappe.query_reports["Item Prices"] = {
|
||||
"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();
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -10,7 +10,8 @@ def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
|
||||
columns = get_columns(filters)
|
||||
item_map = get_item_details()
|
||||
conditions = get_condition(filters)
|
||||
item_map = get_item_details(conditions)
|
||||
pl = get_price_list()
|
||||
last_purchase_rate = get_last_purchase_rate()
|
||||
bom_rate = get_item_bom_rate()
|
||||
@ -41,14 +42,14 @@ def get_columns(filters):
|
||||
|
||||
return columns
|
||||
|
||||
def get_item_details():
|
||||
def get_item_details(conditions):
|
||||
"""returns all items details"""
|
||||
|
||||
item_map = {}
|
||||
|
||||
for i in frappe.db.sql("""select name, item_group, item_name, description,
|
||||
brand, stock_uom from tabItem
|
||||
order by item_code, item_group""", as_dict=1):
|
||||
brand, stock_uom from tabItem %s
|
||||
order by item_code, item_group""" % (conditions), as_dict=1):
|
||||
item_map.setdefault(i.name, i)
|
||||
|
||||
return item_map
|
||||
@ -133,3 +134,15 @@ def get_valuation_rate():
|
||||
item_val_rate_map.setdefault(d.item_code, d.val_rate)
|
||||
|
||||
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
|
||||
|
@ -3,8 +3,8 @@
|
||||
<a class="product-link" href="{{ route|abs_url }}">
|
||||
<div class="col-sm-4 col-xs-4 product-image-wrapper">
|
||||
<div class="product-image-img">
|
||||
{{ product_image_square(thumbnail or website_image) }}
|
||||
<div class="product-text" itemprop="name">{{ item_name }}</div>
|
||||
{{ product_image_square(thumbnail or website_image or image) }}
|
||||
<div class="product-text" itemprop="name">{{ item_name or name }}</div>
|
||||
{% if price_sales_uom %}
|
||||
<div>{{ price_sales_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
Loading…
x
Reference in New Issue
Block a user