Merge branch 'develop' of https://github.com/frappe/erpnext into develop

This commit is contained in:
Sathisha Poojary 2018-04-07 16:41:43 +05:30
commit 6b8eb645c4
179 changed files with 369845 additions and 359199 deletions

View File

@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides from erpnext.hooks import regional_overrides
from frappe.utils import getdate from frappe.utils import getdate
__version__ = '10.1.18' __version__ = '10.1.20'
def get_default_company(user=None): def get_default_company(user=None):
'''Get default company for user''' '''Get default company for user'''

View File

@ -17,18 +17,13 @@ frappe.ui.form.on('Account', {
}); });
}, },
refresh: function(frm) { refresh: function(frm) {
if (frm.doc.__islocal) { frm.toggle_display('account_name', frm.is_new());
frappe.msgprint(__("Please create new account from Chart of Accounts."));
throw "cannot create";
}
frm.toggle_display('account_name', frm.doc.__islocal);
// hide fields if group // hide fields if group
frm.toggle_display(['account_type', 'tax_rate'], cint(frm.doc.is_group) == 0); frm.toggle_display(['account_type', 'tax_rate'], cint(frm.doc.is_group) == 0);
// disable fields // disable fields
frm.toggle_enable(['account_name', 'is_group', 'company'], false); frm.toggle_enable(['is_group', 'company'], false);
if (cint(frm.doc.is_group) == 0) { if (cint(frm.doc.is_group) == 0) {
frm.toggle_display('freeze_account', frm.doc.__onload frm.toggle_display('freeze_account', frm.doc.__onload
@ -36,6 +31,7 @@ frappe.ui.form.on('Account', {
} }
// read-only for root accounts // read-only for root accounts
if (!frm.is_new()) {
if (!frm.doc.parent_account) { if (!frm.doc.parent_account) {
frm.set_read_only(); frm.set_read_only();
frm.set_intro(__("This is a root account and cannot be edited.")); frm.set_intro(__("This is a root account and cannot be edited."));
@ -47,8 +43,6 @@ frappe.ui.form.on('Account', {
// show / hide convert buttons // show / hide convert buttons
frm.trigger('add_toolbar_buttons'); frm.trigger('add_toolbar_buttons');
} }
if(!frm.doc.__islocal) {
frm.add_custom_button(__('Update Account Number'), function () { frm.add_custom_button(__('Update Account Number'), function () {
frm.trigger("update_account_number"); frm.trigger("update_account_number");
}); });

View File

@ -40,6 +40,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -68,6 +69,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0, "unique": 0,
"width": "50%" "width": "50%"
}, },
@ -94,12 +96,13 @@
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -130,6 +133,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -161,6 +165,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -193,6 +198,7 @@
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -223,6 +229,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -253,6 +260,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -285,6 +293,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -313,6 +322,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0, "unique": 0,
"width": "50%" "width": "50%"
}, },
@ -346,6 +356,7 @@
"reqd": 1, "reqd": 1,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -379,6 +390,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -411,6 +423,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -444,6 +457,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -474,6 +488,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -503,6 +518,7 @@
"reqd": 0, "reqd": 0,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -532,6 +548,7 @@
"reqd": 0, "reqd": 0,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -561,6 +578,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
} }
], ],
@ -575,7 +593,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-08-22 17:39:10.711343", "modified": "2018-04-04 03:23:20.011949",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Account", "name": "Account",

View File

@ -274,3 +274,7 @@ def get_name_with_number(new_account, account_number):
if account_number and not new_account[0].isdigit(): if account_number and not new_account[0].isdigit():
new_account = account_number + " - " + new_account new_account = account_number + " - " + new_account
return new_account return new_account
def on_doctype_update():
frappe.db.add_index("Account", ["lft", "rgt"])

View File

@ -101,7 +101,7 @@ def _make_test_records(verbose):
def get_inventory_account(company, warehouse=None): def get_inventory_account(company, warehouse=None):
account = None account = None
if warehouse: if warehouse:
account = get_warehouse_account(warehouse, company) account = get_warehouse_account(frappe.get_doc("Warehouse", warehouse))
else: else:
account = get_company_default_inventory_account(company) account = get_company_default_inventory_account(company)

View File

@ -64,3 +64,6 @@ class CostCenter(NestedSet):
if not merge: if not merge:
frappe.db.set_value("Cost Center", newdn, "cost_center_name", frappe.db.set_value("Cost Center", newdn, "cost_center_name",
" - ".join(newdn.split(" - ")[:-1])) " - ".join(newdn.split(" - ")[:-1]))
def on_doctype_update():
frappe.db.add_index("Cost Center", ["lft", "rgt"])

View File

@ -48,5 +48,10 @@ frappe.treeview_settings["Cost Center"] = {
}, __('Budget')); }, __('Budget'));
}, },
onrender: function(node) {
if(node.is_root){
node.hide_add = true;
}
}
} }

View File

@ -769,6 +769,8 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
party_account = doc.receivable_account party_account = doc.receivable_account
elif dt == "Employee Advance": elif dt == "Employee Advance":
party_account = doc.advance_account party_account = doc.advance_account
elif dt == "Expense Claim":
party_account = doc.payable_account
else: else:
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company) party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)

View File

@ -1302,10 +1302,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
} }
}); });
if (field == 'qty') {
this.remove_zero_qty_item();
}
this.update_paid_amount_status(false) this.update_paid_amount_status(false)
}, },

View File

@ -2,9 +2,8 @@
<style> <style>
@media screen { @media screen {
.print-format { .print-format {
padding: 8mm; padding: 4mm;
margin:4mm; font-size: 8.0pt !important;
font-size: 10.0pt !important;
font-family: Tahoma, sans-serif; font-family: Tahoma, sans-serif;
} }
} }
@ -12,9 +11,8 @@
{% } %} {% } %}
<style> <style>
.print-format { .print-format {
padding: 8mm; padding: 4mm;
margin:4mm; font-size: 8.0pt !important;
font-size: 10.0pt !important;
font-family: Tahoma, sans-serif; font-family: Tahoma, sans-serif;
} }
</style> </style>
@ -41,7 +39,7 @@
var range4 = report.columns[14].label; var range4 = report.columns[14].label;
%} %}
{% if(balance_row) { %} {% if(balance_row) { %}
<table class="table table-bordered table-condensed table-sm small"> <table class="table table-bordered table-condensed">
<caption class="text-right">(Amount in {%= data[0][__("currency")] || "" %})</caption> <caption class="text-right">(Amount in {%= data[0][__("currency")] || "" %})</caption>
<colgroup> <colgroup>
<col style="width: 30mm;"> <col style="width: 30mm;">
@ -100,8 +98,8 @@
<thead> <thead>
<tr> <tr>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %} {% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
<th style="width: 15%">{%= __("Date") %}</th> <th style="width: 18%">{%= __("Date") %}</th>
<th style="width: 20%">{%= __("Reference") %}</th> <th style="width: 17%">{%= __("Reference") %}</th>
{% if(!filters.show_pdc_in_print) { %} {% if(!filters.show_pdc_in_print) { %}
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th> <th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
{% } %} {% } %}

View File

@ -297,8 +297,7 @@ def filter_accounts(accounts, depth=10):
def add_to_list(parent, level): def add_to_list(parent, level):
if level < depth: if level < depth:
children = parent_children_map.get(parent) or [] children = parent_children_map.get(parent) or []
if parent == None: sort_accounts(children, is_root=True if parent==None else False)
sort_root_accounts(children)
for child in children: for child in children:
child.indent = level child.indent = level
@ -310,13 +309,11 @@ def filter_accounts(accounts, depth=10):
return filtered_accounts, accounts_by_name, parent_children_map return filtered_accounts, accounts_by_name, parent_children_map
def sort_root_accounts(roots): def sort_accounts(accounts, is_root=False, key="name"):
"""Sort root types as Asset, Liability, Equity, Income, Expense""" """Sort root types as Asset, Liability, Equity, Income, Expense"""
def compare_roots(a, b): def compare_accounts(a, b):
if a.value and re.split('\W+', a.value)[0].isdigit(): if is_root:
# if chart of accounts is numbered, then sort by number
return cmp(a.value, b.value)
if a.report_type != b.report_type and a.report_type == "Balance Sheet": if a.report_type != b.report_type and a.report_type == "Balance Sheet":
return -1 return -1
if a.root_type != b.root_type and a.root_type == "Asset": if a.root_type != b.root_type and a.root_type == "Asset":
@ -325,10 +322,13 @@ def sort_root_accounts(roots):
return -1 return -1
if a.root_type == "Income" and b.root_type == "Expense": if a.root_type == "Income" and b.root_type == "Expense":
return -1 return -1
else:
if re.split('\W+', a[key])[0].isdigit():
# if chart of accounts is numbered, then sort by number
return cmp(a[key], b[key])
return 1 return 1
roots.sort(key = functools.cmp_to_key(compare_roots)) accounts.sort(key = functools.cmp_to_key(compare_accounts))
def set_gl_entries_by_account( def set_gl_entries_by_account(
company, from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account, ignore_closing_entries=False): company, from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account, ignore_closing_entries=False):

View File

@ -252,16 +252,11 @@ def add_ac(args=None):
if not ac.parent_account: if not ac.parent_account:
ac.parent_account = args.get("parent") ac.parent_account = args.get("parent")
if getattr(ac, 'is_root', None):
ac.parent_account=''
ac.old_parent = "" ac.old_parent = ""
ac.freeze_account = "No" ac.freeze_account = "No"
if cint(ac.get("is_root")): if cint(ac.get("is_root")):
ac.parent_account = None ac.parent_account = None
ac.flags.ignore_mandatory = True ac.flags.ignore_mandatory = True
else:
ac.root_type = None
ac.insert() ac.insert()
@ -663,7 +658,7 @@ def get_companies():
@frappe.whitelist() @frappe.whitelist()
def get_children(doctype, parent, company, is_root=False): def get_children(doctype, parent, company, is_root=False):
from erpnext.accounts.report.financial_statements import sort_root_accounts from erpnext.accounts.report.financial_statements import sort_accounts
fieldname = frappe.db.escape(doctype.lower().replace(' ','_')) fieldname = frappe.db.escape(doctype.lower().replace(' ','_'))
doctype = frappe.db.escape(doctype) doctype = frappe.db.escape(doctype)
@ -678,9 +673,6 @@ def get_children(doctype, parent, company, is_root=False):
and `company` = %s and docstatus<2 and `company` = %s and docstatus<2
order by name""".format(fields=fields, fieldname = fieldname, doctype=doctype), order by name""".format(fields=fields, fieldname = fieldname, doctype=doctype),
company, as_dict=1) company, as_dict=1)
if parent=="Accounts":
sort_root_accounts(acc)
else: else:
# other # other
fields = ", account_currency" if doctype=="Account" else "" fields = ", account_currency" if doctype=="Account" else ""
@ -693,6 +685,7 @@ def get_children(doctype, parent, company, is_root=False):
parent, as_dict=1) parent, as_dict=1)
if doctype == 'Account': if doctype == 'Account':
sort_accounts(acc, is_root, key="value")
company_currency = frappe.db.get_value("Company", company, "default_currency") company_currency = frappe.db.get_value("Company", company, "default_currency")
for each in acc: for each in acc:
each["company_currency"] = company_currency each["company_currency"] = company_currency

View File

@ -178,3 +178,6 @@ def get_children(doctype, parent, is_root=False):
# return nodes # return nodes
return land_units return land_units
def on_doctype_update():
frappe.db.add_index("Land Unit", ["lft", "rgt"])

View File

@ -15,8 +15,8 @@ frappe.ui.form.on('Soil Texture', {
}, },
onload: function(frm) { onload: function(frm) {
if (frm.doc.soil_texture_criteria == undefined) frm.call('load_contents'); if (frm.doc.soil_texture_criteria == undefined) frm.call('load_contents');
if (this.ternary_plot) return; if (frm.doc.ternary_plot) return;
this.ternary_plot = new agriculture.TernaryPlot({ frm.doc.ternary_plot = new agriculture.TernaryPlot({
parent: frm.get_field("ternary_plot").$wrapper, parent: frm.get_field("ternary_plot").$wrapper,
clay: frm.doc.clay_composition, clay: frm.doc.clay_composition,
sand: frm.doc.sand_composition, sand: frm.doc.sand_composition,

View File

@ -270,6 +270,40 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "company",
"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": 1,
"label": "Company",
"length": 0,
"no_copy": 0,
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 1,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -367,6 +401,70 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.docstatus===1",
"fieldname": "order_confirmation_no",
"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": "Order Confirmation No",
"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_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.order_confirmation_no",
"fieldname": "order_confirmation_date",
"fieldtype": "Date",
"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": "Order Confirmation Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -400,40 +498,6 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "company",
"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": 1,
"label": "Company",
"length": 0,
"no_copy": 0,
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 1,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -3258,6 +3322,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -3495,7 +3560,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-02-21 14:26:17.099336", "modified": "2018-03-28 14:09:02.393746",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order", "name": "Purchase Order",

View File

@ -336,7 +336,7 @@ def get_data():
"color": "#009248", "color": "#009248",
"icon": "/assets/erpnext/images/hub_logo.svg", "icon": "/assets/erpnext/images/hub_logo.svg",
"type": "page", "type": "page",
"link": "Hub/Home", "link": "Hub/Item",
"label": _("Hub") "label": _("Hub")
}, },
{ {

View File

@ -18,5 +18,14 @@ def get_data():
"description": _("GoCardless SEPA Mandate"), "description": _("GoCardless SEPA Mandate"),
} }
] ]
},
{
"label": _("Settings"),
"items": [
{
"type": "doctype",
"name": "Woocommerce Settings"
}
]
} }
] ]

View File

@ -667,7 +667,9 @@ class AccountsController(TransactionBase):
self.remove(item) self.remove(item)
def set_payment_schedule(self): def set_payment_schedule(self):
if self.doctype == 'Sales Invoice' and self.is_pos: return if self.doctype == 'Sales Invoice' and self.is_pos:
self.payment_terms_template = ''
return
posting_date = self.get("bill_date") or self.get("posting_date") or self.get("transaction_date") posting_date = self.get("bill_date") or self.get("posting_date") or self.get("transaction_date")
date = self.get("due_date") date = self.get("due_date")

View File

