Merge branch 'develop' into payment-terms

This commit is contained in:
tunde 2017-10-06 02:23:45 +01:00
commit d82677f1e6
104 changed files with 7734 additions and 4675 deletions

View File

@ -4,7 +4,7 @@ import inspect
import frappe
from erpnext.hooks import regional_overrides
__version__ = '9.0.6'
__version__ = '9.1.1'
def get_default_company(user=None):
'''Get default company for user'''

View File

@ -0,0 +1,60 @@
QUnit.module('Payment Entry');
QUnit.test("test payment entry", function(assert) {
assert.expect(7 );
let done = assert.async();
frappe.run_serially([
() => {
return frappe.tests.make('Purchase Invoice', [
{supplier: 'Test Supplier'},
{bill_no: 'in1234'},
{items: [
[
{'qty': 2},
{'item_code': 'Test Product 1'},
{'rate':1000},
]
]},
{update_stock:1},
{supplier_address: 'Test1-Billing'},
{contact_person: 'Contact 3-Test Supplier'},
{tc_name: 'Test Term 1'},
{terms: 'This is just a Test'}
]);
},
() => cur_frm.save(),
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(1),
() => frappe.click_button('Make'),
() => frappe.timeout(2),
() => frappe.click_link('Payment'),
() => frappe.timeout(3),
() => cur_frm.set_value('mode_of_payment','Cash'),
() => frappe.timeout(3),
() => {
assert.equal(frappe.get_route()[1], 'Payment Entry',
'made payment entry');
assert.equal(cur_frm.doc.party, 'Test Supplier',
'supplier set in payment entry');
assert.equal(cur_frm.doc.paid_amount, 2000,
'paid amount set in payment entry');
assert.equal(cur_frm.doc.references[0].allocated_amount, 2000,
'amount allocated against purchase invoice');
assert.equal(cur_frm.doc.references[0].bill_no, 'in1234',
'invoice number allocated against purchase invoice');
assert.equal(cur_frm.get_field('total_allocated_amount').value, 2000,
'correct amount allocated in Write Off');
assert.equal(cur_frm.get_field('unallocated_amount').value, 0,
'correct amount unallocated in Write Off');
},
() => cur_frm.save(),
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(3),
() => done()
]);
});

View File

@ -3,7 +3,17 @@
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class POSSettings(Document):
pass
def validate(self):
self.set_link_for_pos()
def set_link_for_pos(self):
link = 'pos' if self.use_pos_in_offline_mode else 'point-of-sale'
desktop_icon = frappe.db.get_value('Desktop Icon',
{'standard': 1, 'module_name': 'POS'}, 'name')
if desktop_icon:
frappe.db.set_value('Desktop Icon', desktop_icon, 'link', link)

View File

