Merge branch 'develop' into develop

This commit is contained in:
Nabin Hait 2017-10-18 11:16:17 +05:30 committed by GitHub
commit cfb68dee34
234 changed files with 11336 additions and 5435 deletions

View File

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

View File

@ -417,6 +417,46 @@
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "Sales User",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "Purchase User",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
}
],
"quick_entry": 1,
@ -426,4 +466,4 @@
"sort_order": "ASC",
"track_changes": 1,
"track_seen": 0
}
}

View File

@ -55,13 +55,13 @@ frappe.ui.form.on('Asset', {
});
}
frm.trigger("show_graph");
frm.trigger("setup_chart");
}
},
show_graph: function(frm) {
var x_intervals = ["x", frm.doc.purchase_date];
var asset_values = ["Asset Value", frm.doc.gross_purchase_amount];
setup_chart: function(frm) {
var x_intervals = [frm.doc.purchase_date];
var asset_values = [frm.doc.gross_purchase_amount];
var last_depreciation_date = frm.doc.purchase_date;
if(frm.doc.opening_accumulated_depreciation) {
@ -94,32 +94,21 @@ frappe.ui.form.on('Asset', {
last_depreciation_date = frm.doc.disposal_date;
}
frm.dashboard.setup_chart({
frm.dashboard.render_graph({
title: "Asset Value",
data: {
x: 'x',
columns: [x_intervals, asset_values],
regions: {
'Asset Value': [{'start': last_depreciation_date, 'style':'dashed'}]
}
labels: x_intervals,
datasets: [{
color: 'green',
values: asset_values,
formatted: asset_values.map(d => d.toFixed(2))
}]
},
legend: {
show: false
},
axis: {
x: {
type: 'timeseries',
tick: {
format: "%d-%m-%Y"
}
},
y: {
min: 0,
padding: {bottom: 10}
}
}
type: 'line'
});
},
item_code: function(frm) {
if(frm.doc.item_code) {
frappe.call({

View File

@ -12,8 +12,8 @@ from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amo
from erpnext.hr.doctype.employee_loan.employee_loan import update_disbursement_status
class JournalEntry(AccountsController):
def __init__(self, arg1, arg2=None):
super(JournalEntry, self).__init__(arg1, arg2)
def __init__(self, *args, **kwargs):
super(JournalEntry, self).__init__(*args, **kwargs)
def get_feed(self):
return self.voucher_type

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

@ -296,7 +296,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-09-04 17:37:01.192312",
"modified": "2017-10-16 17:37:01.192312",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry Reference",
@ -311,4 +311,4 @@
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}
}

View File

@ -37,10 +37,10 @@ frappe.ui.form.on('POS Profile', {
return { filters: { doc_type: "Sales Invoice", print_format_type: "Js"} };
});
frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'is_online', (r) => {
is_online = r && cint(r.is_online)
frm.toggle_display('offline_pos_section', !is_online);
frm.toggle_display('print_format_for_online', is_online);
frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'use_pos_in_offline_mode', (r) => {
is_offline = r && cint(r.use_pos_in_offline_mode)
frm.toggle_display('offline_pos_section', is_offline);
frm.toggle_display('print_format_for_online', !is_offline);
});
},

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

@ -348,6 +348,8 @@ def apply_internal_priority(pricing_rules, field_set, args):
return filtered_rules or pricing_rules
def set_transaction_type(args):
if args.transaction_type:
return
if args.doctype in ("Opportunity", "Quotation", "Sales Order", "Delivery Note", "Sales Invoice"):
args.transaction_type = "selling"
elif args.doctype in ("Material Request", "Supplier Quotation", "Purchase Order",
@ -356,4 +358,4 @@ def set_transaction_type(args):
elif args.customer:
args.transaction_type = "selling"
else:
args.transaction_type = "buying"
args.transaction_type = "buying"

View File

@ -22,8 +22,8 @@ form_grid_templates = {
}
class PurchaseInvoice(BuyingController):
def __init__(self, arg1, arg2=None):
super(PurchaseInvoice, self).__init__(arg1, arg2)
def __init__(self, *args, **kwargs):
super(PurchaseInvoice, self).__init__(*args, **kwargs)
self.status_updater = [{
'source_dt': 'Purchase Invoice Item',
'target_dt': 'Purchase Order Item',

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},
@ -36,7 +37,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

@ -88,7 +88,7 @@ def update_pos_profile_data(doc, pos_profile, company_data):
doc.naming_series = pos_profile.get('naming_series') or 'SINV-'
doc.letter_head = pos_profile.get('letter_head') or company_data.default_letter_head
doc.ignore_pricing_rule = pos_profile.get('ignore_pricing_rule') or 0
doc.apply_discount_on = pos_profile.get('apply_discount_on') if pos_profile.get('apply_discount') else ''
doc.apply_discount_on = pos_profile.get('apply_discount_on') or ''
doc.customer_group = pos_profile.get('customer_group') or get_root('Customer Group')
doc.territory = pos_profile.get('territory') or get_root('Territory')
doc.terms = frappe.db.get_value('Terms and Conditions', pos_profile.get('tc_name'), 'terms') or doc.terms or ''
@ -417,6 +417,7 @@ def make_contact(args,customer):
'link_doctype': 'Customer',
'link_name': customer
})
doc.flags.ignore_mandatory = True
doc.save(ignore_permissions=True)
def make_address(args, customer):
@ -441,6 +442,7 @@ def make_address(args, customer):
address.is_primary_address = 1
address.is_shipping_address = 1
address.update(args)
address.flags.ignore_mandatory = True
address.save(ignore_permissions = True)
def make_email_queue(email_queue):

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

@ -27,8 +27,8 @@ form_grid_templates = {
}
class SalesInvoice(SellingController):
def __init__(self, arg1, arg2=None):
super(SalesInvoice, self).__init__(arg1, arg2)
def __init__(self, *args, **kwargs):
super(SalesInvoice, self).__init__(*args, **kwargs)
self.status_updater = [{
'source_dt': 'Sales Invoice Item',
'target_field': 'billed_amt',

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-10 17:28:10.105561",
"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,35 @@ 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)
for fieldname in ['naming_series', 'ignore_pricing_rule', 'posting_time'
'select_print_heading', 'remarks', 'owner']:
if new_document.meta.get_field(fieldname):
new_document.set(fieldname, reference_doc.get(fieldname))
# copy item fields
if new_document.meta.get_field('items'):
for i, item in enumerate(new_document.items):
for fieldname in ("page_break",):
item.set(fieldname, reference_doc.items[i].get(fieldname))
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 +302,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

@ -113,6 +113,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
});
this.page.add_menu_item(__("Sync Offline Invoices"), function () {
me.freeze_screen = true;
me.sync_sales_invoice()
});
@ -1684,6 +1685,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
set_interval_for_si_sync: function () {
var me = this;
setInterval(function () {
me.freeze_screen = false;
me.sync_sales_invoice()
}, 60000)
},
@ -1697,9 +1699,12 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.freeze = this.customer_doc.display
}
freeze_screen = this.freeze_screen || false;
if ((this.si_docs.length || this.email_queue_list || this.customers_list) && !this.freeze) {
frappe.call({
method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice",
freeze: freeze_screen,
args: {
doc_list: me.si_docs,
email_queue_list: me.email_queue_list,

View File

@ -68,7 +68,8 @@ def set_address_details(out, party, party_type, doctype=None, company=None):
billing_address_field = "customer_address" if party_type == "Lead" \
else party_type.lower() + "_address"
out[billing_address_field] = get_default_address(party_type, party.name)
out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field]))
if doctype:
out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field]))
# address display
out.address_display = get_address_display(out[billing_address_field])
@ -77,7 +78,8 @@ def set_address_details(out, party, party_type, doctype=None, company=None):
if party_type in ["Customer", "Lead"]:
out.shipping_address_name = get_default_address(party_type, party.name, 'is_shipping_address')
out.shipping_address = get_address_display(out["shipping_address_name"])
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
if doctype:
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
if doctype and doctype in ['Delivery Note', 'Sales Invoice']:
out.update(get_company_address(company))

View File

@ -44,7 +44,7 @@ class ReceivablePayableReport(object):
})
columns += [_("Age (Days)") + ":Int:80"]
self.ageing_col_idx_start = len(columns)
if not "range1" in self.filters:
@ -53,7 +53,7 @@ class ReceivablePayableReport(object):
self.filters["range2"] = "60"
if not "range3" in self.filters:
self.filters["range3"] = "90"
for label in ("0-{range1}".format(range1=self.filters["range1"]),
"{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]),
"{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]),
@ -74,14 +74,14 @@ class ReceivablePayableReport(object):
})
if args.get("party_type") == "Customer":
columns += [
_("Territory") + ":Link/Territory:80",
_("Territory") + ":Link/Territory:80",
_("Customer Group") + ":Link/Customer Group:120"
]
if args.get("party_type") == "Supplier":
columns += [_("Supplier Type") + ":Link/Supplier Type:80"]
columns.append(_("Remarks") + "::200")
return columns
def get_data(self, party_naming_by, args):
@ -97,13 +97,13 @@ class ReceivablePayableReport(object):
self.filters["company"] = frappe.db.get_single_value('Global Defaults', 'default_company')
company_currency = frappe.db.get_value("Company", self.filters.get("company"), "default_currency")
return_entries = self.get_return_entries(args.get("party_type"))
data = []
for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")):
if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers):
outstanding_amount, credit_note_amount = self.get_outstanding_amount(gle,
outstanding_amount, credit_note_amount = self.get_outstanding_amount(gle,
self.filters.report_date, dr_or_cr, return_entries, currency_precision)
if abs(outstanding_amount) > 0.1/10**currency_precision:
row = [gle.posting_date, gle.party]
@ -179,15 +179,15 @@ class ReceivablePayableReport(object):
# entries adjusted with future vouchers
((gle.against_voucher_type, gle.against_voucher) in future_vouchers)
)
def get_return_entries(self, party_type):
doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice"
return [d.name for d in frappe.get_all(doctype, filters={"is_return": 1, "docstatus": 1})]
return [d.name for d in frappe.get_all(doctype, filters={"is_return": 1, "docstatus": 1})]
def get_outstanding_amount(self, gle, report_date, dr_or_cr, return_entries, currency_precision):
payment_amount, credit_note_amount = 0.0, 0.0
reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit"
for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no):
if getdate(e.posting_date) <= report_date and e.name!=gle.name:
amount = flt(e.get(reverse_dr_or_cr)) - flt(e.get(dr_or_cr))
@ -195,11 +195,11 @@ class ReceivablePayableReport(object):
payment_amount += amount
else:
credit_note_amount += amount
outstanding_amount = flt((flt(gle.get(dr_or_cr)) - flt(gle.get(reverse_dr_or_cr)) \
- payment_amount - credit_note_amount), currency_precision)
credit_note_amount = flt(credit_note_amount, currency_precision)
return outstanding_amount, credit_note_amount
def get_party_name(self, party_type, party_name):
@ -207,7 +207,7 @@ class ReceivablePayableReport(object):
def get_territory(self, party_name):
return self.get_party_map("Customer").get(party_name, {}).get("territory") or ""
def get_customer_group(self, party_name):
return self.get_party_map("Customer").get(party_name, {}).get("customer_group") or ""
@ -220,7 +220,7 @@ class ReceivablePayableReport(object):
select_fields = "name, customer_name, territory, customer_group"
elif party_type == "Supplier":
select_fields = "name, supplier_name, supplier_type"
self.party_map = dict(((r.name, r) for r in frappe.db.sql("select {0} from `tab{1}`"
.format(select_fields, party_type), as_dict=True)))
@ -250,8 +250,8 @@ class ReceivablePayableReport(object):
else:
select_fields = "sum(debit) as debit, sum(credit) as credit"
self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party,
voucher_type, voucher_no, against_voucher_type, against_voucher,
self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party,
voucher_type, voucher_no, against_voucher_type, against_voucher,
account_currency, remarks, {0}
from `tabGL Entry`
where docstatus < 2 and party_type=%s and (party is not null and party != '') {1}
@ -277,13 +277,13 @@ class ReceivablePayableReport(object):
if party_type_field=="customer":
if self.filters.get("customer_group"):
lft, rgt = frappe.db.get_value("Customer Group",
lft, rgt = frappe.db.get_value("Customer Group",
self.filters.get("customer_group"), ["lft", "rgt"])
conditions.append("""party in (select name from tabCustomer
where exists(select name from `tabCustomer Group` where lft >= {0} and rgt <= {1}
conditions.append("""party in (select name from tabCustomer
where exists(select name from `tabCustomer Group` where lft >= {0} and rgt <= {1}
and name=tabCustomer.customer_group))""".format(lft, rgt))
if self.filters.get("credit_days_based_on"):
conditions.append("party in (select name from tabCustomer where credit_days_based_on=%s)")
values.append(self.filters.get("credit_days_based_on"))
@ -303,22 +303,22 @@ class ReceivablePayableReport(object):
return self.gl_entries_map.get(party, {})\
.get(against_voucher_type, {})\
.get(against_voucher, [])
def get_chart_data(self, columns, data):
ageing_columns = columns[self.ageing_col_idx_start : self.ageing_col_idx_start+4]
rows = []
for d in data:
rows.append(d[self.ageing_col_idx_start : self.ageing_col_idx_start+4])
if rows:
rows.insert(0, [[d.get("label")] for d in ageing_columns])
return {
"data": {
'rows': rows
'labels': rows
},
"chart_type": 'pie'
"type": 'percentage'
}
def execute(filters=None):

View File

@ -8,18 +8,18 @@ from frappe.utils import flt, cint
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
def execute(filters=None):
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
filters.periodicity, company=filters.company)
asset = get_data(filters.company, "Asset", "Debit", period_list,
asset = get_data(filters.company, "Asset", "Debit", period_list,
only_current_fiscal_year=False, filters=filters,
accumulated_values=filters.accumulated_values)
liability = get_data(filters.company, "Liability", "Credit", period_list,
liability = get_data(filters.company, "Liability", "Credit", period_list,
only_current_fiscal_year=False, filters=filters,
accumulated_values=filters.accumulated_values)
equity = get_data(filters.company, "Equity", "Credit", period_list,
equity = get_data(filters.company, "Equity", "Credit", period_list,
only_current_fiscal_year=False, filters=filters,
accumulated_values=filters.accumulated_values)
@ -43,17 +43,17 @@ def execute(filters=None):
unclosed[period.key] = opening_balance
if provisional_profit_loss:
provisional_profit_loss[period.key] = provisional_profit_loss[period.key] - opening_balance
unclosed["total"]=opening_balance
data.append(unclosed)
if provisional_profit_loss:
data.append(provisional_profit_loss)
if total_credit:
data.append(total_credit)
data.append(total_credit)
columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, company=filters.company)
chart = get_chart_data(filters, columns, asset, liability, equity)
return columns, data, message, chart
@ -87,7 +87,7 @@ def get_provisional_profit_loss(asset, liability, equity, period_list, company):
total += flt(provisional_profit_loss[period.key])
provisional_profit_loss["total"] = total
total_row_total += flt(total_row[period.key])
total_row["total"] = total_row_total
@ -98,7 +98,7 @@ def get_provisional_profit_loss(asset, liability, equity, period_list, company):
"warn_if_negative": True,
"currency": currency
})
return provisional_profit_loss, total_row
def check_opening_balance(asset, liability, equity):
@ -111,17 +111,17 @@ def check_opening_balance(asset, liability, equity):
opening_balance -= flt(liability[0].get("opening_balance", 0), float_precision)
if equity:
opening_balance -= flt(equity[0].get("opening_balance", 0), float_precision)
opening_balance = flt(opening_balance, float_precision)
if opening_balance:
return _("Previous Financial Year is not closed"),opening_balance
return None,None
def get_chart_data(filters, columns, asset, liability, equity):
x_intervals = ['x'] + [d.get("label") for d in columns[2:]]
labels = [d.get("label") for d in columns[2:]]
asset_data, liability_data, equity_data = [], [], []
for p in columns[2:]:
if asset:
asset_data.append(asset[-2].get(p.get("fieldname")))
@ -129,23 +129,25 @@ def get_chart_data(filters, columns, asset, liability, equity):
liability_data.append(liability[-2].get(p.get("fieldname")))
if equity:
equity_data.append(equity[-2].get(p.get("fieldname")))
columns = [x_intervals]
datasets = []
if asset_data:
columns.append(["Assets"] + asset_data)
datasets.append({'title':'Assets', 'values': asset_data})
if liability_data:
columns.append(["Liabilities"] + liability_data)
datasets.append({'title':'Liabilities', 'values': liability_data})
if equity_data:
columns.append(["Equity"] + equity_data)
datasets.append({'title':'Equity', 'values': equity_data})
chart = {
"data": {
'x': 'x',
'columns': columns
'labels': labels,
'datasets': datasets
}
}
if not filters.accumulated_values:
chart["chart_type"] = "bar"
chart["type"] = "bar"
else:
chart["type"] = "line"
return chart

View File

@ -8,15 +8,15 @@ from frappe.utils import flt
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
def execute(filters=None):
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
filters.periodicity, filters.accumulated_values, filters.company)
income = get_data(filters.company, "Income", "Credit", period_list, filters = filters,
accumulated_values=filters.accumulated_values,
accumulated_values=filters.accumulated_values,
ignore_closing_entries=True, ignore_accumulated_values_for_fy= True)
expense = get_data(filters.company, "Expense", "Debit", period_list, filters=filters,
accumulated_values=filters.accumulated_values,
accumulated_values=filters.accumulated_values,
ignore_closing_entries=True, ignore_accumulated_values_for_fy= True)
net_profit_loss = get_net_profit_loss(income, expense, period_list, filters.company)
@ -61,7 +61,7 @@ def get_net_profit_loss(income, expense, period_list, company):
def get_chart_data(filters, columns, income, expense, net_profit_loss):
x_intervals = ['x'] + [d.get("label") for d in columns[2:]]
labels = [d.get("label") for d in columns[2:]]
income_data, expense_data, net_profit = [], [], []
@ -73,27 +73,24 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss):
if net_profit_loss:
net_profit.append(net_profit_loss.get(p.get("fieldname")))
columns = [x_intervals]
datasets = []
if income_data:
columns.append(["Income"] + income_data)
datasets.append({'title': 'Income', 'values': income_data})
if expense_data:
columns.append(["Expense"] + expense_data)
datasets.append({'title': 'Expense', 'values': expense_data})
if net_profit:
columns.append(["Net Profit/Loss"] + net_profit)
datasets.append({'title': 'Net Profit/Loss', 'values': net_profit})
chart = {
"data": {
'x': 'x',
'columns': columns,
'colors': {
'Income': '#5E64FF',
'Expense': '#b8c2cc',
'Net Profit/Loss': '#ff5858'
}
'labels': labels,
'datasets': datasets
}
}
if not filters.accumulated_values:
chart["chart_type"] = "bar"
chart["type"] = "bar"
else:
chart["type"] = "line"
return chart

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,
@ -3427,7 +3458,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-09-22 16:11:49.856808",
"modified": "2017-10-05 14:19:04.102534",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",

View File

@ -20,8 +20,8 @@ form_grid_templates = {
}
class PurchaseOrder(BuyingController):
def __init__(self, arg1, arg2=None):
super(PurchaseOrder, self).__init__(arg1, arg2)
def __init__(self, *args, **kwargs):
super(PurchaseOrder, self).__init__(*args, **kwargs)
self.status_updater = [{
'source_dt': 'Purchase Order Item',
'target_dt': 'Material Request Item',
@ -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

@ -118,6 +118,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(),
@ -149,6 +150,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

@ -1,3 +1,5 @@
# coding=utf-8
from __future__ import unicode_literals
from frappe import _
@ -127,7 +129,6 @@ def get_data():
{
"module_name": "POS",
"color": "#589494",
"icon": "fa fa-th",
"icon": "octicon octicon-credit-card",
"type": "page",
"link": "pos",
@ -267,7 +268,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",
@ -277,4 +286,12 @@ def get_data():
"link": "data-import-tool",
"label": _("Data Import Tool")
},
{
"module_name": "Restaurant",
"color": "#EA81E8",
"icon": "🍔",
"_doctype": "Restaurant",
"link": "List/Restaurant",
"label": _("Restaurant")
}
]

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

@ -15,8 +15,8 @@ from erpnext.exceptions import InvalidCurrency
force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
class AccountsController(TransactionBase):
def __init__(self, arg1, arg2=None):
super(AccountsController, self).__init__(arg1, arg2)
def __init__(self, *args, **kwargs):
super(AccountsController, self).__init__(*args, **kwargs)
@property
def company_currency(self):
@ -83,6 +83,9 @@ class AccountsController(TransactionBase):
self.set(fieldname, today())
break
# set taxes table if missing from `taxes_and_charges`
self.set_taxes()
def calculate_taxes_and_totals(self):
from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
calculate_taxes_and_totals(self)
@ -259,9 +262,9 @@ class AccountsController(TransactionBase):
if not account_currency:
account_currency = get_account_currency(gl_dict.account)
if gl_dict.account and self.doctype not in ["Journal Entry",
if gl_dict.account and self.doctype not in ["Journal Entry",
"Period Closing Voucher", "Payment Entry"]:
self.validate_account_currency(gl_dict.account, account_currency)
set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"), self.company_currency)

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
@ -61,7 +61,7 @@ class BuyingController(StockController):
# set contact and address details for supplier, if they are not mentioned
if getattr(self, "supplier", None):
self.update_if_missing(get_party_details(self.supplier, party_type="Supplier", ignore_permissions=self.flags.ignore_permissions))
self.update_if_missing(get_party_details(self.supplier, party_type="Supplier", ignore_permissions=self.flags.ignore_permissions, doctype=self.doctype, company=self.company))
self.set_missing_item_details(for_validate)
@ -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

@ -165,6 +165,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
and (tabItem.`{key}` LIKE %(txt)s
or tabItem.item_group LIKE %(txt)s
or tabItem.item_name LIKE %(txt)s
or tabItem.barcode LIKE %(txt)s
or tabItem.description LIKE %(txt)s)
{fcond} {mcond}
order by
@ -172,7 +173,8 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
if(locate(%(_txt)s, item_name), locate(%(_txt)s, item_name), 99999),
idx desc,
name, item_name
limit %(start)s, %(page_len)s """.format(key=searchfield,
limit %(start)s, %(page_len)s """.format(
key=searchfield,
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
mcond=get_match_cond(doctype).replace('%', '%%')),
{

View File

@ -49,7 +49,8 @@ class SellingController(StockController):
if getattr(self, "customer", None):
from erpnext.accounts.party import _get_party_details
party_details = _get_party_details(self.customer,
ignore_permissions=self.flags.ignore_permissions)
ignore_permissions=self.flags.ignore_permissions,
doctype=self.doctype, company=self.company)
if not self.meta.get_field("sales_team"):
party_details.pop("sales_team")

View File

@ -42,10 +42,28 @@ class Opportunity(TransactionBase):
if not self.with_items:
self.items = []
def make_new_lead_if_required(self):
"""Set lead against new opportunity"""
if not (self.lead or self.customer) and self.contact_email:
# check if customer is already created agains the self.contact_email
customer = frappe.db.sql("""select
distinct `tabDynamic Link`.link_name as customer
from
`tabContact`,
`tabDynamic Link`
where `tabContact`.email_id='{0}'
and
`tabContact`.name=`tabDynamic Link`.parent
and
ifnull(`tabDynamic Link`.link_name, '')<>''
and
`tabDynamic Link`.link_doctype='Customer'
""".format(self.contact_email), as_dict=True)
if customer and customer[0].customer:
self.customer = customer[0].customer
self.enquiry_from = "Customer"
return
lead_name = frappe.db.get_value("Lead", {"email_id": self.contact_email})
if not lead_name:
sender_name = get_fullname(self.contact_email)

View File

@ -4,6 +4,7 @@ from __future__ import unicode_literals
import frappe
from frappe.utils import today
from erpnext.crm.doctype.lead.lead import make_customer
from erpnext.crm.doctype.opportunity.opportunity import make_quotation
import unittest
@ -25,12 +26,45 @@ class TestOpportunity(unittest.TestCase):
doc = frappe.get_doc('Opportunity', doc.name)
self.assertEquals(doc.status, "Quotation")
def test_make_new_lead_if_required(self):
args = {
"doctype": "Opportunity",
"contact_email":"new.opportunity@example.com",
"enquiry_type": "Sales",
"with_items": 0,
"transaction_date": today()
}
# new lead should be created against the new.opportunity@example.com
opp_doc = frappe.get_doc(args).insert(ignore_permissions=True)
self.assertTrue(opp_doc.lead)
self.assertEquals(opp_doc.enquiry_from, "Lead")
self.assertEquals(frappe.db.get_value("Lead", opp_doc.lead, "email_id"),
'new.opportunity@example.com')
# create new customer and create new contact against 'new.opportunity@example.com'
customer = make_customer(opp_doc.lead).insert(ignore_permissions=True)
frappe.get_doc({
"doctype": "Contact",
"email_id": "new.opportunity@example.com",
"first_name": "_Test Opportunity Customer",
"links": [{
"link_doctype": "Customer",
"link_name": customer.name
}]
}).insert(ignore_permissions=True)
opp_doc = frappe.get_doc(args).insert(ignore_permissions=True)
self.assertTrue(opp_doc.customer)
self.assertEquals(opp_doc.enquiry_from, "Customer")
self.assertEquals(opp_doc.customer, customer.name)
def make_opportunity(**args):
args = frappe._dict(args)
opp_doc = frappe.get_doc({
"doctype": "Opportunity",
"enquiry_from": "Customer" or args.enquiry_from,
"enquiry_from": args.enquiry_from or "Customer",
"enquiry_type": "Sales",
"with_items": args.with_items or 0,
"transaction_date": today()

View File

@ -21,23 +21,13 @@ frappe.query_reports["Minutes to First Response for Opportunity"] = {
get_chart_data: function (columns, result) {
return {
data: {
x: 'Date',
columns: [
['Date'].concat($.map(result, function (d) { return d[0]; })),
['Mins to first response'].concat($.map(result, function (d) { return d[1]; }))
]
// rows: [['Date', 'Mins to first response']].concat(result)
labels: result.map(d => d[0]),
datasets: [{
title: 'Mins to first response',
values: result.map(d => d[1])
}]
},
axis: {
x: {
type: 'timeseries',
tick: {
format: frappe.ui.py_date_format
}
}
},
chart_type: 'line',
type: 'line',
}
}
}

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})

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

View File

@ -16,7 +16,9 @@ To set credit limit go to Customer - Master
<img class="screenshot" alt="Credit Limit" src="/docs/assets/img/accounts/credit-limit-1.png">
Go to the 'More Info section' and enter the amount in the field Credit Limit.
Go to the 'CREDIT LIMIT' section and enter the amount in the field Credit Limit.
If you leave CREDIT LIMIT as 0.00, it has no effect.
In case a need arises to allow more credit to the customer as a good-will, the
Credit Controller has access to submit order even if credit limit is crossed.
@ -26,6 +28,25 @@ has expired, go to accounting settings and make changes.
In the field Credit Controller, select the role who would be authorized to
accept orders or raise credit limits of customers.
To set credit limit at Customer Group Level go to Selling -> Customers -> Customer Group
Go to the 'CREDIT LIMIT' field and enter the amount.
If you leave CREDIT LIMIT as 0.00, it has no effect.
To set credit limit at Company level go to Account -> Company
Go to the 'ACCOUNT SETTINGS' section and enter the amount in the CREDIT LIMIT field.
If you leave CREDIT LIMIT as 0.00, it has no effect.
For 'CREDIT LIMIT' check functionality, Priority (High to Low) is as below
1) Customer
2) Customer Group
3) Company
#### Figure 2: Credit Controller

View File

@ -0,0 +1,9 @@
# Hospitality
ERPNext Hospitality module is designed to handle workflows for Hotels and Restaurants. This is still in early development stage.
### Manage Restaurants
The Restaurant module in ERPNext will help you manage a chain of restaurants. You can create Restaurants, Menus, Tables, Reservations and a manage Order Entry and Billing.
{index}

View File

@ -0,0 +1,4 @@
restaurant
restaurant-menu
reservations
order-entry

View File

@ -0,0 +1,26 @@
# Restaurant Order Entry
The Restaurant Order Entry is the screen where the waiters will punch in orders related to a particular table.
This screen makes it easy for the waiters in your restaurant to punch in orders from various tables.
When the guest places an order, the waiter will select the table number and add the items in the Order Entry. This can be changed until it is time for the bill. Unless you bill a table, you can change the items and they will automatically appear when you select the table ID.
To place an order you can select an item and click the enter key so that the item will be updated in the items table.
<img class="screenshot" alt="Order Entry" src="/docs/assets/img/restaurant/order-entry.png">
You can also choose items with the POS style item selector.
### Billing
When it is time for billing, you just choose the bill and you can select the customer and mode of payment. On saving, a Sales Invoice is generated and the order section becomes empty.
<img class="screenshot" alt="Order Entry" src="/docs/assets/img/restaurant/order-entry-bill.png">
### Sales Invoice
To print the invoice, you can click on the Invoice Link and print the invoice
<img class="screenshot" alt="Sales Invoice" src="/docs/assets/img/restaurant/restaurant-invoice.png">

View File

@ -0,0 +1,13 @@
# Restaurant Reservations
Once you have setup the restaurant and tables, you can start taking in reservations for your restaurant.
To take a reservation, just make a new Restaurant Reservation from the Restaurant Page and set the time, number of people and name of the guest.
<img class="screenshot" alt="Reservation" src="/docs/assets/img/restaurant/reservation.png">
### Kanban
As your guests walk in, You can also manage the reservations by making a simple Kanban board for the same.
<img class="screenshot" alt="Reservation Kanban Board" src="/docs/assets/img/restaurant/reservation-kanban.png">

View File

@ -0,0 +1,7 @@
# Restaurant Menu
For every restaurant you must set an active Restaurant Menu from which orders can be placed. You can also set the rates for each of the item for the day.
When you save the Restaurant Menu, a Price List is created for that Menu and all pricing is linked to that price list. This way you can easily control the items on offer and pricing from the menu.
<img class="screenshot" alt="Restaurant Menu" src="/docs/assets/img/restaurant/restaurant-menu.png">

View File

@ -0,0 +1,19 @@
# Restaurant
The Restaurant record represents one restaurant in your organization. To create a new Restaurant, just set the name, Company and Default Customer.
You can set a unique numbering prefix for each of your restaurants. All invoices for that restuarant will follow that numbering prefix.
If you have a default Sales Taxes and Charges Template, you can add it so that the same charge + tax will be applicable for all invoices in the restaurant.
<img class="screenshot" alt="Restaurant" src="/docs/assets/img/restaurant/restaurant.png">
After your restaurant is created, you can add Tables and Menus for that restaurant
### Adding Tables
You can add a Restaurant Table by creating a new Restaurant Table from the dashboard.
<img class="screenshot" alt="Restaurant Table" src="/docs/assets/img/restaurant/restaurant-table.png">

View File

@ -97,8 +97,8 @@ fiscal year.
A Cost Center is like an Account, but the only difference is that its
structure represents your business more closely than Accounts.
For example, in your Chart of Accounts, you can separate your expenses by its type
(i.e., travel, marketing, etc.). In your Chart of Cost Centers, you can separate
For example, in your Chart of Accounts, you can separate your expenses by its type
(i.e., travel, marketing, etc.). In your Chart of Cost Centers, you can separate
them by product line or business group (e.g., online sales, retail sales, etc.).
> Accounts > Chart of Cost Centers
@ -316,7 +316,7 @@ A record of the monthly salary given to an Employee.
#### Salary Structure
A template identifying all the components of an Employees' salary (earnings),
A template identifying all the components of an Employees' salary (earnings),
tax and other social security deductions.
> Human Resource > Salary and Payroll > Salary Structure

View File

@ -1,7 +1,3 @@
# Getting Started With Erpnext
<!-- Getting Started with ERPNext-->
# Getting Started with ERPNext
There are many ways to get started with ERPNext.

View File

@ -1,9 +1,5 @@
# The Champion
<!-- no-heading -->
<h1 class="white">The Champion</h1>
<img alt="Champion" class="screenshot" src="/docs/assets/img/setup/implementation-image.png">
We have seen dozens of ERP implementations over the past few years and we

View File

@ -1,13 +1,14 @@
# Student Admission
The admission process begins with filling the admission form. The Student Admission record enables to intitate your admission process for a given **Academic year**. ERPNext admission module allow you to create an admission record which can be then published on the ERPNext generate website.
The admission process begins with filling the admission form. The Student Admission record enables to initiate your admission process for a given **Academic year**. ERPNext admission module allows you to create an admission record which can be then published on the ERPNext generate website.
To create a Student Admission record go to :
**Schools** >> **Admissions** >> **Student Admission** >>
<img class="screenshot" alt="Student Applicant" src="/docs/assets/img/schools/student/student-admission.gif">
<img class="screenshot" alt="Student Applicant" src="/docs/assets/img/schools/admission/student-admission.gif">
Once an admission record is created, the age eligibility criteria can be determined for the every program. Similarly, you can also determine the application fee and naming series for every student applicant. If you keep the naming series blank then the default naming series will be applied for every student applicant.
Once a admission record is created it can be published on the website and the student can apply from the web portal itself.
The information provided in the Student Admission records will be used for the validation and creation of the Student Admission records (only if student admission link is filled there)

View File

@ -1,22 +0,0 @@
If you have a contract with the Customer where your organization gives bill to the Customer on a monthly, quarterly, half-yearly or annual basis, you can use subscription feature to make auto invoicing.
<img class="screenshot" alt="Subscription" src="{{docs_base_url}}/assets/img/subscription/subscription.png">
#### Scenario
Subscription for your hosted ERPNext account requires yearly renewal. We use Sales Invoice for generating proforma invoices. To automate proforma invoicing for renewal, we set original Sales Invoice on the subscription form. Recurring proforma invoice is created automatically just before customer's account is about to expire, and requires renewal. This recurring Proforma Invoice is also emailed automatically to the customer.
To set the subscription for the sales invoice
Goto Subscription > select base doctype "Sales Invoice" > select base docname "Invoice No" > Save
<img class="screenshot" alt="Subscription" src="{{docs_base_url}}/assets/img/subscription/subscription.gif">
**From Date and To Date**: This defines contract period with the customer.
**Repeat on Day**: If frequency is set as Monthly, then it will be day of the month on which recurring invoice will be generated.
**Notify By Email**: If you want to notify the user about auto recurring invoice.
**Print Format**: Select a print format to define document view which should be emailed to customer.
**Disabled**: It will stop to make auto recurring documents against the subscription

View File

@ -0,0 +1,18 @@
data = {
'desktop_icons': [
'Item',
'Customer',
'Supplier',
'Lead',
'Sales Order',
'Purchase Order',
'Task',
'Sales Invoice',
'CRM',
'ToDo'
],
'set_value': [
['Stock Settings', None, 'show_barcode_field', 1]
],
'default_portal_role': 'Customer'
}

View File

@ -0,0 +1,37 @@
data = {
'desktop_icons': [
'Student',
'Program',
'Course',
'Student Group',
'Instructor',
'Fees',
'Task',
'ToDo',
'Schools'
],
'default_portal_role': 'Student',
'restricted_roles': [
'Student',
'Instructor',
'Academics User'
],
'modules': [
'Schools'
],
'fixtures': [
dict(doctype='Academic Year', academic_year_name='2013-14'),
dict(doctype='Academic Year', academic_year_name='2014-15'),
dict(doctype='Academic Year', academic_year_name='2015-16'),
dict(doctype='Academic Year', academic_year_name='2016-17'),
dict(doctype='Academic Year', academic_year_name='2017-18'),
dict(doctype='Academic Year', academic_year_name='2018-19'),
dict(doctype='Academic Year', academic_year_name='2019-20'),
dict(doctype='Academic Term', academic_year='2016-17', term_name='Semester 1'),
dict(doctype='Academic Term', academic_year='2016-17', term_name='Semester 2'),
dict(doctype='Academic Term', academic_year='2016-17', term_name='Semester 3'),
dict(doctype='Academic Term', academic_year='2017-18', term_name='Semester 1'),
dict(doctype='Academic Term', academic_year='2017-18', term_name='Semester 2'),
dict(doctype='Academic Term', academic_year='2017-18', term_name='Semester 3')
]
}

View File

@ -0,0 +1,29 @@
data = {
'desktop_icons': [
'Patient',
'Patient Appointment',
'Consultation',
'Lab Test',
'Healthcare',
'Accounts',
'Buying',
'Stock',
'HR',
'ToDo'
],
'default_portal_role': 'Patient',
'restricted_roles': [
'Healthcare Administrator',
'LabTest Approver',
'Laboratory User',
'Nursing User',
'Physician',
'Patient'
],
'custom_fields': {
'Sales Invoice': dict(fieldname='appointment', label='Patient Appointment',
fieldtype='Link', options='Patient Appointment',
insert_after='customer')
},
'on_setup': 'erpnext.healthcare.setup.setup_healthcare'
}

View File

@ -0,0 +1,32 @@
data = {
'desktop_icons': [
'Restaurant',
'Accounts',
'Buying',
'Stock',
'HR',
'Project',
'ToDo'
],
'restricted_roles': [
'Restaurant Manager'
],
'custom_fields': {
'Sales Invoice': [
{
'fieldname': 'restaurant', 'fieldtype': 'Link', 'options': 'Restaurant',
'insert_after': 'customer_name', 'label': 'Restaurant',
},
{
'fieldname': 'restaurant_table', 'fieldtype': 'Link', 'options': 'Restaurant Table',
'insert_after': 'restaurant', 'label': 'Restaurant Table',
}
],
'Price List': [
{
'fieldname':'restaurant_menu', 'fieldtype':'Link', 'options':'Restaurant Menu', 'label':'Restaurant Menu',
'insert_after':'currency'
}
]
}
}

View File

@ -0,0 +1,25 @@
data = {
'desktop_icons': [
'Item',
'BOM',
'Customer',
'Supplier',
'Sales Order',
'Purchase Order',
'Production Order',
'Task',
'Accounts',
'HR',
'ToDo'
],
'properties': [
{'doctype': 'Item', 'fieldname': 'manufacturing', 'property': 'collapsible_depends_on', 'value': 'is_stock_item'},
],
'set_value': [
['Stock Settings', None, 'show_barcode_field', 1]
],
'restricted_roles': [
'Manufacturing User'
],
'default_portal_role': 'Customer'
}

20
erpnext/domains/retail.py Normal file
View File

@ -0,0 +1,20 @@
data = {
'desktop_icons': [
'POS',
'Item',
'Customer',
'Sales Invoice',
'Purchase Order',
'Accounts',
'Task',
'ToDo'
],
'properties': [
{'doctype': 'Item', 'fieldname': 'manufacturing', 'property': 'hidden', 'value': 1},
{'doctype': 'Customer', 'fieldname': 'credit_limit_section', 'property': 'hidden', 'value': 1},
],
'set_value': [
['Stock Settings', None, 'show_barcode_field', 1]
],
'default_portal_role': 'Customer'
}

View File

@ -0,0 +1,19 @@
data = {
'desktop_icons': [
'Project',
'Timesheet',
'Customer',
'Sales Order',
'Sales Invoice',
'CRM',
'Task',
'Expense Claim',
'Employee',
'HR',
'ToDo'
],
'set_value': [
['Stock Settings', None, 'show_barcode_field', 0]
],
'default_portal_role': 'Customer'
}

View File

@ -183,16 +183,20 @@ var btn_create_vital_signs = function (frm) {
var btn_update_status = function(frm, status){
var doc = frm.doc;
frappe.call({
method:
"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_status",
args: {appointmentId: doc.name, status:status},
callback: function(data){
if(!data.exc){
cur_frm.reload_doc();
}
frappe.confirm(__('Are you sure you want to cancel this appointment?'),
function() {
frappe.call({
method:
"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_status",
args: {appointmentId: doc.name, status:status},
callback: function(data){
if(!data.exc){
frm.reload_doc();
}
}
});
}
});
);
};
var btn_invoice_consultation = function(frm){

View File

@ -1,10 +1,12 @@
from __future__ import unicode_literals
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe import _
def setup_healthcare():
if frappe.db.exists('Medical Department', 'Cardiology'):
# already setup
return
create_medical_departments()
create_antibiotics()
create_test_uom()
@ -14,63 +16,55 @@ def setup_healthcare():
create_lab_test_items()
create_lab_test_template()
create_sensitivity()
make_custom_fields()
def make_custom_fields():
custom_fields = {
'Sales Invoice': [
dict(fieldname='appointment', label='Patient Appointment',
fieldtype='Link', options='Patient Appointment',
insert_after='customer')
]
}
create_custom_fields(custom_fields)
def create_medical_departments():
depts = ["Accident and emergency care" ,"Anaesthetics", "Biochemistry", "Cardiology", "Dermatology",
"Diagnostic imaging", "ENT", "Gastroenterology", "General Surgery", "Gynaecology",
"Haematology", "Maternity", "Microbiology", "Nephrology", "Neurology", "Oncology",
"Orthopaedics", "Pathology", "Physiotherapy", "Rheumatology", "Serology", "Urology"]
for d in depts:
departments = [
"Accident And Emergency Care" ,"Anaesthetics", "Biochemistry", "Cardiology", "Dermatology",
"Diagnostic Imaging", "ENT", "Gastroenterology", "General Surgery", "Gynaecology",
"Haematology", "Maternity", "Microbiology", "Nephrology", "Neurology", "Oncology",
"Orthopaedics", "Pathology", "Physiotherapy", "Rheumatology", "Serology", "Urology"
]
for department in departments:
mediacal_department = frappe.new_doc("Medical Department")
mediacal_department.department = d
mediacal_department.department = _(department)
try:
mediacal_department.save()
except frappe.DuplicateEntryError:
pass
def create_antibiotics():
abt = ["Amoxicillin", "Ampicillin", "Bacampicillin", "Carbenicillin", "Cloxacillin", "Dicloxacillin",
"Flucloxacillin", "Mezlocillin", "Nafcillin", "Oxacillin", "Penicillin G", "Penicillin V",
"Piperacillin", "Pivampicillin", "Pivmecillinam", "Ticarcillin", "Cefacetrile (cephacetrile)",
"Cefadroxil (cefadroxyl)", "Cefalexin (cephalexin)", "Cefaloglycin (cephaloglycin)",
"Cefalonium (cephalonium)", "Cefaloridine (cephaloradine)", "Cefalotin (cephalothin)",
"Cefapirin (cephapirin)", "Cefatrizine", "Cefazaflur", "Cefazedone", "Cefazolin (cephazolin)",
"Cefradine (cephradine)", "Cefroxadine", "Ceftezole", "Cefaclor", "Cefamandole", "Cefmetazole",
"Cefonicid", "Cefotetan", "Cefoxitin", "Cefprozil (cefproxil)", "Cefuroxime", "Cefuzonam",
"Cefcapene", "Cefdaloxime", "Cefdinir", "Cefditoren", "Cefetamet", "Cefixime", "Cefmenoxime",
"Cefodizime", "Cefotaxime", "Cefpimizole", "Cefpodoxime", "Cefteram", "Ceftibuten", "Ceftiofur",
"Ceftiolene", "Ceftizoxime", "Ceftriaxone", "Cefoperazone", "Ceftazidime", "Cefclidine", "Cefepime",
"Cefluprenam", "Cefoselis", "Cefozopran", "Cefpirome", "Cefquinome", "Ceftobiprole", "Ceftaroline",
"Cefaclomezine","Cefaloram", "Cefaparole", "Cefcanel", "Cefedrolor", "Cefempidone", "Cefetrizole",
"Cefivitril", "Cefmatilen", "Cefmepidium", "Cefovecin", "Cefoxazole", "Cefrotil", "Cefsumide",
"Cefuracetime", "Ceftioxide", "Ceftazidime/Avibactam", "Ceftolozane/Tazobactam", "Aztreonam",
"Imipenem", "Imipenem/cilastatin", "Doripenem", "Meropenem", "Ertapenem", "Azithromycin",
"Erythromycin", "Clarithromycin", "Dirithromycin", "Roxithromycin", "Telithromycin", "Clindamycin",
"Lincomycin", "Pristinamycin", "Quinupristin/dalfopristin", "Amikacin", "Gentamicin", "Kanamycin",
"Neomycin", "Netilmicin", "Paromomycin", "Streptomycin", "Tobramycin", "Flumequine", "Nalidixic acid",
"Oxolinic acid", "Piromidic acid", "Pipemidic acid", "Rosoxacin", "Ciprofloxacin", "Enoxacin",
"Lomefloxacin", "Nadifloxacin", "Norfloxacin", "Ofloxacin", "Pefloxacin", "Rufloxacin", "Balofloxacin",
"Gatifloxacin", "Grepafloxacin", "Levofloxacin", "Moxifloxacin", "Pazufloxacin", "Sparfloxacin",
"Temafloxacin", "Tosufloxacin", "Besifloxacin", "Clinafloxacin", "Gemifloxacin",
"Sitafloxacin", "Trovafloxacin", "Prulifloxacin", "Sulfamethizole", "Sulfamethoxazole",
"Sulfisoxazole", "Trimethoprim-Sulfamethoxazole", "Demeclocycline", "Doxycycline", "Minocycline",
"Oxytetracycline", "Tetracycline", "Tigecycline", "Chloramphenicol", "Metronidazole",
"Tinidazole", "Nitrofurantoin", "Vancomycin", "Teicoplanin", "Telavancin", "Linezolid",
"Cycloserine 2", "Rifampin", "Rifabutin", "Rifapentine", "Rifalazil", "Bacitracin", "Polymyxin B",
"Viomycin", "Capreomycin"]
abt = [
"Amoxicillin", "Ampicillin", "Bacampicillin", "Carbenicillin", "Cloxacillin", "Dicloxacillin",
"Flucloxacillin", "Mezlocillin", "Nafcillin", "Oxacillin", "Penicillin G", "Penicillin V",
"Piperacillin", "Pivampicillin", "Pivmecillinam", "Ticarcillin", "Cefacetrile (cephacetrile)",
"Cefadroxil (cefadroxyl)", "Cefalexin (cephalexin)", "Cefaloglycin (cephaloglycin)",
"Cefalonium (cephalonium)", "Cefaloridine (cephaloradine)", "Cefalotin (cephalothin)",
"Cefapirin (cephapirin)", "Cefatrizine", "Cefazaflur", "Cefazedone", "Cefazolin (cephazolin)",
"Cefradine (cephradine)", "Cefroxadine", "Ceftezole", "Cefaclor", "Cefamandole", "Cefmetazole",
"Cefonicid", "Cefotetan", "Cefoxitin", "Cefprozil (cefproxil)", "Cefuroxime", "Cefuzonam",
"Cefcapene", "Cefdaloxime", "Cefdinir", "Cefditoren", "Cefetamet", "Cefixime", "Cefmenoxime",
"Cefodizime", "Cefotaxime", "Cefpimizole", "Cefpodoxime", "Cefteram", "Ceftibuten", "Ceftiofur",
"Ceftiolene", "Ceftizoxime", "Ceftriaxone", "Cefoperazone", "Ceftazidime", "Cefclidine", "Cefepime",
"Cefluprenam", "Cefoselis", "Cefozopran", "Cefpirome", "Cefquinome", "Ceftobiprole", "Ceftaroline",
"Cefaclomezine","Cefaloram", "Cefaparole", "Cefcanel", "Cefedrolor", "Cefempidone", "Cefetrizole",
"Cefivitril", "Cefmatilen", "Cefmepidium", "Cefovecin", "Cefoxazole", "Cefrotil", "Cefsumide",
"Cefuracetime", "Ceftioxide", "Ceftazidime/Avibactam", "Ceftolozane/Tazobactam", "Aztreonam",
"Imipenem", "Imipenem/cilastatin", "Doripenem", "Meropenem", "Ertapenem", "Azithromycin",
"Erythromycin", "Clarithromycin", "Dirithromycin", "Roxithromycin", "Telithromycin", "Clindamycin",
"Lincomycin", "Pristinamycin", "Quinupristin/dalfopristin", "Amikacin", "Gentamicin", "Kanamycin",
"Neomycin", "Netilmicin", "Paromomycin", "Streptomycin", "Tobramycin", "Flumequine", "Nalidixic acid",
"Oxolinic acid", "Piromidic acid", "Pipemidic acid", "Rosoxacin", "Ciprofloxacin", "Enoxacin",
"Lomefloxacin", "Nadifloxacin", "Norfloxacin", "Ofloxacin", "Pefloxacin", "Rufloxacin", "Balofloxacin",
"Gatifloxacin", "Grepafloxacin", "Levofloxacin", "Moxifloxacin", "Pazufloxacin", "Sparfloxacin",
"Temafloxacin", "Tosufloxacin", "Besifloxacin", "Clinafloxacin", "Gemifloxacin",
"Sitafloxacin", "Trovafloxacin", "Prulifloxacin", "Sulfamethizole", "Sulfamethoxazole",
"Sulfisoxazole", "Trimethoprim-Sulfamethoxazole", "Demeclocycline", "Doxycycline", "Minocycline",
"Oxytetracycline", "Tetracycline", "Tigecycline", "Chloramphenicol", "Metronidazole",
"Tinidazole", "Nitrofurantoin", "Vancomycin", "Teicoplanin", "Telavancin", "Linezolid",
"Cycloserine 2", "Rifampin", "Rifabutin", "Rifapentine", "Rifalazil", "Bacitracin", "Polymyxin B",
"Viomycin", "Capreomycin"
]
for a in abt:
antibiotic = frappe.new_doc("Antibiotic")
antibiotic.antibiotic_name = a
@ -190,65 +184,65 @@ def create_healthcare_item_groups():
def create_lab_test_items():
records = [
{"doctype": "Item", "item_code": "MCH", "item_name": "MCH", "item_group": "Laboratory",
"stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1},
{"doctype": "Item", "item_code": "LDL", "item_name": "LDL", "item_group": "Laboratory",
"stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1},
{"doctype": "Item", "item_code": "GTT", "item_name": "GTT", "item_group": "Laboratory",
"stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1},
{"doctype": "Item", "item_code": "HDL", "item_name": "HDL", "item_group": "Laboratory",
"stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1},
{"doctype": "Item", "item_code": "BILT", "item_name": "BILT", "item_group": "Laboratory",
"stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1},
{"doctype": "Item", "item_code": "BILD", "item_name": "BILD", "item_group": "Laboratory",
"stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1},
{"doctype": "Item", "item_code": "BP", "item_name": "BP", "item_group": "Laboratory",
"stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1},
{"doctype": "Item", "item_code": "BS", "item_name": "BS", "item_group": "Laboratory",
"stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}
{"doctype": "Item", "item_code": "MCH", "item_name": "MCH", "item_group": _("Laboratory"),
"stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1},
{"doctype": "Item", "item_code": "LDL", "item_name": "LDL", "item_group": _("Laboratory"),
"stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1},
{"doctype": "Item", "item_code": "GTT", "item_name": "GTT", "item_group": _("Laboratory"),
"stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1},
{"doctype": "Item", "item_code": "HDL", "item_name": "HDL", "item_group": _("Laboratory"),
"stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1},
{"doctype": "Item", "item_code": "BILT", "item_name": "BILT", "item_group": _("Laboratory"),
"stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1},
{"doctype": "Item", "item_code": "BILD", "item_name": "BILD", "item_group": _("Laboratory"),
"stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1},
{"doctype": "Item", "item_code": "BP", "item_name": "BP", "item_group": _("Laboratory"),
"stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1},
{"doctype": "Item", "item_code": "BS", "item_name": "BS", "item_group": _("Laboratory"),
"stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}
]
insert_record(records)
def create_lab_test_template():
records = [
{"doctype": "Lab Test Template", "name": "MCH","test_name": "MCH","test_code": "MCH",
"test_group": "Laboratory","department": "Haematology","item": "MCH",
"test_group": _("Laboratory"),"department": _("Haematology"),"item": "MCH",
"test_template_type": "Single","is_billable": 1,"test_rate": 0.0,"test_uom": "Microgram",
"test_normal_range": "27 - 32 Microgram",
"sensitivity": 0,"test_description": "Mean Corpuscular Hemoglobin"},
{"doctype": "Lab Test Template", "name": "LDL","test_name": "LDL (Serum)","test_code": "LDL",
"test_group": "Laboratory","department": "Biochemistry",
"test_group": _("Laboratory"),"department": _("Biochemistry"),
"item": "LDL","test_template_type": "Single",
"is_billable": 1,"test_rate": 0.0,"test_uom": "mg / dl","test_normal_range": "70 - 160 mg/dlLow-density Lipoprotein (LDL)",
"sensitivity": 0,"test_description": "Low-density Lipoprotein (LDL)"},
{"doctype": "Lab Test Template", "name": "GTT","test_name": "GTT","test_code": "GTT",
"test_group": "Laboratory","department": "Haematology",
"test_group": _("Laboratory"),"department": _("Haematology"),
"item": "GTT","test_template_type": "Single",
"is_billable": 1,"test_rate": 0.0,"test_uom": "mg / dl","test_normal_range": "Less than 85 mg/dl",
"sensitivity": 0,"test_description": "Glucose Tolerance Test"},
{"doctype": "Lab Test Template", "name": "HDL","test_name": "HDL (Serum)","test_code": "HDL",
"test_group": "Laboratory","department": "Biochemistry",
"test_group": _("Laboratory"),"department": _("Biochemistry"),
"item": "HDL","test_template_type": "Single",
"is_billable": 1,"test_rate": 0.0,"test_uom": "mg / dl","test_normal_range": "35 - 65 mg/dl",
"sensitivity": 0,"test_description": "High-density Lipoprotein (HDL)"},
{"doctype": "Lab Test Template", "name": "BILT","test_name": "Bilirubin Total","test_code": "BILT",
"test_group": "Laboratory","department": "Biochemistry",
"test_group": _("Laboratory"),"department": _("Biochemistry"),
"item": "BILT","test_template_type": "Single",
"is_billable": 1,"test_rate": 0.0,"test_uom": "mg / dl","test_normal_range": "0.2 - 1.2 mg / dl",
"sensitivity": 0,"test_description": "Bilirubin Total"},
{"doctype": "Lab Test Template", "name": "BILD","test_name": "Bilirubin Direct","test_code": "BILD",
"test_group": "Laboratory","department": "Biochemistry",
"test_group": _("Laboratory"),"department": _("Biochemistry"),
"item": "BILD","test_template_type": "Single",
"is_billable": 1,"test_rate": 0.0,"test_uom": "mg / dl","test_normal_range": "0.4 mg / dl",
"sensitivity": 0,"test_description": "Bilirubin Direct"},
{"doctype": "Lab Test Template", "name": "BP","test_name": "Bile Pigment","test_code": "BP",
"test_group": "Laboratory","department": "Pathology",
"test_group": _("Laboratory"),"department": _("Pathology"),
"item": "BP","test_template_type": "Single",
"is_billable": 1,"test_rate": 0.0,"test_uom": "","test_normal_range": "",
"sensitivity": 0,"test_description": "Bile Pigment"},
{"doctype": "Lab Test Template", "name": "BS","test_name": "Bile Salt","test_code": "BS",
"test_group": "Laboratory","department": "Pathology",
"test_group": _("Laboratory"),"department": _("Pathology"),
"item": "BS","test_template_type": "Single",
"is_billable": 1,"test_rate": 0.0,"test_uom": "","test_normal_range": "",
"sensitivity": 0,"test_description": "Bile Salt"}
@ -257,12 +251,12 @@ def create_lab_test_template():
def create_sensitivity():
records = [
{"doctype": "Sensitivity", "sensitivity": "Low Sensitivity"},
{"doctype": "Sensitivity", "sensitivity": "High Sensitivity"},
{"doctype": "Sensitivity", "sensitivity": "Moderate Sensitivity"},
{"doctype": "Sensitivity", "sensitivity": "Susceptible"},
{"doctype": "Sensitivity", "sensitivity": "Resistant"},
{"doctype": "Sensitivity", "sensitivity": "Intermediate"}
{"doctype": "Sensitivity", "sensitivity": _("Low Sensitivity")},
{"doctype": "Sensitivity", "sensitivity": _("High Sensitivity")},
{"doctype": "Sensitivity", "sensitivity": _("Moderate Sensitivity")},
{"doctype": "Sensitivity", "sensitivity": _("Susceptible")},
{"doctype": "Sensitivity", "sensitivity": _("Resistant")},
{"doctype": "Sensitivity", "sensitivity": _("Intermediate")}
]
insert_record(records)

View File

@ -47,12 +47,22 @@ 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"]
fixtures = ["Web Form"]
domains = {
'Distribution': 'erpnext.domains.distribution',
'Education': 'erpnext.domains.education',
'Healthcare': 'erpnext.domains.healthcare',
'Hospitality': 'erpnext.domains.hospitality',
'Manufacturing': 'erpnext.domains.manufacturing',
'Retail': 'erpnext.domains.retail',
'Services': 'erpnext.domains.services',
}
website_generators = ["Item Group", "Item", "BOM", "Sales Partner",
"Job Opening", "Student Admission"]
@ -133,7 +143,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 +219,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

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
@ -10,16 +11,20 @@
"editable_grid": 1,
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "expense_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": "Expense Date",
"length": 0,
"no_copy": 0,
@ -30,6 +35,7 @@
"print_hide_if_no_value": 0,
"print_width": "150px",
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@ -38,16 +44,20 @@
"width": "150px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"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,
@ -55,6 +65,7 @@
"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,
@ -62,16 +73,20 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "expense_type",
"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": "Expense Claim Type",
"length": 0,
"no_copy": 0,
@ -83,6 +98,7 @@
"print_hide_if_no_value": 0,
"print_width": "150px",
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@ -91,9 +107,11 @@
"width": "150px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "expense_type",
"fieldname": "default_account",
"fieldtype": "Link",
@ -101,7 +119,9 @@
"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 Account",
"length": 0,
"no_copy": 0,
@ -111,6 +131,7 @@
"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,
@ -118,16 +139,20 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_4",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -135,6 +160,7 @@
"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,
@ -142,26 +168,32 @@
"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": 1,
"in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
"oldfieldname": "description",
"oldfieldtype": "Small Text",
"options": "expense_type.description",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "300px",
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@ -170,16 +202,20 @@
"width": "300px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -187,6 +223,7 @@
"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,
@ -194,16 +231,20 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "claim_amount",
"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": "Claim Amount",
"length": 0,
"no_copy": 0,
@ -215,6 +256,7 @@
"print_hide_if_no_value": 0,
"print_width": "150px",
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@ -223,16 +265,20 @@
"width": "150px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_8",
"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,
@ -240,6 +286,7 @@
"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,
@ -247,16 +294,20 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sanctioned_amount",
"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": "Sanctioned Amount",
"length": 0,
"no_copy": 1,
@ -268,6 +319,7 @@
"print_hide_if_no_value": 0,
"print_width": "150px",
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@ -276,17 +328,17 @@
"width": "150px"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-07-11 03:28:00.406154",
"modified": "2017-10-11 12:50:48.606727",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim Detail",
@ -295,7 +347,9 @@
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0
}

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

@ -70,7 +70,8 @@ class SalarySlip(TransactionBase):
'default_amount': amount,
'depends_on_lwp' : struct_row.depends_on_lwp,
'salary_component' : struct_row.salary_component,
'abbr' : struct_row.abbr
'abbr' : struct_row.abbr,
'do_not_include_in_total' : struct_row.do_not_include_in_total
})
else:
component_row.amount = amount
@ -346,11 +347,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

@ -13,6 +13,7 @@ QUnit.test("test Salary Structure", function(assert) {
(r) => {
// Creating Salary Structure for employees);
return frappe.tests.make('Salary Structure', [
{ __newname: 'Test Salary Structure'},
{ company: 'For Testing'},
{ payroll_frequency: 'Monthly'},
{ employees: [
@ -47,11 +48,7 @@ QUnit.test("test Salary Structure", function(assert) {
]);
}
),
() => frappe.timeout(18),
() => cur_dialog.set_value('value','Test Salary Structure'),
() => frappe.timeout(1),
() => frappe.click_button('Create'),
() => frappe.timeout(1),
() => frappe.timeout(3),
() => {
// To check if all the fields are correctly set
assert.ok(cur_frm.doc.employees[0].employee_name.includes('Test Employee 1') &&

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

@ -73,6 +73,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.type == 'Seminar' || doc.type == 'Workshop' || doc.type == 'Conference' || doc.type == 'Exam'",
"fieldname": "has_certificate",
"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": "Has Certificate",
"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,
@ -133,6 +164,69 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.type == 'Seminar' || doc.type == 'Workshop' || doc.type == 'Exam'",
"fieldname": "level",
"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": "Level",
"length": 0,
"no_copy": 0,
"options": "\nBeginner\nExpert\nAdvance",
"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": 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 +778,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-08-11 03:11:25.768563",
"modified": "2017-10-06 10:59:09.217283",
"modified_by": "Administrator",
"module": "HR",
"name": "Training Event",

View File

@ -3,6 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from erpnext.hr.doctype.employee.employee import get_employee_emails
@ -10,3 +11,29 @@ class TrainingEvent(Document):
def validate(self):
self.employee_emails = ', '.join(get_employee_emails([d.employee
for d in self.employees]))
@frappe.whitelist()
def get_events(start, end, filters=None):
"""Returns events for Gantt / Calendar view rendering.
:param start: Start date-time.
:param end: End date-time.
:param filters: Filters (JSON).
"""
from frappe.desk.calendar import get_event_conditions
conditions = get_event_conditions("Training Event", filters)
data = frappe.db.sql("""
select
name, event_name, event_status, start_time, end_time
from
`tabTraining Event`
where (ifnull(start_time, '0000-00-00')!= '0000-00-00') \
and (start_time between %(start)s and %(end)s)
and docstatus < 2
{conditions}
""".format(conditions=conditions), {
"start": start,
"end": end
}, as_dict=True, update={"allDay": 0})
return data

View File

@ -0,0 +1,14 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.views.calendar["Training Event"] = {
field_map: {
"start": "start_time",
"end": "end_time",
"id": "name",
"title": "event_name",
"allDay": "allDay"
},
gantt: true,
get_events_method: "erpnext.hr.doctype.training_event.training_event.get_events",
}

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

@ -39,6 +39,7 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
args: {
method: 'erpnext.hr.doctype.upload_attendance.upload_attendance.upload'
},
no_socketio: true,
sample_url: "e.g. http://example.com/somefile.csv",
callback: function(attachment, r) {
var $log_wrapper = $(cur_frm.fields_dict.import_log.wrapper).empty();

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 (!isNaN(row[fieldname])) { %}
{%= 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

@ -29,26 +29,26 @@ def get_columns():
def get_log_data(filters):
fy = frappe.db.get_value('Fiscal Year', filters.get('fiscal_year'), ['year_start_date', 'year_end_date'], as_dict=True)
data = frappe.db.sql("""select
data = frappe.db.sql("""select
vhcl.license_plate as "License", vhcl.make as "Make", vhcl.model as "Model",
vhcl.location as "Location", log.name as "Log", log.odometer as "Odometer",
vhcl.location as "Location", log.name as "Log", log.odometer as "Odometer",
log.date as "Date", log.fuel_qty as "Fuel Qty", log.price as "Fuel Price"
from
from
`tabVehicle` vhcl,`tabVehicle Log` log
where
where
vhcl.license_plate = log.license_plate and log.docstatus = 1 and date between %s and %s
order by date""" ,(fy.year_start_date, fy.year_end_date), as_dict=1)
dl=list(data)
for row in dl:
row["Service Expense"]= get_service_expense(row["Log"])
return dl
def get_service_expense(logname):
expense_amount = frappe.db.sql("""select sum(expense_amount)
from `tabVehicle Log` log,`tabVehicle Service` ser
expense_amount = frappe.db.sql("""select sum(expense_amount)
from `tabVehicle Log` log,`tabVehicle Service` ser
where ser.parent=log.name and log.name=%s""",logname)
return flt(expense_amount[0][0]) if expense_amount else 0
def get_chart_data(data,period_list):
fuel_exp_data,service_exp_data,fueldata,servicedata = [],[],[],[]
service_exp_data = []
@ -63,19 +63,25 @@ def get_chart_data(data,period_list):
fueldata.append([period.key,total_fuel_exp])
servicedata.append([period.key,total_ser_exp])
x_intervals = ['x'] + [period.key for period in period_list]
labels = [period.key for period in period_list]
fuel_exp_data= [row[1] for row in fueldata]
service_exp_data= [row[1] for row in servicedata]
columns = [x_intervals]
datasets = []
if fuel_exp_data:
columns.append(["Fuel Expenses"]+ fuel_exp_data)
datasets.append({
'title': 'Fuel Expenses',
'values': fuel_exp_data
})
if service_exp_data:
columns.append(["Service Expenses"]+ service_exp_data)
datasets.append({
'title': 'Service Expenses',
'values': service_exp_data
})
chart = {
"data": {
'x': 'x',
'columns': columns
'labels': labels,
'datasets': datasets
}
}
chart["chart_type"] = "line"
chart["type"] = "line"
return chart

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

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-10-09 17:30:17.853929",
"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-10-09 17:30:17.908830",
"modified_by": "Administrator",
"name": "Hub Message to Lead",
"owner": "frappetest@gmail.com",
"page_length": 10,
"remote_objectname": "Hub Message",
"remote_primary_key": "name"
}

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