@ -152,6 +152,11 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
conditions = [] conditions = []
description_cond = ''
if frappe.db.count('Item', cache=True) < 50000:
# scan description only if items are less than 50000
description_cond = 'or tabItem.description LIKE %(txt)s'
return frappe.db.sql("""select tabItem.name, tabItem.item_group, return frappe.db.sql("""select tabItem.name, tabItem.item_group,
if(length(tabItem.item_name) > 40, if(length(tabItem.item_name) > 40,
concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name, concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name,
@ -165,8 +170,8 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
and (tabItem.`{key}` LIKE %(txt)s and (tabItem.`{key}` LIKE %(txt)s
or tabItem.item_group LIKE %(txt)s or tabItem.item_group LIKE %(txt)s
or tabItem.item_name LIKE %(txt)s or tabItem.item_name LIKE %(txt)s
or tabItem.description LIKE %(txt)s) or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s
or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s) {description_cond}))
{fcond} {mcond} {fcond} {mcond}
order by order by
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
@ -176,7 +181,8 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
limit %(start)s, %(page_len)s """.format( limit %(start)s, %(page_len)s """.format(
key=searchfield, key=searchfield,
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'), fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
mcond=get_match_cond(doctype).replace('%', '%%')), mcond=get_match_cond(doctype).replace('%', '%%'),
description_cond = description_cond),
{ {
"today": nowdate(), "today": nowdate(),
"txt": "%%%s%%" % txt, "txt": "%%%s%%" % txt,

View File

@ -34,14 +34,14 @@ def work():
payroll_entry.salary_slip_based_on_timesheet = 0 payroll_entry.salary_slip_based_on_timesheet = 0
payroll_entry.create_salary_slips() payroll_entry.create_salary_slips()
payroll_entry.submit_salary_slips() payroll_entry.submit_salary_slips()
payroll_entry.make_accural_jv_entry() payroll_entry.make_accrual_jv_entry()
# payroll_entry.make_journal_entry(reference_date=frappe.flags.current_date, # payroll_entry.make_journal_entry(reference_date=frappe.flags.current_date,
# reference_number=random_string(10)) # reference_number=random_string(10))
payroll_entry.salary_slip_based_on_timesheet = 1 payroll_entry.salary_slip_based_on_timesheet = 1
payroll_entry.create_salary_slips() payroll_entry.create_salary_slips()
payroll_entry.submit_salary_slips() payroll_entry.submit_salary_slips()
payroll_entry.make_accural_jv_entry() payroll_entry.make_accrual_jv_entry()
# payroll_entry.make_journal_entry(reference_date=frappe.flags.current_date, # payroll_entry.make_journal_entry(reference_date=frappe.flags.current_date,
# reference_number=random_string(10)) # reference_number=random_string(10))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

View File

@ -0,0 +1,57 @@
# WooCommerce Integration
#### Setting Up WooCommerce on ERPNEXT:-
Steps:-
1. From Awesome-bar, go to "Woocommerce Settings" doctype.
2. From your woocommerce site, generate "API consumer key" and "API consumer secret" using Keys/Apps tab.
3. Paste those generated "API consumer key" and "API consumer secret" into "Woocommerce Settings" doctype.
4. In "Woocommerce Server URL" paste the url of your site where ERPNEXT is installed.
5. Make sure "Enable Sync" is checked.
6. Select Account type from Account Details Section.
7. Click Save.
8. After saving, "Secret" and "Endpoint" are generated automatically and can be seen on "Woocommerce Settings" doctype.
9. Now from your woocommerce site, click on webhooks option and click on "Add Webhook".
10. Give name to the webhook of your choice. Click on Status dropdown and select "Active". Select Topic as "Order Created". Copy the "Endpoint" from "Woocommerce Settings" doctype and paste it in "Delivery URL" field. Copy "Secret" from "Woocommerce Settings" doctype and paste it in "Secret" field. Keep API VERSION as it is and click on Save Webhook.
11. Now the WooCommerce is successful setup on your system.
<img class="screenshot" alt="Woocommerce Integration" src="{{docs_base_url}}/assets/img/erpnext_integrations/woocommerce_setting_config.gif">
### Note:- In above gif, inplace of delivery url on woocommerce website, you need to paste the url you will obtain after saving the "Woocommerce Settings" page (i.e. Endpoint from "Woocommerce Settings"). I pasted other url because I was using localhost. Please paste your endpoint in place of Delivery URL.
#### WooCommerce Integration Working:-
Steps:-
1. From your Woocommerce website, register yourself as a user.
2. Now Click on Address Details and provide the required details.
3. For start shopping, click on Shop option and now available products can be seen.
4. Add the desired products into cart and click on View Cart.
5. From Cart, once you have added the desired products, you can click on proceed to checkout.
6. All billing details and Order details can be seen now. Once you are ok with it, click on Place Order button.
7. "Order Received" message can been seen indicating that the order is placed successfully.
8. Now on system where ERPNEXT is installed check the following doctypes: Customer, Address, Item, Sales Order.
<img class="screenshot" alt="Woocommerce Integration" src="{{docs_base_url}}/assets/img/erpnext_integrations/woocommerce_demo.gif">

View File

@ -0,0 +1,32 @@
# Data Export Tool
Use this tool to export data in Excel or CSV format
To open the Data Export Tool, you can either go to Setup > Data Export OR simply type `Data Export` in Global Search.
<img alt="Data Export" class="screenshot" src="{{docs_base_url}}/assets/img/setup/data-export/data-export-tool.gif">
### Options
1. Select Doctype
You will be provided with the list of document types which you can export. Scroll through the list or type the name of the Doctype you want to export.
2. File Type
Select File format of the data to be exported (Excel or CSV).
3. Add Filter (optional)
Add filters to export only required data.
This filter is similar to the filter in list view.
eg. If you want the data of project related to specific company, you can use the filter to select the company.
4. Field Multicheck (optional)
Only checked fields will be exported.
**Note:**
If you do not want a child table to be exported simply deselect all the fields of that child table.

View File

@ -24,7 +24,7 @@ In the document, you can select multiple Purchase Receipts and fetch all items f
<img class="screenshot" alt="Landed Cost Vouher" src="{{docs_base_url}}/assets/img/stock/landed-cost.png"> <img class="screenshot" alt="Landed Cost Vouher" src="{{docs_base_url}}/assets/img/stock/landed-cost.png">
### What happend on submission? ### What happens on submission?
1. On submission of Landed Cost Voucher, the applicable landed cost charges are updated in Purchase Receipt Item table. 1. On submission of Landed Cost Voucher, the applicable landed cost charges are updated in Purchase Receipt Item table.

View File

@ -8,5 +8,4 @@ bulk-rename
renaming-documents renaming-documents
search-filter search-filter
tree-master-renaming tree-master-renaming
pos-view
letter-head-in-the-report letter-head-in-the-report

View File

@ -1,11 +0,0 @@
#POS View
POS (point of sale) view renders form in a different layout, optimized for the quick selection of items. This view has primarily been designed for the retail business.
Using POS View, you can only create Sales Invoice, without switching to standard form view. For other transactions, like Sales Order, Delivery Note, Purchase Order, Purchase Receipt etc., POS View is only used for Item selection. For entering other values in the transaction, you should switch to form view.
<img alt="POS View" class="screenshot" src="{{docs_base_url}}/assets/img/articles/pos-view.gif">
For creating POS Invoice, check following help video.
<iframe width="660" height="371" src="https://www.youtube.com/embed/4WkelWkbP_c" frameborder="0" allowfullscreen></iframe>

View File

@ -26,7 +26,7 @@ class ProgramEnrollmentTool(Document):
elif self.get_students_from == "Program Enrollment": elif self.get_students_from == "Program Enrollment":
condition2 = 'and student_batch_name=%(student_batch)s' if self.student_batch else " " condition2 = 'and student_batch_name=%(student_batch)s' if self.student_batch else " "
students = frappe.db.sql('''select student, student_name, student_batch_name from `tabProgram Enrollment` students = frappe.db.sql('''select student, student_name, student_batch_name from `tabProgram Enrollment`
where program=%(program)s and academic_year=%(academic_year)s {0} {1}''' where program=%(program)s and academic_year=%(academic_year)s {0} {1} and docstatus != 2'''
.format(condition, condition2), self.as_dict(), as_dict=1) .format(condition, condition2), self.as_dict(), as_dict=1)
student_list = [d.student for d in students] student_list = [d.student for d in students]

View File

@ -0,0 +1,206 @@
from __future__ import unicode_literals
import frappe, base64, hashlib, hmac, json
import datetime
from frappe import _
def verify_request():
woocommerce_settings = frappe.get_doc("Woocommerce Settings")
sig = base64.b64encode(
hmac.new(
woocommerce_settings.secret.encode('utf8'),
frappe.request.data,
hashlib.sha256
).digest()
)
if frappe.request.data and \
frappe.get_request_header("X-Wc-Webhook-Signature") and \
not sig == bytes(frappe.get_request_header("X-Wc-Webhook-Signature").encode()):
frappe.throw(_("Unverified Webhook Data"))
frappe.set_user(woocommerce_settings.modified_by)
@frappe.whitelist(allow_guest=True)
def order():
verify_request()
if frappe.request.data:
fd = json.loads(frappe.request.data)
else:
return "success"
event = frappe.get_request_header("X-Wc-Webhook-Event")
if event == "created":
raw_billing_data = fd.get("billing")
customer_woo_com_email = raw_billing_data.get("email")
if frappe.get_value("Customer",{"woocommerce_email": customer_woo_com_email}):
# Edit
link_customer_and_address(raw_billing_data,1)
else:
# Create
link_customer_and_address(raw_billing_data,0)
items_list = fd.get("line_items")
for item in items_list:
item_woo_com_id = item.get("product_id")
if frappe.get_value("Item",{"woocommerce_id": item_woo_com_id}):
#Edit
link_item(item,1)
else:
link_item(item,0)
customer_name = raw_billing_data.get("first_name") + " " + raw_billing_data.get("last_name")
new_sales_order = frappe.new_doc("Sales Order")
new_sales_order.customer = customer_name
created_date = fd.get("date_created").split("T")
new_sales_order.transaction_date = created_date[0]
new_sales_order.po_no = fd.get("id")
new_sales_order.woocommerce_id = fd.get("id")
new_sales_order.naming_series = "SO-"
placed_order_date = created_date[0]
raw_date = datetime.datetime.strptime(placed_order_date, "%Y-%m-%d")
raw_delivery_date = frappe.utils.add_to_date(raw_date,days = 7)
order_delivery_date_str = raw_delivery_date.strftime('%Y-%m-%d')
order_delivery_date = str(order_delivery_date_str)
new_sales_order.delivery_date = order_delivery_date
for item in items_list:
woocomm_item_id = item.get("product_id")
found_item = frappe.get_doc("Item",{"woocommerce_id": woocomm_item_id})
ordered_items_tax = item.get("total_tax")
default_set_company = frappe.get_doc("Global Defaults")
company = default_set_company.default_company
found_company = frappe.get_doc("Company",{"name":company})
company_abbr = found_company.abbr
new_sales_order.append("items",{
"item_code": found_item.item_code,
"item_name": found_item.item_name,
"description": found_item.item_name,
"delivery_date":order_delivery_date,
"uom": "Nos",
"qty": item.get("quantity"),
"rate": item.get("price"),
"warehouse": "Stores" + " - " + company_abbr
})
add_tax_details(new_sales_order,ordered_items_tax,"Ordered Item tax",0)
# shipping_details = fd.get("shipping_lines") # used for detailed order
shipping_total = fd.get("shipping_total")
shipping_tax = fd.get("shipping_tax")
add_tax_details(new_sales_order,shipping_tax,"Shipping Tax",1)
add_tax_details(new_sales_order,shipping_total,"Shipping Total",1)
new_sales_order.submit()
frappe.db.commit()
def link_customer_and_address(raw_billing_data,customer_status):
if customer_status == 0:
# create
customer = frappe.new_doc("Customer")
address = frappe.new_doc("Address")
if customer_status == 1:
# Edit
customer_woo_com_email = raw_billing_data.get("email")
customer = frappe.get_doc("Customer",{"woocommerce_email": customer_woo_com_email})
old_name = customer.customer_name
full_name = str(raw_billing_data.get("first_name"))+ " "+str(raw_billing_data.get("last_name"))
customer.customer_name = full_name
customer.woocommerce_email = str(raw_billing_data.get("email"))
customer.save()
frappe.db.commit()
if customer_status == 1:
frappe.rename_doc("Customer", old_name, full_name)
address = frappe.get_doc("Address",{"woocommerce_email":customer_woo_com_email})
customer = frappe.get_doc("Customer",{"woocommerce_email": customer_woo_com_email})
address.address_line1 = raw_billing_data.get("address_1", "Not Provided")
address.address_line2 = raw_billing_data.get("address_2", "Not Provided")
address.city = raw_billing_data.get("city", "Not Provided")
address.woocommerce_email = str(raw_billing_data.get("email"))
address.address_type = "Shipping"
address.country = frappe.get_value("Country", filters={"code":raw_billing_data.get("country", "IN").lower()})
address.state = raw_billing_data.get("state")
address.pincode = str(raw_billing_data.get("postcode"))
address.phone = str(raw_billing_data.get("phone"))
address.email_id = str(raw_billing_data.get("email"))
address.append("links", {
"link_doctype": "Customer",
"link_name": customer.customer_name
})
address.save()
frappe.db.commit()
if customer_status == 1:
address = frappe.get_doc("Address",{"woocommerce_email":customer_woo_com_email})
old_address_title = address.name
new_address_title = customer.customer_name+"-billing"
address.address_title = customer.customer_name
address.save()
frappe.rename_doc("Address",old_address_title,new_address_title)
frappe.db.commit()
def link_item(item_data,item_status):
if item_status == 0:
#Create Item
item = frappe.new_doc("Item")
if item_status == 1:
#Edit Item
item_woo_com_id = item_data.get("product_id")
item = frappe.get_doc("Item",{"woocommerce_id": item_woo_com_id})
item.item_name = str(item_data.get("name"))
item.item_code = "woocommerce - " + str(item_data.get("product_id"))
item.woocommerce_id = str(item_data.get("product_id"))
item.item_group = "WooCommerce Products"
item.save()
frappe.db.commit()
def add_tax_details(sales_order,price,desc,status):
woocommerce_settings = frappe.get_doc("Woocommerce Settings")
if status == 0:
# Product taxes
account_head_type = woocommerce_settings.tax_account
if status == 1:
# Shipping taxes
account_head_type = woocommerce_settings.f_n_f_account
sales_order.append("taxes",{
"charge_type":"Actual",
"account_head": account_head_type,
"tax_amount": price,
"description": desc
})

View File

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

View File

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

View File

@ -0,0 +1,45 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Woocommerce Settings', {
refresh (frm) {
frm.trigger("add_button_generate_secret");
frm.trigger("check_enabled");
frm.set_query("tax_account", ()=>{
return {
"filters": {
"company": frappe.defaults.get_default("company"),
"is_group": 0
}
};
});
},
enable_sync (frm) {
frm.trigger("check_enabled");
},
add_button_generate_secret(frm) {
frm.add_custom_button(__('Generate Secret'), () => {
frappe.confirm(
__("Apps using current key won't be able to access, are you sure?"),
() => {
frappe.call({
type:"POST",
method:"erpnext.erpnext_integrations.doctype.woocommerce_settings.woocommerce_settings.generate_secret",
}).done(() => {
frm.reload_doc();
}).fail(() => {
frappe.msgprint(__("Could not generate Secret"));
});
}
);
});
},
check_enabled (frm) {
frm.set_df_property("woocommerce_server_url", "reqd", frm.doc.enable_sync);
frm.set_df_property("api_consumer_key", "reqd", frm.doc.enable_sync);
frm.set_df_property("api_consumer_secret", "reqd", frm.doc.enable_sync);
}
});

View File

@ -0,0 +1,465 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-02-12 15:10:05.495713",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "enable_sync",
"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": "Enable Sync",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sb_00",
"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": "",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "woocommerce_server_url",
"fieldtype": "Data",
"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": "Woocommerce Server URL",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "secret",
"fieldtype": "Code",
"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": "Secret",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "cb_00",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "api_consumer_key",
"fieldtype": "Data",
"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": "API consumer key",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "api_consumer_secret",
"fieldtype": "Data",
"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": "API consumer secret",
"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_on_submit": 0,
"bold": 0,
"collapsible": 1,
"columns": 0,
"fieldname": "sb_accounting_details",
"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": "Accounting Details",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "tax_account",
"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": "Tax Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_10",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "f_n_f_account",
"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": "Freight and Forwarding Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"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_on_submit": 0,
"bold": 0,
"collapsible": 1,
"columns": 0,
"fieldname": "endpoints",
"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": "Endpoints",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "endpoint",
"fieldtype": "Code",
"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": "Endpoint",
"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,
"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-03-23 16:57:20.880513",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "Woocommerce Settings",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"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
}

View File

@ -0,0 +1,124 @@
# -*- 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 import _
from frappe.model.document import Document
from six.moves.urllib.parse import urlparse
class WoocommerceSettings(Document):
def validate(self):
self.validate_settings()
self.create_delete_custom_fields()
self.create_webhook_url()
def create_delete_custom_fields(self):
if self.enable_sync:
# create
create_custom_field_id_and_check_status = False
create_custom_field_email_check = False
names = ["Customer-woocommerce_id","Sales Order-woocommerce_id","Item-woocommerce_id","Address-woocommerce_id"]
names_check_box = ["Customer-woocommerce_check","Sales Order-woocommerce_check","Item-woocommerce_check","Address-woocommerce_check"]
email_names = ["Customer-woocommerce_email","Address-woocommerce_email"]
for i in zip(names,names_check_box):
if not frappe.get_value("Custom Field",{"name":i[0]}) or not frappe.get_value("Custom Field",{"name":i[1]}):
create_custom_field_id_and_check_status = True
break;
if create_custom_field_id_and_check_status:
names = ["Customer","Sales Order","Item","Address"]
for name in names:
custom = frappe.new_doc("Custom Field")
custom.dt = name
custom.label = "woocommerce_id"
custom.read_only = 1
custom.save()
custom = frappe.new_doc("Custom Field")
custom.dt = name
custom.label = "woocommerce_check"
custom.fieldtype = "Check"
custom.read_only = 1
custom.save()
for i in email_names:
if not frappe.get_value("Custom Field",{"name":i}):
create_custom_field_email_check = True
break;
if create_custom_field_email_check:
names = ["Customer","Address"]
for name in names:
custom = frappe.new_doc("Custom Field")
custom.dt = name
custom.label = "woocommerce_email"
custom.read_only = 1
custom.save()
if not frappe.get_value("Item Group",{"name": "WooCommerce Products"}):
item_group = frappe.new_doc("Item Group")
item_group.item_group_name = "WooCommerce Products"
item_group.parent_item_group = "All Item Groups"
item_group.save()
elif not self.enable_sync:
# delete
names = ["Customer-woocommerce_id","Sales Order-woocommerce_id","Item-woocommerce_id","Address-woocommerce_id"]
names_check_box = ["Customer-woocommerce_check","Sales Order-woocommerce_check","Item-woocommerce_check","Address-woocommerce_check"]
email_names = ["Customer-woocommerce_email","Address-woocommerce_email"]
for name in names:
frappe.delete_doc("Custom Field",name)
for name in names_check_box:
frappe.delete_doc("Custom Field",name)
for name in email_names:
frappe.delete_doc("Custom Field",name)
frappe.delete_doc("Item Group","WooCommerce Products")
frappe.db.commit()
def validate_settings(self):
if self.enable_sync:
if not self.secret:
self.set("secret", frappe.generate_hash())
if not self.woocommerce_server_url:
frappe.throw(_("Please enter Woocommerce Server URL"))
if not self.api_consumer_key:
frappe.throw(_("Please enter API Consumer Key"))
if not self.api_consumer_secret:
frappe.throw(_("Please enter API Consumer Secret"))
def create_webhook_url(self):
endpoint = "/api/method/erpnext.erpnext_integrations.connectors.woocommerce_connection.order"
try:
url = frappe.request.url
except RuntimeError:
# for CI Test to work
url = "http://localhost:8000"
server_url = '{uri.scheme}://{uri.netloc}'.format(
uri=urlparse(url)
)
delivery_url = server_url + endpoint
self.endpoint = delivery_url
@frappe.whitelist()
def generate_secret():
woocommerce_settings = frappe.get_doc("Woocommerce Settings")
woocommerce_settings.secret = frappe.generate_hash()
woocommerce_settings.save()

View File