@ -7,6 +7,7 @@ QUnit.test("test purchase invoice", function(assert) {
() => {
return frappe.tests.make('Purchase Invoice', [
{supplier: 'Test Supplier'},
{bill_no: 'in123'},
{items: [
[
{'qty': 5},
@ -40,7 +41,7 @@ QUnit.test("test purchase invoice", function(assert) {
},
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(0.3),
() => frappe.timeout(1),
() => done()
]);
});

View File

@ -339,7 +339,7 @@ $.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_
// ------------
cur_frm.cscript.hide_fields = function(doc) {
var parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances',
'advances', 'sales_partner', 'commission_rate', 'total_commission', 'advances', 'from_date', 'to_date'];
'advances', 'advances', 'from_date', 'to_date'];
if(cint(doc.is_pos) == 1) {
hide_field(parent_fields);

View File

@ -3,6 +3,12 @@
frappe.ui.form.on('Subscription', {
setup: function(frm) {
frm.fields_dict['reference_doctype'].get_query = function(doc) {
return {
query: "erpnext.accounts.doctype.subscription.subscription.subscription_doctype_query"
};
};
frm.fields_dict['reference_document'].get_query = function() {
return {
filters: {

View File

@ -135,66 +135,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "disabled",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Disabled",
"length": 0,
"no_copy": 1,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "submit_on_creation",
"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": "Submit on Creation",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -286,12 +226,12 @@
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "next_schedule_date",
"fieldtype": "Date",
"fieldname": "submit_on_creation",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -299,14 +239,44 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Next Schedule Date",
"label": "Submit on Creation",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "disabled",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Disabled",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
@ -320,7 +290,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "frequency_detail",
"fieldname": "section_break_10",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
@ -329,7 +299,95 @@
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "from_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": "From 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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "to_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": "To 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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_13",
"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,
@ -375,35 +433,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_12",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
@ -435,6 +464,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "next_schedule_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": "Next Schedule Date",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -785,7 +844,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-09-28 18:27:48.522098",
"modified": "2017-10-03 17:20:26.919630",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Subscription",
@ -795,7 +854,7 @@
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@ -815,7 +874,7 @@
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@ -835,7 +894,7 @@
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,

View File

@ -10,7 +10,7 @@ from frappe.desk.form import assign_to
from frappe.utils.jinja import validate_template
from dateutil.relativedelta import relativedelta
from frappe.utils.user import get_system_managers
from frappe.utils import cstr, getdate, split_emails, add_days, today
from frappe.utils import cstr, getdate, split_emails, add_days, today, get_last_day, get_first_day
from frappe.model.document import Document
month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}
@ -28,12 +28,23 @@ class Subscription(Document):
self.set_next_schedule_date()
def on_submit(self):
self.update_subscription_id()
# self.update_subscription_id()
self.update_subscription_data()
def on_update_after_submit(self):
self.update_subscription_data()
self.validate_dates()
self.set_next_schedule_date()
def before_cancel(self):
self.unlink_subscription_id()
def unlink_subscription_id(self):
doc = frappe.get_doc(self.reference_doctype, self.reference_document)
if doc.meta.get_field('subscription'):
doc.subscription = None
doc.db_update()
def validate_dates(self):
if self.end_date and getdate(self.start_date) > getdate(self.end_date):
frappe.throw(_("End date must be greater than start date"))
@ -68,6 +79,21 @@ class Subscription(Document):
self.next_schedule_date = get_next_schedule_date(self.start_date,
self.frequency, self.repeat_on_day)
def update_subscription_data(self):
update_doc = False
doc = frappe.get_doc(self.reference_doctype, self.reference_document)
if frappe.get_meta(self.reference_doctype).get_field("from_date"):
doc.from_date = self.from_date
doc.to_date = self.to_date
update_doc = True
if not doc.subscription:
doc.subscription = self.name
update_doc = True
if update_doc:
doc.db_update()
def update_subscription_id(self):
doc = frappe.get_doc(self.reference_doctype, self.reference_document)
if not doc.meta.get_field('subscription'):
@ -116,6 +142,9 @@ def get_subscription_entries(date):
def create_documents(data, schedule_date):
try:
doc = make_new_document(data, schedule_date)
if getattr(doc, "from_date", None):
update_subscription_period(data, doc)
if data.notify_by_email and data.recipients:
print_format = data.print_format or "Standard"
send_notification(doc, data, print_format=print_format)
@ -125,12 +154,19 @@ def create_documents(data, schedule_date):
frappe.db.rollback()
frappe.db.begin()
frappe.log_error(frappe.get_traceback())
disabled_subscription(data)
disable_subscription(data)
frappe.db.commit()
if data.reference_document and not frappe.flags.in_test:
notify_error_to_user(data)
def disabled_subscription(data):
def update_subscription_period(data, doc):
from_date = doc.from_date
to_date = doc.to_date
frappe.db.set_value('Subscription', data.name, 'from_date', from_date)
frappe.db.set_value('Subscription', data.name, 'to_date', to_date)
def disable_subscription(data):
subscription = frappe.get_doc('Subscription', data.name)
subscription.db_set('disabled', 1)
@ -164,9 +200,24 @@ def update_doc(new_document, reference_doc, args, schedule_date):
if new_document.meta.get_field('set_posting_time'):
new_document.set('set_posting_time', 1)
mcount = month_map.get(args.frequency)
if new_document.meta.get_field('subscription'):
new_document.set('subscription', args.name)
if args.from_date and args.to_date:
from_date = get_next_date(args.from_date, mcount)
if (cstr(get_first_day(args.from_date)) == cstr(args.from_date)) and \
(cstr(get_last_day(args.to_date)) == cstr(args.to_date)):
to_date = get_last_day(get_next_date(args.to_date, mcount))
else:
to_date = get_next_date(args.to_date, mcount)
if new_document.meta.get_field('from_date'):
new_document.set('from_date', from_date)
new_document.set('to_date', to_date)
new_document.run_method("on_recurring", reference_doc=reference_doc, subscription_doc=args)
for data in new_document.meta.fields:
if data.fieldtype == 'Date' and data.reqd:
@ -240,4 +291,20 @@ def stop_resume_subscription(subscription, status):
doc.update_status(status)
doc.save()
return doc.status
return doc.status
def subscription_doctype_query(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select parent from `tabDocField`
where fieldname = 'subscription'
and parent like %(txt)s
order by
if(locate(%(_txt)s, parent), locate(%(_txt)s, parent), 99999),
parent
limit %(start)s, %(page_len)s""".format(**{
'key': searchfield,
}), {
'txt': "%%%s%%" % txt,
'_txt': txt.replace("%", ""),
'start': start,
'page_len': page_len
})

View File

@ -1,4 +1,3 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
@ -16,6 +15,8 @@ frappe.ui.form.on("Purchase Order", {
},
onload: function(frm) {
set_schedule_date(frm);
erpnext.queries.setup_queries(frm, "Warehouse", function() {
return erpnext.queries.warehouse(frm.doc);
});
@ -34,6 +35,17 @@ frappe.ui.form.on("Purchase Order Item", {
frm.trigger('calculate_taxes_and_totals');
}
})
},
schedule_date: function(frm, cdt, cdn) {
var row = locals[cdt][cdn];
if (row.schedule_date) {
if(!frm.doc.schedule_date) {
erpnext.utils.copy_value_in_all_row(frm.doc, cdt, cdn, "items", "schedule_date");
} else {
set_schedule_date(frm);
}
}
}
});
@ -120,12 +132,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
},
validate: function() {
// set default schedule date as today if missing.
(this.frm.doc.items || []).forEach(function(d) {
if(!d.schedule_date) {
d.schedule_date = frappe.datetime.nowdate();
}
})
set_schedule_date(this.frm);
},
make_stock_entry: function() {
@ -214,7 +221,12 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
items_add: function(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
if(doc.schedule_date) {
row.schedule_date = doc.schedule_date;
refresh_field("schedule_date", cdn, "items");
} else {
this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
}
},
unclose_purchase_order: function(){
@ -227,8 +239,26 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
delivered_by_supplier: function(){
cur_frm.cscript.update_status('Deliver', 'Delivered')
}
},
get_last_purchase_rate: function() {
frappe.call({
"method": "get_last_purchase_rate",
"doc": cur_frm.doc,
callback: function(r, rt) {
cur_frm.dirty();
cur_frm.cscript.calculate_taxes_and_totals();
}
})
},
items_on_form_rendered: function() {
set_schedule_date(this.frm);
},
schedule_date: function() {
set_schedule_date(this.frm);
}
});
// for backward compatibility: combine new and previous states
@ -270,8 +300,10 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
}
}
cur_frm.cscript.schedule_date = function(doc, cdt, cdn) {
erpnext.utils.copy_value_in_all_row(doc, cdt, cdn, "items", "schedule_date");
function set_schedule_date(frm) {
if(frm.doc.schedule_date){
erpnext.utils.copy_value_in_all_row(frm.doc, frm.doc.doctype, frm.doc.name, "items", "schedule_date");
}
}
frappe.provide("erpnext.buying");

View File

@ -291,9 +291,40 @@
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "schedule_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": "Reqd By 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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -3519,9 +3550,9 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-09-22 16:11:49.856808",
"modified_by": "Administrator",
"module": "Buying",
"modified": "2017-10-05 14:19:04.102534",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
"owner": "Administrator",
"permissions": [

View File

@ -41,6 +41,7 @@ class PurchaseOrder(BuyingController):
self.set_status()
self.validate_supplier()
self.validate_schedule_date()
validate_for_items(self)
self.check_for_closed_status()

View File

@ -146,6 +146,7 @@ class TestPurchaseOrder(unittest.TestCase):
"company": "_Test Company",
"supplier" : "_Test Supplier",
"is_subcontracted" : "No",
"schedule_date": add_days(nowdate(), 1),
"currency" : frappe.db.get_value("Company", "_Test Company", "default_currency"),
"conversion_factor" : 1,
"items" : get_same_items(),
@ -206,6 +207,7 @@ def create_purchase_order(**args):
if args.transaction_date:
po.transaction_date = args.transaction_date
po.schedule_date = add_days(nowdate(), 1)
po.company = args.company or "_Test Company"
po.supplier = args.customer or "_Test Supplier"
po.is_subcontracted = args.is_subcontracted or "No"

View File

@ -30,7 +30,8 @@
],
"supplier": "_Test Supplier",
"supplier_name": "_Test Supplier",
"transaction_date": "2013-02-12"
"transaction_date": "2013-02-12",
"schedule_date": "2013-02-13"
},
{
"advance_paid": 0.0,
@ -63,6 +64,7 @@
],
"supplier": "_Test Supplier",
"supplier_name": "_Test Supplier",
"transaction_date": "2013-02-12"
"transaction_date": "2013-02-12",
"schedule_date": "2013-02-13"
}
]

View File

@ -13,12 +13,21 @@ QUnit.test("test: purchase order", function(assert) {
{items: [
[
{"item_code": 'Test Product 4'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 2)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"qty": 5},
{"uom": 'Unit'},
{"rate": 100},
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
],
[
{"item_code": 'Test Product 1'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"qty": 2},
{"uom": 'Unit'},
{"rate": 100},
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]},
@ -30,12 +39,19 @@ QUnit.test("test: purchase order", function(assert) {
() => {
// Get supplier details
assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct");
assert.ok(cur_frm.doc.schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 1), "Schedule Date correct");
assert.ok($('div.control-value.like-disabled-input.for-description').text().includes('Contact 3'), "Contact display correct");
assert.ok(cur_frm.doc.contact_email == 'test@supplier.com', "Contact email correct");
// Get item details
assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item name correct");
assert.ok(cur_frm.doc.items[0].description == 'Test Product 4', "Description correct");
assert.ok(cur_frm.doc.items[0].qty == 5, "Quantity correct");
assert.ok(cur_frm.doc.items[0].schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 2), "Schedule Date correct");
assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Item name correct");
assert.ok(cur_frm.doc.items[1].description == 'Test Product 1', "Description correct");
assert.ok(cur_frm.doc.items[1].qty == 2, "Quantity correct");
assert.ok(cur_frm.doc.items[1].schedule_date == cur_frm.doc.schedule_date, "Schedule Date correct");
// Calculate total
assert.ok(cur_frm.doc.total == 500, "Total correct");
// Get terms

View File

@ -11,6 +11,7 @@ QUnit.test("test: purchase order with taxes and charges", function(assert) {
{is_subcontracted: 'No'},
{buying_price_list: 'Test-Buying-USD'},
{currency: 'USD'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{items: [
[
{"item_code": 'Test Product 4'},

View File

@ -140,7 +140,7 @@
"allow_on_submit": 1,
"bold": 1,
"collapsible": 0,
"columns": 0,
"columns": 2,
"fieldname": "schedule_date",
"fieldtype": "Date",
"hidden": 0,
@ -148,7 +148,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Reqd By Date",
"length": 0,
@ -382,7 +382,7 @@
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 2,
"columns": 1,
"fieldname": "qty",
"fieldtype": "Float",
"hidden": 0,
@ -749,7 +749,7 @@
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 3,
"columns": 2,
"fieldname": "rate",
"fieldtype": "Currency",
"hidden": 0,
@ -1745,7 +1745,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-09-22 16:47:08.783546",
"modified": "2017-10-05 19:47:12.433095",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",

View File

@ -138,7 +138,7 @@ class RequestforQuotation(BuyingController):
'user_fullname': full_name
}
subject = _("Request for Quotation")
subject = _("Request for Quotation: {0}".format(self.name))
template = "templates/emails/request_for_quotation.html"
sender = frappe.session.user not in STANDARD_USERS and frappe.session.user or None
message = frappe.get_template(template).render(args)

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt
from frappe.utils import flt, nowdate, add_days
from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.buying_controller import BuyingController
@ -151,5 +151,4 @@ def make_quotation(source_name, target_doc=None):
}
}, target_doc)
return doclist
return doclist

View File

@ -315,11 +315,16 @@ def get_data():
"name": "Payment Gateway Account",
"description": _("Setup Gateway accounts.")
},
{
"type": "doctype",
"name": "POS Settings",
"description": _("Setup mode of POS (Online / Offline)")
},
{
"type": "doctype",
"name": "POS Profile",
"label": _("Point-of-Sale Profile"),
"description": _("Rules to calculate shipping amount for a sale")
"description": _("Setup default values for POS Invoices")
},
{
"type": "doctype",

View File

@ -127,7 +127,6 @@ def get_data():
{
"module_name": "POS",
"color": "#589494",
"icon": "fa fa-th",
"icon": "octicon octicon-credit-card",
"type": "page",
"link": "pos",
@ -267,7 +266,15 @@ def get_data():
"color": "#FF888B",
"icon": "octicon octicon-plus",
"type": "module",
"label": _("Healthcare")
"label": _("Healthcare"),
},
{
"module_name": "Hub",
"color": "#009248",
"icon": "/assets/erpnext/images/hub_logo.svg",
"type": "page",
"link": "hub",
"label": _("Hub")
},
{
"module_name": "Data Import Tool",

View File

@ -0,0 +1,24 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return [
{
"label": _("Setup"),
"items": [
{
"type": "doctype",
"name": "Hub Settings"
},
]
},
{
"label": _("Hub"),
"items": [
{
"type": "page",
"name": "hub"
},
]
},
]

View File

@ -123,6 +123,12 @@ def get_data():
"is_query_report": True,
"name": "BOM Search",
"doctype": "BOM"
},
{
"type": "report",
"is_query_report": True,
"name": "BOM Stock Report",
"doctype": "BOM"
}
]
},

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _, msgprint
from frappe.utils import flt,cint, cstr
from frappe.utils import flt,cint, cstr, getdate
from erpnext.accounts.party import get_party_details
from erpnext.stock.get_item_details import get_conversion_factor
@ -408,3 +408,16 @@ class BuyingController(StockController):
"actual_qty": -1*flt(d.consumed_qty),
}))
def validate_schedule_date(self):
if not self.schedule_date:
self.schedule_date = min([d.schedule_date for d in self.get("items")])
if self.schedule_date:
for d in self.get('items'):
if not d.schedule_date:
d.schedule_date = self.schedule_date
if d.schedule_date and getdate(d.schedule_date) < getdate(self.transaction_date):
frappe.throw(_("Expected Date cannot be before Transaction Date"))
else:
frappe.throw(_("Please enter Schedule Date"))

View File

@ -103,6 +103,7 @@ def make_material_request(item_code, qty):
mr.material_request_type = "Purchase"
mr.transaction_date = frappe.flags.current_date
mr.schedule_date = frappe.utils.add_days(mr.transaction_date, 7)
mr.append("items", {
"doctype": "Material Request Item",
@ -128,6 +129,7 @@ def make_subcontract():
po = frappe.new_doc("Purchase Order")
po.is_subcontracted = "Yes"
po.supplier = get_random("Supplier")
po.schedule_date = frappe.utils.add_days(frappe.flags.current_date, 7)
item_code = get_random("Item", {"is_sub_contracted_item": 1})

View File

@ -47,7 +47,7 @@ treeviews = ['Account', 'Cost Center', 'Warehouse', 'Item Group', 'Customer Grou
update_website_context = "erpnext.shopping_cart.utils.update_website_context"
my_account_context = "erpnext.shopping_cart.utils.update_my_account_context"
email_append_to = ["Job Applicant", "Opportunity", "Issue"]
email_append_to = ["Job Applicant", "Lead", "Opportunity", "Issue"]
calendars = ["Task", "Production Order", "Leave Application", "Sales Order", "Holiday List"]
@ -133,7 +133,8 @@ standard_portal_menu_items = [
{"title": _("Prescription"), "route": "/prescription", "reference_doctype": "Consultation", "role":"Patient"},
{"title": _("Patient Appointment"), "route": "/patient-appointments", "reference_doctype": "Patient Appointment", "role":"Patient"},
{"title": _("Fees"), "route": "/fees", "reference_doctype": "Fees", "role":"Student"},
{"title": _("Newsletter"), "route": "/newsletters", "reference_doctype": "Newsletter"}
{"title": _("Newsletter"), "route": "/newsletters", "reference_doctype": "Newsletter"},
{"title": _("Admission"), "route": "/admissions", "reference_doctype": "Student Admission"}
]
default_roles = [
@ -208,7 +209,7 @@ scheduler_events = {
"erpnext.stock.doctype.serial_no.serial_no.update_maintenance_status",
"erpnext.buying.doctype.supplier_scorecard.supplier_scorecard.refresh_scorecards",
"erpnext.setup.doctype.company.company.cache_companies_monthly_sales_history",
"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms",
"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms"
]
}

View File

@ -149,7 +149,7 @@
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 1,
"in_standard_filter": 0,
"in_standard_filter": 1,
"label": "Full Name",
"length": 0,
"no_copy": 0,
@ -431,7 +431,7 @@
"no_copy": 0,
"oldfieldname": "gender",
"oldfieldtype": "Select",
"options": "Gender",
"options": "Gender",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -2432,7 +2432,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-06-13 14:29:13.694009",
"modified": "2017-10-04 11:42:02.495731",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee",

View File

@ -21,7 +21,7 @@ class JobOpening(WebsiteGenerator):
self.route = frappe.scrub(self.job_title).replace('_', '-')
def get_context(self, context):
context.parents = [{'name': 'jobs', 'title': _('All Jobs') }]
context.parents = [{'route': 'jobs', 'title': _('All Jobs') }]
def get_list_context(context):
context.title = _("Jobs")

View File

@ -284,7 +284,7 @@ class ProcessPayroll(Document):
})
# Deductions
for acc, amt in deductions.items():
for acc, amount in deductions.items():
payable_amount -= flt(amount, precision)
accounts.append({
"account": acc,

View File

@ -12,6 +12,7 @@
"editable_grid": 1,
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -42,6 +43,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -73,6 +75,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -101,6 +104,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -131,6 +135,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -159,6 +164,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -189,6 +195,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -221,6 +228,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -253,6 +261,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -284,6 +293,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -314,6 +324,37 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "do_not_include_in_total",
"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": "Do not include in total",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -345,6 +386,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -374,6 +416,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -415,8 +458,8 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-04-13 00:47:33.980646",
"modified_by": "chude.osiegbu@manqala.com",
"modified": "2017-10-02 13:57:22.769751",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Detail",
"name_case": "",

View File

@ -158,14 +158,18 @@ var calculate_earning_total = function(doc, dt, dn, reset_amount) {
var total_earn = 0;
for(var i = 0; i < tbl.length; i++){
if(cint(tbl[i].depends_on_lwp) == 1) {
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days) /
cint(doc.total_working_days)*100)/100;
refresh_field('amount', tbl[i].name, 'earnings');
} else if(reset_amount) {
tbl[i].amount = tbl[i].default_amount;
refresh_field('amount', tbl[i].name, 'earnings');
}
total_earn += flt(tbl[i].amount);
if(!tbl[i].do_not_include_in_total) {
total_earn += flt(tbl[i].amount);
}
}
doc.gross_pay = total_earn;
refresh_many(['amount','gross_pay']);
@ -184,7 +188,9 @@ var calculate_ded_total = function(doc, dt, dn, reset_amount) {
tbl[i].amount = tbl[i].default_amount;
refresh_field('amount', tbl[i].name, 'deductions');
}
total_ded += flt(tbl[i].amount);
if(!tbl[i].do_not_include_in_total) {
total_ded += flt(tbl[i].amount);
}
}
doc.total_deduction = total_ded;
refresh_field('total_deduction');

View File

@ -69,7 +69,8 @@ class SalarySlip(TransactionBase):
'amount': amount,
'default_amount': amount,
'depends_on_lwp' : struct_row.depends_on_lwp,
'salary_component' : struct_row.salary_component
'salary_component' : struct_row.salary_component,
'do_not_include_in_total' : struct_row.do_not_include_in_total
})
else:
component_row.amount = amount
@ -345,11 +346,13 @@ class SalarySlip(TransactionBase):
(flt(d.default_amount) * flt(self.payment_days)
/ cint(self.total_working_days)), self.precision("amount", component_type)
)
elif not self.payment_days and not self.salary_slip_based_on_timesheet:
d.amount = 0
elif not d.amount:
d.amount = d.default_amount
self.set(total_field, self.get(total_field) + flt(d.amount))
if not d.do_not_include_in_total:
self.set(total_field, self.get(total_field) + flt(d.amount))
def calculate_net_pay(self):
if self.salary_structure:

View File

@ -2,6 +2,9 @@
// For license information, please see license.txt
frappe.ui.form.on('Training Event', {
onload_post_render: function(frm) {
frm.get_field("employees").grid.set_multiple_add("employee");
},
refresh: function(frm) {
if(!frm.doc.__islocal) {
frm.add_custom_button(__("Training Result"), function() {
@ -18,4 +21,4 @@ frappe.ui.form.on('Training Event', {
});
}
}
});
});

View File

@ -133,6 +133,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"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": 0,
"label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -684,7 +715,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-08-11 03:11:25.768563",
"modified": "2017-10-05 05:57:08.212658",
"modified_by": "Administrator",
"module": "HR",
"name": "Training Event",

View File

@ -147,7 +147,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Attendance",
"length": 0,
@ -176,7 +176,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-08-11 03:36:22.738253",
"modified": "2017-10-05 08:49:41.869998",
"modified_by": "Administrator",
"module": "HR",
"name": "Training Event Employee",

View File

@ -12,21 +12,21 @@
{% } %}
</tr>
</thead>
<tbody>
<tbody>
{% for(var j=0, k=data.length; j<k; j++) { %}
{%
{%
var row = data[j];
%}
<tr>
{% for(var i=1, l=report.columns.length; i<l; i++) { %}
<td class="text-right">
{% var fieldname = report.columns[i].field; %}
{% if (i > 10) { %}
{%= format_currency(row[fieldname]) %}
{% } else { %}
{% if (!is_null(row[fieldname])) { %}
{%= row[fieldname] %}
{% } %}
{% if (report.columns[i].fieldtype=='Currency' && !isNaN(row[fieldname])) { %}
{%= format_currency(row[fieldname]) %}
{% } else { %}
{% if (!is_null(row[fieldname])) { %}
{%= row[fieldname] %}
{% } %}
{% } %}
</td>
{% } %}

View File

@ -19,6 +19,12 @@ def execute(filters=None):
row = [ss.name, ss.employee, ss.employee_name, ss.branch, ss.department, ss.designation,
ss.company, ss.start_date, ss.end_date, ss.leave_withut_pay, ss.payment_days]
if not ss.branch == None:columns[3] = columns[3].replace('-1','120')
if not ss.department == None: columns[4] = columns[4].replace('-1','120')
if not ss.designation == None: columns[5] = columns[5].replace('-1','120')
if not ss.leave_withut_pay == None: columns[9] = columns[9].replace('-1','130')
for e in earning_types:
row.append(ss_earning_map.get(ss.name, {}).get(e))
@ -34,12 +40,20 @@ def execute(filters=None):
return columns, data
def get_columns(salary_slips):
"""
columns = [
_("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140", _("Branch") + ":Link/Branch:120",
_("Department") + ":Link/Department:120", _("Designation") + ":Link/Designation:120",
_("Company") + ":Link/Company:120", _("Start Date") + "::80", _("End Date") + "::80", _("Leave Without Pay") + ":Float:130",
_("Payment Days") + ":Float:120"
]
"""
columns = [
_("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140", _("Branch") + ":Link/Branch:-1",
_("Department") + ":Link/Department:-1", _("Designation") + ":Link/Designation:-1",
_("Company") + ":Link/Company:120", _("Start Date") + "::80", _("End Date") + "::80", _("Leave Without Pay") + ":Float:-1",
_("Payment Days") + ":Float:120"
]
salary_components = {_("Earning"): [], _("Deduction"): []}

0
erpnext/hub/__init__.py Normal file
View File

View File

@ -2,16 +2,174 @@
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe, requests
import frappe, requests, json
from frappe.utils import now, nowdate, cint
from frappe.utils.nestedset import get_root_of
from frappe.contacts.doctype.contact.contact import get_default_contact
@frappe.whitelist()
def get_items(text, start, limit):
hub = frappe.get_single("Hub Settings")
response = requests.get(hub.hub_url + "/api/method/hub.hub.api.get_items", params={
"access_token": hub.access_token,
"text": text,
"start": start,
"limit": limit
})
response.raise_for_status()
return response.json().get("message")
def enable_hub():
hub_settings = frappe.get_doc('Hub Settings')
hub_settings.register()
frappe.db.commit()
return hub_settings
@frappe.whitelist()
def get_items(start=0, limit=20, category=None, order_by=None, text=None):
connection = get_connection()
filters = {
'hub_category': category,
}
if text:
filters.update({'item_name': ('like', '%' + text + '%')})
response = connection.get_list('Hub Item',
limit_start=start, limit_page_length=limit,
filters=filters)
return response
@frappe.whitelist()
def get_categories():
connection = get_connection()
response = connection.get_list('Hub Category')
return response
@frappe.whitelist()
def get_item_details(hub_sync_id):
connection = get_connection()
return connection.get_doc('Hub Item', hub_sync_id)
@frappe.whitelist()
def get_company_details(hub_sync_id):
connection = get_connection()
return connection.get_doc('Hub Company', hub_sync_id)
def get_connection():
hub_connector = frappe.get_doc(
'Data Migration Connector', 'Hub Connector')
hub_connection = hub_connector.get_connection()
# frappeclient connection
return hub_connection.connection
def make_opportunity(buyer_name, email_id):
buyer_name = "HUB-" + buyer_name
if not frappe.db.exists('Lead', {'email_id': email_id}):
lead = frappe.new_doc("Lead")
lead.lead_name = buyer_name
lead.email_id = email_id
lead.save(ignore_permissions=True)
o = frappe.new_doc("Opportunity")
o.enquiry_from = "Lead"
o.lead = frappe.get_all("Lead", filters={"email_id": email_id}, fields = ["name"])[0]["name"]
o.save(ignore_permissions=True)
@frappe.whitelist()
def make_rfq_and_send_opportunity(item, supplier):
supplier = make_supplier(supplier)
contact = make_contact(supplier)
item = make_item(item)
rfq = make_rfq(item, supplier, contact)
status = send_opportunity(contact)
return {
'rfq': rfq,
'hub_document_created': status
}
def make_supplier(supplier):
# make supplier if not already exists
supplier = frappe._dict(json.loads(supplier))
if not frappe.db.exists('Supplier', {'supplier_name': supplier.supplier_name}):
supplier_doc = frappe.get_doc({
'doctype': 'Supplier',
'supplier_name': supplier.supplier_name,
'supplier_type': supplier.supplier_type,
'supplier_email': supplier.supplier_email
}).insert()
else:
supplier_doc = frappe.get_doc('Supplier', supplier.supplier_name)
return supplier_doc
def make_contact(supplier):
contact_name = get_default_contact('Supplier', supplier.supplier_name)
# make contact if not already exists
if not contact_name:
contact = frappe.get_doc({
'doctype': 'Contact',
'first_name': supplier.supplier_name,
'email_id': supplier.supplier_email,
'is_primary_contact': 1,
'links': [
{'link_doctype': 'Supplier', 'link_name': supplier.supplier_name}
]
}).insert()
else:
contact = frappe.get_doc('Contact', contact_name)
return contact
def make_item(item):
# make item if not already exists
item = frappe._dict(json.loads(item))
if not frappe.db.exists('Item', {'item_code': item.item_code}):
item_doc = frappe.get_doc({
'doctype': 'Item',
'item_code': item.item_code,
'item_group': item.item_group,
'is_item_from_hub': 1
}).insert()
else:
item_doc = frappe.get_doc('Item', item.item_code)
return item_doc
def make_rfq(item, supplier, contact):
# make rfq
rfq = frappe.get_doc({
'doctype': 'Request for Quotation',
'transaction_date': nowdate(),
'status': 'Draft',
'company': frappe.db.get_single_value('Hub Settings', 'company'),
'message_for_supplier': 'Please supply the specified items at the best possible rates',
'suppliers': [
{ 'supplier': supplier.name, 'contact': contact.name }
],
'items': [
{
'item_code': item.item_code,
'qty': 1,
'schedule_date': nowdate(),
'warehouse': item.default_warehouse or get_root_of("Warehouse"),
'description': item.description,
'uom': item.stock_uom
}
]
}).insert()
rfq.save()
rfq.submit()
return rfq
def send_opportunity(contact):
# Make Hub Message on Hub with lead data
doc = {
'doctype': 'Lead',
'lead_name': frappe.db.get_single_value('Hub Settings', 'company'),
'email_id': frappe.db.get_single_value('Hub Settings', 'user')
}
args = frappe._dict(dict(
doctype='Hub Message',
reference_doctype='Lead',
data=json.dumps(doc),
user=contact.email_id
))
connection = get_connection()
response = connection.insert('Hub Message', args)
return response.ok

29
erpnext/hub_node/api.py Normal file
View File

@ -0,0 +1,29 @@
# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
import frappe, json
from frappe.utils import now, nowdate
from erpnext.hub_node.doctype.hub_settings.hub_settings import get_hub_settings
# API wrapper
@frappe.whitelist(allow_guest=True)
def call_method(access_token, method, message):
try:
args = json.loads(message)
if args:
return globals()[method](access_token, args)
else:
return globals()[method](access_token)
except:
print("Client Exception")
print(frappe.get_traceback())
def disable_and_suspend_hub_user(access_token):
hub_settings = get_hub_settings()
hub_settings.publish = 0
hub_settings.publish_pricing = 0
hub_settings.publish_availability = 0
hub_settings.suspended = 1
hub_settings.enabled = 0
hub_settings.save(ignore_permissions=True)

View File

@ -0,0 +1,45 @@
{
"condition": "{'name': ('=', frappe.db.get_single_value('Hub Settings', 'company'))}",
"creation": "2017-09-07 11:38:43.169065",
"docstatus": 0,
"doctype": "Data Migration Mapping",
"fields": [
{
"is_child_table": 0,
"local_fieldname": "name",
"remote_fieldname": "company_name"
},
{
"is_child_table": 0,
"local_fieldname": "country",
"remote_fieldname": "country"
},
{
"is_child_table": 0,
"local_fieldname": "\"city\"",
"remote_fieldname": "seller_city"
},
{
"is_child_table": 0,
"local_fieldname": "eval:frappe.local.site",
"remote_fieldname": "site_name"
},
{
"is_child_table": 0,
"local_fieldname": "eval:frappe.session.user",
"remote_fieldname": "user"
}
],
"idx": 2,
"local_doctype": "Company",
"mapping_name": "Company to Hub Company",
"mapping_type": "Push",
"migration_id_field": "hub_sync_id",
"modified": "2017-09-22 15:32:12.459172",
"modified_by": "Administrator",
"name": "Company to Hub Company",
"owner": "Administrator",
"page_length": 10,
"remote_objectname": "Hub Company",
"remote_primary_key": "name"
}

View File

@ -0,0 +1,29 @@
import frappe, json
def pre_process(doc):
return json.loads(doc['data'])
def post_process(remote_doc=None, local_doc=None, **kwargs):
if not local_doc:
return
hub_message = remote_doc
# update hub message on hub
hub_connector = frappe.get_doc('Data Migration Connector', 'Hub Connector')
connection = hub_connector.get_connection()
connection.update('Hub Message', dict(
status='Synced'
), hub_message['name'])
# make opportunity after lead is created
lead = local_doc
opportunity = frappe.get_doc({
'doctype': 'Opportunity',
'naming_series': 'OPTY-',
'enquiry_type': 'Sales',
'enquiry_from': 'Lead',
'status': 'Open',
'lead': lead.name,
'company': lead.company,
'transaction_date': frappe.utils.today()
}).insert()

View File

@ -0,0 +1,31 @@
{
"condition": "{'reference_doctype': 'Lead', 'user': frappe.db.get_single_value('Hub Settings', 'user'), 'status': 'Pending'}",
"creation": "2017-09-20 15:06:40.279930",
"docstatus": 0,
"doctype": "Data Migration Mapping",
"fields": [
{
"is_child_table": 0,
"local_fieldname": "email_id",
"remote_fieldname": "email_id"
},
{
"is_child_table": 0,
"local_fieldname": "lead_name",
"remote_fieldname": "lead_name"
}
],
"idx": 0,
"local_doctype": "Lead",
"local_primary_key": "email_id",
"mapping_name": "Hub Message to Lead",
"mapping_type": "Pull",
"migration_id_field": "hub_sync_id",
"modified": "2017-09-28 13:21:41.575155",
"modified_by": "faris@erpnext.com",
"name": "Hub Message to Lead",
"owner": "frappetest@gmail.com",
"page_length": 10,
"remote_objectname": "Hub Message",
"remote_primary_key": "name"
}

View File

@ -0,0 +1,54 @@
{
"creation": "2017-09-07 13:27:52.726350",
"docstatus": 0,
"doctype": "Data Migration Mapping",
"fields": [
{
"is_child_table": 0,
"local_fieldname": "item_code",
"remote_fieldname": "item_code"
},
{
"is_child_table": 0,
"local_fieldname": "item_name",
"remote_fieldname": "item_name"
},
{
"is_child_table": 0,
"local_fieldname": "eval:frappe.db.get_default(\"company\")",
"remote_fieldname": "company_name"
},
{
"is_child_table": 0,
"local_fieldname": "image",
"remote_fieldname": "image"
},
{
"is_child_table": 0,
"local_fieldname": "item_group",
"remote_fieldname": "item_group"
},
{
"is_child_table": 0,
"local_fieldname": "eval:frappe.session.user",
"remote_fieldname": "seller"
},
{
"is_child_table": 0,
"local_fieldname": "eval:frappe.db.get_default(\"country\")",
"remote_fieldname": "country"
}
],
"idx": 1,
"local_doctype": "Item",
"mapping_name": "Item to Hub Item",
"mapping_type": "Push",
"migration_id_field": "hub_sync_id",
"modified": "2017-09-22 15:32:12.674169",
"modified_by": "Administrator",
"name": "Item to Hub Item",
"owner": "Administrator",
"page_length": 10,
"remote_objectname": "Hub Item",
"remote_primary_key": "item_code"
}

View File

@ -0,0 +1,26 @@
{
"creation": "2017-09-07 11:39:38.445902",
"docstatus": 0,
"doctype": "Data Migration Plan",
"idx": 1,
"mappings": [
{
"enabled": 1,
"mapping": "Company to Hub Company"
},
{
"enabled": 1,
"mapping": "Item to Hub Item"
},
{
"enabled": 1,
"mapping": "Hub Message to Lead"
}
],
"modified": "2017-09-28 15:37:17.616828",
"modified_by": "faris@erpnext.com",
"module": "Hub Node",
"name": "Hub Sync",
"owner": "Administrator",
"plan_name": "Hub Sync"
}

View File

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

View File

@ -0,0 +1,275 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:hub_category_name",
"beta": 0,
"creation": "2017-08-22 11:31:10.410322",
"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": "hub_category_name",
"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": "Category Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "parent_hub_category",
"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": "Parent Category",
"length": 0,
"no_copy": 0,
"options": "Hub Category",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "is_group",
"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": "Is Group",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Description",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "lft",
"fieldtype": "Int",
"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": "Left",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rgt",
"fieldtype": "Int",
"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": "Right",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "old_parent",
"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": "Old Parent",
"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,
"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": 0,
"max_attachments": 0,
"modified": "2017-09-03 22:04:22.958831",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "Hub Category",
"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": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "hub_category_name",
"track_changes": 1,
"track_seen": 0
}

View File

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

View File

@ -0,0 +1,4 @@
frappe.treeview_settings["Hub Category"] = {
title: __("Hub Category"),
breadcrumb: "Hub"
}

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 Category", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Hub Category
() => frappe.tests.make('Hub Category', [
// 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) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
class TestHubCategory(unittest.TestCase):
pass

View File

@ -1,17 +1,87 @@
frappe.ui.form.on("Hub Settings", "onload", function(frm) {
if(!frm.doc.seller_country) {
frm.set_value("seller_country", frappe.defaults.get_default("Country"));
}
if(!frm.doc.seller_name) {
frm.set_value("seller_name", frappe.defaults.get_default("Company"));
}
});
frappe.ui.form.on("Hub Settings", {
refresh: function(frm) {
frm.add_custom_button(__('Logs'),
() => frappe.set_route('List', 'Data Migration Run', {
data_migration_plan: 'Hub Sync'
}));
frappe.ui.form.on("Hub Settings", "refresh", function(frm) {
// make mandatory if published
frm.toggle_reqd(["seller_name", "seller_email", "seller_country"], frm.doc.publish);
});
frm.trigger("enabled");
if (frm.doc.enabled) {
frm.add_custom_button(__('View Hub'),
() => frappe.set_route('hub'));
frm.add_custom_button(__('Sync'),
() => frm.call('sync'));
}
},
onload: function(frm) {
if(!frm.doc.country) {
frm.set_value("country", frappe.defaults.get_default("Country"));
}
if(!frm.doc.company) {
frm.set_value("company", frappe.defaults.get_default("Company"));
}
},
onload_post_render: function(frm) {
if(frm.get_field("unregister_from_hub").$input)
frm.get_field("unregister_from_hub").$input.addClass("btn-danger");
},
on_update: function(frm) {
},
enabled: function(frm) {
if(!frm.doc.enabled) {
frm.trigger("set_enable_hub_primary_button");
} else {
frm.page.set_primary_action(__("Save Settings"), () => {
frm.save();
});
}
},
frappe.ui.form.on("Hub Settings", "publish", function(frm) {
frm.trigger("refresh");
hub_user_email: function(frm) {
if(frm.doc.hub_user_email){
frm.set_value("hub_user_name", frappe.user.full_name(frm.doc.hub_user_email));
}
},
set_enable_hub_primary_button: (frm) => {
frm.page.set_primary_action(__("Enable Hub"), () => {
if(frappe.session.user === "Administrator") {
frappe.msgprint("Please login as another user.")
} else {
frappe.verify_password(() => {
this.frm.call({
doc: this.frm.doc,
method: "register",
args: {},
freeze: true,
callback: function(r) {},
onerror: function() {
frappe.msgprint(__("Wrong Password"));
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) => {
frappe.verify_password(() => {
var d = frappe.confirm(__('Are you sure you want to unregister?'), () => {
frm.call('unregister');
}, () => {}, __('Confirm Action'));
d.get_primary_btn().addClass("btn-danger");
});
},
});

View File

@ -1,29 +1,41 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 1,
"creation": "2015-02-18 00:59:34.560476",
"custom": 0,
"description": "",
"docstatus": 0,
"doctype": "DocType",
"document_type": "System",
"document_type": "",
"editable_grid": 0,
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "publish",
"columns": 0,
"fieldname": "enabled",
"fieldtype": "Check",
"hidden": 0,
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"label": "Publish Items to Hub",
"in_standard_filter": 0,
"label": "Enabled",
"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,
@ -31,43 +43,29 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "publish",
"fieldname": "section_break_2",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "publish_pricing",
"columns": 0,
"fieldname": "suspended",
"fieldtype": "Check",
"hidden": 0,
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"label": "Publish Pricing",
"in_standard_filter": 0,
"label": "Suspended",
"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,
@ -75,132 +73,124 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "publish_availability",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Publish Availability",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_5",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "sync_now",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Sync Now",
"no_copy": 0,
"options": "sync",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "publish",
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "seller_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Seller Name",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "seller_country",
"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": 0,
"label": "Seller Country",
"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": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "eval:(!doc.enabled)",
"columns": 0,
"depends_on": "",
"fieldname": "seller_profile_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Company and Seller Profile",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"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": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "country",
"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": "Country",
"length": 0,
"no_copy": 0,
"options": "Country",
"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,
@ -208,131 +198,29 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "seller_email",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Seller Email",
"no_copy": 0,
"options": "Email",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_10",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "seller_city",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Seller City",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "seller_website",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Seller Website",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "publish",
"fieldname": "section_break_13",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "seller_description",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"label": "Seller Description",
"in_standard_filter": 0,
"label": "Description",
"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,
@ -340,21 +228,30 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "name_token",
"fieldtype": "Data",
"hidden": 1,
"columns": 0,
"depends_on": "enabled",
"fieldname": "publish_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"label": "Name Token",
"in_standard_filter": 0,
"label": "Publish",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@ -362,21 +259,216 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "access_token",
"fieldtype": "Data",
"hidden": 1,
"columns": 0,
"fieldname": "publish",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"label": "Access Token",
"in_standard_filter": 0,
"label": "Publish Items to Hub",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "publish",
"fieldname": "publish_pricing",
"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": "Publish Pricing",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:(doc.publish && doc.publish_pricing)",
"fieldname": "selling_price_list",
"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": "Selling Price List",
"length": 0,
"no_copy": 0,
"options": "Price List",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "publish",
"fieldname": "publish_availability",
"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": "Publish Availability",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "publish",
"fieldname": "last_sync_datetime",
"fieldtype": "Datetime",
"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": "Last Sync On",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": "",
"columns": 0,
"depends_on": "enabled",
"fieldname": "unregister_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Unregister",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "unregister_from_hub",
"fieldtype": "Button",
"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": "Unregister from Hub",
"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,
@ -384,15 +476,18 @@
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"modified": "2015-02-18 08:14:46.140473",
"modified_by": "Administrator",
"max_attachments": 0,
"modified": "2017-09-21 12:13:50.841646",
"modified_by": "manas@erpnext.com",
"module": "Hub Node",
"name": "Hub Settings",
"name_case": "",
@ -419,8 +514,12 @@
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC"
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -3,94 +3,103 @@
from __future__ import unicode_literals
import frappe, requests, json
from frappe.model.document import Document
from frappe.utils import cint, expand_relative_urls
from frappe import _
from frappe.model.document import Document
from frappe.utils import add_years, now, get_datetime, get_datetime_str
from frappe import _
from erpnext.utilities.product import get_price, get_qty_in_stock
from six import string_types
# hub_url = "http://erpnext.hub:8000"
hub_url = "https://hub.erpnext.org"
# hub_url = "http://192.168.29.145:3000"
class HubSetupError(frappe.ValidationError): pass
class HubSettings(Document):
hub_url = "http://localhost:8001"
def validate(self):
if cint(self.publish):
if not self.name_token:
self.register()
else:
self.update_seller_details()
self.publish_selling_items()
else:
if self.name_token:
self.unpublish()
def publish_selling_items(self):
"""Set `publish_in_hub`=1 for all Sales Items"""
for item in frappe.get_all("Item", fields=["name"],
filters={ "publish_in_hub": "0"}):
frappe.db.set_value("Item", item.name, "publish_in_hub", 1)
def validate(self):
if self.publish_pricing and not self.selling_price_list:
frappe.throw(_("Please select a Price List to publish pricing"))
def get_hub_url(self):
return hub_url
def sync(self):
"""Create and execute Data Migration Run for Hub Sync plan"""
frappe.has_permission('Hub Settings', throw=True)
doc = frappe.get_doc({
'doctype': 'Data Migration Run',
'data_migration_plan': 'Hub Sync',
'data_migration_connector': 'Hub Connector'
}).insert()
doc.run()
def register(self):
"""Register at hub.erpnext.com, save `name_token` and `access_token`"""
response = requests.post(self.hub_url + "/api/method/hub.hub.api.register", data=self.get_args())
response.raise_for_status()
response = response.json()
self.name_token = response.get("message").get("name")
self.access_token = response.get("message").get("access_token")
def unpublish(self):
"""Unpublish from hub.erpnext.com"""
response = requests.post(self.hub_url + "/api/method/hub.hub.api.unpublish", data={
"access_token": self.access_token
})
response.raise_for_status()
def update_seller_details(self):
"""Update details at hub.erpnext.com"""
args = self.get_args()
args["published"] = 1
response = requests.post(self.hub_url + "/api/method/hub.hub.api.update_seller", data={
"access_token": self.access_token,
"args": json.dumps(args)
})
response.raise_for_status()
def get_args(self):
return {
"seller_name": self.seller_name,
"seller_country": self.seller_country,
"seller_city": self.seller_city,
"seller_email": self.seller_email,
"seller_website": self.seller_website,
"seller_description": self.seller_description
""" Create a User on hub.erpnext.org and return username/password """
data = {
'email': frappe.session.user
}
post_url = hub_url + '/api/method/hub.hub.api.register'
def sync(self, verbose=True):
"""Sync items with hub.erpnext.com"""
if not self.publish:
if verbose:
frappe.msgprint(_("Publish to sync items"))
response = requests.post(post_url, data=data)
response.raise_for_status()
message = response.json().get('message')
if message and message.get('password'):
self.user = frappe.session.user
self.create_hub_connector(message)
self.company = frappe.defaults.get_user_default('company')
self.enabled = 1
self.save()
def unregister(self):
""" Disable the User on hub.erpnext.org"""
hub_connector = frappe.get_doc(
'Data Migration Connector', 'Hub Connector')
connection = hub_connector.get_connection()
response_doc = connection.update('User', frappe._dict({'enabled': 0}), hub_connector.username)
if response_doc['enabled'] == 0:
self.enabled = 0
self.save()
def create_hub_connector(self, message):
if frappe.db.exists('Data Migration Connector', 'Hub Connector'):
hub_connector = frappe.get_doc('Data Migration Connector', 'Hub Connector')
hub_connector.username = message['email']
hub_connector.password = message['password']
hub_connector.save()
return
items = frappe.db.get_all("Item",
fields=["name", "item_name", "description", "image", "item_group"],
filters={"publish_in_hub": 1, "synced_with_hub": 0})
frappe.get_doc({
'doctype': 'Data Migration Connector',
'connector_type': 'Frappe',
'connector_name': 'Hub Connector',
'hostname': hub_url,
'username': message['email'],
'password': message['password']
}).insert()
for item in items:
item.item_code = item.name
if item.image:
item.image = expand_relative_urls(item.image)
def reset_hub_publishing_settings(last_sync_datetime = ""):
doc = frappe.get_doc("Hub Settings", "Hub Settings")
doc.reset_publishing_settings(last_sync_datetime)
doc.in_callback = 1
doc.save()
item_list = frappe.db.sql_list("select name from tabItem where publish_in_hub=1")
def reset_hub_settings(last_sync_datetime = ""):
doc = frappe.get_doc("Hub Settings", "Hub Settings")
doc.reset_publishing_settings(last_sync_datetime)
doc.reset_enable()
doc.in_callback = 1
doc.save()
frappe.msgprint(_("Successfully unregistered."))
if items:
response = requests.post(self.hub_url + "/api/method/hub.hub.api.sync", data={
"access_token": self.access_token,
"items": json.dumps(items),
"item_list": json.dumps(item_list)
})
response.raise_for_status()
for item in items:
frappe.db.set_value("Item", item.name, "synced_with_hub", 1)
if verbose:
frappe.msgprint(_("{0} Items synced".format(len(items))))
else:
if verbose:
frappe.msgprint(_("Items already synced"))
@frappe.whitelist()
def sync():
hub_settings = frappe.get_doc('Hub Settings')
hub_settings.sync()

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 Settings", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially('Hub Settings', [
// insert a new Hub Settings
() => frappe.tests.make([
// 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) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
class TestHubSettings(unittest.TestCase):
pass

View File

@ -1,90 +1,864 @@
/* globals Hub, HubList */
frappe.provide('erpnext.hub');
frappe.pages['hub'].on_page_load = function(wrapper) {
var page = frappe.ui.make_app_page({
const page = frappe.ui.make_app_page({
parent: wrapper,
title: 'Hub',
single_column: true
single_col: false
});
frappe.hub = new frappe.Hub({page:page});
wrapper.hub_page = new erpnext.hub.Hub({ page });
};
frappe.pages['hub'].on_page_show = function(wrapper) {
const hub_page = wrapper.hub_page;
const [hub, type, id] = frappe.get_route();
if (!(hub || type || id)) {
hub_page.go_to_home_page();
return;
}
if (type === "Products") {
hub_page.go_to_item_page(id);
} else if (type === "Company") {
hub_page.go_to_company_page(id);
}
}
frappe.pages['hub'].on_page_show = function() {
frappe.hub.refresh();
}
erpnext.hub.Hub = class Hub {
constructor({ page }) {
this.page = page;
frappe.require('/assets/erpnext/css/hub.css', () => {
this.setup();
});
}
frappe.Hub = Class.extend({
init: function(args) {
$.extend(this, args);
this.render();
},
refresh: function() {
if(this.hub && this.hub.publish && !this.hub_list) {
this.setup_list();
}
},
render: function() {
this.page.main.empty();
var me = this;
frappe.model.with_doc("Hub Settings", "Hub Settings", function() {
me.hub = locals["Hub Settings"]["Hub Settings"];
if(!me.hub.publish) {
$(frappe.render_template("register_in_hub", {})).appendTo(me.page.main);
setup() {
this.setup_header();
this.company_cache = {};
this.item_cache = {};
this.filters = {};
this.order_by = '';
this.$hub_main_section =
$(`<div class='hub-main-section'>`).appendTo(this.page.body);
this.bind_events();
this.refresh();
}
refresh() {
this.$hub_main_section.empty();
this.page.page_form.hide();
const $layout_main = this.page.wrapper.find('.layout-main');
const $page_head = this.page.wrapper.find('.page-head');
frappe.model.with_doc('Hub Settings', 'Hub Settings', () => {
this.hub_settings = frappe.get_doc('Hub Settings');
if(this.hub_settings.enabled == 0) {
let $empty_state = this.page.get_empty_state(
__("Register for Hub"),
__(`Let other ERPNext users discover your products
and automate workflow with Supplier from within ERPNext.`),
__("Register")
);
$page_head.hide();
$layout_main
.find('.layout-side-section, .layout-main-section-wrapper')
.hide();
$layout_main.append($empty_state);
$empty_state.find('.btn-primary').on('click', () => {
this.register_for_hub();
});
} else {
me.setup_list();
$page_head.show();
$layout_main.find('.page-card-container').remove();
$layout_main.find('.layout-side-section, .layout-main-section-wrapper').show();
this.setup_live_state();
}
});
},
setup_list: function() {
var me = this;
$(frappe.render_template("hub_body", {})).appendTo(this.page.main);
this.hub_list = this.page.main.find(".hub-list");
this.search = this.page.main.find("input").on("keypress", function(e) {
if(e.which===13) {
me.reset();
}
}
register_for_hub() {
if (frappe.session.user.includes('Administrator')) {
frappe.throw(__('Please login as another user.'))
}
frappe.verify_password(() => {
frappe.call({
method: 'erpnext.hub_node.enable_hub',
callback: (r) => {
if(r.message.enabled == 1) {
Object.assign(this.hub_settings, r.message);
this.refresh();
this.prompt_for_item_sync();
}
}
});
});
this.loading = this.page.main.find(".loading");
this.done = this.page.main.find(".done");
this.more = this.page.main.find(".more")
this.more.find(".btn").on("click", function() { me.next_page() });
this.reset();
},
reset: function() {
this.hub_list.empty();
this.start = 0;
this.page_length = 20;
this.next_page();
},
next_page: function() {
var me = this;
this.loading.toggleClass("hide", false);
}
prompt_for_item_sync() {
frappe.call({
method: "erpnext.hub_node.get_items",
method: 'frappe.client.get_list',
args: {
text: this.get_text(),
start: this.start,
limit: this.page_length
doctype: 'Data Migration Run',
filters: {
'data_migration_plan': 'Hub Sync'
},
limit_page_length: 1
},
callback: function(r) {
me.loading.toggleClass("hide", true);
if(!r.message)
r.message = [];
me.start += r.message.length;
$(frappe.render_template("hub_list", {items: r.message})).appendTo(me.hub_list);
if(r.message.length && r.message.length===me.page_length) {
// more
me.more.removeClass("hide");
me.done.addClass("hide");
} else {
// done
me.more.addClass("hide");
me.done.removeClass("hide");
if (!r) {
frappe.confirm(__('Do you want to publish your Items to Hub ?'), () => {
this.sync_items_to_hub();
});
}
}
})
}
setup_header() {
this.page.page_title = this.page.wrapper.find('.page-title');
this.tag_line = $(`
<div class='tag-line-container'>
<span class='tag-line text-muted small'>
${__('Product listing and discovery for ERPNext users')}
</span>
</div>`)
.appendTo(this.page.page_title);
this.bind_title();
}
setup_live_state() {
if(!this.$search) {
this.setup_filters();
}
this.page.page_form.show();
this.setup_menu();
this.setup_sidebar();
this.render_body();
this.setup_lists();
}
setup_filters() {
// frappe.call({
// method: 'erpnext.hub_node.get_categories'
// }).then((r) => {
// if (r.message) {
// const categories = r.message;
// console.log("categories", categories);
// categories
// .map(c => c.hub_category_name)
// .map(c => this.sidebar.add_item({
// label: c,
// on_click: () => {
// this.home_item_list &&
// this.home_item_list.refresh({
// text: '',
// start: 0,
// limit: 20,
// category: c && c !== 'All Categories' ? c : undefined
// });
// }
// }, __('Hub Category')));
// }
// });
// this.category_select = this.page.add_select(__('Category'),
// [
// {label: __('Sort by Price ...'), value: '' },
// {label: __('High to Low'), value: 'price desc' },
// {label: __('Low to High'), value: 'price' },
// ]
// );
this.price_sort_select = this.page.add_select(__('Sort by Price'),
[
{label: __('Sort by Price ...'), value: '' },
{label: __('High to Low'), value: 'price desc' },
{label: __('Low to High'), value: 'price' },
]
);
this.criteria_select = this.page.add_select(__('Sort by Criteria'),
[
{label: __('Most Popular'), value: 'request_count' },
{label: __('Newest'), value: 'creation' },
]
);
this.price_sort_select.on('change', () => {
this.refresh_item_only_page();
});
},
get_text: function() {
return this.search.val();
},
})
this.criteria_select.on('change', () => {
this.refresh_item_only_page();
});
this.setup_hub_category_filter();
this.setup_search();
}
bind_events() {
const me = this;
this.$hub_main_section
.on('click', '.company-link a', function(e) {
e.preventDefault();
const company_name = $(this).attr('data-company-name');
me.get_company_details(company_name);
})
.on('click', '.breadcrumb li', function(e) {
e.preventDefault();
const $li = $(this);
if ($li.attr('data-route') === 'Home') {
me.go_to_home_page();
}
});
}
update_filters() {
let price_sort = $(this.price_sort_select).val() || '';
let criteria = $(this.criteria_select).val() || '';
let order_by_params = [];
let query_string = '';
if(criteria) {
order_by_params.push(criteria);
// query_string += 'sort_by=' + criteria
}
if(price_sort) order_by_params.push(price_sort);
this.order_by = order_by_params.join(",");
// return query_string;
}
reset_filters() {
this.order_by = '';
$(this.category_select).val('');
$(this.price_sort_select).val('');
$(this.criteria_select).val('Most Popular');
}
refresh_item_only_page() {
this.reset_search();
this.update_filters();
this.go_to_items_only_page(
['hub', 'Products'],
'', 'product-list'
);
}
bind_title() {
this.page.page_title.find('.title-text').on('click', () => {
this.go_to_home_page();
});
}
render_body() {
this.$home_page = $(`
<div class = 'hub-home-page'>
<div class='banner'></div>
<div class='listing-body row'>
<div class='main-list-section'></div>
</div>
</div>
`).appendTo(this.$hub_main_section);
this.$banner = this.$hub_main_section.find('.banner');
this.$listing_body = this.$hub_main_section.find('.listing-body');
this.$main_list_section = this.$hub_main_section.find('.main-list-section');
this.$side_list_section = this.$hub_main_section.find('.side-list-section');
}
setup_lists() {
this.home_item_list = new erpnext.hub.HubList({
parent: this.$main_list_section,
title: 'New',
page_length: 20,
list_css_class: 'home-product-list',
method: 'erpnext.hub_node.get_items',
// order_by: 'request_count',
filters: {text: '', country: this.country}, // filters at the time of creation
on_item_click: (item_code) => {
frappe.set_route('hub', 'Products', item_code);
}
});
this.home_item_list.setup();
}
setup_hub_category_filter() {
const me = this;
this.hub_category_field = this.page.add_field({
fieldtype: 'Autocomplete',
label: 'Hub Category',
change() {
let value = this.get_value();
let title = value;
if (value === 'All Categories') {
// show all items
value = null;
}
me.home_item_list.title = title;
me.home_item_list.refresh({
text: '',
start: 0,
limit: 20,
category: value
});
}
});
frappe.call('erpnext.hub_node.get_categories')
.then((r) => {
if (r.message) {
const categories = r.message;
this.hub_category_field.set_data(
categories.map(c => c.hub_category_name)
);
}
});
}
setup_search() {
this.$search = this.page.add_data(__('Search'));
this.$search.on('keypress', (e) => {
if(e.which === 13) {
var search_term = ($(this.$search).val() || '').toLowerCase();
this.go_to_items_only_page(
['hub', 'search', search_term],
'Search results for \'' + search_term + '\'',
'search-product-list',
{text: search_term}
);
}
});
}
go_to_items_only_page(route, title, class_name, filters = {text: ''}, by_item_codes=0) {
frappe.set_route(route);
this.$hub_main_section.empty();
this.filtered_item_list = new erpnext.hub.HubList({
parent: this.$hub_main_section,
title: title,
page_length: 20,
list_css_class: class_name,
method: 'erpnext.hub_node.get_items',
order_by: this.order_by,
filters: filters,
by_item_codes: by_item_codes
});
this.filtered_item_list.on_item_click = (item_code) => {
frappe.set_route('hub', 'Products', item_code);
}
this.filtered_item_list.setup();
}
go_to_item_page(item_code) {
if(this.item_cache) {
let item = this.item_cache[item_code];
if(item) {
this.render_item_page(item);
return;
}
} else {
this.item_cache = {};
}
frappe.call({
args:{
hub_sync_id: item_code
},
method: "erpnext.hub_node.get_item_details",
callback: (r) => {
let item = r.message;
this.item_cache[item_code] = item;
this.render_item_page(item);
}
});
}
render_item_page(item) {
this.$hub_main_section.empty();
let $item_page =
$(this.get_item_page(item))
.appendTo(this.$hub_main_section);
let $company_items = $item_page.find('.company-items');
let company_item_list = new erpnext.hub.HubList({
parent: $company_items,
title: 'More by ' + item.company_name,
page_length: 5,
list_css_class: 'company-item-list',
method: 'erpnext.hub_node.get_items',
// order_by: 'request_count',
filters: {text: '', company_name: item.company_name, country: this.country},
paginated: 0,
img_size: 150
});
company_item_list.on_item_click = (item_code) => {
frappe.set_route('hub', 'Products', item_code);
}
company_item_list.setup();
$item_page.find('.rfq-btn')
.click((e) => {
const $btn = $(e.target);
this.show_rfq_modal(item)
.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, $btn);
})
.then(r => {
console.log(r);
if (r.message && r.message.rfq) {
$btn.addClass('disabled').html(`<span><i class='fa fa-check'></i> ${__('Quote Requested')}</span>`);
} else {
throw r;
}
})
.catch((e) => {
console.log(e); //eslint-disable-line
});
});
}
show_rfq_modal(item) {
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();
});
}
get_company_details(company_id) {
// get from cache if exists
let company_details = this.company_cache[company_id];
if(this.company_cache[company_id]) {
this.go_to_company_page(company_details);
return;
}
frappe.call({
method: 'erpnext.hub_node.get_company_details',
args: {company_id: company_id}
}).then((r) => {
if (r.message) {
const company_details = r.message.company_details;
this.company_cache[company_id] = company_details;
this.go_to_company_page(company_details)
}
});
}
go_to_company_page(company_details) {
frappe.set_route('hub', 'Company', company_details.company_name);
this.$hub_main_section.empty();
let $company_page =
$(this.get_company_page(company_details))
.appendTo(this.$hub_main_section);
let $company_items = $company_page.find('.company-items');
let company_item_list = new erpnext.hub.HubList({
parent: $company_items,
title: 'More by ' + company_details.company_name,
page_length: 5,
list_css_class: 'company-item-list',
method: 'erpnext.hub_node.get_items',
// order_by: 'request_count',
filters: {text: '', company: company_details.company_name, country: this.country},
paginated: 0,
img_size: 150
});
company_item_list.on_item_click = (item_code) => {
frappe.set_route('hub', 'Products', item_code);
}
company_item_list.setup();
}
get_item_page(item) {
return `
<div class="hub-item-page">
<div class="item-header">
<div class="item-page-image">
${ this.home_item_list.get_item_image(item) }
</div>
<div class="title-content">
<div class="breadcrumbs">
${this.get_breadcrumb(item.item_name, "Products") }
</div>
<div class="title">
<h2>${ item.item_name }</h2>
</div>
<div class="company">
<span class="">${ item.company_name }</span>
</div>
<div class="category">
<span class="text-muted">Products</span>
</div>
<div class="description">
<span class="small">${ item.description ? item.description : "" }</span>
</div>
<div class="price">
${ item.formatted_price ? item.formatted_price : '' }
</div>
<div class="actions">
<a class="btn btn-primary rfq-btn">Request A Quote</a>
</div>
</div>
</div>
<div class="item-more-info"></div>
<div class="company-items">
</div>
</div>
`;
}
get_company_page(company_details) {
return `
<div class="hub-item-page">
<div class="item-header">
<div class="title-content">
<div class="breadcrumbs">
${this.get_breadcrumb(company_details.company_name, "Company") }
</div>
<div class="title">
<h2>${ company_details.company_name }</h2>
</div>
<div class="company">
<span class="">${ company_details.seller_city }</span>
</div>
<div class="description">
<span class="small">${ company_details.seller_description }</span>
</div>
</div>
</div>
<div class="item-more-info"></div>
<div class="company-items">
</div>
</div>
`;
}
get_breadcrumb(name, type) {
return `
<ul class="breadcrumb">
<li data-route="Home">
<a href><span>Home</span></a>
</li>
<li data-route="List">
<a href><span>${type}</span></a>
</li>
<li class="active">
<span>${name}</span>
</li>
</ul>
`;
}
go_to_home_page() {
frappe.set_route('hub');
this.reset_filters();
this.refresh();
}
setup_menu() {
if (this.menu_setup) return;
this.page.add_menu_item(__('Hub Settings'),
() => frappe.set_route('Form', 'Hub Settings'));
this.page.add_menu_item(__('Refresh'), () => this.refresh());
this.page.add_menu_item(__('Sync'), () => this.sync_items_to_hub());
this.menu_setup = true;
}
sync_items_to_hub() {
frappe.call('erpnext.hub_node.doctype.hub_settings.hub_settings.sync')
}
setup_sidebar() {
var me = this;
this.sidebar = new frappe.ui.Sidebar({
wrapper: this.page.wrapper.find('.layout-side-section'),
css_class: 'hub-sidebar'
});
this.add_account_to_sidebar();
}
add_account_to_sidebar() {
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"));
}
get_search_term() {
return this.$search.val();
}
reset_search() {
this.$search.val('');
}
make_rfq(item, supplier, btn) {
console.log(supplier);
return new Promise((resolve, reject) => {
frappe.call({
method: 'erpnext.hub_node.make_rfq_and_send_opportunity',
args: { item, supplier },
callback: resolve,
btn,
}).fail(reject);
});
}
go_to_seen_items() {
this.go_to_items_only_page(
['hub', 'Requested Products'],
__('Requested Products'),
'requested-product-list',
{}, 1
);
}
}
erpnext.hub.HubList = class HubList {
constructor({
parent = null,
title = 'Products',
page_length = 20,
list_css_class = '',
method = 'erpnext.hub_node.get_items',
filters = {text: ''},
order_by = '',
by_item_codes = 0,
paginated = 1,
on_item_click = null,
img_size = 200
}) {
this.parent = parent;
this.title = title;
this.page_length = page_length;
this.list_css_class = list_css_class;
this.method = method;
this.filters = filters;
this.order_by = order_by;
this.by_item_codes = by_item_codes;
this.paginated = paginated;
this.on_item_click = on_item_click;
this.img_size = img_size;
}
// to be called on demand
setup() {
this.container = $(`
<div class='item-list-container ${this.list_css_class}' data-page-length='${this.page_length}'>
<div class='item-list-header'>
<h3>${this.title}</h3>
</div>
<div class='item-list'></div>
<div class='list-state'>
<div class='loading'>
<p class='text-muted text-center'>${__('Loading...')}</p>
</div>
<div class='done hide'>
<p class='text-muted text-center'>${__('No more results')}</p>
</div>
<div class='more text-right'>
<button class='btn btn-default btn-sm'>${__('More')}</div>
</div>
</div>
</div>`)
.appendTo(this.parent);
this.$item_list_title = this.container.find('.item-list-header h3');
this.$list = this.container.find('.item-list');
this.$loading = this.container.find('.loading').hide();
this.$more = this.container.find('.more').hide();
this.$done = this.container.find('.done');
this.$more.on('click', () => {
this.next_page();
});
this.next_page();
}
refresh(filters = this.filters) {
this.reset();
this.set_filters(filters);
this.next_page();
}
reset() {
this.$list.empty();
}
set_filters(filters) {
this.filters = filters;
}
next_page() {
this.$item_list_title.html(this.title);
const start = this.$list.find('.hub-item-wrapper').length;
this.$loading.show();
// build args
let args = {
start: start,
// query one extra
limit: this.page_length + 1
};
Object.assign(args, this.filters);
console.log("filters: ", args);
args.order_by = this.order_by;
args.by_item_codes = this.by_item_codes;
frappe.call({
method: this.method,
args: args,
callback: (r) => {
let items = r.message;
console.log("items: ", items);
this.render_items(items);
}
});
}
render_items(items) {
if(items) {
// clear any filler divs
this.$list.find('.filler').remove();
let done = 0;
console.log("items length", items.length);
if(items.length && items.length > this.page_length) {
// remove the extra queried
items.pop();
} else {
done = 1;
}
items.forEach((item) => {
this.make_item_card(item).appendTo(this.$list);
});
const remainder = items.length % 4;
if (remainder > 0) {
// fill with filler divs to make flexbox happy
Array.from(Array(remainder))
.map(r => $('<div class="filler">').css('width', '200px').appendTo(this.$list));
}
this.update_list_state(done);
} else {
this.update_list_state(1);
}
}
update_list_state(done=0) {
this.$loading.hide();
if(done) {
this.$done.removeClass('hide');
this.$more.hide();
} else {
this.$more.show();
this.$done.addClass('hide');
}
}
make_item_card(item) {
let $item_card = $(`
<div class="hub-item-wrapper" style="max-width: ${this.img_size}px;">
<a class="item-link" href>
<div class="hub-item-image">
${ this.get_item_image(item) }
</div>
<div class="hub-item-title">
<h5 class="bold">
${!item.seen ? item.item_name : `<span class="indicator blue">${item.item_name}</span>`}
<h5>
</div>
</a>
<div class="company-link">
<a data-company-name="${ item.company_id }" class="">${ item.company_name }</a>
</div>
<div>${ item.formatted_price ? item.formatted_price : ''}</div>
</div>
`);
$item_card.find(".item-link").click((e) => {
e.preventDefault();
this.on_item_click && this.on_item_click(item.name);
});
return $item_card;
}
get_item_image(item, size=this.img_size) {
const _size = size + 'px';
const item_image = item.image ?
`<img src="${item.image}"><span class="helper"></span>` :
`<div class="standard-image">${item.item_name[0]}</div>`;
return `
<div class="img-wrapper"
style="max-width: ${_size}; width: ${_size}; height: ${_size};">
${item_image}
</div>`;
}
}

View File

@ -1,20 +0,0 @@
<div class="padding">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<input class="form-control" name="search" placeholder="{%= __("Search") %}">
</div>
</div>
<hr style="margin: 15px -15px">
<div class="hub-list">
</div>
<div class="loading">
<p class="text-muted text-center">{%= __("Loading...") %}</p>
</div>
<div class="more text-center hide">
<button class="btn btn-default btn-sm">{%= __("More") %}</div>
</div>
<div class="done text-center text-extra-muted hide">
<p>{%= __("No more results.") %}</p>
</div>
</div>

View File

@ -1,16 +0,0 @@
{% for(var i=0, l=items.length; i < l; i++) { %}
<div class="list-item">
<div class="row">
<div class="col-sm-6">
<h6>{%= items[i].item_name %}<h6>
</div>
<div class="col-sm-3">
<h6>{%= items[i].seller_name %}<h6>
</div>
<div class="col-sm-3 text-right">
<h6 class="text-muted">{%= items[i].seller_country %}<h6>
</div>
</div>
<p class="text-muted small">{%= items[i].description %}</p>
</div>
{% } %}

View File

@ -1,19 +0,0 @@
<div style="padding: 70px 0px;">
<h2 class="text-center">{%= __("Register For ERPNext Hub") %}</h2>
<br>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<ul>
<li>Free listing of your products</li>
<li>Let other ERPNext users discover your products</li>
<li>Discover products quickly</li>
<li>Automate workflow with Supplier from within ERPNext (later)</li>
</ul>
</div>
</div>
<br>
<div class="text-center">
<a class="btn btn-primary" href="#Form/Hub Settings">
Hub Settings</a>
</div>
</div>

View File

@ -30,12 +30,13 @@ class BOMUpdateTool(Document):
frappe.throw(_("The selected BOMs are not for the same item"))
def update_new_bom(self):
current_bom_unitcost = frappe.db.sql("""select total_cost/quantity
from `tabBOM` where name = %s""", self.current_bom)
current_bom_unitcost = current_bom_unitcost and flt(current_bom_unitcost[0][0]) or 0
new_bom_unitcost = frappe.db.sql("""select total_cost/quantity
from `tabBOM` where name = %s""", self.new_bom)
new_bom_unitcost = flt(new_bom_unitcost[0][0]) if new_bom_unitcost else 0
frappe.db.sql("""update `tabBOM Item` set bom_no=%s,
rate=%s, amount=stock_qty*%s where bom_no = %s and docstatus < 2""",
(self.new_bom, current_bom_unitcost, current_bom_unitcost, self.current_bom))
(self.new_bom, new_bom_unitcost, new_bom_unitcost, self.current_bom))
def get_parent_boms(self):
return [d[0] for d in frappe.db.sql("""select distinct parent

View File

@ -626,4 +626,4 @@ def stop_unstop(production_order, status):
frappe.msgprint(_("Production Order has been {0}").format(status))
pro_order.notify_update()
return pro_order.status
return pro_order.status

View File

@ -515,7 +515,8 @@ class ProductionPlanningTool(Document):
"transaction_date": nowdate(),
"status": "Draft",
"company": self.company,
"requested_by": frappe.session.user
"requested_by": frappe.session.user,
"schedule_date": add_days(nowdate(), cint(item_wrapper.lead_time_days)),
})
material_request.update({"material_request_type": item_wrapper.default_material_request_type})

View File

@ -0,0 +1,27 @@
<h1 class="text-left"><b>{%= __("BOM Stock Report") %}</b></h1>
<h5 class="text-left">{%= filters.bom %}</h5>
<h5 class="text-left">{%= filters.warehouse %}</h5>
<hr>
<table class="table table-bordered">
<thead>
<tr>
<th style="width: 15%">{%= __("Item") %}</th>
<th style="width: 35%">{%= __("Description") %}</th>
<th style="width: 14%">{%= __("Required Qty") %}</th>
<th style="width: 13%">{%= __("In Stock Qty") %}</th>
<th style="width: 23%">{%= __("Enough Parts to Build") %}</th>
</tr>
</thead>
<tbody>
{% for(var i=0, l=data.length; i<l; i++) { %}
<tr>
<td>{%= data[i][ __("Item")] %}</td>
<td>{%= data[i][ __("Description")] %} </td>
<td align="right">{%= data[i][ __("Required Qty")] %} </td>
<td align="right">{%= data[i][ __("In Stock Qty")] %} </td>
<td align="right">{%= data[i][ __("Enough Parts to Build")] %} </td>
</tr>
{% } %}
</tbody>
</table>

View File

@ -448,6 +448,8 @@ erpnext.patches.v8_9.remove_employee_from_salary_structure_parent
erpnext.patches.v8_9.delete_gst_doctypes_for_outside_india_accounts
erpnext.patches.v8_9.set_default_fields_in_variant_settings
erpnext.patches.v8_9.update_billing_gstin_for_indian_account
erpnext.patches.v9_0.fix_subscription_next_date
erpnext.patches.v9_0.set_schedule_date_for_material_request_and_purchase_order
erpnext.patches.v8_10.add_due_date_to_gle
erpnext.patches.v8_10.update_gl_due_date_for_pi_and_si
erpnext.patches.v8_10.add_payment_terms_field_to_supplier

View File

@ -0,0 +1,27 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doctype('Subscription')
doctypes = ('Purchase Order', 'Sales Order', 'Purchase Invoice', 'Sales Invoice')
for data in frappe.get_all('Subscription', fields = ["name", "reference_doctype", "reference_document"],
filters = {'reference_doctype': ('in', doctypes)}):
doc = frappe.get_doc('Subscription', data.name)
fields = ['transaction_date']
if doc.reference_doctype in ['Sales Invoice', 'Purchase Invoice']:
fields = ['posting_date']
fields.extend(['from_date', 'to_date'])
reference_data = frappe.db.get_value(data.reference_doctype,
data.reference_document, fields, as_dict=1)
if reference_data:
doc.start_date = reference_data.get('posting_date') or reference_data.get('transaction_date')
doc.from_date = reference_data.get('from_date')
doc.to_date = reference_data.get('to_date')
doc.set_next_schedule_date()
doc.db_update()

View File

@ -0,0 +1,22 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
for doctype in ("Material Request", "Purchase Order"):
frappe.reload_doctype(doctype)
frappe.reload_doctype(doctype + " Item")
if not frappe.db.has_column(doctype, "schedule_date"):
continue
#Update only submitted MR
for record in frappe.get_all(doctype, filters= [["docstatus", "=", 1]], fields=["name"]):
doc = frappe.get_doc(doctype, record)
if doc.items:
if not doc.schedule_date:
min_schedule_date = min([d.schedule_date for d in doc.items])
frappe.db.set_value(doctype, record,
"schedule_date", min_schedule_date, update_modified=False)

104
erpnext/public/css/hub.css Normal file
View File

@ -0,0 +1,104 @@
/* 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

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg3718"
xml:space="preserve"
width="162.12097"
height="162.3004"
viewBox="0 0 162.12098 162.3004"
sodipodi:docname="hub_logo.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"><metadata
id="metadata3724"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs3722"><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath3734"><path
d="M 0,600 H 1000 V 0 H 0 Z"
id="path3732"
inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="740"
inkscape:window-height="449"
id="namedview3720"
showgrid="false"
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>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -438,4 +438,4 @@ body[data-route="pos"] {
.list-item_content {
padding-right: 45px;
}
}
}

View File

@ -0,0 +1,140 @@
@import "../../../../frappe/frappe/public/less/variables.less";
/* hub */
div[data-page-route="hub"] {
.page-head {
height: 80px;
.title-text {
cursor: pointer;
}
}
.page-content {
margin-top: 80px;
}
.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 {
display: flex;
}
.item-list {
display: flex;
flex-wrap: wrap;
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 {
width: 100%;
}
.breadcrumb {
padding-left: 0;
padding-top: 0;
margin-bottom: 10px;
}
}

View File

@ -1,6 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_guest_to_view": 1,
"allow_import": 0,
"allow_rename": 1,
"autoname": "",
@ -12,97 +12,6 @@
"document_type": "Document",
"editable_grid": 1,
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "academic_year",
"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": 1,
"label": "Academic Year",
"length": 0,
"no_copy": 0,
"options": "Academic Year",
"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
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "admission_start_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": "Admission Start 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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "admission_end_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": "Admission End 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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -139,7 +48,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "naming_series_for_student_applicant",
"depends_on": "",
"fieldname": "route",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
@ -148,9 +58,9 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Naming Series (for Student Applicant)",
"label": "Route",
"length": 0,
"no_copy": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -161,7 +71,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
"unique": 1
},
{
"allow_bulk_edit": 0,
@ -169,8 +79,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "publish",
"fieldtype": "Check",
"fieldname": "application_form_route",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -178,7 +88,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Publish on website",
"label": "Application Form Route",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -188,7 +98,7 @@
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
@ -228,112 +138,19 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "program",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Program",
"length": 0,
"no_copy": 0,
"options": "Program",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "application_fee",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Application Fee",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "currency",
"fieldname": "academic_year",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Currency",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Academic Year",
"length": 0,
"no_copy": 0,
"options": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "route",
"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": "Route",
"length": 0,
"no_copy": 0,
"no_copy": 1,
"options": "Academic Year",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -344,7 +161,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 1
"unique": 0
},
{
"allow_bulk_edit": 0,
@ -352,8 +169,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "application_form_route",
"fieldtype": "Data",
"fieldname": "admission_start_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -361,7 +178,67 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Application Form Route",
"label": "Admission Start Date",
"length": 0,
"no_copy": 1,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "admission_end_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": "Admission End Date",
"length": 0,
"no_copy": 1,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "published",
"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": "Publish on website",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -391,6 +268,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Eligibility and Details",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -405,6 +283,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "program_details",
"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": "Eligibility and Details",
"length": 0,
"no_copy": 0,
"options": "Student Admission Program",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -434,49 +343,20 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "eligibility",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Eligibility",
"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,
"unique": 0
}
],
"has_web_view": 0,
"has_web_view": 1,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_published_field": "published",
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-06-30 08:21:50.722286",
"modified": "2017-10-02 15:16:44.386000",
"modified_by": "Administrator",
"module": "Schools",
"name": "Student Admission",
@ -504,10 +384,11 @@
"write": 1
}
],
"quick_entry": 1,
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Education",
"route": "admissions",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",

View File

@ -4,26 +4,41 @@
from __future__ import unicode_literals
import frappe
from frappe.website.website_generator import WebsiteGenerator
from frappe import _
from frappe.utils import nowdate
from frappe.website.website_generator import WebsiteGenerator
class StudentAdmission(WebsiteGenerator):
website = frappe._dict(
template = "templates/generators/student_admission.html",
condition_field = "publish",
page_title_field = "title"
)
def autoname(self):
if not self.title:
self.title = self.get_title()
self.name = self.title
def validate(self):
if not self.route: #pylint: disable=E0203
self.route = "admissions/" + "-".join(self.title.split(" "))
def get_context(self, context):
context.parents = [{'name': 'admissions', 'title': _('All Student Admissions') }]
context.no_cache = 1
context.show_sidebar = True
context.title = self.title
context.parents = [{'name': 'admissions', 'title': _('All Student Admissions'), 'route': 'admissions' }]
def get_title(self):
return _("Admissions for {0}").format(self.academic_year)
def get_list_context(context):
context.title = _("Student Admissions")
def get_list_context(context=None):
context.update({
"show_sidebar": True,
"title": _("Student Admissions"),
"get_list": get_admission_list,
"row_template": "schools/doctype/student_admission/templates/student_admission_row.html",
})
def get_admission_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"):
return frappe.db.sql('''select name, title, academic_year, modified, admission_start_date, route,
admission_end_date from `tabStudent Admission` where published=1 and admission_end_date >= %s
order by admission_end_date asc limit {0}, {1}
'''.format(limit_start, limit_page_length), [nowdate()], as_dict=1)

View File

@ -0,0 +1,74 @@
{% extends "templates/web.html" %}
{% block breadcrumbs %}
{% include "templates/includes/breadcrumbs.html" %}
{% endblock %}
{% block header %}
<h1>{{ title }}</h1>
{% endblock %}
{% block page_content %}
{% set today = frappe.utils.getdate(frappe.utils.nowdate()) %}
<div class="row transaction-subheading">
<div class="col-xs-6">
<span class="indicator
{% if frappe.utils.getdate(doc.admission_end_date) == today %}
red"> Application will be closed soon
{% elif frappe.utils.getdate(doc.admission_end_date) > today >= frappe.utils.getdate(doc.admission_start_date)%}
green"> Application open
{% elif frappe.utils.getdate(doc.admission_start_date) > today %}
blue"> Application will open
{% else %}
darkgrey
{% endif %}
</span>
</div>
<div class="col-xs-6 text-muted text-right small">
{{ _("Start on") }}: {{ frappe.format_date(admission_start_date) }}<br>
{{ _("End on") }}: {{ frappe.format_date(admission_end_date) }}
</div>
</div><br>
{%- if introduction -%}
<div>{{ introduction }}</div>
{% endif %}
{% if program_details %}
<br>
<div class="table-responsive">
<h3 class="bold">Eligibility and Other Details:</h3>
<table class="table table-bordered table-hover">
<thead>
<tr class="active">
<th style="width: 90px">Program/Std.</th>
<th style="width: 170px">Minumum Age(DOB)</th>
<th style="width: 170px">Maximum Age(DOB)</th>
<th style="width: 100px">Application Fee</th>
</tr>
</thead>
<tbody>
{% for row in program_details %}
<tr>
<td>{{ row.program }}</td>
<td>{{ row.minimum_age }}</td>
<td>{{ row.maximum_age }}</td>
<td>{{ row.application_fee }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
{%- if application_form_route -%}
<br>
<p>
<a class='btn btn-primary'
href='/{{ doc.application_form_route }}?new=1'>
{{ _("Apply Now") }}</a>
</p>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,36 @@
<div class="web-list-item">
{% set today = frappe.utils.getdate(frappe.utils.nowdate()) %}
<a href = "{{ doc.route }}/">
<div class="row">
<div class="col-sm-6 text-left small bold" style="margin-top: -3px;"">
<span class="indicator
{% if frappe.utils.getdate(doc.admission_end_date) == today %}
red
{% elif frappe.utils.getdate(doc.admission_end_date) > today >= frappe.utils.getdate(doc.admission_start_date)%}
green
{% elif frappe.utils.getdate(doc.admission_start_date) > today %}
blue
{% else %}
darkgrey
{% endif %}
">{{ doc.title }}</span>
</div>
<div class="col-sm-3 small">
<span class="text-muted">
Starts on
</span>
<div class="text-muted bold">
{{ frappe.format_date(doc.admission_start_date) }}
</div>
</div>
<div class="col-sm-3 small">
<span class="text-muted">
Ends on
</span>
<div class="bold">
{{ frappe.format_date(doc.admission_end_date) }}
</div>
</div>
</div>
</a>
</div>

View File

@ -0,0 +1,222 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "",
"beta": 0,
"creation": "2017-09-15 12:59:43.207923",
"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": "program",
"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": "Program",
"length": 0,
"no_copy": 0,
"options": "Program",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "minimum_age",
"fieldtype": "Date",
"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": "Minimum Age",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "maximum_age",
"fieldtype": "Date",
"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": "Maximum Age",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "application_fee",
"fieldtype": "Currency",
"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": "Application Fee",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "applicant_naming_series",
"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": "Naming Series (for Student Applicant)",
"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,
"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": "2017-10-02 17:13:52.586218",
"modified_by": "Administrator",
"module": "Schools",
"name": "Student Admission Program",
"name_case": "",
"owner": "Administrator",
"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,9 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
from frappe.model.document import Document
class StudentAdmissionProgram(Document):
pass

View File

@ -6,13 +6,18 @@ from __future__ import print_function, unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import getdate
class StudentApplicant(Document):
def autoname(self):
from frappe.model.naming import set_name_by_naming_series
if self.student_admission:
naming_series = frappe.db.get_value('Student Admission', self.student_admission,
'naming_series_for_student_applicant')
if self.program:
student_admission = get_student_admission_data(self.student_admission, self.program)
if student_admission:
naming_series = student_admission.get("applicant_naming_series")
else:
frappe.throw(_("Select the program first"))
if naming_series:
self.naming_series = naming_series
@ -21,11 +26,35 @@ class StudentApplicant(Document):
def validate(self):
self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name]))
if self.student_admission and self.program and self.date_of_birth:
self.validation_from_student_admission()
def on_update_after_submit(self):
student = frappe.get_list("Student", filters= {"student_applicant": self.name})
if student:
frappe.throw(_("Cannot change status as student {0} is linked with student application {1}").format(student[0].name, self.name))
def on_submit(self):
if self.paid and not self.student_admission:
frappe.throw(_("Please select Student Admission which is mandatory for the paid student applicant"))
def validation_from_student_admission(self):
student_admission = get_student_admission_data(self.student_admission, self.program)
if student_admission:
if not (getdate(student_admission.minimum_age) >= getdate(self.date_of_birth) >=
getdate(student_admission.maximum_age)):
frappe.throw(_("Not eligible for the admission in this program as per DOB"))
def on_payment_authorized(self, *args, **kwargs):
self.db_set('paid', 1)
def get_student_admission_data(student_admission, program):
student_admission = frappe.db.sql("""select sa.admission_start_date, sa.admission_end_date,
sap.program, sap.minimum_age, sap.maximum_age, sap.applicant_naming_series
from `tabStudent Admission` sa, `tabStudent Admission Program` sap
where sa.name = sap.parent and sa.name = %s and sap.program = %s""", (student_admission, program), as_dict=1)
if student_admission:
return student_admission[0]
else:
return None

View File

@ -92,7 +92,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
// delivery note
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && allow_delivery) {
this.frm.add_custom_button(__('Delivery'),
function() { me.make_delivery_note_based_on_delivery_note(); }, __("Make"));
function() { me.make_delivery_note_based_on_delivery_date(); }, __("Make"));
this.frm.add_custom_button(__('Production Order'),
function() { me.make_production_order() }, __("Make"));
@ -270,7 +270,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
})
},
make_delivery_note_based_on_delivery_note: function() {
make_delivery_note_based_on_delivery_date: function() {
var me = this;
var delivery_dates = [];

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
import json
import frappe.utils
from frappe.utils import cstr, flt, getdate, comma_and, cint
from frappe.utils import cstr, flt, getdate, comma_and, cint, nowdate, add_days
from frappe import _
from frappe.model.utils import get_fetch_values
from frappe.model.mapper import get_mapped_doc

View File

@ -49,11 +49,12 @@ erpnext.pos.PointOfSale = class PointOfSale {
this.set_online_status();
},
() => this.setup_pos_profile(),
() => this.make_new_invoice(),
() => {
frappe.timeout(1);
this.make_items();
this.bind_events();
},
() => this.make_new_invoice(),
() => this.page.set_title(__('Point of Sale'))
]);
}
@ -99,6 +100,17 @@ erpnext.pos.PointOfSale = class PointOfSale {
if (value == 'Pay') {
if (!this.payment) {
this.make_payment_modal();
} else {
const mop_field = this.payment.default_mop;
let amount = 0.0;
this.frm.doc.payments.map(p => {
if (p.mode_of_payment == mop_field) {
amount = p.amount;
return;
}
});
this.payment.dialog.set_value(mop_field, flt(amount));
}
this.payment.open_modal();
}
@ -437,6 +449,12 @@ class POSCart {
this.$taxes_and_totals.html(this.get_taxes_and_totals());
this.numpad && this.numpad.reset_value();
this.customer_field.set_value("");
this.wrapper.find('.grand-total-value').text(
format_currency(this.frm.doc.grand_total, this.frm.currency));
const customer = this.frm.doc.customer || this.pos_profile.customer;
this.customer_field.set_value(customer);
}
get_grand_total() {
@ -746,28 +764,41 @@ class POSCart {
// });
this.wrapper.find('.additional_discount_percentage').on('change', (e) => {
const discount_percentage = flt(e.target.value,
precision("additional_discount_percentage"));
frappe.model.set_value(this.frm.doctype, this.frm.docname,
'additional_discount_percentage', e.target.value)
'additional_discount_percentage', discount_percentage)
.then(() => {
let discount_wrapper = this.wrapper.find('.discount_amount');
discount_wrapper.val(this.frm.doc.discount_amount);
discount_wrapper.val(flt(this.frm.doc.discount_amount,
precision('discount_amount')));
discount_wrapper.trigger('change');
});
});
this.wrapper.find('.discount_amount').on('change', (e) => {
const discount_amount = flt(e.target.value, precision('discount_amount'));
frappe.model.set_value(this.frm.doctype, this.frm.docname,
'discount_amount', flt(e.target.value));
'discount_amount', discount_amount);
this.frm.trigger('discount_amount')
.then(() => {
let discount_wrapper = this.wrapper.find('.additional_discount_percentage');
discount_wrapper.val(this.frm.doc.additional_discount_percentage);
this.update_discount_fields();
this.update_taxes_and_totals();
this.update_grand_total();
});
});
}
update_discount_fields() {
let discount_wrapper = this.wrapper.find('.additional_discount_percentage');
let discount_amt_wrapper = this.wrapper.find('.discount_amount');
discount_wrapper.val(flt(this.frm.doc.additional_discount_percentage,
precision('additional_discount_percentage')));
discount_amt_wrapper.val(flt(this.frm.doc.discount_amount,
precision('discount_amount')));
}
set_selected_item($item) {
this.selected_item = $item;
this.$cart_items.find('.list-item').removeClass('current-item qty disc rate');
@ -831,7 +862,7 @@ class POSItems {
this.search_field = frappe.ui.form.make_control({
df: {
fieldtype: 'Data',
label: 'Search Item (Ctrl + I)',
label: 'Search Item ( Ctrl + i )',
placeholder: 'Search by item code, serial number, batch no or barcode'
},
parent: this.wrapper.find('.search-field'),
@ -928,16 +959,21 @@ class POSItems {
if(serial_no) {
this.events.update_cart(items[0].item_code,
'serial_no', serial_no);
this.search_field.set_value('');
this.reset_search_field();
}
if(batch_no) {
this.events.update_cart(items[0].item_code,
'batch_no', batch_no);
this.search_field.set_value('');
this.reset_search_field();
}
});
}
reset_search_field() {
this.search_field.set_value('');
this.search_field.$input.trigger("input");
}
bind_events() {
var me = this;
this.wrapper.on('click', '.pos-item-wrapper', function() {
@ -1179,6 +1215,10 @@ class Payment {
const me = this;
let fields = this.frm.doc.payments.map(p => {
if (p.default) {
this.default_mop = p.mode_of_payment;
}
return {
fieldtype: 'Currency',
label: __(p.mode_of_payment),

View File

@ -24,6 +24,8 @@ def get_items(start, page_length, price_list, item_group, search_value=""):
if batch_no_data:
batch_no, item_code = batch_no_data
item_code, condition = get_conditions(item_code, serial_no, batch_no)
lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt'])
# locate function is used to sort by closest match from the beginning of the value
res = frappe.db.sql("""select i.name as item_code, i.item_name, i.image as item_image,
@ -36,11 +38,11 @@ def get_items(start, page_length, price_list, item_group, search_value=""):
where
i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1
and i.item_group in (select name from `tabItem Group` where lft >= {lft} and rgt <= {rgt})
and (i.item_code like %(item_code)s
or i.item_name like %(item_code)s or i.barcode like %(item_code)s)
limit {start}, {page_length}""".format(start=start, page_length=page_length, lft=lft, rgt=rgt),
and {condition}
limit {start}, {page_length}""".format(start=start,
page_length=page_length, lft=lft, rgt=rgt, condition=condition),
{
'item_code': '%%%s%%'%(frappe.db.escape(item_code)),
'item_code': item_code,
'price_list': price_list
} , as_dict=1)
@ -60,6 +62,15 @@ def get_items(start, page_length, price_list, item_group, search_value=""):
return res
def get_conditions(item_code, serial_no, batch_no):
if serial_no or batch_no:
return frappe.db.escape(item_code), "i.item_code = %(item_code)s"
condition = """(i.item_code like %(item_code)s
or i.item_name like %(item_code)s or i.barcode like %(item_code)s)"""
return '%%%s%%'%(frappe.db.escape(item_code)), condition
@frappe.whitelist()
def submit_invoice(doc):
if isinstance(doc, basestring):

View File

@ -194,461 +194,461 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sales_settings",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sales",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sales_settings",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sales",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sales_monthly_history",
"fieldtype": "Small Text",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sales Monthly History",
"length": 0,
"no_copy": 1,
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sales_monthly_history",
"fieldtype": "Small Text",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sales Monthly History",
"length": 0,
"no_copy": 1,
"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,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "monthly_sales_target",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Monthly Sales Target",
"length": 0,
"no_copy": 0,
"options": "default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "monthly_sales_target",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Monthly Sales Target",
"length": 0,
"no_copy": 0,
"options": "default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_goals",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_goals",
"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,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "total_monthly_sales",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Monthly Sales",
"length": 0,
"no_copy": 1,
"options": "default_currency",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "total_monthly_sales",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Monthly Sales",
"length": 0,
"no_copy": 1,
"options": "default_currency",
"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,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "charts_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Values",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "charts_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Values",
"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,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_letter_head",
"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": "Default Letter Head",
"length": 0,
"no_copy": 0,
"options": "Letter Head",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_letter_head",
"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": "Default Letter Head",
"length": 0,
"no_copy": 0,
"options": "Letter Head",
"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,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_holiday_list",
"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": "Default Holiday List",
"length": 0,
"no_copy": 0,
"options": "Holiday List",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_holiday_list",
"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": "Default Holiday List",
"length": 0,
"no_copy": 0,
"options": "Holiday List",
"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,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_terms",
"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": "Default Terms",
"length": 0,
"no_copy": 0,
"options": "Terms and Conditions",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_terms",
"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": "Default Terms",
"length": 0,
"no_copy": 0,
"options": "Terms and Conditions",
"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,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_currency",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Currency",
"length": 0,
"no_copy": 0,
"options": "Currency",
"permlevel": 0,
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_currency",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Currency",
"length": 0,
"no_copy": 0,
"options": "Currency",
"permlevel": 0,
"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
},
},
{
"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,
"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,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "country",
"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": "Country",
"length": 0,
"no_copy": 0,
"options": "Country",
"permlevel": 0,
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "country",
"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": "Country",
"length": 0,
"no_copy": 0,
"options": "Country",
"permlevel": 0,
"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
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "create_chart_of_accounts_based_on",
"fieldtype": "Select",
"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 Chart Of Accounts Based On",
"length": 0,
"no_copy": 0,
"options": "\nStandard Template\nExisting Company",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "create_chart_of_accounts_based_on",
"fieldtype": "Select",
"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 Chart Of Accounts Based On",
"length": 0,
"no_copy": 0,
"options": "\nStandard Template\nExisting Company",
"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,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"",
"fieldname": "chart_of_accounts",
"fieldtype": "Select",
"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": "Chart Of Accounts Template",
"length": 0,
"no_copy": 1,
"options": "",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"",
"fieldname": "chart_of_accounts",
"fieldtype": "Select",
"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": "Chart Of Accounts Template",
"length": 0,
"no_copy": 1,
"options": "",
"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,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"",
"fieldname": "existing_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": 0,
"label": "Existing Company ",
"length": 0,
"no_copy": 1,
"options": "Company",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"",
"fieldname": "existing_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": 0,
"label": "Existing Company ",
"length": 0,
"no_copy": 1,
"options": "Company",
"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,
"unique": 0
},
{
@ -1978,24 +1978,24 @@
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-building",
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-08-31 11:48:56.278568",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
"owner": "Administrator",
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-building",
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-09-06 15:08:44.360880",
"modified_by": "mohan@annapurna.com",
"module": "Setup",
"name": "Company",
"owner": "Administrator",
"permissions": [
{
"amend": 0,

View File

@ -73,6 +73,7 @@ def make_material_request(items):
mr = frappe.get_doc({
"doctype": "Material Request",
"material_request_type": "Purchase",
"schedule_date": frappe.utils.add_days(frappe.utils.nowdate(), 7),
"items": [{
"schedule_date": frappe.utils.add_days(frappe.utils.nowdate(), 7),
"item_code": i.name,

View File

@ -0,0 +1,45 @@
# 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
from erpnext.shopping_cart.cart import _get_cart_quotation
from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings \
import is_cart_enabled, get_shopping_cart_settings, show_quantity_in_website
from erpnext.utilities.product import get_price, get_qty_in_stock
@frappe.whitelist(allow_guest=True)
def get_product_info_for_website(item_code):
"""get product price / stock info for website"""
if not is_cart_enabled():
return {}
cart_quotation = _get_cart_quotation()
cart_settings = get_shopping_cart_settings()
price = get_price(
item_code,
cart_quotation.selling_price_list,
cart_settings.default_customer_group,
cart_settings.company
)
stock_status = get_qty_in_stock(item_code, "website_warehouse")
product_info = {
"price": price,
"stock_qty": stock_status.stock_qty,
"in_stock": stock_status.in_stock,
"qty": 0,
"uom": frappe.db.get_value("Item", item_code, "stock_uom"),
"show_stock_qty": show_quantity_in_website()
}
if product_info["price"]:
if frappe.session.user != "Guest":
item = cart_quotation.get({"item_code": item_code})
if item:
product_info["qty"] = item[0].qty
return product_info

View File

@ -75,8 +75,13 @@ erpnext.stock.ItemDashboard = Class.extend({
this.content.find('.more').addClass('hidden');
}
$(frappe.render_template('item_dashboard_list', context)).appendTo(this.result);
// If not any stock in any warehouses provide a message to end user
if (context.data.length > 0) {
$(frappe.render_template('item_dashboard_list', context)).appendTo(this.result);
} else {
var message = __(" Currently no stock available in any warehouse")
$("<span class='small'> <i class='fa fa-exclamation-triangle' aria-hidden='true'></i>"+message+"</span>").appendTo(this.result);
}
},
get_item_dashboard_data: function(data, max_count, show_item) {
if(!max_count) max_count = 0;
@ -182,4 +187,4 @@ erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callb
frappe.set_route('Form', doc.doctype, doc.name);
})
});
}
}

View File

@ -103,7 +103,7 @@ def split_batch(batch_no, item_code, warehouse, qty, new_batch_id = None):
def set_batch_nos(doc, warehouse_field, throw = False):
'''Automatically select `batch_no` for outgoing items in item table'''
for d in doc.items:
qty = d.get('stock_qty') or d.get('qty') or 0
qty = d.get('stock_qty') or d.get('transfer_qty') or d.get('qty') or 0
has_batch_no = frappe.db.get_value('Item', d.item_code, 'has_batch_no')
warehouse = d.get(warehouse_field, None)
if has_batch_no and warehouse and qty > 0:

View File

@ -63,7 +63,7 @@ frappe.ui.form.on("Item", {
frm.page.set_inner_btn_group_as_primary(__("Make"));
}
if (frm.doc.variant_of) {
frm.set_intro(__('This Item is a Variant of {0} (Template).',
frm.set_intro(__('This Item is a Variant of {0} (Template).',
[`<a href="#Form/Item/${frm.doc.variant_of}">${frm.doc.variant_of}</a>`]), true);
}
@ -127,7 +127,7 @@ frappe.ui.form.on("Item", {
if(!frm.doc.description)
frm.set_value("description", frm.doc.item_code);
},
is_stock_item: function(frm) {
if(!frm.doc.is_stock_item) {
frm.set_value("has_batch_no", 0);
@ -135,7 +135,7 @@ frappe.ui.form.on("Item", {
frm.set_value("has_serial_no", 0);
}
},
copy_from_item_group: function(frm) {
return frm.call({
doc: frm.doc,
@ -249,15 +249,18 @@ $.extend(erpnext.item, {
if(frm.doc.__islocal)
return;
frappe.require('assets/js/item-dashboard.min.js', function() {
var section = frm.dashboard.add_section('<h5 style="margin-top: 0px;">\
<a href="#stock-balance">' + __("Stock Levels") + '</a></h5>');
erpnext.item.item_dashboard = new erpnext.stock.ItemDashboard({
parent: section,
item_code: frm.doc.name
// Show Stock Levels only if is_stock_item
if (frm.doc.is_stock_item) {
frappe.require('assets/js/item-dashboard.min.js', function() {
var section = frm.dashboard.add_section('<h5 style="margin-top: 0px;">\
<a href="#stock-balance">' + __("Stock Levels") + '</a></h5>');
erpnext.item.item_dashboard = new erpnext.stock.ItemDashboard({
parent: section,
item_code: frm.doc.name
});
erpnext.item.item_dashboard.refresh();
});
erpnext.item.item_dashboard.refresh();
});
}
},
edit_prices_button: function(frm) {

File diff suppressed because it is too large Load Diff

View File

@ -52,7 +52,8 @@ class Item(WebsiteGenerator):
if not self.description:
self.description = self.item_name
self.publish_in_hub = 1
if self.is_sales_item and not self.is_item_from_hub:
self.publish_in_hub = 1
def after_insert(self):
'''set opening stock and item price'''
@ -63,6 +64,10 @@ class Item(WebsiteGenerator):
self.set_opening_stock()
def validate(self):
self.before_update = None
if frappe.db.exists('Item', self.name):
self.before_update = frappe.get_doc('Item', self.name)
super(Item, self).validate()
if not self.item_name:
@ -815,4 +820,3 @@ def check_stock_uom_with_bin(item, stock_uom):
if not matched:
frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.").format(item))

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: Item", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Item
() => frappe.tests.make('Item', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@ -17,6 +17,9 @@ frappe.ui.form.on('Material Request', {
// add item, if previous view was item
erpnext.utils.add_item(frm);
//set schedule_date
set_schedule_date(frm);
// formatter for material request item
frm.set_indicator_formatter('item_code',
function(doc) { return (doc.qty<=doc.ordered_qty) ? "green" : "orange" }),
@ -38,12 +41,18 @@ frappe.ui.form.on("Material Request Item", {
},
item_code: function(frm, doctype, name) {
frm.script_manager.copy_from_first_row('items', frm.selected_doc,
'schedule_date');
set_schedule_date(frm);
},
schedule_date: function(frm, cdt, cdn) {
erpnext.utils.copy_value_in_all_row(frm.doc, cdt, cdn, "items", "schedule_date");
var row = locals[cdt][cdn];
if (row.schedule_date) {
if(!frm.doc.schedule_date) {
erpnext.utils.copy_value_in_all_row(frm.doc, cdt, cdn, "items", "schedule_date");
} else {
set_schedule_date(frm);
}
}
}
});
@ -227,6 +236,28 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten
}
}
});
},
validate: function() {
set_schedule_date(this.frm);
},
items_add: function(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
if(doc.schedule_date) {
row.schedule_date = doc.schedule_date;
refresh_field("schedule_date", cdn, "items");
} else {
this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
}
},
items_on_form_rendered: function() {
set_schedule_date(this.frm);
},
schedule_date: function() {
set_schedule_date(this.frm);
}
});
@ -246,3 +277,9 @@ cur_frm.cscript['Unstop Material Request'] = function(){
cur_frm.refresh();
});
};
function set_schedule_date(frm) {
if(frm.doc.schedule_date){
erpnext.utils.copy_value_in_all_row(frm.doc, frm.doc.doctype, frm.doc.name, "items", "schedule_date");
}
}

View File

@ -42,6 +42,38 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "naming_series",
"fieldtype": "Select",
"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": "Series",
"length": 0,
"no_copy": 1,
"oldfieldname": "naming_series",
"oldfieldtype": "Select",
"options": "MREQ-",
"permlevel": 0,
"print_hide": 1,
"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": 1,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
@ -133,69 +165,33 @@
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "naming_series",
"fieldtype": "Select",
"fieldname": "schedule_date",
"fieldtype": "Date",
"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": "Series",
"length": 0,
"no_copy": 1,
"oldfieldname": "naming_series",
"oldfieldtype": "Select",
"options": "MREQ-",
"permlevel": 0,
"print_hide": 1,
"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": 1,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"label": "Required Date",
"length": 0,
"no_copy": 1,
"oldfieldname": "amended_from",
"oldfieldtype": "Data",
"options": "Material Request",
"no_copy": 0,
"permlevel": 0,
"print_hide": 1,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "150px",
"read_only": 1,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "150px"
"unique": 0
},
{
"allow_bulk_edit": 0,
@ -232,6 +228,40 @@
"unique": 0,
"width": "150px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"oldfieldname": "amended_from",
"oldfieldtype": "Data",
"options": "Material Request",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"print_width": "150px",
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "150px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -686,7 +716,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-07-26 19:43:31.823549",
"modified": "2017-10-05 18:24:17.148782",
"modified_by": "Administrator",
"module": "Stock",
"name": "Material Request",

View File

@ -7,7 +7,7 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import cstr, flt, getdate, new_line_sep
from frappe.utils import cstr, flt, getdate, new_line_sep, nowdate, add_days
from frappe import msgprint, _
from frappe.model.mapper import get_mapped_doc
from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty
@ -53,11 +53,6 @@ class MaterialRequest(BuyingController):
if actual_so_qty and (flt(so_items[so_no][item]) + already_indented > actual_so_qty):
frappe.throw(_("Material Request of maximum {0} can be made for Item {1} against Sales Order {2}").format(actual_so_qty - already_indented, item, so_no))
def validate_schedule_date(self):
for d in self.get('items'):
if d.schedule_date and getdate(d.schedule_date) < getdate(self.transaction_date):
frappe.throw(_("Expected Date cannot be before Material Request Date"))
# Validate
# ---------------------
def validate(self):
@ -70,9 +65,9 @@ class MaterialRequest(BuyingController):
self.status = "Draft"
from erpnext.controllers.status_updater import validate_status
validate_status(self.status, ["Draft", "Submitted", "Stopped", "Cancelled", "Pending",
"Partially Ordered", "Ordered", "Issued", "Transferred"]
)
validate_status(self.status,
["Draft", "Submitted", "Stopped", "Cancelled", "Pending",
"Partially Ordered", "Ordered", "Issued", "Transferred"])
validate_for_items(self)
@ -287,7 +282,7 @@ def make_purchase_order_based_on_supplier(source_name, target_doc=None):
def postprocess(source, target_doc):
target_doc.supplier = source_name
target_doc.schedule_date = add_days(nowdate(), 1)
target_doc.set("items", [d for d in target_doc.get("items")
if d.get("item_code") in supplier_items and d.get("qty") > 0])
@ -320,12 +315,12 @@ def get_material_requests_based_on_supplier(supplier):
material_requests = frappe.db.sql_list("""select distinct mr.name
from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
where mr.name = mr_item.parent
and mr_item.item_code in (%s)
and mr.material_request_type = 'Purchase'
and mr.per_ordered < 99.99
and mr.docstatus = 1
and mr.status != 'Stopped'
order by mr_item.item_code ASC""" % ', '.join(['%s']*len(supplier_items)),
and mr_item.item_code in (%s)
and mr.material_request_type = 'Purchase'
and mr.per_ordered < 99.99
and mr.docstatus = 1
and mr.status != 'Stopped'
order by mr_item.item_code ASC""" % ', '.join(['%s']*len(supplier_items)),
tuple(supplier_items))
else:
material_requests = []

View File

@ -206,6 +206,7 @@ class TestMaterialRequest(unittest.TestCase):
po_doc = make_purchase_order(mr.name)
po_doc.supplier = "_Test Supplier"
po_doc.transaction_date = "2013-07-07"
po_doc.schedule_date = "2013-07-09"
po_doc.get("items")[0].qty = 27.0
po_doc.get("items")[1].qty = 1.5
po_doc.get("items")[0].schedule_date = "2013-07-09"

View File

@ -29,7 +29,8 @@
],
"material_request_type": "Purchase",
"naming_series": "_T-Material Request-",
"transaction_date": "2013-02-18"
"transaction_date": "2013-02-18",
"schedule_date": "2013-02-19"
},
{
"company": "_Test Company",
@ -43,13 +44,14 @@
"item_name": "_Test FG Item",
"parentfield": "items",
"qty": 5,
"schedule_date": "2013-02-18",
"schedule_date": "2013-02-19",
"uom": "_Test UOM 1",
"warehouse": "_Test Warehouse - _TC"
}
],
"material_request_type": "Manufacture",
"naming_series": "_T-Material Request-",
"transaction_date": "2013-02-18"
"transaction_date": "2013-02-18",
"schedule_date": "2013-02-19"
}
]

View File

@ -11,14 +11,25 @@ QUnit.test("test material request", function(assert) {
{'schedule_date': frappe.datetime.add_days(frappe.datetime.nowdate(), 5)},
{'qty': 5},
{'item_code': 'Test Product 1'},
],
[
{'schedule_date': frappe.datetime.add_days(frappe.datetime.nowdate(), 6)},
{'qty': 2},
{'item_code': 'Test Product 2'},
]
]},
]);
},
() => cur_frm.save(),
() => {
assert.ok(cur_frm.doc.schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 5), "Schedule Date correct");
// get_item_details
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
assert.ok(cur_frm.doc.items[0].schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 5), "Schedule Date correct");
assert.ok(cur_frm.doc.items[1].item_name=='Test Product 2', "Item name correct");
assert.ok(cur_frm.doc.items[1].schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 6), "Schedule Date correct");
},
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),

View File

@ -121,7 +121,8 @@ def create_material_request(material_requests):
mr.update({
"company": company,
"transaction_date": nowdate(),
"material_request_type": "Material Transfer" if request_type=="Transfer" else request_type
"material_request_type": "Material Transfer" if request_type=="Transfer" else request_type,
"schedule_date": add_days(nowdate(), cint(items[0].lead_time_days))
})
for d in items:

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