@ -42,6 +42,7 @@
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -73,6 +74,7 @@
"reqd": 0, "reqd": 0,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -104,6 +106,7 @@
"reqd": 1, "reqd": 1,
"search_index": 1, "search_index": 1,
"set_only_once": 1, "set_only_once": 1,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -135,6 +138,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -165,6 +169,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -196,6 +201,7 @@
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
"set_only_once": 1, "set_only_once": 1,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -227,6 +233,7 @@
"reqd": 0, "reqd": 0,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -257,6 +264,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -287,6 +295,7 @@
"reqd": 0, "reqd": 0,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -318,6 +327,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -347,6 +357,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -378,6 +389,7 @@
"reqd": 0, "reqd": 0,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -409,6 +421,7 @@
"reqd": 0, "reqd": 0,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -439,6 +452,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -469,6 +483,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -482,7 +497,7 @@
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 1,
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
@ -500,6 +515,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -531,6 +547,7 @@
"reqd": 0, "reqd": 0,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -563,6 +580,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -594,6 +612,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -625,6 +644,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -656,6 +676,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -686,6 +707,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -715,6 +737,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -745,6 +768,7 @@
"reqd": 0, "reqd": 0,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -774,6 +798,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -805,6 +830,7 @@
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
"set_only_once": 1, "set_only_once": 1,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -836,6 +862,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -865,6 +892,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -895,6 +923,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -924,6 +953,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -954,6 +984,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -983,6 +1014,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -1013,6 +1045,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -1042,6 +1075,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -1072,6 +1106,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -1102,6 +1137,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -1132,6 +1168,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -1162,6 +1199,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -1193,6 +1231,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -1223,6 +1262,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -1253,6 +1293,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -1283,6 +1324,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -1313,6 +1355,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -1343,6 +1386,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -1374,6 +1418,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
} }
], ],
@ -1388,7 +1433,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-11-22 14:32:27.994634", "modified": "2018-04-05 12:20:25.061368",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Lab Test", "name": "Lab Test",
@ -1397,7 +1442,6 @@
"permissions": [ "permissions": [
{ {
"amend": 1, "amend": 1,
"apply_user_permissions": 0,
"cancel": 1, "cancel": 1,
"create": 1, "create": 1,
"delete": 0, "delete": 0,
@ -1417,7 +1461,6 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,
@ -1437,7 +1480,6 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,

View File

@ -40,6 +40,7 @@
"reqd": 1, "reqd": 1,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -71,6 +72,7 @@
"reqd": 0, "reqd": 0,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -101,6 +103,7 @@
"reqd": 1, "reqd": 1,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -133,6 +136,7 @@
"reqd": 1, "reqd": 1,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -164,6 +168,7 @@
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -193,6 +198,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -226,6 +232,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -259,6 +266,7 @@
"reqd": 0, "reqd": 0,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -291,6 +299,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -323,6 +332,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -354,6 +364,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -384,6 +395,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -413,6 +425,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -445,6 +458,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -475,6 +489,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -506,6 +521,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -537,6 +553,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -567,6 +584,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -598,6 +616,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -629,6 +648,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -658,6 +678,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -688,6 +709,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -718,6 +740,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -749,6 +772,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -780,6 +804,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -811,6 +836,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -841,6 +867,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -872,6 +899,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -886,7 +914,7 @@
"hidden": 1, "hidden": 1,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 1,
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
@ -903,6 +931,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
} }
], ],
@ -916,7 +945,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-10-05 12:12:11.918652", "modified": "2018-04-05 12:21:01.837502",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Lab Test Template", "name": "Lab Test Template",
@ -925,7 +954,6 @@
"permissions": [ "permissions": [
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
@ -945,7 +973,6 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,

View File

@ -12,7 +12,7 @@ def get_data():
}, },
{ {
'label': _('Lab Tests and Vital Signs'), 'label': _('Lab Tests and Vital Signs'),
'items': ['Lab Test', 'Vital Signs'] 'items': ['Lab Test', 'Sample Collection', 'Vital Signs']
} }
] ]
} }

View File

@ -0,0 +1,18 @@
from frappe import _
def get_data():
return {
'heatmap': True,
'heatmap_message': _('This is based on transactions against this Physician.'),
'fieldname': 'physician',
'transactions': [
{
'label': _('Appointments and Consultations'),
'items': ['Patient Appointment', 'Consultation']
},
{
'label': _('Lab Tests'),
'items': ['Lab Test']
}
]
}

View File

@ -199,7 +199,6 @@ doc_events = {
"validate": "erpnext.portal.doctype.products_settings.products_settings.home_page_is_products" "validate": "erpnext.portal.doctype.products_settings.products_settings.home_page_is_products"
}, },
"Sales Invoice": { "Sales Invoice": {
'validate': 'erpnext.regional.india.utils.set_place_of_supply',
"on_submit": "erpnext.regional.france.utils.create_transaction_log", "on_submit": "erpnext.regional.france.utils.create_transaction_log",
"on_trash": "erpnext.regional.check_deletion_permission" "on_trash": "erpnext.regional.check_deletion_permission"
}, },
@ -210,7 +209,7 @@ doc_events = {
'Address': { 'Address': {
'validate': 'erpnext.regional.india.utils.validate_gstin_for_india' 'validate': 'erpnext.regional.india.utils.validate_gstin_for_india'
}, },
'Purchase Invoice': { ('Sales Invoice', 'Purchase Invoice', 'Delivery Note'): {
'validate': 'erpnext.regional.india.utils.set_place_of_supply' 'validate': 'erpnext.regional.india.utils.set_place_of_supply'
} }
} }

View File

@ -234,6 +234,40 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"depends_on": "user_id",
"description": "This will restrict user access to other employee records",
"fieldname": "create_user_permission",
"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": "Create User Permission",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -938,40 +972,6 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Provide Email Address registered in company",
"fieldname": "company_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": "Company Email",
"length": 0,
"no_copy": 0,
"oldfieldname": "company_email",
"oldfieldtype": "Data",
"options": "Email",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -1260,6 +1260,36 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "cell_number",
"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": "Cell Number",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -1331,7 +1361,8 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fieldname": "cell_number", "description": "Provide Email Address registered in company",
"fieldname": "company_email",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
@ -1340,9 +1371,12 @@
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Cell Number", "label": "Company Email",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"oldfieldname": "company_email",
"oldfieldtype": "Data",
"options": "Email",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
@ -2668,7 +2702,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-03-09 15:04:16.056386", "modified": "2018-04-05 13:54:36.881449",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Employee", "name": "Employee",
@ -2677,7 +2711,6 @@
"permissions": [ "permissions": [
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,
@ -2697,7 +2730,6 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
@ -2713,12 +2745,10 @@
"set_user_permissions": 0, "set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0, "submit": 0,
"user_permission_doctypes": "[\"Branch\",\"Company\",\"Department\",\"Designation\"]",
"write": 1 "write": 1
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,

View File

@ -42,7 +42,7 @@ class Employee(NestedSet):
self.validate_status() self.validate_status()
self.validate_employee_leave_approver() self.validate_employee_leave_approver()
self.validate_reports_to() self.validate_reports_to()
self.validate_prefered_email() self.validate_preferred_email()
if self.user_id: if self.user_id:
self.validate_for_enabled_user_id() self.validate_for_enabled_user_id()
@ -63,6 +63,7 @@ class Employee(NestedSet):
self.update_user_permissions() self.update_user_permissions()
def update_user_permissions(self): def update_user_permissions(self):
if not self.create_user_permission: return
frappe.permissions.add_user_permission("Employee", self.name, self.user_id) frappe.permissions.add_user_permission("Employee", self.name, self.user_id)
frappe.permissions.set_user_permission_if_allowed("Company", self.company, self.user_id) frappe.permissions.set_user_permission_if_allowed("Company", self.company, self.user_id)
@ -162,7 +163,7 @@ class Employee(NestedSet):
self.update_nsm_model() self.update_nsm_model()
delete_events(self.doctype, self.name) delete_events(self.doctype, self.name)
def validate_prefered_email(self): def validate_preferred_email(self):
if self.prefered_contact_email and not self.get(scrub(self.prefered_contact_email)): if self.prefered_contact_email and not self.get(scrub(self.prefered_contact_email)):
frappe.msgprint(_("Please enter " + self.prefered_contact_email)) frappe.msgprint(_("Please enter " + self.prefered_contact_email))
@ -330,3 +331,7 @@ def get_children(doctype, parent=None, company=None, is_root=False, is_tree=Fals
# return employee # return employee
return employee return employee
def on_doctype_update():
frappe.db.add_index("Employee", ["lft", "rgt"])

View File

@ -82,9 +82,9 @@ cur_frm.cscript.refresh = function(doc) {
if (cint(doc.total_amount_reimbursed) > 0 && frappe.model.can_read(entry_doctype)) { if (cint(doc.total_amount_reimbursed) > 0 && frappe.model.can_read(entry_doctype)) {
cur_frm.add_custom_button(__('Bank Entries'), function() { cur_frm.add_custom_button(__('Bank Entries'), function() {
frappe.route_options = { frappe.route_options = {
entry_route_doctype: me.frm.doc.doctype, party_type: "Employee",
entry_route_name: me.frm.doc.name, party: doc.employee,
company: me.frm.doc.company company: doc.company
}; };
frappe.set_route("List", entry_doctype); frappe.set_route("List", entry_doctype);
}, __("View")); }, __("View"));
@ -181,7 +181,7 @@ frappe.ui.form.on("Expense Claim", {
make_payment_entry: function(frm) { make_payment_entry: function(frm) {
var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry"; var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry";
if(frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) { if(frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) {
method = "erpnext.hr.doctype.expense_claim.expense_claim.make_bank_entry" method = "erpnext.hr.doctype.expense_claim.expense_claim.make_bank_entry";
} }
return frappe.call({ return frappe.call({
method: method, method: method,

View File

@ -164,7 +164,7 @@ class PayrollEntry(Document):
except frappe.ValidationError: except frappe.ValidationError:
not_submitted_ss.append(ss_dict) not_submitted_ss.append(ss_dict)
if submitted_ss: if submitted_ss:
jv_name = self.make_accural_jv_entry() jv_name = self.make_accrual_jv_entry()
frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}") frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}")
.format(ss_obj.start_date, ss_obj.end_date)) .format(ss_obj.start_date, ss_obj.end_date))
@ -237,7 +237,7 @@ class PayrollEntry(Document):
return payroll_payable_account return payroll_payable_account
def make_accural_jv_entry(self): def make_accrual_jv_entry(self):
self.check_permission('write') self.check_permission('write')
earnings = self.get_salary_component_total(component_type = "earnings") or {} earnings = self.get_salary_component_total(component_type = "earnings") or {}
deductions = self.get_salary_component_total(component_type = "deductions") or {} deductions = self.get_salary_component_total(component_type = "deductions") or {}
@ -249,7 +249,7 @@ class PayrollEntry(Document):
if earnings or deductions: if earnings or deductions:
journal_entry = frappe.new_doc('Journal Entry') journal_entry = frappe.new_doc('Journal Entry')
journal_entry.voucher_type = 'Journal Entry' journal_entry.voucher_type = 'Journal Entry'
journal_entry.user_remark = _('Accural Journal Entry for salaries from {0} to {1}')\ journal_entry.user_remark = _('Accrual Journal Entry for salaries from {0} to {1}')\
.format(self.start_date, self.end_date) .format(self.start_date, self.end_date)
journal_entry.company = self.company journal_entry.company = self.company
journal_entry.posting_date = self.posting_date journal_entry.posting_date = self.posting_date

View File

@ -17,6 +17,5 @@ class SalaryComponent(Document):
self.salary_component.split()]).upper() self.salary_component.split()]).upper()
self.salary_component_abbr = self.salary_component_abbr.strip() self.salary_component_abbr = self.salary_component_abbr.strip()
self.salary_component_abbr = append_number_if_name_exists('Salary Component', self.salary_component_abbr,
self.salary_component_abbr = append_number_if_name_exists('Salary Component', 'salary_component_abbr', separator='_', filters={"name": ["!=", self.name]})
self.salary_component_abbr, 'salary_component_abbr', separator='_')

View File

@ -39,6 +39,16 @@ frappe.ui.form.on('Salary Structure', {
} }
} }
}); });
frm.set_query("payment_account", function () {
var account_types = ["Bank", "Cash"];
return {
filters: {
"account_type": ["in", account_types],
"is_group": 0,
"company": frm.doc.company
}
};
});
}, },
refresh: function(frm) { refresh: function(frm) {

View File

@ -22,13 +22,64 @@ def get_list(doctype, start=0, limit=20, fields=["*"], filters="{}", order_by=No
response = connection.get_list(doctype, response = connection.get_list(doctype,
limit_start=start, limit_page_length=limit, limit_start=start, limit_page_length=limit,
filters=filters, fields=fields) filters=filters, fields=fields)
return response
# Bad, need child tables in response
listing = []
for obj in response:
doc = connection.get_doc(doctype, obj['name'])
listing.append(doc)
return listing
@frappe.whitelist()
def get_item_favourites(start=0, limit=20, fields=["*"], order_by=None):
doctype = 'Hub Item'
hub_settings = frappe.get_doc('Hub Settings')
item_names_str = hub_settings.get('custom_data') or '[]'
item_names = json.loads(item_names_str)
filters = json.dumps({
'hub_item_code': ['in', item_names]
})
return get_list(doctype, start, limit, fields, filters, order_by)
@frappe.whitelist()
def update_wishlist_item(item_name, remove=0):
remove = int(remove)
hub_settings = frappe.get_doc('Hub Settings')
data = hub_settings.get('custom_data')
if not data or not json.loads(data):
data = '[]'
hub_settings.custom_data = data
hub_settings.save()
item_names_str = data
item_names = json.loads(item_names_str)
if not remove and item_name not in item_names:
item_names.append(item_name)
if remove and item_name in item_names:
item_names.remove(item_name)
item_names_str = json.dumps(item_names)
hub_settings.custom_data = item_names_str
hub_settings.save()
@frappe.whitelist() @frappe.whitelist()
def get_meta(doctype): def get_meta(doctype):
connection = get_client_connection() connection = get_client_connection()
meta = connection.get_doc('DocType', doctype) meta = connection.get_doc('DocType', doctype)
return meta categories = connection.get_list('Hub Category',
limit_start=0, limit_page_length=300,
filters={}, fields=['name'])
categories = [d.get('name') for d in categories]
return {
'meta': meta,
'companies': connection.get_list('Hub Company',
limit_start=0, limit_page_length=300,
filters={}, fields=['name']),
'categories': categories
}
@frappe.whitelist() @frappe.whitelist()
def get_categories(parent='All Categories'): def get_categories(parent='All Categories'):
@ -40,12 +91,42 @@ def get_categories(parent='All Categories'):
return response return response
@frappe.whitelist() @frappe.whitelist()
def update_category(item_name, category): def update_category(hub_item_code, category):
connection = get_hub_connection() connection = get_hub_connection()
response = connection.update('Hub Item', dict(
# args = frappe._dict(dict(
# doctype='Hub Category',
# hub_category_name=category
# ))
# response = connection.insert('Hub Category', args)
response = connection.update('Hub Item', frappe._dict(dict(
doctype='Hub Item',
hub_category = category hub_category = category
), item_name) )), hub_item_code)
return response.ok
return response
@frappe.whitelist()
def send_review(hub_item_code, review):
review = json.loads(review)
hub_connection = get_hub_connection()
item_doc = hub_connection.connection.get_doc('Hub Item', hub_item_code)
existing_reviews = item_doc.get('reviews')
reviews = [review]
review.setdefault('idx', 0)
for r in existing_reviews:
if r.get('user') != review.get('user'):
reviews.append(r)
response = hub_connection.update('Hub Item', dict(
doctype='Hub Item',
reviews = reviews
), hub_item_code)
return response
@frappe.whitelist() @frappe.whitelist()
def get_details(hub_sync_id=None, doctype='Hub Item'): def get_details(hub_sync_id=None, doctype='Hub Item'):
@ -53,6 +134,11 @@ def get_details(hub_sync_id=None, doctype='Hub Item'):
return return
connection = get_client_connection() connection = get_client_connection()
details = connection.get_doc(doctype, hub_sync_id) details = connection.get_doc(doctype, hub_sync_id)
reviews = details.get('reviews')
if reviews and len(reviews):
for r in reviews:
r.setdefault('pretty_date', frappe.utils.pretty_date(r.get('modified')))
details.setdefault('reviews', reviews)
return details return details
def get_client_connection(): def get_client_connection():

View File

@ -4,10 +4,6 @@
"doctype": "Data Migration Plan", "doctype": "Data Migration Plan",
"idx": 1, "idx": 1,
"mappings": [ "mappings": [
{
"enabled": 1,
"mapping": "Company to Hub Company"
},
{ {
"enabled": 1, "enabled": 1,
"mapping": "Item to Hub Item" "mapping": "Item to Hub Item"

View File

@ -6,20 +6,33 @@ frappe.ui.form.on("Hub Settings", {
})); }));
frm.trigger("enabled"); frm.trigger("enabled");
if (frm.doc.enabled) {
frm.add_custom_button(__('View Hub'), frm.add_custom_button(__('View Hub'),
() => frappe.set_route('hub')); () => frappe.set_route('Hub', 'Item'));
if (frm.doc.enabled) {
frm.add_custom_button(__('Sync'), frm.add_custom_button(__('Sync'),
() => frm.call('sync')); () => frm.call('sync'));
} }
}, },
onload: function(frm) { onload: function(frm) {
let token = frappe.urllib.get_arg("access_token");
if(token) {
let email = frm.get_field("user");
console.log('token', frappe.urllib.get_arg("access_token"));
get_user_details(frm, token, email);
let row = frappe.model.add_child(frm.doc, "Hub Users", "users");
row.user = frappe.session.user;
}
if(!frm.doc.country) { if(!frm.doc.country) {
frm.set_value("country", frappe.defaults.get_default("Country")); frm.set_value("country", frappe.defaults.get_default("Country"));
} }
if(!frm.doc.company) { if(!frm.doc.company) {
frm.set_value("company", frappe.defaults.get_default("Company")); frm.set_value("company", frappe.defaults.get_default("Company"));
} }
if(!frm.doc.user) {
frm.set_value("user", frappe.session.user);
}
}, },
onload_post_render: function(frm) { onload_post_render: function(frm) {
if(frm.get_field("unregister_from_hub").$input) if(frm.get_field("unregister_from_hub").$input)
@ -48,7 +61,35 @@ frappe.ui.form.on("Hub Settings", {
if(frappe.session.user === "Administrator") { if(frappe.session.user === "Administrator") {
frappe.msgprint(__("Please login as another user.")) frappe.msgprint(__("Please login as another user."))
} else { } else {
frappe.verify_password(() => { // frappe.verify_password(() => {
// } );
frm.trigger("call_pre_reg");
// frm.trigger("call_register");
}
});
},
call_pre_reg: (frm) => {
this.frm.call({
doc: this.frm.doc,
method: "pre_reg",
args: {},
freeze: true,
callback: function(r) {
console.log(r.message);
authorize(frm, r.message.client_id, r.message.redirect_uri);
},
onerror: function() {
frappe.msgprint(__("Wrong Password"));
frm.set_value("enabled", 0);
}
});
},
call_register: (frm) => {
this.frm.call({ this.frm.call({
doc: this.frm.doc, doc: this.frm.doc,
method: "register", method: "register",
@ -60,22 +101,8 @@ frappe.ui.form.on("Hub Settings", {
frm.set_value("enabled", 0); frm.set_value("enabled", 0);
} }
}); });
} );
}
});
}, },
// update_hub: (frm) => {
// this.frm.call({
// doc: this.frm.doc,
// method: "update_hub",
// args: {},
// freeze: true,
// callback: function(r) { },
// onerror: function() { }
// });
// },
unregister_from_hub: (frm) => { unregister_from_hub: (frm) => {
frappe.verify_password(() => { frappe.verify_password(() => {
var d = frappe.confirm(__('Are you sure you want to unregister?'), () => { var d = frappe.confirm(__('Are you sure you want to unregister?'), () => {
@ -85,3 +112,67 @@ frappe.ui.form.on("Hub Settings", {
}); });
}, },
}); });
// let hub_url = 'https://hubmarket.org'
let hub_url = 'http://159.89.175.122'
// let hub_url = 'http://erpnext.hub:8000'
function authorize(frm, client_id, redirect_uri) {
// queryStringData is details of OAuth Client (Implicit Grant) on Custom App
var queryStringData = {
response_type : "token",
client_id : client_id,
redirect_uri : redirect_uri
}
// Get current raw route and build url
const route = "/desk#" + frappe.get_raw_route_str();
localStorage.removeItem("route"); // Clear previously set route if any
localStorage.setItem("route", route);
// Go authorize!
let api_route = "/api/method/frappe.integrations.oauth2.authorize?";
let url = hub_url + api_route + $.param(queryStringData);
window.location.replace(url, 'test');
}
function get_user_details(frm, token, email) {
console.log('user_details');
var route = localStorage.getItem("route");
if (token && route) {
// Clean up access token from route
frappe.set_route(frappe.get_route().join("/"))
// query protected resource e.g. Hub Items with token
var call = {
"async": true,
"crossDomain": true,
"url": hub_url + "/api/resource/User",
"method": "GET",
"data": {
// "email": email,
"fields": '["name", "first_name", "language"]',
"limit_page_length": 1
},
"headers": {
"authorization": "Bearer " + token,
"content-type": "application/x-www-form-urlencoded"
}
}
$.ajax(call).done(function (response) {
// display openid profile
console.log('response', response);
let data = response.data[0];
frm.set_value("enabled", 1);
frm.set_value("hub_username", data.first_name);
frm.set_value("hub_user_status", "Starter");
frm.set_value("language", data.language);
frm.save();
// clear route from localStorage
localStorage.removeItem("route");
});
}
}

View File

@ -40,6 +40,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -70,6 +71,39 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "enabled",
"fieldname": "hub_username",
"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": "Hub Username",
"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 "unique": 0
}, },
{ {
@ -101,6 +135,102 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_0",
"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,
"label": "",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "enabled",
"fieldname": "hub_user_status",
"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": "Status",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "enabled",
"fieldname": "language",
"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": "Language",
"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 "unique": 0
}, },
{ {
@ -133,6 +263,38 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company_registered",
"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": "Company Registered",
"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 "unique": 0
}, },
{ {
@ -164,6 +326,38 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company_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": "Company Email",
"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 "unique": 0
}, },
{ {
@ -195,6 +389,38 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company_logo",
"fieldtype": "Attach Image",
"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": "Company Logo",
"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 "unique": 0
}, },
{ {
@ -225,6 +451,70 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "users_sb",
"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": "Enabled Users",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "users",
"fieldtype": "Table",
"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": "Users",
"length": 0,
"no_copy": 0,
"options": "Hub Users",
"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 "unique": 0
}, },
{ {
@ -256,6 +546,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -286,6 +577,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -317,6 +609,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -349,6 +642,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -380,6 +674,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -411,6 +706,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -418,6 +714,39 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"default": "",
"depends_on": "eval:1",
"fieldname": "custom_data",
"fieldtype": "Code",
"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": "Custom Data",
"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_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "", "collapsible_depends_on": "",
"columns": 0, "columns": 0,
"depends_on": "enabled", "depends_on": "enabled",
@ -443,6 +772,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -473,6 +803,7 @@
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
} }
], ],
@ -486,8 +817,8 @@
"issingle": 1, "issingle": 1,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-09-21 12:13:50.841646", "modified": "2018-03-26 00:55:17.929140",
"modified_by": "manas@erpnext.com", "modified_by": "test1@example.com",
"module": "Hub Node", "module": "Hub Node",
"name": "Hub Settings", "name": "Hub Settings",
"name_case": "", "name_case": "",

View File

@ -11,6 +11,21 @@ from erpnext.utilities.product import get_price, get_qty_in_stock
from six import string_types from six import string_types
hub_url = "https://hubmarket.org" hub_url = "https://hubmarket.org"
# hub_url = "http://159.89.175.122"
# hub_url = "http://erpnext.hub:8000"
class OAuth2Session():
def __init__(self, headers):
self.headers = headers
def get(self, url, params, headers, verify):
res = requests.get(url, params=params, headers=self.headers, verify=verify)
return res
def post(self, url, data, verify):
res = requests.post(url, data=data, headers=self.headers, verify=verify)
return res
def put(self, url, data, verify):
res = requests.put(url, data=data, headers=self.headers, verify=verify)
return res
class HubSetupError(frappe.ValidationError): pass class HubSetupError(frappe.ValidationError): pass
@ -35,6 +50,33 @@ class HubSettings(Document):
doc.run() doc.run()
def pre_reg(self):
site_name = frappe.local.site + ':' + str(frappe.conf.webserver_port)
protocol = 'http://'
route = '/token'
data = {
'site_name': site_name,
'protocol': protocol,
'route': route
}
redirect_url = protocol + site_name + route
post_url = hub_url + '/api/method/hub.hub.api.pre_reg'
response = requests.post(post_url, data=data)
response.raise_for_status()
message = response.json().get('message')
if message and message.get('client_id'):
print("======CLIENT_ID======")
print(message.get('client_id'))
return {
'client_id': message.get('client_id'),
'redirect_uri': redirect_url
}
def register(self): def register(self):
""" Create a User on hub.erpnext.org and return username/password """ """ Create a User on hub.erpnext.org and return username/password """
data = { data = {

View File

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

View File

@ -0,0 +1,93 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-03-18 09:33:50.267762",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item_code",
"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": "Item Code",
"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": 1,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-03-18 09:34:01.757713",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "Hub Tracked Item",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 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": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 1,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,72 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-03-06 04:38:49.891787",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "user",
"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": "User",
"length": 0,
"no_copy": 0,
"options": "User",
"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,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-03-06 04:41:17.916243",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "Hub Users",
"name_case": "",
"owner": "test1@example.com",
"permissions": [],
"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
}

View File

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

View File

View File

@ -515,3 +515,5 @@ erpnext.patches.v10_0.update_hub_connector_domain
erpnext.patches.v10_0.set_student_party_type erpnext.patches.v10_0.set_student_party_type
erpnext.patches.v10_0.update_project_in_sle erpnext.patches.v10_0.update_project_in_sle
erpnext.patches.v10_0.fix_reserved_qty_for_sub_contract erpnext.patches.v10_0.fix_reserved_qty_for_sub_contract
erpnext.patches.v11_0.add_index_on_nestedset_doctypes
erpnext.patches.v11_0.remove_modules_setup_page

View File

@ -0,0 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
def execute():
frappe.reload_doc("agriculture", "doctype", "land_unit")
for dt in ("Account", "Cost Center", "File", "Employee", "Land Unit", "Task", "Customer Group", "Sales Person", "Territory"):
frappe.get_doc("DocType", dt).run_module_method("on_doctype_update")

View File

@ -0,0 +1,8 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
frappe.delete_doc("Page", "modules_setup")

View File

@ -15,7 +15,8 @@ def update_hr_permissions():
# add set user permissions rights to HR Manager # add set user permissions rights to HR Manager
frappe.db.sql("""update `tabDocPerm` set `set_user_permissions`=1 where parent in ('Employee', 'Leave Application') frappe.db.sql("""update `tabDocPerm` set `set_user_permissions`=1 where parent in ('Employee', 'Leave Application')
and role='HR Manager' and permlevel=0 and `read`=1""") and role='HR Manager' and permlevel=0 and `read`=1""")
docperm_meta = frappe.get_meta('DocPerm')
if docperm_meta.get_field('apply_user_permissions'):
# apply user permissions on Employee and Leave Application # apply user permissions on Employee and Leave Application
frappe.db.sql("""update `tabDocPerm` set `apply_user_permissions`=1 where parent in ('Employee', 'Leave Application') frappe.db.sql("""update `tabDocPerm` set `apply_user_permissions`=1 where parent in ('Employee', 'Leave Application')
and role in ('Employee', 'Leave Approver') and permlevel=0 and `read`=1""") and role in ('Employee', 'Leave Approver') and permlevel=0 and `read`=1""")

View File

@ -236,3 +236,6 @@ def add_multiple_tasks(data, parent):
new_doc['subject'] = d.get("subject") new_doc['subject'] = d.get("subject")
new_task = frappe.get_doc(new_doc) new_task = frappe.get_doc(new_doc)
new_task.insert() new_task.insert()
def on_doctype_update():
frappe.db.add_index("Task", ["lft", "rgt"])

View File

@ -53,16 +53,6 @@ frappe.ui.form.on("Timesheet", {
if (frm.doc.docstatus < 1) { if (frm.doc.docstatus < 1) {
$.each(frm.doc.time_logs || [], function(i, row) {
if(row.from_time && !row.completed) {
if (row.to_time && frappe.datetime.now_datetime() > row.to_time) {
frappe.utils.play_sound("alert");
frappe.msgprint(__(`Timer exceeded the expected hours for activity ${row.activity_type} in row ${row.idx}.`));
}
}
frm.refresh_fields();
});
let button = 'Start Timer'; let button = 'Start Timer';
$.each(frm.doc.time_logs || [], function(i, row) { $.each(frm.doc.time_logs || [], function(i, row) {
if ((row.from_time <= frappe.datetime.now_datetime()) && !row.completed) { if ((row.from_time <= frappe.datetime.now_datetime()) && !row.completed) {
@ -72,8 +62,16 @@ frappe.ui.form.on("Timesheet", {
frm.add_custom_button(__(button), function() { frm.add_custom_button(__(button), function() {
var flag = true; var flag = true;
// Fetch the row for timer where activity is not completed and from_time is not <= now_time
$.each(frm.doc.time_logs || [], function(i, row) { $.each(frm.doc.time_logs || [], function(i, row) {
// Fetch the row for which from_time is not present
if (flag && row.activity_type && !row.from_time){
erpnext.timesheet.timer(frm, row);
row.from_time = frappe.datetime.now_datetime();
frm.refresh_fields("time_logs");
frm.save();
flag = false;
}
// Fetch the row for timer where activity is not completed and from_time is before now_time
if (flag && row.from_time <= frappe.datetime.now_datetime() && !row.completed) { if (flag && row.from_time <= frappe.datetime.now_datetime() && !row.completed) {
let timestamp = moment(frappe.datetime.now_datetime()).diff(moment(row.from_time),"seconds"); let timestamp = moment(frappe.datetime.now_datetime()).diff(moment(row.from_time),"seconds");
erpnext.timesheet.timer(frm, row, timestamp); erpnext.timesheet.timer(frm, row, timestamp);

View File

@ -1,6 +1,7 @@
{ {
"css/erpnext.css": [ "css/erpnext.css": [
"public/less/erpnext.less" "public/less/erpnext.less",
"public/less/hub.less"
], ],
"js/erpnext-web.min.js": [ "js/erpnext-web.min.js": [
"public/js/website_utils.js", "public/js/website_utils.js",

View File

@ -1,130 +0,0 @@
body[data-route^="Hub/"] .freeze .image-view-container .list-row-col {
background-color: #fafbfc;
color: #fafbfc;
}
body[data-route^="Hub/"] .freeze .image-view-container .placeholder-text {
color: #fafbfc;
}
body[data-route^="Hub/"] .freeze {
display: none;
}
body[data-route^="Hub/"] .image-view-container {
justify-content: space-around;
}
.img-wrapper {
border: 1px solid #d1d8dd;
border-radius: 3px;
padding: 12px;
overflow: hidden;
text-align: center;
white-space: nowrap;
}
.img-wrapper .helper {
height: 100%;
display: inline-block;
vertical-align: middle;
}
/* hub */
div[data-page-route="hub"] .page-head {
height: 80px;
}
div[data-page-route="hub"] .page-head .title-text {
cursor: pointer;
}
div[data-page-route="hub"] .page-content {
margin-top: 80px;
}
div[data-page-route="hub"] .page-title h1 {
margin-bottom: 0px;
}
div[data-page-route="hub"] .account-details {
margin-top: 20px;
}
div[data-page-route="hub"] [data-original-title="Search"] {
float: right;
width: 220px;
}
div[data-page-route="hub"] .hub-main-section {
padding: 30px;
}
div[data-page-route="hub"] .listing-body {
margin: 0;
}
div[data-page-route="hub"] .main-list-section {
padding: 0;
}
div[data-page-route="hub"] .side-list-section {
padding: 0;
}
div[data-page-route="hub"] .item-list-header h3 {
font-weight: normal;
}
div[data-page-route="hub"] .hub-item-page h2 {
margin-top: 10px;
}
div[data-page-route="hub"] .hub-item-page .item-header {
display: flex;
}
div[data-page-route="hub"] .hub-item-page .item-page-image {
flex: 1;
}
div[data-page-route="hub"] .hub-item-page .title-content {
flex: 3;
}
div[data-page-route="hub"] .hub-item-page .title-content .description {
margin: 30px 0px;
}
div[data-page-route="hub"] .hub-item-page .title-content .actions {
margin-top: 30px;
}
div[data-page-route="hub"] .hub-item-page .title-content .actions .rfq-btn.disabled {
background-color: #b1bdca;
color: #fff;
border-color: #b1bdca;
}
div[data-page-route="hub"] .hub-item-page .company-items {
margin-top: 40px;
}
div[data-page-route="hub"] .company-header {
display: flex;
}
div[data-page-route="hub"] .item-list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
div[data-page-route="hub"] .hub-item-wrapper {
margin-bottom: 20px;
}
div[data-page-route="hub"] .img-wrapper {
border: 1px solid #d1d8dd;
border-radius: 3px;
padding: 12px;
overflow: hidden;
text-align: center;
white-space: nowrap;
}
div[data-page-route="hub"] .img-wrapper img {
max-width: 100%;
max-height: 100%;
display: inline-block;
vertical-align: middle;
}
div[data-page-route="hub"] .img-wrapper .helper {
height: 100%;
display: inline-block;
vertical-align: middle;
}
div[data-page-route="hub"] .img-wrapper .standard-image {
font-size: 72px;
border: none;
background-color: #fafbfc;
}
div[data-page-route="hub"] .hub-item-title {
width: 100%;
}
div[data-page-route="hub"] .breadcrumb {
padding-left: 0;
padding-top: 0;
margin-bottom: 10px;
}

View File

@ -1,76 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 87.312496 91.397155"
xmlns:cc="http://creativecommons.org/ns#" class="hub-icon"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" height="345.43808"
xmlns:svg="http://www.w3.org/2000/svg" width="330">
xmlns="http://www.w3.org/2000/svg" <g
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" transform="translate(121.51931,-138.66452)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="layer1">
version="1.1" <rect
id="svg3718" ry="13.229166"
xml:space="preserve" y="142.74918"
width="162.12097" x="-121.51931"
height="162.3004" height="87.3125"
viewBox="0 0 162.12098 162.3004" width="87.3125"
sodipodi:docname="hub_logo.svg" id="rect828"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"><metadata style="opacity:1;vector-effect:none;fill:#89da29;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
id="metadata3724"><rdf:RDF><cc:Work rx="13.229166" />
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type <path
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs id="path830"
id="defs3722"><clipPath d="m -121.51931,202.96343 v 13.86892 c 0,7.32897 5.90017,13.22917 13.22916,13.22917 h 60.854162 c 6.610072,0 12.056133,-4.80013 13.061216,-11.1187 -43.339761,0.1608 -54.359752,-16.03276 -87.144538,-15.97939 z"
clipPathUnits="userSpaceOnUse" clip-path="none"
id="clipPath3734"><path style="opacity:1;vector-effect:none;fill:#63c923;fill-opacity:1;stroke:none;stroke-width:3.96875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
d="M 0,600 H 1000 V 0 H 0 Z" <path
id="path3732" id="path832"
inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview d="m -34.20681,202.96343 c -32.784694,-0.0533 -43.804846,16.14019 -87.14455,15.97939 1.00509,6.31857 6.45115,11.1187 13.06122,11.1187 h 60.854164 c 7.328992,0 13.229166,-5.9002 13.229166,-13.22917 z"
pagecolor="#ffffff" clip-path="none"
bordercolor="#666666" style="opacity:1;vector-effect:none;fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:3.96875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
borderopacity="1" <path
objecttolerance="10" style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.79375005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
gridtolerance="10" d="m -84.351263,175.75725 c -1.30945,0 -2.376091,1.06665 -2.376091,2.37608 v 10.02885 0.001 c 0.06583,4.83083 4.01156,8.73477 8.857351,8.73486 4.8718,5e-5 8.846821,-3.94421 8.871295,-8.81134 v -0.001 -9.95288 c 0,-1.30943 -1.066113,-2.37557 -2.375589,-2.37557 -1.309396,0 -2.376064,1.06614 -2.376064,2.37557 v 9.8888 c 0,2.26045 -1.858169,4.10983 -4.119642,4.10983 -2.263616,0 -4.105699,-1.82766 -4.105699,-4.08968 v -9.90844 c 0,-1.30943 -1.066138,-2.37608 -2.375561,-2.37608 z m -20.887107,0.0925 c -1.30943,0 -2.37609,1.06717 -2.37609,2.3766 v 16.45119 c 0,1.30944 1.06666,2.37609 2.37609,2.37609 1.30945,0 2.37556,-1.06665 2.37556,-2.37609 v -5.97327 h 8.22534 v 5.97327 c 0,1.30944 1.066641,2.37609 2.376091,2.37609 1.309423,0 2.375561,-1.06665 2.375561,-2.37609 v -16.45119 c 0,-1.30943 -1.066138,-2.3766 -2.375561,-2.3766 -1.30945,0 -2.376091,1.06717 -2.376091,2.3766 v 5.72627 h -8.22534 v -5.72627 c 0,-1.30943 -1.06611,-2.3766 -2.37556,-2.3766 z m 41.77419,0 c -0.654712,0 -1.248675,0.26711 -1.678967,0.69764 -0.05368,0.0537 -0.105119,0.10983 -0.153458,0.16846 v 5.3e-4 c -0.04839,0.0586 -0.09427,0.11929 -0.136949,0.18242 v 5.3e-4 c -0.256381,0.37936 -0.406691,0.83617 -0.406691,1.32705 v 16.45119 c 0,0.1635 0.01693,0.3242 0.04858,0.47852 0.09512,0.46331 0.32594,0.87828 0.64852,1.20096 0.161369,0.16136 0.345308,0.29938 0.547264,0.40928 v 0 c 0.134567,0.0732 0.276781,0.13403 0.425318,0.18035 v 0 c 0.148537,0.0463 0.303186,0.0783 0.462518,0.0946 v 0 c 0.07959,0.008 0.160708,0.0124 0.242358,0.0124 h 8.33181 c 0.08747,0 0.167931,-0.0145 0.251142,-0.0238 l 0.09509,0.005 c 0.06019,0.003 0.119407,0.005 0.178779,0.006 h 0.0037 0.0048 c 3.578305,-2e-5 6.487954,-2.90916 6.487981,-6.48747 v -0.001 c -0.0026,-1.51334 -0.578009,-2.9475 -1.540484,-4.10673 0.962448,-1.15892 1.537785,-2.59314 1.540484,-4.10621 v -0.001 c -2.7e-5,-3.57831 -2.909676,-6.48744 -6.487981,-6.48746 h -0.533294 z m 8.865103,4.75062 c 0.96393,0 1.736831,0.77394 1.736831,1.73788 0,0.96394 -0.772901,1.73684 -1.736831,1.73684 v 0 h -0.532792 -5.955718 v -3.47317 h 5.956248 z m 0,8.21552 v 0 c 0.963507,5.3e-4 1.735799,0.77373 1.735799,1.73736 0,0.96394 -0.772901,1.73684 -1.736831,1.73684 h -0.0048 l -0.533294,0.0119 h -5.951591 v -3.4742 h 5.959846 z"
guidetolerance="10" id="path834" />
inkscape:pageopacity="0" <path
inkscape:pageshadow="2" id="path838"
inkscape:window-width="740" d="m -77.859375,138.66406 c -9.653316,0 -18.439915,3.93483 -24.767575,10.28125 a 3.9691471,3.9691471 0 1 0 5.621091,5.60352 c 4.899576,-4.9141 11.6422,-7.94727 19.146484,-7.94727 7.501101,0 14.241542,3.03098 19.140625,7.94141 a 3.9691471,3.9691471 0 1 0 5.619141,-5.60547 c -6.327038,-6.34169 -15.110547,-10.27344 -24.759766,-10.27344 z"
inkscape:window-height="449" style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#63c923;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.93750048;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
id="namedview3720" </g>
showgrid="false" </svg>
inkscape:zoom="1.1278353"
inkscape:cx="108.86803"
inkscape:cy="50.640564"
inkscape:window-x="605"
inkscape:window-y="98"
inkscape:window-maximized="0"
inkscape:current-layer="g3726"
fit-margin-top="10"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="10" /><g
id="g3726"
inkscape:groupmode="layer"
inkscape:label="hub logo"
transform="matrix(1.3333333,0,0,-1.3333333,-579.98759,522.65266)"><g
id="g3728"><g
id="g3730"
clip-path="url(#clipPath3734)"><g
id="g3740"
transform="translate(469.3467,296.2959)"><path
d="m 0,0 c -16.597,0 -31.587,12.581 -32.298,13.186 l -2.058,1.751 1.131,2.452 c 0.44,0.956 11.012,23.476 30.001,27.662 l 1.527,0.336 C 10.532,48.083 22.675,40.327 25.37,28.098 28.065,15.871 20.307,3.728 8.081,1.033 L 6.551,0.698 C 4.36,0.214 2.167,0 0,0"
style="fill:#6be273;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3742"
inkscape:connector-curvature="0" /></g><g
id="g3744"
transform="translate(519.9316,324.9663)"><path
d="m 0,0 c -8.052,0 -16.033,3.513 -21.457,10.266 -4.599,5.724 -6.689,12.896 -5.892,20.196 0.797,7.297 4.39,13.85 10.111,18.446 l 1.524,1.225 c 7.78,6.245 18.871,9.487 32.081,9.388 9.773,-0.076 17.263,-1.928 17.578,-2.006 l 2.621,-0.657 0.074,-2.699 C 36.675,52.862 37.313,22.21 18.708,7.269 L 17.184,6.044 C 12.118,1.977 6.044,0 0,0"
style="fill:#49c44c;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3746"
inkscape:connector-curvature="0" /></g><g
id="g3748"
transform="translate(533.21,367.6562)"><path
d="m 0,0 c -5.312,-1.335 -13.328,-3.353 -20.459,-7.646 -8.907,-5.364 -13.925,-12.827 -14.923,-22.183 l -0.02,-0.188 v -14.901 c -3.873,3.824 -8.159,6.202 -12.809,7.101 -12.143,2.341 -23.14,-5.967 -29.049,-10.433 l 4.296,-5.686 c 5.024,3.797 14.373,10.864 23.402,9.122 5.207,-1.005 9.965,-4.887 14.16,-11.54 v -33.538 h 7.128 v 59.49 c 1.878,16.413 20.17,21.016 30.01,23.492 z"
style="fill:#cef6d1;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3750"
inkscape:connector-curvature="0" /></g></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -172,17 +172,17 @@ agriculture.TernaryPlot = class TernaryPlot {
make_plot_marking() { make_plot_marking() {
let { triangle_side: t, spacing: s, scaling_factor: p } = this.config; let { triangle_side: t, spacing: s, scaling_factor: p } = this.config;
let clay = this.paper.text(t * Snap.cos(60) / 2, s + t * Snap.cos(30) / 2, "Clay").attr({ let clay = this.paper.text(t * Snap.cos(60) / 2, s + t * Snap.cos(30) / 2, __("Clay")).attr({
fill: frappe.ui.color.get('black') fill: frappe.ui.color.get('black')
}); });
clay.transform("r300"); clay.transform("r300");
let silt = this.paper.text(t, s + t * Snap.cos(30) / 2, "Silt").attr({ let silt = this.paper.text(t, s + t * Snap.cos(30) / 2, __("Silt")).attr({
fill: frappe.ui.color.get('black') fill: frappe.ui.color.get('black')
}); });
silt.transform("r60"); silt.transform("r60");
let sand = this.paper.text(35 + t * Snap.cos(60), 90 + t * Snap.cos(30), "Sand").attr({ let sand = this.paper.text(35 + t * Snap.cos(60), 90 + t * Snap.cos(30), __("Sand")).attr({
fill: frappe.ui.color.get('black') fill: frappe.ui.color.get('black')
}); });
sand.transform("r0"); sand.transform("r0");

View File

@ -106,7 +106,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
} }
if( if(
this.frm.fields_dict["payment_terms_template"] this.frm.docstatus < 2
&& this.frm.fields_dict["payment_terms_template"]
&& this.frm.fields_dict["payment_schedule"] && this.frm.fields_dict["payment_schedule"]
&& this.frm.doc.payment_terms_template && this.frm.doc.payment_terms_template
&& !this.frm.doc.payment_schedule.length && !this.frm.doc.payment_schedule.length

View File

@ -7,25 +7,33 @@ frappe.views.HubFactory = frappe.views.Factory.extend({
const assets = { const assets = {
'List': [ 'List': [
'/assets/erpnext/js/hub/hub_page.js', '/assets/erpnext/js/hub/hub_listing.js',
'/assets/erpnext/css/hub.css',
], ],
'Form': [ 'Form': [
'/assets/erpnext/js/hub/hub_form.js', '/assets/erpnext/js/hub/hub_form.js'
'/assets/erpnext/css/hub.css',
] ]
}; };
frappe.model.with_doc('Hub Settings', 'Hub Settings', () => { frappe.model.with_doc('Hub Settings', 'Hub Settings', () => {
this.hub_settings = frappe.get_doc('Hub Settings'); this.hub_settings = frappe.get_doc('Hub Settings');
if (!erpnext.hub.pages[page_name]) { if (!erpnext.hub.pages[page_name]) {
if(!frappe.is_online()) {
this.render_offline_card();
return;
}
if (!route[2]) { if (!route[2]) {
frappe.require(assets['List'], () => { frappe.require(assets['List'], () => {
if(page === 'Favourites') {
erpnext.hub.pages[page_name] = new erpnext.hub['Favourites']({
parent: this.make_page(true, page_name),
hub_settings: this.hub_settings
});
} else {
erpnext.hub.pages[page_name] = new erpnext.hub[page+'Listing']({ erpnext.hub.pages[page_name] = new erpnext.hub[page+'Listing']({
parent: this.make_page(true, page_name), parent: this.make_page(true, page_name),
hub_settings: this.hub_settings hub_settings: this.hub_settings
}); });
window.hub_page = erpnext.hub.pages[page_name]; }
}); });
} else { } else {
frappe.require(assets['Form'], () => { frappe.require(assets['Form'], () => {
@ -35,13 +43,29 @@ frappe.views.HubFactory = frappe.views.Factory.extend({
parent: this.make_page(true, page_name), parent: this.make_page(true, page_name),
hub_settings: this.hub_settings hub_settings: this.hub_settings
}); });
window.hub_page = erpnext.hub.pages[page_name];
}); });
} }
window.hub_page = erpnext.hub.pages[page_name];
} else { } else {
frappe.container.change_to(page_name); frappe.container.change_to(page_name);
window.hub_page = erpnext.hub.pages[page_name]; window.hub_page = erpnext.hub.pages[page_name];
} }
}); });
},
render_offline_card() {
let html = `<div class='page-card' style='margin: 140px auto;'>
<div class='page-card-head'>
<span class='indicator red'>${'Failed to connect'}</span>
</div>
<p>${ __("Please check your network connection.") }</p>
<div><a href='#Hub/Item' class='btn btn-primary btn-sm'>
${ __("Reload") }</a></div>
</div>`;
let page = $('#body_div');
page.append(html);
return;
} }
}); });

View File

@ -1,6 +1,6 @@
frappe.provide('erpnext.hub'); frappe.provide('erpnext.hub');
erpnext.hub.HubForm = class HubForm extends frappe.views.BaseList { erpnext.hub.HubDetailsPage = class HubDetailsPage extends frappe.views.BaseList {
setup_defaults() { setup_defaults() {
super.setup_defaults(); super.setup_defaults();
this.method = 'erpnext.hub_node.get_details'; this.method = 'erpnext.hub_node.get_details';
@ -8,6 +8,25 @@ erpnext.hub.HubForm = class HubForm extends frappe.views.BaseList {
this.page_name = route[2]; this.page_name = route[2];
} }
setup_fields() {
return this.get_meta()
.then(r => {
this.meta = r.message.meta || this.meta;
this.categories = r.message.categories || [];
this.bootstrap_data(r.message);
this.getFormFields();
});
}
bootstrap_data() { }
get_meta() {
return new Promise(resolve =>
frappe.call('erpnext.hub_node.get_meta', {doctype: 'Hub ' + this.doctype}, resolve));
}
set_breadcrumbs() { set_breadcrumbs() {
frappe.breadcrumbs.add({ frappe.breadcrumbs.add({
label: __('Hub'), label: __('Hub'),
@ -27,6 +46,45 @@ erpnext.hub.HubForm = class HubForm extends frappe.views.BaseList {
setup_sort_selector() { } setup_sort_selector() { }
// let category = this.quick_view.get_values().hub_category;
// return new Promise((resolve, reject) => {
// frappe.call({
// method: 'erpnext.hub_node.update_category',
// args: {
// hub_item_code: values.hub_item_code,
// category: category,
// },
// callback: (r) => {
// resolve();
// },
// freeze: true
// }).fail(reject);
// });
get_timeline() {
return `<div class="timeline">
<div class="timeline-head">
</div>
<div class="timeline-new-email">
<button class="btn btn-default btn-reply-email btn-xs">
${__("Reply")}
</button>
</div>
<div class="timeline-items"></div>
</div>`;
}
get_footer() {
return `<div class="form-footer">
<div class="after-save">
<div class="form-comments"></div>
</div>
<div class="pull-right scroll-to-top">
<a onclick="frappe.utils.scroll_to(0)"><i class="fa fa-chevron-up text-muted"></i></a>
</div>
</div>`;
}
get_args() { get_args() {
return { return {
hub_sync_id: this.unique_id, hub_sync_id: this.unique_id,
@ -48,108 +106,379 @@ erpnext.hub.HubForm = class HubForm extends frappe.views.BaseList {
<span class="helper"></span>` : <span class="helper"></span>` :
`<div class="standard-image">${frappe.get_abbr(this.page_title)}</div>`; `<div class="standard-image">${frappe.get_abbr(this.page_title)}</div>`;
this.sidebar.remove_item('image');
this.sidebar.add_item({ this.sidebar.add_item({
name: 'image',
label: image_html label: image_html
}); });
let fields = this.get_field_configs(); if(!this.form) {
let fields = this.formFields;
this.form = new frappe.ui.FieldGroup({ this.form = new frappe.ui.FieldGroup({
parent: this.$result, parent: this.$result,
fields fields
}); });
this.form.make(); this.form.make();
this.form.set_values(this.data);
} }
toggle_result_area() { if(this.data.hub_category) {
this.$result.toggle(this.unique_id); this.form.fields_dict.set_category.hide();
this.$paging_area.toggle(this.data.length > 0); }
this.$no_result.toggle(this.data.length == 0);
const show_more = (this.start + this.page_length) <= this.data.length; this.form.set_values(this.data);
this.$paging_area.find('.btn-more') this.$result.show();
.toggle(show_more);
this.$timelineList && this.$timelineList.empty();
if(this.data.reviews && this.data.reviews.length) {
this.data.reviews.map(review => {
this.addReviewToTimeline(review);
})
}
this.postRender()
}
postRender() {}
attachFooter() {
let footerHtml = `<div class="form-footer">
<div class="form-comments"></div>
<div class="pull-right scroll-to-top">
<a onclick="frappe.utils.scroll_to(0)"><i class="fa fa-chevron-up text-muted"></i></a>
</div>
</div>`;
let parent = $('<div>').appendTo(this.page.main.parent());
this.$footer = $(footerHtml).appendTo(parent);
}
attachTimeline() {
let timelineHtml = `<div class="timeline">
<div class="timeline-head">
</div>
<div class="timeline-new-email">
<button class="btn btn-default btn-reply-email btn-xs">
${ __("Reply") }
</button>
</div>
<div class="timeline-items"></div>
</div>`;
let parent = this.$footer.find(".form-comments");
this.$timeline = $(timelineHtml).appendTo(parent);
this.$timelineList = this.$timeline.find(".timeline-items");
}
attachReviewArea() {
this.comment_area = new frappe.ui.ReviewArea({
parent: this.$footer.find('.timeline-head'),
mentions: [],
on_submit: (val) => {
val.user = frappe.session.user;
val.username = frappe.session.user_fullname;
frappe.call({
method: 'erpnext.hub_node.send_review',
args: {
hub_item_code: this.data.hub_item_code,
review: val
},
callback: (r) => {
this.refresh();
this.comment_area.reset();
},
freeze: true
});
}
});
}
addReviewToTimeline(data) {
let username = data.username || data.user || __("Anonymous")
let imageHtml = data.user_image
? `<div class="avatar-frame" style="background-image: url(${data.user_image})"></div>`
: `<div class="standard-image" style="background-color: #fafbfc">${frappe.get_abbr(username)}</div>`
let editHtml = data.own
? `<div class="pull-right hidden-xs close-btn-container">
<span class="small text-muted">
${'data.delete'}
</span>
</div>
<div class="pull-right edit-btn-container">
<span class="small text-muted">
${'data.edit'}
</span>
</div>`
: '';
let ratingHtml = '';
for(var i = 0; i < 5; i++) {
let starIcon = 'fa-star-o'
if(i < data.rating) {
starIcon = 'fa-star';
}
ratingHtml += `<i class="fa fa-fw ${starIcon} star-icon" data-idx='${i}'></i>`;
}
$(this.getTimelineItem(data, imageHtml, editHtml, ratingHtml))
.appendTo(this.$timelineList);
}
getTimelineItem(data, imageHtml, editHtml, ratingHtml) {
return `<div class="media timeline-item user-content" data-doctype="${''}" data-name="${''}">
<span class="pull-left avatar avatar-medium hidden-xs" style="margin-top: 1px">
${imageHtml}
</span>
<div class="pull-left media-body">
<div class="media-content-wrapper">
<div class="action-btns">${editHtml}</div>
<div class="comment-header clearfix small ${'linksActive'}">
<span class="pull-left avatar avatar-small visible-xs">
${imageHtml}
</span>
<div class="asset-details">
<span class="author-wrap">
<i class="octicon octicon-quote hidden-xs fa-fw"></i>
<span>${data.username}</span>
</span>
<a href="#Form/${''}" class="text-muted">
<span class="text-muted hidden-xs">&ndash;</span>
<span class="indicator-right ${'green'}
delivery-status-indicator">
<span class="hidden-xs">${data.pretty_date}</span>
</span>
</a>
<a class="text-muted reply-link pull-right timeline-content-show"
title="${__('Reply')}"> ${''} </a>
<span class="comment-likes hidden-xs">
<i class="octicon octicon-heart like-action text-extra-muted not-liked fa-fw">
</i>
<span class="likes-count text-muted">10</span>
</span>
</div>
</div>
<div class="reply timeline-content-show">
<div class="timeline-item-content">
<p class="text-muted small">
<b>${data.subject}</b>
</p>
<hr>
<p class="text-muted small">
${ratingHtml}
</p>
<hr>
<p>
${data.content}
</p>
</div>
</div>
</div>
</div>
</div>`;
}
prepareFormFields(fields, fieldnames) {
return fields
.filter(field => fieldnames.includes(field.fieldname))
.map(field => {
let {
label,
fieldname,
fieldtype,
} = field;
let read_only = 1;
return {
label,
fieldname,
fieldtype,
read_only,
};
});
} }
}; };
erpnext.hub.ItemPage = class ItemPage extends erpnext.hub.HubForm{ erpnext.hub.ItemPage = class ItemPage extends erpnext.hub.HubDetailsPage {
constructor(opts) {
super(opts);
this.show();
}
setup_defaults() { setup_defaults() {
super.setup_defaults(); super.setup_defaults();
this.doctype = 'Item'; this.doctype = 'Item';
this.image_field_name = 'image'; this.image_field_name = 'image';
} }
get_field_configs() { setup_page_head() {
let fields = []; super.setup_page_head();
this.fields.map(fieldname => { this.set_primary_action();
fields.push({
label: toTitle(frappe.model.unscrub(fieldname)),
fieldname,
fieldtype: 'Data',
read_only: 1
});
});
let category_field = {
label: 'Hub Category',
fieldname: 'hub_category',
fieldtype: 'Data'
} }
if(this.data.company_name === this.hub_settings.company) { setup_side_bar() {
this.page.set_primary_action(__('Update'), () => { this.setup_side_bar();
this.update_on_hub(); this.attachFooter();
}, 'octicon octicon-plus'); this.attachTimeline();
this.attachReviewArea();
}
set_primary_action() {
let item = this.data;
this.page.set_primary_action(__('Request a Quote'), () => {
this.show_rfq_modal()
.then(values => {
item.item_code = values.item_code;
delete values.item_code;
const supplier = values;
return [item, supplier];
})
.then(([item, supplier]) => {
return this.make_rfq(item, supplier, this.page.btn_primary);
})
.then(r => {
console.log(r);
if (r.message && r.message.rfq) {
this.page.btn_primary.addClass('disabled').html(`<span><i class='fa fa-check'></i> ${__('Quote Requested')}</span>`);
} else { } else {
category_field.read_only = 1; throw r;
}
})
.catch((e) => {
console.log(e); //eslint-disable-line
});
}, 'octicon octicon-plus');
} }
fields.unshift(category_field); make_rfq(item, supplier, btn) {
console.log(supplier);
return fields;
}
update_on_hub() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
frappe.call({ frappe.call({
method: 'erpnext.hub_node.update_category', method: 'erpnext.hub_node.make_rfq_and_send_opportunity',
args: { item: this.unique_id, category: this.form.get_value('hub_category') }, args: { item, supplier },
callback: resolve, callback: resolve,
freeze: true btn,
}).fail(reject); }).fail(reject);
}); });
} }
setup_fields() { postRender() {
this.fields = ['hub_item_code', 'item_name', 'item_code', 'description', this.categoryDialog = new frappe.ui.Dialog({
'seller', 'company_name', 'country']; title: __('Suggest Category'),
fields: [
{
label: __('Category'),
fieldname: 'category',
fieldtype: 'Autocomplete',
options: this.categories,
reqd: 1
}
],
primary_action_label: __("Send"),
primary_action: () => {
let values = this.categoryDialog.get_values();
frappe.call({
method: 'erpnext.hub_node.update_category',
args: {
hub_item_code: this.data.hub_item_code,
category: values.category
},
callback: () => {
this.categoryDialog.hide();
this.refresh();
},
freeze: true
}).fail(() => {});
}
});
}
getFormFields() {
let colOneFieldnames = ['item_name', 'item_code', 'description'];
let colTwoFieldnames = ['seller', 'company_name', 'country'];
let colOneFields = this.prepareFormFields(this.meta.fields, colOneFieldnames);
let colTwoFields = this.prepareFormFields(this.meta.fields, colTwoFieldnames);
let miscFields = [
{
label: __('Category'),
fieldname: 'hub_category',
fieldtype: 'Data',
read_only: 1
},
{
label: __('Suggest Category?'),
fieldname: 'set_category',
fieldtype: 'Button',
click: () => {
this.categoryDialog.show();
}
},
{
fieldname: 'cb1',
fieldtype: 'Column Break'
}
];
this.formFields = colOneFields.concat(miscFields, colTwoFields);
}
show_rfq_modal() {
let item = this.data;
return new Promise(res => {
let fields = [
{ label: __('Item Code'), fieldtype: 'Data', fieldname: 'item_code', default: item.item_code },
{ fieldtype: 'Column Break' },
{ label: __('Item Group'), fieldtype: 'Link', fieldname: 'item_group', default: item.item_group },
{ label: __('Supplier Details'), fieldtype: 'Section Break' },
{ label: __('Supplier Name'), fieldtype: 'Data', fieldname: 'supplier_name', default: item.company_name },
{ label: __('Supplier Email'), fieldtype: 'Data', fieldname: 'supplier_email', default: item.seller },
{ fieldtype: 'Column Break' },
{ label: __('Supplier Type'), fieldname: 'supplier_type',
fieldtype: 'Link', options: 'Supplier Type' }
];
fields = fields.map(f => { f.reqd = 1; return f; });
const d = new frappe.ui.Dialog({
title: __('Request for Quotation'),
fields: fields,
primary_action_label: __('Send'),
primary_action: (values) => {
res(values);
d.hide();
}
});
d.show();
});
} }
} }
erpnext.hub.CompanyPage = class CompanyPage extends erpnext.hub.HubForm{ erpnext.hub.CompanyPage = class CompanyPage extends erpnext.hub.HubDetailsPage {
constructor(opts) {
super(opts);
this.show();
}
setup_defaults() { setup_defaults() {
super.setup_defaults(); super.setup_defaults();
this.doctype = 'Company'; this.doctype = 'Company';
this.image_field_name = 'company_logo'; this.image_field_name = 'company_logo';
} }
get_field_configs() { getFormFields() {
let fields = []; let fieldnames = ['company_name', 'description', 'route', 'country', 'seller', 'site_name'];;
this.fields.map(fieldname => { this.formFields = this.prepareFormFields(this.meta.fields, fieldnames);
fields.push({
label: toTitle(frappe.model.unscrub(fieldname)),
fieldname,
fieldtype: 'Data',
read_only: 1
});
});
return fields;
}
setup_fields() {
this.fields = ['company_name', 'description', 'route', 'country', 'seller', 'site_name'];
} }
} }

View File

@ -0,0 +1,726 @@
frappe.provide('erpnext.hub');
erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList {
setup_defaults() {
super.setup_defaults();
this.page_title = __('');
this.method = 'erpnext.hub_node.get_list';
this.cache = {};
const route = frappe.get_route();
this.page_name = route[1];
this.menu_items = this.menu_items.concat(this.get_menu_items());
this.imageFieldName = 'image';
this.show_filters = 0;
}
set_title() {
const title = this.page_title;
let iconHtml = `<img class="hub-icon" src="assets/erpnext/images/hub_logo.svg">`;
let titleHtml = `<span class="hub-page-title">${title}</span>`;
this.page.set_title(iconHtml + titleHtml, '', false, title);
}
setup_fields() {
return this.get_meta()
.then(r => {
this.meta = r.message.meta || this.meta;
frappe.model.sync(this.meta);
this.bootstrap_data(r.message);
this.prepareFormFields();
});
}
get_meta() {
return new Promise(resolve =>
frappe.call('erpnext.hub_node.get_meta', {doctype: this.doctype}, resolve));
}
set_breadcrumbs() { }
prepareFormFields() { }
bootstrap_data() { }
get_menu_items() {
const items = [
{
label: __('Hub Settings'),
action: () => frappe.set_route('Form', 'Hub Settings'),
standard: true
},
{
label: __('Favourites'),
action: () => frappe.set_route('Hub', 'Favourites'),
standard: true
},
// {
// label: __('Toggle Sidebar'),
// action: () => this.toggle_side_bar(),
// standard: true
// }
];
return items;
}
setup_side_bar() {
this.sidebar = new frappe.ui.Sidebar({
wrapper: this.page.wrapper.find('.layout-side-section'),
css_class: 'hub-sidebar'
});
}
setup_sort_selector() {
this.sort_selector = new frappe.ui.SortSelector({
parent: this.filter_area.$filter_list_wrapper,
doctype: this.doctype,
args: this.order_by,
onchange: () => this.refresh(true)
});
}
setup_view() { }
get_args() {
return {
doctype: this.doctype,
start: this.start,
limit: this.page_length,
order_by: this.order_by,
// fields: this.fields,
filters: this.get_filters_for_args()
};
}
update_data(r) {
const data = r.message;
if (this.start === 0) {
this.data = data;
} else {
this.data = this.data.concat(data);
}
this.data_dict = {};
}
freeze(toggle) {
// if(!this.$freeze) return;
// this.$freeze.toggle(toggle);
// if (this.$freeze.find('.image-view-container').length) return;
// const html = Array.from(new Array(4)).map(d => this.card_html({
// name: 'Loading...',
// item_name: 'Loading...'
// })).join('');
// this.$freeze.html(`<div class="image-view-container border-top">${html}</div>`);
}
render() {
this.data_dict = {};
this.render_image_view();
this.setup_quick_view();
this.setup_like();
}
render_offline_card() {
let html = `<div class='page-card'>
<div class='page-card-head'>
<span class='indicator red'>
{{ _("Payment Cancelled") }}</span>
</div>
<p>${ __("Your payment is cancelled.") }</p>
<div><a href='' class='btn btn-primary btn-sm'>
${ __("Continue") }</a></div>
</div>`;
let page = this.page.wrapper.find('.layout-side-section')
page.append(html);
return;
}
render_image_view() {
var html = this.data.map(this.item_html.bind(this)).join("");
if (this.start === 0) {
// ${this.getHeaderHtml()}
this.$result.html(`
<div class="image-view-container small">
${html}
</div>
`);
}
if(this.data.length) {
this.doc = this.data[0];
}
this.data.map(this.loadImage.bind(this));
this.data_dict = {};
this.data.map(d => {
this.data_dict[d.hub_item_code] = d;
});
}
getHeaderHtml(title, image, content) {
// let company_html =
return `
<header class="list-row-head text-muted small">
<div style="display: flex;">
<div class="list-header-icon">
<img title="${title}" alt="${title}" src="${image}">
</div>
<div class="list-header-info">
<h5>
${title}
</h5>
<span class="margin-vertical-10 level-item">
${content}
</span>
</div>
</div>
</header>
`;
}
renderHeader() {
return `<header class="level list-row-head text-muted small">
<div class="level-left list-header-subject">
<div class="list-row-col list-subject level ">
<img title="Riadco%20Group" alt="Riadco Group" src="https://cdn.pbrd.co/images/HdaPxcg.png">
<span class="level-item">Products by Blah blah</span>
</div>
</div>
<div class="level-left checkbox-actions">
<div class="level list-subject">
<input class="level-item list-check-all hidden-xs" type="checkbox" title="${__("Select All")}">
<span class="level-item list-header-meta"></span>
</div>
</div>
<div class="level-right">
${''}
</div>
</header>`;
}
get_image_html(encoded_name, src, alt_text) {
return `<img data-name="${encoded_name}" src="${ src }" alt="${ alt_text }">`;
}
get_image_placeholder(title) {
return `<span class="placeholder-text">${ frappe.get_abbr(title) }</span>`;
}
loadImage(item) {
item._name = encodeURI(item.name);
const encoded_name = item._name;
const title = strip_html(item[this.meta.title_field || 'name']);
let placeholder = this.get_image_placeholder(title);
let $container = this.$result.find(`.image-field[data-name="${encoded_name}"]`);
if(!item[this.imageFieldName]) {
$container.prepend(placeholder);
$container.addClass('no-image');
}
frappe.load_image(item[this.imageFieldName],
(imageObj) => {
$container.prepend(imageObj)
},
() => {
$container.prepend(placeholder);
$container.addClass('no-image');
},
(imageObj) => {
imageObj.title = encoded_name;
imageObj.alt = title;
}
)
}
setup_quick_view() {
if(this.quick_view) return;
this.quick_view = new frappe.ui.Dialog({
title: 'Quick View',
fields: this.formFields
});
this.quick_view.set_primary_action(__('Request a Quote'), () => {
this.show_rfq_modal()
.then(values => {
item.item_code = values.item_code;
delete values.item_code;
const supplier = values;
return [item, supplier];
})
.then(([item, supplier]) => {
return this.make_rfq(item, supplier, this.page.btn_primary);
})
.then(r => {
console.log(r);
if (r.message && r.message.rfq) {
this.page.btn_primary.addClass('disabled').html(`<span><i class='fa fa-check'></i> ${__('Quote Requested')}</span>`);
} else {
throw r;
}
})
.catch((e) => {
console.log(e); //eslint-disable-line
});
}, 'octicon octicon-plus');
this.$result.on('click', '.btn.zoom-view', (e) => {
e.preventDefault();
e.stopPropagation();
var name = $(e.target).attr('data-name');
name = decodeURIComponent(name);
this.quick_view.set_title(name);
let values = this.data_dict[name];
this.quick_view.set_values(values);
let fields = [];
this.quick_view.show();
return false;
});
}
setup_like() {
if(this.setup_like_done) return;
this.setup_like_done = 1;
this.$result.on('click', '.btn.like-button', (e) => {
if($(e.target).hasClass('changing')) return;
$(e.target).addClass('changing');
e.preventDefault();
e.stopPropagation();
var name = $(e.target).attr('data-name');
name = decodeURIComponent(name);
let values = this.data_dict[name];
let heart = $(e.target);
if(heart.hasClass('like-button')) {
heart = $(e.target).find('.octicon');
}
let remove = 1;
if(heart.hasClass('liked')) {
// unlike
heart.removeClass('liked');
} else {
// like
remove = 0;
heart.addClass('liked');
}
frappe.call({
method: 'erpnext.hub_node.update_wishlist_item',
args: {
item_name: values.hub_item_code,
remove: remove
},
callback: (r) => {
let message = __("Added to Favourites");
if(remove) {
message = __("Removed from Favourites");
}
frappe.show_alert(message);
},
freeze: true
});
$(e.target).removeClass('changing');
return false;
});
}
}
erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing {
constructor(opts) {
super(opts);
this.show();
}
setup_defaults() {
super.setup_defaults();
this.doctype = 'Hub Item';
this.page_title = __('Products');
this.fields = ['name', 'hub_item_code', 'image', 'item_name', 'item_code', 'company_name', 'description', 'country'];
this.filters = [];
}
render() {
this.data_dict = {};
this.render_image_view();
this.setup_quick_view();
this.setup_like();
}
bootstrap_data(response) {
let companies = response.companies.map(d => d.name);
this.custom_filter_configs = [
{
fieldtype: 'Autocomplete',
label: __('Select Company'),
condition: 'like',
fieldname: 'company_name',
options: companies
},
{
fieldtype: 'Link',
label: __('Select Country'),
options: 'Country',
condition: 'like',
fieldname: 'country'
}
];
}
prepareFormFields() {
let fieldnames = ['item_name', 'description', 'company_name', 'country'];
this.formFields = this.meta.fields
.filter(field => fieldnames.includes(field.fieldname))
.map(field => {
let {
label,
fieldname,
fieldtype,
} = field;
let read_only = 1;
return {
label,
fieldname,
fieldtype,
read_only,
};
});
this.formFields.unshift({
label: 'image',
fieldname: 'image',
fieldtype: 'Attach Image'
});
}
setup_side_bar() {
super.setup_side_bar();
let $pitch = $(`<div class="border" style="
margin-top: 10px;
padding: 0px 10px;
border-radius: 3px;
">
<h5>Sell on HubMarket</h5>
<p>Over 2000 products listed. Register your company to start selling.</p>
</div>`);
this.sidebar.$sidebar.append($pitch);
this.category_tree = new frappe.ui.Tree({
parent: this.sidebar.$sidebar,
label: 'All Categories',
expandable: true,
args: {parent: this.current_category},
method: 'erpnext.hub_node.get_categories',
on_click: (node) => {
this.update_category(node.label);
}
});
this.sidebar.add_item({
label: __('Companies'),
on_click: () => frappe.set_route('Hub', 'Company')
}, undefined, true);
this.sidebar.add_item({
label: this.hub_settings.company,
on_click: () => frappe.set_route('Form', 'Company', this.hub_settings.company)
}, __("Account"));
this.sidebar.add_item({
label: __("Favourites"),
on_click: () => frappe.set_route('Hub', 'Favourites')
}, __("Account"));
this.sidebar.add_item({
label: __("Settings"),
on_click: () => frappe.set_route('Form', 'Hub Settings')
}, __("Account"));
}
update_category(label) {
this.current_category = (label=='All Categories') ? undefined : label;
this.refresh();
}
get_filters_for_args() {
if(!this.filter_area) return;
let filters = {};
this.filter_area.get().forEach(f => {
let field = f[1] !== 'name' ? f[1] : 'item_name';
filters[field] = [f[2], f[3]];
});
if(this.current_category) {
filters['hub_category'] = this.current_category;
}
return filters;
}
update_data(r) {
super.update_data(r);
this.data_dict = {};
this.data.map(d => {
this.data_dict[d.hub_item_code] = d;
});
}
item_html(item) {
item._name = encodeURI(item.name);
const encoded_name = item._name;
const title = strip_html(item[this.meta.title_field || 'name']);
const _class = !item[this.imageFieldName] ? 'no-image' : '';
const route = `#Hub/Item/${item.hub_item_code}`;
const company_name = item['company_name'];
const reviewLength = (item.reviews || []).length;
const ratingAverage = reviewLength
? item.reviews
.map(r => r.rating)
.reduce((a, b) => a + b, 0)/reviewLength
: -1;
let ratingHtml = ``;
for(var i = 0; i < 5; i++) {
let starClass = 'fa-star';
if(i >= ratingAverage) starClass = 'fa-star-o';
ratingHtml += `<i class='fa fa-fw ${starClass} star-icon' data-index=${i}></i>`;
}
let item_html = `
<div class="image-view-item">
<div class="image-view-header">
<div class="list-row-col list-subject ellipsis level">
<span class="level-item bold ellipsis" title="McGuffin">
<a href="${route}">${title}</a>
</span>
</div>
<div class="text-muted small" style="margin: 5px 0px;">
${ratingHtml}
(${reviewLength})
</div>
<div class="list-row-col">
<a href="${'#Hub/Company/'+company_name}"><p>${ company_name }</p></a>
</div>
</div>
<div class="image-view-body">
<a data-name="${encoded_name}"
title="${encoded_name}"
href="${route}"
>
<div class="image-field ${_class}"
data-name="${encoded_name}"
>
<button class="btn btn-default zoom-view" data-name="${encoded_name}">
<i class="octicon octicon-eye" data-name="${encoded_name}"></i>
</button>
<button class="btn btn-default like-button" data-name="${encoded_name}">
<i class="octicon octicon-heart" data-name="${encoded_name}"></i>
</button>
</div>
</a>
</div>
</div>
`;
return item_html;
}
};
erpnext.hub.Favourites = class Favourites extends erpnext.hub.ItemListing {
constructor(opts) {
super(opts);
this.show();
}
setup_defaults() {
super.setup_defaults();
this.doctype = 'Hub Item';
this.page_title = __('Favourites');
this.fields = ['name', 'hub_item_code', 'image', 'item_name', 'item_code', 'company_name', 'description', 'country'];
this.filters = [];
this.method = 'erpnext.hub_node.get_item_favourites';
}
setup_filter_area() { }
setup_sort_selector() { }
// setupHe
getHeaderHtml() {
return '';
}
get_args() {
return {
start: this.start,
limit: this.page_length,
order_by: this.order_by,
fields: this.fields
};
}
bootstrap_data(response) { }
prepareFormFields() { }
setup_side_bar() {
this.sidebar = new frappe.ui.Sidebar({
wrapper: this.page.wrapper.find('.layout-side-section'),
css_class: 'hub-sidebar'
});
this.sidebar.add_item({
label: __('Back to Products'),
on_click: () => frappe.set_route('Hub', 'Item')
});
}
update_category(label) {
this.current_category = (label=='All Categories') ? undefined : label;
this.refresh();
}
get_filters_for_args() {
if(!this.filter_area) return;
let filters = {};
this.filter_area.get().forEach(f => {
let field = f[1] !== 'name' ? f[1] : 'item_name';
filters[field] = [f[2], f[3]];
});
if(this.current_category) {
filters['hub_category'] = this.current_category;
}
return filters;
}
update_data(r) {
super.update_data(r);
this.data_dict = {};
this.data.map(d => {
this.data_dict[d.hub_item_code] = d;
});
}
};
erpnext.hub.CompanyListing = class CompanyListing extends erpnext.hub.HubListing {
constructor(opts) {
super(opts);
this.show();
}
render() {
this.data_dict = {};
this.render_image_view();
}
setup_defaults() {
super.setup_defaults();
this.doctype = 'Hub Company';
this.page_title = __('Companies');
this.fields = ['company_logo', 'name', 'site_name', 'seller_city', 'seller_description', 'seller', 'country', 'company_name'];
this.filters = [];
this.custom_filter_configs = [
{
fieldtype: 'Link',
label: 'Country',
options: 'Country',
condition: 'like',
fieldname: 'country'
}
];
this.imageFieldName = 'company_logo';
}
setup_side_bar() {
this.sidebar = new frappe.ui.Sidebar({
wrapper: this.page.wrapper.find('.layout-side-section'),
css_class: 'hub-sidebar'
});
this.sidebar.add_item({
label: __('Back to Products'),
on_click: () => frappe.set_route('Hub', 'Item')
});
}
get_filters_for_args() {
let filters = {};
this.filter_area.get().forEach(f => {
let field = f[1] !== 'name' ? f[1] : 'company_name';
filters[field] = [f[2], f[3]];
});
return filters;
}
item_html(company) {
company._name = encodeURI(company.company_name);
const encoded_name = company._name;
const title = strip_html(company.company_name);
const _class = !company[this.imageFieldName] ? 'no-image' : '';
const company_name = company['company_name'];
const route = `#Hub/Company/${company_name}`;
let image_html = company.company_logo ?
`<img src="${company.company_logo}"><span class="helper"></span>` :
`<div class="standard-image">${frappe.get_abbr(company.company_name)}</div>`;
let item_html = `
<div class="image-view-item">
<div class="image-view-header">
<div class="list-row-col list-subject ellipsis level">
<span class="level-item bold ellipsis" title="McGuffin">
<a href="${route}">${title}</a>
</span>
</div>
</div>
<div class="image-view-body">
<a data-name="${encoded_name}"
title="${encoded_name}"
href="${route}">
<div class="image-field ${_class}"
data-name="${encoded_name}">
</div>
</a>
</div>
</div>
`;
return item_html;
}
};

View File

@ -1,244 +0,0 @@
frappe.provide('erpnext.hub');
erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList {
setup_defaults() {
super.setup_defaults();
this.page_title = __('Hub');
this.method = 'erpnext.hub_node.get_list';
const route = frappe.get_route();
this.page_name = route[1];
}
setup_fields() {
return this.get_meta()
.then(r => {
this.meta = r.message || this.meta;
frappe.model.sync(this.meta);
});
}
get_meta() {
return new Promise(resolve =>
frappe.call('erpnext.hub_node.get_meta', {doctype: this.doctype}, resolve));
}
set_breadcrumbs() { }
setup_side_bar() {
this.sidebar = new frappe.ui.Sidebar({
wrapper: this.page.wrapper.find('.layout-side-section'),
css_class: 'hub-sidebar'
});
}
setup_sort_selector() { }
setup_view() { }
get_args() {
return {
doctype: this.doctype,
start: this.start,
limit: this.page_length,
order_by: this.order_by,
fields: this.fields,
filters: this.get_filters_for_args()
};
}
update_data(r) {
const data = r.message;
if (this.start === 0) {
this.data = data;
} else {
this.data = this.data.concat(data);
}
}
freeze(toggle) {
this.$freeze.toggle(toggle);
if (this.$freeze.find('.image-view-container').length) return;
const html = Array.from(new Array(4)).map(d => this.card_html({
name: 'Loading...',
item_name: 'Loading...'
})).join('');
this.$freeze.html(`<div class="image-view-container border-top">${html}</div>`);
}
render() {
this.render_image_view();
}
render_image_view() {
let data = this.data;
if (this.start === 0) {
this.$result.html('<div class="image-view-container small padding-top">');
data = this.data.slice(this.start);
}
var html = data.map(this.card_html.bind(this)).join("");
this.$result.find('.image-view-container').append(html);
}
}
erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing {
setup_defaults() {
super.setup_defaults();
this.doctype = 'Hub Item';
this.fields = ['name', 'hub_item_code', 'image', 'item_name', 'item_code', 'company_name'];
this.filters = [];
this.custom_filter_configs = [
{
fieldtype: 'Data',
label: 'Company',
condition: 'like',
fieldname: 'company_name',
},
{
fieldtype: 'Link',
label: 'Country',
options: 'Country',
condition: 'like',
fieldname: 'country'
}
];
}
setup_side_bar() {
super.setup_side_bar();
this.category_tree = new frappe.ui.Tree({
parent: this.sidebar.$sidebar,
label: 'All Categories',
expandable: true,
args: {parent: this.current_category},
method: 'erpnext.hub_node.get_categories',
on_click: (node) => {
this.update_category(node.label);
}
});
this.sidebar.add_item({
label: __('Companies'),
on_click: () => frappe.set_route('Hub', 'Company')
});
this.sidebar.add_item({
label: this.hub_settings.company,
on_click: () => frappe.set_route('Form', 'Company', this.hub_settings.company)
}, __("Account"));
this.sidebar.add_item({
label: __("My Orders"),
on_click: () => frappe.set_route('List', 'Request for Quotation')
}, __("Account"));
}
update_category(label) {
this.current_category = (label=='All Categories') ? undefined : label;
this.refresh();
}
get_filters_for_args() {
let filters = {};
this.filter_area.get().forEach(f => {
let field = f[1] !== 'name' ? f[1] : 'item_name';
filters[field] = [f[2], f[3]];
});
if(this.current_category) {
filters['hub_category'] = this.current_category;
}
return filters;
}
card_html(item) {
item._name = encodeURI(item.name);
const encoded_name = item._name;
const title = strip_html(item['item_name' || 'item_code']);
const company_name = item['company_name'];
const route = `#Hub/Item/${item.hub_item_code}`;
const image_html = item.image ?
`<img src="${item.image}">
<span class="helper"></span>` :
`<div class="standard-image">${frappe.get_abbr(title)}</div>`;
return `
<div class="hub-item-wrapper margin-bottom" style="width: 200px;">
<a href="${route}">
<div class="hub-item-image">
<div class="img-wrapper" style="height: 200px; width: 200px">
${ image_html }
</div>
</div>
<div class="hub-item-title">
<h5 class="bold">
${ title }
</h5>
</div>
</a>
<a href="${'#Hub/Company/'+company_name}"><p>${ company_name }</p></a>
</div>
`;
}
};
erpnext.hub.CompanyListing = class CompanyListing extends erpnext.hub.HubListing {
setup_defaults() {
super.setup_defaults();
this.doctype = 'Hub Company';
this.fields = ['company_logo', 'name', 'site_name', 'seller_city', 'seller_description', 'seller', 'country', 'company_name'];
this.filters = [];
this.custom_filter_configs = [
{
fieldtype: 'Link',
label: 'Country',
options: 'Country',
condition: 'like',
fieldname: 'country'
}
];
}
get_filters_for_args() {
let filters = {};
this.filter_area.get().forEach(f => {
let field = f[1] !== 'name' ? f[1] : 'company_name';
filters[field] = [f[2], f[3]];
});
return filters;
}
card_html(company) {
company._name = encodeURI(company.name);
const route = `#Hub/Company/${company.company_name}`;
let image_html = company.company_logo ?
`<img src="${company.company_logo}"><span class="helper"></span>` :
`<div class="standard-image">${frappe.get_abbr(company.company_name)}</div>`;
return `
<div class="hub-item-wrapper margin-bottom" style="width: 200px;">
<a href="${route}">
<div class="hub-item-image">
<div class="img-wrapper" style="height: 200px; width: 200px">
${ image_html }
</div>
</div>
<div class="hub-item-title">
<h5 class="bold">
${ company.company_name }
</h5>
</div>
</a>
</div>
`;
}
};

View File

@ -50,7 +50,7 @@ erpnext.timesheet.control_timer = function(frm, dialog, row, timestamp=0) {
var currentIncrement = timestamp; var currentIncrement = timestamp;
var initialised = row ? true : false; var initialised = row ? true : false;
var clicked = false; var clicked = false;
var flag = true; // Alert only once
// If row with not completed status, initialize timer with the time elapsed on click of 'Start Timer'. // If row with not completed status, initialize timer with the time elapsed on click of 'Start Timer'.
if (row) { if (row) {
initialised = true; initialised = true;
@ -66,7 +66,7 @@ erpnext.timesheet.control_timer = function(frm, dialog, row, timestamp=0) {
// New activity if no activities found // New activity if no activities found
var args = dialog.get_values(); var args = dialog.get_values();
if(!args) return; if(!args) return;
if (!frm.doc.time_logs[0].activity_type) { if (frm.doc.time_logs.length <= 1 && !frm.doc.time_logs[0].activity_type && !frm.doc.time_logs[0].from_time) {
frm.doc.time_logs = []; frm.doc.time_logs = [];
} }
row = frappe.model.add_child(frm.doc, "Timesheet Detail", "time_logs"); row = frappe.model.add_child(frm.doc, "Timesheet Detail", "time_logs");
@ -130,8 +130,15 @@ erpnext.timesheet.control_timer = function(frm, dialog, row, timestamp=0) {
if (!$('.modal-dialog').is(':visible')) { if (!$('.modal-dialog').is(':visible')) {
reset(); reset();
} }
if(hours > 99) if(hours > 99999)
reset(); reset();
if(cur_dialog && cur_dialog.get_value('expected_hours') > 0) {
if(flag && (currentIncrement >= (cur_dialog.get_value('expected_hours') * 3600))) {
frappe.utils.play_sound("alert");
frappe.msgprint(__("Timer exceeded the given hours."));
flag = false;
}
}
$(".hours").text(hours < 10 ? ("0" + hours.toString()) : hours.toString()); $(".hours").text(hours < 10 ? ("0" + hours.toString()) : hours.toString());
$(".minutes").text(minutes < 10 ? ("0" + minutes.toString()) : minutes.toString()); $(".minutes").text(minutes < 10 ? ("0" + minutes.toString()) : minutes.toString());
$(".seconds").text(seconds < 10 ? ("0" + seconds.toString()) : seconds.toString()); $(".seconds").text(seconds < 10 ? ("0" + seconds.toString()) : seconds.toString());

View File

@ -23,6 +23,20 @@
margin-top: -3px; margin-top: -3px;
} }
.app-icon-svg {
display: inline-block;
margin: auto;
text-align: center;
border-radius: 16px;
cursor: pointer;
box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.15);
}
.app-icon-svg .hub-icon {
height: 72px;
width: 72px;
}
.dashboard-list-item { .dashboard-list-item {
background-color: inherit; background-color: inherit;
padding: 5px 0px; padding: 5px 0px;

View File

@ -1,24 +1,13 @@
@import "../../../../frappe/frappe/public/less/variables.less"; @import "../../../../frappe/frappe/public/less/variables.less";
body[data-route^="Hub/"] { body[data-route^="Hub/"] {
.freeze .image-view-container { .hub-icon {
.list-row-col { width: 40px;
background-color: @light-bg; height: 40px;
color: @light-bg;
} }
.placeholder-text { .hub-page-title {
color: @light-bg; margin-left: 10px;
}
}
.freeze {
display: none;
}
.image-view-container {
justify-content: space-around;
}
} }
.img-wrapper { .img-wrapper {
@ -36,141 +25,147 @@ body[data-route^="Hub/"] {
} }
} }
/* hub */ .tree {
div[data-page-route="hub"] { margin: 10px 0px;
.page-head { padding: 0px;
height: 80px; height: 100%;
position: relative;
}
.title-text { .tree.with-skeleton.opened::before {
left: 9px;
top: 14px;
height: calc(~"100% - 32px");
}
.list-header-icon {
width: 72px;
border-radius: 4px;
flex-shrink: 0;
margin: 10px;
padding: 1px;
border: 1px solid @border-color;
height: 72px;
display: flex;
align-items: center;
justify-content: center;
img {
border-radius: 4px;
}
}
.star-icon.fa-star {
color: @indicator-orange;
}
.octicon-heart.liked {
color: @indicator-red;
}
.margin-vertical-10 {
margin: 10px 0px;
}
.margin-vertical-15 {
margin: 15px 0px;
}
.frappe-list .result {
min-height: 100px;
}
.frappe-control[data-fieldtype="Attach Image"] {
width: 140px;
height: 180px;
margin-top: 20px;
}
.frappe-control[data-fieldtype="Attach Image"] .form-group {
display: none;
}
.frappe-control[data-fieldtype="Attach Image"] .clearfix {
display: none;
}
.missing-image {
display: block;
position: relative;
border-radius: 4px;
border: 1px solid #d1d8dd;
border-radius: 6px;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
.missing-image .octicon {
position: relative;
top: 50%;
transform: translate(0px, -50%);
-webkit-transform: translate(0px, -50%);
}
.attach-image-display {
display: block;
position: relative;
border-radius: 4px;
}
.img-container {
height: 100%;
width: 100%;
padding: 2px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
border: 1px solid #d1d8dd;
border-radius: 6px;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
.img-overlay {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
width: 100%;
height: 100%;
color: #777777;
background-color: rgba(255, 255, 255, 0.7);
opacity: 0;
}
.img-overlay:hover {
opacity: 1;
cursor: pointer; cursor: pointer;
} }
} }
.page-content { .image-view-container {
margin-top: 80px; .image-view-body {
} &:hover .like-button {
opacity: 0.7;
.page-title h1 {
margin-bottom: 0px;
}
.account-details {
margin-top: 20px;
}
[data-original-title="Search"] {
float: right;
width: 220px;
}
.hub-main-section {
padding: 30px;
}
.listing-body {
margin: 0;
}
.main-list-section {
padding: 0;
// border-right: 1px solid #d1d8dd;
}
.side-list-section {
padding: 0;
}
.item-list-header h3 {
font-weight: normal;
}
.hub-item-page {
h2 {
margin-top: 10px;
}
.item-header {
display: flex;
}
.item-page-image {
flex: 1;
}
.title-content {
flex: 3;
.description {
margin: 30px 0px;
}
.actions {
margin-top: 30px;
.rfq-btn.disabled {
background-color: #b1bdca;
color: #fff;
border-color: #b1bdca;
}
}
}
.company-items {
margin-top: 40px;
} }
} }
.company-header { .like-button {
display: flex; bottom: 10px !important;
} left: 10px !important;
width: 36px;
height: 36px;
opacity: 0;
font-size: 16px;
color: @text-color;
position: absolute;
.item-list { // show zoom button on mobile devices
display: flex; @media (max-width: @screen-xs) {
flex-wrap: wrap; opacity: 0.5
justify-content: space-between;
}
.hub-item-wrapper {
margin-bottom: 20px;
}
.img-wrapper {
border: 1px solid @border-color;
border-radius: 3px;
padding: 12px;
overflow: hidden;
text-align: center;
white-space: nowrap;
img {
max-width: 100%;
max-height: 100%;
display: inline-block;
vertical-align: middle;
}
.helper {
height: 100%;
display: inline-block;
vertical-align: middle;
}
.standard-image {
font-size: 72px;
border: none;
background-color: @light-bg;
} }
} }
.hub-item-title { .image-view-body:hover .like-button {
width: 100%; opacity: 0.7;
} }
.breadcrumb {
padding-left: 0;
padding-top: 0;
margin-bottom: 10px;
} }
.rating-area .star-icon {
cursor: pointer;
font-size: 15px;
} }

View File

@ -180,7 +180,11 @@ def make_custom_fields():
'Sales Invoice Item': [hsn_sac_field], 'Sales Invoice Item': [hsn_sac_field],
'Purchase Order Item': [hsn_sac_field], 'Purchase Order Item': [hsn_sac_field],
'Purchase Receipt Item': [hsn_sac_field], 'Purchase Receipt Item': [hsn_sac_field],
'Purchase Invoice Item': [hsn_sac_field] 'Purchase Invoice Item': [hsn_sac_field],
'Employee': [
dict(fieldname='ifsc_code', label='IFSC Code',
fieldtype='Data', insert_after='bank_ac_no', print_hide=1,
depends_on='eval:doc.salary_mode == "Bank"') ]
} }
create_custom_fields(custom_fields) create_custom_fields(custom_fields)

View File

@ -64,7 +64,7 @@ def get_itemised_tax_breakup_data(doc):
def set_place_of_supply(doc, method): def set_place_of_supply(doc, method):
if not frappe.get_meta('Address').has_field('gst_state'): return if not frappe.get_meta('Address').has_field('gst_state'): return
if doc.doctype == "Sales Invoice": if doc.doctype in ("Sales Invoice", "Delivery Note"):
address_name = doc.shipping_address_name or doc.customer_address address_name = doc.shipping_address_name or doc.customer_address
elif doc.doctype == "Purchase Invoice": elif doc.doctype == "Purchase Invoice":
address_name = doc.shipping_address or doc.supplier_address address_name = doc.shipping_address or doc.supplier_address

View File

@ -587,6 +587,10 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
"Sales Team": { "Sales Team": {
"doctype": "Sales Team", "doctype": "Sales Team",
"add_if_empty": True "add_if_empty": True
},
"Payment Schedule": {
"doctype": "Payment Schedule",
"add_if_empty": True
} }
}, target_doc, postprocess, ignore_permissions=ignore_permissions) }, target_doc, postprocess, ignore_permissions=ignore_permissions)

View File

@ -8,18 +8,13 @@ import unittest
from erpnext.selling.doctype.sales_order.sales_order \ from erpnext.selling.doctype.sales_order.sales_order \
import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from frappe.tests.test_permissions import set_user_permission_doctypes
from erpnext.selling.doctype.sales_order.sales_order import make_work_orders from erpnext.selling.doctype.sales_order.sales_order import make_work_orders
import json import json
class TestSalesOrder(unittest.TestCase): class TestSalesOrder(unittest.TestCase):
def tearDown(self): def tearDown(self):
frappe.set_user("Administrator") pass
for role in ("Stock User", "Sales User"):
set_user_permission_doctypes(doctypes="Sales Order", role=role,
apply_user_permissions=0, user_permission_doctypes=None)
def test_make_material_request(self): def test_make_material_request(self):
so = make_sales_order(do_not_submit=True) so = make_sales_order(do_not_submit=True)
@ -259,10 +254,6 @@ class TestSalesOrder(unittest.TestCase):
existing_reserved_qty_item2 + 20) existing_reserved_qty_item2 + 20)
def test_warehouse_user(self): def test_warehouse_user(self):
for role in ("Stock User", "Sales User"):
set_user_permission_doctypes(doctypes="Sales Order", role=role,
apply_user_permissions=1, user_permission_doctypes=["Warehouse"])
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com") frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com")
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com") frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
frappe.permissions.add_user_permission("Company", "_Test Company 1", "test2@example.com") frappe.permissions.add_user_permission("Company", "_Test Company 1", "test2@example.com")

View File

@ -886,9 +886,11 @@ class POSCart {
const is_stock_item = this.get_item_details(item.item_code).is_stock_item; const is_stock_item = this.get_item_details(item.item_code).is_stock_item;
const rate = format_currency(item.rate, this.frm.doc.currency); const rate = format_currency(item.rate, this.frm.doc.currency);
const indicator_class = (!is_stock_item || item.actual_qty >= item.qty) ? 'green' : 'red'; const indicator_class = (!is_stock_item || item.actual_qty >= item.qty) ? 'green' : 'red';
const batch_no = item.batch_no || '';
return ` return `
<div class="list-item indicator ${indicator_class}" data-item-code="${item.item_code}" <div class="list-item indicator ${indicator_class}" data-item-code="${item.item_code}"
data-batch-no="${item.batch_no}" title="Item: ${item.item_name} Available Qty: ${item.actual_qty}"> data-batch-no="${batch_no}" title="Item: ${item.item_name} Available Qty: ${item.actual_qty}">
<div class="item-name list-item__content list-item__content--flex-1.5 ellipsis"> <div class="item-name list-item__content list-item__content--flex-1.5 ellipsis">
${item.item_name} ${item.item_name}
</div> </div>

View File

@ -85,7 +85,7 @@ def delete_lead_addresses(company_name):
in ({leads})""".format(leads=",".join(leads))) in ({leads})""".format(leads=",".join(leads)))
if addresses: if addresses:
addresses = ["'%s'"%addr for addr in addresses] addresses = ["'%s'"%frappe.db.escape(addr) for addr in addresses]
frappe.db.sql("""delete from tabAddress where name in ({addresses}) and frappe.db.sql("""delete from tabAddress where name in ({addresses}) and
name not in (select distinct dl1.parent from `tabDynamic Link` dl1 name not in (select distinct dl1.parent from `tabDynamic Link` dl1

View File

@ -25,3 +25,6 @@ def get_parent_customer_groups(customer_group):
return frappe.db.sql("""select name from `tabCustomer Group` return frappe.db.sql("""select name from `tabCustomer Group`
where lft <= %s and rgt >= %s where lft <= %s and rgt >= %s
order by lft asc""", (lft, rgt), as_dict=True) order by lft asc""", (lft, rgt), as_dict=True)
def on_doctype_update():
frappe.db.add_index("Customer Group", ["lft", "rgt"])

View File

@ -33,3 +33,6 @@ class SalesPerson(NestedSet):
if sales_person and sales_person != self.name: if sales_person and sales_person != self.name:
frappe.throw(_("Another Sales Person {0} exists with the same Employee id").format(sales_person)) frappe.throw(_("Another Sales Person {0} exists with the same Employee id").format(sales_person))
def on_doctype_update():
frappe.db.add_index("Sales Person", ["lft", "rgt"])

View File

@ -22,3 +22,5 @@ class Territory(NestedSet):
super(Territory, self).on_update() super(Territory, self).on_update()
self.validate_one_root() self.validate_one_root()
def on_doctype_update():
frappe.db.add_index("Territory", ["lft", "rgt"])

View File

@ -27,7 +27,6 @@ def set_default_settings(args):
domain_settings = frappe.get_single('Domain Settings') domain_settings = frappe.get_single('Domain Settings')
domain_settings.set_active_domains(args.get('domains')) domain_settings.set_active_domains(args.get('domains'))
domain_settings.save()
stock_settings = frappe.get_doc("Stock Settings") stock_settings = frappe.get_doc("Stock Settings")
stock_settings.item_naming_by = "Item Code" stock_settings.item_naming_by = "Item Code"

View File

@ -15,32 +15,38 @@ def get_warehouse_account_map():
if not frappe.flags.warehouse_account_map or frappe.flags.in_test: if not frappe.flags.warehouse_account_map or frappe.flags.in_test:
warehouse_account = frappe._dict() warehouse_account = frappe._dict()
for d in frappe.get_all('Warehouse', filters = {"is_group": 0}, for d in frappe.get_all('Warehouse',
fields = ["name", "account", "parent_warehouse", "company"]): fields = ["name", "account", "parent_warehouse", "company"],
order_by="lft, rgt"):
if not d.account: if not d.account:
d.account = get_warehouse_account(d.name, d.company) d.account = get_warehouse_account(d, warehouse_account)
if d.account: if d.account:
d.account_currency = frappe.db.get_value('Account', d.account, 'account_currency') d.account_currency = frappe.db.get_value('Account', d.account, 'account_currency', cache=True)
warehouse_account.setdefault(d.name, d) warehouse_account.setdefault(d.name, d)
frappe.flags.warehouse_account_map = warehouse_account frappe.flags.warehouse_account_map = warehouse_account
return frappe.flags.warehouse_account_map return frappe.flags.warehouse_account_map
def get_warehouse_account(warehouse, company): def get_warehouse_account(warehouse, warehouse_account=None):
lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"]) account = warehouse.account
if not account and warehouse.parent_warehouse:
if warehouse_account:
account = warehouse_account.get(warehouse.parent_warehouse).account
else:
account = frappe.db.sql(""" account = frappe.db.sql("""
select select
account from `tabWarehouse` account from `tabWarehouse`
where where
lft <= %s and rgt >= %s and company = %s lft <= %s and rgt >= %s and company = %s
and account is not null and ifnull(account, '') !='' and account is not null and ifnull(account, '') !=''
order by lft desc limit 1""", (lft, rgt, company), as_list=1) order by lft desc limit 1""", (warehouse.lft, warehouse.rgt, warehouse.company), as_list=1)
account = account[0][0] if account else None account = account[0][0] if account else None
if not account: if not account:
account = get_company_default_inventory_account(company) account = get_company_default_inventory_account(warehouse.company)
return account return account

View File

@ -9,7 +9,7 @@ import googlemaps
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils.user import get_user_fullname from frappe.utils.user import get_user_fullname
from frappe.utils import getdate from frappe.utils import getdate, cstr
from frappe.integrations.doctype.google_maps.google_maps import round_timedelta from frappe.integrations.doctype.google_maps.google_maps import round_timedelta
from frappe.integrations.doctype.google_maps.google_maps import format_address from frappe.integrations.doctype.google_maps.google_maps import format_address
@ -141,40 +141,33 @@ def calculate_time_matrix(name):
@frappe.whitelist() @frappe.whitelist()
def notify_customers(docname, date, driver, vehicle, sender_email, delivery_notification): def notify_customers(docname, date, driver, vehicle, sender_email, delivery_notification):
sender_name = get_user_fullname(sender_email) sender_name = get_user_fullname(sender_email)
delivery_stops = frappe.get_all('Delivery Stop', {"parent": docname})
attachments = [] attachments = []
for delivery_stop in delivery_stops: parent_doc = frappe.get_doc('Delivery Trip', docname)
delivery_stop_info = frappe.db.get_value( args = parent_doc.as_dict()
"Delivery Stop",
delivery_stop.name, for delivery_stop in parent_doc.delivery_stops:
["notified_by_email", "estimated_arrival", "details", "contact", "delivery_note"], contact_info = frappe.db.get_value("Contact", delivery_stop.contact,
as_dict=1)
contact_info = frappe.db.get_value("Contact", delivery_stop_info.contact,
["first_name", "last_name", "email_id", "gender"], as_dict=1) ["first_name", "last_name", "email_id", "gender"], as_dict=1)
if delivery_stop_info.delivery_note: args.update(delivery_stop.as_dict())
args.update(contact_info)
if delivery_stop.delivery_note:
default_print_format = frappe.get_meta('Delivery Note').default_print_format default_print_format = frappe.get_meta('Delivery Note').default_print_format
attachments = frappe.attach_print('Delivery Note', attachments = frappe.attach_print('Delivery Note',
delivery_stop_info.delivery_note, delivery_stop.delivery_note,
file_name="Delivery Note", file_name="Delivery Note",
print_format=default_print_format or "Standard") print_format=default_print_format or "Standard")
if not delivery_stop_info.notified_by_email and contact_info.email_id: 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) 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"]) sender_designation = frappe.db.get_value("Employee", sender_email, ["designation"])
estimated_arrival = str(delivery_stop_info.estimated_arrival)[:-3] estimated_arrival = cstr(delivery_stop.estimated_arrival)[:-3]
email_template = frappe.get_doc("Standard Reply", delivery_notification) email_template = frappe.get_doc("Standard Reply", delivery_notification)
message = frappe.render_template( message = frappe.render_template(email_template.response, args)
email_template.response,
dict(contact_info=contact_info, sender_name=sender_name,
details=delivery_stop_info.details,
estimated_arrival=estimated_arrival,
date=getdate(date).strftime('%d.%m.%y'), vehicle=vehicle,
driver_info=driver_info,
sender_designation=sender_designation)
)
frappe.sendmail( frappe.sendmail(
recipients=contact_info.email_id, recipients=contact_info.email_id,
sender=sender_email, sender=sender_email,

View File

@ -532,7 +532,7 @@ class Item(WebsiteGenerator):
def on_trash(self): def on_trash(self):
super(Item, self).on_trash() super(Item, self).on_trash()
frappe.db.sql("""delete from tabBin where item_code=%s""", self.item_code) frappe.db.sql("""delete from tabBin where item_code=%s""", self.name)
frappe.db.sql("delete from `tabItem Price` where item_code=%s", self.name) frappe.db.sql("delete from `tabItem Price` where item_code=%s", self.name)
for variant_of in frappe.get_all("Item", filters={"variant_of": self.name}): for variant_of in frappe.get_all("Item", filters={"variant_of": self.name}):
frappe.delete_doc("Item", variant_of.name) frappe.delete_doc("Item", variant_of.name)

View File

@ -12,7 +12,6 @@ from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFre
from erpnext.stock.stock_ledger import get_previous_sle from erpnext.stock.stock_ledger import get_previous_sle
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
from erpnext.stock.doctype.item.test_item import set_item_variant_settings, make_item_variant, create_item from erpnext.stock.doctype.item.test_item import set_item_variant_settings, make_item_variant, create_item
from frappe.tests.test_permissions import set_user_permission_doctypes
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.stock.doctype.stock_entry.stock_entry import move_sample_to_retention_warehouse from erpnext.stock.doctype.stock_entry.stock_entry import move_sample_to_retention_warehouse
@ -35,10 +34,6 @@ class TestStockEntry(unittest.TestCase):
frappe.set_user("Administrator") frappe.set_user("Administrator")
set_perpetual_inventory(0) set_perpetual_inventory(0)
for role in ("Stock User", "Sales User"):
set_user_permission_doctypes(doctypes="Stock Entry", role=role,
apply_user_permissions=0, user_permission_doctypes=None)
def test_fifo(self): def test_fifo(self):
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
item_code = "_Test Item 2" item_code = "_Test Item 2"
@ -485,10 +480,6 @@ class TestStockEntry(unittest.TestCase):
# permission tests # permission tests
def test_warehouse_user(self): def test_warehouse_user(self):
for role in ("Stock User", "Sales User"):
set_user_permission_doctypes(doctypes="Stock Entry", role=role,
apply_user_permissions=1, user_permission_doctypes=["Warehouse"])
frappe.defaults.add_default("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com", "User Permission") frappe.defaults.add_default("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com", "User Permission")
frappe.defaults.add_default("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com", "User Permission") frappe.defaults.add_default("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com", "User Permission")
test_user = frappe.get_doc("User", "test@example.com") test_user = frappe.get_doc("User", "test@example.com")

View File

@ -3,7 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, erpnext import frappe, erpnext
from frappe.utils import cint, validate_email_add from frappe.utils import cint
from frappe import throw, _ from frappe import throw, _
from frappe.utils.nestedset import NestedSet from frappe.utils.nestedset import NestedSet
from erpnext.stock import get_warehouse_account from erpnext.stock import get_warehouse_account
@ -22,7 +22,7 @@ class Warehouse(NestedSet):
def onload(self): def onload(self):
'''load account name for General Ledger Report''' '''load account name for General Ledger Report'''
account = self.account or get_warehouse_account(self.name, self.company) account = self.account or get_warehouse_account(self)
if account: if account:
self.set_onload('account', account) self.set_onload('account', account)

View File

@ -40,6 +40,8 @@ def get_data(item):
name = item_dict["variant_name"] name = item_dict["variant_name"]
for d in attribute_list: for d in attribute_list:
attr_dict = attr_val_map[name]
if attr_dict and attr_dict.get(d):
item_dict[d] = attr_val_map[name][d] item_dict[d] = attr_val_map[name][d]
item_dict["Open Orders"] = order_count_map.get(name) or 0 item_dict["Open Orders"] = order_count_map.get(name) or 0

View File

@ -7,8 +7,9 @@ from frappe import _
def execute(filters=None): def execute(filters=None):
columns = get_columns() columns = get_columns()
sl_entries = get_stock_ledger_entries(filters) item_conditions = get_item_conditions(filters)
item_details = get_item_details(filters) item_details = get_item_details(filters, item_conditions)
sl_entries = get_stock_ledger_entries(filters, item_conditions, item_details)
opening_row = get_opening_balance(filters, columns) opening_row = get_opening_balance(filters, columns)
data = [] data = []
@ -52,7 +53,12 @@ def get_columns():
return columns return columns
def get_stock_ledger_entries(filters): def get_stock_ledger_entries(filters, item_conditions, item_details):
item_conditions_sql = ''
if item_conditions:
items = ['"' + frappe.db.escape(i) + '"' for i in item_details.keys()]
if items:
item_conditions_sql = 'and sle.item_code in ({})'.format(', '.join(items))
return frappe.db.sql("""select concat_ws(" ", posting_date, posting_time) as date, return frappe.db.sql("""select concat_ws(" ", posting_date, posting_time) as date,
item_code, warehouse, actual_qty, qty_after_transaction, incoming_rate, valuation_rate, item_code, warehouse, actual_qty, qty_after_transaction, incoming_rate, valuation_rate,
stock_value, voucher_type, voucher_no, batch_no, serial_no, company, project stock_value, voucher_type, voucher_no, batch_no, serial_no, company, project
@ -60,14 +66,18 @@ def get_stock_ledger_entries(filters):
where company = %(company)s and where company = %(company)s and
posting_date between %(from_date)s and %(to_date)s posting_date between %(from_date)s and %(to_date)s
{sle_conditions} {sle_conditions}
{item_conditions_sql}
order by posting_date asc, posting_time asc, name asc"""\ order by posting_date asc, posting_time asc, name asc"""\
.format(sle_conditions=get_sle_conditions(filters)), filters, as_dict=1) .format(
sle_conditions=get_sle_conditions(filters),
item_conditions_sql = item_conditions_sql
), filters, as_dict=1)
def get_item_details(filters): def get_item_details(filters, item_conditions):
item_details = {} item_details = {}
for item in frappe.db.sql("""select name, item_name, description, item_group, for item in frappe.db.sql("""select name, item_name, description, item_group,
brand, stock_uom from `tabItem` item {item_conditions}"""\ brand, stock_uom from `tabItem` item {item_conditions}"""\
.format(item_conditions=get_item_conditions(filters)), filters, as_dict=1): .format(item_conditions=item_conditions), filters, as_dict=1):
item_details.setdefault(item.name, item) item_details.setdefault(item.name, item)
return item_details return item_details
@ -85,10 +95,6 @@ def get_item_conditions(filters):
def get_sle_conditions(filters): def get_sle_conditions(filters):
conditions = [] conditions = []
item_conditions=get_item_conditions(filters)
if item_conditions:
conditions.append("""sle.item_code in (select item.name from tabItem item
{item_conditions})""".format(item_conditions=item_conditions))
if filters.get("warehouse"): if filters.get("warehouse"):
warehouse_condition = get_warehouse_condition(filters.get("warehouse")) warehouse_condition = get_warehouse_condition(filters.get("warehouse"))
if warehouse_condition: if warehouse_condition:

View File

@ -444,7 +444,7 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
last_valuation_rate = frappe.db.sql("""select valuation_rate last_valuation_rate = frappe.db.sql("""select valuation_rate
from `tabStock Ledger Entry` from `tabStock Ledger Entry`
where item_code = %s and warehouse = %s where item_code = %s and warehouse = %s
and valuation_rate > 0 and valuation_rate >= 0
order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse)) order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse))
if not last_valuation_rate: if not last_valuation_rate:

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